From 856547be238fded2396eaf82e7c7d9502836bb22 Mon Sep 17 00:00:00 2001 From: Garux Date: Mon, 5 Apr 2021 17:06:49 +0300 Subject: [PATCH 001/335] Fix Q1 MDL group frame loading, e.g. Q1 progs/flame2.mdl --- code/AssetLib/MDL/MDLFileData.h | 4 +++- code/AssetLib/MDL/MDLLoader.cpp | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/code/AssetLib/MDL/MDLFileData.h b/code/AssetLib/MDL/MDLFileData.h index bbb6f7728..872cee7f8 100644 --- a/code/AssetLib/MDL/MDLFileData.h +++ b/code/AssetLib/MDL/MDLFileData.h @@ -696,6 +696,8 @@ struct GroupFrame //! 0 = simple frame, !0 = group frame int32_t type; + int32_t numframes; + //! Minimum vertex for all single frames Vertex min; @@ -703,7 +705,7 @@ struct GroupFrame Vertex max; //! Time for all single frames - float *time; + float time; // float[numframes] //! List of single frames SimpleFrame *frames; diff --git a/code/AssetLib/MDL/MDLLoader.cpp b/code/AssetLib/MDL/MDLLoader.cpp index a4286a716..b5010a37f 100644 --- a/code/AssetLib/MDL/MDLLoader.cpp +++ b/code/AssetLib/MDL/MDLLoader.cpp @@ -428,7 +428,7 @@ void MDLImporter::InternReadFile_Quake1() { } else { // get the first frame in the group BE_NCONST MDL::GroupFrame *pcFrames2 = (BE_NCONST MDL::GroupFrame *)pcFrames; - pcFirstFrame = &(pcFrames2->frames[0]); + pcFirstFrame = (MDL::SimpleFrame *)( &pcFrames2->time + pcFrames2->numframes ); } BE_NCONST MDL::Vertex *pcVertices = (BE_NCONST MDL::Vertex *)((pcFirstFrame->name) + sizeof(pcFirstFrame->name)); VALIDATE_FILE_SIZE((const unsigned char *)(pcVertices + pcHeader->num_verts)); From f761dc72f426444d56dbe9ed12b2823b08a68aa9 Mon Sep 17 00:00:00 2001 From: Krishty Date: Fri, 16 Apr 2021 23:43:56 +0200 Subject: [PATCH 002/335] style fix - initializing and assigning empty std::string properly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit std::string s(""); s = ""; calls the copy constructor, which in turn calls strlen(), … assigning a default-constructed string generates fewer instructions and is therefore preferred. With C++11 uniform initialization, you’d simply write s = { } instead. --- code/AssetLib/3DS/3DSConverter.cpp | 2 +- code/AssetLib/3DS/3DSLoader.cpp | 2 +- code/AssetLib/3MF/D3MFOpcPackage.cpp | 2 +- code/AssetLib/AC/ACLoader.h | 4 ++-- code/AssetLib/ASE/ASEParser.cpp | 2 +- code/AssetLib/Collada/ColladaLoader.cpp | 2 +- code/AssetLib/FBX/FBXMeshGeometry.cpp | 2 +- code/AssetLib/FBX/FBXParser.cpp | 8 ++++---- code/AssetLib/FBX/FBXProperties.cpp | 2 +- code/AssetLib/IFC/IFCLoader.cpp | 4 ++-- code/AssetLib/Irr/IRRLoader.cpp | 2 +- code/AssetLib/Irr/IRRMeshLoader.cpp | 2 +- code/AssetLib/LWO/LWOFileData.h | 2 +- code/AssetLib/LWS/LWSLoader.h | 2 +- .../MDL/HalfLife/UniqueNameGenerator.cpp | 2 +- code/AssetLib/MMD/MMDImporter.cpp | 2 +- code/AssetLib/MMD/MMDPmxParser.cpp | 2 +- code/AssetLib/OpenGEX/OpenGEXImporter.cpp | 2 +- code/AssetLib/OpenGEX/OpenGEXImporter.h | 2 +- code/AssetLib/Q3BSP/Q3BSPFileData.h | 2 +- code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp | 16 ++++++++-------- code/AssetLib/X/XFileHelper.h | 2 +- code/AssetLib/XGL/XGLLoader.cpp | 2 +- code/AssetLib/glTF/glTFAsset.h | 2 +- code/AssetLib/glTF2/glTF2Asset.h | 2 +- code/Common/BaseImporter.cpp | 2 +- code/Common/FileSystemFilter.h | 2 +- code/Common/Importer.cpp | 6 +++--- code/Common/scene.cpp | 2 +- code/PostProcessing/RemoveRedundantMaterials.h | 2 +- include/assimp/DefaultIOStream.h | 2 +- include/assimp/IOSystem.hpp | 2 +- include/assimp/Importer.hpp | 2 +- include/assimp/XmlParser.h | 2 +- 34 files changed, 48 insertions(+), 48 deletions(-) diff --git a/code/AssetLib/3DS/3DSConverter.cpp b/code/AssetLib/3DS/3DSConverter.cpp index 12ac7e69e..c977867c0 100644 --- a/code/AssetLib/3DS/3DSConverter.cpp +++ b/code/AssetLib/3DS/3DSConverter.cpp @@ -212,7 +212,7 @@ void Discreet3DSImporter::ConvertMaterial(D3DS::Material &oldMat, mat.AddProperty(&tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE); // Be sure this is only done for the first material - mBackgroundImage = std::string(""); + mBackgroundImage = std::string(); } // At first add the base ambient color of the scene to the material diff --git a/code/AssetLib/3DS/3DSLoader.cpp b/code/AssetLib/3DS/3DSLoader.cpp index 0a64f6870..10aa10786 100644 --- a/code/AssetLib/3DS/3DSLoader.cpp +++ b/code/AssetLib/3DS/3DSLoader.cpp @@ -164,7 +164,7 @@ void Discreet3DSImporter::InternReadFile(const std::string &pFile, mRootNode->mHierarchyIndex = -1; mRootNode->mParent = nullptr; mMasterScale = 1.0f; - mBackgroundImage = ""; + mBackgroundImage = std::string(); bHasBG = false; bIsPrj = false; diff --git a/code/AssetLib/3MF/D3MFOpcPackage.cpp b/code/AssetLib/3MF/D3MFOpcPackage.cpp index 514e3c2f3..ec0861fc1 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.cpp +++ b/code/AssetLib/3MF/D3MFOpcPackage.cpp @@ -188,7 +188,7 @@ bool D3MFOpcPackage::validate() { std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream *stream) { XmlParser xmlParser; if (!xmlParser.parse(stream)) { - return ""; + return std::string(); } OpcPackageRelationshipReader reader(xmlParser); diff --git a/code/AssetLib/AC/ACLoader.h b/code/AssetLib/AC/ACLoader.h index 77de16b8b..5ae2e80bb 100644 --- a/code/AssetLib/AC/ACLoader.h +++ b/code/AssetLib/AC/ACLoader.h @@ -123,9 +123,9 @@ public: struct Object { Object() : type(World), - name(""), + name(), children(), - texture(""), + texture(), texRepeat(1.f, 1.f), texOffset(0.0f, 0.0f), rotation(), diff --git a/code/AssetLib/ASE/ASEParser.cpp b/code/AssetLib/ASE/ASEParser.cpp index 21ec26593..530766730 100644 --- a/code/AssetLib/ASE/ASEParser.cpp +++ b/code/AssetLib/ASE/ASEParser.cpp @@ -685,7 +685,7 @@ void Parser::ParseLV3MapBlock(Texture &map) { // Files with 'None' as map name are produced by // an Maja to ASE exporter which name I forgot .. ASSIMP_LOG_WARN("ASE: Skipping invalid map entry"); - map.mMapName = ""; + map.mMapName = std::string(); } continue; diff --git a/code/AssetLib/Collada/ColladaLoader.cpp b/code/AssetLib/Collada/ColladaLoader.cpp index 902eebc4b..faaa7cdba 100644 --- a/code/AssetLib/Collada/ColladaLoader.cpp +++ b/code/AssetLib/Collada/ColladaLoader.cpp @@ -1241,7 +1241,7 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse continue; } entry.mTargetId = entry.mTransformId; - entry.mTransformId = ""; + entry.mTransformId = std::string(); } entry.mChannel = &(*cit); diff --git a/code/AssetLib/FBX/FBXMeshGeometry.cpp b/code/AssetLib/FBX/FBXMeshGeometry.cpp index fd8a0ff98..2bca8dff2 100644 --- a/code/AssetLib/FBX/FBXMeshGeometry.cpp +++ b/code/AssetLib/FBX/FBXMeshGeometry.cpp @@ -330,7 +330,7 @@ void MeshGeometry::ReadVertexData(const std::string& type, int index, const Scop } const Element* Name = source["Name"]; - m_uvNames[index] = ""; + m_uvNames[index] = std::string(); if(Name) { m_uvNames[index] = ParseTokenAsString(GetRequiredToken(*Name,0)); } diff --git a/code/AssetLib/FBX/FBXParser.cpp b/code/AssetLib/FBX/FBXParser.cpp index 046d26c95..37cf83cf9 100644 --- a/code/AssetLib/FBX/FBXParser.cpp +++ b/code/AssetLib/FBX/FBXParser.cpp @@ -462,7 +462,7 @@ std::string ParseTokenAsString(const Token& t, const char*& err_out) if (t.Type() != TokenType_DATA) { err_out = "expected TOK_DATA token"; - return ""; + return std::string(); } if(t.IsBinary()) @@ -470,7 +470,7 @@ std::string ParseTokenAsString(const Token& t, const char*& err_out) const char* data = t.begin(); if (data[0] != 'S') { err_out = "failed to parse S(tring), unexpected data type (binary)"; - return ""; + return std::string(); } // read string length @@ -484,13 +484,13 @@ std::string ParseTokenAsString(const Token& t, const char*& err_out) const size_t length = static_cast(t.end() - t.begin()); if(length < 2) { err_out = "token is too short to hold a string"; - return ""; + return std::string(); } const char* s = t.begin(), *e = t.end() - 1; if (*s != '\"' || *e != '\"') { err_out = "expected double quoted string"; - return ""; + return std::string(); } return std::string(s+1,length-2); diff --git a/code/AssetLib/FBX/FBXProperties.cpp b/code/AssetLib/FBX/FBXProperties.cpp index 2f39fc7dc..1e4cd0ead 100644 --- a/code/AssetLib/FBX/FBXProperties.cpp +++ b/code/AssetLib/FBX/FBXProperties.cpp @@ -155,7 +155,7 @@ std::string PeekPropertyName(const Element& element) ai_assert(element.KeyToken().StringContents() == "P"); const TokenList& tok = element.Tokens(); if(tok.size() < 4) { - return ""; + return std::string(); } return ParseTokenAsString(*tok[0]); diff --git a/code/AssetLib/IFC/IFCLoader.cpp b/code/AssetLib/IFC/IFCLoader.cpp index be100df57..df1ca32de 100644 --- a/code/AssetLib/IFC/IFCLoader.cpp +++ b/code/AssetLib/IFC/IFCLoader.cpp @@ -567,7 +567,7 @@ typedef std::map Metadata; // ------------------------------------------------------------------------------------------------ void ProcessMetadata(const Schema_2x3::ListOf, 1, 0> &set, ConversionData &conv, Metadata &properties, - const std::string &prefix = "", + const std::string &prefix = std::string(), unsigned int nest = 0) { for (const Schema_2x3::IfcProperty &property : set) { const std::string &key = prefix.length() > 0 ? (prefix + "." + property.Name) : property.Name; @@ -618,7 +618,7 @@ void ProcessMetadata(const Schema_2x3::ListOfHasProperties, conv, properties, key, nest + 1); } } else { - properties[key] = ""; + properties[key] = std::string(); } } } diff --git a/code/AssetLib/Irr/IRRLoader.cpp b/code/AssetLib/Irr/IRRLoader.cpp index 9c1136499..ed92c93bb 100644 --- a/code/AssetLib/Irr/IRRLoader.cpp +++ b/code/AssetLib/Irr/IRRLoader.cpp @@ -859,7 +859,7 @@ void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy // Check whether we can read from the file if (file.get() == nullptr) { - throw DeadlyImportError("Failed to open IRR file " + pFile + ""); + throw DeadlyImportError("Failed to open IRR file " + pFile); } // Construct the irrXML parser diff --git a/code/AssetLib/Irr/IRRMeshLoader.cpp b/code/AssetLib/Irr/IRRMeshLoader.cpp index 97d74026f..edcff6c83 100644 --- a/code/AssetLib/Irr/IRRMeshLoader.cpp +++ b/code/AssetLib/Irr/IRRMeshLoader.cpp @@ -135,7 +135,7 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile, // Check whether we can read from the file if (file.get() == NULL) - throw DeadlyImportError("Failed to open IRRMESH file " + pFile + ""); + throw DeadlyImportError("Failed to open IRRMESH file " + pFile); // Construct the irrXML parser XmlParser parser; diff --git a/code/AssetLib/LWO/LWOFileData.h b/code/AssetLib/LWO/LWOFileData.h index 70642c537..93ac1a236 100644 --- a/code/AssetLib/LWO/LWOFileData.h +++ b/code/AssetLib/LWO/LWOFileData.h @@ -502,7 +502,7 @@ struct Surface { Surface() : mColor(0.78431f, 0.78431f, 0.78431f), bDoubleSided(false), mDiffuseValue(1.f), mSpecularValue(0.f), mTransparency(0.f), mGlossiness(0.4f), mLuminosity(0.f), mColorHighlights(0.f), mMaximumSmoothAngle(0.f) // 0 == not specified, no smoothing , - mVCMap(""), + mVCMap(), mVCMapType(AI_LWO_RGBA), mIOR(1.f) // vakuum , diff --git a/code/AssetLib/LWS/LWSLoader.h b/code/AssetLib/LWS/LWSLoader.h index 93251cd5d..cd7a0543e 100644 --- a/code/AssetLib/LWS/LWSLoader.h +++ b/code/AssetLib/LWS/LWSLoader.h @@ -88,7 +88,7 @@ struct NodeDesc { id(), number(0), parent(0), - name(""), + name(), isPivotSet(false), lightColor(1.f, 1.f, 1.f), lightIntensity(1.f), diff --git a/code/AssetLib/MDL/HalfLife/UniqueNameGenerator.cpp b/code/AssetLib/MDL/HalfLife/UniqueNameGenerator.cpp index 72081ea73..dde81454b 100644 --- a/code/AssetLib/MDL/HalfLife/UniqueNameGenerator.cpp +++ b/code/AssetLib/MDL/HalfLife/UniqueNameGenerator.cpp @@ -95,7 +95,7 @@ void UniqueNameGenerator::make_unique(std::vector &names) { auto generate_unique_name = [&](const std::string &base_name) -> std::string { auto *duplicate_info = &names_to_duplicates[base_name]; - std::string new_name = ""; + std::string new_name; bool found_identical_name; bool tried_with_base_name_only = false; diff --git a/code/AssetLib/MMD/MMDImporter.cpp b/code/AssetLib/MMD/MMDImporter.cpp index e4ecc1445..2895c3879 100644 --- a/code/AssetLib/MMD/MMDImporter.cpp +++ b/code/AssetLib/MMD/MMDImporter.cpp @@ -75,7 +75,7 @@ using namespace std; // Default constructor MMDImporter::MMDImporter() : m_Buffer(), - m_strAbsPath("") { + m_strAbsPath() { DefaultIOSystem io; m_strAbsPath = io.getOsSeparator(); } diff --git a/code/AssetLib/MMD/MMDPmxParser.cpp b/code/AssetLib/MMD/MMDPmxParser.cpp index 320182c3d..cb4787efd 100644 --- a/code/AssetLib/MMD/MMDPmxParser.cpp +++ b/code/AssetLib/MMD/MMDPmxParser.cpp @@ -91,7 +91,7 @@ namespace pmx std::vector buffer; if (size == 0) { - return std::string(""); + return std::string(); } buffer.reserve(size); stream->read((char*) buffer.data(), size); diff --git a/code/AssetLib/OpenGEX/OpenGEXImporter.cpp b/code/AssetLib/OpenGEX/OpenGEXImporter.cpp index 14c9ea37f..b29aeeeb1 100644 --- a/code/AssetLib/OpenGEX/OpenGEXImporter.cpp +++ b/code/AssetLib/OpenGEX/OpenGEXImporter.cpp @@ -206,7 +206,7 @@ USE_ODDLPARSER_NS //------------------------------------------------------------------------------------------------ static void propId2StdString(Property *prop, std::string &name, std::string &key) { - name = key = ""; + name = key = std::string(); if (nullptr == prop) { return; } diff --git a/code/AssetLib/OpenGEX/OpenGEXImporter.h b/code/AssetLib/OpenGEX/OpenGEXImporter.h index 677ef73aa..3c26a4a30 100644 --- a/code/AssetLib/OpenGEX/OpenGEXImporter.h +++ b/code/AssetLib/OpenGEX/OpenGEXImporter.h @@ -79,7 +79,7 @@ struct MetricInfo { int m_intValue; MetricInfo() - : m_stringValue( "" ) + : m_stringValue( ) , m_floatValue( 0.0f ) , m_intValue( -1 ) { // empty diff --git a/code/AssetLib/Q3BSP/Q3BSPFileData.h b/code/AssetLib/Q3BSP/Q3BSPFileData.h index ba826d541..a121cdbcd 100644 --- a/code/AssetLib/Q3BSP/Q3BSPFileData.h +++ b/code/AssetLib/Q3BSP/Q3BSPFileData.h @@ -178,7 +178,7 @@ struct Q3BSPModel { m_Textures(), m_Lightmaps(), m_EntityData(), - m_ModelName( "" ) + m_ModelName() { // empty } diff --git a/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp b/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp index c66582fea..85e79a12c 100644 --- a/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp +++ b/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp @@ -113,7 +113,7 @@ static void extractIds(const std::string &key, int &id1, int &id2) { // ------------------------------------------------------------------------------------------------ // Local helper function to normalize filenames. static void normalizePathName(const std::string &rPath, std::string &normalizedPath) { - normalizedPath = ""; + normalizedPath = std::string(); if (rPath.empty()) { return; } @@ -183,7 +183,7 @@ void Q3BSPFileImporter::InternReadFile(const std::string &rFile, aiScene *scene, throw DeadlyImportError("Failed to open file ", rFile, "."); } - std::string archiveName(""), mapName(""); + std::string archiveName, mapName; separateMapName(rFile, archiveName, mapName); if (mapName.empty()) { @@ -202,8 +202,8 @@ void Q3BSPFileImporter::InternReadFile(const std::string &rFile, aiScene *scene, // ------------------------------------------------------------------------------------------------ // Separates the map name from the import name. void Q3BSPFileImporter::separateMapName(const std::string &importName, std::string &archiveName, std::string &mapName) { - archiveName = ""; - mapName = ""; + archiveName = std::string(); + mapName = std::string(); if (importName.empty()) { return; } @@ -221,7 +221,7 @@ void Q3BSPFileImporter::separateMapName(const std::string &importName, std::stri // ------------------------------------------------------------------------------------------------ // Returns the first map in the map archive. bool Q3BSPFileImporter::findFirstMapInArchive(ZipArchiveIOSystem &bspArchive, std::string &mapName) { - mapName = ""; + mapName = std::string(); std::vector fileList; bspArchive.getFileListExtension(fileList, "bsp"); if (fileList.empty()) { @@ -440,7 +440,7 @@ void Q3BSPFileImporter::createMaterials(const Q3BSP::Q3BSPModel *pModel, aiScene if (-1 != textureId) { sQ3BSPTexture *pTexture = pModel->m_Textures[textureId]; if (nullptr != pTexture) { - std::string tmp("*"), texName(""); + std::string tmp("*"), texName; tmp += pTexture->strName; tmp += ".jpg"; normalizePathName(tmp, texName); @@ -512,7 +512,7 @@ size_t Q3BSPFileImporter::countTriangles(const std::vector // ------------------------------------------------------------------------------------------------ // Creates the faces-to-material map. void Q3BSPFileImporter::createMaterialMap(const Q3BSP::Q3BSPModel *pModel) { - std::string key(""); + std::string key; std::vector *pCurFaceArray = nullptr; for (size_t idx = 0; idx < pModel->m_Faces.size(); idx++) { Q3BSP::sQ3BSPFace *pQ3BSPFace = pModel->m_Faces[idx]; @@ -660,7 +660,7 @@ bool Q3BSPFileImporter::expandFile(ZipArchiveIOSystem *pArchive, const std::stri if (rExtList.empty()) { rFile = rFilename; - rExt = ""; + rExt = std::string(); return true; } diff --git a/code/AssetLib/X/XFileHelper.h b/code/AssetLib/X/XFileHelper.h index 8ba166350..5bdaf4d35 100644 --- a/code/AssetLib/X/XFileHelper.h +++ b/code/AssetLib/X/XFileHelper.h @@ -130,7 +130,7 @@ struct Mesh { std::vector mBones; - explicit Mesh(const std::string &pName = "") AI_NO_EXCEPT + explicit Mesh(const std::string &pName = std::string()) AI_NO_EXCEPT : mName( pName ) , mPositions() , mPosFaces() diff --git a/code/AssetLib/XGL/XGLLoader.cpp b/code/AssetLib/XGL/XGLLoader.cpp index 824f0c2c3..00e8bafb2 100644 --- a/code/AssetLib/XGL/XGLLoader.cpp +++ b/code/AssetLib/XGL/XGLLoader.cpp @@ -142,7 +142,7 @@ void XGLImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy // check whether we can read from the file if (stream.get() == NULL) { - throw DeadlyImportError("Failed to open XGL/ZGL file " + pFile + ""); + throw DeadlyImportError("Failed to open XGL/ZGL file " + pFile); } // see if its compressed, if so uncompress it diff --git a/code/AssetLib/glTF/glTFAsset.h b/code/AssetLib/glTF/glTFAsset.h index 1da70832b..8e4f1ff8f 100644 --- a/code/AssetLib/glTF/glTFAsset.h +++ b/code/AssetLib/glTF/glTFAsset.h @@ -1029,7 +1029,7 @@ namespace glTF AssetMetadata() : premultipliedAlpha(false) - , version("") + , version() { } }; diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index 53b079117..af48aa01b 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -1067,7 +1067,7 @@ struct AssetMetadata { void Read(Document &doc); AssetMetadata() : - version("") {} + version() {} }; // diff --git a/code/Common/BaseImporter.cpp b/code/Common/BaseImporter.cpp index 0657216cf..7da615ac4 100644 --- a/code/Common/BaseImporter.cpp +++ b/code/Common/BaseImporter.cpp @@ -271,7 +271,7 @@ std::string BaseImporter::GetExtension(const std::string &file) { // no file extension at all if (pos == std::string::npos) { - return ""; + return std::string(); } // thanks to Andy Maloney for the hint diff --git a/code/Common/FileSystemFilter.h b/code/Common/FileSystemFilter.h index 1440cf97d..92f199870 100644 --- a/code/Common/FileSystemFilter.h +++ b/code/Common/FileSystemFilter.h @@ -76,7 +76,7 @@ public: if (std::string::npos != (ss2 = mBase.find_last_of("\\/"))) { mBase.erase(ss2,mBase.length()-ss2); } else { - mBase = ""; + mBase = std::string(); } // make sure the directory is terminated properly diff --git a/code/Common/Importer.cpp b/code/Common/Importer.cpp index db7fc9e1c..8cea2cd09 100644 --- a/code/Common/Importer.cpp +++ b/code/Common/Importer.cpp @@ -149,7 +149,7 @@ void AllocateFromAssimpHeap::operator delete[] ( void* data) { Importer::Importer() : pimpl( new ImporterPimpl ) { pimpl->mScene = nullptr; - pimpl->mErrorString = ""; + pimpl->mErrorString = std::string(); // Allocate a default IO handler pimpl->mIOHandler = new DefaultIOSystem; @@ -387,7 +387,7 @@ void Importer::FreeScene( ) { delete pimpl->mScene; pimpl->mScene = nullptr; - pimpl->mErrorString = ""; + pimpl->mErrorString = std::string(); pimpl->mException = std::exception_ptr(); ASSIMP_END_EXCEPTION_REGION(void); } @@ -434,7 +434,7 @@ aiScene* Importer::GetOrphanedScene() { ASSIMP_BEGIN_EXCEPTION_REGION(); pimpl->mScene = nullptr; - pimpl->mErrorString = ""; // reset error string + pimpl->mErrorString = std::string(); pimpl->mException = std::exception_ptr(); ASSIMP_END_EXCEPTION_REGION(aiScene*); diff --git a/code/Common/scene.cpp b/code/Common/scene.cpp index 82430f5fc..67feb7afb 100644 --- a/code/Common/scene.cpp +++ b/code/Common/scene.cpp @@ -43,7 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include aiNode::aiNode() -: mName("") +: mName() , mParent(nullptr) , mNumChildren(0) , mChildren(nullptr) diff --git a/code/PostProcessing/RemoveRedundantMaterials.h b/code/PostProcessing/RemoveRedundantMaterials.h index 6b4f8b1fb..4210a7fe2 100644 --- a/code/PostProcessing/RemoveRedundantMaterials.h +++ b/code/PostProcessing/RemoveRedundantMaterials.h @@ -81,7 +81,7 @@ public: /** @brief Set list of fixed (inmutable) materials * @param fixed See #AI_CONFIG_PP_RRM_EXCLUDE_LIST */ - void SetFixedMaterialsString(const std::string& fixed = "") { + void SetFixedMaterialsString(const std::string& fixed = std::string()) { mConfigFixedMaterials = fixed; } diff --git a/include/assimp/DefaultIOStream.h b/include/assimp/DefaultIOStream.h index b1bb66902..6d0e13149 100644 --- a/include/assimp/DefaultIOStream.h +++ b/include/assimp/DefaultIOStream.h @@ -119,7 +119,7 @@ private: AI_FORCE_INLINE DefaultIOStream::DefaultIOStream() AI_NO_EXCEPT : mFile(nullptr) -, mFilename("") +, mFilename() , mCachedSize(SIZE_MAX) { // empty } diff --git a/include/assimp/IOSystem.hpp b/include/assimp/IOSystem.hpp index b05eebce6..76f876440 100644 --- a/include/assimp/IOSystem.hpp +++ b/include/assimp/IOSystem.hpp @@ -294,7 +294,7 @@ bool IOSystem::PushDirectory( const std::string &path ) { AI_FORCE_INLINE const std::string &IOSystem::CurrentDirectory() const { if ( m_pathStack.empty() ) { - static const std::string Dummy(""); + static const std::string Dummy; return Dummy; } return m_pathStack[ m_pathStack.size()-1 ]; diff --git a/include/assimp/Importer.hpp b/include/assimp/Importer.hpp index 18734c302..09b5b6883 100644 --- a/include/assimp/Importer.hpp +++ b/include/assimp/Importer.hpp @@ -286,7 +286,7 @@ public: * @see GetPropertyInteger() */ std::string GetPropertyString(const char *szName, - const std::string &sErrorReturn = "") const; + const std::string &sErrorReturn = std::string()) const; // ------------------------------------------------------------------- /** Get a matrix configuration property diff --git a/include/assimp/XmlParser.h b/include/assimp/XmlParser.h index 15c4ff9ff..f525d3549 100644 --- a/include/assimp/XmlParser.h +++ b/include/assimp/XmlParser.h @@ -239,7 +239,7 @@ public: } static inline bool getValueAsString( XmlNode &node, std::string &text ) { - text = ""; + text = std::string(); if (node.empty()) { return false; } From a19299d501e1130bc69fc2bf1d7a3cb466b1998d Mon Sep 17 00:00:00 2001 From: Krishty Date: Sat, 17 Apr 2021 00:32:04 +0200 Subject: [PATCH 003/335] moved MD2/MDC tables from BSS to const data Visual C++ is unable to identify them as constant data during optimization, so explicitly declare them const. --- code/AssetLib/MD2/MD2NormalTable.h | 2 +- code/AssetLib/MDC/MDCNormalTable.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/code/AssetLib/MD2/MD2NormalTable.h b/code/AssetLib/MD2/MD2NormalTable.h index 3eff0d666..b2330d862 100644 --- a/code/AssetLib/MD2/MD2NormalTable.h +++ b/code/AssetLib/MD2/MD2NormalTable.h @@ -51,7 +51,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define AI_MDL_NORMALTABLE_H_INC -float g_avNormals[162][3] = { +const float g_avNormals[162][3] = { { -0.525731f, 0.000000f, 0.850651f }, { -0.442863f, 0.238856f, 0.864188f }, { -0.295242f, 0.000000f, 0.955423f }, diff --git a/code/AssetLib/MDC/MDCNormalTable.h b/code/AssetLib/MDC/MDCNormalTable.h index f47e97f33..c96eba7bd 100644 --- a/code/AssetLib/MDC/MDCNormalTable.h +++ b/code/AssetLib/MDC/MDCNormalTable.h @@ -36,7 +36,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define MDC_NORMAL_TABLE_INCLUDED /* mdc decoding normal table */ -float mdcNormals[ 256 ][ 3 ] = +const float mdcNormals[ 256 ][ 3 ] = { { 1.000000f, 0.000000f, 0.000000f }, { 0.980785f, 0.195090f, 0.000000f }, From 06437862888e914c6003f1dd87be1b6c629d4910 Mon Sep 17 00:00:00 2001 From: Tobias Rittig Date: Mon, 19 Apr 2021 15:27:59 +0200 Subject: [PATCH 004/335] Add PBRT to exportable file formats list As of #3580 assimp can export to the newest version of the PBRT renderer. This was not listed here. --- doc/Fileformats.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/Fileformats.md b/doc/Fileformats.md index c9116e2e7..89b68e17f 100644 --- a/doc/Fileformats.md +++ b/doc/Fileformats.md @@ -81,6 +81,7 @@ __Exporters__: - JSON (for WebGl, via https://github.com/acgessler/assimp2json) - ASSBIN - STEP +- [PBRTv4](https://github.com/mmp/pbrt-v4) - glTF 1.0 (partial) - glTF 2.0 (partial) - 3MF ( experimental ) From 746d5cf964120967a72322ad98723390707f15f4 Mon Sep 17 00:00:00 2001 From: "Max Vollmer (Microsoft Havok)" <60260460+ms-maxvollmer@users.noreply.github.com> Date: Wed, 21 Apr 2021 16:17:03 +0100 Subject: [PATCH 005/335] * Throw instead of assert on invalid file input * Check JSON object type before accessing members * Ensure samplers input and output references are set before accessing them --- code/AssetLib/glTF2/glTF2Asset.inl | 27 ++++++++++++++++++++-- code/AssetLib/glTF2/glTF2Importer.cpp | 6 ++--- code/PostProcessing/SortByPTypeProcess.cpp | 4 +++- 3 files changed, 31 insertions(+), 6 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 77537028f..8a793c144 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -162,6 +162,9 @@ inline static bool ReadValue(Value &val, T &out) { template inline static bool ReadMember(Value &obj, const char *id, T &out) { + if (!obj.IsObject()) { + return false; + } Value::MemberIterator it = obj.FindMember(id); if (it != obj.MemberEnd()) { return ReadHelper::Read(it->value, out); @@ -176,6 +179,9 @@ inline static T MemberOrDefault(Value &obj, const char *id, T defaultValue) { } inline Value *FindMember(Value &val, const char *id) { + if (!val.IsObject()) { + return nullptr; + } Value::MemberIterator it = val.FindMember(id); return (it != val.MemberEnd()) ? &it->value : nullptr; } @@ -193,6 +199,9 @@ inline void throwUnexpectedTypeError(const char (&expectedTypeName)[N], const ch // Look-up functions with type checks. Context and extra context help the user identify the problem if there's an error. inline Value *FindStringInContext(Value &val, const char *memberId, const char* context, const char* extraContext = nullptr) { + if (!val.IsObject()) { + return nullptr; + } Value::MemberIterator it = val.FindMember(memberId); if (it == val.MemberEnd()) { return nullptr; @@ -204,6 +213,9 @@ inline Value *FindStringInContext(Value &val, const char *memberId, const char* } inline Value *FindNumberInContext(Value &val, const char *memberId, const char* context, const char* extraContext = nullptr) { + if (!val.IsObject()) { + return nullptr; + } Value::MemberIterator it = val.FindMember(memberId); if (it == val.MemberEnd()) { return nullptr; @@ -215,6 +227,9 @@ inline Value *FindNumberInContext(Value &val, const char *memberId, const char* } inline Value *FindUIntInContext(Value &val, const char *memberId, const char* context, const char* extraContext = nullptr) { + if (!val.IsObject()) { + return nullptr; + } Value::MemberIterator it = val.FindMember(memberId); if (it == val.MemberEnd()) { return nullptr; @@ -226,6 +241,9 @@ inline Value *FindUIntInContext(Value &val, const char *memberId, const char* co } inline Value *FindArrayInContext(Value &val, const char *memberId, const char* context, const char* extraContext = nullptr) { + if (!val.IsObject()) { + return nullptr; + } Value::MemberIterator it = val.FindMember(memberId); if (it == val.MemberEnd()) { return nullptr; @@ -237,6 +255,9 @@ inline Value *FindArrayInContext(Value &val, const char *memberId, const char* c } inline Value *FindObjectInContext(Value &val, const char *memberId, const char* context, const char* extraContext = nullptr) { + if (!val.IsObject()) { + return nullptr; + } Value::MemberIterator it = val.FindMember(memberId); if (it == val.MemberEnd()) { return nullptr; @@ -886,7 +907,7 @@ inline void Accessor::Read(Value &obj, Asset &r) { componentType = MemberOrDefault(obj, "componentType", ComponentType_BYTE); { const Value* countValue = FindUInt(obj, "count"); - if (!countValue || countValue->GetInt() < 1) + if (!countValue || countValue->GetUint() < 1) { throw DeadlyImportError("A strictly positive count value is required, when reading ", id.c_str(), name.empty() ? "" : " (" + name + ")"); } @@ -1105,7 +1126,9 @@ inline Accessor::Indexer::Indexer(Accessor &acc) : template T Accessor::Indexer::GetValue(int i) { ai_assert(data); - ai_assert(i * stride < accessor.GetMaxByteSize()); + if (i * stride >= accessor.GetMaxByteSize()) { + throw DeadlyImportError("GLTF: Invalid index ", i, ", count out of range for buffer with stride ", stride, " and size ", accessor.GetMaxByteSize(), "."); + } // Ensure that the memcpy doesn't overwrite the local. const size_t sizeToCopy = std::min(elemSize, sizeof(T)); T value = T(); diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index dca6fcb2d..b6b6a364c 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1170,7 +1170,7 @@ aiNodeAnim *CreateNodeAnim(glTF2::Asset&, Node &node, AnimationSamplers &sampler static const float kMillisecondsFromSeconds = 1000.f; - if (samplers.translation) { + if (samplers.translation && samplers.translation->input && samplers.translation->output) { float *times = nullptr; samplers.translation->input->ExtractData(times); aiVector3D *values = nullptr; @@ -1194,7 +1194,7 @@ aiNodeAnim *CreateNodeAnim(glTF2::Asset&, Node &node, AnimationSamplers &sampler anim->mPositionKeys->mValue.z = node.translation.value[2]; } - if (samplers.rotation) { + if (samplers.rotation && samplers.rotation->input && samplers.rotation->output) { float *times = nullptr; samplers.rotation->input->ExtractData(times); aiQuaternion *values = nullptr; @@ -1222,7 +1222,7 @@ aiNodeAnim *CreateNodeAnim(glTF2::Asset&, Node &node, AnimationSamplers &sampler anim->mRotationKeys->mValue.w = node.rotation.value[3]; } - if (samplers.scale) { + if (samplers.scale && samplers.scale->input && samplers.scale->output) { float *times = nullptr; samplers.scale->input->ExtractData(times); aiVector3D *values = nullptr; diff --git a/code/PostProcessing/SortByPTypeProcess.cpp b/code/PostProcessing/SortByPTypeProcess.cpp index dd6902692..38549b9a0 100644 --- a/code/PostProcessing/SortByPTypeProcess.cpp +++ b/code/PostProcessing/SortByPTypeProcess.cpp @@ -135,7 +135,9 @@ void SortByPTypeProcess::Execute(aiScene *pScene) { std::vector::iterator meshIdx = replaceMeshIndex.begin(); for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { aiMesh *const mesh = pScene->mMeshes[i]; - ai_assert(0 != mesh->mPrimitiveTypes); + if (mesh->mPrimitiveTypes == 0) { + throw DeadlyImportError("GLTF: Mesh with invalid primitive type: ", mesh->mName.C_Str()); + } // if there's just one primitive type in the mesh there's nothing to do for us unsigned int num = 0; From 44dc08f128c97667104a5dc6a85bf1ea645ce1b5 Mon Sep 17 00:00:00 2001 From: "Max Vollmer (Microsoft Havok)" <60260460+ms-maxvollmer@users.noreply.github.com> Date: Wed, 21 Apr 2021 16:20:58 +0100 Subject: [PATCH 006/335] Remove GLTF tag, postprocessing is format independent --- code/PostProcessing/SortByPTypeProcess.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/PostProcessing/SortByPTypeProcess.cpp b/code/PostProcessing/SortByPTypeProcess.cpp index 38549b9a0..20ab63249 100644 --- a/code/PostProcessing/SortByPTypeProcess.cpp +++ b/code/PostProcessing/SortByPTypeProcess.cpp @@ -136,7 +136,7 @@ void SortByPTypeProcess::Execute(aiScene *pScene) { for (unsigned int i = 0; i < pScene->mNumMeshes; ++i) { aiMesh *const mesh = pScene->mMeshes[i]; if (mesh->mPrimitiveTypes == 0) { - throw DeadlyImportError("GLTF: Mesh with invalid primitive type: ", mesh->mName.C_Str()); + throw DeadlyImportError("Mesh with invalid primitive type: ", mesh->mName.C_Str()); } // if there's just one primitive type in the mesh there's nothing to do for us From aae3788247ca35395e5044f5a23709aef9fa0365 Mon Sep 17 00:00:00 2001 From: Scott Baldric Date: Thu, 22 Apr 2021 08:49:47 -0500 Subject: [PATCH 007/335] Fix: Removing double delete of texture items. Textures were being double deleted after a merge scene because the texture array wasn't being properly deleted at the end of merging. Furthermore, the texture array was being sized to the number of materials instead of the number of textures. --- code/Common/SceneCombiner.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/code/Common/SceneCombiner.cpp b/code/Common/SceneCombiner.cpp index e39660cd6..fe00dfe1f 100644 --- a/code/Common/SceneCombiner.cpp +++ b/code/Common/SceneCombiner.cpp @@ -45,7 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // possible as new fields are added to assimp structures. // ---------------------------------------------------------------------------- -/** +/** * @file Implements Assimp::SceneCombiner. This is a smart utility * class that combines multiple scenes, meshes, ... into one. Currently * these utilities are used by the IRR and LWS loaders and the @@ -359,7 +359,7 @@ void SceneCombiner::MergeScenes(aiScene **_dest, aiScene *master, std::vectormNumTextures) { - aiTexture **pip = dest->mTextures = new aiTexture *[dest->mNumMaterials]; + aiTexture **pip = dest->mTextures = new aiTexture *[dest->mNumTextures]; cnt = 0; for (unsigned int n = 0; n < src.size(); ++n) { SceneHelper *cur = &src[n]; @@ -638,6 +638,8 @@ void SceneCombiner::MergeScenes(aiScene **_dest, aiScene *master, std::vectormMaterials = nullptr; delete[] deleteMe->mAnimations; deleteMe->mAnimations = nullptr; + delete[] deleteMe->mTextures; + deleteMe->mTextures = nullptr; deleteMe->mRootNode = nullptr; From 196deea7ce96d937e30a055b772f8ad1dded29c1 Mon Sep 17 00:00:00 2001 From: Krishty Date: Fri, 23 Apr 2021 15:05:09 +0200 Subject: [PATCH 008/335] added missing file extensions to aiImporterDesc::mFileExtensions --- code/AssetLib/Collada/ColladaLoader.cpp | 2 +- code/AssetLib/M3D/M3DImporter.cpp | 5 ++--- code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/code/AssetLib/Collada/ColladaLoader.cpp b/code/AssetLib/Collada/ColladaLoader.cpp index 902eebc4b..a3a54886e 100644 --- a/code/AssetLib/Collada/ColladaLoader.cpp +++ b/code/AssetLib/Collada/ColladaLoader.cpp @@ -75,7 +75,7 @@ static const aiImporterDesc desc = { 3, 1, 5, - "dae zae" + "dae xml zae" }; static const float kMillisecondsFromSeconds = 1000.f; diff --git a/code/AssetLib/M3D/M3DImporter.cpp b/code/AssetLib/M3D/M3DImporter.cpp index 8cbda23cb..b7ba74c8a 100644 --- a/code/AssetLib/M3D/M3DImporter.cpp +++ b/code/AssetLib/M3D/M3DImporter.cpp @@ -95,10 +95,9 @@ static const aiImporterDesc desc = { 0, 0, 0, -#ifdef M3D_ASCII - "m3d a3d" -#else "m3d" +#ifdef M3D_ASCII + " a3d" #endif }; diff --git a/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp b/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp index c66582fea..c8eaf85ce 100644 --- a/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp +++ b/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp @@ -75,7 +75,7 @@ static const aiImporterDesc desc = { 0, 0, 0, - "pk3" + "bsp pk3" }; namespace Assimp { From 9dc66b0003cc0f045860a1a3122ae8e69f89a279 Mon Sep 17 00:00:00 2001 From: Krishty Date: Fri, 23 Apr 2021 15:15:21 +0200 Subject: [PATCH 009/335] removed dead code BaseImporter::GetExtensionList() is not a virtual function; overriding it is useless. This probably stemmed from a misunderstanding. --- code/AssetLib/AMF/AMFImporter.cpp | 4 ---- code/AssetLib/AMF/AMFImporter.hpp | 1 - code/AssetLib/Blender/BlenderLoader.cpp | 6 ------ code/AssetLib/Blender/BlenderLoader.h | 1 - code/AssetLib/X3D/X3DImporter.cpp | 4 ---- code/AssetLib/X3D/X3DImporter.hpp | 1 - 6 files changed, 17 deletions(-) diff --git a/code/AssetLib/AMF/AMFImporter.cpp b/code/AssetLib/AMF/AMFImporter.cpp index 37756aea0..1a3efba9a 100644 --- a/code/AssetLib/AMF/AMFImporter.cpp +++ b/code/AssetLib/AMF/AMFImporter.cpp @@ -517,10 +517,6 @@ bool AMFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool p return false; } -void AMFImporter::GetExtensionList(std::set &pExtensionList) { - pExtensionList.insert("amf"); -} - const aiImporterDesc *AMFImporter::GetInfo() const { return &Description; } diff --git a/code/AssetLib/AMF/AMFImporter.hpp b/code/AssetLib/AMF/AMFImporter.hpp index ef61be463..18ef83c24 100644 --- a/code/AssetLib/AMF/AMFImporter.hpp +++ b/code/AssetLib/AMF/AMFImporter.hpp @@ -277,7 +277,6 @@ public: void ParseHelper_Node_Enter(AMFNodeElementBase *child); void ParseHelper_Node_Exit(); bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool pCheckSig) const; - void GetExtensionList(std::set &pExtensionList); void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler); const aiImporterDesc *GetInfo() const; bool Find_NodeElement(const std::string &pID, const AMFNodeElementBase::EType pType, AMFNodeElementBase **pNodeElement) const; diff --git a/code/AssetLib/Blender/BlenderLoader.cpp b/code/AssetLib/Blender/BlenderLoader.cpp index 989e9d59a..1c9482631 100644 --- a/code/AssetLib/Blender/BlenderLoader.cpp +++ b/code/AssetLib/Blender/BlenderLoader.cpp @@ -132,12 +132,6 @@ bool BlenderImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bo return false; } -// ------------------------------------------------------------------------------------------------ -// List all extensions handled by this loader -void BlenderImporter::GetExtensionList(std::set &app) { - app.insert("blend"); -} - // ------------------------------------------------------------------------------------------------ // Loader registry entry const aiImporterDesc *BlenderImporter::GetInfo() const { diff --git a/code/AssetLib/Blender/BlenderLoader.h b/code/AssetLib/Blender/BlenderLoader.h index fdbb586c9..42da76514 100644 --- a/code/AssetLib/Blender/BlenderLoader.h +++ b/code/AssetLib/Blender/BlenderLoader.h @@ -110,7 +110,6 @@ public: protected: const aiImporterDesc* GetInfo () const; - void GetExtensionList(std::set& app); void SetupProperties(const Importer* pImp); void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); void ParseBlendFile(Blender::FileDatabase& out, std::shared_ptr stream); diff --git a/code/AssetLib/X3D/X3DImporter.cpp b/code/AssetLib/X3D/X3DImporter.cpp index 287fdeceb..121b7490e 100644 --- a/code/AssetLib/X3D/X3DImporter.cpp +++ b/code/AssetLib/X3D/X3DImporter.cpp @@ -167,10 +167,6 @@ bool X3DImporter::CanRead( const std::string &pFile, IOSystem * /*pIOHandler*/, return false; } -void X3DImporter::GetExtensionList( std::set &extensionList ) { - extensionList.insert("x3d"); -} - void X3DImporter::InternReadFile( const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler ) { std::shared_ptr stream(pIOHandler->Open(pFile, "rb")); if (!stream) { diff --git a/code/AssetLib/X3D/X3DImporter.hpp b/code/AssetLib/X3D/X3DImporter.hpp index 3d263a2cc..c96bb17d8 100644 --- a/code/AssetLib/X3D/X3DImporter.hpp +++ b/code/AssetLib/X3D/X3DImporter.hpp @@ -307,7 +307,6 @@ public: /// \param [in] pIOHandler - pointer to IO helper object. void ParseFile(const std::string &pFile, IOSystem *pIOHandler); bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool pCheckSig) const; - void GetExtensionList(std::set &pExtensionList); void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler); const aiImporterDesc *GetInfo() const; void Clear(); From 0b14eb7523e05a675d383d95fbfbe2abae5b0201 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 23 Apr 2021 15:30:12 +0200 Subject: [PATCH 010/335] Fix formatter. --- tools/assimp_cmd/ImageExtractor.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tools/assimp_cmd/ImageExtractor.cpp b/tools/assimp_cmd/ImageExtractor.cpp index 58aa42fa8..105c4fe37 100644 --- a/tools/assimp_cmd/ImageExtractor.cpp +++ b/tools/assimp_cmd/ImageExtractor.cpp @@ -130,8 +130,9 @@ int SaveAsBMP(FILE *file, const aiTexel *data, unsigned int width, unsigned int s[0] = t->b; s[1] = t->g; s[2] = t->r; - if (4 == numc) + if (4 == numc) { s[3] = t->a; + } } } @@ -296,7 +297,7 @@ int Assimp_Extract(const char *const *params, unsigned int num) { // check whether the requested texture is existing if (texIdx >= scene->mNumTextures) { - ::printf("assimp extract: Texture %i requested, but there are just %i textures\n", + ::printf("assimp extract: Texture %u requested, but there are just %i textures\n", texIdx, scene->mNumTextures); return AssimpCmdExtractError::TextureIndexIsOutOfRange; } @@ -325,7 +326,7 @@ int Assimp_Extract(const char *const *params, unsigned int num) { // if the texture is a compressed one, we'll export // it to its native file format if (!tex->mHeight) { - printf("assimp extract: Texture %i is compressed (%s). Writing native file format.\n", + printf("assimp extract: Texture %u is compressed (%s). Writing native file format.\n", i, tex->achFormatHint); // modify file extension @@ -350,7 +351,7 @@ int Assimp_Extract(const char *const *params, unsigned int num) { } ::fclose(p); - printf("assimp extract: Wrote texture %i to %s\n", i, out_cpy.c_str()); + printf("assimp extract: Wrote texture %u to %s\n", i, out_cpy.c_str()); if (texIdx != 0xffffffff) { return m; } From afe947d5db64c21e42e7a6d91b952bf0bea74144 Mon Sep 17 00:00:00 2001 From: Krishty Date: Sat, 24 Apr 2021 12:38:31 +0200 Subject: [PATCH 011/335] fixed malformatted message --- code/Common/BaseImporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/Common/BaseImporter.cpp b/code/Common/BaseImporter.cpp index 0657216cf..20043e931 100644 --- a/code/Common/BaseImporter.cpp +++ b/code/Common/BaseImporter.cpp @@ -97,7 +97,7 @@ void BaseImporter::UpdateImporterScale(Importer *pImp) { // Set active scaling pImp->SetPropertyFloat(AI_CONFIG_APP_SCALE_KEY, static_cast(activeScale)); - ASSIMP_LOG_DEBUG_F("UpdateImporterScale scale set: %f", activeScale); + ASSIMP_LOG_DEBUG_F("UpdateImporterScale scale set: ", activeScale); } // ------------------------------------------------------------------------------------------------ From e6a47d93c2f630688d49eec82bb7226e236d2399 Mon Sep 17 00:00:00 2001 From: Krishty Date: Sat, 24 Apr 2021 13:29:15 +0200 Subject: [PATCH 012/335] removed dead code from 0d29203e24a8bc2c75278931a6bd25b2ae5848de --- code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp | 1 + code/Common/BaseImporter.cpp | 14 -------- include/assimp/BaseImporter.h | 37 +-------------------- 3 files changed, 2 insertions(+), 50 deletions(-) diff --git a/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp b/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp index 4df0d0d1d..eee9ed346 100644 --- a/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp +++ b/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp @@ -57,6 +57,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include #ifdef MDL_HALFLIFE_LOG_WARN_HEADER #undef MDL_HALFLIFE_LOG_WARN_HEADER diff --git a/code/Common/BaseImporter.cpp b/code/Common/BaseImporter.cpp index 0657216cf..6e65692f0 100644 --- a/code/Common/BaseImporter.cpp +++ b/code/Common/BaseImporter.cpp @@ -65,20 +65,6 @@ using namespace Assimp; // Constructor to be privately used by Importer BaseImporter::BaseImporter() AI_NO_EXCEPT : m_progress() { - /** - * Assimp Importer - * unit conversions available - * if you need another measurment unit add it below. - * it's currently defined in assimp that we prefer meters. - * - * NOTE: Initialised here rather than in the header file - * to workaround a VS2013 bug with brace initialisers - * */ - importerUnits[ImporterUnits::M] = 1.0; - importerUnits[ImporterUnits::CM] = 0.01; - importerUnits[ImporterUnits::MM] = 0.001; - importerUnits[ImporterUnits::INCHES] = 0.0254; - importerUnits[ImporterUnits::FEET] = 0.3048; } // ------------------------------------------------------------------------------------------------ diff --git a/include/assimp/BaseImporter.h b/include/assimp/BaseImporter.h index 86d7ba70f..80c79b9a2 100644 --- a/include/assimp/BaseImporter.h +++ b/include/assimp/BaseImporter.h @@ -51,10 +51,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "Exceptional.h" -#include #include #include -#include #include #include #include @@ -179,42 +177,10 @@ public: /** * Will be called only by scale process when scaling is requested. */ - virtual void SetFileScale(double scale) { + void SetFileScale(double scale) { fileScale = scale; } - virtual double GetFileScale() const { - return fileScale; - } - - enum ImporterUnits { - M, - MM, - CM, - INCHES, - FEET - }; - - /** - * Assimp Importer - * unit conversions available - * NOTE: Valid options are initialised in the - * constructor in the implementation file to - * work around a VS2013 compiler bug if support - * for that compiler is dropped in the future - * initialisation can be moved back here - * */ - std::map importerUnits; - - virtual void SetApplicationUnits(const ImporterUnits &unit) { - importerScale = importerUnits[unit]; - applicationUnits = unit; - } - - virtual const ImporterUnits &GetApplicationUnits() { - return applicationUnits; - } - // ------------------------------------------------------------------- /** Called by #Importer::GetExtensionList for each loaded importer. * Take the extension list contained in the structure returned by @@ -223,7 +189,6 @@ public: void GetExtensionList(std::set &extensions); protected: - ImporterUnits applicationUnits = ImporterUnits::M; double importerScale = 1.0; double fileScale = 1.0; From 5c64a4dc1eb09aea9bcfc0be93136ab753a9eea1 Mon Sep 17 00:00:00 2001 From: Krishty Date: Sat, 24 Apr 2021 18:33:57 +0200 Subject: [PATCH 013/335] devirtualized a function that is called once and never overridden --- include/assimp/BaseImporter.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/assimp/BaseImporter.h b/include/assimp/BaseImporter.h index 80c79b9a2..38bec1afd 100644 --- a/include/assimp/BaseImporter.h +++ b/include/assimp/BaseImporter.h @@ -385,7 +385,7 @@ public: // static utilities private: /* Pushes state into importer for the importer scale */ - virtual void UpdateImporterScale(Importer *pImp); + void UpdateImporterScale(Importer *pImp); protected: /// Error description in case there was one. From ee8d1b041709b59ef79ed1b237acca0836fcfa35 Mon Sep 17 00:00:00 2001 From: Eric Wasylishen Date: Sat, 24 Apr 2021 14:13:51 -0600 Subject: [PATCH 014/335] SimpleTexturedDirectx11: embedded texture loading fixes - remove assumption that embedded texture names start with "*0", etc. - rename ModelLoader::getTextureFromModel() to loadEmbeddedTexture() - support loading uncompressed embedded textures Fixes display of bullsquid.mdl from Half-Life (which has an embdedded texture named "bottommap.bmp") --- .../SimpleTexturedDirectx11/ModelLoader.cpp | 83 +++++++++---------- .../SimpleTexturedDirectx11/ModelLoader.h | 4 +- 2 files changed, 42 insertions(+), 45 deletions(-) diff --git a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/ModelLoader.cpp b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/ModelLoader.cpp index 733d3d620..92760d691 100644 --- a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/ModelLoader.cpp +++ b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/ModelLoader.cpp @@ -42,22 +42,12 @@ void ModelLoader::Draw(ID3D11DeviceContext * devcon) { } } -std::string textype; - Mesh ModelLoader::processMesh(aiMesh * mesh, const aiScene * scene) { // Data to fill std::vector vertices; std::vector indices; std::vector textures; - if (mesh->mMaterialIndex >= 0) { - aiMaterial* mat = scene->mMaterials[mesh->mMaterialIndex]; - - if (textype.empty()) { - textype = determineTextureType(scene, mat); - } - } - // Walk through each of the mesh's vertices for (UINT i = 0; i < mesh->mNumVertices; i++) { VERTEX vertex; @@ -108,9 +98,10 @@ std::vector ModelLoader::loadMaterialTextures(aiMaterial * mat, aiTextu if (!skip) { // If texture hasn't been loaded already, load it HRESULT hr; Texture texture; - if (textype == "embedded compressed texture") { - int textureindex = getTextureIndex(&str); - texture.texture = getTextureFromModel(scene, textureindex); + + const aiTexture* embeddedTexture = scene->GetEmbeddedTexture(str.C_Str()); + if (embeddedTexture != nullptr) { + texture.texture = loadEmbeddedTexture(embeddedTexture); } else { std::string filename = std::string(str.C_Str()); filename = directory_ + '/' + filename; @@ -148,38 +139,46 @@ void ModelLoader::processNode(aiNode * node, const aiScene * scene) { } } -std::string ModelLoader::determineTextureType(const aiScene * scene, aiMaterial * mat) { - aiString textypeStr; - mat->GetTexture(aiTextureType_DIFFUSE, 0, &textypeStr); - std::string textypeteststr = textypeStr.C_Str(); - if (textypeteststr == "*0" || textypeteststr == "*1" || textypeteststr == "*2" || textypeteststr == "*3" || textypeteststr == "*4" || textypeteststr == "*5") { - if (scene->mTextures[0]->mHeight == 0) { - return "embedded compressed texture"; - } else { - return "embedded non-compressed texture"; - } - } - if (textypeteststr.find('.') != std::string::npos) { - return "textures are on disk"; - } - - return "."; -} - -int ModelLoader::getTextureIndex(aiString * str) { - std::string tistr; - tistr = str->C_Str(); - tistr = tistr.substr(1); - return stoi(tistr); -} - -ID3D11ShaderResourceView * ModelLoader::getTextureFromModel(const aiScene * scene, int textureindex) { +ID3D11ShaderResourceView * ModelLoader::loadEmbeddedTexture(const aiTexture* embeddedTexture) { HRESULT hr; - ID3D11ShaderResourceView *texture; + ID3D11ShaderResourceView *texture = nullptr; - int* size = reinterpret_cast(&scene->mTextures[textureindex]->mWidth); + if (embeddedTexture->mHeight != 0) { + // Load an uncompressed ARGB8888 embedded texture + D3D11_TEXTURE2D_DESC desc; + desc.Width = embeddedTexture->mWidth; + desc.Height = embeddedTexture->mHeight; + desc.MipLevels = 1; + desc.ArraySize = 1; + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = 0; + desc.MiscFlags = 0; - hr = CreateWICTextureFromMemory(dev_, devcon_, reinterpret_cast(scene->mTextures[textureindex]->pcData), *size, nullptr, &texture); + D3D11_SUBRESOURCE_DATA subresourceData; + subresourceData.pSysMem = embeddedTexture->pcData; + subresourceData.SysMemPitch = embeddedTexture->mWidth * 4; + subresourceData.SysMemSlicePitch = embeddedTexture->mWidth * embeddedTexture->mHeight * 4; + + ID3D11Texture2D *texture2D = nullptr; + hr = dev_->CreateTexture2D(&desc, &subresourceData, &texture2D); + if (FAILED(hr)) + MessageBox(hwnd_, "CreateTexture2D failed!", "Error!", MB_ICONERROR | MB_OK); + + hr = dev_->CreateShaderResourceView(texture2D, nullptr, &texture); + if (FAILED(hr)) + MessageBox(hwnd_, "CreateShaderResourceView failed!", "Error!", MB_ICONERROR | MB_OK); + + return texture; + } + + // mHeight is 0, so try to load a compressed texture of mWidth bytes + const size_t size = embeddedTexture->mWidth; + + hr = CreateWICTextureFromMemory(dev_, devcon_, reinterpret_cast(embeddedTexture->pcData), size, nullptr, &texture); if (FAILED(hr)) MessageBox(hwnd_, "Texture couldn't be created from memory!", "Error!", MB_ICONERROR | MB_OK); diff --git a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/ModelLoader.h b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/ModelLoader.h index 9d3ed50b3..a04484f08 100644 --- a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/ModelLoader.h +++ b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/ModelLoader.h @@ -35,9 +35,7 @@ private: void processNode(aiNode* node, const aiScene* scene); Mesh processMesh(aiMesh* mesh, const aiScene* scene); std::vector loadMaterialTextures(aiMaterial* mat, aiTextureType type, std::string typeName, const aiScene* scene); - std::string determineTextureType(const aiScene* scene, aiMaterial* mat); - int getTextureIndex(aiString* str); - ID3D11ShaderResourceView* getTextureFromModel(const aiScene* scene, int textureindex); + ID3D11ShaderResourceView* loadEmbeddedTexture(const aiTexture* embeddedTexture); }; #endif // !MODEL_LOADER_H From a5d0e99548056e42b6b74e033b6d4eec04548869 Mon Sep 17 00:00:00 2001 From: Gordon Chapman Date: Mon, 26 Apr 2021 12:19:20 -0700 Subject: [PATCH 015/335] Fixed error in blendShapeChannel Weighting --- code/AssetLib/FBX/FBXExporter.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/AssetLib/FBX/FBXExporter.cpp b/code/AssetLib/FBX/FBXExporter.cpp index 322161411..e519f7e77 100644 --- a/code/AssetLib/FBX/FBXExporter.cpp +++ b/code/AssetLib/FBX/FBXExporter.cpp @@ -1789,13 +1789,13 @@ void FBXExporter::WriteObjects () blendchannel_uid, blendshape_name + FBX::SEPARATOR + "SubDeformer", "BlendShapeChannel" ); sdnode.AddChild("Version", int32_t(100)); - sdnode.AddChild("DeformPercent", int32_t(100)); + sdnode.AddChild("DeformPercent", float_t(0.0)); FBX::Node p("Properties70"); - p.AddP70numberA("DeformPercent", 100.); + p.AddP70numberA("DeformPercent", 0.0); sdnode.AddChild(p); // TODO: Normally just one weight per channel, adding stub for later development std::vectorfFullWeights; - fFullWeights.push_back(0.); + fFullWeights.push_back(100.); sdnode.AddChild("FullWeights", fFullWeights); sdnode.Dump(outstream, binary, indent); From 708d124745a4256e8e8850dd9cbce4853a971e9f Mon Sep 17 00:00:00 2001 From: Jason C Date: Mon, 26 Apr 2021 19:42:22 -0400 Subject: [PATCH 016/335] Update aiProcess_PreTransformVertices docs to match behavior. Addresses #3820, the easy way. --- include/assimp/postprocess.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/include/assimp/postprocess.h b/include/assimp/postprocess.h index 01eabaef9..ca2c2c22d 100644 --- a/include/assimp/postprocess.h +++ b/include/assimp/postprocess.h @@ -209,9 +209,14 @@ enum aiPostProcessSteps /**
Removes the node graph and pre-transforms all vertices with * the local transformation matrices of their nodes. * - * The output scene still contains nodes, however there is only a - * root node with children, each one referencing only one mesh, - * and each mesh referencing one material. For rendering, you can + * If the resulting scene can be reduced to a single mesh, with a single + * material, no lights, and no cameras, then the output scene will contain + * only a root node (with no children) that references the single mesh. + * Otherwise, the output scene will be reduced to a root node with a single + * level of child nodes, each one referencing one mesh, and each mesh + * referencing one material. + * + * In either case, for rendering, you can * simply render all meshes in order - you don't need to pay * attention to local transformations and the node hierarchy. * Animations are removed during this step. From 3acd42c22ed48225919cf03a092262a2e706fbdd Mon Sep 17 00:00:00 2001 From: Jason C Date: Mon, 26 Apr 2021 20:27:28 -0400 Subject: [PATCH 017/335] Remove newline from name of Blender importer. Addresses #3797. Re-submitting this as a quick fix to the immediate issue while I think about the website field. --- code/AssetLib/Blender/BlenderLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/Blender/BlenderLoader.cpp b/code/AssetLib/Blender/BlenderLoader.cpp index 989e9d59a..71ab67721 100644 --- a/code/AssetLib/Blender/BlenderLoader.cpp +++ b/code/AssetLib/Blender/BlenderLoader.cpp @@ -88,7 +88,7 @@ using namespace Assimp::Blender; using namespace Assimp::Formatter; static const aiImporterDesc blenderDesc = { - "Blender 3D Importer \nhttp://www.blender3d.org", + "Blender 3D Importer (http://www.blender3d.org)", "", "", "No animation support yet", From 260cc6bd2674a858ca5193b2ed47ed1c1d5efd01 Mon Sep 17 00:00:00 2001 From: Krishty Date: Wed, 28 Apr 2021 01:02:24 +0200 Subject: [PATCH 018/335] reverted regression in 3DS transformation (issue #3802) The regression was introduced to align 3DS export and import, but in fact it broke the transformation matrices on import. This commit reverts the relevant lines. Furthermore, matrix layout was double-checked with two other 3DS importers. Export was not considered. --- code/AssetLib/3DS/3DSLoader.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/AssetLib/3DS/3DSLoader.cpp b/code/AssetLib/3DS/3DSLoader.cpp index 0a64f6870..3dabf9da1 100644 --- a/code/AssetLib/3DS/3DSLoader.cpp +++ b/code/AssetLib/3DS/3DSLoader.cpp @@ -981,9 +981,9 @@ void Discreet3DSImporter::ParseMeshChunk() { mMesh.mMat.a3 = stream->GetF4(); mMesh.mMat.b3 = stream->GetF4(); mMesh.mMat.c3 = stream->GetF4(); - mMesh.mMat.d1 = stream->GetF4(); - mMesh.mMat.d2 = stream->GetF4(); - mMesh.mMat.d3 = stream->GetF4(); + mMesh.mMat.a4 = stream->GetF4(); + mMesh.mMat.b4 = stream->GetF4(); + mMesh.mMat.c4 = stream->GetF4(); } break; case Discreet3DS::CHUNK_MAPLIST: { From 6abdd0cd3e5f199f0c3bd5f4b08eb3c296edce81 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 28 Apr 2021 16:38:22 +0200 Subject: [PATCH 019/335] Fix crash when reading 0 bytes - This is a valid option so crash shall not happen --- code/Common/DefaultIOStream.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/code/Common/DefaultIOStream.cpp b/code/Common/DefaultIOStream.cpp index 115fea63c..ba9b9d625 100644 --- a/code/Common/DefaultIOStream.cpp +++ b/code/Common/DefaultIOStream.cpp @@ -90,10 +90,12 @@ DefaultIOStream::~DefaultIOStream() { size_t DefaultIOStream::Read(void *pvBuffer, size_t pSize, size_t pCount) { + if (0 == pCount) { + return 0; + } ai_assert(nullptr != pvBuffer); ai_assert(0 != pSize); - ai_assert(0 != pCount); - + return (mFile ? ::fread(pvBuffer, pSize, pCount, mFile) : 0); } From cf498c979a64426f4a0d87a82d8c67b84c90fdb6 Mon Sep 17 00:00:00 2001 From: Jason C Date: Wed, 28 Apr 2021 11:16:49 -0400 Subject: [PATCH 020/335] ASSIMP_ENABLE_DEV_IMPORTERS env var to control registration of wip importers; applied to X3D - GetImporterInstanceList reads ASSIMP_ENABLE_DEV_IMPORTERS env var. Development importers are enabled if the env var is set and is not equal to the literal string "0". - X3D importer will not be registered unless ASSIMP_ENABLE_DEV_IMPORTERS is set; addresses #3647. TODO: If this change is incorporated, it should be documented. NOTE: Effective git branch structure is a better solution. This is an alternate for #3825. --- code/Common/ImporterRegistry.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/code/Common/ImporterRegistry.cpp b/code/Common/ImporterRegistry.cpp index a902fc89d..ddfcf6798 100644 --- a/code/Common/ImporterRegistry.cpp +++ b/code/Common/ImporterRegistry.cpp @@ -48,6 +48,7 @@ corresponding preprocessor flag to selectively disable formats. #include #include +#include // ------------------------------------------------------------------------------------------------ // Importers @@ -204,7 +205,17 @@ corresponding preprocessor flag to selectively disable formats. namespace Assimp { // ------------------------------------------------------------------------------------------------ -void GetImporterInstanceList(std::vector &out) { +void GetImporterInstanceList(std::vector &out) { + + // Some importers may be unimplemented or otherwise unsuitable for general use + // in their current state. Devs can set ASSIMP_ENABLE_DEV_IMPORTERS in their + // local environment to enable them, otherwise they're left out of the registry. + const char *envStr = std::getenv("ASSIMP_ENABLE_DEV_IMPORTERS"); + bool devImportersEnabled = envStr && strcmp(envStr, "0"); + + // Ensure no unused var warnings if all uses are #ifndef'd away below: + (void)devImportersEnabled; + // ---------------------------------------------------------------------------- // Add an instance of each worker class here // (register_new_importers_here) @@ -354,7 +365,9 @@ void GetImporterInstanceList(std::vector &out) { out.push_back(new D3MFImporter()); #endif #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER - out.push_back(new X3DImporter()); + if (devImportersEnabled) { // https://github.com/assimp/assimp/issues/3647 + out.push_back(new X3DImporter()); + } #endif #ifndef ASSIMP_BUILD_NO_MMD_IMPORTER out.push_back(new MMDImporter()); From db142da571d99a3a44a0eed52a819bc7c4a85617 Mon Sep 17 00:00:00 2001 From: Garux Date: Thu, 29 Apr 2021 19:44:06 +0300 Subject: [PATCH 021/335] orient mdc correctly --- code/AssetLib/MDC/MDCLoader.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/code/AssetLib/MDC/MDCLoader.cpp b/code/AssetLib/MDC/MDCLoader.cpp index 17a349768..c53c31b77 100644 --- a/code/AssetLib/MDC/MDCLoader.cpp +++ b/code/AssetLib/MDC/MDCLoader.cpp @@ -465,6 +465,13 @@ void MDCImporter::InternReadFile( pcMat->AddProperty(&path, AI_MATKEY_TEXTURE_DIFFUSE(0)); } } + + // 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); } #endif // !! ASSIMP_BUILD_NO_MDC_IMPORTER From ebf5ef9a4be21f1f8c5a5f311aad342f41878291 Mon Sep 17 00:00:00 2001 From: Garux Date: Thu, 29 Apr 2021 20:02:20 +0300 Subject: [PATCH 022/335] consider pScene->mRootNode->mTransformation set by some importers while using AI_CONFIG_PP_PTV_ROOT_TRANSFORMATION --- code/PostProcessing/PretransformVertices.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/PostProcessing/PretransformVertices.cpp b/code/PostProcessing/PretransformVertices.cpp index d1740f30b..2691ed488 100644 --- a/code/PostProcessing/PretransformVertices.cpp +++ b/code/PostProcessing/PretransformVertices.cpp @@ -429,7 +429,7 @@ void PretransformVertices::Execute(aiScene *pScene) { const unsigned int iOldNodes = CountNodes(pScene->mRootNode); if (configTransform) { - pScene->mRootNode->mTransformation = configTransformation; + pScene->mRootNode->mTransformation = configTransformation * pScene->mRootNode->mTransformation; } // first compute absolute transformation matrices for all nodes From 4798ff3882bac60e89d2015783d2966ffc8e620c Mon Sep 17 00:00:00 2001 From: Garux Date: Thu, 29 Apr 2021 19:51:03 +0300 Subject: [PATCH 023/335] fix hl1 mdl orientation, tex coords, face windings order --- code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp | 6 +++--- code/AssetLib/MDL/MDLLoader.cpp | 18 +++++++++++++++--- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp b/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp index 4df0d0d1d..e9020637b 100644 --- a/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp +++ b/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp @@ -868,7 +868,7 @@ void HL1MDLLoader::read_meshes() { scene_mesh->mNormals[v] = bind_pose_normals[pTrivert->normindex]; scene_mesh->mTextureCoords[0][v] = aiVector3D( pTrivert->s * texcoords_s_scale, - pTrivert->t * texcoords_t_scale, 0); + pTrivert->t * -texcoords_t_scale, 0); } // Add face and indices. @@ -879,9 +879,9 @@ void HL1MDLLoader::read_meshes() { aiFace *face = &scene_mesh->mFaces[f]; face->mNumIndices = 3; face->mIndices = new unsigned int[3]; - face->mIndices[0] = mesh_faces[f].v0; + face->mIndices[0] = mesh_faces[f].v2; face->mIndices[1] = mesh_faces[f].v1; - face->mIndices[2] = mesh_faces[f].v2; + face->mIndices[2] = mesh_faces[f].v0; } // Add mesh bones. diff --git a/code/AssetLib/MDL/MDLLoader.cpp b/code/AssetLib/MDL/MDLLoader.cpp index a4286a716..0d694a2cc 100644 --- a/code/AssetLib/MDL/MDLLoader.cpp +++ b/code/AssetLib/MDL/MDLLoader.cpp @@ -199,6 +199,7 @@ void MDLImporter::InternReadFile(const std::string &pFile, const uint32_t iMagicWord = *((uint32_t *)mBuffer); // Determine the file subtype and call the appropriate member function + bool is_half_life = false; // Original Quake1 format if (AI_MDL_MAGIC_NUMBER_BE == iMagicWord || AI_MDL_MAGIC_NUMBER_LE == iMagicWord) { @@ -240,6 +241,7 @@ void MDLImporter::InternReadFile(const std::string &pFile, else if (AI_MDL_MAGIC_NUMBER_BE_HL2a == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_HL2a == iMagicWord || AI_MDL_MAGIC_NUMBER_BE_HL2b == iMagicWord || AI_MDL_MAGIC_NUMBER_LE_HL2b == iMagicWord) { iGSFileVersion = 0; + is_half_life = true; HalfLife::HalfLifeMDLBaseHeader *pHeader = (HalfLife::HalfLifeMDLBaseHeader *)mBuffer; if (pHeader->version == AI_MDL_HL1_VERSION) { @@ -255,9 +257,19 @@ void MDLImporter::InternReadFile(const std::string &pFile, ". Magic word (", std::string((char *)&iMagicWord, 4), ") is not known"); } - // 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); + if (is_half_life){ + // Now rotate the whole scene 90 degrees around the z and x axes to convert to internal coordinate system + pScene->mRootNode->mTransformation = aiMatrix4x4( + 0.f, -1.f, 0.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + -1.f, 0.f, 0.f, 0.f, + 0.f, 0.f, 0.f, 1.f); + } + else { + // 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); + } DeleteBufferAndCleanup(); } catch (...) { From 3717e66faeae90d8fa4daff5f139767ce1feb388 Mon Sep 17 00:00:00 2001 From: Garux Date: Thu, 29 Apr 2021 19:57:57 +0300 Subject: [PATCH 024/335] consider aiProcess_FlipWindingOrder in aiProcess_GenNormals & aiProcess_GenSmoothNormals --- code/PostProcessing/GenFaceNormalsProcess.cpp | 3 +++ code/PostProcessing/GenFaceNormalsProcess.h | 1 + code/PostProcessing/GenVertexNormalsProcess.cpp | 3 +++ code/PostProcessing/GenVertexNormalsProcess.h | 1 + 4 files changed, 8 insertions(+) diff --git a/code/PostProcessing/GenFaceNormalsProcess.cpp b/code/PostProcessing/GenFaceNormalsProcess.cpp index a73df2b5d..3e8612d29 100644 --- a/code/PostProcessing/GenFaceNormalsProcess.cpp +++ b/code/PostProcessing/GenFaceNormalsProcess.cpp @@ -70,6 +70,7 @@ GenFaceNormalsProcess::~GenFaceNormalsProcess() { // Returns whether the processing step is present in the given flag field. bool GenFaceNormalsProcess::IsActive(unsigned int pFlags) const { force_ = (pFlags & aiProcess_ForceGenNormals) != 0; + flippedWindingOrder_ = (pFlags & aiProcess_FlipWindingOrder) != 0; return (pFlags & aiProcess_GenNormals) != 0; } @@ -134,6 +135,8 @@ bool GenFaceNormalsProcess::GenMeshFaceNormals(aiMesh *pMesh) { const aiVector3D *pV1 = &pMesh->mVertices[face.mIndices[0]]; const aiVector3D *pV2 = &pMesh->mVertices[face.mIndices[1]]; const aiVector3D *pV3 = &pMesh->mVertices[face.mIndices[face.mNumIndices - 1]]; + if (flippedWindingOrder_) + std::swap( pV2, pV3 ); const aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1)).NormalizeSafe(); for (unsigned int i = 0; i < face.mNumIndices; ++i) { diff --git a/code/PostProcessing/GenFaceNormalsProcess.h b/code/PostProcessing/GenFaceNormalsProcess.h index 4b9222af3..eefff6c73 100644 --- a/code/PostProcessing/GenFaceNormalsProcess.h +++ b/code/PostProcessing/GenFaceNormalsProcess.h @@ -80,6 +80,7 @@ public: private: bool GenMeshFaceNormals(aiMesh* pcMesh); mutable bool force_ = false; + mutable bool flippedWindingOrder_ = false; }; } // end of namespace Assimp diff --git a/code/PostProcessing/GenVertexNormalsProcess.cpp b/code/PostProcessing/GenVertexNormalsProcess.cpp index 5c9a6b754..e82bc3e6f 100644 --- a/code/PostProcessing/GenVertexNormalsProcess.cpp +++ b/code/PostProcessing/GenVertexNormalsProcess.cpp @@ -70,6 +70,7 @@ GenVertexNormalsProcess::~GenVertexNormalsProcess() { // Returns whether the processing step is present in the given flag field. bool GenVertexNormalsProcess::IsActive(unsigned int pFlags) const { force_ = (pFlags & aiProcess_ForceGenNormals) != 0; + flippedWindingOrder_ = (pFlags & aiProcess_FlipWindingOrder) != 0; return (pFlags & aiProcess_GenSmoothNormals) != 0; } @@ -142,6 +143,8 @@ bool GenVertexNormalsProcess::GenMeshVertexNormals(aiMesh *pMesh, unsigned int m const aiVector3D *pV1 = &pMesh->mVertices[face.mIndices[0]]; const aiVector3D *pV2 = &pMesh->mVertices[face.mIndices[1]]; const aiVector3D *pV3 = &pMesh->mVertices[face.mIndices[face.mNumIndices - 1]]; + if (flippedWindingOrder_) + std::swap( pV2, pV3 ); const aiVector3D vNor = ((*pV2 - *pV1) ^ (*pV3 - *pV1)).NormalizeSafe(); for (unsigned int i = 0; i < face.mNumIndices; ++i) { diff --git a/code/PostProcessing/GenVertexNormalsProcess.h b/code/PostProcessing/GenVertexNormalsProcess.h index 8b98ea8e6..8fc301ab7 100644 --- a/code/PostProcessing/GenVertexNormalsProcess.h +++ b/code/PostProcessing/GenVertexNormalsProcess.h @@ -104,6 +104,7 @@ private: /** Configuration option: maximum smoothing angle, in radians*/ ai_real configMaxAngle; mutable bool force_ = false; + mutable bool flippedWindingOrder_ = false; }; } // end of namespace Assimp From 666b5eff76484252e3191a29b1a3fa333095b6ec Mon Sep 17 00:00:00 2001 From: Krishty Date: Thu, 29 Apr 2021 21:29:10 +0200 Subject: [PATCH 025/335] added .step extension to IFC loader The extension .step is at least as common as .stp, so both should be supported. --- code/AssetLib/IFC/IFCLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/IFC/IFCLoader.cpp b/code/AssetLib/IFC/IFCLoader.cpp index be100df57..3c0690c93 100644 --- a/code/AssetLib/IFC/IFCLoader.cpp +++ b/code/AssetLib/IFC/IFCLoader.cpp @@ -115,7 +115,7 @@ static const aiImporterDesc desc = { 0, 0, 0, - "ifc ifczip stp" + "ifc ifczip step stp" }; // ------------------------------------------------------------------------------------------------ From edf12bd3571a0297d5a2784704b206df69655bbf Mon Sep 17 00:00:00 2001 From: Garux Date: Thu, 29 Apr 2021 20:14:57 +0300 Subject: [PATCH 026/335] fix md2 orientation --- code/AssetLib/MD2/MD2Loader.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/code/AssetLib/MD2/MD2Loader.cpp b/code/AssetLib/MD2/MD2Loader.cpp index 9ccbcdfca..41cec5ab6 100644 --- a/code/AssetLib/MD2/MD2Loader.cpp +++ b/code/AssetLib/MD2/MD2Loader.cpp @@ -433,10 +433,6 @@ void MD2Importer::InternReadFile( const std::string& pFile, aiVector3D& vNormal = pcMesh->mNormals[iCurrent]; LookupNormalIndex(pcVerts[iIndex].lightNormalIndex,vNormal); - // flip z and y to become right-handed - std::swap((float&)vNormal.z,(float&)vNormal.y); - std::swap((float&)vec.z,(float&)vec.y); - if (m_pcHeader->numTexCoords) { // validate texture coordinates iIndex = pcTriangles[i].textureIndices[c]; @@ -454,7 +450,15 @@ void MD2Importer::InternReadFile( const std::string& pFile, } pScene->mMeshes[0]->mFaces[i].mIndices[c] = iCurrent; } + // flip the face order + std::swap( pScene->mMeshes[0]->mFaces[i].mIndices[0], pScene->mMeshes[0]->mFaces[i].mIndices[2] ); } + // 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); } #endif // !! ASSIMP_BUILD_NO_MD2_IMPORTER From 149224091f2967badf71d074b1839a45afa1695e Mon Sep 17 00:00:00 2001 From: Garux Date: Fri, 30 Apr 2021 10:37:06 +0300 Subject: [PATCH 027/335] support missing closing brace in material list after Ascii Scene Exporter v2.51 --- code/AssetLib/ASE/ASEParser.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/code/AssetLib/ASE/ASEParser.cpp b/code/AssetLib/ASE/ASEParser.cpp index 21ec26593..958e3b9a8 100644 --- a/code/AssetLib/ASE/ASEParser.cpp +++ b/code/AssetLib/ASE/ASEParser.cpp @@ -498,6 +498,12 @@ void Parser::ParseLV1MaterialListBlock() { ParseLV2MaterialBlock(sMat); continue; } + if( iDepth == 1 ){ + // CRUDE HACK: support missing brace after "Ascii Scene Exporter v2.51" + LogWarning("Missing closing brace in material list"); + --filePtr; + return; + } } AI_ASE_HANDLE_TOP_LEVEL_SECTION(); } From feeb89a1dd4b572f6380fa075e67efbe7d3dee1e Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 30 Apr 2021 16:49:15 +0200 Subject: [PATCH 028/335] closes https://github.com/assimp/assimp/issues/3831 : update zip --- contrib/zip/.gitignore | 58 -- contrib/zip/CMakeLists.txt | 44 +- contrib/zip/README.md | 149 ++++- contrib/zip/src/miniz.h | 74 ++- contrib/zip/src/zip.c | 1094 ++++++++++++++++++++++++------- contrib/zip/src/zip.h | 136 +++- contrib/zip/test/CMakeLists.txt | 41 +- 7 files changed, 1219 insertions(+), 377 deletions(-) delete mode 100644 contrib/zip/.gitignore diff --git a/contrib/zip/.gitignore b/contrib/zip/.gitignore deleted file mode 100644 index 49b2cb2fd..000000000 --- a/contrib/zip/.gitignore +++ /dev/null @@ -1,58 +0,0 @@ -/build/ -/test/build/ -/xcodeproj/ -.vscode/ - -# Object files -*.o -*.ko -*.obj -*.elf - -# Precompiled Headers -*.gch -*.pch - -# Libraries -*.lib -*.a -*.la -*.lo - -# Shared objects (inc. Windows DLLs) -*.dll -*.so -*.so.* -*.dylib -*.suo - -# Executables -*.exe -*.out -*.app -*.i*86 -*.x86_64 -*.hex - -# Temporary -*.swp -.DS_Store - -# CMake -CMakeScripts -*.cmake - -# Xcode -*.build -*.xcodeproj -zip.sln -zip.vcxproj.filters -zip.vcxproj -ALL_BUILD.vcxproj.filters -ALL_BUILD.vcxproj -CMakeFiles/ -zip.dir/ -test/test.exe.vcxproj.filters -test/test.exe.vcxproj -test/test.exe.dir/ - diff --git a/contrib/zip/CMakeLists.txt b/contrib/zip/CMakeLists.txt index cdfa94b3b..bba4e7bde 100644 --- a/contrib/zip/CMakeLists.txt +++ b/contrib/zip/CMakeLists.txt @@ -1,26 +1,29 @@ -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.4) project(zip LANGUAGES C - VERSION "0.1.18") + VERSION "0.1.19") set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) - +set(CMAKE_VERBOSE_MAKEFILE ON) option(CMAKE_DISABLE_TESTING "Disable test creation" OFF) -if (MSVC) - # Use secure functions by default and suppress warnings about "deprecated" functions - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_NONSTDC_NO_WARNINGS=1 /D _CRT_SECURE_NO_WARNINGS=1") -elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR - "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR - "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang") - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99 -Wall -Wextra -Werror -pedantic") -endif (MSVC) - # zip set(SRC src/miniz.h src/zip.h src/zip.c) -add_library(${PROJECT_NAME} ${SRC}) + +# this is the "object library" target: compiles the sources only once +add_library(OBJLIB OBJECT ${SRC}) +# shared libraries need PIC +set_property(TARGET OBJLIB PROPERTY POSITION_INDEPENDENT_CODE 1) + +# static and shared libraries built from the same object files +if (BUILD_SHARED_LIBS) + add_library(${PROJECT_NAME} SHARED $) + include(GenerateExportHeader) + generate_export_header(${PROJECT_NAME}) +else() + add_library(${PROJECT_NAME} STATIC $) +endif() + target_include_directories(${PROJECT_NAME} PUBLIC $ $ @@ -34,6 +37,17 @@ if (NOT CMAKE_DISABLE_TESTING) add_sanitizers(${PROJECT_NAME} ${test_out}) endif() +if (MSVC) + # Use secure functions by default and suppress warnings about "deprecated" functions + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT=1") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /D _CRT_NONSTDC_NO_WARNINGS=1 /D _CRT_SECURE_NO_WARNINGS=1") +elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR + "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR + "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99 -Wall -Wextra -Werror -pedantic -Wno-deprecated") +endif (MSVC) + #### # Installation (https://github.com/forexample/package-example) { diff --git a/contrib/zip/README.md b/contrib/zip/README.md index bdd0822b6..308327a3f 100644 --- a/contrib/zip/README.md +++ b/contrib/zip/README.md @@ -2,7 +2,6 @@ This is done by hacking awesome [miniz](https://code.google.com/p/miniz) library and layering functions on top of the miniz v1.15 API. [![Build](https://github.com/kuba--/zip/workflows/build/badge.svg)](https://github.com/kuba--/zip/actions?query=workflow%3Abuild) -[![Version](https://badge.fury.io/gh/kuba--%2Fzip.svg)](https://github.com/kuba--/zip/releases) # The Idea @@ -155,10 +154,52 @@ struct zip_t *zip = zip_open("foo.zip", 0, 'r'); zip_close(zip); ``` +* Create a new zip archive in memory (stream API). + +```c +char *outbuf = NULL; +size_t outbufsize = 0; + +const char *inbuf = "Append some data here...\0"; +struct zip_t *zip = zip_stream_open(NULL, 0, ZIP_DEFAULT_COMPRESSION_LEVEL, 'w'); +{ + zip_entry_open(zip, "foo-1.txt"); + { + zip_entry_write(zip, inbuf, strlen(inbuf)); + } + zip_entry_close(zip); + + /* copy compressed stream into outbuf */ + zip_stream_copy(zip, (void **)&outbuf, &outbufsize); +} +zip_stream_close(zip); + +free(outbuf); +``` + +* Extract a zip entry into a memory (stream API). + +```c +char *buf = NULL; +ssize_t bufsize = 0; + +struct zip_t *zip = zip_stream_open(zipstream, zipstreamsize, 0, 'r'); +{ + zip_entry_open(zip, "foo-1.txt"); + { + zip_entry_read(zip, (void **)&buf, &bufsize); + } + zip_entry_close(zip); +} +zip_stream_close(zip); + +free(buf); +``` + * List of all zip entries ```c struct zip_t *zip = zip_open("foo.zip", 0, 'r'); -int i, n = zip_total_entries(zip); +int i, n = zip_entries_total(zip); for (i = 0; i < n; ++i) { zip_entry_openbyindex(zip, i); { @@ -172,6 +213,49 @@ for (i = 0; i < n; ++i) { zip_close(zip); ``` +* Compress folder (recursively) +```c +void zip_walk(struct zip_t *zip, const char *path) { + DIR *dir; + struct dirent *entry; + char fullpath[MAX_PATH]; + struct stat s; + + memset(fullpath, 0, MAX_PATH); + dir = opendir(path); + assert(dir); + + while ((entry = readdir(dir))) { + // skip "." and ".." + if (!strcmp(entry->d_name, ".\0") || !strcmp(entry->d_name, "..\0")) + continue; + + snprintf(fullpath, sizeof(fullpath), "%s/%s", path, entry->d_name); + stat(fullpath, &s); + if (S_ISDIR(s.st_mode)) + zip_walk(zip, fullpath); + else { + zip_entry_open(zip, fullpath); + zip_entry_fwrite(zip, fullpath); + zip_entry_close(zip); + } + } + + closedir(dir); +} +``` + +* Deletes zip archive entries. +```c +char *entries[] = {"unused.txt", "remove.ini", "delete.me"}; + +struct zip_t *zip = zip_open("foo.zip", 0, 'd'); +{ + zip_entries_delete(zip, entries, 3); +} +zip_close(zip); +``` + # Bindings Compile zip library as a dynamic library. ```shell @@ -181,7 +265,7 @@ $ cmake -DBUILD_SHARED_LIBS=true .. $ make ``` -### Go (cgo) +### [Go](https://golang.org) (cgo) ```go package main @@ -211,7 +295,7 @@ func main() { } ``` -### Rust (ffi) +### [Rust](https://www.rust-lang.org) (ffi) ```rust extern crate libc; use std::ffi::CString; @@ -236,7 +320,7 @@ extern "C" { } fn main() { - let path = CString::new("/tmp/test.zip").unwrap(); + let path = CString::new("/tmp/rust.zip").unwrap(); let mode: libc::c_char = 'w' as libc::c_char; let entryname = CString::new("test.txt").unwrap(); @@ -258,7 +342,7 @@ fn main() { } ``` -### Ruby (ffi) +### [Ruby](http://www.ruby-lang.org) (ffi) Install _ffi_ gem. ```shell $ gem install ffi @@ -291,7 +375,7 @@ Zip.zip_entry_close(ptr) Zip.zip_close(ptr) ``` -### Python (cffi) +### [Python](https://www.python.org) (cffi) Install _cffi_ package ```shell $ pip install cffi @@ -325,7 +409,36 @@ Zip.zip_entry_close(ptr) Zip.zip_close(ptr) ``` -### Ring +### [Never](https://never-lang.readthedocs.io/) (ffi) +```never +extern "libzip.so" func zip_open(zipname: string, level: int, mode: char) -> c_ptr +extern "libzip.so" func zip_close(zip: c_ptr) -> void + +extern "libzip.so" func zip_entry_open(zip: c_ptr, entryname: string) -> int +extern "libzip.so" func zip_entry_close(zip: c_ptr) -> int +extern "libzip.so" func zip_entry_write(zip: c_ptr, buf: string, bufsize: int) -> int +extern "libzip.so" func zip_entry_fwrite(zip: c_ptr, filename: string) -> int + +func main() -> int +{ + let content = "Test content" + + let zip = zip_open("/tmp/never.zip", 6, 'w'); + + zip_entry_open(zip, "test.file"); + zip_entry_fwrite(zip, "/tmp/test.txt"); + zip_entry_close(zip); + + zip_entry_open(zip, "test.content"); + zip_entry_write(zip, content, length(content)); + zip_entry_close(zip); + + zip_close(zip); + 0 +} +``` + +### [Ring](http://ring-lang.net) The language comes with RingZip based on this library ```ring load "ziplib.ring" @@ -342,13 +455,15 @@ new Zip { } ``` -# Contribution Rules/Coding Standards -No need to throw away your coding style, just do your best to follow default clang-format style. -Apply `clang-format` to the source files before commit: -```sh -for file in $(git ls-files | \grep -E '\.(c|h)$' | \grep -v -- '#') -do - clang-format -i $file -done -``` +# Check out more cool projects which use this library: +- [Filament](https://github.com/google/filament): Filament is a real-time physically based rendering engine for Android, iOS, Linux, macOS, Windows, and WebGL. It is designed to be as small as possible and as efficient as possible on Android. +- [Hermes JS Engine](https://github.com/facebook/hermes): Hermes is a JavaScript engine optimized for fast start-up of React Native apps on Android. It features ahead-of-time static optimization and compact bytecode. +- [Open Asset Import Library](https://github.com/assimp/assimp): A library to import and export various 3d-model-formats including scene-post-processing to generate missing render data. +- [PowerToys](https://github.com/microsoft/PowerToys): Set of utilities for power users to tune and streamline their Windows 10 experience for greater productivity. +- [The Ring Programming Language](https://ring-lang.github.io): Innovative and practical general-purpose multi-paradigm language. +- [The V Programming Language](https://github.com/vlang/v): Simple, fast, safe, compiled. For developing maintainable software. +- [TIC-80](https://github.com/nesbox/TIC-80): TIC-80 is a FREE and OPEN SOURCE fantasy computer for making, playing and sharing tiny games. +- [Urho3D](https://github.com/urho3d/Urho3D): Urho3D is a free lightweight, cross-platform 2D and 3D game engine implemented in C++ and released under the MIT license. Greatly inspired by OGRE and Horde3D. +- [Vcpkg](https://github.com/microsoft/vcpkg): Vcpkg helps you manage C and C++ libraries on Windows, Linux and MacOS. +- [and more...](https://grep.app/search?q=kuba--/zip) diff --git a/contrib/zip/src/miniz.h b/contrib/zip/src/miniz.h index 7570ae929..0a5560b24 100644 --- a/contrib/zip/src/miniz.h +++ b/contrib/zip/src/miniz.h @@ -400,7 +400,7 @@ typedef enum { #ifndef MINIZ_NO_ZLIB_APIS // Heap allocation callbacks. -// Note that mz_alloc_func parameter types purpsosely differ from zlib's: +// Note that mz_alloc_func parameter types purposely differ from zlib's: // items/size is size_t, not unsigned long. typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); typedef void (*mz_free_func)(void *opaque, void *address); @@ -2194,7 +2194,8 @@ tinfl_status tinfl_decompress(tinfl_decompressor *r, } else tree_cur = pTable->m_tree[-tree_cur - 1]; } - tree_cur -= ((rev_code >>= 1) & 1); + rev_code >>= 1; + tree_cur -= (rev_code & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; } if (r->m_type == 2) { @@ -3970,6 +3971,7 @@ mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, #ifdef _MSC_VER #pragma warning(push) +#pragma warning(disable : 4121 4127 4244) #pragma warning(disable : 4204) // nonstandard extension used : non-constant // aggregate initializer (also supported by GNU // C and C99, so no big deal) @@ -4098,10 +4100,6 @@ void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, pLen_out, 6, MZ_FALSE); } -#ifdef _MSC_VER -#pragma warning(pop) -#endif - // ------------------- .ZIP archive reading #ifndef MINIZ_NO_ARCHIVE_APIS @@ -4112,18 +4110,39 @@ void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, #include #include -#if defined(_MSC_VER) +#if defined(_MSC_VER) || defined(__MINGW32__) + +#include + +static wchar_t *str2wstr(const char *str) { + int len = (int) strlen(str) + 1; + wchar_t *wstr = malloc(len * sizeof(wchar_t)); + MultiByteToWideChar(CP_UTF8, 0, str, len * sizeof(char), wstr, len); + return wstr; +} + static FILE *mz_fopen(const char *pFilename, const char *pMode) { - FILE *pFile = NULL; - fopen_s(&pFile, pFilename, pMode); + wchar_t *wFilename = str2wstr(pFilename); + wchar_t *wMode = str2wstr(pMode); + FILE *pFile = _wfopen(wFilename, wMode); + + free(wFilename); + free(wMode); + return pFile; } + static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) { - FILE *pFile = NULL; - if (freopen_s(&pFile, pPath, pMode, pStream)) - return NULL; + wchar_t *wPath = str2wstr(pPath); + wchar_t *wMode = str2wstr(pMode); + FILE *pFile = _wfreopen(wPath, wMode, pStream); + + free(wPath); + free(wMode); + return pFile; } + #ifndef MINIZ_NO_TIME #include #endif @@ -4144,7 +4163,7 @@ static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) { #include #endif #define MZ_FILE FILE -#define MZ_FOPEN(f, m) fopen(f, m) +#define MZ_FOPEN(f, m) mz_fopen #define MZ_FCLOSE fclose #define MZ_FREAD fread #define MZ_FWRITE fwrite @@ -4153,7 +4172,7 @@ static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) { #define MZ_FILE_STAT_STRUCT _stat #define MZ_FILE_STAT _stat #define MZ_FFLUSH fflush -#define MZ_FREOPEN(f, m, s) freopen(f, m, s) +#define MZ_FREOPEN(f, m, s) mz_freopen #define MZ_DELETE_FILE remove #elif defined(__TINYC__) #ifndef MINIZ_NO_TIME @@ -5361,13 +5380,9 @@ mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, } else { // Temporarily allocate a read buffer. read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); -#if defined(_MSC_VER) && !defined(__clang__) - if (((0, sizeof(size_t) == sizeof(mz_uint32))) && - (read_buf_size > 0x7FFFFFFF)) -#else if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) -#endif return MZ_FALSE; + if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) return MZ_FALSE; @@ -5454,11 +5469,7 @@ void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; -#if defined(_MSC_VER) && !defined(__clang__) - if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) -#else if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) -#endif return NULL; if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) @@ -5560,14 +5571,10 @@ mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) { // The file is stored or the caller has requested the compressed data. if (pZip->m_pState->m_pMem) { -#if defined (_MSC_VER) && !defined(__clang__) - if (((0, sizeof(size_t) == sizeof(mz_uint32))) && - (file_stat.m_comp_size > 0xFFFFFFFF)) -#else if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) -#endif return MZ_FALSE; + if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) status = TINFL_STATUS_FAILED; @@ -6085,7 +6092,7 @@ mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) { if (!pZip->m_file_offset_alignment) return 0; n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); - return (mz_uint)(pZip->m_file_offset_alignment - n) & + return (pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1); } @@ -6289,7 +6296,10 @@ mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, mz_uint32 ext_attributes) { mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; mz_uint16 method = 0, dos_time = 0, dos_date = 0; +#ifndef MINIZ_NO_TIME time_t file_modified_time; +#endif + mz_uint64 local_dir_header_ofs, cur_archive_file_ofs, uncomp_size = 0, comp_size = 0; size_t archive_name_size; @@ -6326,10 +6336,12 @@ mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, comment_size + archive_name_size) > 0xFFFFFFFF)) return MZ_FALSE; +#ifndef MINIZ_NO_TIME memset(&file_modified_time, 0, sizeof(file_modified_time)); if (!mz_zip_get_file_modified_time(pSrc_filename, &file_modified_time)) return MZ_FALSE; mz_zip_time_t_to_dos_time(file_modified_time, &dos_time, &dos_date); +#endif pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); if (!pSrc_file) @@ -6814,6 +6826,10 @@ void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, return p; } +#ifdef _MSC_VER +#pragma warning(pop) +#endif + #endif // #ifndef MINIZ_NO_STDIO #endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS diff --git a/contrib/zip/src/zip.c b/contrib/zip/src/zip.c index 83e8e2a41..1f7fa8b76 100644 --- a/contrib/zip/src/zip.c +++ b/contrib/zip/src/zip.c @@ -18,11 +18,6 @@ /* Win32, DOS, MSVC, MSVS */ #include -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4706) -#endif // _MSC_VER - #define MKDIR(DIRNAME) _mkdir(DIRNAME) #define STRCLONE(STR) ((STR) ? _strdup(STR) : NULL) #define HAS_DEVICE(P) \ @@ -32,17 +27,29 @@ #else -#include // needed for symlink() on BSD -int symlink(const char *target, const char *linkpath); // needed on Linux +#include // needed for symlink() #define MKDIR(DIRNAME) mkdir(DIRNAME, 0755) #define STRCLONE(STR) ((STR) ? strdup(STR) : NULL) #endif +#ifdef __MINGW32__ +#include +#include +#endif + #include "miniz.h" #include "zip.h" +#ifdef _MSC_VER +#include +#pragma warning(disable : 4706) + +#define ftruncate(fd, sz) (-(_chsize_s((fd), (sz)) != 0)) +#define fileno _fileno +#endif + #ifndef HAS_DEVICE #define HAS_DEVICE(P) 0 #endif @@ -63,7 +70,84 @@ int symlink(const char *target, const char *linkpath); // needed on Linux } \ } while (0) -static const char *base_name(const char *name) { +struct zip_entry_t { + int index; + char *name; + mz_uint64 uncomp_size; + mz_uint64 comp_size; + mz_uint32 uncomp_crc32; + mz_uint64 offset; + mz_uint8 header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; + mz_uint64 header_offset; + mz_uint16 method; + mz_zip_writer_add_state state; + tdefl_compressor comp; + mz_uint32 external_attr; + time_t m_time; +}; + +struct zip_t { + mz_zip_archive archive; + mz_uint level; + struct zip_entry_t entry; +}; + +enum zip_modify_t { + MZ_KEEP = 0, + MZ_DELETE = 1, + MZ_MOVE = 2, +}; + +struct zip_entry_mark_t { + int file_index; + enum zip_modify_t type; + mz_uint64 m_local_header_ofs; + mz_uint64 lf_length; +}; + +static const char *const zip_errlist[30] = { + NULL, + "not initialized\0", + "invalid entry name\0", + "entry not found\0", + "invalid zip mode\0", + "invalid compression level\0", + "no zip 64 support\0", + "memset error\0", + "cannot write data to entry\0", + "cannot initialize tdefl compressor\0", + "invalid index\0", + "header not found\0", + "cannot flush tdefl buffer\0", + "cannot write entry header\0", + "cannot create entry header\0", + "cannot write to central dir\0", + "cannot open file\0", + "invalid entry type\0", + "extracting data using no memory allocation\0", + "file not found\0", + "no permission\0", + "out of memory\0", + "invalid zip archive name\0", + "make dir error\0" + "symlink error\0" + "close archive error\0" + "capacity size too small\0", + "fseek error\0", + "fread error\0", + "fwrite error\0", +}; + +const char *zip_strerror(int errnum) { + errnum = -errnum; + if (errnum <= 0 || errnum >= 30) { + return NULL; + } + + return zip_errlist[errnum]; +} + +static const char *zip_basename(const char *name) { char const *p; char const *base = name += FILESYSTEM_PREFIX_LEN(name); int all_slashes = 1; @@ -82,7 +166,7 @@ static const char *base_name(const char *name) { return base; } -static int mkpath(char *path) { +static int zip_mkpath(char *path) { char *p; char npath[MAX_PATH + 1]; int len = 0; @@ -107,7 +191,7 @@ static int mkpath(char *path) { if (MKDIR(npath) == -1) { if (errno != EEXIST) { - return -1; + return ZIP_EMKDIR; } } } @@ -117,7 +201,7 @@ static int mkpath(char *path) { return 0; } -static char *strrpl(const char *str, size_t n, char oldchar, char newchar) { +static char *zip_strrpl(const char *str, size_t n, char oldchar, char newchar) { char c; size_t i; char *rpl = (char *)calloc((1 + n), sizeof(char)); @@ -136,27 +220,562 @@ static char *strrpl(const char *str, size_t n, char oldchar, char newchar) { return begin; } -struct zip_entry_t { - int index; - char *name; - mz_uint64 uncomp_size; - mz_uint64 comp_size; - mz_uint32 uncomp_crc32; - mz_uint64 offset; - mz_uint8 header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; - mz_uint64 header_offset; - mz_uint16 method; - mz_zip_writer_add_state state; - tdefl_compressor comp; - mz_uint32 external_attr; - time_t m_time; -}; +static char *zip_name_normalize(char *name, char *const nname, size_t len) { + size_t offn = 0; + size_t offnn = 0, ncpy = 0; -struct zip_t { - mz_zip_archive archive; - mz_uint level; - struct zip_entry_t entry; -}; + if (name == NULL || nname == NULL || len <= 0) { + return NULL; + } + // skip trailing '/' + while (ISSLASH(*name)) + name++; + + for (; offn < len; offn++) { + if (ISSLASH(name[offn])) { + if (ncpy > 0 && strcmp(&nname[offnn], ".\0") && + strcmp(&nname[offnn], "..\0")) { + offnn += ncpy; + nname[offnn++] = name[offn]; // append '/' + } + ncpy = 0; + } else { + nname[offnn + ncpy] = name[offn]; + ncpy++; + } + } + + // at the end, extra check what we've already copied + if (ncpy == 0 || !strcmp(&nname[offnn], ".\0") || + !strcmp(&nname[offnn], "..\0")) { + nname[offnn] = 0; + } + return nname; +} + +static mz_bool zip_name_match(const char *name1, const char *name2) { + int len2 = (int) strlen(name2); + char *nname2 = zip_strrpl(name2, len2, '\\', '/'); + if (!nname2) { + return MZ_FALSE; + } + + mz_bool res = (strcmp(name1, nname2) == 0) ? MZ_TRUE : MZ_FALSE; + CLEANUP(nname2); + return res; +} + +static int zip_archive_truncate(mz_zip_archive *pzip) { + mz_zip_internal_state *pState = pzip->m_pState; + mz_uint64 file_size = pzip->m_archive_size; + if ((pzip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) { + return 0; + } + if (pzip->m_zip_mode == MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED) { + if (pState->m_pFile) { + int fd = fileno(pState->m_pFile); + return ftruncate(fd, file_size); + } + } + return 0; +} + +static int zip_archive_extract(mz_zip_archive *zip_archive, const char *dir, + int (*on_extract)(const char *filename, + void *arg), + void *arg) { + int err = 0; + mz_uint i, n; + char path[MAX_PATH + 1]; + char symlink_to[MAX_PATH + 1]; + mz_zip_archive_file_stat info; + size_t dirlen = 0; + mz_uint32 xattr = 0; + + memset(path, 0, sizeof(path)); + memset(symlink_to, 0, sizeof(symlink_to)); + + dirlen = strlen(dir); + if (dirlen + 1 > MAX_PATH) { + return ZIP_EINVENTNAME; + } + + memset((void *)&info, 0, sizeof(mz_zip_archive_file_stat)); + +#if defined(_MSC_VER) + strcpy_s(path, MAX_PATH, dir); +#else + strcpy(path, dir); +#endif + + if (!ISSLASH(path[dirlen - 1])) { +#if defined(_WIN32) || defined(__WIN32__) + path[dirlen] = '\\'; +#else + path[dirlen] = '/'; +#endif + ++dirlen; + } + + // Get and print information about each file in the archive. + n = mz_zip_reader_get_num_files(zip_archive); + for (i = 0; i < n; ++i) { + if (!mz_zip_reader_file_stat(zip_archive, i, &info)) { + // Cannot get information about zip archive; + err = ZIP_ENOENT; + goto out; + } + + if (!zip_name_normalize(info.m_filename, info.m_filename, + strlen(info.m_filename))) { + // Cannot normalize file name; + err = ZIP_EINVENTNAME; + goto out; + } +#if defined(_MSC_VER) + strncpy_s(&path[dirlen], MAX_PATH - dirlen, info.m_filename, + MAX_PATH - dirlen); +#else + strncpy(&path[dirlen], info.m_filename, MAX_PATH - dirlen); +#endif + err = zip_mkpath(path); + if (err < 0) { + // Cannot make a path + goto out; + } + + if ((((info.m_version_made_by >> 8) == 3) || + ((info.m_version_made_by >> 8) == + 19)) // if zip is produced on Unix or macOS (3 and 19 from + // section 4.4.2.2 of zip standard) + && info.m_external_attr & + (0x20 << 24)) { // and has sym link attribute (0x80 is file, 0x40 + // is directory) +#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \ + defined(__MINGW32__) +#else + if (info.m_uncomp_size > MAX_PATH || + !mz_zip_reader_extract_to_mem_no_alloc(zip_archive, i, symlink_to, + MAX_PATH, 0, NULL, 0)) { + err = ZIP_EMEMNOALLOC; + goto out; + } + symlink_to[info.m_uncomp_size] = '\0'; + if (symlink(symlink_to, path) != 0) { + err = ZIP_ESYMLINK; + goto out; + } +#endif + } else { + if (!mz_zip_reader_is_file_a_directory(zip_archive, i)) { + if (!mz_zip_reader_extract_to_file(zip_archive, i, path, 0)) { + // Cannot extract zip archive to file + err = ZIP_ENOFILE; + goto out; + } + } + +#if defined(_MSC_VER) + (void)xattr; // unused +#else + xattr = (info.m_external_attr >> 16) & 0xFFFF; + if (xattr > 0) { + if (chmod(path, (mode_t)xattr) < 0) { + err = ZIP_ENOPERM; + goto out; + } + } +#endif + } + + if (on_extract) { + if (on_extract(path, arg) < 0) { + goto out; + } + } + } + +out: + // Close the archive, freeing any resources it was using + if (!mz_zip_reader_end(zip_archive)) { + // Cannot end zip reader + err = ZIP_ECLSZIP; + } + return err; +} + +static inline void zip_archive_finalize(mz_zip_archive *pzip) { + mz_zip_writer_finalize_archive(pzip); + zip_archive_truncate(pzip); +} + +static int zip_entry_mark(struct zip_t *zip, + struct zip_entry_mark_t *entry_mark, int n, + char *const entries[], const size_t len) { + int err = 0; + if (!zip || !entry_mark || !entries) { + return ZIP_ENOINIT; + } + + mz_zip_archive_file_stat file_stat; + mz_uint64 d_pos = (mz_uint64) ~0; + for (int i = 0; i < n; ++i) { + err = zip_entry_openbyindex(zip, i); + if (err) { + return err; + } + + mz_bool name_matches = MZ_FALSE; + for (int j = 0; j < (const int)len; ++j) { + if (zip_name_match(zip->entry.name, entries[j])) { + name_matches = MZ_TRUE; + break; + } + } + if (name_matches) { + entry_mark[i].type = MZ_DELETE; + } else { + entry_mark[i].type = MZ_KEEP; + } + + if (!mz_zip_reader_file_stat(&zip->archive, i, &file_stat)) { + return ZIP_ENOENT; + } + + zip_entry_close(zip); + + entry_mark[i].m_local_header_ofs = file_stat.m_local_header_ofs; + entry_mark[i].file_index = -1; + entry_mark[i].lf_length = 0; + if ((entry_mark[i].type) == MZ_DELETE && + (d_pos > entry_mark[i].m_local_header_ofs)) { + d_pos = entry_mark[i].m_local_header_ofs; + } + } + for (int i = 0; i < n; ++i) { + if ((entry_mark[i].m_local_header_ofs > d_pos) && + (entry_mark[i].type != MZ_DELETE)) { + entry_mark[i].type = MZ_MOVE; + } + } + return err; +} + +static int zip_index_next(mz_uint64 *local_header_ofs_array, int cur_index) { + int new_index = 0; + for (int i = cur_index - 1; i >= 0; --i) { + if (local_header_ofs_array[cur_index] > local_header_ofs_array[i]) { + new_index = i + 1; + return new_index; + } + } + return new_index; +} + +static int zip_sort(mz_uint64 *local_header_ofs_array, int cur_index) { + int nxt_index = zip_index_next(local_header_ofs_array, cur_index); + + if (nxt_index != cur_index) { + mz_uint64 temp = local_header_ofs_array[cur_index]; + for (int i = cur_index; i > nxt_index; i--) { + local_header_ofs_array[i] = local_header_ofs_array[i - 1]; + } + local_header_ofs_array[nxt_index] = temp; + } + return nxt_index; +} + +static int zip_index_update(struct zip_entry_mark_t *entry_mark, int last_index, + int nxt_index) { + for (int j = 0; j < last_index; j++) { + if (entry_mark[j].file_index >= nxt_index) { + entry_mark[j].file_index += 1; + } + } + entry_mark[nxt_index].file_index = last_index; + return 0; +} + +static int zip_entry_finalize(struct zip_t *zip, + struct zip_entry_mark_t *entry_mark, + const int n) { + + mz_uint64 *local_header_ofs_array = (mz_uint64 *)calloc(n, sizeof(mz_uint64)); + if (!local_header_ofs_array) { + return ZIP_EOOMEM; + } + + for (int i = 0; i < n; ++i) { + local_header_ofs_array[i] = entry_mark[i].m_local_header_ofs; + int index = zip_sort(local_header_ofs_array, i); + + if (index != i) { + zip_index_update(entry_mark, i, index); + } + entry_mark[i].file_index = index; + } + + mz_uint64 *length = (mz_uint64 *)calloc(n, sizeof(mz_uint64)); + if (!length) { + CLEANUP(local_header_ofs_array); + return ZIP_EOOMEM; + } + for (int i = 0; i < n - 1; i++) { + length[i] = local_header_ofs_array[i + 1] - local_header_ofs_array[i]; + } + length[n - 1] = zip->archive.m_archive_size - local_header_ofs_array[n - 1]; + + for (int i = 0; i < n; i++) { + entry_mark[i].lf_length = length[entry_mark[i].file_index]; + } + + CLEANUP(length); + CLEANUP(local_header_ofs_array); + return 0; +} + +static int zip_entry_set(struct zip_t *zip, struct zip_entry_mark_t *entry_mark, + int n, char *const entries[], const size_t len) { + int err = 0; + + if ((err = zip_entry_mark(zip, entry_mark, n, entries, len)) < 0) { + return err; + } + if ((err = zip_entry_finalize(zip, entry_mark, n)) < 0) { + return err; + } + return 0; +} + +static mz_int64 zip_file_move(MZ_FILE *m_pFile, const mz_uint64 to, + const mz_uint64 from, const mz_uint64 length, + mz_uint8 *move_buf, + const mz_int64 capacity_size) { + if ((mz_int64)length > capacity_size) { + return ZIP_ECAPSIZE; + } + if (MZ_FSEEK64(m_pFile, from, SEEK_SET)) { + MZ_FCLOSE(m_pFile); + return ZIP_EFSEEK; + } + + if (fread(move_buf, 1, length, m_pFile) != length) { + MZ_FCLOSE(m_pFile); + return ZIP_EFREAD; + } + if (MZ_FSEEK64(m_pFile, to, SEEK_SET)) { + MZ_FCLOSE(m_pFile); + return ZIP_EFSEEK; + } + if (fwrite(move_buf, 1, length, m_pFile) != length) { + MZ_FCLOSE(m_pFile); + return ZIP_EFWRITE; + } + return (mz_int64)length; +} + +static mz_int64 zip_files_move(MZ_FILE *m_pFile, mz_uint64 writen_num, + mz_uint64 read_num, mz_uint64 length) { + int n = 0; + const mz_int64 page_size = 1 << 12; // 4K + mz_uint8 *move_buf = (mz_uint8 *)calloc(1, page_size); + if (move_buf == NULL) { + return ZIP_EOOMEM; + } + + mz_int64 moved_length = 0; + mz_int64 move_count = 0; + while ((mz_int64)length > 0) { + move_count = ((mz_int64)length >= page_size) ? page_size : (mz_int64)length; + n = (int) zip_file_move(m_pFile, writen_num, read_num, move_count, move_buf, + page_size); + if (n < 0) { + moved_length = n; + goto cleanup; + } + + if (n != move_count) { + goto cleanup; + } + + writen_num += move_count; + read_num += move_count; + length -= move_count; + moved_length += move_count; + } + +cleanup: + CLEANUP(move_buf); + return moved_length; +} + +static int zip_central_dir_move(mz_zip_internal_state *pState, int begin, + int end, int entry_num) { + if (begin == entry_num) { + return 0; + } + + mz_uint64 l_size = 0; + mz_uint64 r_size = 0; + mz_uint64 d_size = 0; + mz_uint8 *next = NULL; + mz_uint8 *deleted = &MZ_ZIP_ARRAY_ELEMENT( + &pState->m_central_dir, mz_uint8, + MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, begin)); + l_size = (mz_uint32)(deleted - (mz_uint8 *)(pState->m_central_dir.m_p)); + if (end == entry_num) { + r_size = 0; + } else { + next = &MZ_ZIP_ARRAY_ELEMENT( + &pState->m_central_dir, mz_uint8, + MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, end)); + r_size = pState->m_central_dir.m_size - + (mz_uint32)(next - (mz_uint8 *)(pState->m_central_dir.m_p)); + d_size = next - deleted; + } + + if (l_size == 0) { + memmove(pState->m_central_dir.m_p, next, r_size); + pState->m_central_dir.m_p = MZ_REALLOC(pState->m_central_dir.m_p, r_size); + for (int i = end; i < entry_num; i++) { + MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint64, i) -= + d_size; + } + } + + if (l_size * r_size != 0) { + memmove(deleted, next, r_size); + for (int i = end; i < entry_num; i++) { + MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint64, i) -= + d_size; + } + } + + pState->m_central_dir.m_size = l_size + r_size; + return 0; +} + +static int zip_central_dir_delete(mz_zip_internal_state *pState, + int *deleted_entry_index_array, + int entry_num) { + int i = 0; + int begin = 0; + int end = 0; + int d_num = 0; + while (i < entry_num) { + while ((!deleted_entry_index_array[i]) && (i < entry_num)) { + i++; + } + begin = i; + + while ((deleted_entry_index_array[i]) && (i < entry_num)) { + i++; + } + end = i; + zip_central_dir_move(pState, begin, end, entry_num); + } + + i = 0; + while (i < entry_num) { + while ((!deleted_entry_index_array[i]) && (i < entry_num)) { + i++; + } + begin = i; + if (begin == entry_num) { + break; + } + while ((deleted_entry_index_array[i]) && (i < entry_num)) { + i++; + } + end = i; + int k = 0; + for (int j = end; j < entry_num; j++) { + MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, + begin + k) = + (mz_uint32)MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, + mz_uint32, j); + k++; + } + d_num += end - begin; + } + + pState->m_central_dir_offsets.m_size = + sizeof(mz_uint32) * (entry_num - d_num); + return 0; +} + +static int zip_entries_delete_mark(struct zip_t *zip, + struct zip_entry_mark_t *entry_mark, + int entry_num) { + mz_uint64 writen_num = 0; + mz_uint64 read_num = 0; + mz_uint64 deleted_length = 0; + mz_uint64 move_length = 0; + int i = 0; + int deleted_entry_num = 0; + int n = 0; + + mz_bool *deleted_entry_flag_array = + (mz_bool *)calloc(entry_num, sizeof(mz_bool)); + if (deleted_entry_flag_array == NULL) { + return ZIP_EOOMEM; + } + + mz_zip_internal_state *pState = zip->archive.m_pState; + zip->archive.m_zip_mode = MZ_ZIP_MODE_WRITING; + + if (MZ_FSEEK64(pState->m_pFile, 0, SEEK_SET)) { + CLEANUP(deleted_entry_flag_array); + return ZIP_ENOENT; + } + + while (i < entry_num) { + while ((entry_mark[i].type == MZ_KEEP) && (i < entry_num)) { + writen_num += entry_mark[i].lf_length; + read_num = writen_num; + i++; + } + + while ((entry_mark[i].type == MZ_DELETE) && (i < entry_num)) { + deleted_entry_flag_array[i] = MZ_TRUE; + read_num += entry_mark[i].lf_length; + deleted_length += entry_mark[i].lf_length; + i++; + deleted_entry_num++; + } + + while ((entry_mark[i].type == MZ_MOVE) && (i < entry_num)) { + move_length += entry_mark[i].lf_length; + mz_uint8 *p = &MZ_ZIP_ARRAY_ELEMENT( + &pState->m_central_dir, mz_uint8, + MZ_ZIP_ARRAY_ELEMENT(&pState->m_central_dir_offsets, mz_uint32, i)); + if (!p) { + CLEANUP(deleted_entry_flag_array); + return ZIP_ENOENT; + } + mz_uint32 offset = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); + offset -= (mz_uint32)deleted_length; + MZ_WRITE_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS, offset); + i++; + } + + n = (int) zip_files_move(pState->m_pFile, writen_num, read_num, move_length); + if (n != (mz_int64)move_length) { + CLEANUP(deleted_entry_flag_array); + return n; + } + writen_num += move_length; + read_num += move_length; + } + + zip->archive.m_archive_size -= deleted_length; + zip->archive.m_total_files = entry_num - deleted_entry_num; + + zip_central_dir_delete(pState, deleted_entry_flag_array, entry_num); + CLEANUP(deleted_entry_flag_array); + + return deleted_entry_num; +} struct zip_t *zip_open(const char *zipname, int level, char mode) { struct zip_t *zip = NULL; @@ -189,6 +808,7 @@ struct zip_t *zip_open(const char *zipname, int level, char mode) { case 'r': case 'a': + case 'd': if (!mz_zip_reader_init_file( &(zip->archive), zipname, zip->level | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) { @@ -196,7 +816,7 @@ struct zip_t *zip_open(const char *zipname, int level, char mode) { // zip_archive reader goto cleanup; } - if (mode == 'a' && + if ((mode == 'a' || mode == 'd') && !mz_zip_writer_init_from_reader(&(zip->archive), zipname)) { mz_zip_reader_end(&(zip->archive)); goto cleanup; @@ -219,7 +839,7 @@ void zip_close(struct zip_t *zip) { // Always finalize, even if adding failed for some reason, so we have a // valid central directory. mz_zip_writer_finalize_archive(&(zip->archive)); - + zip_archive_truncate(&(zip->archive)); mz_zip_writer_end(&(zip->archive)); mz_zip_reader_end(&(zip->archive)); @@ -228,14 +848,9 @@ void zip_close(struct zip_t *zip) { } int zip_is64(struct zip_t *zip) { - if (!zip) { - // zip_t handler is not initialized - return -1; - } - - if (!zip->archive.m_pState) { - // zip state is not initialized - return -1; + if (!zip || !zip->archive.m_pState) { + // zip_t handler or zip state is not initialized + return ZIP_ENOINIT; } return (int)zip->archive.m_pState->m_zip64; @@ -246,14 +861,19 @@ int zip_entry_open(struct zip_t *zip, const char *entryname) { mz_zip_archive *pzip = NULL; mz_uint num_alignment_padding_bytes, level; mz_zip_archive_file_stat stats; + int err = 0; - if (!zip || !entryname) { - return -1; + if (!zip) { + return ZIP_ENOINIT; + } + + if (!entryname) { + return ZIP_EINVENTNAME; } entrylen = strlen(entryname); - if (entrylen < 1) { - return -1; + if (entrylen == 0) { + return ZIP_EINVENTNAME; } /* @@ -267,10 +887,13 @@ int zip_entry_open(struct zip_t *zip, const char *entryname) { and UNIX file systems etc. If input came from standard input, there is no file name field. */ - zip->entry.name = strrpl(entryname, entrylen, '\\', '/'); + if (zip->entry.name) { + CLEANUP(zip->entry.name); + } + zip->entry.name = zip_strrpl(entryname, entrylen, '\\', '/'); if (!zip->entry.name) { // Cannot parse zip entry name - return -1; + return ZIP_EINVENTNAME; } pzip = &(zip->archive); @@ -278,10 +901,12 @@ int zip_entry_open(struct zip_t *zip, const char *entryname) { zip->entry.index = mz_zip_reader_locate_file(pzip, zip->entry.name, NULL, 0); if (zip->entry.index < 0) { + err = ZIP_ENOENT; goto cleanup; } if (!mz_zip_reader_file_stat(pzip, (mz_uint)zip->entry.index, &stats)) { + err = ZIP_ENOENT; goto cleanup; } @@ -292,7 +917,9 @@ int zip_entry_open(struct zip_t *zip, const char *entryname) { zip->entry.header_offset = stats.m_local_header_ofs; zip->entry.method = stats.m_method; zip->entry.external_attr = stats.m_external_attr; +#ifndef MINIZ_NO_TIME zip->entry.m_time = stats.m_time; +#endif return 0; } @@ -318,11 +945,13 @@ int zip_entry_open(struct zip_t *zip, const char *entryname) { mz_zip_writer_compute_padding_needed_for_file_alignment(pzip); if (!pzip->m_pState || (pzip->m_zip_mode != MZ_ZIP_MODE_WRITING)) { - // Wrong zip mode + // Invalid zip mode + err = ZIP_EINVMODE; goto cleanup; } if (zip->level & MZ_ZIP_FLAG_COMPRESSED_DATA) { - // Wrong zip compression level + // Invalid zip compression level + err = ZIP_EINVLVL; goto cleanup; } // no zip64 support yet @@ -331,12 +960,14 @@ int zip_entry_open(struct zip_t *zip, const char *entryname) { MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + entrylen) > 0xFFFFFFFF)) { // No zip64 support yet + err = ZIP_ENOSUP64; goto cleanup; } if (!mz_zip_writer_write_zeros(pzip, zip->entry.offset, num_alignment_padding_bytes + sizeof(zip->entry.header))) { // Cannot memset zip entry header + err = ZIP_EMEMSET; goto cleanup; } @@ -350,6 +981,7 @@ int zip_entry_open(struct zip_t *zip, const char *entryname) { if (pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, zip->entry.name, entrylen) != entrylen) { // Cannot write data to zip entry + err = ZIP_EWRTENT; goto cleanup; } @@ -366,6 +998,7 @@ int zip_entry_open(struct zip_t *zip, const char *entryname) { (int)level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) { // Cannot initialize the zip compressor + err = ZIP_ETDEFLINIT; goto cleanup; } } @@ -376,7 +1009,7 @@ int zip_entry_open(struct zip_t *zip, const char *entryname) { cleanup: CLEANUP(zip->entry.name); - return -1; + return err; } int zip_entry_openbyindex(struct zip_t *zip, int index) { @@ -388,28 +1021,26 @@ int zip_entry_openbyindex(struct zip_t *zip, int index) { if (!zip) { // zip_t handler is not initialized - return -1; + return ZIP_ENOINIT; } pZip = &(zip->archive); if (pZip->m_zip_mode != MZ_ZIP_MODE_READING) { // open by index requires readonly mode - return -1; + return ZIP_EINVMODE; } if (index < 0 || (mz_uint)index >= pZip->m_total_files) { // index out of range - return -1; + return ZIP_EINVIDX; } - if (!(pHeader = &MZ_ZIP_ARRAY_ELEMENT( &pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, index)))) { // cannot find header in central directory - return -1; + return ZIP_ENOHDR; } - namelen = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; @@ -424,14 +1055,17 @@ int zip_entry_openbyindex(struct zip_t *zip, int index) { and UNIX file systems etc. If input came from standard input, there is no file name field. */ - zip->entry.name = strrpl(pFilename, namelen, '\\', '/'); + if (zip->entry.name) { + CLEANUP(zip->entry.name); + } + zip->entry.name = zip_strrpl(pFilename, namelen, '\\', '/'); if (!zip->entry.name) { // local entry name is NULL - return -1; + return ZIP_EINVENTNAME; } if (!mz_zip_reader_file_stat(pZip, (mz_uint)index, &stats)) { - return -1; + return ZIP_ENOENT; } zip->entry.index = index; @@ -442,7 +1076,9 @@ int zip_entry_openbyindex(struct zip_t *zip, int index) { zip->entry.header_offset = stats.m_local_header_ofs; zip->entry.method = stats.m_method; zip->entry.external_attr = stats.m_external_attr; +#ifndef MINIZ_NO_TIME zip->entry.m_time = stats.m_time; +#endif return 0; } @@ -452,17 +1088,17 @@ int zip_entry_close(struct zip_t *zip) { mz_uint level; tdefl_status done; mz_uint16 entrylen; - mz_uint16 dos_time, dos_date; - int status = -1; + mz_uint16 dos_time = 0, dos_date = 0; + int err = 0; if (!zip) { // zip_t handler is not initialized + err = ZIP_ENOINIT; goto cleanup; } pzip = &(zip->archive); if (pzip->m_zip_mode == MZ_ZIP_MODE_READING) { - status = 0; goto cleanup; } @@ -471,6 +1107,7 @@ int zip_entry_close(struct zip_t *zip) { done = tdefl_compress_buffer(&(zip->entry.comp), "", 0, TDEFL_FINISH); if (done != TDEFL_STATUS_DONE && done != TDEFL_STATUS_OKAY) { // Cannot flush compressed buffer + err = ZIP_ETDEFLBUF; goto cleanup; } zip->entry.comp_size = zip->entry.state.m_comp_size; @@ -479,18 +1116,22 @@ int zip_entry_close(struct zip_t *zip) { } entrylen = (mz_uint16)strlen(zip->entry.name); - // no zip64 support yet if ((zip->entry.comp_size > 0xFFFFFFFF) || (zip->entry.offset > 0xFFFFFFFF)) { // No zip64 support, yet + err = ZIP_ENOSUP64; goto cleanup; } +#ifndef MINIZ_NO_TIME mz_zip_time_t_to_dos_time(zip->entry.m_time, &dos_time, &dos_date); +#endif + if (!mz_zip_writer_create_local_dir_header( pzip, zip->entry.header, entrylen, 0, zip->entry.uncomp_size, zip->entry.comp_size, zip->entry.uncomp_crc32, zip->entry.method, 0, dos_time, dos_date)) { // Cannot create zip entry header + err = ZIP_ECRTHDR; goto cleanup; } @@ -498,6 +1139,7 @@ int zip_entry_close(struct zip_t *zip) { zip->entry.header, sizeof(zip->entry.header)) != sizeof(zip->entry.header)) { // Cannot write zip entry header + err = ZIP_EWRTHDR; goto cleanup; } @@ -507,19 +1149,19 @@ int zip_entry_close(struct zip_t *zip) { zip->entry.method, 0, dos_time, dos_date, zip->entry.header_offset, zip->entry.external_attr)) { // Cannot write to zip central dir + err = ZIP_EWRTDIR; goto cleanup; } pzip->m_total_files++; pzip->m_archive_size = zip->entry.offset; - status = 0; cleanup: if (zip) { zip->entry.m_time = 0; CLEANUP(zip->entry.name); } - return status; + return err; } const char *zip_entry_name(struct zip_t *zip) { @@ -534,7 +1176,7 @@ const char *zip_entry_name(struct zip_t *zip) { int zip_entry_index(struct zip_t *zip) { if (!zip) { // zip_t handler is not initialized - return -1; + return ZIP_ENOINIT; } return zip->entry.index; @@ -543,12 +1185,12 @@ int zip_entry_index(struct zip_t *zip) { int zip_entry_isdir(struct zip_t *zip) { if (!zip) { // zip_t handler is not initialized - return -1; + return ZIP_ENOINIT; } if (zip->entry.index < 0) { // zip entry is not opened - return -1; + return ZIP_EINVIDX; } return (int)mz_zip_reader_is_file_a_directory(&zip->archive, @@ -570,7 +1212,7 @@ int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize) { if (!zip) { // zip_t handler is not initialized - return -1; + return ZIP_ENOINIT; } pzip = &(zip->archive); @@ -584,7 +1226,7 @@ int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize) { if ((pzip->m_pWrite(pzip->m_pIO_opaque, zip->entry.offset, buf, bufsize) != bufsize)) { // Cannot write buffer - return -1; + return ZIP_EWRTENT; } zip->entry.offset += bufsize; zip->entry.comp_size += bufsize; @@ -593,7 +1235,7 @@ int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize) { TDEFL_NO_FLUSH); if (status != TDEFL_STATUS_DONE && status != TDEFL_STATUS_OKAY) { // Cannot compress buffer - return -1; + return ZIP_ETDEFLBUF; } } } @@ -602,7 +1244,7 @@ int zip_entry_write(struct zip_t *zip, const void *buf, size_t bufsize) { } int zip_entry_fwrite(struct zip_t *zip, const char *filename) { - int status = 0; + int err = 0; size_t n = 0; FILE *stream = NULL; mz_uint8 buf[MZ_ZIP_MAX_IO_BUF_SIZE]; @@ -610,14 +1252,14 @@ int zip_entry_fwrite(struct zip_t *zip, const char *filename) { if (!zip) { // zip_t handler is not initialized - return -1; + return ZIP_ENOINIT; } memset(buf, 0, MZ_ZIP_MAX_IO_BUF_SIZE); memset((void *)&file_stat, 0, sizeof(struct MZ_FILE_STAT_STRUCT)); if (MZ_FILE_STAT(filename, &file_stat) != 0) { // problem getting information - check errno - return -1; + return ZIP_ENOENT; } if ((file_stat.st_mode & 0200) == 0) { @@ -634,19 +1276,19 @@ int zip_entry_fwrite(struct zip_t *zip, const char *filename) { #endif { // Cannot open filename - return -1; + return ZIP_EOPNFILE; } while ((n = fread(buf, sizeof(mz_uint8), MZ_ZIP_MAX_IO_BUF_SIZE, stream)) > 0) { if (zip_entry_write(zip, buf, n) < 0) { - status = -1; + err = ZIP_EWRTENT; break; } } fclose(stream); - return status; + return err; } ssize_t zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize) { @@ -656,19 +1298,19 @@ ssize_t zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize) { if (!zip) { // zip_t handler is not initialized - return -1; + return ZIP_ENOINIT; } pzip = &(zip->archive); if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) { // the entry is not found or we do not have read access - return -1; + return ZIP_ENOENT; } idx = (mz_uint)zip->entry.index; if (mz_zip_reader_is_file_a_directory(pzip, idx)) { // the entry is a directory - return -1; + return ZIP_EINVENTTYPE; } *buf = mz_zip_reader_extract_to_heap(pzip, idx, &size, 0); @@ -683,18 +1325,18 @@ ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize) { if (!zip) { // zip_t handler is not initialized - return -1; + return ZIP_ENOINIT; } pzip = &(zip->archive); if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) { // the entry is not found or we do not have read access - return -1; + return ZIP_ENOENT; } if (!mz_zip_reader_extract_to_mem_no_alloc(pzip, (mz_uint)zip->entry.index, buf, bufsize, 0, NULL, 0)) { - return -1; + return ZIP_EMEMNOALLOC; } return (ssize_t)zip->entry.uncomp_size; @@ -703,45 +1345,43 @@ ssize_t zip_entry_noallocread(struct zip_t *zip, void *buf, size_t bufsize) { int zip_entry_fread(struct zip_t *zip, const char *filename) { mz_zip_archive *pzip = NULL; mz_uint idx; -#if defined(_MSC_VER) -#else mz_uint32 xattr = 0; -#endif mz_zip_archive_file_stat info; if (!zip) { // zip_t handler is not initialized - return -1; + return ZIP_ENOINIT; } memset((void *)&info, 0, sizeof(mz_zip_archive_file_stat)); pzip = &(zip->archive); if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) { // the entry is not found or we do not have read access - return -1; + return ZIP_ENOENT; } idx = (mz_uint)zip->entry.index; if (mz_zip_reader_is_file_a_directory(pzip, idx)) { // the entry is a directory - return -1; + return ZIP_EINVENTTYPE; } if (!mz_zip_reader_extract_to_file(pzip, idx, filename, 0)) { - return -1; + return ZIP_ENOFILE; } #if defined(_MSC_VER) + (void)xattr; // unused #else if (!mz_zip_reader_file_stat(pzip, idx, &info)) { // Cannot get information about zip archive; - return -1; + return ZIP_ENOFILE; } xattr = (info.m_external_attr >> 16) & 0xFFFF; if (xattr > 0) { if (chmod(filename, (mode_t)xattr) < 0) { - return -1; + return ZIP_ENOPERM; } } #endif @@ -758,32 +1398,147 @@ int zip_entry_extract(struct zip_t *zip, if (!zip) { // zip_t handler is not initialized - return -1; + return ZIP_ENOINIT; } pzip = &(zip->archive); if (pzip->m_zip_mode != MZ_ZIP_MODE_READING || zip->entry.index < 0) { // the entry is not found or we do not have read access - return -1; + return ZIP_ENOENT; } idx = (mz_uint)zip->entry.index; return (mz_zip_reader_extract_to_callback(pzip, idx, on_extract, arg, 0)) ? 0 - : -1; + : ZIP_EINVIDX; } -int zip_total_entries(struct zip_t *zip) { +int zip_entries_total(struct zip_t *zip) { if (!zip) { // zip_t handler is not initialized - return -1; + return ZIP_ENOINIT; } return (int)zip->archive.m_total_files; } +int zip_entries_delete(struct zip_t *zip, char *const entries[], + const size_t len) { + int n = 0; + int err = 0; + struct zip_entry_mark_t *entry_mark = NULL; + + if (zip == NULL || (entries == NULL && len != 0)) { + return ZIP_ENOINIT; + } + + if (entries == NULL && len == 0) { + return 0; + } + + n = zip_entries_total(zip); + + entry_mark = + (struct zip_entry_mark_t *)calloc(n, sizeof(struct zip_entry_mark_t)); + if (!entry_mark) { + return ZIP_EOOMEM; + } + + zip->archive.m_zip_mode = MZ_ZIP_MODE_READING; + + err = zip_entry_set(zip, entry_mark, n, entries, len); + if (err < 0) { + CLEANUP(entry_mark); + return err; + } + + err = zip_entries_delete_mark(zip, entry_mark, n); + CLEANUP(entry_mark); + return err; +} + +int zip_stream_extract(const char *stream, size_t size, const char *dir, + int (*on_extract)(const char *filename, void *arg), + void *arg) { + mz_zip_archive zip_archive; + if (!stream || !dir) { + // Cannot parse zip archive stream + return ZIP_ENOINIT; + } + if (!memset(&zip_archive, 0, sizeof(mz_zip_archive))) { + // Cannot memset zip archive + return ZIP_EMEMSET; + } + if (!mz_zip_reader_init_mem(&zip_archive, stream, size, 0)) { + // Cannot initialize zip_archive reader + return ZIP_ENOINIT; + } + + return zip_archive_extract(&zip_archive, dir, on_extract, arg); +} + +struct zip_t *zip_stream_open(const char *stream, size_t size, int level, + char mode) { + struct zip_t *zip = (struct zip_t *)calloc((size_t)1, sizeof(struct zip_t)); + if (!zip) { + return NULL; + } + + if (level < 0) { + level = MZ_DEFAULT_LEVEL; + } + if ((level & 0xF) > MZ_UBER_COMPRESSION) { + // Wrong compression level + goto cleanup; + } + zip->level = (mz_uint)level; + + if ((stream != NULL) && (size > 0) && (mode == 'r')) { + if (!mz_zip_reader_init_mem(&(zip->archive), stream, size, 0)) { + goto cleanup; + } + } else if ((stream == NULL) && (size == 0) && (mode == 'w')) { + // Create a new archive. + if (!mz_zip_writer_init_heap(&(zip->archive), 0, 1024)) { + // Cannot initialize zip_archive writer + goto cleanup; + } + } else { + goto cleanup; + } + return zip; + +cleanup: + CLEANUP(zip); + return NULL; +} + +ssize_t zip_stream_copy(struct zip_t *zip, void **buf, ssize_t *bufsize) { + if (!zip) { + return ZIP_ENOINIT; + } + + zip_archive_finalize(&(zip->archive)); + + if (bufsize != NULL) { + *bufsize = zip->archive.m_archive_size; + } + *buf = calloc(sizeof(unsigned char), zip->archive.m_archive_size); + memcpy(*buf, zip->archive.m_pState->m_pMem, zip->archive.m_archive_size); + + return zip->archive.m_archive_size; +} + +void zip_stream_close(struct zip_t *zip) { + if (zip) { + mz_zip_writer_end(&(zip->archive)); + mz_zip_reader_end(&(zip->archive)); + CLEANUP(zip); + } +} + int zip_create(const char *zipname, const char *filenames[], size_t len) { - int status = 0; + int err = 0; size_t i; mz_zip_archive zip_archive; struct MZ_FILE_STAT_STRUCT file_stat; @@ -791,32 +1546,34 @@ int zip_create(const char *zipname, const char *filenames[], size_t len) { if (!zipname || strlen(zipname) < 1) { // zip_t archive name is empty or NULL - return -1; + return ZIP_EINVZIPNAME; } // Create a new archive. if (!memset(&(zip_archive), 0, sizeof(zip_archive))) { // Cannot memset zip archive - return -1; + return ZIP_EMEMSET; } if (!mz_zip_writer_init_file(&zip_archive, zipname, 0)) { // Cannot initialize zip_archive writer - return -1; + return ZIP_ENOINIT; } - memset((void *)&file_stat, 0, sizeof(struct MZ_FILE_STAT_STRUCT)); + if (!memset((void *)&file_stat, 0, sizeof(struct MZ_FILE_STAT_STRUCT))) { + return ZIP_EMEMSET; + } for (i = 0; i < len; ++i) { const char *name = filenames[i]; if (!name) { - status = -1; + err = ZIP_EINVENTNAME; break; } if (MZ_FILE_STAT(name, &file_stat) != 0) { // problem getting information - check errno - status = -1; + err = ZIP_ENOFILE; break; } @@ -826,150 +1583,39 @@ int zip_create(const char *zipname, const char *filenames[], size_t len) { } ext_attributes |= (mz_uint32)((file_stat.st_mode & 0xFFFF) << 16); - if (!mz_zip_writer_add_file(&zip_archive, base_name(name), name, "", 0, + if (!mz_zip_writer_add_file(&zip_archive, zip_basename(name), name, "", 0, ZIP_DEFAULT_COMPRESSION_LEVEL, ext_attributes)) { // Cannot add file to zip_archive - status = -1; + err = ZIP_ENOFILE; break; } } mz_zip_writer_finalize_archive(&zip_archive); mz_zip_writer_end(&zip_archive); - return status; + return err; } int zip_extract(const char *zipname, const char *dir, int (*on_extract)(const char *filename, void *arg), void *arg) { - int status = -1; - mz_uint i, n; - char path[MAX_PATH + 1]; - char symlink_to[MAX_PATH + 1]; mz_zip_archive zip_archive; - mz_zip_archive_file_stat info; - size_t dirlen = 0; -#if defined(_MSC_VER) -#else - mz_uint32 xattr = 0; -#endif - - - memset(path, 0, sizeof(path)); - memset(symlink_to, 0, sizeof(symlink_to)); - if (!memset(&(zip_archive), 0, sizeof(zip_archive))) { - // Cannot memset zip archive - return -1; - } if (!zipname || !dir) { // Cannot parse zip archive name - return -1; + return ZIP_EINVZIPNAME; } - dirlen = strlen(dir); - if (dirlen + 1 > MAX_PATH) { - return -1; + if (!memset(&zip_archive, 0, sizeof(mz_zip_archive))) { + // Cannot memset zip archive + return ZIP_EMEMSET; } // Now try to open the archive. if (!mz_zip_reader_init_file(&zip_archive, zipname, 0)) { // Cannot initialize zip_archive reader - return -1; + return ZIP_ENOINIT; } - memset((void *)&info, 0, sizeof(mz_zip_archive_file_stat)); - -#if defined(_MSC_VER) - strcpy_s(path, MAX_PATH, dir); -#else - strcpy(path, dir); -#endif - - if (!ISSLASH(path[dirlen - 1])) { -#if defined(_WIN32) || defined(__WIN32__) - path[dirlen] = '\\'; -#else - path[dirlen] = '/'; -#endif - ++dirlen; - } - - // Get and print information about each file in the archive. - n = mz_zip_reader_get_num_files(&zip_archive); - for (i = 0; i < n; ++i) { - if (!mz_zip_reader_file_stat(&zip_archive, i, &info)) { - // Cannot get information about zip archive; - goto out; - } -#if defined(_MSC_VER) - strncpy_s(&path[dirlen], MAX_PATH - dirlen, info.m_filename, - MAX_PATH - dirlen); -#else - strncpy(&path[dirlen], info.m_filename, MAX_PATH - dirlen); -#endif - if (mkpath(path) < 0) { - // Cannot make a path - goto out; - } - - if ((((info.m_version_made_by >> 8) == 3) || - ((info.m_version_made_by >> 8) == - 19)) // if zip is produced on Unix or macOS (3 and 19 from - // section 4.4.2.2 of zip standard) - && info.m_external_attr & - (0x20 << 24)) { // and has sym link attribute (0x80 is file, 0x40 - // is directory) -#if defined(_WIN32) || defined(__WIN32__) || defined(_MSC_VER) || \ - defined(__MINGW32__) -#else - if (info.m_uncomp_size > MAX_PATH || - !mz_zip_reader_extract_to_mem_no_alloc(&zip_archive, i, symlink_to, - MAX_PATH, 0, NULL, 0)) { - goto out; - } - symlink_to[info.m_uncomp_size] = '\0'; - if (symlink(symlink_to, path) != 0) { - goto out; - } -#endif - } else { - if (!mz_zip_reader_is_file_a_directory(&zip_archive, i)) { - if (!mz_zip_reader_extract_to_file(&zip_archive, i, path, 0)) { - // Cannot extract zip archive to file - goto out; - } - } - -#if defined(_MSC_VER) -#else - xattr = (info.m_external_attr >> 16) & 0xFFFF; - if (xattr > 0) { - if (chmod(path, (mode_t)xattr) < 0) { - goto out; - } - } -#endif - } - - if (on_extract) { - if (on_extract(path, arg) < 0) { - goto out; - } - } - } - status = 0; - -out: - // Close the archive, freeing any resources it was using - if (!mz_zip_reader_end(&zip_archive)) { - // Cannot end zip reader - status = -1; - } - - return status; + return zip_archive_extract(&zip_archive, dir, on_extract, arg); } - -#ifdef _MSC_VER -#pragma warning(pop) -#endif // _MSC_VER diff --git a/contrib/zip/src/zip.h b/contrib/zip/src/zip.h index 87f3654f4..70ab2cee6 100644 --- a/contrib/zip/src/zip.h +++ b/contrib/zip/src/zip.h @@ -15,19 +15,11 @@ #include #include -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4127 ) -#endif //_MSC_VER - #ifdef __cplusplus extern "C" { #endif -#if !defined(_SSIZE_T_DEFINED) && !defined(_SSIZE_T_DEFINED_) && \ - !defined(__DEFINED_ssize_t) && !defined(__ssize_t_defined) && \ - !defined(_SSIZE_T) && !defined(_SSIZE_T_) && !defined(_SSIZE_T_DECLARED) - +#if !defined(_POSIX_C_SOURCE) && defined(_MSC_VER) // 64-bit Windows is the only mainstream platform // where sizeof(long) != sizeof(void*) #ifdef _WIN64 @@ -35,15 +27,6 @@ typedef long long ssize_t; /* byte count or error */ #else typedef long ssize_t; /* byte count or error */ #endif - -#define _SSIZE_T_DEFINED -#define _SSIZE_T_DEFINED_ -#define __DEFINED_ssize_t -#define __ssize_t_defined -#define _SSIZE_T -#define _SSIZE_T_ -#define _SSIZE_T_DECLARED - #endif #ifndef MAX_PATH @@ -64,9 +47,49 @@ typedef long ssize_t; /* byte count or error */ /** * Default zip compression level. */ - #define ZIP_DEFAULT_COMPRESSION_LEVEL 6 +/** + * Error codes + */ +#define ZIP_ENOINIT -1 // not initialized +#define ZIP_EINVENTNAME -2 // invalid entry name +#define ZIP_ENOENT -3 // entry not found +#define ZIP_EINVMODE -4 // invalid zip mode +#define ZIP_EINVLVL -5 // invalid compression level +#define ZIP_ENOSUP64 -6 // no zip 64 support +#define ZIP_EMEMSET -7 // memset error +#define ZIP_EWRTENT -8 // cannot write data to entry +#define ZIP_ETDEFLINIT -9 // cannot initialize tdefl compressor +#define ZIP_EINVIDX -10 // invalid index +#define ZIP_ENOHDR -11 // header not found +#define ZIP_ETDEFLBUF -12 // cannot flush tdefl buffer +#define ZIP_ECRTHDR -13 // cannot create entry header +#define ZIP_EWRTHDR -14 // cannot write entry header +#define ZIP_EWRTDIR -15 // cannot write to central dir +#define ZIP_EOPNFILE -16 // cannot open file +#define ZIP_EINVENTTYPE -17 // invalid entry type +#define ZIP_EMEMNOALLOC -18 // extracting data using no memory allocation +#define ZIP_ENOFILE -19 // file not found +#define ZIP_ENOPERM -20 // no permission +#define ZIP_EOOMEM -21 // out of memory +#define ZIP_EINVZIPNAME -22 // invalid zip archive name +#define ZIP_EMKDIR -23 // make dir error +#define ZIP_ESYMLINK -24 // symlink error +#define ZIP_ECLSZIP -25 // close archive error +#define ZIP_ECAPSIZE -26 // capacity size too small +#define ZIP_EFSEEK -27 // fseek error +#define ZIP_EFREAD -28 // fread error +#define ZIP_EFWRITE -29 // fwrite error + +/** + * Looks up the error message string coresponding to an error number. + * @param errnum error number + * @return error message string coresponding to errnum or NULL if error is not + * found. + */ +extern const char *zip_strerror(int errnum); + /** * @struct zip_t * @@ -242,8 +265,8 @@ extern ssize_t zip_entry_read(struct zip_t *zip, void **buf, size_t *bufsize); * * @note ensure supplied output buffer is large enough. * zip_entry_size function (returns uncompressed size for the current - * entry) can be handy to estimate how big buffer is needed. for large - * entries, please take a look at zip_entry_extract function. + * entry) can be handy to estimate how big buffer is needed. + * For large entries, please take a look at zip_entry_extract function. * * @return the return code - the number of bytes actually read on success. * Otherwise a -1 on error (e.g. bufsize is not large enough). @@ -285,7 +308,71 @@ zip_entry_extract(struct zip_t *zip, * @return the return code - the number of entries on success, negative number * (< 0) on error. */ -extern int zip_total_entries(struct zip_t *zip); +extern int zip_entries_total(struct zip_t *zip); + +/** + * Deletes zip archive entries. + * + * @param zip zip archive handler. + * @param entries array of zip archive entries to be deleted. + * @param len the number of entries to be deleted. + * @return the number of deleted entries, or negative number (< 0) on error. + */ +extern int zip_entries_delete(struct zip_t *zip, char *const entries[], + size_t len); + +/** + * Extracts a zip archive stream into directory. + * + * If on_extract is not NULL, the callback will be called after + * successfully extracted each zip entry. + * Returning a negative value from the callback will cause abort and return an + * error. The last argument (void *arg) is optional, which you can use to pass + * data to the on_extract callback. + * + * @param stream zip archive stream. + * @param size stream size. + * @param dir output directory. + * @param on_extract on extract callback. + * @param arg opaque pointer. + * + * @return the return code - 0 on success, negative number (< 0) on error. + */ +extern int zip_stream_extract(const char *stream, size_t size, const char *dir, + int (*on_extract)(const char *filename, + void *arg), + void *arg); + +/** + * Opens zip archive stream into memory. + * + * @param stream zip archive stream. + * @param size stream size. + * + * @return the zip archive handler or NULL on error + */ +extern struct zip_t *zip_stream_open(const char *stream, size_t size, int level, + char mode); + +/** + * Copy zip archive stream output buffer. + * + * @param zip zip archive handler. + * @param buf output buffer. User should free buf. + * @param bufsize output buffer size (in bytes). + * + * @return copy size + */ +extern ssize_t zip_stream_copy(struct zip_t *zip, void **buf, ssize_t *bufsize); + +/** + * Close zip archive releases resources. + * + * @param zip zip archive handler. + * + * @return + */ +extern void zip_stream_close(struct zip_t *zip); /** * Creates a new archive and puts files into a single zip archive. @@ -319,11 +406,6 @@ extern int zip_extract(const char *zipname, const char *dir, void *arg); /** @} */ - -#ifdef _MSC_VER -#pragma warning(pop) -#endif //_MSC_VER - #ifdef __cplusplus } #endif diff --git a/contrib/zip/test/CMakeLists.txt b/contrib/zip/test/CMakeLists.txt index a7c3650f7..0da1684d4 100644 --- a/contrib/zip/test/CMakeLists.txt +++ b/contrib/zip/test/CMakeLists.txt @@ -1,11 +1,38 @@ -cmake_minimum_required(VERSION 3.10) +cmake_minimum_required(VERSION 3.4) -# test -set(test_out test.out) +# tests +set(test_write_out test_write.out) +add_executable(${test_write_out} test_write.c) +target_link_libraries(${test_write_out} zip) +add_test(NAME ${test_write_out} COMMAND ${test_write_out}) +set(test_write_out ${test_write_out} PARENT_SCOPE) -add_executable(${test_out} test.c) -target_link_libraries(${test_out} zip) +set(test_append_out test_append.out) +add_executable(${test_append_out} test_append.c) +target_link_libraries(${test_append_out} zip) +add_test(NAME ${test_append_out} COMMAND ${test_append_out}) +set(test_append_out ${test_append_out} PARENT_SCOPE) -add_test(NAME ${test_out} COMMAND ${test_out}) +set(test_read_out test_read.out) +add_executable(${test_read_out} test_read.c) +target_link_libraries(${test_read_out} zip) +add_test(NAME ${test_read_out} COMMAND ${test_read_out}) +set(test_read_out ${test_read_out} PARENT_SCOPE) -set(test_out ${test_out} PARENT_SCOPE) +set(test_extract_out test_extract.out) +add_executable(${test_extract_out} test_extract.c) +target_link_libraries(${test_extract_out} zip) +add_test(NAME ${test_extract_out} COMMAND ${test_extract_out}) +set(test_extract_out ${test_extract_out} PARENT_SCOPE) + +set(test_entry_out test_entry.out) +add_executable(${test_entry_out} test_entry.c) +target_link_libraries(${test_entry_out} zip) +add_test(NAME ${test_entry_out} COMMAND ${test_entry_out}) +set(test_entry_out ${test_entry_out} PARENT_SCOPE) + +set(test_permissions_out test_permissions.out) +add_executable(${test_permissions_out} test_permissions.c) +target_link_libraries(${test_permissions_out} zip) +add_test(NAME ${test_permissions_out} COMMAND ${test_permissions_out}) +set(test_permissions_out ${test_permissions_out} PARENT_SCOPE) From 0b7ebef497f0bde699e85c8935f7201cf638470d Mon Sep 17 00:00:00 2001 From: Garux Date: Fri, 30 Apr 2021 22:51:21 +0300 Subject: [PATCH 029/335] fix path separator in md3 shader loading --- code/AssetLib/MD3/MD3Loader.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/code/AssetLib/MD3/MD3Loader.cpp b/code/AssetLib/MD3/MD3Loader.cpp index e27079766..fc8bd9037 100644 --- a/code/AssetLib/MD3/MD3Loader.cpp +++ b/code/AssetLib/MD3/MD3Loader.cpp @@ -483,8 +483,9 @@ void MD3Importer::ReadShader(Q3Shader::ShaderData &fill) const { // If no specific dir or file is given, use our default search behaviour if (!configShaderFile.length()) { - if (!Q3Shader::LoadShader(fill, path + "..\\..\\..\\scripts\\" + model_file + ".shader", mIOHandler)) { - Q3Shader::LoadShader(fill, path + "..\\..\\..\\scripts\\" + filename + ".shader", mIOHandler); + const char sep = mIOHandler->getOsSeparator(); + if (!Q3Shader::LoadShader(fill, path + ".." + sep + ".." + sep + ".." + sep + "scripts" + sep + model_file + ".shader", mIOHandler)) { + Q3Shader::LoadShader(fill, path + ".." + sep + ".." + sep + ".." + sep + "scripts" + sep + filename + ".shader", mIOHandler); } } else { // If the given string specifies a file, load this file. From 55abc49d6de53bfdbc66937932f63019a3aad5c5 Mon Sep 17 00:00:00 2001 From: Garux Date: Fri, 30 Apr 2021 22:59:05 +0300 Subject: [PATCH 030/335] improve md3::Q3 shader::cull keyword support only use nonstandard winding order with `cull back`; might be excess too, since engine doesn't support this --- code/AssetLib/MD3/MD3Loader.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/code/AssetLib/MD3/MD3Loader.cpp b/code/AssetLib/MD3/MD3Loader.cpp index fc8bd9037..87fe2509c 100644 --- a/code/AssetLib/MD3/MD3Loader.cpp +++ b/code/AssetLib/MD3/MD3Loader.cpp @@ -196,11 +196,11 @@ bool Q3Shader::LoadShader(ShaderData &fill, const std::string &pFile, IOSystem * // 'cull' specifies culling behaviour for the model else if (TokenMatchI(buff, "cull", 4)) { SkipSpaces(&buff); - if (!ASSIMP_strincmp(buff, "back", 4)) { + if (!ASSIMP_strincmp(buff, "back", 4)) { // render face's backside, does not function in Q3 engine (bug) curData->cull = Q3Shader::CULL_CCW; - } else if (!ASSIMP_strincmp(buff, "front", 5)) { + } else if (!ASSIMP_strincmp(buff, "front", 5)) { // is not valid keyword in Q3, but occurs in shaders curData->cull = Q3Shader::CULL_CW; - } else if (!ASSIMP_strincmp(buff, "none", 4) || !ASSIMP_strincmp(buff, "disable", 7)) { + } else if (!ASSIMP_strincmp(buff, "none", 4) || !ASSIMP_strincmp(buff, "twosided", 8) || !ASSIMP_strincmp(buff, "disable", 7)) { curData->cull = Q3Shader::CULL_NONE; } else { ASSIMP_LOG_ERROR("Q3Shader: Unrecognized cull mode"); @@ -986,8 +986,8 @@ void MD3Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy pcMesh->mTextureCoords[0][iCurrent].x = pcUVs[index].U; pcMesh->mTextureCoords[0][iCurrent].y = 1.0f - pcUVs[index].V; } - // Flip face order if necessary - if (!shader || shader->cull == Q3Shader::CULL_CW) { + // Flip face order normally, unless shader is backfacing + if (!(shader && shader->cull == Q3Shader::CULL_CCW)) { std::swap(pcMesh->mFaces[i].mIndices[2], pcMesh->mFaces[i].mIndices[1]); } ++pcTriangles; From af0aca796ebab7663bba2778f7f30dd424f324ca Mon Sep 17 00:00:00 2001 From: contriteobserver Date: Fri, 30 Apr 2021 21:07:01 -0700 Subject: [PATCH 031/335] now compiling M3D ASCII support by default addresses issue #3777 --- code/AssetLib/M3D/M3DExporter.cpp | 12 ++++------ code/AssetLib/M3D/M3DImporter.cpp | 10 -------- code/AssetLib/M3D/M3DWrapper.h | 1 - code/AssetLib/M3D/m3d.h | 38 +++---------------------------- 4 files changed, 7 insertions(+), 54 deletions(-) diff --git a/code/AssetLib/M3D/M3DExporter.cpp b/code/AssetLib/M3D/M3DExporter.cpp index 856932947..bcac1d98a 100644 --- a/code/AssetLib/M3D/M3DExporter.cpp +++ b/code/AssetLib/M3D/M3DExporter.cpp @@ -294,21 +294,17 @@ void ExportSceneM3D( // Worker function for exporting a scene to ASCII A3D. // Prototyped and registered in Exporter.cpp void ExportSceneM3DA( - const char *, - IOSystem *, - const aiScene *, - const ExportProperties * + const char *pFile, + IOSystem *pIOSystem, + const aiScene *pScene, + const ExportProperties *pProperties ) { -#ifdef M3D_ASCII // initialize the exporter M3DExporter exporter(pScene, pProperties); // perform ascii export exporter.doExport(pFile, pIOSystem, true); -#else - throw DeadlyExportError("Assimp configured without M3D_ASCII support"); -#endif } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/M3D/M3DImporter.cpp b/code/AssetLib/M3D/M3DImporter.cpp index 8cbda23cb..56c272be8 100644 --- a/code/AssetLib/M3D/M3DImporter.cpp +++ b/code/AssetLib/M3D/M3DImporter.cpp @@ -95,11 +95,7 @@ static const aiImporterDesc desc = { 0, 0, 0, -#ifdef M3D_ASCII "m3d a3d" -#else - "m3d" -#endif }; namespace Assimp { @@ -119,9 +115,7 @@ bool M3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool c const std::string extension = GetExtension(pFile); if (extension == "m3d" -#ifdef M3D_ASCII || extension == "a3d" -#endif ) return true; else if (!extension.length() || checkSig) { @@ -141,9 +135,7 @@ bool M3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool c return false; } return !memcmp(data, "3DMO", 4) /* bin */ -#ifdef M3D_ASCII || !memcmp(data, "3dmo", 4) /* ASCII */ -#endif ; } return false; @@ -176,12 +168,10 @@ void M3DImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSys if (!memcmp(buffer.data(), "3DMO", 4) && memcmp(buffer.data() + 4, &fileSize, 4)) { throw DeadlyImportError("Bad binary header in file ", file, "."); } -#ifdef M3D_ASCII // make sure there's a terminator zero character, as input must be ASCIIZ if (!memcmp(buffer.data(), "3dmo", 4)) { buffer.push_back(0); } -#endif // Get the path for external assets std::string folderName("./"); diff --git a/code/AssetLib/M3D/M3DWrapper.h b/code/AssetLib/M3D/M3DWrapper.h index 5c370e607..782e908d2 100644 --- a/code/AssetLib/M3D/M3DWrapper.h +++ b/code/AssetLib/M3D/M3DWrapper.h @@ -54,7 +54,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Assimp specific M3D configuration. Comment out these defines to remove functionality //#define ASSIMP_USE_M3D_READFILECB -//#define M3D_ASCII #include "m3d.h" diff --git a/code/AssetLib/M3D/m3d.h b/code/AssetLib/M3D/m3d.h index dfc30aec3..11398cfab 100644 --- a/code/AssetLib/M3D/m3d.h +++ b/code/AssetLib/M3D/m3d.h @@ -231,14 +231,9 @@ enum { typedef struct { uint8_t format; uint8_t id; -#ifdef M3D_ASCII #define M3D_PROPERTYDEF(f, i, n) \ { (f), (i), (char *)(n) } char *key; -#else -#define M3D_PROPERTYDEF(f, i, n) \ - { (f), (i) } -#endif } m3dpd_t; /* material property types */ @@ -376,18 +371,11 @@ enum { #define M3D_CMDMAXARG 8 /* if you increase this, add more arguments to the macro below */ typedef struct { -#ifdef M3D_ASCII #define M3D_CMDDEF(t, n, p, a, b, c, d, e, f, g, h) \ { \ (char *)(n), (p), { (a), (b), (c), (d), (e), (f), (g), (h) } \ } char *key; -#else -#define M3D_CMDDEF(t, n, p, a, b, c, d, e, f, g, h) \ - { \ - (p), { (a), (b), (c), (d), (e), (f), (g), (h) } \ - } -#endif uint8_t p; uint8_t a[M3D_CMDMAXARG]; } m3dcd_t; @@ -2059,15 +2047,13 @@ unsigned char *_m3dstbi_zlib_compress(unsigned char *data, int data_len, int *ou #define M3D_CHUNKMAGIC(m, a, b, c, d) ((m)[0] == (a) && (m)[1] == (b) && (m)[2] == (c) && (m)[3] == (d)) -#ifdef M3D_ASCII #include /* sprintf and strtod cares about number locale */ #include /* get sprintf */ -#endif #ifdef M3D_PROFILING #include #endif -#if !defined(M3D_NOIMPORTER) && defined(M3D_ASCII) +#if !defined(M3D_NOIMPORTER) /* helper functions for the ASCII parser */ static char *_m3d_findarg(char *s) { while (s && *s && *s != ' ' && *s != '\t' && *s != '\r' && *s != '\n') @@ -2118,7 +2104,7 @@ static char *_m3d_getfloat(char *s, M3D_FLOAT *ret) { return _m3d_findarg(e); } #endif -#if !defined(M3D_NODUP) && (!defined(M3D_NOIMPORTER) || defined(M3D_ASCII) || defined(M3D_EXPORTER)) +#if !defined(M3D_NODUP) && (!defined(M3D_NOIMPORTER) || defined(M3D_EXPORTER)) /* helper function to create safe strings */ char *_m3d_safestr(char *in, int morelines) { char *out, *o, *i = in; @@ -2426,21 +2412,17 @@ m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d #ifndef M3D_NOWEIGHTS m3ds_t *sk; #endif -#ifdef M3D_ASCII m3ds_t s; M3D_INDEX bi[M3D_BONEMAXLEVEL + 1], level; const char *ol; char *ptr, *pe, *fn; -#endif #ifdef M3D_PROFILING struct timeval tv0, tv1, tvd; gettimeofday(&tv0, NULL); #endif if (!data || (!M3D_CHUNKMAGIC(data, '3', 'D', 'M', 'O') -#ifdef M3D_ASCII && !M3D_CHUNKMAGIC(data, '3', 'd', 'm', 'o') -#endif )) return NULL; model = (m3d_t *)M3D_MALLOC(sizeof(m3d_t)); @@ -2457,7 +2439,6 @@ m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d model->texture = mtllib->texture; model->flags |= M3D_FLG_MTLLIB; } -#ifdef M3D_ASCII /* ASCII variant? */ if (M3D_CHUNKMAGIC(data, '3', 'd', 'm', 'o')) { model->errcode = M3D_ERR_BADFILE; @@ -3034,7 +3015,6 @@ m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d setlocale(LC_NUMERIC, ol); goto postprocess; } -#endif /* Binary variant */ if (!M3D_CHUNKMAGIC(data + 8, 'H', 'E', 'A', 'D')) { buff = (unsigned char *)stbi_zlib_decode_malloc_guesssize_headerflag((const char *)data + 8, ((m3dchunk_t *)data)->length - 8, @@ -3698,9 +3678,7 @@ m3d_t *m3d_load(unsigned char *data, m3dread_t readfilecb, m3dfree_t freecb, m3d } } /* calculate normals, normalize skin weights, create bone/vertex cross-references and calculate transform matrices */ -#ifdef M3D_ASCII postprocess: -#endif if (model) { M3D_LOG("Post-process"); #ifdef M3D_PROFILING @@ -3989,7 +3967,6 @@ void m3d_free(m3d_t *model) { unsigned int i, j; if (!model) return; -#ifdef M3D_ASCII /* if model imported from ASCII, we have to free all strings as well */ if (model->flags & M3D_FLG_FREESTR) { if (model->name) M3D_FREE(model->name); @@ -4047,7 +4024,6 @@ void m3d_free(m3d_t *model) { if (model->preview.data) M3D_FREE(model->preview.data); } -#endif if (model->flags & M3D_FLG_FREERAW) M3D_FREE(model->raw); if (model->tmap) M3D_FREE(model->tmap); @@ -4315,7 +4291,6 @@ static void _m3d_round(int quality, m3dv_t *src, m3dv_t *dst) { if (dst->w == (M3D_FLOAT)-0.0) dst->w = (M3D_FLOAT)0.0; } -#ifdef M3D_ASCII /* add a bone to ascii output */ static char *_m3d_prtbone(char *ptr, m3db_t *bone, M3D_INDEX numbone, M3D_INDEX parent, uint32_t level, M3D_INDEX *vrtxidx) { uint32_t i, j; @@ -4334,16 +4309,13 @@ static char *_m3d_prtbone(char *ptr, m3db_t *bone, M3D_INDEX numbone, M3D_INDEX } return ptr; } -#endif /** * Function to encode an in-memory model into on storage Model 3D format */ unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size) { -#ifdef M3D_ASCII const char *ol; char *ptr; -#endif char vc_s, vi_s, si_s, ci_s, ti_s, bi_s, nb_s, sk_s, fc_s, hi_s, fi_s; char *sn = NULL, *sl = NULL, *sa = NULL, *sd = NULL; unsigned char *out = NULL, *z = NULL, weights[M3D_NUMBONE], *norm = NULL; @@ -4369,9 +4341,7 @@ unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size return NULL; } model->errcode = M3D_SUCCESS; -#ifdef M3D_ASCII if (flags & M3D_EXP_ASCII) quality = M3D_EXP_DOUBLE; -#endif vrtxidx = (M3D_INDEX *)M3D_MALLOC(model->numvertex * sizeof(M3D_INDEX)); if (!vrtxidx) goto memerr; memset(vrtxidx, 255, model->numvertex * sizeof(M3D_INDEX)); @@ -4800,7 +4770,6 @@ unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size } M3D_LOG("Serializing model"); -#ifdef M3D_ASCII if (flags & M3D_EXP_ASCII) { /* use CRLF to make model creators on Win happy... */ sd = _m3d_safestr(model->desc, 1); @@ -5073,7 +5042,7 @@ unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size ptr += sprintf(ptr, "\r\n"); } /* mathematical shapes face */ - if (model->numshape !(flags & M3D_EXP_NOFACE)) { + if (model->numshape != (flags & M3D_EXP_NOFACE)) { for (j = 0; j < model->numshape; j++) { sn = _m3d_safestr(model->shape[j].name, 0); if (!sn) { @@ -5287,7 +5256,6 @@ unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size if (!out) goto memerr; out[len] = 0; } else -#endif { /* stricly only use LF (newline) in binary */ sd = _m3d_safestr(model->desc, 3); From e51bb1e77e5d26aaaa40ef9bcffb196aa3049dfb Mon Sep 17 00:00:00 2001 From: contriteobserver Date: Fri, 30 Apr 2021 21:26:57 -0700 Subject: [PATCH 032/335] fixed signed/unsigned mismatch warning --- code/AssetLib/M3D/m3d.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/M3D/m3d.h b/code/AssetLib/M3D/m3d.h index 11398cfab..7dcb29593 100644 --- a/code/AssetLib/M3D/m3d.h +++ b/code/AssetLib/M3D/m3d.h @@ -5042,7 +5042,7 @@ unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size ptr += sprintf(ptr, "\r\n"); } /* mathematical shapes face */ - if (model->numshape != (flags & M3D_EXP_NOFACE)) { + if (model->numshape != (M3D_INDEX)(flags & M3D_EXP_NOFACE)) { for (j = 0; j < model->numshape; j++) { sn = _m3d_safestr(model->shape[j].name, 0); if (!sn) { From 813b64ef520a8b35548684e0bc42362bdb386e01 Mon Sep 17 00:00:00 2001 From: contriteobserver Date: Fri, 30 Apr 2021 21:51:02 -0700 Subject: [PATCH 033/335] corrected M3D_EXP_NOFACE test --- code/AssetLib/M3D/m3d.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/M3D/m3d.h b/code/AssetLib/M3D/m3d.h index 7dcb29593..68265959e 100644 --- a/code/AssetLib/M3D/m3d.h +++ b/code/AssetLib/M3D/m3d.h @@ -5042,7 +5042,7 @@ unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size ptr += sprintf(ptr, "\r\n"); } /* mathematical shapes face */ - if (model->numshape != (M3D_INDEX)(flags & M3D_EXP_NOFACE)) { + if (model->numshape && (!(flags & M3D_EXP_NOFACE))) { for (j = 0; j < model->numshape; j++) { sn = _m3d_safestr(model->shape[j].name, 0); if (!sn) { From eab1c9c3c04bb8e9244f485684f7d3ed5b79a15a Mon Sep 17 00:00:00 2001 From: Garux Date: Sat, 1 May 2021 00:27:28 +0300 Subject: [PATCH 034/335] add `AI_CONFIG_IMPORT_MD3_LOAD_SHADERS` bool option the purpose is use of this loader with idtech3 FS this requires full original material name, which euqals to Q3 shader path result of deduction is not usable inside Q3 FS at all option in general is "do not tinker with the path" --- code/AssetLib/MD3/MD3Loader.cpp | 14 ++++++++++++-- code/AssetLib/MD3/MD3Loader.h | 3 +++ include/assimp/config.h.in | 13 +++++++++++-- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/code/AssetLib/MD3/MD3Loader.cpp b/code/AssetLib/MD3/MD3Loader.cpp index 87fe2509c..53a0eea41 100644 --- a/code/AssetLib/MD3/MD3Loader.cpp +++ b/code/AssetLib/MD3/MD3Loader.cpp @@ -450,6 +450,9 @@ void MD3Importer::SetupProperties(const Importer *pImp) { // AI_CONFIG_IMPORT_MD3_SKIN_NAME configSkinFile = (pImp->GetPropertyString(AI_CONFIG_IMPORT_MD3_SKIN_NAME, "default")); + // AI_CONFIG_IMPORT_MD3_LOAD_SHADERS + configLoadShaders = (pImp->GetPropertyBool(AI_CONFIG_IMPORT_MD3_LOAD_SHADERS, true)); + // AI_CONFIG_IMPORT_MD3_SHADER_SRC configShaderFile = (pImp->GetPropertyString(AI_CONFIG_IMPORT_MD3_SHADER_SRC, "")); @@ -781,7 +784,9 @@ void MD3Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy // And check whether we can locate a shader file for this model Q3Shader::ShaderData shaders; - ReadShader(shaders); + if (configLoadShaders){ + ReadShader(shaders); + } // Adjust all texture paths in the shader const char *header_name = pcHeader->NAME; @@ -863,7 +868,12 @@ void MD3Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy std::string convertedPath; if (texture_name) { - ConvertPath(texture_name, header_name, convertedPath); + if (configLoadShaders){ + ConvertPath(texture_name, header_name, convertedPath); + } + else{ + convertedPath = texture_name; + } } const Q3Shader::ShaderDataBlock *shader = nullptr; diff --git a/code/AssetLib/MD3/MD3Loader.h b/code/AssetLib/MD3/MD3Loader.h index d34df2d77..1a4e5982c 100644 --- a/code/AssetLib/MD3/MD3Loader.h +++ b/code/AssetLib/MD3/MD3Loader.h @@ -297,6 +297,9 @@ protected: /** Configuration option: name of skin file to be read */ std::string configSkinFile; + /** Configuration option: whether to load shaders */ + bool configLoadShaders; + /** Configuration option: name or path of shader */ std::string configShaderFile; diff --git a/include/assimp/config.h.in b/include/assimp/config.h.in index d78568da7..016cabbad 100644 --- a/include/assimp/config.h.in +++ b/include/assimp/config.h.in @@ -669,7 +669,7 @@ enum aiComponent // --------------------------------------------------------------------------- /** @brief Set wether the importer shall not remove empty bones. - * + * * Empty bone are often used to define connections for other models. */ #define AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES \ @@ -854,6 +854,15 @@ enum aiComponent #define AI_CONFIG_IMPORT_MD3_SKIN_NAME \ "IMPORT_MD3_SKIN_NAME" +// --------------------------------------------------------------------------- +/** @brief Specify if to try load Quake 3 shader files. This also controls + * original surface name handling: when disabled it will be used unchanged. + * + * Property type: bool. Default value: true. + */ +#define AI_CONFIG_IMPORT_MD3_LOAD_SHADERS \ + "IMPORT_MD3_LOAD_SHADERS" + // --------------------------------------------------------------------------- /** @brief Specify the Quake 3 shader file to be used for a particular * MD3 file. This can also be a search path. @@ -1058,7 +1067,7 @@ enum aiComponent #define AI_CONFIG_EXPORT_XFILE_64BIT "EXPORT_XFILE_64BIT" /** @brief Specifies whether the assimp export shall be able to export point clouds - * + * * When this flag is not defined the render data has to contain valid faces. * Point clouds are only a collection of vertices which have nor spatial organization * by a face and the validation process will remove them. Enabling this feature will From c2d3d22271cb81aacce318df85a1cf8be1ad8122 Mon Sep 17 00:00:00 2001 From: Jason C Date: Sat, 1 May 2021 10:58:29 -0400 Subject: [PATCH 035/335] Fix crash in CanRead when file can not be opened. Addresses #3849 --- code/AssetLib/M3D/M3DImporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/M3D/M3DImporter.cpp b/code/AssetLib/M3D/M3DImporter.cpp index 8cbda23cb..91fa54b75 100644 --- a/code/AssetLib/M3D/M3DImporter.cpp +++ b/code/AssetLib/M3D/M3DImporter.cpp @@ -137,7 +137,7 @@ bool M3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool c */ std::unique_ptr pStream(pIOHandler->Open(pFile, "rb")); unsigned char data[4]; - if (4 != pStream->Read(data, 1, 4)) { + if (!pStream || 4 != pStream->Read(data, 1, 4)) { return false; } return !memcmp(data, "3DMO", 4) /* bin */ From 65a2b98b867a3dece8b2428c0defe8acb7f4fafd Mon Sep 17 00:00:00 2001 From: Krishty Date: Sat, 1 May 2021 18:46:12 +0200 Subject: [PATCH 036/335] updated C4D importer to use the Cineware SDK MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Maxon’s Melange SDK has been renamed Cineware SDK as of 21.004, and with it all namespaces and types. This commit - makes CMake use contrib/Cineware instead of contrib/Melange; - renames Assimp’s namespace melange to namespace cineware; - removes useless functions and formatter references from class C4DImporter; - removes duplicate conversion of cineware::String to aiString in the importer; - updates comments accordingly; - updates copyright info. --- CMakeLists.txt | 15 ++++---- code/AssetLib/C4D/C4DImporter.cpp | 64 +++++++++++-------------------- code/AssetLib/C4D/C4DImporter.h | 39 ++++++++----------- 3 files changed, 46 insertions(+), 72 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 97a3641f5..7027e3300 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -527,12 +527,12 @@ ENDIF() MARK_AS_ADVANCED ( ASSIMP_BUILD_ARCHITECTURE ASSIMP_BUILD_COMPILER ) SET ( ASSIMP_BUILD_NONFREE_C4D_IMPORTER OFF CACHE BOOL - "Build the C4D importer, which relies on the non-free Melange SDK." + "Build the C4D importer, which relies on the non-free Cineware SDK." ) IF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER) IF ( MSVC ) - SET(C4D_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/contrib/Melange/includes") + SET(C4D_INCLUDES "${CMAKE_CURRENT_SOURCE_DIR}/contrib/Cineware/includes") # pick the correct prebuilt library IF(MSVC15) @@ -551,22 +551,23 @@ IF (ASSIMP_BUILD_NONFREE_C4D_IMPORTER) ) ENDIF() - SET(C4D_LIB_BASE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/contrib/Melange/libraries/win") + SET(C4D_LIB_BASE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/contrib/Cineware/libraries/win") SET(C4D_DEBUG_LIBRARIES - "${C4D_LIB_BASE_PATH}/melangelib${C4D_LIB_POSTFIX}/melangelib_debug.lib" + "${C4D_LIB_BASE_PATH}/cinewarelib${C4D_LIB_POSTFIX}/cinewarelib_debug.lib" "${C4D_LIB_BASE_PATH}/jpeglib${C4D_LIB_POSTFIX}/jpeglib_debug.lib" ) SET(C4D_RELEASE_LIBRARIES - "${C4D_LIB_BASE_PATH}/melangelib${C4D_LIB_POSTFIX}/melangelib_release.lib" + "${C4D_LIB_BASE_PATH}/cinewarelib${C4D_LIB_POSTFIX}/cinewarelib_release.lib" "${C4D_LIB_BASE_PATH}/jpeglib${C4D_LIB_POSTFIX}/jpeglib_release.lib" ) - # winsock and winmm are necessary dependencies of melange (this is undocumented, but true.) + # winsock and winmm are necessary (and undocumented) dependencies of Cineware SDK because + # it can be used to communicate with a running Cinema 4D instance SET(C4D_EXTRA_LIBRARIES WSock32.lib Winmm.lib) ELSE () MESSAGE( FATAL_ERROR - "C4D is currently only available on Windows with melange SDK installed in contrib/Melange" + "C4D is currently only available on Windows with Cineware SDK installed in contrib/Cineware" ) ENDIF () ELSE () diff --git a/code/AssetLib/C4D/C4DImporter.cpp b/code/AssetLib/C4D/C4DImporter.cpp index 24fd6f622..594bcfddd 100644 --- a/code/AssetLib/C4D/C4DImporter.cpp +++ b/code/AssetLib/C4D/C4DImporter.cpp @@ -2,7 +2,7 @@ Open Asset Import Library (assimp) ---------------------------------------------------------------------- -Copyright (c) 2006-2020, assimp team +Copyright (c) 2006-2021, assimp team All rights reserved. Redistribution and use of this software in source and binary forms, @@ -51,7 +51,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endif #include "C4DImporter.h" -#include #include #include #include @@ -65,7 +64,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "c4d_file.h" #include "default_alien_overloads.h" -using namespace melange; +namespace { + +aiString aiStringFrom(cineware::String const & cinestring) { + aiString result; + cinestring.GetCString(result.data, MAXLEN-1); + result.length = static_cast(cinestring.GetLength()); + return result; +} + +} + +using namespace Assimp; +using namespace cineware; // overload this function and fill in your own unique data void GetWriterInfo(int &id, String &appname) { @@ -73,9 +84,6 @@ void GetWriterInfo(int &id, String &appname) { appname = "Open Asset Import Library"; } -using namespace Assimp; -using namespace Assimp::Formatter; - namespace Assimp { template<> const char* LogFunctions::Prefix() { static auto prefix = "C4D: "; @@ -97,17 +105,6 @@ static const aiImporterDesc desc = { }; -// ------------------------------------------------------------------------------------------------ -C4DImporter::C4DImporter() -: BaseImporter() { - // empty -} - -// ------------------------------------------------------------------------------------------------ -C4DImporter::~C4DImporter() { - // empty -} - // ------------------------------------------------------------------------------------------------ bool C4DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const { const std::string& extension = GetExtension(pFile); @@ -125,11 +122,6 @@ const aiImporterDesc* C4DImporter::GetInfo () const { return &desc; } -// ------------------------------------------------------------------------------------------------ -void C4DImporter::SetupProperties(const Importer* /*pImp*/) { - // nothing to be done for the moment -} - // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. @@ -199,8 +191,8 @@ void C4DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS // ------------------------------------------------------------------------------------------------ -bool C4DImporter::ReadShader(aiMaterial* out, melange::BaseShader* shader) { - // based on Melange sample code (C4DImportExport.cpp) +bool C4DImporter::ReadShader(aiMaterial* out, BaseShader* shader) { + // based on Cineware sample code (C4DImportExport.cpp) while(shader) { if(shader->GetType() == Xlayer) { BaseContainer* container = shader->GetDataInstance(); @@ -242,9 +234,7 @@ bool C4DImporter::ReadShader(aiMaterial* out, melange::BaseShader* shader) { lsl = lsl->GetNext(); } } else if ( shader->GetType() == Xbitmap ) { - aiString path; - shader->GetFileName().GetString().GetCString(path.data, MAXLEN-1); - path.length = ::strlen(path.data); + auto const path = aiStringFrom(shader->GetFileName().GetString()); out->AddProperty(&path, AI_MATKEY_TEXTURE_DIFFUSE(0)); return true; } else { @@ -257,18 +247,15 @@ bool C4DImporter::ReadShader(aiMaterial* out, melange::BaseShader* shader) { } // ------------------------------------------------------------------------------------------------ -void C4DImporter::ReadMaterials(melange::BaseMaterial* mat) { - // based on Melange sample code +void C4DImporter::ReadMaterials(BaseMaterial* mat) { + // based on Cineware sample code while (mat) { - const String& name = mat->GetName(); if (mat->GetType() == Mmaterial) { aiMaterial* out = new aiMaterial(); material_mapping[mat] = static_cast(materials.size()); materials.push_back(out); - aiString ai_name; - name.GetCString(ai_name.data, MAXLEN-1); - ai_name.length = ::strlen(ai_name.data); + auto const ai_name = aiStringFrom(mat->GetName()); out->AddProperty(&ai_name, AI_MATKEY_NAME); Material& m = dynamic_cast(*mat); @@ -305,19 +292,15 @@ void C4DImporter::RecurseHierarchy(BaseObject* object, aiNode* parent) { ai_assert(parent != nullptr ); std::vector nodes; - // based on Melange sample code + // based on Cineware sample code while (object) { - const String& name = object->GetName(); const LONG type = object->GetType(); const Matrix& ml = object->GetMl(); - aiString string; - name.GetCString(string.data, MAXLEN-1); - string.length = ::strlen(string.data); aiNode* const nd = new aiNode(); nd->mParent = parent; - nd->mName = string; + nd->mName = aiStringFrom(object->GetName()); nd->mTransformation.a1 = ml.v1.x; nd->mTransformation.b1 = ml.v1.y; @@ -370,7 +353,7 @@ aiMesh* C4DImporter::ReadMesh(BaseObject* object) { ai_assert(object != nullptr); ai_assert( object->GetType() == Opolygon ); - // based on Melange sample code + // based on Cineware sample code PolygonObject* const polyObject = dynamic_cast(object); ai_assert(polyObject != nullptr); @@ -618,4 +601,3 @@ unsigned int C4DImporter::ResolveMaterial(PolygonObject* obj) { } #endif // ASSIMP_BUILD_NO_C4D_IMPORTER - diff --git a/code/AssetLib/C4D/C4DImporter.h b/code/AssetLib/C4D/C4DImporter.h index f9406c3e0..c44cf5e37 100644 --- a/code/AssetLib/C4D/C4DImporter.h +++ b/code/AssetLib/C4D/C4DImporter.h @@ -2,7 +2,7 @@ Open Asset Import Library (assimp) ---------------------------------------------------------------------- -Copyright (c) 2006-2020, assimp team +Copyright (c) 2006-2021, assimp team All rights reserved. Redistribution and use of this software in source and binary forms, @@ -56,8 +56,8 @@ struct aiMaterial; struct aiImporterDesc; -namespace melange { - class BaseObject; // c4d_file.h +namespace cineware { + class BaseObject; class PolygonObject; class BaseMaterial; class BaseShader; @@ -71,43 +71,34 @@ namespace Assimp { } // ------------------------------------------------------------------------------------------- -/** Importer class to load Cinema4D files using the Melange library to be obtained from - * www.plugincafe.com +/** Importer class to load Cinema4D files using the Cineware library to be obtained from + * https://developers.maxon.net * - * Note that Melange is not free software. */ + * Note that Cineware is not free software. */ // ------------------------------------------------------------------------------------------- class C4DImporter : public BaseImporter, public LogFunctions { public: - C4DImporter(); - ~C4DImporter(); - bool CanRead( const std::string& pFile, IOSystem* pIOHandler, - bool checkSig) const; + bool CanRead( const std::string& pFile, IOSystem*, bool checkSig) const override; protected: - // -------------------- - const aiImporterDesc* GetInfo () const; + const aiImporterDesc* GetInfo () const override; - // -------------------- - void SetupProperties(const Importer* pImp); - - // -------------------- - void InternReadFile( const std::string& pFile, aiScene* pScene, - IOSystem* pIOHandler); + void InternReadFile( const std::string& pFile, aiScene*, IOSystem* ) override; private: - void ReadMaterials(melange::BaseMaterial* mat); - void RecurseHierarchy(melange::BaseObject* object, aiNode* parent); - aiMesh* ReadMesh(melange::BaseObject* object); - unsigned int ResolveMaterial(melange::PolygonObject* obj); + void ReadMaterials(cineware::BaseMaterial* mat); + void RecurseHierarchy(cineware::BaseObject* object, aiNode* parent); + aiMesh* ReadMesh(cineware::BaseObject* object); + unsigned int ResolveMaterial(cineware::PolygonObject* obj); - bool ReadShader(aiMaterial* out, melange::BaseShader* shader); + bool ReadShader(aiMaterial* out, cineware::BaseShader* shader); std::vector meshes; std::vector materials; - typedef std::map MaterialMap; + typedef std::map MaterialMap; MaterialMap material_mapping; }; // !class C4DImporter From e73a2ed5e0b044033ed6870715bc80e77ca36393 Mon Sep 17 00:00:00 2001 From: Krishty Date: Sat, 1 May 2021 23:20:37 +0200 Subject: [PATCH 037/335] style fix: namespace instead of class with public static members --- code/AssetLib/3DS/3DSHelper.h | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/code/AssetLib/3DS/3DSHelper.h b/code/AssetLib/3DS/3DSHelper.h index f3f9efb25..1930c0c40 100644 --- a/code/AssetLib/3DS/3DSHelper.h +++ b/code/AssetLib/3DS/3DSHelper.h @@ -61,20 +61,10 @@ namespace D3DS { #include // --------------------------------------------------------------------------- -/** Discreet3DS class: Helper class for loading 3ds files. Defines chunks -* and data structures. +/** Defines chunks and data structures. */ -class Discreet3DS { -private: - Discreet3DS() AI_NO_EXCEPT { - // empty - } +namespace Discreet3DS { - ~Discreet3DS() { - // empty - } - -public: //! data structure for a single chunk in a .3ds file struct Chunk { uint16_t Flag; @@ -314,7 +304,7 @@ public: // camera sub-chunks CHUNK_CAM_RANGES = 0x4720 }; -}; +} // --------------------------------------------------------------------------- /** Helper structure representing a 3ds mesh face */ From fe5a23e1101d6c36596040878d333328a09ab026 Mon Sep 17 00:00:00 2001 From: Krishty Date: Mon, 3 May 2021 17:01:59 +0200 Subject: [PATCH 038/335] fixed bloat in SIB importer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SIB importer, upon needing an empty aiString, did not create a new one but rather copied a predefined global empty string. Since aiStrings contain large buffers, Assimp copied 1028 B of zeros instead of setting five bytes (at least when compiled with Visual C++). Since aiString is a user-defined type without a constexpr constructor, Visual C++ had to generate a thread-safe run-time initializer as well. Now it’s just two instructions. --- code/AssetLib/SIB/SIBImporter.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/code/AssetLib/SIB/SIBImporter.cpp b/code/AssetLib/SIB/SIBImporter.cpp index 6898fb65c..f73301c21 100644 --- a/code/AssetLib/SIB/SIBImporter.cpp +++ b/code/AssetLib/SIB/SIBImporter.cpp @@ -179,8 +179,7 @@ static void UnknownChunk(StreamReaderLE * /*stream*/, const SIBChunk &chunk) { // Reads a UTF-16LE string and returns it at UTF-8. static aiString ReadString(StreamReaderLE *stream, uint32_t numWChars) { if (nullptr == stream || 0 == numWChars) { - static const aiString empty; - return empty; + return aiString(); } // Allocate buffers (max expansion is 1 byte -> 4 bytes for UTF-8) From e7211790fb6307ae0e73a44a5529b709877d2f89 Mon Sep 17 00:00:00 2001 From: Tom spot Callaway Date: Mon, 3 May 2021 13:27:52 -0400 Subject: [PATCH 039/335] PBR material support --- code/AssetLib/FBX/FBXConverter.cpp | 53 ++++++++++++++++++++++++++++- code/AssetLib/FBX/FBXProperties.cpp | 2 +- include/assimp/material.h | 12 +++++++ 3 files changed, 65 insertions(+), 2 deletions(-) diff --git a/code/AssetLib/FBX/FBXConverter.cpp b/code/AssetLib/FBX/FBXConverter.cpp index cb033a651..177fa636c 100644 --- a/code/AssetLib/FBX/FBXConverter.cpp +++ b/code/AssetLib/FBX/FBXConverter.cpp @@ -2126,7 +2126,12 @@ void FBXConverter::SetShadingPropertiesCommon(aiMaterial *out_mat, const Propert const aiColor3D &Emissive = GetColorPropertyFromMaterial(props, "Emissive", ok); if (ok) { out_mat->AddProperty(&Emissive, 1, AI_MATKEY_COLOR_EMISSIVE); - } + } else { + const aiColor3D &emissiveColor = GetColorPropertyFromMaterial(props, "Maya|emissive", ok); + if (ok) { + out_mat->AddProperty(&emissiveColor, 1, AI_MATKEY_COLOR_EMISSIVE); + } + } const aiColor3D &Ambient = GetColorPropertyFromMaterial(props, "Ambient", ok); if (ok) { @@ -2207,6 +2212,52 @@ void FBXConverter::SetShadingPropertiesCommon(aiMaterial *out_mat, const Propert if (ok) { out_mat->AddProperty(&DispFactor, 1, "$mat.displacementscaling", 0, 0); } + + // PBR material information + const aiColor3D &baseColor = GetColorPropertyFromMaterial(props, "Maya|base_color", ok); + if (ok) { + out_mat->AddProperty(&baseColor, 1, AI_MATKEY_BASE_COLOR); + } + + const float useColorMap = PropertyGet(props, "Maya|use_color_map", ok); + if (ok) { + out_mat->AddProperty(&useColorMap, 1, AI_MATKEY_USE_COLOR_MAP); + } + + const float useMetallicMap = PropertyGet(props, "Maya|use_metallic_map", ok); + if (ok) { + out_mat->AddProperty(&useMetallicMap, 1, AI_MATKEY_USE_METALLIC_MAP); + } + + const float metallicFactor = PropertyGet(props, "Maya|metallic", ok); + if (ok) { + out_mat->AddProperty(&metallicFactor, 1, AI_MATKEY_METALLIC_FACTOR); + } + + const float useRoughnessMap = PropertyGet(props, "Maya|use_roughness_map", ok); + if (ok) { + out_mat->AddProperty(&useRoughnessMap, 1, AI_MATKEY_USE_ROUGHNESS_MAP); + } + + const float roughnessFactor = PropertyGet(props, "Maya|roughness", ok); + if (ok) { + out_mat->AddProperty(&roughnessFactor, 1, AI_MATKEY_ROUGHNESS_FACTOR); + } + + const float useEmissiveMap = PropertyGet(props, "Maya|use_emissive_map", ok); + if (ok) { + out_mat->AddProperty(&useEmissiveMap, 1, AI_MATKEY_USE_EMISSIVE_MAP); + } + + const float emissiveIntensity = PropertyGet(props, "Maya|emissive_intensity", ok); + if (ok) { + out_mat->AddProperty(&emissiveIntensity, 1, AI_MATKEY_EMISSIVE_INTENSITY); + } + + const float useAOMap = PropertyGet(props, "Maya|use_ao_map", ok); + if (ok) { + out_mat->AddProperty(&useAOMap, 1, AI_MATKEY_USE_AO_MAP); + } } void FBXConverter::SetShadingPropertiesRaw(aiMaterial *out_mat, const PropertyTable &props, const TextureMap &_textures, const MeshGeometry *const mesh) { diff --git a/code/AssetLib/FBX/FBXProperties.cpp b/code/AssetLib/FBX/FBXProperties.cpp index 1e4cd0ead..1a5ebffd1 100644 --- a/code/AssetLib/FBX/FBXProperties.cpp +++ b/code/AssetLib/FBX/FBXProperties.cpp @@ -131,7 +131,7 @@ Property* ReadTypedProperty(const Element& element) ParseTokenAsFloat(*tok[6])) ); } - else if (!strcmp(cs,"double") || !strcmp(cs,"Number") || !strcmp(cs,"Float") || !strcmp(cs,"FieldOfView") || !strcmp( cs, "UnitScaleFactor" ) ) { + else if (!strcmp(cs,"double") || !strcmp(cs,"Number") || !strcmp(cs,"float") || !strcmp(cs,"Float") || !strcmp(cs,"FieldOfView") || !strcmp( cs, "UnitScaleFactor" ) ) { checkTokenCount(tok, 5); return new TypedProperty(ParseTokenAsFloat(*tok[4])); } diff --git a/include/assimp/material.h b/include/assimp/material.h index f3daa62dc..08c0491c0 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -920,6 +920,18 @@ extern "C" { #define AI_MATKEY_SHADER_PRIMITIVE "?sh.ps", 0, 0 #define AI_MATKEY_SHADER_COMPUTE "?sh.cs", 0, 0 +// --------------------------------------------------------------------------- +// PBR material support +#define AI_MATKEY_USE_COLOR_MAP "$mat.useColorMap", 0, 0 +#define AI_MATKEY_BASE_COLOR "$clr.base", 0, 0 +#define AI_MATKEY_USE_METALLIC_MAP "$mat.useMetallicMap", 0, 0 +#define AI_MATKEY_METALLIC_FACTOR "$mat.metallicFactor", 0, 0 +#define AI_MATKEY_USE_ROUGHNESS_MAP "$mat.useRoughnessMap", 0, 0 +#define AI_MATKEY_ROUGHNESS_FACTOR "$mat.roughnessFactor", 0, 0 +#define AI_MATKEY_USE_EMISSIVE_MAP "$mat.useEmissiveMap", 0, 0 +#define AI_MATKEY_EMISSIVE_INTENSITY "$mat.emissiveIntensity", 0, 0 +#define AI_MATKEY_USE_AO_MAP "$mat.useAOMap", 0, 0 + // --------------------------------------------------------------------------- // Pure key names for all texture-related properties //! @cond MATS_DOC_FULL From f91b439f79cc106f08678a265562191c67b184cf Mon Sep 17 00:00:00 2001 From: Tom spot Callaway Date: Mon, 3 May 2021 13:40:31 -0400 Subject: [PATCH 040/335] preserve UV Stream names in FBX files --- code/AssetLib/Assxml/AssxmlFileWriter.cpp | 7 +++++-- code/AssetLib/FBX/FBXConverter.cpp | 2 ++ include/assimp/mesh.h | 4 ++++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/code/AssetLib/Assxml/AssxmlFileWriter.cpp b/code/AssetLib/Assxml/AssxmlFileWriter.cpp index 6d876b3aa..24fda5955 100644 --- a/code/AssetLib/Assxml/AssxmlFileWriter.cpp +++ b/code/AssetLib/Assxml/AssxmlFileWriter.cpp @@ -598,8 +598,11 @@ static void WriteDump(const char *pFile, const char *cmd, const aiScene *scene, if (!mesh->mTextureCoords[a]) break; - ioprintf(io, "\t\t \n", mesh->mNumVertices, - a, mesh->mNumUVComponents[a]); + ioprintf(io, "\t\t \n", + mesh->mNumVertices, + a, + mesh->mTextureCoordsNames[a].C_Str(), + mesh->mNumUVComponents[a]); if (!shortened) { if (mesh->mNumUVComponents[a] == 3) { diff --git a/code/AssetLib/FBX/FBXConverter.cpp b/code/AssetLib/FBX/FBXConverter.cpp index cb033a651..4778f9b07 100644 --- a/code/AssetLib/FBX/FBXConverter.cpp +++ b/code/AssetLib/FBX/FBXConverter.cpp @@ -1126,6 +1126,8 @@ unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, c *out_uv++ = aiVector3D(v.x, v.y, 0.0f); } + out_mesh->mTextureCoordsNames[i] = mesh.GetTextureCoordChannelName(i); + out_mesh->mNumUVComponents[i] = 2; } diff --git a/include/assimp/mesh.h b/include/assimp/mesh.h index 427dba008..989ed3800 100644 --- a/include/assimp/mesh.h +++ b/include/assimp/mesh.h @@ -674,6 +674,10 @@ struct aiMesh { */ C_STRUCT aiVector3D *mTextureCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; + /** Vertex stream names. + */ + C_STRUCT aiString mTextureCoordsNames[AI_MAX_NUMBER_OF_TEXTURECOORDS]; + /** Specifies the number of components for a given UV channel. * Up to three channels are supported (UVW, for accessing volume * or cube maps). If the value is 2 for a given channel n, the From 55fd820ed782e97a76925e0926dfa021baa1c6f5 Mon Sep 17 00:00:00 2001 From: kkulling Date: Tue, 4 May 2021 10:57:30 +0200 Subject: [PATCH 041/335] use const chars --- code/AssetLib/3DS/3DSExporter.cpp | 5 +- code/AssetLib/3MF/3MFXmlTags.h | 101 +++++------ code/AssetLib/3MF/D3MFExporter.cpp | 37 ++-- code/AssetLib/3MF/D3MFImporter.cpp | 164 +++++++++--------- code/AssetLib/3MF/D3MFOpcPackage.cpp | 6 +- code/AssetLib/AMF/AMFImporter_Postprocess.cpp | 4 +- code/CApi/CInterfaceIOWrapper.cpp | 4 +- contrib/zip/src/zip.c | 2 +- 8 files changed, 172 insertions(+), 151 deletions(-) diff --git a/code/AssetLib/3DS/3DSExporter.cpp b/code/AssetLib/3DS/3DSExporter.cpp index 108d917d1..07e9ccfc7 100644 --- a/code/AssetLib/3DS/3DSExporter.cpp +++ b/code/AssetLib/3DS/3DSExporter.cpp @@ -102,13 +102,14 @@ private: // preserves the mesh's given name if it has one. |index| is the index // of the mesh in |aiScene::mMeshes|. std::string GetMeshName(const aiMesh &mesh, unsigned int index, const aiNode &node) { - static const std::string underscore = "_"; + static const char underscore = '_'; char postfix[10] = { 0 }; ASSIMP_itoa10(postfix, index); std::string result = node.mName.C_Str(); if (mesh.mName.length > 0) { - result += underscore + mesh.mName.C_Str(); + result += underscore; + result += mesh.mName.C_Str(); } return result + underscore + postfix; } diff --git a/code/AssetLib/3MF/3MFXmlTags.h b/code/AssetLib/3MF/3MFXmlTags.h index 3996c60e5..910006bc9 100644 --- a/code/AssetLib/3MF/3MFXmlTags.h +++ b/code/AssetLib/3MF/3MFXmlTags.h @@ -44,62 +44,65 @@ namespace Assimp { namespace D3MF { namespace XmlTag { + // Root tag + static const char *RootTag = "3MF"; + // Meta-data - static const std::string meta = "metadata"; - static const std::string meta_name = "name"; + static const char *meta = "metadata"; + static const char *meta_name = "name"; // Model-data specific tags - static const std::string model = "model"; - static const std::string model_unit = "unit"; - static const std::string metadata = "metadata"; - static const std::string resources = "resources"; - static const std::string object = "object"; - static const std::string mesh = "mesh"; - static const std::string components = "components"; - static const std::string component = "component"; - static const std::string vertices = "vertices"; - static const std::string vertex = "vertex"; - static const std::string triangles = "triangles"; - static const std::string triangle = "triangle"; - static const std::string x = "x"; - static const std::string y = "y"; - static const std::string z = "z"; - static const std::string v1 = "v1"; - static const std::string v2 = "v2"; - static const std::string v3 = "v3"; - static const std::string id = "id"; - static const std::string pid = "pid"; - static const std::string pindex = "pindex"; - static const std::string p1 = "p1"; - static const std::string name = "name"; - static const std::string type = "type"; - static const std::string build = "build"; - static const std::string item = "item"; - static const std::string objectid = "objectid"; - static const std::string transform = "transform"; + static const char *model = "model"; + static const char *model_unit = "unit"; + static const char *metadata = "metadata"; + static const char *resources = "resources"; + static const char *object = "object"; + static const char *mesh = "mesh"; + static const char *components = "components"; + static const char *component = "component"; + static const char *vertices = "vertices"; + static const char *vertex = "vertex"; + static const char *triangles = "triangles"; + static const char *triangle = "triangle"; + static const char *x = "x"; + static const char *y = "y"; + static const char *z = "z"; + static const char *v1 = "v1"; + static const char *v2 = "v2"; + static const char *v3 = "v3"; + static const char *id = "id"; + static const char *pid = "pid"; + static const char *pindex = "pindex"; + static const char *p1 = "p1"; + static const char *name = "name"; + static const char *type = "type"; + static const char *build = "build"; + static const char *item = "item"; + static const char *objectid = "objectid"; + static const char *transform = "transform"; // Material definitions - static const std::string basematerials = "basematerials"; - static const std::string basematerials_id = "id"; - static const std::string basematerials_base = "base"; - static const std::string basematerials_name = "name"; - static const std::string basematerials_displaycolor = "displaycolor"; + static const char *basematerials = "basematerials"; + static const char *basematerials_id = "id"; + static const char *basematerials_base = "base"; + static const char *basematerials_name = "name"; + static const char *basematerials_displaycolor = "displaycolor"; // Meta info tags - static const std::string CONTENT_TYPES_ARCHIVE = "[Content_Types].xml"; - static const std::string ROOT_RELATIONSHIPS_ARCHIVE = "_rels/.rels"; - static const std::string SCHEMA_CONTENTTYPES = "http://schemas.openxmlformats.org/package/2006/content-types"; - static const std::string SCHEMA_RELATIONSHIPS = "http://schemas.openxmlformats.org/package/2006/relationships"; - static const std::string RELS_RELATIONSHIP_CONTAINER = "Relationships"; - static const std::string RELS_RELATIONSHIP_NODE = "Relationship"; - static const std::string RELS_ATTRIB_TARGET = "Target"; - static const std::string RELS_ATTRIB_TYPE = "Type"; - static const std::string RELS_ATTRIB_ID = "Id"; - static const std::string PACKAGE_START_PART_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel"; - static const std::string PACKAGE_PRINT_TICKET_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/printticket"; - static const std::string PACKAGE_TEXTURE_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dtexture"; - static const std::string PACKAGE_CORE_PROPERTIES_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"; - static const std::string PACKAGE_THUMBNAIL_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"; + static const char *CONTENT_TYPES_ARCHIVE = "[Content_Types].xml"; + static const char *ROOT_RELATIONSHIPS_ARCHIVE = "_rels/.rels"; + static const char *SCHEMA_CONTENTTYPES = "http://schemas.openxmlformats.org/package/2006/content-types"; + static const char *SCHEMA_RELATIONSHIPS = "http://schemas.openxmlformats.org/package/2006/relationships"; + static const char *RELS_RELATIONSHIP_CONTAINER = "Relationships"; + static const char *RELS_RELATIONSHIP_NODE = "Relationship"; + static const char *RELS_ATTRIB_TARGET = "Target"; + static const char *RELS_ATTRIB_TYPE = "Type"; + static const char *RELS_ATTRIB_ID = "Id"; + static const char *PACKAGE_START_PART_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel"; + static const char *PACKAGE_PRINT_TICKET_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/printticket"; + static const char *PACKAGE_TEXTURE_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dtexture"; + static const char *PACKAGE_CORE_PROPERTIES_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"; + static const char *PACKAGE_THUMBNAIL_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"; } } // Namespace D3MF diff --git a/code/AssetLib/3MF/D3MFExporter.cpp b/code/AssetLib/3MF/D3MFExporter.cpp index 4a16a0ad3..76a601cbe 100644 --- a/code/AssetLib/3MF/D3MFExporter.cpp +++ b/code/AssetLib/3MF/D3MFExporter.cpp @@ -307,18 +307,26 @@ void D3MFExporter::writeMesh(aiMesh *mesh) { return; } - mModelOutput << "<" << XmlTag::mesh << ">" << std::endl; - mModelOutput << "<" << XmlTag::vertices << ">" << std::endl; + mModelOutput << "<" + << XmlTag::mesh + << ">" << "\n"; + mModelOutput << "<" + << XmlTag::vertices + << ">" << "\n"; for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { writeVertex(mesh->mVertices[i]); } - mModelOutput << "" << std::endl; + mModelOutput << "" + << "\n"; const unsigned int matIdx(mesh->mMaterialIndex); writeFaces(mesh, matIdx); - mModelOutput << "" << std::endl; + mModelOutput << "" + << "\n"; } void D3MFExporter::writeVertex(const aiVector3D &pos) { @@ -334,27 +342,34 @@ void D3MFExporter::writeFaces(aiMesh *mesh, unsigned int matIdx) { if (!mesh->HasFaces()) { return; } - mModelOutput << "<" << XmlTag::triangles << ">" << std::endl; + mModelOutput << "<" + << XmlTag::triangles << ">" + << "\n"; for (unsigned int i = 0; i < mesh->mNumFaces; ++i) { aiFace ¤tFace = mesh->mFaces[i]; mModelOutput << "<" << XmlTag::triangle << " v1=\"" << currentFace.mIndices[0] << "\" v2=\"" << currentFace.mIndices[1] << "\" v3=\"" << currentFace.mIndices[2] << "\" pid=\"1\" p1=\"" + ai_to_string(matIdx) + "\" />"; - mModelOutput << std::endl; + mModelOutput << "\n"; } - mModelOutput << ""; - mModelOutput << std::endl; + mModelOutput << ""; + mModelOutput << "\n"; } void D3MFExporter::writeBuild() { - mModelOutput << "<" << XmlTag::build << ">" << std::endl; + mModelOutput << "<" + << XmlTag::build + << ">" + << "\n"; for (size_t i = 0; i < mBuildItems.size(); ++i) { mModelOutput << "<" << XmlTag::item << " objectid=\"" << i + 2 << "\"/>"; - mModelOutput << std::endl; + mModelOutput << "\n"; } mModelOutput << ""; - mModelOutput << std::endl; + mModelOutput << "\n"; } void D3MFExporter::zipContentType(const std::string &filename) { diff --git a/code/AssetLib/3MF/D3MFImporter.cpp b/code/AssetLib/3MF/D3MFImporter.cpp index f4ddb6054..b2d421610 100644 --- a/code/AssetLib/3MF/D3MFImporter.cpp +++ b/code/AssetLib/3MF/D3MFImporter.cpp @@ -72,32 +72,39 @@ enum class ResourceType { RT_Unknown }; // To be extended with other resource types (eg. material extension resources like Texture2d, Texture2dGroup...) -class Resource -{ +class Resource { public: - Resource(int id) : - mId(id) {} - - virtual ~Resource() {} - int mId; - virtual ResourceType getType() { + Resource(int id) : + mId(id) { + // empty + } + + virtual ~Resource() { + // empty + } + + virtual ResourceType getType() const { return ResourceType::RT_Unknown; } }; class BaseMaterials : public Resource { public: - BaseMaterials(int id) : - Resource(id), - mMaterials(), - mMaterialIndex() {} - std::vector mMaterials; std::vector mMaterialIndex; - virtual ResourceType getType() { + BaseMaterials(int id) : + Resource(id), + mMaterials(), + mMaterialIndex() { + // empty + } + + ~BaseMaterials() = default; + + ResourceType getType() const override { return ResourceType::RT_BaseMaterials; } }; @@ -109,24 +116,26 @@ struct Component { class Object : public Resource { public: - std::vector mMeshes; + std::vector mMeshes; std::vector mMeshIndex; std::vector mComponents; std::string mName; Object(int id) : Resource(id), - mName(std::string("Object_") + ai_to_string(id)) {} + mName(std::string("Object_") + ai_to_string(id)) { + // empty + } - virtual ResourceType getType() { + ~Object() = default; + + ResourceType getType() const override { return ResourceType::RT_Object; } }; - class XmlSerializer { public: - XmlSerializer(XmlParser *xmlParser) : mResourcesDictionnary(), mMaterialCount(0), @@ -136,7 +145,7 @@ public: } ~XmlSerializer() { - for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); it++) { + for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); ++it ) { delete it->second; } } @@ -146,28 +155,28 @@ public: return; } - scene->mRootNode = new aiNode("3MF"); + scene->mRootNode = new aiNode(XmlTag::RootTag); - XmlNode node = mXmlParser->getRootNode().child("model"); + XmlNode node = mXmlParser->getRootNode().child(XmlTag::model); if (node.empty()) { return; } - XmlNode resNode = node.child("resources"); - for (XmlNode currentNode = resNode.first_child(); currentNode; currentNode = currentNode.next_sibling()) { + XmlNode resNode = node.child(XmlTag::resources); + for (auto ¤tNode : resNode.children()) { const std::string ¤tNodeName = currentNode.name(); - if (currentNodeName == D3MF::XmlTag::object) { - ReadObject(currentNode);; - } else if (currentNodeName == D3MF::XmlTag::basematerials) { + if (currentNodeName == XmlTag::object) { + ReadObject(currentNode); + } else if (currentNodeName == XmlTag::basematerials) { ReadBaseMaterials(currentNode); - } else if (currentNodeName == D3MF::XmlTag::meta) { + } else if (currentNodeName == XmlTag::meta) { ReadMetadata(currentNode); } } - XmlNode buildNode = node.child("build"); - for (XmlNode currentNode = buildNode.first_child(); currentNode; currentNode = currentNode.next_sibling()) { + XmlNode buildNode = node.child(XmlTag::build); + for (auto ¤tNode : resNode.children()) { const std::string ¤tNodeName = currentNode.name(); - if (currentNodeName == D3MF::XmlTag::item) { + if (currentNodeName == XmlTag::item) { int objectId = -1; std::string transformationMatrixStr; aiMatrix4x4 transformationMatrix; @@ -186,7 +195,6 @@ public: } } - // import the metadata if (!mMetaData.empty()) { const size_t numMeta(mMetaData.size()); @@ -201,22 +209,21 @@ public: scene->mNumMeshes = static_cast(mMeshCount); if (scene->mNumMeshes != 0) { scene->mMeshes = new aiMesh *[scene->mNumMeshes](); - for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); it++) { + for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); ++it) { if (it->second->getType() == ResourceType::RT_Object) { - Object *obj = static_cast(it->second); + Object *obj = static_cast(it->second); for (unsigned int i = 0; i < obj->mMeshes.size(); ++i) { scene->mMeshes[obj->mMeshIndex[i]] = obj->mMeshes[i]; } } } } - // import the materials - scene->mNumMaterials = static_cast(mMaterialCount); + scene->mNumMaterials = mMaterialCount; if (scene->mNumMaterials != 0) { scene->mMaterials = new aiMaterial *[scene->mNumMaterials]; - for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); it++) { + for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); ++it) { if (it->second->getType() == ResourceType::RT_BaseMaterials) { BaseMaterials *baseMaterials = static_cast(it->second); for (unsigned int i = 0; i < baseMaterials->mMaterials.size(); ++i) { @@ -228,35 +235,36 @@ public: } private: + void addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform) { + ai_assert(nullptr != obj); - void addObjectToNode(aiNode* parent, Object* obj, aiMatrix4x4 nodeTransform) { aiNode *sceneNode = new aiNode(obj->mName); sceneNode->mNumMeshes = static_cast(obj->mMeshes.size()); sceneNode->mMeshes = new unsigned int[sceneNode->mNumMeshes]; std::copy(obj->mMeshIndex.begin(), obj->mMeshIndex.end(), sceneNode->mMeshes); sceneNode->mTransformation = nodeTransform; - - parent->addChildren(1, &sceneNode); + if (nullptr != parent) { + parent->addChildren(1, &sceneNode); + } for (size_t i = 0; i < obj->mComponents.size(); ++i) { Component c = obj->mComponents[i]; auto it = mResourcesDictionnary.find(c.mObjectId); if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) { - addObjectToNode(sceneNode, static_cast(it->second), c.mTransformation); + addObjectToNode(sceneNode, static_cast(it->second), c.mTransformation); } - } } - bool getNodeAttribute(const XmlNode& node, const std::string& attribute, std::string& value) { + bool getNodeAttribute(const XmlNode &node, const std::string &attribute, std::string &value) { pugi::xml_attribute objectAttribute = node.attribute(attribute.c_str()); if (!objectAttribute.empty()) { value = objectAttribute.as_string(); return true; - } else { - return false; } + + return false; } bool getNodeAttribute(const XmlNode &node, const std::string &attribute, int &value) { @@ -265,9 +273,9 @@ private: if (ret) { value = std::atoi(strValue.c_str()); return true; - } else { - return false; - } + } + + return false; } aiMatrix4x4 parseTransformMatrix(std::string matrixStr) { @@ -287,7 +295,7 @@ private: } } if (currentNumber.size() > 0) { - float f = std::stof(currentNumber); + const float f = std::stof(currentNumber); numbers.push_back(f); } @@ -311,29 +319,26 @@ private: transformMatrix.b4 = numbers[10]; transformMatrix.c4 = numbers[11]; transformMatrix.d4 = 1; + return transformMatrix; } void ReadObject(XmlNode &node) { int id = -1, pid = -1, pindex = -1; - bool hasId = getNodeAttribute(node, D3MF::XmlTag::id, id); - //bool hasType = getNodeAttribute(node, D3MF::XmlTag::type, type); not used currently - bool hasPid = getNodeAttribute(node, D3MF::XmlTag::pid, pid); - bool hasPindex = getNodeAttribute(node, D3MF::XmlTag::pindex, pindex); - - std::string idStr = ai_to_string(id); - + bool hasId = getNodeAttribute(node, XmlTag::id, id); + bool hasPid = getNodeAttribute(node, XmlTag::pid, pid); + bool hasPindex = getNodeAttribute(node, XmlTag::pindex, pindex); if (!hasId) { return; } Object *obj = new Object(id); - for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { + for (XmlNode ¤tNode : node.children()) { const std::string ¤tName = currentNode.name(); if (currentName == D3MF::XmlTag::mesh) { auto mesh = ReadMesh(currentNode); - mesh->mName.Set(idStr); + mesh->mName.Set(ai_to_string(id)); if (hasPid) { auto it = mResourcesDictionnary.find(pid); @@ -347,7 +352,7 @@ private: obj->mMeshIndex.push_back(mMeshCount); mMeshCount++; } else if (currentName == D3MF::XmlTag::components) { - for (XmlNode currentSubNode = currentNode.first_child(); currentSubNode; currentSubNode = currentSubNode.next_sibling()) { + for (XmlNode ¤tSubNode : currentNode.children()) { if (currentSubNode.name() == D3MF::XmlTag::component) { int objectId = -1; std::string componentTransformStr; @@ -369,21 +374,20 @@ private: aiMesh *ReadMesh(XmlNode &node) { aiMesh *mesh = new aiMesh(); - for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { + for (XmlNode ¤tNode : node.children()) { const std::string ¤tName = currentNode.name(); - if (currentName == D3MF::XmlTag::vertices) { + if (currentName == XmlTag::vertices) { ImportVertices(currentNode, mesh); - } else if (currentName == D3MF::XmlTag::triangles) { + } else if (currentName == XmlTag::triangles) { ImportTriangles(currentNode, mesh); } - } return mesh; } void ReadMetadata(XmlNode &node) { - pugi::xml_attribute attribute = node.attribute(D3MF::XmlTag::meta_name.c_str()); + pugi::xml_attribute attribute = node.attribute(D3MF::XmlTag::meta_name); const std::string name = attribute.as_string(); const std::string value = node.value(); if (name.empty()) { @@ -398,7 +402,7 @@ private: void ImportVertices(XmlNode &node, aiMesh *mesh) { std::vector vertices; - for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { + for (XmlNode ¤tNode : node.children()) { const std::string ¤tName = currentNode.name(); if (currentName == D3MF::XmlTag::vertex) { vertices.push_back(ReadVertex(currentNode)); @@ -412,9 +416,9 @@ private: aiVector3D ReadVertex(XmlNode &node) { aiVector3D vertex; - vertex.x = ai_strtof(node.attribute(D3MF::XmlTag::x.c_str()).as_string(), nullptr); - vertex.y = ai_strtof(node.attribute(D3MF::XmlTag::y.c_str()).as_string(), nullptr); - vertex.z = ai_strtof(node.attribute(D3MF::XmlTag::z.c_str()).as_string(), nullptr); + vertex.x = ai_strtof(node.attribute(D3MF::XmlTag::x).as_string(), nullptr); + vertex.y = ai_strtof(node.attribute(D3MF::XmlTag::y).as_string(), nullptr); + vertex.z = ai_strtof(node.attribute(D3MF::XmlTag::z).as_string(), nullptr); return vertex; } @@ -433,8 +437,7 @@ private: if (hasPid && hasP1) { auto it = mResourcesDictionnary.find(pid); - if (it != mResourcesDictionnary.end()) - { + if (it != mResourcesDictionnary.end()) { if (it->second->getType() == ResourceType::RT_BaseMaterials) { BaseMaterials *baseMaterials = static_cast(it->second); mesh->mMaterialIndex = baseMaterials->mMaterialIndex[p1]; @@ -457,9 +460,9 @@ private: face.mNumIndices = 3; face.mIndices = new unsigned int[face.mNumIndices]; - face.mIndices[0] = static_cast(std::atoi(node.attribute(D3MF::XmlTag::v1.c_str()).as_string())); - face.mIndices[1] = static_cast(std::atoi(node.attribute(D3MF::XmlTag::v2.c_str()).as_string())); - face.mIndices[2] = static_cast(std::atoi(node.attribute(D3MF::XmlTag::v3.c_str()).as_string())); + face.mIndices[0] = static_cast(std::atoi(node.attribute(XmlTag::v1).as_string())); + face.mIndices[1] = static_cast(std::atoi(node.attribute(XmlTag::v2).as_string())); + face.mIndices[2] = static_cast(std::atoi(node.attribute(XmlTag::v3).as_string())); return face; } @@ -467,11 +470,10 @@ private: void ReadBaseMaterials(XmlNode &node) { int id = -1; if (getNodeAttribute(node, D3MF::XmlTag::basematerials_id, id)) { - BaseMaterials* baseMaterials = new BaseMaterials(id); + BaseMaterials *baseMaterials = new BaseMaterials(id); - for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) - { - if (currentNode.name() == D3MF::XmlTag::basematerials_base) { + for (XmlNode ¤tNode : node.children()) { + if (currentNode.name() == XmlTag::basematerials_base) { baseMaterials->mMaterialIndex.push_back(mMaterialCount); baseMaterials->mMaterials.push_back(readMaterialDef(currentNode, id)); mMaterialCount++; @@ -488,7 +490,7 @@ private: } //format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1) - const size_t len(strlen(color)); + const size_t len = strlen(color); if (9 != len && 7 != len) { return false; } @@ -517,7 +519,7 @@ private: } void assignDiffuseColor(XmlNode &node, aiMaterial *mat) { - const char *color = node.attribute(D3MF::XmlTag::basematerials_displaycolor.c_str()).as_string(); + const char *color = node.attribute(XmlTag::basematerials_displaycolor).as_string(); aiColor4D diffuse; if (parseColor(color, diffuse)) { mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE); @@ -531,7 +533,7 @@ private: bool hasName = getNodeAttribute(node, D3MF::XmlTag::basematerials_name, name); std::string stdMaterialName; - std::string strId(ai_to_string(basematerialsId)); + const std::string strId(ai_to_string(basematerialsId)); stdMaterialName += "id"; stdMaterialName += strId; stdMaterialName += "_"; @@ -556,7 +558,7 @@ private: std::string value; }; std::vector mMetaData; - std::map mResourcesDictionnary; + std::map mResourcesDictionnary; unsigned int mMaterialCount, mMeshCount; XmlParser *mXmlParser; }; diff --git a/code/AssetLib/3MF/D3MFOpcPackage.cpp b/code/AssetLib/3MF/D3MFOpcPackage.cpp index c46b83b03..d5bbcd618 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.cpp +++ b/code/AssetLib/3MF/D3MFOpcPackage.cpp @@ -103,9 +103,9 @@ public: std::string name = currentNode.name(); if (name == "Relationship") { OpcPackageRelationshipPtr relPtr(new OpcPackageRelationship()); - relPtr->id = currentNode.attribute(XmlTag::RELS_ATTRIB_ID.c_str()).as_string(); - relPtr->type = currentNode.attribute(XmlTag::RELS_ATTRIB_TYPE.c_str()).as_string(); - relPtr->target = currentNode.attribute(XmlTag::RELS_ATTRIB_TARGET.c_str()).as_string(); + relPtr->id = currentNode.attribute(XmlTag::RELS_ATTRIB_ID).as_string(); + relPtr->type = currentNode.attribute(XmlTag::RELS_ATTRIB_TYPE).as_string(); + relPtr->target = currentNode.attribute(XmlTag::RELS_ATTRIB_TARGET).as_string(); if (validateRels(relPtr)) { m_relationShips.push_back(relPtr); } diff --git a/code/AssetLib/AMF/AMFImporter_Postprocess.cpp b/code/AssetLib/AMF/AMFImporter_Postprocess.cpp index 036b647e8..43d0de52f 100644 --- a/code/AssetLib/AMF/AMFImporter_Postprocess.cpp +++ b/code/AssetLib/AMF/AMFImporter_Postprocess.cpp @@ -428,10 +428,10 @@ void AMFImporter::Postprocess_BuildMeshSet(const AMFMesh &pNodeElement, const st if (pBiggerThan != nullptr) { bool found = false; - + const size_t biggerThan = *pBiggerThan; for (const SComplexFace &face : pFaceList) { for (size_t idx_vert = 0; idx_vert < face.Face.mNumIndices; idx_vert++) { - if (face.Face.mIndices[idx_vert] > *pBiggerThan) { + if (face.Face.mIndices[idx_vert] > biggerThan) { rv = face.Face.mIndices[idx_vert]; found = true; break; diff --git a/code/CApi/CInterfaceIOWrapper.cpp b/code/CApi/CInterfaceIOWrapper.cpp index ac358a4a8..8e2ac95c0 100644 --- a/code/CApi/CInterfaceIOWrapper.cpp +++ b/code/CApi/CInterfaceIOWrapper.cpp @@ -47,7 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { -CIOStreamWrapper::~CIOStreamWrapper(void) { +CIOStreamWrapper::~CIOStreamWrapper() { /* Various places depend on this destructor to close the file */ if (mFile) { mIO->mFileSystem->CloseProc(mIO->mFileSystem, mFile); @@ -78,7 +78,7 @@ aiReturn CIOStreamWrapper::Seek(size_t pOffset, } // ................................................................... -size_t CIOStreamWrapper::Tell(void) const { +size_t CIOStreamWrapper::Tell() const { return mFile->TellProc(mFile); } diff --git a/contrib/zip/src/zip.c b/contrib/zip/src/zip.c index 1f7fa8b76..29af2ec99 100644 --- a/contrib/zip/src/zip.c +++ b/contrib/zip/src/zip.c @@ -44,7 +44,7 @@ #ifdef _MSC_VER #include -#pragma warning(disable : 4706) +#pragma warning(disable : 4706 4244 4028) #define ftruncate(fd, sz) (-(_chsize_s((fd), (sz)) != 0)) #define fileno _fileno From 0e17939e8deed6c50796f334d6910ea0136a840b Mon Sep 17 00:00:00 2001 From: kimkulling Date: Tue, 4 May 2021 12:09:38 +0200 Subject: [PATCH 042/335] Use const char* const --- code/AssetLib/3MF/3MFXmlTags.h | 102 ++++++++++++++--------------- code/AssetLib/3MF/D3MFImporter.cpp | 1 - 2 files changed, 51 insertions(+), 52 deletions(-) diff --git a/code/AssetLib/3MF/3MFXmlTags.h b/code/AssetLib/3MF/3MFXmlTags.h index 910006bc9..d447556d6 100644 --- a/code/AssetLib/3MF/3MFXmlTags.h +++ b/code/AssetLib/3MF/3MFXmlTags.h @@ -45,65 +45,65 @@ namespace D3MF { namespace XmlTag { // Root tag - static const char *RootTag = "3MF"; + const char* const RootTag = "3MF"; // Meta-data - static const char *meta = "metadata"; - static const char *meta_name = "name"; + const char* const meta = "metadata"; + const char* const meta_name = "name"; // Model-data specific tags - static const char *model = "model"; - static const char *model_unit = "unit"; - static const char *metadata = "metadata"; - static const char *resources = "resources"; - static const char *object = "object"; - static const char *mesh = "mesh"; - static const char *components = "components"; - static const char *component = "component"; - static const char *vertices = "vertices"; - static const char *vertex = "vertex"; - static const char *triangles = "triangles"; - static const char *triangle = "triangle"; - static const char *x = "x"; - static const char *y = "y"; - static const char *z = "z"; - static const char *v1 = "v1"; - static const char *v2 = "v2"; - static const char *v3 = "v3"; - static const char *id = "id"; - static const char *pid = "pid"; - static const char *pindex = "pindex"; - static const char *p1 = "p1"; - static const char *name = "name"; - static const char *type = "type"; - static const char *build = "build"; - static const char *item = "item"; - static const char *objectid = "objectid"; - static const char *transform = "transform"; + const char* const model = "model"; + const char* const model_unit = "unit"; + const char* const metadata = "metadata"; + const char* const resources = "resources"; + const char* const object = "object"; + const char* const mesh = "mesh"; + const char* const components = "components"; + const char* const component = "component"; + const char* const vertices = "vertices"; + const char* const vertex = "vertex"; + const char* const triangles = "triangles"; + const char* const triangle = "triangle"; + const char* const x = "x"; + const char* const y = "y"; + const char* const z = "z"; + const char* const v1 = "v1"; + const char* const v2 = "v2"; + const char* const v3 = "v3"; + const char* const id = "id"; + const char* const pid = "pid"; + const char* const pindex = "pindex"; + const char* const p1 = "p1"; + const char* const name = "name"; + const char* const type = "type"; + const char* const build = "build"; + const char* const item = "item"; + const char* const objectid = "objectid"; + const char* const transform = "transform"; // Material definitions - static const char *basematerials = "basematerials"; - static const char *basematerials_id = "id"; - static const char *basematerials_base = "base"; - static const char *basematerials_name = "name"; - static const char *basematerials_displaycolor = "displaycolor"; + const char* const basematerials = "basematerials"; + const char* const basematerials_id = "id"; + const char* const basematerials_base = "base"; + const char* const basematerials_name = "name"; + const char* const basematerials_displaycolor = "displaycolor"; // Meta info tags - static const char *CONTENT_TYPES_ARCHIVE = "[Content_Types].xml"; - static const char *ROOT_RELATIONSHIPS_ARCHIVE = "_rels/.rels"; - static const char *SCHEMA_CONTENTTYPES = "http://schemas.openxmlformats.org/package/2006/content-types"; - static const char *SCHEMA_RELATIONSHIPS = "http://schemas.openxmlformats.org/package/2006/relationships"; - static const char *RELS_RELATIONSHIP_CONTAINER = "Relationships"; - static const char *RELS_RELATIONSHIP_NODE = "Relationship"; - static const char *RELS_ATTRIB_TARGET = "Target"; - static const char *RELS_ATTRIB_TYPE = "Type"; - static const char *RELS_ATTRIB_ID = "Id"; - static const char *PACKAGE_START_PART_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel"; - static const char *PACKAGE_PRINT_TICKET_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/printticket"; - static const char *PACKAGE_TEXTURE_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dtexture"; - static const char *PACKAGE_CORE_PROPERTIES_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"; - static const char *PACKAGE_THUMBNAIL_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"; -} + const char* const CONTENT_TYPES_ARCHIVE = "[Content_Types].xml"; + const char* const ROOT_RELATIONSHIPS_ARCHIVE = "_rels/.rels"; + const char* const SCHEMA_CONTENTTYPES = "http://schemas.openxmlformats.org/package/2006/content-types"; + const char* const SCHEMA_RELATIONSHIPS = "http://schemas.openxmlformats.org/package/2006/relationships"; + const char* const RELS_RELATIONSHIP_CONTAINER = "Relationships"; + const char* const RELS_RELATIONSHIP_NODE = "Relationship"; + const char* const RELS_ATTRIB_TARGET = "Target"; + const char* const RELS_ATTRIB_TYPE = "Type"; + const char* const RELS_ATTRIB_ID = "Id"; + const char* const PACKAGE_START_PART_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel"; + const char* const PACKAGE_PRINT_TICKET_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/printticket"; + const char* const PACKAGE_TEXTURE_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dtexture"; + const char* const PACKAGE_CORE_PROPERTIES_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"; + const char* const PACKAGE_THUMBNAIL_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"; + } } // Namespace D3MF } // Namespace Assimp diff --git a/code/AssetLib/3MF/D3MFImporter.cpp b/code/AssetLib/3MF/D3MFImporter.cpp index b2d421610..580f65dd2 100644 --- a/code/AssetLib/3MF/D3MFImporter.cpp +++ b/code/AssetLib/3MF/D3MFImporter.cpp @@ -173,7 +173,6 @@ public: } } - XmlNode buildNode = node.child(XmlTag::build); for (auto ¤tNode : resNode.children()) { const std::string ¤tNodeName = currentNode.name(); if (currentNodeName == XmlTag::item) { From ee5170c18a011398b9e3a677105e9196242b0f02 Mon Sep 17 00:00:00 2001 From: kimkulling Date: Tue, 4 May 2021 14:40:25 +0200 Subject: [PATCH 043/335] - fix security issue --- code/AssetLib/3MF/D3MFImporter.cpp | 17 +++++++++-------- code/AssetLib/3MF/D3MFImporter.h | 2 +- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/code/AssetLib/3MF/D3MFImporter.cpp b/code/AssetLib/3MF/D3MFImporter.cpp index 580f65dd2..a976a4731 100644 --- a/code/AssetLib/3MF/D3MFImporter.cpp +++ b/code/AssetLib/3MF/D3MFImporter.cpp @@ -42,6 +42,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef ASSIMP_BUILD_NO_3MF_IMPORTER #include "D3MFImporter.h" +#include "3MFXmlTags.h" +#include "D3MFOpcPackage.h" #include #include @@ -51,16 +53,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include + #include #include #include #include #include - -#include "3MFXmlTags.h" -#include "D3MFOpcPackage.h" -#include - #include namespace Assimp { @@ -489,7 +488,7 @@ private: } //format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1) - const size_t len = strlen(color); + const size_t len = strnlen_s(color, 9); if (9 != len && 7 != len) { return false; } @@ -564,6 +563,8 @@ private: } //namespace D3MF +using namespace D3MF; + static const aiImporterDesc desc = { "3mf Importer", "", @@ -613,11 +614,11 @@ const aiImporterDesc *D3MFImporter::GetInfo() const { } void D3MFImporter::InternReadFile(const std::string &filename, aiScene *pScene, IOSystem *pIOHandler) { - D3MF::D3MFOpcPackage opcPackage(pIOHandler, filename); + D3MFOpcPackage opcPackage(pIOHandler, filename); XmlParser xmlParser; if (xmlParser.parse(opcPackage.RootStream())) { - D3MF::XmlSerializer xmlSerializer(&xmlParser); + XmlSerializer xmlSerializer(&xmlParser); xmlSerializer.ImportXml(pScene); } } diff --git a/code/AssetLib/3MF/D3MFImporter.h b/code/AssetLib/3MF/D3MFImporter.h index a65c4102f..811c463b6 100644 --- a/code/AssetLib/3MF/D3MFImporter.h +++ b/code/AssetLib/3MF/D3MFImporter.h @@ -47,9 +47,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { +/// @brief The 3MF-importer class. class D3MFImporter : public BaseImporter { public: - // BaseImporter interface D3MFImporter(); ~D3MFImporter(); bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const; From 7690f92c816d1973696027b177f2bdff727ec780 Mon Sep 17 00:00:00 2001 From: kimkulling Date: Tue, 4 May 2021 14:48:39 +0200 Subject: [PATCH 044/335] Fix version of strnlen --- code/AssetLib/3MF/D3MFImporter.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/code/AssetLib/3MF/D3MFImporter.cpp b/code/AssetLib/3MF/D3MFImporter.cpp index a976a4731..8343aa1c6 100644 --- a/code/AssetLib/3MF/D3MFImporter.cpp +++ b/code/AssetLib/3MF/D3MFImporter.cpp @@ -488,7 +488,11 @@ private: } //format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1) +#ifdef _WIN32 const size_t len = strnlen_s(color, 9); +#else + const size_t len = strnlen(color, 9); +#endif if (9 != len && 7 != len) { return false; } From e85a69a9603d51f1a55eed6f9cfb37bfeb89b684 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 4 May 2021 15:31:07 +0200 Subject: [PATCH 045/335] Update D3MFImporter.cpp --- code/AssetLib/3MF/D3MFImporter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/AssetLib/3MF/D3MFImporter.cpp b/code/AssetLib/3MF/D3MFImporter.cpp index a976a4731..3535ed039 100644 --- a/code/AssetLib/3MF/D3MFImporter.cpp +++ b/code/AssetLib/3MF/D3MFImporter.cpp @@ -61,6 +61,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include namespace Assimp { namespace D3MF { From 8ad9c937f171bd716bbd5692af1c501acbaded88 Mon Sep 17 00:00:00 2001 From: Krishty Date: Tue, 4 May 2021 19:10:24 +0200 Subject: [PATCH 046/335] enabled debug information in MSVC release build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No effect on runtime speed/size. Slightly slower link time, but debugging experience improves by a million times. - /Zi – Store debug information in a .pdb file, not directly in the DLL/EXE - /DEBUG:FULL – generate debug information during link - /PDBALTPATH:%_PDB% – do not store the file system path of the .pdb, just the filename and hash (no disclose paths on distribution) - /OPT:REF /OPT:ICF – remove unreferenced functions and fold identical functions (this was enabled before, but requires explicit enabling if /DEBUG:FULL is specified) --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 97a3641f5..f1b60936d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -268,6 +268,8 @@ ELSEIF(MSVC) ADD_COMPILE_OPTIONS(/wd4351) ENDIF() SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_DEBUG /Zi /Od") + SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi") + SET(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG:FULL /PDBALTPATH:%_PDB% /OPT:REF /OPT:ICF") ELSEIF (CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) IF(NOT ASSIMP_HUNTER_ENABLED) SET(CMAKE_CXX_STANDARD 11) From f17d58cadd714dda9ad186a465234a714727ed63 Mon Sep 17 00:00:00 2001 From: Eric Olson Date: Tue, 4 May 2021 13:38:38 -0500 Subject: [PATCH 047/335] Use POINTER(c_char) for binary data with pyassimp "For a general character pointer that may also point to binary data, POINTER(c_char) must be used." c_char_p is for a zero-terminated string. Reference: https://docs.python.org/3/library/ctypes.html#ctypes.c_char_p Applying this change to the 4.1.4 released python module fixes #2339 for me in Ubuntu. --- port/PyAssimp/pyassimp/structs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/port/PyAssimp/pyassimp/structs.py b/port/PyAssimp/pyassimp/structs.py index 809afae54..e1fba1950 100644 --- a/port/PyAssimp/pyassimp/structs.py +++ b/port/PyAssimp/pyassimp/structs.py @@ -1,6 +1,6 @@ #-*- coding: utf-8 -*- -from ctypes import POINTER, c_void_p, c_uint, c_char, c_float, Structure, c_char_p, c_double, c_ubyte, c_size_t, c_uint32 +from ctypes import POINTER, c_void_p, c_uint, c_char, c_float, Structure, c_double, c_ubyte, c_size_t, c_uint32 class Vector2D(Structure): @@ -1121,7 +1121,7 @@ class Scene(Structure): ("mMetadata", POINTER(Metadata)), # Internal data, do not touch - ("mPrivate", c_char_p), + ("mPrivate", POINTER(c_char)), ] assimp_structs_as_tuple = (Matrix4x4, From 2a126f9f62a8174448a5ac31d94a3534eefd909f Mon Sep 17 00:00:00 2001 From: Krishty Date: Mon, 3 May 2021 21:46:53 +0200 Subject: [PATCH 048/335] reduced Ogre string bloat The Ogre importer used std::string where a string literal would have been sufficient. Saves another 600 B of code and data. --- code/AssetLib/Ogre/OgreBinarySerializer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/AssetLib/Ogre/OgreBinarySerializer.cpp b/code/AssetLib/Ogre/OgreBinarySerializer.cpp index 0fc18feb9..68b1cf1ed 100644 --- a/code/AssetLib/Ogre/OgreBinarySerializer.cpp +++ b/code/AssetLib/Ogre/OgreBinarySerializer.cpp @@ -55,9 +55,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { namespace Ogre { -const std::string MESH_VERSION_1_8 = "[MeshSerializer_v1.8]"; -const std::string SKELETON_VERSION_1_8 = "[Serializer_v1.80]"; -const std::string SKELETON_VERSION_1_1 = "[Serializer_v1.10]"; +static constexpr auto MESH_VERSION_1_8 = "[MeshSerializer_v1.8]"; +static constexpr auto SKELETON_VERSION_1_8 = "[Serializer_v1.80]"; +static constexpr auto SKELETON_VERSION_1_1 = "[Serializer_v1.10]"; const unsigned short HEADER_CHUNK_ID = 0x1000; From 7b6dab5e209257a17b4ca3bcc7834e2c8cc402b1 Mon Sep 17 00:00:00 2001 From: Krishty Date: Mon, 3 May 2021 21:52:48 +0200 Subject: [PATCH 049/335] reduced DXF string bloat The DXF importer defined a global std::string constant, only to convert it back to a C string on use. This commit defines the constant as a C string right away, thus saving 340 B of code and data. --- code/AssetLib/DXF/DXFLoader.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/code/AssetLib/DXF/DXFLoader.cpp b/code/AssetLib/DXF/DXFLoader.cpp index d4a6be4ad..b42d497f4 100644 --- a/code/AssetLib/DXF/DXFLoader.cpp +++ b/code/AssetLib/DXF/DXFLoader.cpp @@ -63,11 +63,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace Assimp; // AutoCAD Binary DXF -const std::string AI_DXF_BINARY_IDENT = std::string("AutoCAD Binary DXF\r\n\x1a\0"); -const size_t AI_DXF_BINARY_IDENT_LEN = 24u; +static constexpr char AI_DXF_BINARY_IDENT[] = "AutoCAD Binary DXF\r\n\x1a"; +static constexpr size_t AI_DXF_BINARY_IDENT_LEN = sizeof AI_DXF_BINARY_IDENT; // default vertex color that all uncolored vertices will receive -const aiColor4D AI_DXF_DEFAULT_COLOR(aiColor4D(0.6f, 0.6f, 0.6f, 0.6f)); +static const aiColor4D AI_DXF_DEFAULT_COLOR(aiColor4D(0.6f, 0.6f, 0.6f, 0.6f)); // color indices for DXF - 16 are supported, the table is // taken directly from the DXF spec. @@ -156,10 +156,10 @@ void DXFImporter::InternReadFile( const std::string& filename, aiScene* pScene, } // Check whether this is a binary DXF file - we can't read binary DXF files :-( - char buff[AI_DXF_BINARY_IDENT_LEN+1] = {0}; + char buff[AI_DXF_BINARY_IDENT_LEN] = {0}; file->Read(buff,AI_DXF_BINARY_IDENT_LEN,1); - if (0 == strncmp(AI_DXF_BINARY_IDENT.c_str(),buff,AI_DXF_BINARY_IDENT_LEN)) { + if (0 == memcmp(AI_DXF_BINARY_IDENT,buff,AI_DXF_BINARY_IDENT_LEN)) { throw DeadlyImportError("DXF: Binary files are not supported at the moment"); } @@ -549,7 +549,7 @@ void DXFImporter::ParseEntities(DXF::LineReader& reader, DXF::FileData& output) ++reader; } - ASSIMP_LOG_VERBOSE_DEBUG_F( "DXF: got ", block.lines.size()," polylines and ", block.insertions.size(), + ASSIMP_LOG_VERBOSE_DEBUG_F( "DXF: got ", block.lines.size()," polylines and ", block.insertions.size(), " inserted blocks in ENTITIES" ); } From f3c18556d1331e43ac5828fa82bf10fee966df96 Mon Sep 17 00:00:00 2001 From: Krishty Date: Tue, 4 May 2021 22:03:44 +0200 Subject: [PATCH 050/335] reduced OpenGEX string bloat The OpenGEX importer defined a few global std::string constants, only to convert them back to C strings on use. This commit defines them as C strings from the beginning. strncmp() was used to compare these strings to other strings, but the length limit was set to string length, which made it equivalent to strcmp(), just slower. Fixed that as well. --- code/AssetLib/OpenGEX/OpenGEXImporter.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/code/AssetLib/OpenGEX/OpenGEXImporter.cpp b/code/AssetLib/OpenGEX/OpenGEXImporter.cpp index b29aeeeb1..7ee278521 100644 --- a/code/AssetLib/OpenGEX/OpenGEXImporter.cpp +++ b/code/AssetLib/OpenGEX/OpenGEXImporter.cpp @@ -735,22 +735,22 @@ enum MeshAttribute { TexCoord }; -static const std::string PosToken = "position"; -static const std::string ColToken = "color"; -static const std::string NormalToken = "normal"; -static const std::string TexCoordToken = "texcoord"; +constexpr auto PosToken = "position"; +constexpr auto ColToken = "color"; +constexpr auto NormalToken = "normal"; +constexpr auto TexCoordToken = "texcoord"; //------------------------------------------------------------------------------------------------ static MeshAttribute getAttributeByName(const char *attribName) { ai_assert(nullptr != attribName); - if (0 == strncmp(PosToken.c_str(), attribName, PosToken.size())) { + if (0 == strcmp(PosToken, attribName)) { return Position; - } else if (0 == strncmp(ColToken.c_str(), attribName, ColToken.size())) { + } else if (0 == strcmp(ColToken, attribName)) { return Color; - } else if (0 == strncmp(NormalToken.c_str(), attribName, NormalToken.size())) { + } else if (0 == strcmp(NormalToken, attribName)) { return Normal; - } else if (0 == strncmp(TexCoordToken.c_str(), attribName, TexCoordToken.size())) { + } else if (0 == strcmp(TexCoordToken, attribName)) { return TexCoord; } From b57ce004f805f133cf731311c3c8a6f08b0d7db9 Mon Sep 17 00:00:00 2001 From: Krishty Date: Tue, 4 May 2021 09:45:26 +0200 Subject: [PATCH 051/335] reduced FBX string bloat The FBX importer used two std::strings where string literals would have been sufficient. --- code/AssetLib/FBX/FBXMeshGeometry.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/AssetLib/FBX/FBXMeshGeometry.cpp b/code/AssetLib/FBX/FBXMeshGeometry.cpp index 2bca8dff2..4f87c5a2c 100644 --- a/code/AssetLib/FBX/FBXMeshGeometry.cpp +++ b/code/AssetLib/FBX/FBXMeshGeometry.cpp @@ -604,15 +604,15 @@ void MeshGeometry::ReadVertexDataTangents(std::vector& tangents_out, } // ------------------------------------------------------------------------------------------------ -static const std::string BinormalIndexToken = "BinormalIndex"; -static const std::string BinormalsIndexToken = "BinormalsIndex"; +static const char * BinormalIndexToken = "BinormalIndex"; +static const char * BinormalsIndexToken = "BinormalsIndex"; void MeshGeometry::ReadVertexDataBinormals(std::vector& binormals_out, const Scope& source, const std::string& MappingInformationType, const std::string& ReferenceInformationType) { const char * str = source.Elements().count( "Binormals" ) > 0 ? "Binormals" : "Binormal"; - const char * strIdx = source.Elements().count( "Binormals" ) > 0 ? BinormalsIndexToken.c_str() : BinormalIndexToken.c_str(); + const char * strIdx = source.Elements().count( "Binormals" ) > 0 ? BinormalsIndexToken : BinormalIndexToken; ResolveVertexDataArray(binormals_out,source,MappingInformationType,ReferenceInformationType, str, strIdx, From 2925592c640132e2b34d9aa52d7e36d675b18092 Mon Sep 17 00:00:00 2001 From: Jason C Date: Tue, 4 May 2021 04:55:08 -0400 Subject: [PATCH 052/335] [assimp] Make sure ctype calls use unsigned char. Cast to unsigned char as required by C++ (see C++ **[cctype.cyn]** -> ISO C99 section 7.4, [see also](https://en.cppreference.com/w/cpp/string/byte/isspace)). Addresses https://github.com/assimp/assimp/issues/3867 and then some. --- code/AssetLib/3DS/3DSConverter.cpp | 2 +- code/AssetLib/AMF/AMFImporter.cpp | 2 +- code/AssetLib/BVH/BVHLoader.cpp | 4 ++-- code/AssetLib/Blender/BlenderLoader.cpp | 6 +++--- code/AssetLib/Collada/ColladaParser.cpp | 2 +- code/AssetLib/FBX/FBXMaterial.cpp | 2 +- code/AssetLib/MD3/MD3Loader.cpp | 2 +- code/AssetLib/X/XFileImporter.cpp | 4 ++-- code/AssetLib/X/XFileParser.cpp | 4 ++-- code/Common/BaseImporter.cpp | 6 +++--- include/assimp/ParsingUtils.h | 2 +- include/assimp/StringComparison.h | 8 ++++---- include/assimp/StringUtils.h | 2 +- 13 files changed, 23 insertions(+), 23 deletions(-) diff --git a/code/AssetLib/3DS/3DSConverter.cpp b/code/AssetLib/3DS/3DSConverter.cpp index c977867c0..aca16b0d6 100644 --- a/code/AssetLib/3DS/3DSConverter.cpp +++ b/code/AssetLib/3DS/3DSConverter.cpp @@ -69,7 +69,7 @@ void Discreet3DSImporter::ReplaceDefaultMaterial() { for (unsigned int i = 0; i < mScene->mMaterials.size(); ++i) { std::string s = mScene->mMaterials[i].mName; for (std::string::iterator it = s.begin(); it != s.end(); ++it) { - *it = static_cast(::tolower(*it)); + *it = static_cast(::tolower(static_cast(*it))); } if (std::string::npos == s.find("default")) continue; diff --git a/code/AssetLib/AMF/AMFImporter.cpp b/code/AssetLib/AMF/AMFImporter.cpp index 1a3efba9a..e77b65f77 100644 --- a/code/AssetLib/AMF/AMFImporter.cpp +++ b/code/AssetLib/AMF/AMFImporter.cpp @@ -205,7 +205,7 @@ void AMFImporter::ParseHelper_FixTruncatedFloatString(const char *pInStr, std::s } static bool ParseHelper_Decode_Base64_IsBase64(const char pChar) { - return (isalnum(pChar) || (pChar == '+') || (pChar == '/')); + return (isalnum((unsigned char)pChar) || (pChar == '+') || (pChar == '/')); } void AMFImporter::ParseHelper_Decode_Base64(const std::string &pInputBase64, std::vector &pOutputData) const { diff --git a/code/AssetLib/BVH/BVHLoader.cpp b/code/AssetLib/BVH/BVHLoader.cpp index 8ae99033c..cf78fb6c6 100644 --- a/code/AssetLib/BVH/BVHLoader.cpp +++ b/code/AssetLib/BVH/BVHLoader.cpp @@ -359,7 +359,7 @@ void BVHLoader::ReadMotion(aiScene * /*pScene*/) { std::string BVHLoader::GetNextToken() { // skip any preceding whitespace while (mReader != mBuffer.end()) { - if (!isspace(*mReader)) + if (!isspace((unsigned char)*mReader)) break; // count lines @@ -372,7 +372,7 @@ std::string BVHLoader::GetNextToken() { // collect all chars till the next whitespace. BVH is easy in respect to that. std::string token; while (mReader != mBuffer.end()) { - if (isspace(*mReader)) + if (isspace((unsigned char)*mReader)) break; token.push_back(*mReader); diff --git a/code/AssetLib/Blender/BlenderLoader.cpp b/code/AssetLib/Blender/BlenderLoader.cpp index 3722b9c73..56f4e985f 100644 --- a/code/AssetLib/Blender/BlenderLoader.cpp +++ b/code/AssetLib/Blender/BlenderLoader.cpp @@ -420,9 +420,9 @@ void BlenderImporter::ResolveImage(aiMaterial *out, const Material *mat, const M --s; } - curTex->achFormatHint[0] = s + 1 > e ? '\0' : (char)::tolower(s[1]); - curTex->achFormatHint[1] = s + 2 > e ? '\0' : (char)::tolower(s[2]); - curTex->achFormatHint[2] = s + 3 > e ? '\0' : (char)::tolower(s[3]); + curTex->achFormatHint[0] = s + 1 > e ? '\0' : (char)::tolower((unsigned char)s[1]); + curTex->achFormatHint[1] = s + 2 > e ? '\0' : (char)::tolower((unsigned char)s[2]); + curTex->achFormatHint[2] = s + 3 > e ? '\0' : (char)::tolower((unsigned char)s[3]); curTex->achFormatHint[3] = '\0'; // tex->mHeight = 0; diff --git a/code/AssetLib/Collada/ColladaParser.cpp b/code/AssetLib/Collada/ColladaParser.cpp index 42166fdd4..1ef109f11 100644 --- a/code/AssetLib/Collada/ColladaParser.cpp +++ b/code/AssetLib/Collada/ColladaParser.cpp @@ -234,7 +234,7 @@ void ColladaParser::UriDecodePath(aiString &ss) { #if defined(_MSC_VER) if (ss.data[0] == '/' && isalpha((unsigned char)ss.data[1]) && ss.data[2] == ':') { #else - if (ss.data[0] == '/' && isalpha(ss.data[1]) && ss.data[2] == ':') { + if (ss.data[0] == '/' && isalpha((unsigned char)ss.data[1]) && ss.data[2] == ':') { #endif --ss.length; ::memmove(ss.data, ss.data + 1, ss.length); diff --git a/code/AssetLib/FBX/FBXMaterial.cpp b/code/AssetLib/FBX/FBXMaterial.cpp index 3af014bc3..409d7304a 100644 --- a/code/AssetLib/FBX/FBXMaterial.cpp +++ b/code/AssetLib/FBX/FBXMaterial.cpp @@ -82,7 +82,7 @@ Material::Material(uint64_t id, const Element& element, const Document& doc, con // lower-case shading because Blender (for example) writes "Phong" for (size_t i = 0; i < shading.length(); ++i) { - shading[i] = static_cast(tolower(shading[i])); + shading[i] = static_cast(tolower(static_cast(shading[i]))); } std::string templateName; if(shading == "phong") { diff --git a/code/AssetLib/MD3/MD3Loader.cpp b/code/AssetLib/MD3/MD3Loader.cpp index e27079766..3002aff67 100644 --- a/code/AssetLib/MD3/MD3Loader.cpp +++ b/code/AssetLib/MD3/MD3Loader.cpp @@ -702,7 +702,7 @@ void MD3Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy } filename = mFile.substr(s), path = mFile.substr(0, s); for (std::string::iterator it = filename.begin(); it != filename.end(); ++it) { - *it = static_cast(tolower(*it)); + *it = static_cast(tolower(static_cast(*it))); } // Load multi-part model file, if necessary diff --git a/code/AssetLib/X/XFileImporter.cpp b/code/AssetLib/X/XFileImporter.cpp index cb245ed74..1fcd6b3db 100644 --- a/code/AssetLib/X/XFileImporter.cpp +++ b/code/AssetLib/X/XFileImporter.cpp @@ -667,8 +667,8 @@ void XFileImporter::ConvertMaterials( aiScene* pScene, std::vector &extensions) { } for (size_t i = 0; i < read; ++i) { - buffer[i] = static_cast(::tolower(buffer[i])); + buffer[i] = static_cast(::tolower((unsigned char)buffer[i])); } // It is not a proper handling of unicode files here ... @@ -214,7 +214,7 @@ void BaseImporter::GetExtensionList(std::set &extensions) { token.clear(); const char *ptr(tokens[i]); for (size_t tokIdx = 0; tokIdx < len; ++tokIdx) { - token.push_back(static_cast(tolower(*ptr))); + token.push_back(static_cast(tolower(static_cast(*ptr)))); ++ptr; } const char *r = strstr(buffer, token.c_str()); @@ -223,7 +223,7 @@ void BaseImporter::GetExtensionList(std::set &extensions) { } // We need to make sure that we didn't accidentially identify the end of another token as our token, // e.g. in a previous version the "gltf " present in some gltf files was detected as "f " - if (noAlphaBeforeTokens && (r != buffer && isalpha(r[-1]))) { + if (noAlphaBeforeTokens && (r != buffer && isalpha(static_cast(r[-1])))) { continue; } // We got a match, either we don't care where it is, or it happens to diff --git a/include/assimp/ParsingUtils.h b/include/assimp/ParsingUtils.h index b52b0610e..b5074869e 100644 --- a/include/assimp/ParsingUtils.h +++ b/include/assimp/ParsingUtils.h @@ -262,7 +262,7 @@ AI_FORCE_INLINE unsigned int tokenize(const string_type &str, std::vector= n) return 0; - c1 = tolower(*s1++); - c2 = tolower(*s2++); + c1 = tolower((unsigned char)*(s1++)); + c2 = tolower((unsigned char)*(s2++)); } while (c1 && (c1 == c2)); return c1 - c2; diff --git a/include/assimp/StringUtils.h b/include/assimp/StringUtils.h index 4afd717cf..93b7c3f0b 100644 --- a/include/assimp/StringUtils.h +++ b/include/assimp/StringUtils.h @@ -157,7 +157,7 @@ AI_FORCE_INLINE std::string ai_decimal_to_hexa(T toConvert) { ss >> result; for (size_t i = 0; i < result.size(); ++i) { - result[i] = (char)toupper(result[i]); + result[i] = (char)toupper((unsigned char)result[i]); } return result; From 1ec8d4b6cf15c8a14e8cc33834b9164c85cd79d7 Mon Sep 17 00:00:00 2001 From: Jason C Date: Tue, 4 May 2021 17:22:44 -0400 Subject: [PATCH 053/335] [draco] Make sure ctype calls use unsigned char. Addresses https://github.com/assimp/assimp/issues/3867 and then some. --- contrib/draco/src/draco/io/parser_utils.cc | 2 +- contrib/draco/src/draco/io/ply_reader.cc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contrib/draco/src/draco/io/parser_utils.cc b/contrib/draco/src/draco/io/parser_utils.cc index 753a1b314..4f95f6f84 100644 --- a/contrib/draco/src/draco/io/parser_utils.cc +++ b/contrib/draco/src/draco/io/parser_utils.cc @@ -252,7 +252,7 @@ DecoderBuffer ParseLineIntoDecoderBuffer(DecoderBuffer *buffer) { std::string ToLower(const std::string &str) { std::string out; - std::transform(str.begin(), str.end(), std::back_inserter(out), tolower); + std::transform(str.begin(), str.end(), std::back_inserter(out), [](unsigned char c){return tolower(c);}); return out; } diff --git a/contrib/draco/src/draco/io/ply_reader.cc b/contrib/draco/src/draco/io/ply_reader.cc index ea7f2689a..cb32df225 100644 --- a/contrib/draco/src/draco/io/ply_reader.cc +++ b/contrib/draco/src/draco/io/ply_reader.cc @@ -268,14 +268,14 @@ std::vector PlyReader::SplitWords(const std::string &line) { while ((end = line.find_first_of(" \t\n\v\f\r", start)) != std::string::npos) { const std::string word(line.substr(start, end - start)); - if (!std::all_of(word.begin(), word.end(), isspace)) { + if (!std::all_of(word.begin(), word.end(), [](unsigned char c){return isspace(c);})) { output.push_back(word); } start = end + 1; } const std::string last_word(line.substr(start)); - if (!std::all_of(last_word.begin(), last_word.end(), isspace)) { + if (!std::all_of(last_word.begin(), last_word.end(), [](unsigned char c){return isspace(c);})) { output.push_back(last_word); } return output; From 7dd7a053a91322fad88cdf958c6d0b3b7b91cb90 Mon Sep 17 00:00:00 2001 From: Jason C Date: Tue, 4 May 2021 17:25:45 -0400 Subject: [PATCH 054/335] [gtest] Fixed a rogue std::isalnum Use IsAlNum instead (gtest-port.h), which deals with char signedness correctly. This was the only spot in gtest where a cctype function was called instead of its gtest-port.h equivalent. Addresses https://github.com/assimp/assimp/issues/3867 and then some. --- contrib/gtest/include/gtest/internal/gtest-param-util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/gtest/include/gtest/internal/gtest-param-util.h b/contrib/gtest/include/gtest/internal/gtest-param-util.h index 82cab9b02..9d725a433 100644 --- a/contrib/gtest/include/gtest/internal/gtest-param-util.h +++ b/contrib/gtest/include/gtest/internal/gtest-param-util.h @@ -644,7 +644,7 @@ class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase { // Check for invalid characters for (std::string::size_type index = 0; index < name.size(); ++index) { - if (!isalnum(name[index]) && name[index] != '_') + if (!IsAlNum(name[index]) && name[index] != '_') return false; } From 200086c4c5ea18593dace1f3800d1213b8b3588c Mon Sep 17 00:00:00 2001 From: Jason C Date: Tue, 4 May 2021 17:26:17 -0400 Subject: [PATCH 055/335] [assimp_view] Make sure ctype calls use unsigned char. Addresses https://github.com/assimp/assimp/issues/3867 and then some. --- tools/assimp_view/Material.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/assimp_view/Material.cpp b/tools/assimp_view/Material.cpp index 8141fb58c..bcc93011e 100644 --- a/tools/assimp_view/Material.cpp +++ b/tools/assimp_view/Material.cpp @@ -272,7 +272,7 @@ bool CMaterialManager::TryLongerPath(char* szTemp,aiString* p_szString) szExtFound - 1 - info.cFileName); for (unsigned int i = 0; i < iSizeFound;++i) - info.cFileName[i] = (CHAR)tolower(info.cFileName[i]); + info.cFileName[i] = (CHAR)tolower((unsigned char)info.cFileName[i]); if (0 == memcmp(info.cFileName,szFile2, std::min(iSizeFound,iSize))) { @@ -354,7 +354,7 @@ int CMaterialManager::FindValidPath(aiString* p_szString) for (unsigned int i = 0;;++i) { if ('\0' == szTemp[i])break; - szTemp[i] = (char)tolower(szTemp[i]); + szTemp[i] = (char)tolower((unsigned char)szTemp[i]); } if(TryLongerPath(szTemp,p_szString))return 1; From c8ad8c6017c78534d54a8478d3aa0b1203cc515b Mon Sep 17 00:00:00 2001 From: Jason C Date: Tue, 4 May 2021 04:15:17 -0400 Subject: [PATCH 056/335] [mmd] Remove stderr spam. Removed stderr spam and cleaned up exception text. Addresses #3865. --- code/AssetLib/MMD/MMDPmxParser.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/code/AssetLib/MMD/MMDPmxParser.cpp b/code/AssetLib/MMD/MMDPmxParser.cpp index cb4787efd..d57dc169a 100644 --- a/code/AssetLib/MMD/MMDPmxParser.cpp +++ b/code/AssetLib/MMD/MMDPmxParser.cpp @@ -478,8 +478,7 @@ namespace pmx void PmxSoftBody::Read(std::istream * /*stream*/, PmxSetting * /*setting*/) { - std::cerr << "Not Implemented Exception" << std::endl; - throw DeadlyImportError("MMD: Not Implemented Exception"); + throw DeadlyImportError("MMD: Soft Body support is not implemented."); } void PmxModel::Init() @@ -516,15 +515,13 @@ namespace pmx char magic[4]; stream->read((char*) magic, sizeof(char) * 4); if (magic[0] != 0x50 || magic[1] != 0x4d || magic[2] != 0x58 || magic[3] != 0x20) - { - std::cerr << "invalid magic number." << std::endl; - throw DeadlyImportError("MMD: invalid magic number."); + { + throw DeadlyImportError("MMD: Invalid magic number."); } stream->read((char*) &version, sizeof(float)); if (version != 2.0f && version != 2.1f) { - std::cerr << "this is not ver2.0 or ver2.1 but " << version << "." << std::endl; - throw DeadlyImportError("MMD: this is not ver2.0 or ver2.1 but ", ai_to_string(version)); + throw DeadlyImportError("MMD: Unsupported version (must be 2.0 or 2.1): ", ai_to_string(version)); } this->setting.Read(stream); From a9fb1e56ae1504001b3848c2019fb622fd6b72e3 Mon Sep 17 00:00:00 2001 From: Jason C Date: Tue, 4 May 2021 19:16:23 -0400 Subject: [PATCH 057/335] Add ai_str_toprintable; fixed garbage messages in HMP, MDL, Q3D loaders. - ai_str_toprintable: See docs in StringUtils.h. - HMP, MDL, Q3D: In particular, newlines in binary data were complicating logging. --- code/AssetLib/HMP/HMPLoader.cpp | 8 ++------ code/AssetLib/MDL/MDLLoader.cpp | 2 +- code/AssetLib/Q3D/Q3DLoader.cpp | 3 ++- include/assimp/StringUtils.h | 27 +++++++++++++++++++++++++++ 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/code/AssetLib/HMP/HMPLoader.cpp b/code/AssetLib/HMP/HMPLoader.cpp index 56401f5c9..cd14cb9c3 100644 --- a/code/AssetLib/HMP/HMPLoader.cpp +++ b/code/AssetLib/HMP/HMPLoader.cpp @@ -47,6 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AssetLib/HMP/HMPLoader.h" #include "AssetLib/MD2/MD2FileData.h" +#include #include #include #include @@ -151,12 +152,7 @@ void HMPImporter::InternReadFile(const std::string &pFile, InternReadFile_HMP7(); } else { // Print the magic word to the logger - char szBuffer[5]; - szBuffer[0] = ((char *)&iMagic)[0]; - szBuffer[1] = ((char *)&iMagic)[1]; - szBuffer[2] = ((char *)&iMagic)[2]; - szBuffer[3] = ((char *)&iMagic)[3]; - szBuffer[4] = '\0'; + std::string szBuffer = ai_str_toprintable((const char *)&iMagic, sizeof(iMagic)); delete[] mBuffer; mBuffer = nullptr; diff --git a/code/AssetLib/MDL/MDLLoader.cpp b/code/AssetLib/MDL/MDLLoader.cpp index a4286a716..4c0fcd339 100644 --- a/code/AssetLib/MDL/MDLLoader.cpp +++ b/code/AssetLib/MDL/MDLLoader.cpp @@ -252,7 +252,7 @@ void MDLImporter::InternReadFile(const std::string &pFile, } else { // print the magic word to the log file throw DeadlyImportError("Unknown MDL subformat ", pFile, - ". Magic word (", std::string((char *)&iMagicWord, 4), ") is not known"); + ". Magic word (", ai_str_toprintable((const char *)&iMagicWord, sizeof(iMagicWord)), ") is not known"); } // Now rotate the whole scene 90 degrees around the x axis to convert to internal coordinate system diff --git a/code/AssetLib/Q3D/Q3DLoader.cpp b/code/AssetLib/Q3D/Q3DLoader.cpp index b52f86672..b6684d1b8 100644 --- a/code/AssetLib/Q3D/Q3DLoader.cpp +++ b/code/AssetLib/Q3D/Q3DLoader.cpp @@ -47,6 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // internal headers #include "Q3DLoader.h" +#include #include #include #include @@ -115,7 +116,7 @@ void Q3DImporter::InternReadFile(const std::string &pFile, // Check the file's signature if (ASSIMP_strincmp((const char *)stream.GetPtr(), "quick3Do", 8) && ASSIMP_strincmp((const char *)stream.GetPtr(), "quick3Ds", 8)) { - throw DeadlyImportError("Not a Quick3D file. Signature string is: ", std::string((const char *)stream.GetPtr(), 8)); + throw DeadlyImportError("Not a Quick3D file. Signature string is: ", ai_str_toprintable((const char *)stream.GetPtr(), 8)); } // Print the file format version diff --git a/include/assimp/StringUtils.h b/include/assimp/StringUtils.h index 4afd717cf..5ee1f48d8 100644 --- a/include/assimp/StringUtils.h +++ b/include/assimp/StringUtils.h @@ -249,4 +249,31 @@ AI_FORCE_INLINE std::string ai_str_toupper(const std::string &in) { return out; } +// --------------------------------------------------------------------------------- +/// @brief Make a string printable by replacing all non-printable characters with +/// the specified placeholder character. +/// @param in The incoming string. +/// @param placeholder Placeholder character, default is a question mark. +/// @return The string, with all non-printable characters replaced. +AI_FORCE_INLINE std::string ai_str_toprintable(const std::string &in, char placeholder = '?') { + std::string out(in); + std::transform(out.begin(), out.end(), out.begin(), [placeholder] (unsigned char c) { + return isprint(c) ? (char)c : placeholder; + }); + return out; +} + +// --------------------------------------------------------------------------------- +/// @brief Make a string printable by replacing all non-printable characters with +/// the specified placeholder character. +/// @param in The incoming string. +/// @param len The length of the incoming string. +/// @param placeholder Placeholder character, default is a question mark. +/// @return The string, with all non-printable characters replaced. Will return an +/// empty string if in is null or len is <= 0. +AI_FORCE_INLINE std::string ai_str_toprintable(const char *in, int len, char placeholder = '?') { + return (in && len > 0) ? ai_str_toprintable(std::string(in, len), placeholder) : std::string(); +} + + #endif From 9a04f5d4b0ea3cafba84dfcdc3f8746cc3eaac0a Mon Sep 17 00:00:00 2001 From: Jason C Date: Tue, 4 May 2021 21:24:44 -0400 Subject: [PATCH 058/335] Fix garbage messages in SIB, MD2, and MDC loaders. --- code/AssetLib/MD2/MD2Loader.cpp | 32 +++++++++++++------------------ code/AssetLib/MDC/MDCLoader.cpp | 13 +++---------- code/AssetLib/SIB/SIBImporter.cpp | 7 ++++--- 3 files changed, 20 insertions(+), 32 deletions(-) diff --git a/code/AssetLib/MD2/MD2Loader.cpp b/code/AssetLib/MD2/MD2Loader.cpp index 9ccbcdfca..5308ac89c 100644 --- a/code/AssetLib/MD2/MD2Loader.cpp +++ b/code/AssetLib/MD2/MD2Loader.cpp @@ -53,6 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include @@ -148,46 +149,39 @@ void MD2Importer::ValidateHeader( ) if (m_pcHeader->magic != AI_MD2_MAGIC_NUMBER_BE && m_pcHeader->magic != AI_MD2_MAGIC_NUMBER_LE) { - char szBuffer[5]; - szBuffer[0] = ((char*)&m_pcHeader->magic)[0]; - szBuffer[1] = ((char*)&m_pcHeader->magic)[1]; - szBuffer[2] = ((char*)&m_pcHeader->magic)[2]; - szBuffer[3] = ((char*)&m_pcHeader->magic)[3]; - szBuffer[4] = '\0'; - - throw DeadlyImportError("Invalid MD2 magic word: should be IDP2, the " - "magic word found is " + std::string(szBuffer)); + throw DeadlyImportError("Invalid MD2 magic word: expected IDP2, found ", + ai_str_toprintable((char *)&m_pcHeader->magic, 4)); } // check file format version if (m_pcHeader->version != 8) - ASSIMP_LOG_WARN( "Unsupported md2 file version. Continuing happily ..."); + ASSIMP_LOG_WARN( "Unsupported MD2 file version. Continuing happily ..."); // check some values whether they are valid if (0 == m_pcHeader->numFrames) - throw DeadlyImportError( "Invalid md2 file: NUM_FRAMES is 0"); + throw DeadlyImportError( "Invalid MD2 file: NUM_FRAMES is 0"); if (m_pcHeader->offsetEnd > (uint32_t)fileSize) - throw DeadlyImportError( "Invalid md2 file: File is too small"); + throw DeadlyImportError( "Invalid MD2 file: File is too small"); if (m_pcHeader->numSkins > AI_MAX_ALLOC(MD2::Skin)) { - throw DeadlyImportError("Invalid MD2 header: too many skins, would overflow"); + throw DeadlyImportError("Invalid MD2 header: Too many skins, would overflow"); } if (m_pcHeader->numVertices > AI_MAX_ALLOC(MD2::Vertex)) { - throw DeadlyImportError("Invalid MD2 header: too many vertices, would overflow"); + throw DeadlyImportError("Invalid MD2 header: Too many vertices, would overflow"); } if (m_pcHeader->numTexCoords > AI_MAX_ALLOC(MD2::TexCoord)) { - throw DeadlyImportError("Invalid MD2 header: too many texcoords, would overflow"); + throw DeadlyImportError("Invalid MD2 header: Too many texcoords, would overflow"); } if (m_pcHeader->numTriangles > AI_MAX_ALLOC(MD2::Triangle)) { - throw DeadlyImportError("Invalid MD2 header: too many triangles, would overflow"); + throw DeadlyImportError("Invalid MD2 header: Too many triangles, would overflow"); } if (m_pcHeader->numFrames > AI_MAX_ALLOC(MD2::Frame)) { - throw DeadlyImportError("Invalid MD2 header: too many frames, would overflow"); + throw DeadlyImportError("Invalid MD2 header: Too many frames, would overflow"); } // -1 because Frame already contains one @@ -199,7 +193,7 @@ void MD2Importer::ValidateHeader( ) m_pcHeader->offsetFrames + m_pcHeader->numFrames * frameSize >= fileSize || m_pcHeader->offsetEnd > fileSize) { - throw DeadlyImportError("Invalid MD2 header: some offsets are outside the file"); + throw DeadlyImportError("Invalid MD2 header: Some offsets are outside the file"); } if (m_pcHeader->numSkins > AI_MD2_MAX_SKINS) @@ -210,7 +204,7 @@ void MD2Importer::ValidateHeader( ) ASSIMP_LOG_WARN("The model contains more vertices than Quake 2 supports"); if (m_pcHeader->numFrames <= configFrameID ) - throw DeadlyImportError("The requested frame is not existing the file"); + throw DeadlyImportError("MD2: The requested frame (", configFrameID, ") does not exist in the file"); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/MDC/MDCLoader.cpp b/code/AssetLib/MDC/MDCLoader.cpp index 17a349768..ef5fdbfcd 100644 --- a/code/AssetLib/MDC/MDCLoader.cpp +++ b/code/AssetLib/MDC/MDCLoader.cpp @@ -53,6 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include @@ -143,16 +144,8 @@ void MDCImporter::ValidateHeader() { if (pcHeader->ulIdent != AI_MDC_MAGIC_NUMBER_BE && pcHeader->ulIdent != AI_MDC_MAGIC_NUMBER_LE) { - char szBuffer[5]; - szBuffer[0] = ((char *)&pcHeader->ulIdent)[0]; - szBuffer[1] = ((char *)&pcHeader->ulIdent)[1]; - szBuffer[2] = ((char *)&pcHeader->ulIdent)[2]; - szBuffer[3] = ((char *)&pcHeader->ulIdent)[3]; - szBuffer[4] = '\0'; - - throw DeadlyImportError("Invalid MDC magic word: should be IDPC, the " - "magic word found is " + - std::string(szBuffer)); + throw DeadlyImportError("Invalid MDC magic word: expected IDPC, found ", + ai_str_toprintable((char *)&pcHeader->ulIdent, 4)); } if (pcHeader->ulVersion != AI_MDC_VERSION) { diff --git a/code/AssetLib/SIB/SIBImporter.cpp b/code/AssetLib/SIB/SIBImporter.cpp index 6898fb65c..5c43c8587 100644 --- a/code/AssetLib/SIB/SIBImporter.cpp +++ b/code/AssetLib/SIB/SIBImporter.cpp @@ -68,6 +68,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #include @@ -166,14 +167,14 @@ static aiColor3D ReadColor(StreamReaderLE *stream) { } static void UnknownChunk(StreamReaderLE * /*stream*/, const SIBChunk &chunk) { - char temp[5] = { + char temp[4] = { static_cast((chunk.Tag >> 24) & 0xff), static_cast((chunk.Tag >> 16) & 0xff), static_cast((chunk.Tag >> 8) & 0xff), - static_cast(chunk.Tag & 0xff), '\0' + static_cast(chunk.Tag & 0xff) }; - ASSIMP_LOG_WARN((Formatter::format(), "SIB: Skipping unknown '", temp, "' chunk.")); + ASSIMP_LOG_WARN((Formatter::format(), "SIB: Skipping unknown '", ai_str_toprintable(temp, 4), "' chunk.")); } // Reads a UTF-16LE string and returns it at UTF-8. From 558457e5bf2711ab845239fe4b98c872f4b05d33 Mon Sep 17 00:00:00 2001 From: Jason C Date: Tue, 4 May 2021 03:04:12 -0400 Subject: [PATCH 059/335] [openddlparser] Remove default log handler and unsolicited output. This addresses part of #3862. - Remove default log handler. - Log callback can now be set to nullptr, which just makes logging a no-op. - Initial log callback is nullptr. - Also tweaked format of token error log message and removed newline. Assimp code that uses this may regain logging output by installing a callback and directing the output through appropriate logging facilities. --- contrib/openddlparser/code/OpenDDLParser.cpp | 47 ++++++-------------- 1 file changed, 13 insertions(+), 34 deletions(-) diff --git a/contrib/openddlparser/code/OpenDDLParser.cpp b/contrib/openddlparser/code/OpenDDLParser.cpp index 024c26f41..6a9f802ec 100644 --- a/contrib/openddlparser/code/OpenDDLParser.cpp +++ b/contrib/openddlparser/code/OpenDDLParser.cpp @@ -72,13 +72,15 @@ const char *getTypeToken(Value::ValueType type) { } static void logInvalidTokenError(char *in, const std::string &exp, OpenDDLParser::logCallback callback) { - std::stringstream stream; - stream << "Invalid token \"" << *in << "\"" - << " expected \"" << exp << "\"" << std::endl; - std::string full(in); - std::string part(full.substr(0, 50)); - stream << part; - callback(ddl_error_msg, stream.str()); + if (callback) { + std::string full(in); + std::string part(full.substr(0, 50)); + std::stringstream stream; + stream << "Invalid token \"" << *in << "\" " + << "(expected \"" << exp << "\") " + << "in: \"" << part << "\""; + callback(ddl_error_msg, stream.str()); + } } static bool isIntegerType(Value::ValueType integerType) { @@ -111,26 +113,8 @@ static DDLNode *createDDLNode(Text *id, OpenDDLParser *parser) { return node; } -static void logMessage(LogSeverity severity, const std::string &msg) { - std::string log; - if (ddl_debug_msg == severity) { - log += "Debug:"; - } else if (ddl_info_msg == severity) { - log += "Info :"; - } else if (ddl_warn_msg == severity) { - log += "Warn :"; - } else if (ddl_error_msg == severity) { - log += "Error:"; - } else { - log += "None :"; - } - - log += msg; - std::cout << log; -} - OpenDDLParser::OpenDDLParser() : - m_logCallback(logMessage), + m_logCallback(nullptr), m_buffer(), m_stack(), m_context(nullptr) { @@ -138,7 +122,7 @@ OpenDDLParser::OpenDDLParser() : } OpenDDLParser::OpenDDLParser(const char *buffer, size_t len) : - m_logCallback(&logMessage), m_buffer(), m_context(nullptr) { + m_logCallback(nullptr), m_buffer(), m_context(nullptr) { if (0 != len) { setBuffer(buffer, len); } @@ -149,13 +133,8 @@ OpenDDLParser::~OpenDDLParser() { } void OpenDDLParser::setLogCallback(logCallback callback) { - if (nullptr != callback) { - // install user-specific log callback - m_logCallback = callback; - } else { - // install default log callback - m_logCallback = &logMessage; - } + // install user-specific log callback; null = no log callback + m_logCallback = callback; } OpenDDLParser::logCallback OpenDDLParser::getLogCallback() const { From f8609c2c2dc65d40a13c6e542f0cf0058821444b Mon Sep 17 00:00:00 2001 From: Jason C Date: Tue, 4 May 2021 03:32:49 -0400 Subject: [PATCH 060/335] [opengex] Direct OpenDDLParser log messages to assimp logger Also filter unprintable characters. Addresses second part of #3862. --- code/AssetLib/OpenGEX/OpenGEXImporter.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/code/AssetLib/OpenGEX/OpenGEXImporter.cpp b/code/AssetLib/OpenGEX/OpenGEXImporter.cpp index b29aeeeb1..44b0bbf7b 100644 --- a/code/AssetLib/OpenGEX/OpenGEXImporter.cpp +++ b/code/AssetLib/OpenGEX/OpenGEXImporter.cpp @@ -45,6 +45,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include #include #include @@ -223,6 +224,18 @@ static void propId2StdString(Property *prop, std::string &name, std::string &key } } +//------------------------------------------------------------------------------------------------ +static void logDDLParserMessage (LogSeverity severity, const std::string &rawmsg) { + std::string msg = ai_str_toprintable(rawmsg); + switch (severity) { + case ddl_debug_msg: ASSIMP_LOG_DEBUG(msg); break; + case ddl_info_msg: ASSIMP_LOG_INFO(msg); break; + case ddl_warn_msg: ASSIMP_LOG_WARN(msg); break; + case ddl_error_msg: ASSIMP_LOG_ERROR(msg); break; + default: ASSIMP_LOG_VERBOSE_DEBUG(msg); break; + } +} + //------------------------------------------------------------------------------------------------ OpenGEXImporter::VertexContainer::VertexContainer() : m_numColors(0), m_colors(nullptr), m_numUVComps(), m_textureCoords() { @@ -306,6 +319,7 @@ void OpenGEXImporter::InternReadFile(const std::string &filename, aiScene *pScen pIOHandler->Close(file); OpenDDLParser myParser; + myParser.setLogCallback(&logDDLParserMessage); myParser.setBuffer(&buffer[0], buffer.size()); bool success(myParser.parse()); if (success) { From a03dc4edaa05731e81c5ac9edef78f9ac90097fa Mon Sep 17 00:00:00 2001 From: Jason C Date: Tue, 4 May 2021 20:45:08 -0400 Subject: [PATCH 061/335] [amf] Fix minor typo in error message. Added missing space to detail string on parse failure. --- code/AssetLib/AMF/AMFImporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/AMF/AMFImporter.cpp b/code/AssetLib/AMF/AMFImporter.cpp index 1a3efba9a..07b033b38 100644 --- a/code/AssetLib/AMF/AMFImporter.cpp +++ b/code/AssetLib/AMF/AMFImporter.cpp @@ -268,7 +268,7 @@ void AMFImporter::ParseFile(const std::string &pFile, IOSystem *pIOHandler) { mXmlParser = new XmlParser(); if (!mXmlParser->parse(file.get())) { delete mXmlParser; - throw DeadlyImportError("Failed to create XML reader for file" + pFile + "."); + throw DeadlyImportError("Failed to create XML reader for file ", pFile, "."); } // Start reading, search for root tag From ccd1a4455e3f5611e32cc5f933ec7f3f60316b70 Mon Sep 17 00:00:00 2001 From: Jason C Date: Tue, 4 May 2021 21:14:39 -0400 Subject: [PATCH 062/335] [ply] Fix minor typo in error message. --- code/AssetLib/Ply/PlyLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/Ply/PlyLoader.cpp b/code/AssetLib/Ply/PlyLoader.cpp index ce52636dd..93d48bcbf 100644 --- a/code/AssetLib/Ply/PlyLoader.cpp +++ b/code/AssetLib/Ply/PlyLoader.cpp @@ -172,7 +172,7 @@ void PLYImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy (headerCheck[1] != 'L' && headerCheck[1] != 'l') || (headerCheck[2] != 'Y' && headerCheck[2] != 'y')) { streamedBuffer.close(); - throw DeadlyImportError("Invalid .ply file: Magic number \'ply\' is no there"); + throw DeadlyImportError("Invalid .ply file: Incorrect magic number (expected 'ply' or 'PLY')."); } std::vector mBuffer2; From 6e65115253dbb8806308c8efd399516fcfbd6ba9 Mon Sep 17 00:00:00 2001 From: Jason C Date: Tue, 4 May 2021 21:10:02 -0400 Subject: [PATCH 063/335] [assimp/xml] Improved XML parse error message. Fixed typo, added detail. --- include/assimp/XmlParser.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/assimp/XmlParser.h b/include/assimp/XmlParser.h index f525d3549..bd5281049 100644 --- a/include/assimp/XmlParser.h +++ b/include/assimp/XmlParser.h @@ -136,7 +136,9 @@ public: if (parse_result.status == pugi::status_ok) { return true; } else { - ASSIMP_LOG_DEBUG("Error while parse xml."); + std::ostringstream oss; + oss << "Error while parsing XML: " << parse_result.description() << " @ " << parse_result.offset; + ASSIMP_LOG_DEBUG(oss.str()); return false; } } From f15dcfa98174120ace6077ecbb8e0240bec597be Mon Sep 17 00:00:00 2001 From: kkulling Date: Wed, 5 May 2021 13:10:52 +0200 Subject: [PATCH 064/335] - Fix model parsing --- code/AssetLib/3MF/D3MFImporter.cpp | 49 +++++++++++++++--------------- include/assimp/StringUtils.h | 3 +- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/code/AssetLib/3MF/D3MFImporter.cpp b/code/AssetLib/3MF/D3MFImporter.cpp index f7a3325cb..747af7cfc 100644 --- a/code/AssetLib/3MF/D3MFImporter.cpp +++ b/code/AssetLib/3MF/D3MFImporter.cpp @@ -163,7 +163,7 @@ public: } XmlNode resNode = node.child(XmlTag::resources); for (auto ¤tNode : resNode.children()) { - const std::string ¤tNodeName = currentNode.name(); + const std::string currentNodeName = currentNode.name(); if (currentNodeName == XmlTag::object) { ReadObject(currentNode); } else if (currentNodeName == XmlTag::basematerials) { @@ -173,8 +173,9 @@ public: } } - for (auto ¤tNode : resNode.children()) { - const std::string ¤tNodeName = currentNode.name(); + XmlNode buildNode = node.child(XmlTag::build); + for (auto ¤tNode : buildNode.children()) { + const std::string currentNodeName = currentNode.name(); if (currentNodeName == XmlTag::item) { int objectId = -1; std::string transformationMatrixStr; @@ -196,7 +197,7 @@ public: // import the metadata if (!mMetaData.empty()) { - const size_t numMeta(mMetaData.size()); + const size_t numMeta = mMetaData.size(); scene->mMetaData = aiMetadata::Alloc(static_cast(numMeta)); for (size_t i = 0; i < numMeta; ++i) { aiString val(mMetaData[i].value); @@ -211,6 +212,7 @@ public: for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); ++it) { if (it->second->getType() == ResourceType::RT_Object) { Object *obj = static_cast(it->second); + ai_assert(nullptr != obj); for (unsigned int i = 0; i < obj->mMeshes.size(); ++i) { scene->mMeshes[obj->mMeshIndex[i]] = obj->mMeshes[i]; } @@ -352,7 +354,8 @@ private: mMeshCount++; } else if (currentName == D3MF::XmlTag::components) { for (XmlNode ¤tSubNode : currentNode.children()) { - if (currentSubNode.name() == D3MF::XmlTag::component) { + const std::string subNodeName = currentSubNode.name(); + if (subNodeName == D3MF::XmlTag::component) { int objectId = -1; std::string componentTransformStr; aiMatrix4x4 componentTransform; @@ -360,8 +363,9 @@ private: componentTransform = parseTransformMatrix(componentTransformStr); } - if (getNodeAttribute(currentSubNode, D3MF::XmlTag::objectid, objectId)) + if (getNodeAttribute(currentSubNode, D3MF::XmlTag::objectid, objectId)) { obj->mComponents.push_back({ objectId, componentTransform }); + } } } } @@ -374,7 +378,7 @@ private: aiMesh *mesh = new aiMesh(); for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); + const std::string currentName = currentNode.name(); if (currentName == XmlTag::vertices) { ImportVertices(currentNode, mesh); } else if (currentName == XmlTag::triangles) { @@ -402,8 +406,8 @@ private: void ImportVertices(XmlNode &node, aiMesh *mesh) { std::vector vertices; for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == D3MF::XmlTag::vertex) { + const std::string currentName = currentNode.name(); + if (currentName == XmlTag::vertex) { vertices.push_back(ReadVertex(currentNode)); } } @@ -415,22 +419,22 @@ private: aiVector3D ReadVertex(XmlNode &node) { aiVector3D vertex; - vertex.x = ai_strtof(node.attribute(D3MF::XmlTag::x).as_string(), nullptr); - vertex.y = ai_strtof(node.attribute(D3MF::XmlTag::y).as_string(), nullptr); - vertex.z = ai_strtof(node.attribute(D3MF::XmlTag::z).as_string(), nullptr); + vertex.x = ai_strtof(node.attribute(XmlTag::x).as_string(), nullptr); + vertex.y = ai_strtof(node.attribute(XmlTag::y).as_string(), nullptr); + vertex.z = ai_strtof(node.attribute(XmlTag::z).as_string(), nullptr); return vertex; } void ImportTriangles(XmlNode &node, aiMesh *mesh) { std::vector faces; - for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { - const std::string ¤tName = currentNode.name(); - if (currentName == D3MF::XmlTag::triangle) { + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == XmlTag::triangle) { aiFace face = ReadTriangle(currentNode); faces.push_back(face); - int pid = 0, p1; + int pid = 0, p1 = 0; bool hasPid = getNodeAttribute(currentNode, D3MF::XmlTag::pid, pid); bool hasP1 = getNodeAttribute(currentNode, D3MF::XmlTag::p1, p1); @@ -472,10 +476,11 @@ private: BaseMaterials *baseMaterials = new BaseMaterials(id); for (XmlNode ¤tNode : node.children()) { - if (currentNode.name() == XmlTag::basematerials_base) { + const std::string currentName = currentNode.name(); + if (currentName == XmlTag::basematerials_base) { baseMaterials->mMaterialIndex.push_back(mMaterialCount); baseMaterials->mMaterials.push_back(readMaterialDef(currentNode, id)); - mMaterialCount++; + ++mMaterialCount; } } @@ -489,11 +494,7 @@ private: } //format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1) -#ifdef _WIN32 - const size_t len = strnlen_s(color, 9); -#else - const size_t len = strnlen(color, 9); -#endif + const size_t len = strlen(color); if (9 != len && 7 != len) { return false; } @@ -603,7 +604,7 @@ bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bo if (!ZipArchiveIOSystem::isZipArchive(pIOHandler, filename)) { return false; } - D3MF::D3MFOpcPackage opcPackage(pIOHandler, filename); + D3MFOpcPackage opcPackage(pIOHandler, filename); return opcPackage.validate(); } diff --git a/include/assimp/StringUtils.h b/include/assimp/StringUtils.h index 4afd717cf..d716549e5 100644 --- a/include/assimp/StringUtils.h +++ b/include/assimp/StringUtils.h @@ -4,7 +4,6 @@ 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, @@ -249,4 +248,4 @@ AI_FORCE_INLINE std::string ai_str_toupper(const std::string &in) { return out; } -#endif +#endif // INCLUDED_AI_STRINGUTILS_H From 2a6b84c8ea16f0950803dc6cc62de2801c26a4ee Mon Sep 17 00:00:00 2001 From: kkulling Date: Wed, 5 May 2021 14:43:51 +0200 Subject: [PATCH 065/335] - closes https://github.com/assimp/assimp/issues/3830 - Fix rgba2hex - Add tests --- include/assimp/StringUtils.h | 3 +- test/AssimpLog_C.txt | 3184 ++++++++++ test/AssimpLog_Cpp.txt | 3184 ++++++++++ test/dae.dae | 80 + test/dna.txt | 8008 ++++++++++++++++++++++++ test/spiderExport.stl | 10946 +++++++++++++++++++++++++++++++++ test/test.3mf | Bin 0 -> 1143 bytes test/testExport.stl | 33 + test/unit/utStringUtils.cpp | 13 +- 9 files changed, 25448 insertions(+), 3 deletions(-) create mode 100644 test/AssimpLog_C.txt create mode 100644 test/AssimpLog_Cpp.txt create mode 100644 test/dae.dae create mode 100644 test/dna.txt create mode 100644 test/spiderExport.stl create mode 100644 test/test.3mf create mode 100644 test/testExport.stl diff --git a/include/assimp/StringUtils.h b/include/assimp/StringUtils.h index d716549e5..d536b2f0a 100644 --- a/include/assimp/StringUtils.h +++ b/include/assimp/StringUtils.h @@ -54,6 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include #ifdef _MSC_VER #define AI_SIZEFMT "%Iu" @@ -176,7 +177,7 @@ AI_FORCE_INLINE std::string ai_rgba2hex(int r, int g, int b, int a, bool with_he if (with_head) { ss << "#"; } - ss << std::hex << (r << 24 | g << 16 | b << 8 | a); + ss << std::hex << std::setfill('0') << std::setw(8) << (r << 24 | g << 16 | b << 8 | a); return ss.str(); } diff --git a/test/AssimpLog_C.txt b/test/AssimpLog_C.txt new file mode 100644 index 000000000..b843b6f07 --- /dev/null +++ b/test/AssimpLog_C.txt @@ -0,0 +1,3184 @@ +Info, T34284: Load dae.dae +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Collada Importer. +Info, T34284: Import root directory is '.\' +Debug, T34284: Collada schema version is 1.4.n +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: START `t1` +Debug, T34284: END `t1`, dt= 0.0064414 s +Info, T34284: Load D:/projects/assimp/test/models/OBJ/spider.obj +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 5 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_post_linear.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 5 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_xz_bezier.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 5 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_xz_stepped.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 5 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_oldformat_56.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 2 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_post_offset_repeat.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 5 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_xz_hermite.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 5 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_y_pre_ofrep_post_osc.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 5 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Warn, T34284: Validation warning: aiNodeAnim::mPositionKeys[1].mTime (-72.00000) is smaller than aiAnimation::mPositionKeys[0] (which is 0.00000) +Warn, T34284: Validation warning: aiNodeAnim::mPositionKeys[5].mTime (-21.00000) is smaller than aiAnimation::mPositionKeys[4] (which is 0.00000) +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_oldformat_6.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 3 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_post_repeat.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 5 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_xz_linear.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 5 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_post_constant.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 5 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_post_reset.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 5 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_xz_spline.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 5 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/LWO/LWO2/boxuv.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWO/LWO2\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/LWO/LWO2/formatDetection +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWO/LWO2\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/SMD/triangle.smd +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Valve SMD Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/SMD\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/SMD/holy_grailref.smd +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Valve SMD Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/SMD\' +Error, T34284: [SMD/VTA] Bone index overflow. The bone index will be ignored, the weight will be assigned to the vertex' parent node +Skipping one or more lines with the same contents +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/glTF/TwoBoxes/TwoBoxes.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: glTF Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF/TwoBoxes\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF/IncorrectVertexArrays/Cube_v1.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: glTF Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF/IncorrectVertexArrays\' +Warn, T34284: The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped. +Warn, T34284: The number of vertices was not compatible with the LINES mode. Some vertices were dropped. +Warn, T34284: The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped. +Warn, T34284: The number of vertices was not compatible with the LINES mode. Some vertices were dropped. +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Warn, T34284: Validation warning: There are unreferenced vertices +Skipping one or more lines with the same contents +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF/TwoBoxes/TwoBoxes.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: glTF Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF/TwoBoxes\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxTextured-glTF/BoxTextured.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxTextured-glTF\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 1 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/2CylinderEngine-glTF-Binary/2CylinderEngine.glb +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Reading GLTF2 binary +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/2CylinderEngine-glTF-Binary\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Reading GLTF2 binary +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 34 materials +Debug, T34284: Importing 29 meshes +Debug, T34284: Importing 1 cameras +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxTextured-glTF/BoxTextured.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxTextured-glTF\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 1 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: GenVertexNormalsProcess begin +Debug, T34284: GenVertexNormalsProcess finished. Normals are already there +Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxTextured-glTF-Embedded/BoxTextured.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxTextured-glTF-Embedded\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 1 embedded textures +Debug, T34284: Importing 1 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: GenVertexNormalsProcess begin +Debug, T34284: GenVertexNormalsProcess finished. Normals are already there +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_00.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_01.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_02.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_03.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_04.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_05.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_06.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_07.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_08.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_09.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_10.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_11.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_12.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/simple_skin/simple_skin.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/simple_skin\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 1 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Debug, T34284: ScenePreprocessor: Dummy position track has been generated +Info, T34284: Load D:/projects/assimp/test/models/glTF2/cameras/Cameras.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/cameras\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing 2 cameras +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Warn, T34284: Validation warning: 0.000000 is not a valid value for aiCamera::mHorizontalFOV +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/IncorrectVertexArrays/Cube.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/IncorrectVertexArrays\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 8 meshes +Warn, T34284: The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped. +Warn, T34284: The number of vertices was not compatible with the LINES mode. Some vertices were dropped. +Warn, T34284: The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped. +Warn, T34284: The number of vertices was not compatible with the LINES mode. Some vertices were dropped. +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Warn, T34284: Validation warning: There are unreferenced vertices +Skipping one or more lines with the same contents +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/textureTransform/TextureTransformTest.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/textureTransform\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 9 materials +Debug, T34284: Importing 9 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxTextured-glTF/BoxTextured.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxTextured-glTF\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 1 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: TriangulateProcess begin +Debug, T34284: TriangulateProcess finished. There was nothing to be done. +Debug, T34284: SortByPTypeProcess begin +Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) +Debug, T34284: SortByPTypeProcess finished +Debug, T34284: JoinVerticesProcess begin +Debug, T34284: Mesh 0 (Mesh) | Verts in: 24 out: 24 | ~0% +Debug, T34284: JoinVerticesProcess finished +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Sample-Models/AnimatedMorphCube-glTF/AnimatedMorphCube.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Sample-Models/AnimatedMorphCube-glTF\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 1 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 1 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: TriangulateProcess begin +Debug, T34284: TriangulateProcess finished. There was nothing to be done. +Debug, T34284: SortByPTypeProcess begin +Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) +Debug, T34284: SortByPTypeProcess finished +Debug, T34284: JoinVerticesProcess begin +Debug, T34284: Mesh 0 (Cube) | Verts in: 24 out: 24 | ~0% +Debug, T34284: JoinVerticesProcess finished +Info, T34284: Load D:/projects/assimp/test/models/glTF2/MissingBin/BoxTextured.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxWithInfinites-glTF-Binary/BoxWithInfinites.glb +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Reading GLTF2 binary +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxWithInfinites-glTF-Binary\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Reading GLTF2 binary +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 1 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: TriangulateProcess begin +Debug, T34284: TriangulateProcess finished. There was nothing to be done. +Debug, T34284: SortByPTypeProcess begin +Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) +Debug, T34284: SortByPTypeProcess finished +Debug, T34284: JoinVerticesProcess begin +Debug, T34284: Mesh 0 (Mesh) | Verts in: 24 out: 10 | ~58.3333% +Info, T34284: JoinVerticesProcess finished | Verts in: 24 out: 10 | ~58.3333 +Debug, T34284: TriangulateProcess begin +Debug, T34284: TriangulateProcess finished. There was nothing to be done. +Debug, T34284: SortByPTypeProcess begin +Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) +Debug, T34284: SortByPTypeProcess finished +Debug, T34284: JoinVerticesProcess begin +Debug, T34284: Mesh 0 (Mesh) | Verts in: 24 out: 10 | ~58.3333% +Info, T34284: JoinVerticesProcess finished | Verts in: 24 out: 10 | ~58.3333 +Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxBadNormals-glTF-Binary/BoxBadNormals.glb +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Reading GLTF2 binary +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxBadNormals-glTF-Binary\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Reading GLTF2 binary +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 1 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: TriangulateProcess begin +Debug, T34284: TriangulateProcess finished. There was nothing to be done. +Debug, T34284: SortByPTypeProcess begin +Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) +Debug, T34284: SortByPTypeProcess finished +Debug, T34284: JoinVerticesProcess begin +Debug, T34284: Mesh 0 (Mesh) | Verts in: 24 out: 24 | ~0% +Debug, T34284: JoinVerticesProcess finished +Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxBadNormals-glTF-Binary/BoxBadNormals_out.glb +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: (Deleting previous scene) +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Reading GLTF2 binary +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxBadNormals-glTF-Binary\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Reading GLTF2 binary +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 1 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxTextured-glTF/BoxTextured.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxTextured-glTF\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 1 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxTexcoords-glTF\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 1 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/RecursiveNodes/RecursiveNodes.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Load D:/projects/assimp/test/models/glTF2/TestNoRootNode/NoScene.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/TestNoRootNode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 0 meshes +Error, T34284: GLTF: No scene +Info, T34284: Load D:/projects/assimp/test/models/glTF2/TestNoRootNode/SceneWithoutNodes.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/TestNoRootNode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 0 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/issue_3269/texcoord_crash.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Load D:/projects/assimp/test/models/glTF2/IndexOutOfRange/IndexOutOfRange.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/IndexOutOfRange\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 1 materials +Debug, T34284: Importing 1 meshes +Warn, T34284: Some faces had out-of-range indices. Those faces were dropped. +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Warn, T34284: Validation warning: There are unreferenced vertices +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/IndexOutOfRange/AllIndicesOutOfRange.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/IndexOutOfRange\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 1 materials +Debug, T34284: Importing 1 meshes +Warn, T34284: Some faces had out-of-range indices. Those faces were dropped. +Error, T34284: Mesh "Mesh" has no faces +Info, T34284: Load D:/projects/assimp/test/models/glTF2/draco/2CylinderEngine.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Load D:/projects/assimp/test/models/glTF2/wrongTypes/badArray.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Load D:/projects/assimp/test/models/glTF2/wrongTypes/badString.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Load D:/projects/assimp/test/models/glTF2/wrongTypes/badUint.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Load D:/projects/assimp/test/models/glTF2/wrongTypes/badNumber.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Load D:/projects/assimp/test/models/glTF2/wrongTypes/badObject.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Load D:/projects/assimp/test/models/glTF2/wrongTypes/badExtension.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Load D:/projects/assimp/test/models/HMP/terrain.hmp +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: 3D GameStudio Heightmap (HMP) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/HMP\' +Debug, T34284: HMP subtype: 3D GameStudio A7, magic word is HMP7 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/IFC/AC14-FZK-Haus.ifc +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Industry Foundation Classes (IFC) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/IFC\' +Debug, T34284: IFC: File schema is 'IFC2X3' +Debug, T34284: STEP: got 82226 object records with 5788 inverse index entries +Debug, T34284: IFC: got units used for lengths +Debug, T34284: IFC: got units used for angles +Debug, T34284: IFC: got world coordinate system +Debug, T34284: IFC: looking at spatial structure `Gelände` +Debug, T34284: IFC: selecting this spatial structure as root structure +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Skipping one or more lines with the same contents +Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline +Debug, T34284: IFC: removing duplicate vertices +Error, T34284: IFC: failed to generate all window caps +Skipping one or more lines with the same contents +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: removing degenerate faces +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Skipping one or more lines with the same contents +Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline +Debug, T34284: IFC: removing duplicate vertices +Error, T34284: IFC: failed to generate all window caps +Skipping one or more lines with the same contents +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: removing degenerate faces +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Skipping one or more lines with the same contents +Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline +Debug, T34284: IFC: removing duplicate vertices +Error, T34284: IFC: failed to generate all window caps +Skipping one or more lines with the same contents +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Skipping one or more lines with the same contents +Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline +Debug, T34284: IFC: removing duplicate vertices +Error, T34284: IFC: failed to generate all window caps +Skipping one or more lines with the same contents +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Skipping one or more lines with the same contents +Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline +Debug, T34284: IFC: removing duplicate vertices +Error, T34284: IFC: failed to generate all window caps +Skipping one or more lines with the same contents +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Skipping one or more lines with the same contents +Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline +Debug, T34284: IFC: removing duplicate vertices +Error, T34284: IFC: failed to generate all window caps +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: removing degenerate faces +Skipping one or more lines with the same contents +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: removing degenerate faces +Skipping one or more lines with the same contents +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: skipping IfcAnnotation entity due to importer settings +Skipping one or more lines with the same contents +Debug, T34284: IFC: skipping IfcSpace entity due to importer settings +Skipping one or more lines with the same contents +Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: generating CSG geometry by plane clipping (IfcBooleanClippingResult) +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline +Debug, T34284: IFC: removing duplicate vertices +Error, T34284: IFC: failed to generate all window caps +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: generating CSG geometry by plane clipping with polygonal bounding (IfcBooleanClippingResult) +Skipping one or more lines with the same contents +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: removing degenerate faces +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: removing degenerate faces +Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: generating CSG geometry by plane clipping (IfcBooleanClippingResult) +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline +Debug, T34284: IFC: removing duplicate vertices +Error, T34284: IFC: failed to generate all window caps +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: generating CSG geometry by plane clipping with polygonal bounding (IfcBooleanClippingResult) +Skipping one or more lines with the same contents +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: removing degenerate faces +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: generating CSG geometry by plane clipping (IfcBooleanClippingResult) +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: generating CSG geometry by plane clipping (IfcBooleanClippingResult) +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Skipping one or more lines with the same contents +Debug, T34284: IFC: generating CSG geometry by plane clipping (IfcBooleanClippingResult) +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: generating CSG geometry by plane clipping (IfcBooleanClippingResult) +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: removing duplicate vertices +Warn, T34284: IFC: failed to resolve all openings, presumably their topology is not supported by Assimp +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: removing degenerate faces +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: skipping IfcAnnotation entity due to importer settings +Skipping one or more lines with the same contents +Debug, T34284: IFC: skipping IfcSpace entity due to importer settings +Debug, T34284: IFC: STEP: evaluated 70094 object records +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load $$$___magic___$$$. +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Found positive match for header keyword: HEADER +Info, T34284: Found a matching importer for this file format: Drawing Interchange Format (DXF) Importer. +Info, T34284: Import root directory is '.\' +Warn, T34284: DXF: EOF reached, but did not encounter DXF EOF marker +Debug, T34284: DXF: Unexpanded polycount is 0, vertex count is 0 +Error, T34284: DXF: no data blocks loaded +Info, T34284: Load D:/projects/assimp/test/models/FBX/spider.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing binary FBX file +Debug, T34284: FBX version: 7400 +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7400 +Debug, T34284: UpdateImporterScale scale set: 0.01 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/box.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing binary FBX file +Debug, T34284: FBX version: 7400 +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7400 +Info, T34284: FBX: generating full transformation chain for node: root +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/cubes_nonames.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing ASCII FBX file +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7500 +Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless +Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 +Debug, T34284: UpdateImporterScale scale set: 0.01 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/cubes_with_names.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing ASCII FBX file +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7500 +Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless +Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 +Debug, T34284: UpdateImporterScale scale set: 0.01 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/cubes_with_mirroring_and_pivot.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing ASCII FBX file +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7500 +Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless +Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 +Info, T34284: FBX: generating full transformation chain for node: Cube1 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/close_to_identity_transforms.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing ASCII FBX file +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7500 +Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless +Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 +Info, T34284: FBX: generating full transformation chain for node: Cube1 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/phong_cube.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing binary FBX file +Debug, T34284: FBX version: 7400 +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7400 +Warn, T34284: FBX-DOM (TOK_KEY, offset 0x4190) source object for connection does not exist +Debug, T34284: UpdateImporterScale scale set: 0.01 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/global_settings.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing binary FBX file +Debug, T34284: FBX version: 7400 +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7400 +Error, T34284: FBX: no material assigned to mesh, setting default material +Debug, T34284: UpdateImporterScale scale set: 5 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/embedded_ascii/box.FBX +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX/embedded_ascii\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing ASCII FBX file +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7400 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Warn, T34284: Validation warning: A specular shading model is specified but the value of the AI_MATKEY_SHININESS_STRENGTH key is 0.0 +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/embedded_ascii/box_embedded_texture_fragmented.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX/embedded_ascii\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing ASCII FBX file +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7500 +Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless +Error, T34284: FBX: ignoring additional binormal layer +Error, T34284: FBX: ignoring additional tangent layer +Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/box_orphant_embedded_texture.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing ASCII FBX file +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7500 +Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless +Error, T34284: FBX: ignoring additional binormal layer +Error, T34284: FBX: ignoring additional tangent layer +Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/global_settings.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing binary FBX file +Debug, T34284: FBX version: 7400 +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7400 +Error, T34284: FBX: no material assigned to mesh, setting default material +Debug, T34284: UpdateImporterScale scale set: 5 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/cubes_with_outofrange_float.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing ASCII FBX file +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7500 +Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless +Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 +Debug, T34284: UpdateImporterScale scale set: 0.01 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/maxPbrMaterial_metalRough.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing ASCII FBX file +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7700 +Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless +Warn, T34284: FBX-DOM (TOK_KEY, line 679, col 13) shading mode not recognized: unknown +Info, T34284: FBX: generating full transformation chain for node: Box001 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/maxPbrMaterial_specGloss.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing ASCII FBX file +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7700 +Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless +Warn, T34284: FBX-DOM (TOK_KEY, line 679, col 13) shading mode not recognized: unknown +Info, T34284: FBX: generating full transformation chain for node: Box001 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load $$$___magic___$$$.3ds +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Discreet 3DS Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: 3DS file format version: 3 +Warn, T34284: 3DS: Skipping TCB animation info +Skipping one or more lines with the same contents +Error, T34284: 3DS: Skipping FOV animation track. This is not supported +Warn, T34284: 3DS: Skipping TCB animation info +Skipping one or more lines with the same contents +Info, T34284: 3DS: Generating default material +Debug, T34284: 3DS: Converting camera roll track ... +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Debug, T34284: ScenePreprocessor: Setting animation duration +Info, T34284: Entering post processing pipeline +Debug, T34284: RemoveRedundantMatsProcess begin +Debug, T34284: RemoveRedundantMatsProcess finished +Debug, T34284: GenUVCoordsProcess begin +Debug, T34284: GenUVCoordsProcess finished +Debug, T34284: TriangulateProcess begin +Debug, T34284: TriangulateProcess finished. There was nothing to be done. +Debug, T34284: FindDegeneratesProcess begin +Debug, T34284: FindDegeneratesProcess finished +Debug, T34284: SortByPTypeProcess begin +Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) +Debug, T34284: SortByPTypeProcess finished +Debug, T34284: FindInvalidDataProcess begin +Info, T34284: FindInvalidDataProcess finished. Found issues ... +Debug, T34284: SplitLargeMeshesProcess_Triangle begin +Debug, T34284: SplitLargeMeshesProcess_Triangle finished. There was nothing to do +Debug, T34284: Generate spatially-sorted vertex cache +Debug, T34284: GenVertexNormalsProcess begin +Debug, T34284: GenVertexNormalsProcess finished. Normals are already there +Debug, T34284: CalcTangentsProcess begin +Info, T34284: CalcTangentsProcess finished. Tangents have been calculated +Debug, T34284: JoinVerticesProcess begin +Debug, T34284: Mesh 0 (0) | Verts in: 36 out: 24 | ~33.3333% +Info, T34284: JoinVerticesProcess finished | Verts in: 36 out: 24 | ~33.3333 +Debug, T34284: SplitLargeMeshesProcess_Vertex begin +Debug, T34284: SplitLargeMeshesProcess_Vertex finished. There was nothing to do +Debug, T34284: LimitBoneWeightsProcess begin +Debug, T34284: LimitBoneWeightsProcess end +Debug, T34284: ImproveCacheLocalityProcess begin +Debug, T34284: Mesh %u | ACMR in: 0 out: 2 | ~20 +Info, T34284: Cache relevant are 1 meshes (12 faces). Average output ACMR is 2 +Debug, T34284: ImproveCacheLocalityProcess finished. +Info, T34284: Leaving post processing pipeline +Info, T34284: Registering custom importer for these file extensions: applelinuxmacwindows +Info, T34284: Unregistering custom importer: +Info, T34284: Load D:/projects/assimp/test/models/X/test.x +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Warn, T34284: Unknown data object in mesh in x file +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Entering post processing pipeline +Debug, T34284: RemoveRedundantMatsProcess begin +Debug, T34284: RemoveRedundantMatsProcess finished +Debug, T34284: OptimizeGraphProcess begin +Debug, T34284: OptimizeGraphProcess finished +Debug, T34284: GenUVCoordsProcess begin +Debug, T34284: GenUVCoordsProcess finished +Debug, T34284: TriangulateProcess begin +Debug, T34284: TriangulateProcess finished. There was nothing to be done. +Debug, T34284: FindDegeneratesProcess begin +Debug, T34284: FindDegeneratesProcess finished +Debug, T34284: SortByPTypeProcess begin +Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) +Debug, T34284: SortByPTypeProcess finished +Debug, T34284: FindInvalidDataProcess begin +Info, T34284: FindInvalidDataProcess finished. Found issues ... +Debug, T34284: Skipping OptimizeMeshesProcess +Debug, T34284: Generate spatially-sorted vertex cache +Debug, T34284: GenVertexNormalsProcess begin +Debug, T34284: GenVertexNormalsProcess finished. Normals are already there +Debug, T34284: JoinVerticesProcess begin +Debug, T34284: Mesh 0 (pCube1) | Verts in: 36 out: 24 | ~33.3333% +Info, T34284: JoinVerticesProcess finished | Verts in: 36 out: 24 | ~33.3333 +Info, T34284: Leaving post processing pipeline +Info, T34284: Load D:/projects/assimp/test/models/X/Testwuson.X +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: (Deleting previous scene) +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Warn, T34284: Unknown data object in animation of .x file +Skipping one or more lines with the same contents +Warn, T34284: Unknown data object in frame in x file +Skipping one or more lines with the same contents +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Entering post processing pipeline +Debug, T34284: RemoveRedundantMatsProcess begin +Debug, T34284: RemoveRedundantMatsProcess finished +Debug, T34284: OptimizeGraphProcess begin +Debug, T34284: OptimizeGraphProcess finished +Debug, T34284: GenUVCoordsProcess begin +Debug, T34284: GenUVCoordsProcess finished +Debug, T34284: TriangulateProcess begin +Debug, T34284: TriangulateProcess finished. There was nothing to be done. +Debug, T34284: FindDegeneratesProcess begin +Debug, T34284: FindDegeneratesProcess finished +Debug, T34284: SortByPTypeProcess begin +Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) +Debug, T34284: SortByPTypeProcess finished +Debug, T34284: FindInvalidDataProcess begin +Info, T34284: FindInvalidDataProcess finished. Found issues ... +Debug, T34284: Skipping OptimizeMeshesProcess +Debug, T34284: Generate spatially-sorted vertex cache +Debug, T34284: GenVertexNormalsProcess begin +Debug, T34284: GenVertexNormalsProcess finished. Normals are already there +Debug, T34284: JoinVerticesProcess begin +Debug, T34284: Mesh 0 (Wuson) | Verts in: 11196 out: 3205 | ~71.3737% +Info, T34284: JoinVerticesProcess finished | Verts in: 11196 out: 3205 | ~71.3737 +Info, T34284: Leaving post processing pipeline +Info, T34284: Load D:/projects/assimp/test/models/X/anim_test.x +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: (Deleting previous scene) +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Warn, T34284: Length of texture file name is zero. Skipping this texture. +Warn, T34284: Unknown data object in mesh in x file +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Entering post processing pipeline +Debug, T34284: RemoveRedundantMatsProcess begin +Debug, T34284: RemoveRedundantMatsProcess finished +Debug, T34284: OptimizeGraphProcess begin +Debug, T34284: OptimizeGraphProcess finished +Debug, T34284: GenUVCoordsProcess begin +Debug, T34284: GenUVCoordsProcess finished +Debug, T34284: TriangulateProcess begin +Debug, T34284: TriangulateProcess finished. There was nothing to be done. +Debug, T34284: FindDegeneratesProcess begin +Debug, T34284: FindDegeneratesProcess finished +Debug, T34284: SortByPTypeProcess begin +Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) +Debug, T34284: SortByPTypeProcess finished +Debug, T34284: FindInvalidDataProcess begin +Warn, T34284: Simplified dummy tracks with just one key +Skipping one or more lines with the same contents +Info, T34284: FindInvalidDataProcess finished. Found issues ... +Debug, T34284: Skipping OptimizeMeshesProcess +Debug, T34284: Generate spatially-sorted vertex cache +Debug, T34284: GenVertexNormalsProcess begin +Debug, T34284: GenVertexNormalsProcess finished. Normals are already there +Debug, T34284: JoinVerticesProcess begin +Debug, T34284: Mesh 0 (pCylinder1) | Verts in: 2520 out: 485 | ~80.754% +Info, T34284: JoinVerticesProcess finished | Verts in: 2520 out: 485 | ~80.754 +Info, T34284: Leaving post processing pipeline +Info, T34284: Load D:/projects/assimp/test/models/X/anim_test.x +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: (Deleting previous scene) +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Warn, T34284: Length of texture file name is zero. Skipping this texture. +Warn, T34284: Unknown data object in mesh in x file +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Entering post processing pipeline +Debug, T34284: RemoveRedundantMatsProcess begin +Debug, T34284: RemoveRedundantMatsProcess finished +Debug, T34284: OptimizeGraphProcess begin +Debug, T34284: OptimizeGraphProcess finished +Debug, T34284: GenUVCoordsProcess begin +Debug, T34284: GenUVCoordsProcess finished +Debug, T34284: TriangulateProcess begin +Debug, T34284: TriangulateProcess finished. There was nothing to be done. +Debug, T34284: FindDegeneratesProcess begin +Debug, T34284: FindDegeneratesProcess finished +Debug, T34284: SortByPTypeProcess begin +Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) +Debug, T34284: SortByPTypeProcess finished +Debug, T34284: FindInvalidDataProcess begin +Warn, T34284: Simplified dummy tracks with just one key +Skipping one or more lines with the same contents +Info, T34284: FindInvalidDataProcess finished. Found issues ... +Debug, T34284: Skipping OptimizeMeshesProcess +Debug, T34284: Generate spatially-sorted vertex cache +Debug, T34284: GenVertexNormalsProcess begin +Debug, T34284: GenVertexNormalsProcess finished. Normals are already there +Debug, T34284: JoinVerticesProcess begin +Debug, T34284: Mesh 0 (pCylinder1) | Verts in: 2520 out: 485 | ~80.754% +Info, T34284: JoinVerticesProcess finished | Verts in: 2520 out: 485 | ~80.754 +Info, T34284: Leaving post processing pipeline +Info, T34284: Load D:/projects/assimp/test/models/X/BCN_Epileptic.X +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: (Deleting previous scene) +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Warn, T34284: Unknown data object in animation of .x file +Skipping one or more lines with the same contents +Warn, T34284: Unknown data object in frame in x file +Skipping one or more lines with the same contents +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Entering post processing pipeline +Debug, T34284: RemoveRedundantMatsProcess begin +Debug, T34284: RemoveRedundantMatsProcess finished +Debug, T34284: OptimizeGraphProcess begin +Debug, T34284: OptimizeGraphProcess finished +Debug, T34284: GenUVCoordsProcess begin +Debug, T34284: GenUVCoordsProcess finished +Debug, T34284: TriangulateProcess begin +Debug, T34284: TriangulateProcess finished. There was nothing to be done. +Debug, T34284: FindDegeneratesProcess begin +Debug, T34284: FindDegeneratesProcess finished +Debug, T34284: SortByPTypeProcess begin +Info, T34284: Points: 0, Lines: 0, Triangles: 3, Polygons: 0 (Meshes, X = removed) +Debug, T34284: SortByPTypeProcess finished +Debug, T34284: FindInvalidDataProcess begin +Info, T34284: FindInvalidDataProcess finished. Found issues ... +Debug, T34284: OptimizeMeshesProcess begin +Debug, T34284: OptimizeMeshesProcess finished +Debug, T34284: Generate spatially-sorted vertex cache +Debug, T34284: GenVertexNormalsProcess begin +Debug, T34284: GenVertexNormalsProcess finished. Normals are already there +Debug, T34284: JoinVerticesProcess begin +Debug, T34284: Mesh 0 (Torso) | Verts in: 5898 out: 1170 | ~80.1628% +Debug, T34284: Mesh 1 (Head) | Verts in: 6108 out: 1196 | ~80.4191% +Debug, T34284: Mesh 2 (Legs) | Verts in: 3372 out: 648 | ~80.7829% +Info, T34284: JoinVerticesProcess finished | Verts in: 15378 out: 3014 | ~80.4006 +Info, T34284: Leaving post processing pipeline +Info, T34284: Registering custom importer for these file extensions: fail +Info, T34284: Load deadlyImportError.fail +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Failing importer. +Info, T34284: Import root directory is './' +Error, T34284: Deadly import error test. Details: 42 More Details: Failure +Info, T34284: Registering custom importer for these file extensions: fail +Info, T34284: Load stdException.fail +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Failing importer. +Info, T34284: Import root directory is './' +Error, T34284: std::exception test +Info, T34284: Registering custom importer for these file extensions: fail +Info, T34284: Load unexpectedException.fail +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Failing importer. +Info, T34284: Import root directory is './' +Info, T34284: Load D:/projects/assimp/test/models/3D/box_a.3d +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Unreal Mesh Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3D\' +Debug, T34284: UNREAL: data file is D:/projects/assimp/test/models/3D/box_d.3d +Debug, T34284: UNREAL: aniv file is D:/projects/assimp/test/models/3D/box_a.3d +Debug, T34284: UNREAL: uc file is D:/projects/assimp/test/models/3D/box.uc +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/3D/box_d.3d +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Unreal Mesh Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3D\' +Debug, T34284: UNREAL: data file is D:/projects/assimp/test/models/3D/box_d.3d +Debug, T34284: UNREAL: aniv file is D:/projects/assimp/test/models/3D/box_a.3d +Debug, T34284: UNREAL: uc file is D:/projects/assimp/test/models/3D/box.uc +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/3D/box.uc +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Unreal Mesh Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3D\' +Debug, T34284: UNREAL: data file is D:/projects/assimp/test/models/3D/box_d.3d +Debug, T34284: UNREAL: aniv file is D:/projects/assimp/test/models/3D/box_a.3d +Debug, T34284: UNREAL: uc file is D:/projects/assimp/test/models/3D/box.uc +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/3DS/fels.3ds +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Discreet 3DS Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3DS\' +Info, T34284: 3DS file format version: 3 +Warn, T34284: No hierarchy information has been found in the file. +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/3DS/testFormatDetection +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Discreet 3DS Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3DS\' +Info, T34284: 3DS file format version: 3 +Warn, T34284: 3DS: Skipping TCB animation info +Skipping one or more lines with the same contents +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/AC/closedLine.ac +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: AC3D Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' +Info, T34284: AC3D file format version: 11 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/AC/nosurfaces.ac +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: AC3D Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' +Info, T34284: AC3D file format version: 11 +Warn, T34284: AC3D: No material has been found +Info, T34284: AC3D: No surfaces defined in object definition, a point list is returned +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/AC/openLine.ac +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: AC3D Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' +Info, T34284: AC3D file format version: 11 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/AC/sample_subdiv.ac +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: AC3D Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' +Info, T34284: AC3D file format version: 11 +Info, T34284: AC3D: Evaluating subdivision surface: cylinder +Debug, T34284: Catmull-Clark Subdivider: got 130 bad edges touching only one face (totally 61505 edges). +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/AC/SphereWithLight.ac +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: AC3D Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' +Info, T34284: AC3D file format version: 11 +Debug, T34284: AC3D: Light source encountered +Info, T34284: AC3D: Evaluating subdivision surface: sphere +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/AC/SphereWithLight_UTF16LE.ac +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: AC3D Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' +Debug, T34284: Found UTF-16 BOM ... +Error, T34284: AC3D: No valid AC3D file, magic sequence not found +Info, T34284: Load D:/projects/assimp/test/models/AC/SphereWithLight_UTF8BOM.ac +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: AC3D Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' +Debug, T34284: Found UTF-8 BOM ... +Info, T34284: AC3D file format version: 11 +Debug, T34284: AC3D: Light source encountered +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/AC/SphereWithLightUvScaling4X.ac +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: AC3D Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' +Info, T34284: AC3D file format version: 11 +Debug, T34284: AC3D: Light source encountered +Info, T34284: AC3D: Evaluating subdivision surface: sphere +Debug, T34284: Catmull-Clark Subdivider: got 12 bad edges touching only one face (totally 17670 edges). +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/AC/Wuson.ac +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: AC3D Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' +Info, T34284: AC3D file format version: 11 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/AC/Wuson.acc +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: AC3D Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' +Info, T34284: AC3D file format version: 11 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/AC/TestFormatDetection +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: AC3D Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' +Info, T34284: AC3D file format version: 11 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/AMF/test1.amf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Additive manufacturing file format(AMF) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AMF\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Adding default material 'DefaultMaterial' +Info, T34284: Load D:/projects/assimp/test/models/AMF/test_with_mat.amf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Additive manufacturing file format(AMF) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AMF\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Adding default material 'DefaultMaterial' +Info, T34284: Load D:/projects/assimp/test/models/ASE/ThreeCubesGreen.ASE +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: ASE Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/ASE\' +Info, T34284: Line 1: Comment: AsciiExport-Version 2,00 - Tue May 06 17:21:38 2008 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/3MF/box.3mf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: 3mf Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3MF\' +Warn, T34284: Ignored file of unknown type: 3D/3dmodel.model +Warn, T34284: Ignored file of unsupported type CONTENT_TYPES_ARCHIVES[Content_Types].xml +Debug, T34284: 3D/3dmodel.model +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Adding default material 'DefaultMaterial' +Info, T34284: Load D:/projects/assimp/test/models/3MF/box.3mf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: 3mf Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3MF\' +Warn, T34284: Ignored file of unknown type: 3D/3dmodel.model +Warn, T34284: Ignored file of unsupported type CONTENT_TYPES_ARCHIVES[Content_Types].xml +Debug, T34284: 3D/3dmodel.model +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ScenePreprocessor: Adding default material 'DefaultMaterial' +Info, T34284: Load D:/projects/assimp/test/models/3MF/box.3mf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: 3mf Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3MF\' +Warn, T34284: Ignored file of unknown type: 3D/3dmodel.model +Warn, T34284: Ignored file of unsupported type CONTENT_TYPES_ARCHIVES[Content_Types].xml +Debug, T34284: 3D/3dmodel.model +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ScenePreprocessor: Adding default material 'DefaultMaterial' +Info, T34284: Load test.3mf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: 3mf Importer. +Info, T34284: Import root directory is '.\' +Warn, T34284: Ignored file of unknown type: 3D/3DModel.model +Warn, T34284: Ignored file of unsupported type CONTENT_TYPES_ARCHIVES[Content_Types].xml +Debug, T34284: 3D/3DModel.model +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: Load D:/projects/assimp/test/models/Q3D/earth.q3o +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Quick3D Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/Q3D\' +Info, T34284: Quick3D File format version: 30 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/STL/Spider_ascii.stl +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/STL/Spider_ascii.stl +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/STL/Spider_ascii.stl +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/STL/formatDetection +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Found positive match for header keyword: solid +Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/STL/triangle_with_two_solids.stl +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/STL/triangle_with_empty_solid.stl +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' +Warn, T34284: STL: mesh is empty or invalid; no data loaded +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: Load D:/projects/assimp/test/models/STL/triangle_with_empty_solid.stl +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: (Deleting previous scene) +Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' +Warn, T34284: STL: mesh is empty or invalid; no data loaded +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Error, T34284: Validation failed: The mesh emptySolid contains no vertices +Info, T34284: Load D:/projects/assimp/test/models/STL/Spider_ascii.stl +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: PretransformVerticesProcess begin +Debug, T34284: PretransformVerticesProcess finished +Info, T34284: Removed 2 nodes and 0 animation channels (1 output nodes) +Info, T34284: Kept 0 lights and 0 cameras. +Info, T34284: Moved 1 meshes to WCS (number of output meshes: 1) +Debug, T34284: TriangulateProcess begin +Debug, T34284: TriangulateProcess finished. There was nothing to be done. +Debug, T34284: GenFaceNormalsProcess begin +Debug, T34284: GenFaceNormalsProcess finished. Normals are already there +Info, T34284: Load spiderExport.stl +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: (Deleting previous scene) +Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. +Info, T34284: Import root directory is '.\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: TriangulateProcess begin +Debug, T34284: TriangulateProcess finished. There was nothing to be done. +Debug, T34284: GenFaceNormalsProcess begin +Info, T34284: Normal vectors are undefined for line and point meshes +Debug, T34284: GenFaceNormalsProcess finished. Normals are already there +Info, T34284: Load D:/projects/assimp/test/models/X/test.x +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Warn, T34284: Unknown data object in mesh in x file +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/X/OV_GetNextToken +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Info, T34284: Successfully decompressed MSZIP-compressed file +Error, T34284: Opening brace expected. +Info, T34284: Load D:/projects/assimp/test/models/X/anim_test.x +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Warn, T34284: Length of texture file name is zero. Skipping this texture. +Warn, T34284: Unknown data object in mesh in x file +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/X/BCN_Epileptic.X +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Warn, T34284: Unknown data object in animation of .x file +Skipping one or more lines with the same contents +Warn, T34284: Unknown data object in frame in x file +Skipping one or more lines with the same contents +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/X/fromtruespace_bin32.x +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Warn, T34284: Unknown data object in animation of .x file +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/X/kwxport_test_cubewithvcolors.x +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Warn, T34284: Unknown data object in animation of .x file +Skipping one or more lines with the same contents +Warn, T34284: Unknown data object in frame in x file +Warn, T34284: Unknown data object in mesh in x file +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/X/test_cube_binary.x +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/X/test_cube_compressed.x +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Info, T34284: Successfully decompressed MSZIP-compressed file +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/X/test_cube_text.x +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/X/Testwuson.X +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Warn, T34284: Unknown data object in animation of .x file +Skipping one or more lines with the same contents +Warn, T34284: Unknown data object in frame in x file +Skipping one or more lines with the same contents +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/X/TestFormatDetection +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Warn, T34284: Unknown data object in mesh in x file +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models-nonbsd/X/dwarf.x +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models-nonbsd/X\' +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Skipping one or more lines with the same contents +Info, T34284: Load D:/projects/assimp/test/models/X3D/ComputerKeyboard.x3d +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: File extension not known, trying signature-based detection +Info, T34284: Found a matching importer for this file format: Extensible 3D(X3D) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X3D\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Error, T34284: Validation failed: aiScene::mNumMeshes is 0. At least one mesh must be there +Info, T34284: Load D:/projects/assimp/test/models/DXF/PinkEggFromLW.dxf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Drawing Interchange Format (DXF) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/DXF\' +Debug, T34284: DXF: got 0 entries in BLOCKS +Debug, T34284: DXF: got 288 polylines and 0 inserted blocks in ENTITIES +Debug, T34284: DXF: Unexpanded polycount is 288, vertex count is 1152 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/DXF/lineTest +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Found positive match for header keyword: SECTION +Info, T34284: Found a matching importer for this file format: Drawing Interchange Format (DXF) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/DXF\' +Info, T34284: DXF Comment: VISION3D DXF +Debug, T34284: DXF: got 0 entries in BLOCKS +Debug, T34284: DXF: got 3 polylines and 0 inserted blocks in ENTITIES +Debug, T34284: DXF: Unexpanded polycount is 3, vertex count is 9 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/DXF/issue_2229.dxf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Drawing Interchange Format (DXF) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/DXF\' +Debug, T34284: DXF: skipped over control group (273 lines) +Debug, T34284: DXF: got 2 entries in BLOCKS +Warn, T34284: DXF: invalid vertex index, indices are one-based. +Skipping one or more lines with the same contents +Warn, T34284: DXF: unexpected face count in polymesh: 1173, expected 1174 +Warn, T34284: DXF: invalid vertex index, indices are one-based. +Skipping one or more lines with the same contents +Debug, T34284: DXF: got 4 polylines and 0 inserted blocks in ENTITIES +Debug, T34284: DXF: skipped over control group (3 lines) +Skipping one or more lines with the same contents +Debug, T34284: DXF: Unexpanded polycount is 1289, vertex count is 768 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/PLY/cube.ply +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' +Debug, T34284: PLY::DOM::ParseInstance() begin +Debug, T34284: PLY::DOM::ParseHeader() begin +Debug, T34284: PLY::DOM::ParseHeader() succeeded +Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin +Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded +Debug, T34284: PLY::DOM::ParseInstance() succeeded +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/PLY/cube.ply +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' +Debug, T34284: PLY::DOM::ParseInstance() begin +Debug, T34284: PLY::DOM::ParseHeader() begin +Debug, T34284: PLY::DOM::ParseHeader() succeeded +Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin +Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded +Debug, T34284: PLY::DOM::ParseInstance() succeeded +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: PretransformVerticesProcess begin +Debug, T34284: PretransformVerticesProcess finished +Info, T34284: Removed 1 nodes and 0 animation channels (1 output nodes) +Info, T34284: Kept 0 lights and 0 cameras. +Info, T34284: Moved 1 meshes to WCS (number of output meshes: 1) +Info, T34284: Load D:/projects/assimp/test/models/PLY/cube.ply +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' +Debug, T34284: PLY::DOM::ParseInstance() begin +Debug, T34284: PLY::DOM::ParseHeader() begin +Debug, T34284: PLY::DOM::ParseHeader() succeeded +Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin +Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded +Debug, T34284: PLY::DOM::ParseInstance() succeeded +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/PLY/cube.ply +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: (Deleting previous scene) +Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' +Debug, T34284: PLY::DOM::ParseInstance() begin +Debug, T34284: PLY::DOM::ParseHeader() begin +Debug, T34284: PLY::DOM::ParseHeader() succeeded +Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin +Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded +Debug, T34284: PLY::DOM::ParseInstance() succeeded +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/PLY/cube_uv.ply +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' +Debug, T34284: PLY::DOM::ParseInstance() begin +Debug, T34284: PLY::DOM::ParseHeader() begin +Debug, T34284: PLY::DOM::ParseHeader() succeeded +Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin +Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded +Debug, T34284: PLY::DOM::ParseInstance() succeeded +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/PLY/cube_binary.ply +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' +Debug, T34284: PLY::DOM::ParseInstanceBinary() begin +Debug, T34284: PLY::DOM::ParseHeader() begin +Debug, T34284: PLY::DOM::ParseHeader() succeeded +Debug, T34284: PLY::DOM::ParseElementInstanceListsBinary() begin +Debug, T34284: PLY::DOM::ParseElementInstanceListsBinary() succeeded +Debug, T34284: PLY::DOM::ParseInstanceBinary() succeeded +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/PLY/float-color.ply +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' +Debug, T34284: PLY::DOM::ParseInstance() begin +Debug, T34284: PLY::DOM::ParseHeader() begin +Debug, T34284: PLY::DOM::ParseHeader() succeeded +Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin +Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded +Debug, T34284: PLY::DOM::ParseInstance() succeeded +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/PLY/issue623.ply +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' +Debug, T34284: PLY::DOM::ParseInstance() begin +Debug, T34284: PLY::DOM::ParseHeader() begin +Debug, T34284: PLY::DOM::ParseHeader() succeeded +Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin +Warn, T34284: Unable to parse property instance. Skipping this element instance +Skipping one or more lines with the same contents +Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded +Debug, T34284: PLY::DOM::ParseInstance() succeeded +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: Load $$$___magic___$$$. +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Found positive match for header keyword: ply +Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. +Info, T34284: Import root directory is '.\' +Debug, T34284: PLY::DOM::ParseInstance() begin +Debug, T34284: PLY::DOM::ParseHeader() begin +Debug, T34284: PLY::DOM::ParseHeader() succeeded +Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin +Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded +Debug, T34284: PLY::DOM::ParseInstance() succeeded +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: Load D:/projects/assimp/test/models/OBJ/spider.obj +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/OBJ/spider.obj +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: GenVertexNormalsProcess begin +Debug, T34284: GenVertexNormalsProcess finished. Normals are already there +Debug, T34284: GenVertexNormalsProcess begin +Debug, T34284: GenVertexNormalsProcess finished. Normals are already there +Info, T34284: Load $$$___magic___$$$. +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: File extension not known, trying signature-based detection +Debug, T34284: Found positive match for header keyword: usemtl +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is '.\' +Error, T34284: OBJ: failed to locate material Default, creating new material +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: Load $$$___magic___$$$. +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: File extension not known, trying signature-based detection +Debug, T34284: Found positive match for header keyword: usemtl +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is '.\' +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: Load D:/projects/assimp/test/models/OBJ/cube_with_vertexcolors.obj +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: GenVertexNormalsProcess begin +Debug, T34284: GenVertexNormalsProcess finished. Normals are already there +Info, T34284: Load D:/projects/assimp/test/models/OBJ/cube_with_vertexcolors_uni.obj +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: GenVertexNormalsProcess begin +Debug, T34284: GenVertexNormalsProcess finished. Normals are already there +Info, T34284: Load $$$___magic___$$$. +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: File extension not known, trying signature-based detection +Debug, T34284: Found positive match for header keyword: mtllib +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is '.\' +Error, T34284: OBJ: Unable to locate material file $blobfile.mtl +Info, T34284: OBJ: Opening fallback material file $$$___magic___$mtl +Error, T34284: OBJ: Unable to locate fallback material file $$$___magic___$mtl +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load $$$___magic___$$$. +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: File extension not known, trying signature-based detection +Debug, T34284: Found positive match for header keyword: v +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is '.\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Error, T34284: Validation failed: Mesh contains no faces +Info, T34284: Load $$$___magic___$$$. +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: File extension not known, trying signature-based detection +Debug, T34284: Found positive match for header keyword: v +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is '.\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load $$$___magic___$$$. +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: File extension not known, trying signature-based detection +Debug, T34284: Found positive match for header keyword: v +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is '.\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load $$$___magic___$$$. +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: File extension not known, trying signature-based detection +Debug, T34284: Found positive match for header keyword: v +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is '.\' +Error, T34284: OBJ: Invalid component in homogeneous vector (Division by zero) +Info, T34284: Load $$$___magic___$$$. +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: File extension not known, trying signature-based detection +Debug, T34284: Found positive match for header keyword: v +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is '.\' +Error, T34284: OBJ: Invalid face indice +Info, T34284: Load $$$___magic___$$$. +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: File extension not known, trying signature-based detection +Debug, T34284: Found positive match for header keyword: v +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is '.\' +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: Load $$$___magic___$$$. +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: File extension not known, trying signature-based detection +Debug, T34284: Found positive match for header keyword: v +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is '.\' +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: Load D:/projects/assimp/test/models/OBJ/cube_mtllib_after_g.obj +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' +Error, T34284: OBJ: Unable to locate material file cube_mtllib_after_g.mat +Info, T34284: OBJ: Opening fallback material file D:/projects/assimp/test/models/OBJ/cube_mtllib_after_g.mtl +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/OBJ/point_cloud.obj +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ScenePreprocessor: Adding default material 'DefaultMaterial' +Info, T34284: Load D:/projects/assimp/test/models/OBJ/box_without_lineending.obj +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' +Error, T34284: OBJ: failed to locate material Default, creating new material +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: Load $$$___magic___$$$. +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: File extension not known, trying signature-based detection +Debug, T34284: Found positive match for header keyword: v +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is '.\' +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: Load D:/projects/assimp/test/models/OpenGEX/Example.ogex +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Open Game Engine Exchange. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OpenGEX\' +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: Load D:/projects/assimp/test/models/OpenGEX/light_issue1262.ogex +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Open Game Engine Exchange. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OpenGEX\' +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: Load D:/projects/assimp/test/models/OpenGEX/empty_camera.ogex +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Open Game Engine Exchange. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OpenGEX\' +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: Load D:/projects/assimp/test/models/SIB/heffalump.sib +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Silo SIB Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/SIB\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/BLEND/AreaLight_269.blend +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Blender 3D Importer (http://www.blender3d.org). +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/BLEND\' +Info, T34284: BLEND: Blender version is 2.69 (64bit: true, little endian: true) +Debug, T34284: REND +Debug, T34284: TEST +Debug, T34284: GLOB +Debug, T34284: WM +Debug, T34284: DATA +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SC +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: CA +Debug, T34284: LA +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: LA +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: LA +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: WO +Debug, T34284: DATA +Debug, T34284: OB +Skipping one or more lines with the same contents +Debug, T34284: DATA +Debug, T34284: OB +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: OB +Debug, T34284: MA +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: TE +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: ME +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: LS +Debug, T34284: DATA +Debug, T34284: DNA1 +Debug, T34284: BlenderDNA: Got 551 structures with totally 6353 fields +Info, T34284: BlenderDNA: Dumped dna to dna.txt +Debug, T34284: ENDB +Warn, T34284: +Skipping one or more lines with the same contents +Warn, T34284: BlendDNA: Did not find a field named `coeff_const` in structure `Lamp` +Warn, T34284: BlendDNA: Did not find a field named `coeff_lin` in structure `Lamp` +Warn, T34284: BlendDNA: Did not find a field named `coeff_quad` in structure `Lamp` +Warn, T34284: +Skipping one or more lines with the same contents +Warn, T34284: BlendDNA: Did not find a field named `coeff_const` in structure `Lamp` +Warn, T34284: BlendDNA: Did not find a field named `coeff_lin` in structure `Lamp` +Warn, T34284: BlendDNA: Did not find a field named `coeff_quad` in structure `Lamp` +Warn, T34284: +Skipping one or more lines with the same contents +Warn, T34284: BlendDNA: Did not find a field named `coeff_const` in structure `Lamp` +Warn, T34284: BlendDNA: Did not find a field named `coeff_lin` in structure `Lamp` +Warn, T34284: BlendDNA: Did not find a field named `coeff_quad` in structure `Lamp` +Info, T34284: (Stats) Fields read: 918, pointers resolved: 23, cache hits: 3, cached objects: 19 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Warn, T34284: Validation warning: aiLight::mAttenuationXXX - all are zero +Skipping one or more lines with the same contents +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/BLEND/box.blend +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Blender 3D Importer (http://www.blender3d.org). +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/BLEND\' +Info, T34284: BLEND: Blender version is 2.76 (64bit: true, little endian: true) +Debug, T34284: REND +Debug, T34284: TEST +Debug, T34284: GLOB +Debug, T34284: WM +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SC +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: CA +Debug, T34284: LA +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: WO +Debug, T34284: DATA +Debug, T34284: OB +Debug, T34284: DATA +Debug, T34284: OB +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: OB +Debug, T34284: DATA +Debug, T34284: MA +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: TE +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: ME +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: LS +Debug, T34284: DATA +Debug, T34284: DNA1 +Debug, T34284: BlenderDNA: Got 604 structures with totally 6955 fields +Info, T34284: BlenderDNA: Dumped dna to dna.txt +Debug, T34284: ENDB +Warn, T34284: +Skipping one or more lines with the same contents +Warn, T34284: BlendDNA: Did not find a field named `coeff_const` in structure `Lamp` +Warn, T34284: BlendDNA: Did not find a field named `coeff_lin` in structure `Lamp` +Warn, T34284: BlendDNA: Did not find a field named `coeff_quad` in structure `Lamp` +Info, T34284: (Stats) Fields read: 668, pointers resolved: 17, cache hits: 3, cached objects: 13 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Warn, T34284: Validation warning: aiLight::mAttenuationXXX - all are zero +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/BLEND/4Cubes4Mats_248.blend +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Blender 3D Importer (http://www.blender3d.org). +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/BLEND\' +Info, T34284: BLEND: Blender version is 2.48 (64bit: false, little endian: true) +Debug, T34284: REND +Debug, T34284: GLOB +Debug, T34284: SR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SC +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: CA +Debug, T34284: LA +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: WO +Debug, T34284: TX +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: OB +Skipping one or more lines with the same contents +Debug, T34284: DATA +Debug, T34284: OB +Debug, T34284: DATA +Debug, T34284: OB +Debug, T34284: DATA +Debug, T34284: OB +Debug, T34284: DATA +Debug, T34284: OB +Debug, T34284: MA +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: MA +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: MA +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: MA +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: TE +Debug, T34284: ME +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: ME +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: ME +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: ME +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: DNA1 +Debug, T34284: BlenderDNA: Got 310 structures with totally 3868 fields +Info, T34284: BlenderDNA: Dumped dna to dna.txt +Debug, T34284: ENDB +Warn, T34284: +Skipping one or more lines with the same contents +Warn, T34284: BlendDNA: Did not find a field named `sensor_x` in structure `Camera` +Warn, T34284: +Skipping one or more lines with the same contents +Warn, T34284: BlendDNA: Did not find a field named `totloop` in structure `Mesh` +Warn, T34284: BlendDNA: Did not find a field named `totpoly` in structure `Mesh` +Warn, T34284: BlendDNA: Did not find a field named `*mloop` in structure `Mesh` +Warn, T34284: BlendDNA: Did not find a field named `*mloopuv` in structure `Mesh` +Warn, T34284: BlendDNA: Did not find a field named `*mloopcol` in structure `Mesh` +Warn, T34284: BlendDNA: Did not find a field named `*mpoly` in structure `Mesh` +Warn, T34284: BlendDNA: Did not find a field named `*mtpoly` in structure `Mesh` +Warn, T34284: +Skipping one or more lines with the same contents +Warn, T34284: BlendDNA: Did not find a field named `rot` in structure `MTex` +Warn, T34284: BlendDNA: Did not find a field named `colspecfac` in structure `MTex` +Warn, T34284: BlendDNA: Did not find a field named `mirrfac` in structure `MTex` +Warn, T34284: BlendDNA: Did not find a field named `alphafac` in structure `MTex` +Warn, T34284: BlendDNA: Did not find a field named `difffac` in structure `MTex` +Warn, T34284: BlendDNA: Did not find a field named `specfac` in structure `MTex` +Warn, T34284: BlendDNA: Did not find a field named `emitfac` in structure `MTex` +Warn, T34284: BlendDNA: Did not find a field named `hardfac` in structure `MTex` +Warn, T34284: BlendDNA: Did not find a field named `material_type` in structure `Material` +Warn, T34284: BlendDNA: Did not find a field named `pr_texture` in structure `Material` +Warn, T34284: BlendDNA: Did not find a field named `shadowonly_flag` in structure `Material` +Warn, T34284: BlendDNA: Did not find a field named `index` in structure `Material` +Warn, T34284: BlendDNA: Did not find a field named `vcol_alpha` in structure `Material` +Warn, T34284: BlendDNA: Did not find a field named `typemap` in structure `CustomData` +Error, T34284: Constructing BlenderDNA Structure encountered an error +Info, T34284: Load D:/projects/assimp/test/models/BLEND/blender_269_regress1.blend +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Blender 3D Importer (http://www.blender3d.org). +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/BLEND\' +Info, T34284: BLEND: Blender version is 2.69 (64bit: true, little endian: true) +Debug, T34284: REND +Debug, T34284: TEST +Debug, T34284: GLOB +Debug, T34284: WM +Debug, T34284: DATA +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SC +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: CA +Debug, T34284: LA +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: KE +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: WO +Debug, T34284: DATA +Debug, T34284: OB +Debug, T34284: DATA +Debug, T34284: OB +Debug, T34284: DATA +Debug, T34284: OB +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: MA +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: TE +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: ME +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: LS +Debug, T34284: DATA +Debug, T34284: DNA1 +Debug, T34284: BlenderDNA: Got 551 structures with totally 6353 fields +Info, T34284: BlenderDNA: Dumped dna to dna.txt diff --git a/test/AssimpLog_Cpp.txt b/test/AssimpLog_Cpp.txt new file mode 100644 index 000000000..b843b6f07 --- /dev/null +++ b/test/AssimpLog_Cpp.txt @@ -0,0 +1,3184 @@ +Info, T34284: Load dae.dae +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Collada Importer. +Info, T34284: Import root directory is '.\' +Debug, T34284: Collada schema version is 1.4.n +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: START `t1` +Debug, T34284: END `t1`, dt= 0.0064414 s +Info, T34284: Load D:/projects/assimp/test/models/OBJ/spider.obj +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 5 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_post_linear.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 5 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_xz_bezier.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 5 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_xz_stepped.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 5 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_oldformat_56.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 2 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_post_offset_repeat.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 5 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_xz_hermite.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 5 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_y_pre_ofrep_post_osc.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 5 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Warn, T34284: Validation warning: aiNodeAnim::mPositionKeys[1].mTime (-72.00000) is smaller than aiAnimation::mPositionKeys[0] (which is 0.00000) +Warn, T34284: Validation warning: aiNodeAnim::mPositionKeys[5].mTime (-21.00000) is smaller than aiAnimation::mPositionKeys[4] (which is 0.00000) +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_oldformat_6.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 3 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_post_repeat.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 5 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_xz_linear.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 5 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_post_constant.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 5 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_post_reset.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 5 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/LWS/move_xz_spline.lws +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' +Debug, T34284: LWS: Skipping over plugin-specific data +Skipping one or more lines with the same contents +Info, T34284: LWS file format version is 5 +Info, T34284: %%% BEGIN EXTERNAL FILE %%% +Info, T34284: File: simple_cube.lwo +Info, T34284: Load simple_cube.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: %%% END EXTERNAL FILE %%% +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/LWO/LWO2/boxuv.lwo +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWO/LWO2\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/LWO/LWO2/formatDetection +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWO/LWO2\' +Info, T34284: LWO file format: LWO2 (>= LightWave 6) +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/SMD/triangle.smd +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Valve SMD Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/SMD\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/SMD/holy_grailref.smd +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Valve SMD Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/SMD\' +Error, T34284: [SMD/VTA] Bone index overflow. The bone index will be ignored, the weight will be assigned to the vertex' parent node +Skipping one or more lines with the same contents +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Info, T34284: Load D:/projects/assimp/test/models/glTF/TwoBoxes/TwoBoxes.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: glTF Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF/TwoBoxes\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF/IncorrectVertexArrays/Cube_v1.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: glTF Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF/IncorrectVertexArrays\' +Warn, T34284: The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped. +Warn, T34284: The number of vertices was not compatible with the LINES mode. Some vertices were dropped. +Warn, T34284: The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped. +Warn, T34284: The number of vertices was not compatible with the LINES mode. Some vertices were dropped. +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Warn, T34284: Validation warning: There are unreferenced vertices +Skipping one or more lines with the same contents +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF/TwoBoxes/TwoBoxes.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: glTF Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF/TwoBoxes\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxTextured-glTF/BoxTextured.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxTextured-glTF\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 1 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/2CylinderEngine-glTF-Binary/2CylinderEngine.glb +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Reading GLTF2 binary +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/2CylinderEngine-glTF-Binary\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Reading GLTF2 binary +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 34 materials +Debug, T34284: Importing 29 meshes +Debug, T34284: Importing 1 cameras +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxTextured-glTF/BoxTextured.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxTextured-glTF\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 1 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: GenVertexNormalsProcess begin +Debug, T34284: GenVertexNormalsProcess finished. Normals are already there +Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxTextured-glTF-Embedded/BoxTextured.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxTextured-glTF-Embedded\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 1 embedded textures +Debug, T34284: Importing 1 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: GenVertexNormalsProcess begin +Debug, T34284: GenVertexNormalsProcess finished. Normals are already there +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_00.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_01.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_02.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_03.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_04.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_05.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_06.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_07.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_08.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_09.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_10.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_11.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_12.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/simple_skin/simple_skin.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/simple_skin\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 1 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Debug, T34284: ScenePreprocessor: Dummy position track has been generated +Info, T34284: Load D:/projects/assimp/test/models/glTF2/cameras/Cameras.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/cameras\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing 2 cameras +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Warn, T34284: Validation warning: 0.000000 is not a valid value for aiCamera::mHorizontalFOV +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/IncorrectVertexArrays/Cube.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/IncorrectVertexArrays\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 8 meshes +Warn, T34284: The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped. +Warn, T34284: The number of vertices was not compatible with the LINES mode. Some vertices were dropped. +Warn, T34284: The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped. +Warn, T34284: The number of vertices was not compatible with the LINES mode. Some vertices were dropped. +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Warn, T34284: Validation warning: There are unreferenced vertices +Skipping one or more lines with the same contents +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/textureTransform/TextureTransformTest.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/textureTransform\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 9 materials +Debug, T34284: Importing 9 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxTextured-glTF/BoxTextured.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxTextured-glTF\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 1 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: TriangulateProcess begin +Debug, T34284: TriangulateProcess finished. There was nothing to be done. +Debug, T34284: SortByPTypeProcess begin +Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) +Debug, T34284: SortByPTypeProcess finished +Debug, T34284: JoinVerticesProcess begin +Debug, T34284: Mesh 0 (Mesh) | Verts in: 24 out: 24 | ~0% +Debug, T34284: JoinVerticesProcess finished +Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Sample-Models/AnimatedMorphCube-glTF/AnimatedMorphCube.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Sample-Models/AnimatedMorphCube-glTF\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 1 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 1 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: TriangulateProcess begin +Debug, T34284: TriangulateProcess finished. There was nothing to be done. +Debug, T34284: SortByPTypeProcess begin +Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) +Debug, T34284: SortByPTypeProcess finished +Debug, T34284: JoinVerticesProcess begin +Debug, T34284: Mesh 0 (Cube) | Verts in: 24 out: 24 | ~0% +Debug, T34284: JoinVerticesProcess finished +Info, T34284: Load D:/projects/assimp/test/models/glTF2/MissingBin/BoxTextured.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxWithInfinites-glTF-Binary/BoxWithInfinites.glb +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Reading GLTF2 binary +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxWithInfinites-glTF-Binary\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Reading GLTF2 binary +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 1 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: TriangulateProcess begin +Debug, T34284: TriangulateProcess finished. There was nothing to be done. +Debug, T34284: SortByPTypeProcess begin +Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) +Debug, T34284: SortByPTypeProcess finished +Debug, T34284: JoinVerticesProcess begin +Debug, T34284: Mesh 0 (Mesh) | Verts in: 24 out: 10 | ~58.3333% +Info, T34284: JoinVerticesProcess finished | Verts in: 24 out: 10 | ~58.3333 +Debug, T34284: TriangulateProcess begin +Debug, T34284: TriangulateProcess finished. There was nothing to be done. +Debug, T34284: SortByPTypeProcess begin +Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) +Debug, T34284: SortByPTypeProcess finished +Debug, T34284: JoinVerticesProcess begin +Debug, T34284: Mesh 0 (Mesh) | Verts in: 24 out: 10 | ~58.3333% +Info, T34284: JoinVerticesProcess finished | Verts in: 24 out: 10 | ~58.3333 +Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxBadNormals-glTF-Binary/BoxBadNormals.glb +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Reading GLTF2 binary +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxBadNormals-glTF-Binary\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Reading GLTF2 binary +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 1 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: TriangulateProcess begin +Debug, T34284: TriangulateProcess finished. There was nothing to be done. +Debug, T34284: SortByPTypeProcess begin +Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) +Debug, T34284: SortByPTypeProcess finished +Debug, T34284: JoinVerticesProcess begin +Debug, T34284: Mesh 0 (Mesh) | Verts in: 24 out: 24 | ~0% +Debug, T34284: JoinVerticesProcess finished +Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxBadNormals-glTF-Binary/BoxBadNormals_out.glb +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: (Deleting previous scene) +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Reading GLTF2 binary +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxBadNormals-glTF-Binary\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Reading GLTF2 binary +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 1 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxTextured-glTF/BoxTextured.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxTextured-glTF\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 1 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxTexcoords-glTF\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 1 materials +Debug, T34284: Importing 1 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/RecursiveNodes/RecursiveNodes.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Load D:/projects/assimp/test/models/glTF2/TestNoRootNode/NoScene.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/TestNoRootNode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 0 meshes +Error, T34284: GLTF: No scene +Info, T34284: Load D:/projects/assimp/test/models/glTF2/TestNoRootNode/SceneWithoutNodes.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/TestNoRootNode\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 0 materials +Debug, T34284: Importing 0 meshes +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/issue_3269/texcoord_crash.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Load D:/projects/assimp/test/models/glTF2/IndexOutOfRange/IndexOutOfRange.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/IndexOutOfRange\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 1 materials +Debug, T34284: Importing 1 meshes +Warn, T34284: Some faces had out-of-range indices. Those faces were dropped. +Debug, T34284: Importing nodes +Debug, T34284: Importing 0 animations +Debug, T34284: Importing metadata +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Warn, T34284: Validation warning: There are unreferenced vertices +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/glTF2/IndexOutOfRange/AllIndicesOutOfRange.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Found a matching importer for this file format: glTF2 Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/IndexOutOfRange\' +Debug, T34284: Reading GLTF2 file +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Debug, T34284: Importing 1 materials +Debug, T34284: Importing 1 meshes +Warn, T34284: Some faces had out-of-range indices. Those faces were dropped. +Error, T34284: Mesh "Mesh" has no faces +Info, T34284: Load D:/projects/assimp/test/models/glTF2/draco/2CylinderEngine.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Load D:/projects/assimp/test/models/glTF2/wrongTypes/badArray.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Load D:/projects/assimp/test/models/glTF2/wrongTypes/badString.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Load D:/projects/assimp/test/models/glTF2/wrongTypes/badUint.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Load D:/projects/assimp/test/models/glTF2/wrongTypes/badNumber.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Load D:/projects/assimp/test/models/glTF2/wrongTypes/badObject.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Load D:/projects/assimp/test/models/glTF2/wrongTypes/badExtension.gltf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Loading GLTF2 asset +Debug, T34284: Parsing GLTF2 JSON +Info, T34284: Load D:/projects/assimp/test/models/HMP/terrain.hmp +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: 3D GameStudio Heightmap (HMP) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/HMP\' +Debug, T34284: HMP subtype: 3D GameStudio A7, magic word is HMP7 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/IFC/AC14-FZK-Haus.ifc +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Industry Foundation Classes (IFC) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/IFC\' +Debug, T34284: IFC: File schema is 'IFC2X3' +Debug, T34284: STEP: got 82226 object records with 5788 inverse index entries +Debug, T34284: IFC: got units used for lengths +Debug, T34284: IFC: got units used for angles +Debug, T34284: IFC: got world coordinate system +Debug, T34284: IFC: looking at spatial structure `Gelände` +Debug, T34284: IFC: selecting this spatial structure as root structure +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Skipping one or more lines with the same contents +Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline +Debug, T34284: IFC: removing duplicate vertices +Error, T34284: IFC: failed to generate all window caps +Skipping one or more lines with the same contents +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: removing degenerate faces +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Skipping one or more lines with the same contents +Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline +Debug, T34284: IFC: removing duplicate vertices +Error, T34284: IFC: failed to generate all window caps +Skipping one or more lines with the same contents +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: removing degenerate faces +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Skipping one or more lines with the same contents +Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline +Debug, T34284: IFC: removing duplicate vertices +Error, T34284: IFC: failed to generate all window caps +Skipping one or more lines with the same contents +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Skipping one or more lines with the same contents +Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline +Debug, T34284: IFC: removing duplicate vertices +Error, T34284: IFC: failed to generate all window caps +Skipping one or more lines with the same contents +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Skipping one or more lines with the same contents +Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline +Debug, T34284: IFC: removing duplicate vertices +Error, T34284: IFC: failed to generate all window caps +Skipping one or more lines with the same contents +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Skipping one or more lines with the same contents +Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline +Debug, T34284: IFC: removing duplicate vertices +Error, T34284: IFC: failed to generate all window caps +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: removing degenerate faces +Skipping one or more lines with the same contents +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: removing degenerate faces +Skipping one or more lines with the same contents +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: skipping IfcAnnotation entity due to importer settings +Skipping one or more lines with the same contents +Debug, T34284: IFC: skipping IfcSpace entity due to importer settings +Skipping one or more lines with the same contents +Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: generating CSG geometry by plane clipping (IfcBooleanClippingResult) +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline +Debug, T34284: IFC: removing duplicate vertices +Error, T34284: IFC: failed to generate all window caps +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: generating CSG geometry by plane clipping with polygonal bounding (IfcBooleanClippingResult) +Skipping one or more lines with the same contents +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: removing degenerate faces +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: removing degenerate faces +Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: generating CSG geometry by plane clipping (IfcBooleanClippingResult) +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline +Debug, T34284: IFC: removing duplicate vertices +Error, T34284: IFC: failed to generate all window caps +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: generating CSG geometry by plane clipping with polygonal bounding (IfcBooleanClippingResult) +Skipping one or more lines with the same contents +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: removing degenerate faces +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: generating CSG geometry by plane clipping (IfcBooleanClippingResult) +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: generating CSG geometry by plane clipping (IfcBooleanClippingResult) +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Skipping one or more lines with the same contents +Debug, T34284: IFC: generating CSG geometry by plane clipping (IfcBooleanClippingResult) +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: generating CSG geometry by plane clipping (IfcBooleanClippingResult) +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: removing duplicate vertices +Warn, T34284: IFC: failed to resolve all openings, presumably their topology is not supported by Assimp +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: removing degenerate faces +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: removing duplicate vertices +Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) +Debug, T34284: IFC: skipping IfcAnnotation entity due to importer settings +Skipping one or more lines with the same contents +Debug, T34284: IFC: skipping IfcSpace entity due to importer settings +Debug, T34284: IFC: STEP: evaluated 70094 object records +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load $$$___magic___$$$. +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Found positive match for header keyword: HEADER +Info, T34284: Found a matching importer for this file format: Drawing Interchange Format (DXF) Importer. +Info, T34284: Import root directory is '.\' +Warn, T34284: DXF: EOF reached, but did not encounter DXF EOF marker +Debug, T34284: DXF: Unexpanded polycount is 0, vertex count is 0 +Error, T34284: DXF: no data blocks loaded +Info, T34284: Load D:/projects/assimp/test/models/FBX/spider.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing binary FBX file +Debug, T34284: FBX version: 7400 +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7400 +Debug, T34284: UpdateImporterScale scale set: 0.01 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/box.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing binary FBX file +Debug, T34284: FBX version: 7400 +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7400 +Info, T34284: FBX: generating full transformation chain for node: root +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/cubes_nonames.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing ASCII FBX file +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7500 +Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless +Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 +Debug, T34284: UpdateImporterScale scale set: 0.01 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/cubes_with_names.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing ASCII FBX file +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7500 +Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless +Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 +Debug, T34284: UpdateImporterScale scale set: 0.01 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/cubes_with_mirroring_and_pivot.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing ASCII FBX file +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7500 +Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless +Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 +Info, T34284: FBX: generating full transformation chain for node: Cube1 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/close_to_identity_transforms.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing ASCII FBX file +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7500 +Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless +Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 +Info, T34284: FBX: generating full transformation chain for node: Cube1 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/phong_cube.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing binary FBX file +Debug, T34284: FBX version: 7400 +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7400 +Warn, T34284: FBX-DOM (TOK_KEY, offset 0x4190) source object for connection does not exist +Debug, T34284: UpdateImporterScale scale set: 0.01 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/global_settings.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing binary FBX file +Debug, T34284: FBX version: 7400 +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7400 +Error, T34284: FBX: no material assigned to mesh, setting default material +Debug, T34284: UpdateImporterScale scale set: 5 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/embedded_ascii/box.FBX +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX/embedded_ascii\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing ASCII FBX file +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7400 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Warn, T34284: Validation warning: A specular shading model is specified but the value of the AI_MATKEY_SHININESS_STRENGTH key is 0.0 +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/embedded_ascii/box_embedded_texture_fragmented.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX/embedded_ascii\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing ASCII FBX file +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7500 +Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless +Error, T34284: FBX: ignoring additional binormal layer +Error, T34284: FBX: ignoring additional tangent layer +Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/box_orphant_embedded_texture.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing ASCII FBX file +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7500 +Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless +Error, T34284: FBX: ignoring additional binormal layer +Error, T34284: FBX: ignoring additional tangent layer +Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/global_settings.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing binary FBX file +Debug, T34284: FBX version: 7400 +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7400 +Error, T34284: FBX: no material assigned to mesh, setting default material +Debug, T34284: UpdateImporterScale scale set: 5 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/cubes_with_outofrange_float.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing ASCII FBX file +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7500 +Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless +Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 +Debug, T34284: UpdateImporterScale scale set: 0.01 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/maxPbrMaterial_metalRough.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing ASCII FBX file +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7700 +Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless +Warn, T34284: FBX-DOM (TOK_KEY, line 679, col 13) shading mode not recognized: unknown +Info, T34284: FBX: generating full transformation chain for node: Box001 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/FBX/maxPbrMaterial_specGloss.fbx +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' +Debug, T34284: Reading FBX file +Debug, T34284: Tokenizing ASCII FBX file +Debug, T34284: Parsing FBX tokens +Debug, T34284: Creating FBX Document +Debug, T34284: FBX Version: 7700 +Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless +Warn, T34284: FBX-DOM (TOK_KEY, line 679, col 13) shading mode not recognized: unknown +Info, T34284: FBX: generating full transformation chain for node: Box001 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load $$$___magic___$$$.3ds +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Discreet 3DS Importer. +Info, T34284: Import root directory is '.\' +Info, T34284: 3DS file format version: 3 +Warn, T34284: 3DS: Skipping TCB animation info +Skipping one or more lines with the same contents +Error, T34284: 3DS: Skipping FOV animation track. This is not supported +Warn, T34284: 3DS: Skipping TCB animation info +Skipping one or more lines with the same contents +Info, T34284: 3DS: Generating default material +Debug, T34284: 3DS: Converting camera roll track ... +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Debug, T34284: ScenePreprocessor: Setting animation duration +Info, T34284: Entering post processing pipeline +Debug, T34284: RemoveRedundantMatsProcess begin +Debug, T34284: RemoveRedundantMatsProcess finished +Debug, T34284: GenUVCoordsProcess begin +Debug, T34284: GenUVCoordsProcess finished +Debug, T34284: TriangulateProcess begin +Debug, T34284: TriangulateProcess finished. There was nothing to be done. +Debug, T34284: FindDegeneratesProcess begin +Debug, T34284: FindDegeneratesProcess finished +Debug, T34284: SortByPTypeProcess begin +Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) +Debug, T34284: SortByPTypeProcess finished +Debug, T34284: FindInvalidDataProcess begin +Info, T34284: FindInvalidDataProcess finished. Found issues ... +Debug, T34284: SplitLargeMeshesProcess_Triangle begin +Debug, T34284: SplitLargeMeshesProcess_Triangle finished. There was nothing to do +Debug, T34284: Generate spatially-sorted vertex cache +Debug, T34284: GenVertexNormalsProcess begin +Debug, T34284: GenVertexNormalsProcess finished. Normals are already there +Debug, T34284: CalcTangentsProcess begin +Info, T34284: CalcTangentsProcess finished. Tangents have been calculated +Debug, T34284: JoinVerticesProcess begin +Debug, T34284: Mesh 0 (0) | Verts in: 36 out: 24 | ~33.3333% +Info, T34284: JoinVerticesProcess finished | Verts in: 36 out: 24 | ~33.3333 +Debug, T34284: SplitLargeMeshesProcess_Vertex begin +Debug, T34284: SplitLargeMeshesProcess_Vertex finished. There was nothing to do +Debug, T34284: LimitBoneWeightsProcess begin +Debug, T34284: LimitBoneWeightsProcess end +Debug, T34284: ImproveCacheLocalityProcess begin +Debug, T34284: Mesh %u | ACMR in: 0 out: 2 | ~20 +Info, T34284: Cache relevant are 1 meshes (12 faces). Average output ACMR is 2 +Debug, T34284: ImproveCacheLocalityProcess finished. +Info, T34284: Leaving post processing pipeline +Info, T34284: Registering custom importer for these file extensions: applelinuxmacwindows +Info, T34284: Unregistering custom importer: +Info, T34284: Load D:/projects/assimp/test/models/X/test.x +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Warn, T34284: Unknown data object in mesh in x file +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Entering post processing pipeline +Debug, T34284: RemoveRedundantMatsProcess begin +Debug, T34284: RemoveRedundantMatsProcess finished +Debug, T34284: OptimizeGraphProcess begin +Debug, T34284: OptimizeGraphProcess finished +Debug, T34284: GenUVCoordsProcess begin +Debug, T34284: GenUVCoordsProcess finished +Debug, T34284: TriangulateProcess begin +Debug, T34284: TriangulateProcess finished. There was nothing to be done. +Debug, T34284: FindDegeneratesProcess begin +Debug, T34284: FindDegeneratesProcess finished +Debug, T34284: SortByPTypeProcess begin +Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) +Debug, T34284: SortByPTypeProcess finished +Debug, T34284: FindInvalidDataProcess begin +Info, T34284: FindInvalidDataProcess finished. Found issues ... +Debug, T34284: Skipping OptimizeMeshesProcess +Debug, T34284: Generate spatially-sorted vertex cache +Debug, T34284: GenVertexNormalsProcess begin +Debug, T34284: GenVertexNormalsProcess finished. Normals are already there +Debug, T34284: JoinVerticesProcess begin +Debug, T34284: Mesh 0 (pCube1) | Verts in: 36 out: 24 | ~33.3333% +Info, T34284: JoinVerticesProcess finished | Verts in: 36 out: 24 | ~33.3333 +Info, T34284: Leaving post processing pipeline +Info, T34284: Load D:/projects/assimp/test/models/X/Testwuson.X +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: (Deleting previous scene) +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Warn, T34284: Unknown data object in animation of .x file +Skipping one or more lines with the same contents +Warn, T34284: Unknown data object in frame in x file +Skipping one or more lines with the same contents +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Entering post processing pipeline +Debug, T34284: RemoveRedundantMatsProcess begin +Debug, T34284: RemoveRedundantMatsProcess finished +Debug, T34284: OptimizeGraphProcess begin +Debug, T34284: OptimizeGraphProcess finished +Debug, T34284: GenUVCoordsProcess begin +Debug, T34284: GenUVCoordsProcess finished +Debug, T34284: TriangulateProcess begin +Debug, T34284: TriangulateProcess finished. There was nothing to be done. +Debug, T34284: FindDegeneratesProcess begin +Debug, T34284: FindDegeneratesProcess finished +Debug, T34284: SortByPTypeProcess begin +Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) +Debug, T34284: SortByPTypeProcess finished +Debug, T34284: FindInvalidDataProcess begin +Info, T34284: FindInvalidDataProcess finished. Found issues ... +Debug, T34284: Skipping OptimizeMeshesProcess +Debug, T34284: Generate spatially-sorted vertex cache +Debug, T34284: GenVertexNormalsProcess begin +Debug, T34284: GenVertexNormalsProcess finished. Normals are already there +Debug, T34284: JoinVerticesProcess begin +Debug, T34284: Mesh 0 (Wuson) | Verts in: 11196 out: 3205 | ~71.3737% +Info, T34284: JoinVerticesProcess finished | Verts in: 11196 out: 3205 | ~71.3737 +Info, T34284: Leaving post processing pipeline +Info, T34284: Load D:/projects/assimp/test/models/X/anim_test.x +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: (Deleting previous scene) +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Warn, T34284: Length of texture file name is zero. Skipping this texture. +Warn, T34284: Unknown data object in mesh in x file +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Entering post processing pipeline +Debug, T34284: RemoveRedundantMatsProcess begin +Debug, T34284: RemoveRedundantMatsProcess finished +Debug, T34284: OptimizeGraphProcess begin +Debug, T34284: OptimizeGraphProcess finished +Debug, T34284: GenUVCoordsProcess begin +Debug, T34284: GenUVCoordsProcess finished +Debug, T34284: TriangulateProcess begin +Debug, T34284: TriangulateProcess finished. There was nothing to be done. +Debug, T34284: FindDegeneratesProcess begin +Debug, T34284: FindDegeneratesProcess finished +Debug, T34284: SortByPTypeProcess begin +Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) +Debug, T34284: SortByPTypeProcess finished +Debug, T34284: FindInvalidDataProcess begin +Warn, T34284: Simplified dummy tracks with just one key +Skipping one or more lines with the same contents +Info, T34284: FindInvalidDataProcess finished. Found issues ... +Debug, T34284: Skipping OptimizeMeshesProcess +Debug, T34284: Generate spatially-sorted vertex cache +Debug, T34284: GenVertexNormalsProcess begin +Debug, T34284: GenVertexNormalsProcess finished. Normals are already there +Debug, T34284: JoinVerticesProcess begin +Debug, T34284: Mesh 0 (pCylinder1) | Verts in: 2520 out: 485 | ~80.754% +Info, T34284: JoinVerticesProcess finished | Verts in: 2520 out: 485 | ~80.754 +Info, T34284: Leaving post processing pipeline +Info, T34284: Load D:/projects/assimp/test/models/X/anim_test.x +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: (Deleting previous scene) +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Warn, T34284: Length of texture file name is zero. Skipping this texture. +Warn, T34284: Unknown data object in mesh in x file +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Entering post processing pipeline +Debug, T34284: RemoveRedundantMatsProcess begin +Debug, T34284: RemoveRedundantMatsProcess finished +Debug, T34284: OptimizeGraphProcess begin +Debug, T34284: OptimizeGraphProcess finished +Debug, T34284: GenUVCoordsProcess begin +Debug, T34284: GenUVCoordsProcess finished +Debug, T34284: TriangulateProcess begin +Debug, T34284: TriangulateProcess finished. There was nothing to be done. +Debug, T34284: FindDegeneratesProcess begin +Debug, T34284: FindDegeneratesProcess finished +Debug, T34284: SortByPTypeProcess begin +Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) +Debug, T34284: SortByPTypeProcess finished +Debug, T34284: FindInvalidDataProcess begin +Warn, T34284: Simplified dummy tracks with just one key +Skipping one or more lines with the same contents +Info, T34284: FindInvalidDataProcess finished. Found issues ... +Debug, T34284: Skipping OptimizeMeshesProcess +Debug, T34284: Generate spatially-sorted vertex cache +Debug, T34284: GenVertexNormalsProcess begin +Debug, T34284: GenVertexNormalsProcess finished. Normals are already there +Debug, T34284: JoinVerticesProcess begin +Debug, T34284: Mesh 0 (pCylinder1) | Verts in: 2520 out: 485 | ~80.754% +Info, T34284: JoinVerticesProcess finished | Verts in: 2520 out: 485 | ~80.754 +Info, T34284: Leaving post processing pipeline +Info, T34284: Load D:/projects/assimp/test/models/X/BCN_Epileptic.X +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: (Deleting previous scene) +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Warn, T34284: Unknown data object in animation of .x file +Skipping one or more lines with the same contents +Warn, T34284: Unknown data object in frame in x file +Skipping one or more lines with the same contents +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Entering post processing pipeline +Debug, T34284: RemoveRedundantMatsProcess begin +Debug, T34284: RemoveRedundantMatsProcess finished +Debug, T34284: OptimizeGraphProcess begin +Debug, T34284: OptimizeGraphProcess finished +Debug, T34284: GenUVCoordsProcess begin +Debug, T34284: GenUVCoordsProcess finished +Debug, T34284: TriangulateProcess begin +Debug, T34284: TriangulateProcess finished. There was nothing to be done. +Debug, T34284: FindDegeneratesProcess begin +Debug, T34284: FindDegeneratesProcess finished +Debug, T34284: SortByPTypeProcess begin +Info, T34284: Points: 0, Lines: 0, Triangles: 3, Polygons: 0 (Meshes, X = removed) +Debug, T34284: SortByPTypeProcess finished +Debug, T34284: FindInvalidDataProcess begin +Info, T34284: FindInvalidDataProcess finished. Found issues ... +Debug, T34284: OptimizeMeshesProcess begin +Debug, T34284: OptimizeMeshesProcess finished +Debug, T34284: Generate spatially-sorted vertex cache +Debug, T34284: GenVertexNormalsProcess begin +Debug, T34284: GenVertexNormalsProcess finished. Normals are already there +Debug, T34284: JoinVerticesProcess begin +Debug, T34284: Mesh 0 (Torso) | Verts in: 5898 out: 1170 | ~80.1628% +Debug, T34284: Mesh 1 (Head) | Verts in: 6108 out: 1196 | ~80.4191% +Debug, T34284: Mesh 2 (Legs) | Verts in: 3372 out: 648 | ~80.7829% +Info, T34284: JoinVerticesProcess finished | Verts in: 15378 out: 3014 | ~80.4006 +Info, T34284: Leaving post processing pipeline +Info, T34284: Registering custom importer for these file extensions: fail +Info, T34284: Load deadlyImportError.fail +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Failing importer. +Info, T34284: Import root directory is './' +Error, T34284: Deadly import error test. Details: 42 More Details: Failure +Info, T34284: Registering custom importer for these file extensions: fail +Info, T34284: Load stdException.fail +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Failing importer. +Info, T34284: Import root directory is './' +Error, T34284: std::exception test +Info, T34284: Registering custom importer for these file extensions: fail +Info, T34284: Load unexpectedException.fail +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Failing importer. +Info, T34284: Import root directory is './' +Info, T34284: Load D:/projects/assimp/test/models/3D/box_a.3d +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Unreal Mesh Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3D\' +Debug, T34284: UNREAL: data file is D:/projects/assimp/test/models/3D/box_d.3d +Debug, T34284: UNREAL: aniv file is D:/projects/assimp/test/models/3D/box_a.3d +Debug, T34284: UNREAL: uc file is D:/projects/assimp/test/models/3D/box.uc +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/3D/box_d.3d +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Unreal Mesh Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3D\' +Debug, T34284: UNREAL: data file is D:/projects/assimp/test/models/3D/box_d.3d +Debug, T34284: UNREAL: aniv file is D:/projects/assimp/test/models/3D/box_a.3d +Debug, T34284: UNREAL: uc file is D:/projects/assimp/test/models/3D/box.uc +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/3D/box.uc +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Unreal Mesh Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3D\' +Debug, T34284: UNREAL: data file is D:/projects/assimp/test/models/3D/box_d.3d +Debug, T34284: UNREAL: aniv file is D:/projects/assimp/test/models/3D/box_a.3d +Debug, T34284: UNREAL: uc file is D:/projects/assimp/test/models/3D/box.uc +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/3DS/fels.3ds +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Discreet 3DS Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3DS\' +Info, T34284: 3DS file format version: 3 +Warn, T34284: No hierarchy information has been found in the file. +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/3DS/testFormatDetection +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Discreet 3DS Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3DS\' +Info, T34284: 3DS file format version: 3 +Warn, T34284: 3DS: Skipping TCB animation info +Skipping one or more lines with the same contents +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/AC/closedLine.ac +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: AC3D Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' +Info, T34284: AC3D file format version: 11 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/AC/nosurfaces.ac +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: AC3D Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' +Info, T34284: AC3D file format version: 11 +Warn, T34284: AC3D: No material has been found +Info, T34284: AC3D: No surfaces defined in object definition, a point list is returned +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/AC/openLine.ac +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: AC3D Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' +Info, T34284: AC3D file format version: 11 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/AC/sample_subdiv.ac +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: AC3D Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' +Info, T34284: AC3D file format version: 11 +Info, T34284: AC3D: Evaluating subdivision surface: cylinder +Debug, T34284: Catmull-Clark Subdivider: got 130 bad edges touching only one face (totally 61505 edges). +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/AC/SphereWithLight.ac +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: AC3D Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' +Info, T34284: AC3D file format version: 11 +Debug, T34284: AC3D: Light source encountered +Info, T34284: AC3D: Evaluating subdivision surface: sphere +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/AC/SphereWithLight_UTF16LE.ac +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: AC3D Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' +Debug, T34284: Found UTF-16 BOM ... +Error, T34284: AC3D: No valid AC3D file, magic sequence not found +Info, T34284: Load D:/projects/assimp/test/models/AC/SphereWithLight_UTF8BOM.ac +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: AC3D Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' +Debug, T34284: Found UTF-8 BOM ... +Info, T34284: AC3D file format version: 11 +Debug, T34284: AC3D: Light source encountered +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/AC/SphereWithLightUvScaling4X.ac +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: AC3D Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' +Info, T34284: AC3D file format version: 11 +Debug, T34284: AC3D: Light source encountered +Info, T34284: AC3D: Evaluating subdivision surface: sphere +Debug, T34284: Catmull-Clark Subdivider: got 12 bad edges touching only one face (totally 17670 edges). +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/AC/Wuson.ac +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: AC3D Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' +Info, T34284: AC3D file format version: 11 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/AC/Wuson.acc +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: AC3D Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' +Info, T34284: AC3D file format version: 11 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/AC/TestFormatDetection +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: AC3D Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' +Info, T34284: AC3D file format version: 11 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/AMF/test1.amf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Additive manufacturing file format(AMF) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AMF\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Adding default material 'DefaultMaterial' +Info, T34284: Load D:/projects/assimp/test/models/AMF/test_with_mat.amf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Additive manufacturing file format(AMF) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AMF\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Adding default material 'DefaultMaterial' +Info, T34284: Load D:/projects/assimp/test/models/ASE/ThreeCubesGreen.ASE +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: ASE Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/ASE\' +Info, T34284: Line 1: Comment: AsciiExport-Version 2,00 - Tue May 06 17:21:38 2008 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/3MF/box.3mf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: 3mf Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3MF\' +Warn, T34284: Ignored file of unknown type: 3D/3dmodel.model +Warn, T34284: Ignored file of unsupported type CONTENT_TYPES_ARCHIVES[Content_Types].xml +Debug, T34284: 3D/3dmodel.model +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Adding default material 'DefaultMaterial' +Info, T34284: Load D:/projects/assimp/test/models/3MF/box.3mf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: 3mf Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3MF\' +Warn, T34284: Ignored file of unknown type: 3D/3dmodel.model +Warn, T34284: Ignored file of unsupported type CONTENT_TYPES_ARCHIVES[Content_Types].xml +Debug, T34284: 3D/3dmodel.model +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ScenePreprocessor: Adding default material 'DefaultMaterial' +Info, T34284: Load D:/projects/assimp/test/models/3MF/box.3mf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: 3mf Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3MF\' +Warn, T34284: Ignored file of unknown type: 3D/3dmodel.model +Warn, T34284: Ignored file of unsupported type CONTENT_TYPES_ARCHIVES[Content_Types].xml +Debug, T34284: 3D/3dmodel.model +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ScenePreprocessor: Adding default material 'DefaultMaterial' +Info, T34284: Load test.3mf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: 3mf Importer. +Info, T34284: Import root directory is '.\' +Warn, T34284: Ignored file of unknown type: 3D/3DModel.model +Warn, T34284: Ignored file of unsupported type CONTENT_TYPES_ARCHIVES[Content_Types].xml +Debug, T34284: 3D/3DModel.model +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: Load D:/projects/assimp/test/models/Q3D/earth.q3o +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Quick3D Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/Q3D\' +Info, T34284: Quick3D File format version: 30 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/STL/Spider_ascii.stl +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/STL/Spider_ascii.stl +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/STL/Spider_ascii.stl +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/STL/formatDetection +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Found positive match for header keyword: solid +Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/STL/triangle_with_two_solids.stl +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/STL/triangle_with_empty_solid.stl +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' +Warn, T34284: STL: mesh is empty or invalid; no data loaded +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: Load D:/projects/assimp/test/models/STL/triangle_with_empty_solid.stl +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: (Deleting previous scene) +Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' +Warn, T34284: STL: mesh is empty or invalid; no data loaded +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Error, T34284: Validation failed: The mesh emptySolid contains no vertices +Info, T34284: Load D:/projects/assimp/test/models/STL/Spider_ascii.stl +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: PretransformVerticesProcess begin +Debug, T34284: PretransformVerticesProcess finished +Info, T34284: Removed 2 nodes and 0 animation channels (1 output nodes) +Info, T34284: Kept 0 lights and 0 cameras. +Info, T34284: Moved 1 meshes to WCS (number of output meshes: 1) +Debug, T34284: TriangulateProcess begin +Debug, T34284: TriangulateProcess finished. There was nothing to be done. +Debug, T34284: GenFaceNormalsProcess begin +Debug, T34284: GenFaceNormalsProcess finished. Normals are already there +Info, T34284: Load spiderExport.stl +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: (Deleting previous scene) +Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. +Info, T34284: Import root directory is '.\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: TriangulateProcess begin +Debug, T34284: TriangulateProcess finished. There was nothing to be done. +Debug, T34284: GenFaceNormalsProcess begin +Info, T34284: Normal vectors are undefined for line and point meshes +Debug, T34284: GenFaceNormalsProcess finished. Normals are already there +Info, T34284: Load D:/projects/assimp/test/models/X/test.x +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Warn, T34284: Unknown data object in mesh in x file +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/X/OV_GetNextToken +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Info, T34284: Successfully decompressed MSZIP-compressed file +Error, T34284: Opening brace expected. +Info, T34284: Load D:/projects/assimp/test/models/X/anim_test.x +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Warn, T34284: Length of texture file name is zero. Skipping this texture. +Warn, T34284: Unknown data object in mesh in x file +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/X/BCN_Epileptic.X +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Warn, T34284: Unknown data object in animation of .x file +Skipping one or more lines with the same contents +Warn, T34284: Unknown data object in frame in x file +Skipping one or more lines with the same contents +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/X/fromtruespace_bin32.x +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Warn, T34284: Unknown data object in animation of .x file +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/X/kwxport_test_cubewithvcolors.x +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Warn, T34284: Unknown data object in animation of .x file +Skipping one or more lines with the same contents +Warn, T34284: Unknown data object in frame in x file +Warn, T34284: Unknown data object in mesh in x file +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/X/test_cube_binary.x +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/X/test_cube_compressed.x +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Info, T34284: Successfully decompressed MSZIP-compressed file +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/X/test_cube_text.x +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/X/Testwuson.X +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Warn, T34284: Unknown data object in animation of .x file +Skipping one or more lines with the same contents +Warn, T34284: Unknown data object in frame in x file +Skipping one or more lines with the same contents +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/X/TestFormatDetection +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' +Warn, T34284: Unknown data object in mesh in x file +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models-nonbsd/X/dwarf.x +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models-nonbsd/X\' +Debug, T34284: MakeLeftHandedProcess begin +Debug, T34284: MakeLeftHandedProcess finished +Debug, T34284: FlipWindingOrderProcess begin +Debug, T34284: FlipWindingOrderProcess finished +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated +Skipping one or more lines with the same contents +Info, T34284: Load D:/projects/assimp/test/models/X3D/ComputerKeyboard.x3d +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: File extension not known, trying signature-based detection +Info, T34284: Found a matching importer for this file format: Extensible 3D(X3D) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X3D\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Error, T34284: Validation failed: aiScene::mNumMeshes is 0. At least one mesh must be there +Info, T34284: Load D:/projects/assimp/test/models/DXF/PinkEggFromLW.dxf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Drawing Interchange Format (DXF) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/DXF\' +Debug, T34284: DXF: got 0 entries in BLOCKS +Debug, T34284: DXF: got 288 polylines and 0 inserted blocks in ENTITIES +Debug, T34284: DXF: Unexpanded polycount is 288, vertex count is 1152 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/DXF/lineTest +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Found positive match for header keyword: SECTION +Info, T34284: Found a matching importer for this file format: Drawing Interchange Format (DXF) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/DXF\' +Info, T34284: DXF Comment: VISION3D DXF +Debug, T34284: DXF: got 0 entries in BLOCKS +Debug, T34284: DXF: got 3 polylines and 0 inserted blocks in ENTITIES +Debug, T34284: DXF: Unexpanded polycount is 3, vertex count is 9 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/DXF/issue_2229.dxf +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Drawing Interchange Format (DXF) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/DXF\' +Debug, T34284: DXF: skipped over control group (273 lines) +Debug, T34284: DXF: got 2 entries in BLOCKS +Warn, T34284: DXF: invalid vertex index, indices are one-based. +Skipping one or more lines with the same contents +Warn, T34284: DXF: unexpected face count in polymesh: 1173, expected 1174 +Warn, T34284: DXF: invalid vertex index, indices are one-based. +Skipping one or more lines with the same contents +Debug, T34284: DXF: got 4 polylines and 0 inserted blocks in ENTITIES +Debug, T34284: DXF: skipped over control group (3 lines) +Skipping one or more lines with the same contents +Debug, T34284: DXF: Unexpanded polycount is 1289, vertex count is 768 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/PLY/cube.ply +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' +Debug, T34284: PLY::DOM::ParseInstance() begin +Debug, T34284: PLY::DOM::ParseHeader() begin +Debug, T34284: PLY::DOM::ParseHeader() succeeded +Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin +Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded +Debug, T34284: PLY::DOM::ParseInstance() succeeded +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/PLY/cube.ply +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' +Debug, T34284: PLY::DOM::ParseInstance() begin +Debug, T34284: PLY::DOM::ParseHeader() begin +Debug, T34284: PLY::DOM::ParseHeader() succeeded +Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin +Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded +Debug, T34284: PLY::DOM::ParseInstance() succeeded +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: PretransformVerticesProcess begin +Debug, T34284: PretransformVerticesProcess finished +Info, T34284: Removed 1 nodes and 0 animation channels (1 output nodes) +Info, T34284: Kept 0 lights and 0 cameras. +Info, T34284: Moved 1 meshes to WCS (number of output meshes: 1) +Info, T34284: Load D:/projects/assimp/test/models/PLY/cube.ply +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' +Debug, T34284: PLY::DOM::ParseInstance() begin +Debug, T34284: PLY::DOM::ParseHeader() begin +Debug, T34284: PLY::DOM::ParseHeader() succeeded +Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin +Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded +Debug, T34284: PLY::DOM::ParseInstance() succeeded +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/PLY/cube.ply +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: (Deleting previous scene) +Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' +Debug, T34284: PLY::DOM::ParseInstance() begin +Debug, T34284: PLY::DOM::ParseHeader() begin +Debug, T34284: PLY::DOM::ParseHeader() succeeded +Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin +Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded +Debug, T34284: PLY::DOM::ParseInstance() succeeded +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/PLY/cube_uv.ply +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' +Debug, T34284: PLY::DOM::ParseInstance() begin +Debug, T34284: PLY::DOM::ParseHeader() begin +Debug, T34284: PLY::DOM::ParseHeader() succeeded +Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin +Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded +Debug, T34284: PLY::DOM::ParseInstance() succeeded +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/PLY/cube_binary.ply +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' +Debug, T34284: PLY::DOM::ParseInstanceBinary() begin +Debug, T34284: PLY::DOM::ParseHeader() begin +Debug, T34284: PLY::DOM::ParseHeader() succeeded +Debug, T34284: PLY::DOM::ParseElementInstanceListsBinary() begin +Debug, T34284: PLY::DOM::ParseElementInstanceListsBinary() succeeded +Debug, T34284: PLY::DOM::ParseInstanceBinary() succeeded +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/PLY/float-color.ply +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' +Debug, T34284: PLY::DOM::ParseInstance() begin +Debug, T34284: PLY::DOM::ParseHeader() begin +Debug, T34284: PLY::DOM::ParseHeader() succeeded +Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin +Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded +Debug, T34284: PLY::DOM::ParseInstance() succeeded +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/PLY/issue623.ply +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' +Debug, T34284: PLY::DOM::ParseInstance() begin +Debug, T34284: PLY::DOM::ParseHeader() begin +Debug, T34284: PLY::DOM::ParseHeader() succeeded +Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin +Warn, T34284: Unable to parse property instance. Skipping this element instance +Skipping one or more lines with the same contents +Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded +Debug, T34284: PLY::DOM::ParseInstance() succeeded +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: Load $$$___magic___$$$. +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Debug, T34284: Found positive match for header keyword: ply +Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. +Info, T34284: Import root directory is '.\' +Debug, T34284: PLY::DOM::ParseInstance() begin +Debug, T34284: PLY::DOM::ParseHeader() begin +Debug, T34284: PLY::DOM::ParseHeader() succeeded +Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin +Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded +Debug, T34284: PLY::DOM::ParseInstance() succeeded +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: Load D:/projects/assimp/test/models/OBJ/spider.obj +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/OBJ/spider.obj +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: GenVertexNormalsProcess begin +Debug, T34284: GenVertexNormalsProcess finished. Normals are already there +Debug, T34284: GenVertexNormalsProcess begin +Debug, T34284: GenVertexNormalsProcess finished. Normals are already there +Info, T34284: Load $$$___magic___$$$. +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: File extension not known, trying signature-based detection +Debug, T34284: Found positive match for header keyword: usemtl +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is '.\' +Error, T34284: OBJ: failed to locate material Default, creating new material +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: Load $$$___magic___$$$. +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: File extension not known, trying signature-based detection +Debug, T34284: Found positive match for header keyword: usemtl +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is '.\' +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: Load D:/projects/assimp/test/models/OBJ/cube_with_vertexcolors.obj +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: GenVertexNormalsProcess begin +Debug, T34284: GenVertexNormalsProcess finished. Normals are already there +Info, T34284: Load D:/projects/assimp/test/models/OBJ/cube_with_vertexcolors_uni.obj +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Debug, T34284: GenVertexNormalsProcess begin +Debug, T34284: GenVertexNormalsProcess finished. Normals are already there +Info, T34284: Load $$$___magic___$$$. +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: File extension not known, trying signature-based detection +Debug, T34284: Found positive match for header keyword: mtllib +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is '.\' +Error, T34284: OBJ: Unable to locate material file $blobfile.mtl +Info, T34284: OBJ: Opening fallback material file $$$___magic___$mtl +Error, T34284: OBJ: Unable to locate fallback material file $$$___magic___$mtl +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load $$$___magic___$$$. +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: File extension not known, trying signature-based detection +Debug, T34284: Found positive match for header keyword: v +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is '.\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Error, T34284: Validation failed: Mesh contains no faces +Info, T34284: Load $$$___magic___$$$. +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: File extension not known, trying signature-based detection +Debug, T34284: Found positive match for header keyword: v +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is '.\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load $$$___magic___$$$. +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: File extension not known, trying signature-based detection +Debug, T34284: Found positive match for header keyword: v +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is '.\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load $$$___magic___$$$. +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: File extension not known, trying signature-based detection +Debug, T34284: Found positive match for header keyword: v +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is '.\' +Error, T34284: OBJ: Invalid component in homogeneous vector (Division by zero) +Info, T34284: Load $$$___magic___$$$. +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: File extension not known, trying signature-based detection +Debug, T34284: Found positive match for header keyword: v +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is '.\' +Error, T34284: OBJ: Invalid face indice +Info, T34284: Load $$$___magic___$$$. +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: File extension not known, trying signature-based detection +Debug, T34284: Found positive match for header keyword: v +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is '.\' +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: Load $$$___magic___$$$. +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: File extension not known, trying signature-based detection +Debug, T34284: Found positive match for header keyword: v +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is '.\' +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: Load D:/projects/assimp/test/models/OBJ/cube_mtllib_after_g.obj +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' +Error, T34284: OBJ: Unable to locate material file cube_mtllib_after_g.mat +Info, T34284: OBJ: Opening fallback material file D:/projects/assimp/test/models/OBJ/cube_mtllib_after_g.mtl +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/OBJ/point_cloud.obj +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ScenePreprocessor: Adding default material 'DefaultMaterial' +Info, T34284: Load D:/projects/assimp/test/models/OBJ/box_without_lineending.obj +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' +Error, T34284: OBJ: failed to locate material Default, creating new material +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: Load $$$___magic___$$$. +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: File extension not known, trying signature-based detection +Debug, T34284: Found positive match for header keyword: v +Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. +Info, T34284: Import root directory is '.\' +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: Load D:/projects/assimp/test/models/OpenGEX/Example.ogex +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Open Game Engine Exchange. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OpenGEX\' +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: Load D:/projects/assimp/test/models/OpenGEX/light_issue1262.ogex +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Open Game Engine Exchange. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OpenGEX\' +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: Load D:/projects/assimp/test/models/OpenGEX/empty_camera.ogex +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Open Game Engine Exchange. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OpenGEX\' +Debug, T34284: UpdateImporterScale scale set: 1 +Info, T34284: Load D:/projects/assimp/test/models/SIB/heffalump.sib +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Silo SIB Importer. +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/SIB\' +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/BLEND/AreaLight_269.blend +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Blender 3D Importer (http://www.blender3d.org). +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/BLEND\' +Info, T34284: BLEND: Blender version is 2.69 (64bit: true, little endian: true) +Debug, T34284: REND +Debug, T34284: TEST +Debug, T34284: GLOB +Debug, T34284: WM +Debug, T34284: DATA +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SC +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: CA +Debug, T34284: LA +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: LA +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: LA +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: WO +Debug, T34284: DATA +Debug, T34284: OB +Skipping one or more lines with the same contents +Debug, T34284: DATA +Debug, T34284: OB +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: OB +Debug, T34284: MA +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: TE +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: ME +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: LS +Debug, T34284: DATA +Debug, T34284: DNA1 +Debug, T34284: BlenderDNA: Got 551 structures with totally 6353 fields +Info, T34284: BlenderDNA: Dumped dna to dna.txt +Debug, T34284: ENDB +Warn, T34284: +Skipping one or more lines with the same contents +Warn, T34284: BlendDNA: Did not find a field named `coeff_const` in structure `Lamp` +Warn, T34284: BlendDNA: Did not find a field named `coeff_lin` in structure `Lamp` +Warn, T34284: BlendDNA: Did not find a field named `coeff_quad` in structure `Lamp` +Warn, T34284: +Skipping one or more lines with the same contents +Warn, T34284: BlendDNA: Did not find a field named `coeff_const` in structure `Lamp` +Warn, T34284: BlendDNA: Did not find a field named `coeff_lin` in structure `Lamp` +Warn, T34284: BlendDNA: Did not find a field named `coeff_quad` in structure `Lamp` +Warn, T34284: +Skipping one or more lines with the same contents +Warn, T34284: BlendDNA: Did not find a field named `coeff_const` in structure `Lamp` +Warn, T34284: BlendDNA: Did not find a field named `coeff_lin` in structure `Lamp` +Warn, T34284: BlendDNA: Did not find a field named `coeff_quad` in structure `Lamp` +Info, T34284: (Stats) Fields read: 918, pointers resolved: 23, cache hits: 3, cached objects: 19 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Warn, T34284: Validation warning: aiLight::mAttenuationXXX - all are zero +Skipping one or more lines with the same contents +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/BLEND/box.blend +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Blender 3D Importer (http://www.blender3d.org). +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/BLEND\' +Info, T34284: BLEND: Blender version is 2.76 (64bit: true, little endian: true) +Debug, T34284: REND +Debug, T34284: TEST +Debug, T34284: GLOB +Debug, T34284: WM +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SC +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: CA +Debug, T34284: LA +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: WO +Debug, T34284: DATA +Debug, T34284: OB +Debug, T34284: DATA +Debug, T34284: OB +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: OB +Debug, T34284: DATA +Debug, T34284: MA +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: TE +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: ME +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: LS +Debug, T34284: DATA +Debug, T34284: DNA1 +Debug, T34284: BlenderDNA: Got 604 structures with totally 6955 fields +Info, T34284: BlenderDNA: Dumped dna to dna.txt +Debug, T34284: ENDB +Warn, T34284: +Skipping one or more lines with the same contents +Warn, T34284: BlendDNA: Did not find a field named `coeff_const` in structure `Lamp` +Warn, T34284: BlendDNA: Did not find a field named `coeff_lin` in structure `Lamp` +Warn, T34284: BlendDNA: Did not find a field named `coeff_quad` in structure `Lamp` +Info, T34284: (Stats) Fields read: 668, pointers resolved: 17, cache hits: 3, cached objects: 13 +Debug, T34284: UpdateImporterScale scale set: 1 +Debug, T34284: ValidateDataStructureProcess begin +Warn, T34284: Validation warning: aiLight::mAttenuationXXX - all are zero +Debug, T34284: ValidateDataStructureProcess end +Info, T34284: Load D:/projects/assimp/test/models/BLEND/4Cubes4Mats_248.blend +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Blender 3D Importer (http://www.blender3d.org). +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/BLEND\' +Info, T34284: BLEND: Blender version is 2.48 (64bit: false, little endian: true) +Debug, T34284: REND +Debug, T34284: GLOB +Debug, T34284: SR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SC +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: CA +Debug, T34284: LA +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: WO +Debug, T34284: TX +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: OB +Skipping one or more lines with the same contents +Debug, T34284: DATA +Debug, T34284: OB +Debug, T34284: DATA +Debug, T34284: OB +Debug, T34284: DATA +Debug, T34284: OB +Debug, T34284: DATA +Debug, T34284: OB +Debug, T34284: MA +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: MA +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: MA +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: MA +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: TE +Debug, T34284: ME +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: ME +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: ME +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: ME +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: DNA1 +Debug, T34284: BlenderDNA: Got 310 structures with totally 3868 fields +Info, T34284: BlenderDNA: Dumped dna to dna.txt +Debug, T34284: ENDB +Warn, T34284: +Skipping one or more lines with the same contents +Warn, T34284: BlendDNA: Did not find a field named `sensor_x` in structure `Camera` +Warn, T34284: +Skipping one or more lines with the same contents +Warn, T34284: BlendDNA: Did not find a field named `totloop` in structure `Mesh` +Warn, T34284: BlendDNA: Did not find a field named `totpoly` in structure `Mesh` +Warn, T34284: BlendDNA: Did not find a field named `*mloop` in structure `Mesh` +Warn, T34284: BlendDNA: Did not find a field named `*mloopuv` in structure `Mesh` +Warn, T34284: BlendDNA: Did not find a field named `*mloopcol` in structure `Mesh` +Warn, T34284: BlendDNA: Did not find a field named `*mpoly` in structure `Mesh` +Warn, T34284: BlendDNA: Did not find a field named `*mtpoly` in structure `Mesh` +Warn, T34284: +Skipping one or more lines with the same contents +Warn, T34284: BlendDNA: Did not find a field named `rot` in structure `MTex` +Warn, T34284: BlendDNA: Did not find a field named `colspecfac` in structure `MTex` +Warn, T34284: BlendDNA: Did not find a field named `mirrfac` in structure `MTex` +Warn, T34284: BlendDNA: Did not find a field named `alphafac` in structure `MTex` +Warn, T34284: BlendDNA: Did not find a field named `difffac` in structure `MTex` +Warn, T34284: BlendDNA: Did not find a field named `specfac` in structure `MTex` +Warn, T34284: BlendDNA: Did not find a field named `emitfac` in structure `MTex` +Warn, T34284: BlendDNA: Did not find a field named `hardfac` in structure `MTex` +Warn, T34284: BlendDNA: Did not find a field named `material_type` in structure `Material` +Warn, T34284: BlendDNA: Did not find a field named `pr_texture` in structure `Material` +Warn, T34284: BlendDNA: Did not find a field named `shadowonly_flag` in structure `Material` +Warn, T34284: BlendDNA: Did not find a field named `index` in structure `Material` +Warn, T34284: BlendDNA: Did not find a field named `vcol_alpha` in structure `Material` +Warn, T34284: BlendDNA: Did not find a field named `typemap` in structure `CustomData` +Error, T34284: Constructing BlenderDNA Structure encountered an error +Info, T34284: Load D:/projects/assimp/test/models/BLEND/blender_269_regress1.blend +Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : +Info, T34284: Found a matching importer for this file format: Blender 3D Importer (http://www.blender3d.org). +Info, T34284: Import root directory is 'D:/projects/assimp/test/models/BLEND\' +Info, T34284: BLEND: Blender version is 2.69 (64bit: true, little endian: true) +Debug, T34284: REND +Debug, T34284: TEST +Debug, T34284: GLOB +Debug, T34284: WM +Debug, T34284: DATA +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SN +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: SC +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: CA +Debug, T34284: LA +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: KE +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: WO +Debug, T34284: DATA +Debug, T34284: OB +Debug, T34284: DATA +Debug, T34284: OB +Debug, T34284: DATA +Debug, T34284: OB +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: MA +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: TE +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: ME +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: BR +Debug, T34284: DATA +Skipping one or more lines with the same contents +Debug, T34284: LS +Debug, T34284: DATA +Debug, T34284: DNA1 +Debug, T34284: BlenderDNA: Got 551 structures with totally 6353 fields +Info, T34284: BlenderDNA: Dumped dna to dna.txt diff --git a/test/dae.dae b/test/dae.dae new file mode 100644 index 000000000..f8db45d6d --- /dev/null +++ b/test/dae.dae @@ -0,0 +1,80 @@ + + + + + Assimp + Assimp Exporter + + 2021-05-05T14:41:31 + 2021-05-05T14:41:31 + + Y_UP + + + + + + + + + + 1 0 0 1 + + + 0.0373546556 + + + + + + + + + + + + + + + + 1 0 0 0 1 0 0 0 1 + + + + + + + + + + + + + + 3 +

0 1 2

+
+
+
+
+ + + + + + 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 + + + + + + + + + + + + + + +
diff --git a/test/dna.txt b/test/dna.txt new file mode 100644 index 000000000..b89d0db4b --- /dev/null +++ b/test/dna.txt @@ -0,0 +1,8008 @@ +Field format: type name offset size +Structure format: name size +Link 16 + + Link *next 0 8 + Link *prev 8 8 + +LinkData 24 + + LinkData *next 0 8 + LinkData *prev 8 8 + void *data 16 8 + +ListBase 16 + + void *first 0 8 + void *last 8 8 + +vec2s 4 + + short x 0 2 + short y 2 2 + +vec2f 8 + + float x 0 4 + float y 4 4 + +vec3f 12 + + float x 0 4 + float y 4 4 + float z 8 4 + +rcti 16 + + int xmin 0 4 + int xmax 4 4 + int ymin 8 4 + int ymax 12 4 + +rctf 16 + + float xmin 0 4 + float xmax 4 4 + float ymin 8 4 + float ymax 12 4 + +IDPropertyData 32 + + void *pointer 0 8 + ListBase group 8 16 + int val 24 4 + int val2 28 4 + +IDProperty 128 + + IDProperty *next 0 8 + IDProperty *prev 8 8 + char type 16 1 + char subtype 17 1 + short flag 18 2 + char name 20 64 + int saved 84 4 + IDPropertyData data 88 32 + int len 120 4 + int totallen 124 4 + +ID 120 + + void *next 0 8 + void *prev 8 8 + ID *newid 16 8 + Library *lib 24 8 + char name 32 66 + short pad 98 2 + short us 100 2 + short flag 102 2 + int icon_id 104 4 + int pad2 108 4 + IDProperty *properties 112 8 + +Library 2200 + + ID id 0 120 + ID *idblock 120 8 + FileData *filedata 128 8 + char name 136 1024 + char filepath 1160 1024 + Library *parent 2184 8 + PackedFile *packedfile 2192 8 + +PreviewImage 56 + + int w 0 8 + int h 8 8 + short changed 16 4 + short changed_timestamp 20 4 + int *rect 24 16 + GPUTexture *gputexture 40 16 + +IpoDriver 144 + + Object *ob 0 8 + short blocktype 8 2 + short adrcode 10 2 + short type 12 2 + short flag 14 2 + char name 16 128 + +IpoCurve 112 + + IpoCurve *next 0 8 + IpoCurve *prev 8 8 + BPoint *bp 16 8 + BezTriple *bezt 24 8 + rctf maxrct 32 16 + rctf totrct 48 16 + short blocktype 64 2 + short adrcode 66 2 + short vartype 68 2 + short totvert 70 2 + short ipo 72 2 + short extrap 74 2 + short flag 76 2 + short rt 78 2 + float ymin 80 4 + float ymax 84 4 + int bitmask 88 4 + float slide_min 92 4 + float slide_max 96 4 + float curval 100 4 + IpoDriver *driver 104 8 + +Ipo 160 + + ID id 0 120 + ListBase curve 120 16 + rctf cur 136 16 + short blocktype 152 2 + short showkey 154 2 + short muteipo 156 2 + short pad 158 2 + +KeyBlock 184 + + KeyBlock *next 0 8 + KeyBlock *prev 8 8 + float pos 16 4 + float curval 20 4 + short type 24 2 + short pad1 26 2 + short relative 28 2 + short flag 30 2 + int totelem 32 4 + int uid 36 4 + void *data 40 8 + char name 48 64 + char vgroup 112 64 + float slidermin 176 4 + float slidermax 180 4 + +Key 224 + + ID id 0 120 + AnimData *adt 120 8 + KeyBlock *refkey 128 8 + char elemstr 136 32 + int elemsize 168 4 + int pad 172 4 + ListBase block 176 16 + Ipo *ipo 192 8 + ID *from 200 8 + short type 208 2 + short totkey 210 2 + short slurph 212 2 + short flag 214 2 + float ctime 216 4 + int uidgen 220 4 + +TextLine 40 + + TextLine *next 0 8 + TextLine *prev 8 8 + char *line 16 8 + char *format 24 8 + int len 32 4 + int blen 36 4 + +Text 208 + + ID id 0 120 + char *name 120 8 + int flags 128 4 + int nlines 132 4 + ListBase lines 136 16 + TextLine *curl 152 8 + TextLine *sell 160 8 + int curc 168 4 + int selc 172 4 + char *undo_buf 176 8 + int undo_pos 184 4 + int undo_len 188 4 + void *compiled 192 8 + double mtime 200 8 + +PackedFile 16 + + int size 0 4 + int seek 4 4 + void *data 8 8 + +Camera 200 + + ID id 0 120 + AnimData *adt 120 8 + char type 128 1 + char dtx 129 1 + short flag 130 2 + float passepartalpha 132 4 + float clipsta 136 4 + float clipend 140 4 + float lens 144 4 + float ortho_scale 148 4 + float drawsize 152 4 + float sensor_x 156 4 + float sensor_y 160 4 + float shiftx 164 4 + float shifty 168 4 + float YF_dofdist 172 4 + Ipo *ipo 176 8 + Object *dof_ob 184 8 + char sensor_fit 192 1 + char pad 193 7 + +ImageUser 40 + + Scene *scene 0 8 + int framenr 8 4 + int frames 12 4 + int offset 16 4 + int sfra 20 4 + char fie_ima 24 1 + char cycl 25 1 + char ok 26 1 + char pad 27 1 + short multi_index 28 2 + short layer 30 2 + short pass 32 2 + short flag 34 2 + int pad2 36 4 + +Image 1408 + + ID id 0 120 + char name 120 1024 + ListBase ibufs 1144 16 + GPUTexture *gputexture 1160 8 + anim *anim 1168 8 + RenderResult *rr 1176 8 + RenderResult *renders 1184 64 + short render_slot 1248 2 + short last_render_slot 1250 2 + short ok 1252 2 + short flag 1254 2 + short source 1256 2 + short type 1258 2 + int lastframe 1260 4 + short tpageflag 1264 2 + short totbind 1266 2 + short xrep 1268 2 + short yrep 1270 2 + short twsta 1272 2 + short twend 1274 2 + int bindcode 1276 4 + int *repbind 1280 8 + PackedFile *packedfile 1288 8 + PreviewImage *preview 1296 8 + float lastupdate 1304 4 + int lastused 1308 4 + short animspeed 1312 2 + short pad2 1314 2 + int gen_x 1316 4 + int gen_y 1320 4 + char gen_type 1324 1 + char gen_flag 1325 1 + short gen_depth 1326 2 + float aspx 1328 4 + float aspy 1332 4 + ColorManagedColorspaceSettings colorspace_settings 1336 64 + char alpha_mode 1400 1 + char pad 1401 7 + +MTex 312 + + short texco 0 2 + short mapto 2 2 + short maptoneg 4 2 + short blendtype 6 2 + Object *object 8 8 + Tex *tex 16 8 + char uvname 24 64 + char projx 88 1 + char projy 89 1 + char projz 90 1 + char mapping 91 1 + float ofs 92 12 + float size 104 12 + float rot 116 4 + short texflag 120 2 + short colormodel 122 2 + short pmapto 124 2 + short pmaptoneg 126 2 + short normapspace 128 2 + short which_output 130 2 + char brush_map_mode 132 1 + char pad 133 7 + float r 140 4 + float g 144 4 + float b 148 4 + float k 152 4 + float def_var 156 4 + float rt 160 4 + float colfac 164 4 + float varfac 168 4 + float norfac 172 4 + float dispfac 176 4 + float warpfac 180 4 + float colspecfac 184 4 + float mirrfac 188 4 + float alphafac 192 4 + float difffac 196 4 + float specfac 200 4 + float emitfac 204 4 + float hardfac 208 4 + float raymirrfac 212 4 + float translfac 216 4 + float ambfac 220 4 + float colemitfac 224 4 + float colreflfac 228 4 + float coltransfac 232 4 + float densfac 236 4 + float scatterfac 240 4 + float reflfac 244 4 + float timefac 248 4 + float lengthfac 252 4 + float clumpfac 256 4 + float dampfac 260 4 + float kinkfac 264 4 + float roughfac 268 4 + float padensfac 272 4 + float gravityfac 276 4 + float lifefac 280 4 + float sizefac 284 4 + float ivelfac 288 4 + float fieldfac 292 4 + float shadowfac 296 4 + float zenupfac 300 4 + float zendownfac 304 4 + float blendfac 308 4 + +CBData 24 + + float r 0 4 + float g 4 4 + float b 8 4 + float a 12 4 + float pos 16 4 + int cur 20 4 + +ColorBand 776 + + short flag 0 2 + short tot 2 2 + short cur 4 2 + short ipotype 6 2 + CBData data 8 768 + +EnvMap 200 + + Object *object 0 8 + Image *ima 8 8 + ImBuf *cube 16 48 + float imat 64 64 + float obimat 128 36 + short type 164 2 + short stype 166 2 + float clipsta 168 4 + float clipend 172 4 + float viewscale 176 4 + int notlay 180 4 + short cuberes 184 2 + short depth 186 2 + int ok 188 4 + int lastframe 192 4 + short recalc 196 2 + short lastsize 198 2 + +PointDensity 104 + + short flag 0 2 + short falloff_type 2 2 + float falloff_softness 4 4 + float radius 8 4 + short source 12 2 + short color_source 14 2 + int totpoints 16 4 + int pdpad 20 4 + Object *object 24 8 + int psys 32 4 + short psys_cache_space 36 2 + short ob_cache_space 38 2 + void *point_tree 40 8 + float *point_data 48 8 + float noise_size 56 4 + short noise_depth 60 2 + short noise_influence 62 2 + short noise_basis 64 2 + short pdpad3 66 6 + float noise_fac 72 4 + float speed_scale 76 4 + float falloff_speed_scale 80 4 + float pdpad2 84 4 + ColorBand *coba 88 8 + CurveMapping *falloff_curve 96 8 + +VoxelData 1088 + + int resol 0 12 + int interp_type 12 4 + short file_format 16 2 + short flag 18 2 + short extend 20 2 + short smoked_type 22 2 + short data_type 24 2 + short pad 26 2 + int _pad 28 4 + Object *object 32 8 + float int_multiplier 40 4 + int still_frame 44 4 + char source_path 48 1024 + float *dataset 1072 8 + int cachedframe 1080 4 + int ok 1084 4 + +OceanTex 80 + + Object *object 0 8 + char oceanmod 8 64 + int output 72 4 + int pad 76 4 + +Tex 416 + + ID id 0 120 + AnimData *adt 120 8 + float noisesize 128 4 + float turbul 132 4 + float bright 136 4 + float contrast 140 4 + float saturation 144 4 + float rfac 148 4 + float gfac 152 4 + float bfac 156 4 + float filtersize 160 4 + float pad2 164 4 + float mg_H 168 4 + float mg_lacunarity 172 4 + float mg_octaves 176 4 + float mg_offset 180 4 + float mg_gain 184 4 + float dist_amount 188 4 + float ns_outscale 192 4 + float vn_w1 196 4 + float vn_w2 200 4 + float vn_w3 204 4 + float vn_w4 208 4 + float vn_mexp 212 4 + short vn_distm 216 2 + short vn_coltype 218 2 + short noisedepth 220 2 + short noisetype 222 2 + short noisebasis 224 2 + short noisebasis2 226 2 + short imaflag 228 2 + short flag 230 2 + short type 232 2 + short stype 234 2 + float cropxmin 236 4 + float cropymin 240 4 + float cropxmax 244 4 + float cropymax 248 4 + int texfilter 252 4 + int afmax 256 4 + short xrepeat 260 2 + short yrepeat 262 2 + short extend 264 2 + short fie_ima 266 2 + int len 268 4 + int frames 272 4 + int offset 276 4 + int sfra 280 4 + float checkerdist 284 4 + float nabla 288 4 + float pad1 292 4 + ImageUser iuser 296 40 + bNodeTree *nodetree 336 8 + Ipo *ipo 344 8 + Image *ima 352 8 + ColorBand *coba 360 8 + EnvMap *env 368 8 + PreviewImage *preview 376 8 + PointDensity *pd 384 8 + VoxelData *vd 392 8 + OceanTex *ot 400 8 + char use_nodes 408 1 + char pad 409 7 + +TexMapping 144 + + float loc 0 12 + float rot 12 12 + float size 24 12 + int flag 36 4 + char projx 40 1 + char projy 41 1 + char projz 42 1 + char mapping 43 1 + int type 44 4 + float mat 48 64 + float min 112 12 + float max 124 12 + Object *ob 136 8 + +ColorMapping 824 + + ColorBand coba 0 776 + float bright 776 4 + float contrast 780 4 + float saturation 784 4 + int flag 788 4 + float blend_color 792 12 + float blend_factor 804 4 + int blend_type 808 4 + int pad 812 12 + +Lamp 528 + + ID id 0 120 + AnimData *adt 120 8 + short type 128 2 + short flag 130 2 + int mode 132 4 + short colormodel 136 2 + short totex 138 2 + float r 140 4 + float g 144 4 + float b 148 4 + float k 152 4 + float shdwr 156 4 + float shdwg 160 4 + float shdwb 164 4 + float shdwpad 168 4 + float energy 172 4 + float dist 176 4 + float spotsize 180 4 + float spotblend 184 4 + float haint 188 4 + float att1 192 4 + float att2 196 4 + CurveMapping *curfalloff 200 8 + short falloff_type 208 2 + short pad2 210 2 + float clipsta 212 4 + float clipend 216 4 + float shadspotsize 220 4 + float bias 224 4 + float soft 228 4 + float compressthresh 232 4 + float bleedbias 236 4 + float pad5 240 8 + short bufsize 248 2 + short samp 250 2 + short buffers 252 2 + short filtertype 254 2 + char bufflag 256 1 + char buftype 257 1 + short ray_samp 258 2 + short ray_sampy 260 2 + short ray_sampz 262 2 + short ray_samp_type 264 2 + short area_shape 266 2 + float area_size 268 4 + float area_sizey 272 4 + float area_sizez 276 4 + float adapt_thresh 280 4 + short ray_samp_method 284 2 + short shadowmap_type 286 2 + short texact 288 2 + short shadhalostep 290 2 + short sun_effect_type 292 2 + short skyblendtype 294 2 + float horizon_brightness 296 4 + float spread 300 4 + float sun_brightness 304 4 + float sun_size 308 4 + float backscattered_light 312 4 + float sun_intensity 316 4 + float atm_turbidity 320 4 + float atm_inscattering_factor 324 4 + float atm_extinction_factor 328 4 + float atm_distance_factor 332 4 + float skyblendfac 336 4 + float sky_exposure 340 4 + float shadow_frustum_size 344 4 + short sky_colorspace 348 2 + char pad4 350 2 + Ipo *ipo 352 8 + MTex *mtex 360 144 + short pr_texture 504 2 + short use_nodes 506 2 + char pad6 508 4 + PreviewImage *preview 512 8 + bNodeTree *nodetree 520 8 + +VolumeSettings 88 + + float density 0 4 + float emission 4 4 + float scattering 8 4 + float reflection 12 4 + float emission_col 16 12 + float transmission_col 28 12 + float reflection_col 40 12 + float density_scale 52 4 + float depth_cutoff 56 4 + float asymmetry 60 4 + short stepsize_type 64 2 + short shadeflag 66 2 + short shade_type 68 2 + short precache_resolution 70 2 + float stepsize 72 4 + float ms_diff 76 4 + float ms_intensity 80 4 + float ms_spread 84 4 + +GameSettings 16 + + int flag 0 4 + int alpha_blend 4 4 + int face_orientation 8 4 + int pad1 12 4 + +Material 904 + + ID id 0 120 + AnimData *adt 120 8 + short material_type 128 2 + short flag 130 2 + float r 132 4 + float g 136 4 + float b 140 4 + float specr 144 4 + float specg 148 4 + float specb 152 4 + float mirr 156 4 + float mirg 160 4 + float mirb 164 4 + float ambr 168 4 + float ambb 172 4 + float ambg 176 4 + float amb 180 4 + float emit 184 4 + float ang 188 4 + float spectra 192 4 + float ray_mirror 196 4 + float alpha 200 4 + float ref 204 4 + float spec 208 4 + float zoffs 212 4 + float add 216 4 + float translucency 220 4 + VolumeSettings vol 224 88 + GameSettings game 312 16 + float fresnel_mir 328 4 + float fresnel_mir_i 332 4 + float fresnel_tra 336 4 + float fresnel_tra_i 340 4 + float filter 344 4 + float tx_limit 348 4 + float tx_falloff 352 4 + short ray_depth 356 2 + short ray_depth_tra 358 2 + short har 360 2 + char seed1 362 1 + char seed2 363 1 + float gloss_mir 364 4 + float gloss_tra 368 4 + short samp_gloss_mir 372 2 + short samp_gloss_tra 374 2 + float adapt_thresh_mir 376 4 + float adapt_thresh_tra 380 4 + float aniso_gloss_mir 384 4 + float dist_mir 388 4 + short fadeto_mir 392 2 + short shade_flag 394 2 + int mode 396 4 + int mode_l 400 4 + short flarec 404 2 + short starc 406 2 + short linec 408 2 + short ringc 410 2 + float hasize 412 4 + float flaresize 416 4 + float subsize 420 4 + float flareboost 424 4 + float strand_sta 428 4 + float strand_end 432 4 + float strand_ease 436 4 + float strand_surfnor 440 4 + float strand_min 444 4 + float strand_widthfade 448 4 + char strand_uvname 452 64 + float sbias 516 4 + float lbias 520 4 + float shad_alpha 524 4 + int septex 528 4 + char rgbsel 532 1 + char texact 533 1 + char pr_type 534 1 + char use_nodes 535 1 + short pr_lamp 536 2 + short pr_texture 538 2 + short ml_flag 540 2 + char mapflag 542 1 + char pad 543 1 + short diff_shader 544 2 + short spec_shader 546 2 + float roughness 548 4 + float refrac 552 4 + float param 556 16 + float rms 572 4 + float darkness 576 4 + short texco 580 2 + short mapto 582 2 + ColorBand *ramp_col 584 8 + ColorBand *ramp_spec 592 8 + char rampin_col 600 1 + char rampin_spec 601 1 + char rampblend_col 602 1 + char rampblend_spec 603 1 + short ramp_show 604 2 + short pad3 606 2 + float rampfac_col 608 4 + float rampfac_spec 612 4 + MTex *mtex 616 144 + bNodeTree *nodetree 760 8 + Ipo *ipo 768 8 + Group *group 776 8 + PreviewImage *preview 784 8 + float friction 792 4 + float fh 796 4 + float reflect 800 4 + float fhdist 804 4 + float xyfrict 808 4 + short dynamode 812 2 + short pad2 814 2 + float sss_radius 816 12 + float sss_col 828 12 + float sss_error 840 4 + float sss_scale 844 4 + float sss_ior 848 4 + float sss_colfac 852 4 + float sss_texfac 856 4 + float sss_front 860 4 + float sss_back 864 4 + short sss_flag 868 2 + short sss_preset 870 2 + int mapto_textured 872 4 + short shadowonly_flag 876 2 + short index 878 2 + short vcol_alpha 880 2 + short pad4 882 6 + ListBase gpumaterial 888 16 + +VFont 1168 + + ID id 0 120 + char name 120 1024 + VFontData *data 1144 8 + PackedFile *packedfile 1152 8 + PackedFile *temp_pf 1160 8 + +MetaElem 104 + + MetaElem *next 0 8 + MetaElem *prev 8 8 + BoundBox *bb 16 8 + short type 24 2 + short flag 26 2 + short selcol1 28 2 + short selcol2 30 2 + float x 32 4 + float y 36 4 + float z 40 4 + float quat 44 16 + float expx 60 4 + float expy 64 4 + float expz 68 4 + float rad 72 4 + float rad2 76 4 + float s 80 4 + float len 84 4 + float *mat 88 8 + float *imat 96 8 + +MetaBall 248 + + ID id 0 120 + AnimData *adt 120 8 + ListBase elems 128 16 + ListBase disp 144 16 + ListBase *editelems 160 8 + Ipo *ipo 168 8 + Material **mat 176 8 + char flag 184 1 + char flag2 185 1 + short totcol 186 2 + short texflag 188 2 + short pad 190 2 + float loc 192 12 + float size 204 12 + float rot 216 12 + float wiresize 228 4 + float rendersize 232 4 + float thresh 236 4 + MetaElem *lastelem 240 8 + +BezTriple 56 + + float vec 0 36 + float alfa 36 4 + float weight 40 4 + float radius 44 4 + short ipo 48 2 + char h1 50 1 + char h2 51 1 + char f1 52 1 + char f2 53 1 + char f3 54 1 + char hide 55 1 + +BPoint 36 + + float vec 0 16 + float alfa 16 4 + float weight 20 4 + short f1 24 2 + short hide 26 2 + float radius 28 4 + float pad 32 4 + +Nurb 80 + + Nurb *next 0 8 + Nurb *prev 8 8 + short type 16 2 + short mat_nr 18 2 + short hide 20 2 + short flag 22 2 + short pntsu 24 2 + short pntsv 26 2 + short resolu 28 2 + short resolv 30 2 + short orderu 32 2 + short orderv 34 2 + short flagu 36 2 + short flagv 38 2 + float *knotsu 40 8 + float *knotsv 48 8 + BPoint *bp 56 8 + BezTriple *bezt 64 8 + short tilt_interp 72 2 + short radius_interp 74 2 + int charidx 76 4 + +CharInfo 8 + + short kern 0 2 + short mat_nr 2 2 + char flag 4 1 + char pad 5 1 + short pad2 6 2 + +TextBox 16 + + float x 0 4 + float y 4 4 + float w 8 4 + float h 12 4 + +EditNurb 32 + + ListBase nurbs 0 16 + GHash *keyindex 16 8 + int shapenr 24 4 + char pad 28 4 + +Curve 504 + + ID id 0 120 + AnimData *adt 120 8 + BoundBox *bb 128 8 + ListBase nurb 136 16 + ListBase disp 152 16 + EditNurb *editnurb 168 8 + Object *bevobj 176 8 + Object *taperobj 184 8 + Object *textoncurve 192 8 + Ipo *ipo 200 8 + Key *key 208 8 + Material **mat 216 8 + float loc 224 12 + float size 236 12 + float rot 248 12 + short type 260 2 + short texflag 262 2 + short drawflag 264 2 + short twist_mode 266 2 + float twist_smooth 268 4 + float smallcaps_scale 272 4 + int pathlen 276 4 + short bevresol 280 2 + short totcol 282 2 + int flag 284 4 + float width 288 4 + float ext1 292 4 + float ext2 296 4 + short resolu 300 2 + short resolv 302 2 + short resolu_ren 304 2 + short resolv_ren 306 2 + int actnu 308 4 + void *lastsel 312 8 + short len 320 2 + short lines 322 2 + short pos 324 2 + short spacemode 326 2 + float spacing 328 4 + float linedist 332 4 + float shear 336 4 + float fsize 340 4 + float wordspace 344 4 + float ulpos 348 4 + float ulheight 352 4 + float xof 356 4 + float yof 360 4 + float linewidth 364 4 + char *str 368 8 + SelBox *selboxes 376 8 + EditFont *editfont 384 8 + char family 392 24 + VFont *vfont 416 8 + VFont *vfontb 424 8 + VFont *vfonti 432 8 + VFont *vfontbi 440 8 + int sepchar 448 4 + float ctime 452 4 + int totbox 456 4 + int actbox 460 4 + TextBox *tb 464 8 + int selstart 472 4 + int selend 476 4 + CharInfo *strinfo 480 8 + CharInfo curinfo 488 8 + float bevfac1 496 4 + float bevfac2 500 4 + +Mesh 1336 + + ID id 0 120 + AnimData *adt 120 8 + BoundBox *bb 128 8 + Ipo *ipo 136 8 + Key *key 144 8 + Material **mat 152 8 + MSelect *mselect 160 8 + MPoly *mpoly 168 8 + MTexPoly *mtpoly 176 8 + MLoop *mloop 184 8 + MLoopUV *mloopuv 192 8 + MLoopCol *mloopcol 200 8 + MFace *mface 208 8 + MTFace *mtface 216 8 + TFace *tface 224 8 + MVert *mvert 232 8 + MEdge *medge 240 8 + MDeformVert *dvert 248 8 + MCol *mcol 256 8 + Mesh *texcomesh 264 8 + BMEditMesh *edit_btmesh 272 8 + CustomData vdata 280 192 + CustomData edata 472 192 + CustomData fdata 664 192 + CustomData pdata 856 192 + CustomData ldata 1048 192 + int totvert 1240 4 + int totedge 1244 4 + int totface 1248 4 + int totselect 1252 4 + int totpoly 1256 4 + int totloop 1260 4 + int act_face 1264 4 + float loc 1268 12 + float size 1280 12 + float rot 1292 12 + int drawflag 1304 4 + short texflag 1308 2 + short pad2 1310 6 + short smoothresh 1316 2 + short flag 1318 2 + char cd_flag 1320 1 + char pad 1321 1 + char subdiv 1322 1 + char subdivr 1323 1 + char subsurftype 1324 1 + char editflag 1325 1 + short totcol 1326 2 + Multires *mr 1328 8 + +TFace 64 + + void *tpage 0 8 + float uv 8 32 + int col 40 16 + char flag 56 1 + char transp 57 1 + short mode 58 2 + short tile 60 2 + short unwrap 62 2 + +MFace 20 + + int v1 0 4 + int v2 4 4 + int v3 8 4 + int v4 12 4 + short mat_nr 16 2 + char edcode 18 1 + char flag 19 1 + +MEdge 12 + + int v1 0 4 + int v2 4 4 + char crease 8 1 + char bweight 9 1 + short flag 10 2 + +MDeformWeight 8 + + int def_nr 0 4 + float weight 4 4 + +MDeformVert 16 + + MDeformWeight *dw 0 8 + int totweight 8 4 + int flag 12 4 + +MVert 20 + + float co 0 12 + short no 12 6 + char flag 18 1 + char bweight 19 1 + +MCol 4 + + char a 0 1 + char r 1 1 + char g 2 1 + char b 3 1 + +MPoly 12 + + int loopstart 0 4 + int totloop 4 4 + short mat_nr 8 2 + char flag 10 1 + char pad 11 1 + +MLoop 8 + + int v 0 4 + int e 4 4 + +MTexPoly 16 + + Image *tpage 0 8 + char flag 8 1 + char transp 9 1 + short mode 10 2 + short tile 12 2 + short pad 14 2 + +MLoopUV 12 + + float uv 0 8 + int flag 8 4 + +MLoopCol 4 + + char r 0 1 + char g 1 1 + char b 2 1 + char a 3 1 + +MSelect 8 + + int index 0 4 + int type 4 4 + +MTFace 48 + + float uv 0 32 + Image *tpage 32 8 + char flag 40 1 + char transp 41 1 + short mode 42 2 + short tile 44 2 + short unwrap 46 2 + +MFloatProperty 4 + + float f 0 4 + +MIntProperty 4 + + int i 0 4 + +MStringProperty 256 + + char s 0 255 + char s_len 255 1 + +OrigSpaceFace 32 + + float uv 0 32 + +OrigSpaceLoop 8 + + float uv 0 8 + +MDisps 20 + + int totdisp 0 4 + int level 4 4 + float (*disps)() 8 4 + int *hidden 12 8 + +MultiresCol 16 + + float a 0 4 + float r 4 4 + float g 8 4 + float b 12 4 + +MultiresColFace 64 + + MultiresCol col 0 64 + +MultiresFace 24 + + int v 0 16 + int mid 16 4 + char flag 20 1 + char mat_nr 21 1 + char pad 22 2 + +MultiresEdge 12 + + int v 0 8 + int mid 8 4 + +MultiresLevel 64 + + MultiresLevel *next 0 8 + MultiresLevel *prev 8 8 + MultiresFace *faces 16 8 + MultiresColFace *colfaces 24 8 + MultiresEdge *edges 32 8 + int totvert 40 4 + int totface 44 4 + int totedge 48 4 + int pad 52 4 + MVert *verts 56 8 + +Multires 432 + + ListBase levels 0 16 + MVert *verts 16 8 + char level_count 24 1 + char current 25 1 + char newlvl 26 1 + char edgelvl 27 1 + char pinlvl 28 1 + char renderlvl 29 1 + char use_col 30 1 + char flag 31 1 + CustomData vdata 32 192 + CustomData fdata 224 192 + short *edge_flags 416 8 + char *edge_creases 424 8 + +MRecast 4 + + int i 0 4 + +GridPaintMask 16 + + float *data 0 8 + int level 8 4 + int pad 12 4 + +MVertSkin 16 + + float radius 0 12 + int flag 12 4 + +FreestyleEdge 4 + + char flag 0 1 + char pad 1 3 + +FreestyleFace 4 + + char flag 0 1 + char pad 1 3 + +ModifierData 112 + + ModifierData *next 0 8 + ModifierData *prev 8 8 + int type 16 4 + int mode 20 4 + int stackindex 24 4 + int pad 28 4 + char name 32 64 + Scene *scene 96 8 + char *error 104 8 + +MappingInfoModifierData 200 + + ModifierData modifier 0 112 + Tex *texture 112 8 + Object *map_object 120 8 + char uvlayer_name 128 64 + int uvlayer_tmp 192 4 + int texmapping 196 4 + +SubsurfModifierData 136 + + ModifierData modifier 0 112 + short subdivType 112 2 + short levels 114 2 + short renderLevels 116 2 + short flags 118 2 + void *emCache 120 8 + void *mCache 128 8 + +LatticeModifierData 192 + + ModifierData modifier 0 112 + Object *object 112 8 + char name 120 64 + float strength 184 4 + char pad 188 4 + +CurveModifierData 192 + + ModifierData modifier 0 112 + Object *object 112 8 + char name 120 64 + short defaxis 184 2 + char pad 186 6 + +BuildModifierData 128 + + ModifierData modifier 0 112 + float start 112 4 + float length 116 4 + int randomize 120 4 + int seed 124 4 + +MaskModifierData 192 + + ModifierData modifier 0 112 + Object *ob_arm 112 8 + char vgroup 120 64 + int mode 184 4 + int flag 188 4 + +ArrayModifierData 192 + + ModifierData modifier 0 112 + Object *start_cap 112 8 + Object *end_cap 120 8 + Object *curve_ob 128 8 + Object *offset_ob 136 8 + float offset 144 12 + float scale 156 12 + float length 168 4 + float merge_dist 172 4 + int fit_type 176 4 + int offset_type 180 4 + int flags 184 4 + int count 188 4 + +MirrorModifierData 128 + + ModifierData modifier 0 112 + short axis 112 2 + short flag 114 2 + float tolerance 116 4 + Object *mirror_ob 120 8 + +EdgeSplitModifierData 120 + + ModifierData modifier 0 112 + float split_angle 112 4 + int flags 116 4 + +BevelModifierData 200 + + ModifierData modifier 0 112 + float value 112 4 + int res 116 4 + int pad 120 4 + short flags 124 2 + short val_flags 126 2 + short lim_flags 128 2 + short e_flags 130 2 + float bevel_angle 132 4 + char defgrp_name 136 64 + +SmokeModifierData 144 + + ModifierData modifier 0 112 + SmokeDomainSettings *domain 112 8 + SmokeFlowSettings *flow 120 8 + SmokeCollSettings *coll 128 8 + float time 136 4 + int type 140 4 + +DisplaceModifierData 280 + + ModifierData modifier 0 112 + Tex *texture 112 8 + Object *map_object 120 8 + char uvlayer_name 128 64 + int uvlayer_tmp 192 4 + int texmapping 196 4 + float strength 200 4 + int direction 204 4 + char defgrp_name 208 64 + float midlevel 272 4 + int pad 276 4 + +UVProjectModifierData 296 + + ModifierData modifier 0 112 + Object *projectors 112 80 + Image *image 192 8 + int flags 200 4 + int num_projectors 204 4 + float aspectx 208 4 + float aspecty 212 4 + float scalex 216 4 + float scaley 220 4 + char uvlayer_name 224 64 + int uvlayer_tmp 288 4 + int pad 292 4 + +DecimateModifierData 200 + + ModifierData modifier 0 112 + float percent 112 4 + short iter 116 2 + char delimit 118 1 + char pad 119 1 + float angle 120 4 + char defgrp_name 124 64 + short flag 188 2 + short mode 190 2 + int face_count 192 4 + int pad2 196 4 + +SmoothModifierData 184 + + ModifierData modifier 0 112 + float fac 112 4 + char defgrp_name 116 64 + short flag 180 2 + short repeat 182 2 + +CastModifierData 200 + + ModifierData modifier 0 112 + Object *object 112 8 + float fac 120 4 + float radius 124 4 + float size 128 4 + char defgrp_name 132 64 + short flag 196 2 + short type 198 2 + +WaveModifierData 320 + + ModifierData modifier 0 112 + Tex *texture 112 8 + Object *map_object 120 8 + char uvlayer_name 128 64 + int uvlayer_tmp 192 4 + int texmapping 196 4 + Object *objectcenter 200 8 + char defgrp_name 208 64 + short flag 272 2 + short pad 274 2 + float startx 276 4 + float starty 280 4 + float height 284 4 + float width 288 4 + float narrow 292 4 + float speed 296 4 + float damp 300 4 + float falloff 304 4 + float timeoffs 308 4 + float lifetime 312 4 + float pad1 316 4 + +ArmatureModifierData 200 + + ModifierData modifier 0 112 + short deformflag 112 2 + short multi 114 2 + int pad2 116 4 + Object *object 120 8 + float *prevCos 128 8 + char defgrp_name 136 64 + +HookModifierData 344 + + ModifierData modifier 0 112 + Object *object 112 8 + char subtarget 120 64 + float parentinv 184 64 + float cent 248 12 + float falloff 260 4 + int *indexar 264 8 + int totindex 272 4 + float force 276 4 + char name 280 64 + +SoftbodyModifierData 112 + + ModifierData modifier 0 112 + +ClothModifierData 168 + + ModifierData modifier 0 112 + Scene *scene 112 8 + Cloth *clothObject 120 8 + ClothSimSettings *sim_parms 128 8 + ClothCollSettings *coll_parms 136 8 + PointCache *point_cache 144 8 + ListBase ptcaches 152 16 + +CollisionModifierData 192 + + ModifierData modifier 0 112 + MVert *x 112 8 + MVert *xnew 120 8 + MVert *xold 128 8 + MVert *current_xnew 136 8 + MVert *current_x 144 8 + MVert *current_v 152 8 + MFace *mfaces 160 8 + int numverts 168 4 + int numfaces 172 4 + float time_x 176 4 + float time_xnew 180 4 + BVHTree *bvhtree 184 8 + +SurfaceModifierData 152 + + ModifierData modifier 0 112 + MVert *x 112 8 + MVert *v 120 8 + DerivedMesh *dm 128 8 + BVHTreeFromMesh *bvhtree 136 8 + int cfra 144 4 + int numverts 148 4 + +BooleanModifierData 128 + + ModifierData modifier 0 112 + Object *object 112 8 + int operation 120 4 + int pad 124 4 + +MDefInfluence 8 + + int vertex 0 4 + float weight 4 4 + +MDefCell 8 + + int offset 0 4 + int totinfluence 4 4 + +MeshDeformModifierData 360 + + ModifierData modifier 0 112 + Object *object 112 8 + char defgrp_name 120 64 + short gridsize 184 2 + short flag 186 2 + short mode 188 2 + short pad 190 2 + MDefInfluence *bindinfluences 192 8 + int *bindoffsets 200 8 + float *bindcagecos 208 8 + int totvert 216 4 + int totcagevert 220 4 + MDefCell *dyngrid 224 8 + MDefInfluence *dyninfluences 232 8 + int *dynverts 240 8 + int *pad2 248 8 + int dyngridsize 256 4 + int totinfluence 260 4 + float dyncellmin 264 12 + float dyncellwidth 276 4 + float bindmat 280 64 + float *bindweights 344 8 + float *bindcos 352 8 + void (*bindfunc)() 360 0 + +ParticleSystemModifierData 144 + + ModifierData modifier 0 112 + ParticleSystem *psys 112 8 + DerivedMesh *dm 120 8 + int totdmvert 128 4 + int totdmedge 132 4 + int totdmface 136 4 + short flag 140 2 + short rt 142 2 + +ParticleInstanceModifierData 136 + + ModifierData modifier 0 112 + Object *ob 112 8 + short psys 120 2 + short flag 122 2 + short axis 124 2 + short rt 126 2 + float position 128 4 + float random_position 132 4 + +ExplodeModifierData 192 + + ModifierData modifier 0 112 + int *facepa 112 8 + short flag 120 2 + short vgroup 122 2 + float protect 124 4 + char uvname 128 64 + +MultiresModifierData 120 + + ModifierData modifier 0 112 + char lvl 112 1 + char sculptlvl 113 1 + char renderlvl 114 1 + char totlvl 115 1 + char simple 116 1 + char flags 117 1 + char pad 118 2 + +FluidsimModifierData 128 + + ModifierData modifier 0 112 + FluidsimSettings *fss 112 8 + PointCache *point_cache 120 8 + +ShrinkwrapModifierData 208 + + ModifierData modifier 0 112 + Object *target 112 8 + Object *auxTarget 120 8 + char vgroup_name 128 64 + float keepDist 192 4 + short shrinkType 196 2 + short shrinkOpts 198 2 + float projLimit 200 4 + char projAxis 204 1 + char subsurfLevels 205 1 + char pad 206 2 + +SimpleDeformModifierData 200 + + ModifierData modifier 0 112 + Object *origin 112 8 + char vgroup_name 120 64 + float factor 184 4 + float limit 188 8 + char mode 196 1 + char axis 197 1 + char pad 198 2 + +ShapeKeyModifierData 112 + + ModifierData modifier 0 112 + +SolidifyModifierData 216 + + ModifierData modifier 0 112 + char defgrp_name 112 64 + float offset 176 4 + float offset_fac 180 4 + float offset_fac_vg 184 4 + float offset_clamp 188 4 + float pad 192 4 + float crease_inner 196 4 + float crease_outer 200 4 + float crease_rim 204 4 + int flag 208 4 + short mat_ofs 212 2 + short mat_ofs_rim 214 2 + +ScrewModifierData 144 + + ModifierData modifier 0 112 + Object *ob_axis 112 8 + int steps 120 4 + int render_steps 124 4 + int iter 128 4 + float screw_ofs 132 4 + float angle 136 4 + short axis 140 2 + short flag 142 2 + +OceanModifierData 1296 + + ModifierData modifier 0 112 + Ocean *ocean 112 8 + OceanCache *oceancache 120 8 + int resolution 128 4 + int spatial_size 132 4 + float wind_velocity 136 4 + float damp 140 4 + float smallest_wave 144 4 + float depth 148 4 + float wave_alignment 152 4 + float wave_direction 156 4 + float wave_scale 160 4 + float chop_amount 164 4 + float foam_coverage 168 4 + float time 172 4 + int bakestart 176 4 + int bakeend 180 4 + char cachepath 184 1024 + char foamlayername 1208 64 + char cached 1272 1 + char geometry_mode 1273 1 + char flag 1274 1 + char refresh 1275 1 + short repeat_x 1276 2 + short repeat_y 1278 2 + int seed 1280 4 + float size 1284 4 + float foam_fade 1288 4 + int pad 1292 4 + +WarpModifierData 304 + + ModifierData modifier 0 112 + Tex *texture 112 8 + Object *map_object 120 8 + char uvlayer_name 128 64 + int uvlayer_tmp 192 4 + int texmapping 196 4 + Object *object_from 200 8 + Object *object_to 208 8 + CurveMapping *curfalloff 216 8 + char defgrp_name 224 64 + float strength 288 4 + float falloff_radius 292 4 + char flag 296 1 + char falloff_type 297 1 + char pad 298 6 + +WeightVGEditModifierData 360 + + ModifierData modifier 0 112 + char defgrp_name 112 64 + short edit_flags 176 2 + short falloff_type 178 2 + float default_weight 180 4 + CurveMapping *cmap_curve 184 8 + float add_threshold 192 4 + float rem_threshold 196 4 + float mask_constant 200 4 + char mask_defgrp_name 204 64 + int mask_tex_use_channel 268 4 + Tex *mask_texture 272 8 + Object *mask_tex_map_obj 280 8 + int mask_tex_mapping 288 4 + char mask_tex_uvlayer_name 292 64 + int pad_i1 356 4 + +WeightVGMixModifierData 416 + + ModifierData modifier 0 112 + char defgrp_name_a 112 64 + char defgrp_name_b 176 64 + float default_weight_a 240 4 + float default_weight_b 244 4 + char mix_mode 248 1 + char mix_set 249 1 + char pad_c1 250 6 + float mask_constant 256 4 + char mask_defgrp_name 260 64 + int mask_tex_use_channel 324 4 + Tex *mask_texture 328 8 + Object *mask_tex_map_obj 336 8 + int mask_tex_mapping 344 4 + char mask_tex_uvlayer_name 348 64 + int pad_i1 412 4 + +WeightVGProximityModifierData 360 + + ModifierData modifier 0 112 + char defgrp_name 112 64 + int proximity_mode 176 4 + int proximity_flags 180 4 + Object *proximity_ob_target 184 8 + float mask_constant 192 4 + char mask_defgrp_name 196 64 + int mask_tex_use_channel 260 4 + Tex *mask_texture 264 8 + Object *mask_tex_map_obj 272 8 + int mask_tex_mapping 280 4 + char mask_tex_uvlayer_name 284 64 + float min_dist 348 4 + float max_dist 352 4 + short falloff_type 356 2 + short pad_s1 358 2 + +DynamicPaintModifierData 136 + + ModifierData modifier 0 112 + DynamicPaintCanvasSettings *canvas 112 8 + DynamicPaintBrushSettings *brush 120 8 + int type 128 4 + int pad 132 4 + +RemeshModifierData 128 + + ModifierData modifier 0 112 + float threshold 112 4 + float scale 116 4 + float hermite_num 120 4 + char depth 124 1 + char flag 125 1 + char mode 126 1 + char pad 127 1 + +SkinModifierData 120 + + ModifierData modifier 0 112 + float branch_smoothing 112 4 + char flag 116 1 + char symmetry_axes 117 1 + char pad 118 2 + +TriangulateModifierData 120 + + ModifierData modifier 0 112 + int flag 112 4 + int pad 116 4 + +LaplacianSmoothModifierData 192 + + ModifierData modifier 0 112 + float lambda 112 4 + float lambda_border 116 4 + float pad1 120 4 + char defgrp_name 124 64 + short flag 188 2 + short repeat 190 2 + +UVWarpModifierData 400 + + ModifierData modifier 0 112 + char axis_u 112 1 + char axis_v 113 1 + char pad 114 6 + float center 120 8 + Object *object_src 128 8 + char bone_src 136 64 + Object *object_dst 200 8 + char bone_dst 208 64 + char vgroup_name 272 64 + char uvlayer_name 336 64 + +MeshCacheModifierData 1176 + + ModifierData modifier 0 112 + char flag 112 1 + char type 113 1 + char time_mode 114 1 + char play_mode 115 1 + char forward_axis 116 1 + char up_axis 117 1 + char flip_axis 118 1 + char interp 119 1 + float factor 120 4 + char deform_mode 124 1 + char pad 125 7 + float frame_start 132 4 + float frame_scale 136 4 + float eval_frame 140 4 + float eval_time 144 4 + float eval_factor 148 4 + char filepath 152 1024 + +EditLatt 16 + + Lattice *latt 0 8 + int shapenr 8 4 + char pad 12 4 + +Lattice 280 + + ID id 0 120 + AnimData *adt 120 8 + short pntsu 128 2 + short pntsv 130 2 + short pntsw 132 2 + short flag 134 2 + short opntsu 136 2 + short opntsv 138 2 + short opntsw 140 2 + short pad2 142 2 + char typeu 144 1 + char typev 145 1 + char typew 146 1 + char pad3 147 1 + int actbp 148 4 + float fu 152 4 + float fv 156 4 + float fw 160 4 + float du 164 4 + float dv 168 4 + float dw 172 4 + BPoint *def 176 8 + Ipo *ipo 184 8 + Key *key 192 8 + MDeformVert *dvert 200 8 + char vgroup 208 64 + EditLatt *editlatt 272 8 + +bDeformGroup 88 + + bDeformGroup *next 0 8 + bDeformGroup *prev 8 8 + char name 16 64 + char flag 80 1 + char pad 81 7 + +BoundBox 104 + + float vec 0 96 + int flag 96 4 + int pad 100 4 + +Object 1416 + + ID id 0 120 + AnimData *adt 120 8 + SculptSession *sculpt 128 8 + short type 136 2 + short partype 138 2 + int par1 140 4 + int par2 144 4 + int par3 148 4 + char parsubstr 152 64 + Object *parent 216 8 + Object *track 224 8 + Object *proxy 232 8 + Object *proxy_group 240 8 + Object *proxy_from 248 8 + Ipo *ipo 256 8 + BoundBox *bb 264 8 + bAction *action 272 8 + bAction *poselib 280 8 + bPose *pose 288 8 + void *data 296 8 + bGPdata *gpd 304 8 + bAnimVizSettings avs 312 48 + bMotionPath *mpath 360 8 + ListBase constraintChannels 368 16 + ListBase effect 384 16 + ListBase defbase 400 16 + ListBase modifiers 416 16 + int mode 432 4 + int restore_mode 436 4 + Material **mat 440 8 + char *matbits 448 8 + int totcol 456 4 + int actcol 460 4 + float loc 464 12 + float dloc 476 12 + float orig 488 12 + float size 500 12 + float dsize 512 12 + float dscale 524 12 + float rot 536 12 + float drot 548 12 + float quat 560 16 + float dquat 576 16 + float rotAxis 592 12 + float drotAxis 604 12 + float rotAngle 616 4 + float drotAngle 620 4 + float obmat 624 64 + float parentinv 688 64 + float constinv 752 64 + float imat 816 64 + float imat_ren 880 64 + int lay 944 4 + float sf 948 4 + short flag 952 2 + short colbits 954 2 + short transflag 956 2 + short protectflag 958 2 + short trackflag 960 2 + short upflag 962 2 + short nlaflag 964 2 + short ipoflag 966 2 + short scaflag 968 2 + char scavisflag 970 1 + char depsflag 971 1 + int dupon 972 4 + int dupoff 976 4 + int dupsta 980 4 + int dupend 984 4 + float mass 988 4 + float damping 992 4 + float inertia 996 4 + float formfactor 1000 4 + float rdamping 1004 4 + float sizefac 1008 4 + float margin 1012 4 + float max_vel 1016 4 + float min_vel 1020 4 + float m_contactProcessingThreshold 1024 4 + float obstacleRad 1028 4 + float step_height 1032 4 + float jump_speed 1036 4 + float fall_speed 1040 4 + short col_group 1044 2 + short col_mask 1046 2 + short rotmode 1048 2 + char boundtype 1050 1 + char collision_boundtype 1051 1 + short dtx 1052 2 + char dt 1054 1 + char empty_drawtype 1055 1 + float empty_drawsize 1056 4 + float dupfacesca 1060 4 + ListBase prop 1064 16 + ListBase sensors 1080 16 + ListBase controllers 1096 16 + ListBase actuators 1112 16 + float bbsize 1128 12 + short index 1140 2 + short actdef 1142 2 + float col 1144 16 + int gameflag 1160 4 + int gameflag2 1164 4 + BulletSoftBody *bsoft 1168 8 + char restrictflag 1176 1 + char recalc 1177 1 + short softflag 1178 2 + float anisotropicFriction 1180 12 + ListBase constraints 1192 16 + ListBase nlastrips 1208 16 + ListBase hooks 1224 16 + ListBase particlesystem 1240 16 + PartDeflect *pd 1256 8 + SoftBody *soft 1264 8 + Group *dup_group 1272 8 + char body_type 1280 1 + char shapeflag 1281 1 + short shapenr 1282 2 + float smoothresh 1284 4 + FluidsimSettings *fluidsimSettings 1288 8 + DerivedMesh *derivedDeform 1296 8 + DerivedMesh *derivedFinal 1304 8 + int *pad 1312 8 + uint64_t lastDataMask 1320 8 + uint64_t customdata_mask 1328 8 + int state 1336 4 + int init_state 1340 4 + ListBase gpulamp 1344 16 + ListBase pc_ids 1360 16 + ListBase *duplilist 1376 8 + RigidBodyOb *rigidbody_object 1384 8 + RigidBodyCon *rigidbody_constraint 1392 8 + float ima_ofs 1400 8 + CurveCache *curve_cache 1408 8 + +ObHook 256 + + ObHook *next 0 8 + ObHook *prev 8 8 + Object *parent 16 8 + float parentinv 24 64 + float mat 88 64 + float cent 152 12 + float falloff 164 4 + char name 168 64 + int *indexar 232 8 + int totindex 240 4 + int curindex 244 4 + short type 248 2 + short active 250 2 + float force 252 4 + +DupliObject 224 + + DupliObject *next 0 8 + DupliObject *prev 8 8 + Object *ob 16 8 + int origlay 24 4 + int pad 28 4 + float mat 32 64 + float omat 96 64 + float orco 160 12 + float uv 172 8 + short type 180 2 + char no_draw 182 1 + char animated 183 1 + int persistent_id 184 32 + ParticleSystem *particle_system 216 8 + +PartDeflect 160 + + int flag 0 4 + short deflect 4 2 + short forcefield 6 2 + short falloff 8 2 + short shape 10 2 + short tex_mode 12 2 + short kink 14 2 + short kink_axis 16 2 + short zdir 18 2 + float f_strength 20 4 + float f_damp 24 4 + float f_flow 28 4 + float f_size 32 4 + float f_power 36 4 + float maxdist 40 4 + float mindist 44 4 + float f_power_r 48 4 + float maxrad 52 4 + float minrad 56 4 + float pdef_damp 60 4 + float pdef_rdamp 64 4 + float pdef_perm 68 4 + float pdef_frict 72 4 + float pdef_rfrict 76 4 + float pdef_stickness 80 4 + float absorption 84 4 + float pdef_sbdamp 88 4 + float pdef_sbift 92 4 + float pdef_sboft 96 4 + float clump_fac 100 4 + float clump_pow 104 4 + float kink_freq 108 4 + float kink_shape 112 4 + float kink_amp 116 4 + float free_end 120 4 + float tex_nabla 124 4 + Tex *tex 128 8 + RNG *rng 136 8 + float f_noise 144 4 + int seed 148 4 + Object *f_source 152 8 + +EffectorWeights 80 + + Group *group 0 8 + float weight 8 56 + float global_gravity 64 4 + short flag 68 2 + short rt 70 6 + int pad 76 4 + +PTCacheExtra 32 + + PTCacheExtra *next 0 8 + PTCacheExtra *prev 8 8 + int type 16 4 + int totdata 20 4 + void *data 24 8 + +PTCacheMem 176 + + PTCacheMem *next 0 8 + PTCacheMem *prev 8 8 + int frame 16 4 + int totpoint 20 4 + int data_types 24 4 + int flag 28 4 + void *data 32 64 + void *cur 96 64 + ListBase extradata 160 16 + +PointCache 1312 + + PointCache *next 0 8 + PointCache *prev 8 8 + int flag 16 4 + int step 20 4 + int simframe 24 4 + int startframe 28 4 + int endframe 32 4 + int editframe 36 4 + int last_exact 40 4 + int last_valid 44 4 + int pad 48 4 + int totpoint 52 4 + int index 56 4 + short compression 60 2 + short rt 62 2 + char name 64 64 + char prev_name 128 64 + char info 192 64 + char path 256 1024 + char *cached_frames 1280 8 + ListBase mem_cache 1288 16 + PTCacheEdit *edit 1304 8 + void (*free_edit)() 1312 0 + +SBVertex 16 + + float vec 0 16 + +BulletSoftBody 120 + + int flag 0 4 + float linStiff 4 4 + float angStiff 8 4 + float volume 12 4 + int viterations 16 4 + int piterations 20 4 + int diterations 24 4 + int citerations 28 4 + float kSRHR_CL 32 4 + float kSKHR_CL 36 4 + float kSSHR_CL 40 4 + float kSR_SPLT_CL 44 4 + float kSK_SPLT_CL 48 4 + float kSS_SPLT_CL 52 4 + float kVCF 56 4 + float kDP 60 4 + float kDG 64 4 + float kLF 68 4 + float kPR 72 4 + float kVC 76 4 + float kDF 80 4 + float kMT 84 4 + float kCHR 88 4 + float kKHR 92 4 + float kSHR 96 4 + float kAHR 100 4 + int collisionflags 104 4 + int numclusteriterations 108 4 + float welding 112 4 + float margin 116 4 + +SoftBody 472 + + int totpoint 0 4 + int totspring 4 4 + BodyPoint *bpoint 8 8 + BodySpring *bspring 16 8 + char pad 24 1 + char msg_lock 25 1 + short msg_value 26 2 + float nodemass 28 4 + char namedVG_Mass 32 64 + float grav 96 4 + float mediafrict 100 4 + float rklimit 104 4 + float physics_speed 108 4 + float goalspring 112 4 + float goalfrict 116 4 + float mingoal 120 4 + float maxgoal 124 4 + float defgoal 128 4 + short vertgroup 132 2 + char namedVG_Softgoal 134 64 + short fuzzyness 198 2 + float inspring 200 4 + float infrict 204 4 + char namedVG_Spring_K 208 64 + int sfra 272 4 + int efra 276 4 + int interval 280 4 + short local 284 2 + short solverflags 286 2 + SBVertex **keys 288 8 + int totpointkey 296 4 + int totkey 300 4 + float secondspring 304 4 + float colball 308 4 + float balldamp 312 4 + float ballstiff 316 4 + short sbc_mode 320 2 + short aeroedge 322 2 + short minloops 324 2 + short maxloops 326 2 + short choke 328 2 + short solver_ID 330 2 + short plastic 332 2 + short springpreload 334 2 + SBScratch *scratch 336 8 + float shearstiff 344 4 + float inpush 348 4 + PointCache *pointcache 352 8 + ListBase ptcaches 360 16 + EffectorWeights *effector_weights 376 8 + float lcom 384 12 + float lrot 396 36 + float lscale 432 36 + int last_frame 468 4 + +FluidVertexVelocity 12 + + float vel 0 12 + +FluidsimSettings 1256 + + FluidsimModifierData *fmd 0 8 + int threads 8 4 + int pad1 12 4 + short type 16 2 + short show_advancedoptions 18 2 + short resolutionxyz 20 2 + short previewresxyz 22 2 + float realsize 24 4 + short guiDisplayMode 28 2 + short renderDisplayMode 30 2 + float viscosityValue 32 4 + short viscosityMode 36 2 + short viscosityExponent 38 2 + float grav 40 12 + float animStart 52 4 + float animEnd 56 4 + int bakeStart 60 4 + int bakeEnd 64 4 + int frameOffset 68 4 + int pad2 72 4 + float gstar 76 4 + int maxRefine 80 4 + float iniVelx 84 4 + float iniVely 88 4 + float iniVelz 92 4 + Mesh *orgMesh 96 8 + Mesh *meshBB 104 8 + char surfdataPath 112 1024 + float bbStart 1136 12 + float bbSize 1148 12 + Ipo *ipo 1160 8 + short typeFlags 1168 2 + char domainNovecgen 1170 1 + char volumeInitType 1171 1 + float partSlipValue 1172 4 + int generateTracers 1176 4 + float generateParticles 1180 4 + float surfaceSmoothing 1184 4 + int surfaceSubdivs 1188 4 + int flag 1192 4 + float particleInfSize 1196 4 + float particleInfAlpha 1200 4 + float farFieldSize 1204 4 + FluidVertexVelocity *meshVelocities 1208 8 + int totvert 1216 4 + float cpsTimeStart 1220 4 + float cpsTimeEnd 1224 4 + float cpsQuality 1228 4 + float attractforceStrength 1232 4 + float attractforceRadius 1236 4 + float velocityforceStrength 1240 4 + float velocityforceRadius 1244 4 + int lastgoodframe 1248 4 + float animRate 1252 4 + +World 528 + + ID id 0 120 + AnimData *adt 120 8 + short colormodel 128 2 + short totex 130 2 + short texact 132 2 + short mistype 134 2 + float horr 136 4 + float horg 140 4 + float horb 144 4 + float zenr 148 4 + float zeng 152 4 + float zenb 156 4 + float ambr 160 4 + float ambg 164 4 + float ambb 168 4 + float exposure 172 4 + float exp 176 4 + float range 180 4 + float linfac 184 4 + float logfac 188 4 + float gravity 192 4 + float activityBoxRadius 196 4 + short skytype 200 2 + short mode 202 2 + short occlusionRes 204 2 + short physicsEngine 206 2 + short ticrate 208 2 + short maxlogicstep 210 2 + short physubstep 212 2 + short maxphystep 214 2 + float misi 216 4 + float miststa 220 4 + float mistdist 224 4 + float misthi 228 4 + float starr 232 4 + float starg 236 4 + float starb 240 4 + float stark 244 4 + float starsize 248 4 + float starmindist 252 4 + float stardist 256 4 + float starcolnoise 260 4 + short dofsta 264 2 + short dofend 266 2 + short dofmin 268 2 + short dofmax 270 2 + float aodist 272 4 + float aodistfac 276 4 + float aoenergy 280 4 + float aobias 284 4 + short aomode 288 2 + short aosamp 290 2 + short aomix 292 2 + short aocolor 294 2 + float ao_adapt_thresh 296 4 + float ao_adapt_speed_fac 300 4 + float ao_approx_error 304 4 + float ao_approx_correction 308 4 + float ao_indirect_energy 312 4 + float ao_env_energy 316 4 + float ao_pad2 320 4 + short ao_indirect_bounces 324 2 + short ao_pad 326 2 + short ao_samp_method 328 2 + short ao_gather_method 330 2 + short ao_approx_passes 332 2 + short flag 334 2 + float *aosphere 336 8 + float *aotables 344 8 + Ipo *ipo 352 8 + MTex *mtex 360 144 + short pr_texture 504 2 + short use_nodes 506 2 + short pad 508 4 + PreviewImage *preview 512 8 + bNodeTree *nodetree 520 8 + +Base 40 + + Base *next 0 8 + Base *prev 8 8 + int lay 16 4 + int selcol 20 4 + int flag 24 4 + short sx 28 2 + short sy 30 2 + Object *object 32 8 + +AviCodecData 184 + + void *lpFormat 0 8 + void *lpParms 8 8 + int cbFormat 16 4 + int cbParms 20 4 + int fccType 24 4 + int fccHandler 28 4 + int dwKeyFrameEvery 32 4 + int dwQuality 36 4 + int dwBytesPerSecond 40 4 + int dwFlags 44 4 + int dwInterleaveEvery 48 4 + int pad 52 4 + char avicodecname 56 128 + +QuicktimeCodecData 152 + + void *cdParms 0 8 + void *pad 8 8 + int cdSize 16 4 + int pad2 20 4 + char qtcodecname 24 128 + +QuicktimeCodecSettings 64 + + int codecType 0 4 + int codecSpatialQuality 4 4 + int codec 8 4 + int codecFlags 12 4 + int colorDepth 16 4 + int codecTemporalQuality 20 4 + int minSpatialQuality 24 4 + int minTemporalQuality 28 4 + int keyFrameRate 32 4 + int bitRate 36 4 + int audiocodecType 40 4 + int audioSampleRate 44 4 + short audioBitDepth 48 2 + short audioChannels 50 2 + int audioCodecFlags 52 4 + int audioBitRate 56 4 + int pad1 60 4 + +FFMpegCodecData 72 + + int type 0 4 + int codec 4 4 + int audio_codec 8 4 + int video_bitrate 12 4 + int audio_bitrate 16 4 + int audio_mixrate 20 4 + int audio_channels 24 4 + int audio_pad 28 4 + float audio_volume 32 4 + int gop_size 36 4 + int flags 40 4 + int rc_min_rate 44 4 + int rc_max_rate 48 4 + int rc_buffer_size 52 4 + int mux_packet_size 56 4 + int mux_rate 60 4 + IDProperty *properties 64 8 + +AudioData 32 + + int mixrate 0 4 + float main 4 4 + float speed_of_sound 8 4 + float doppler_factor 12 4 + int distance_model 16 4 + short flag 20 2 + short pad 22 2 + float volume 24 4 + float pad2 28 4 + +SceneRenderLayer 184 + + SceneRenderLayer *next 0 8 + SceneRenderLayer *prev 8 8 + char name 16 64 + Material *mat_override 80 8 + Group *light_override 88 8 + int lay 96 4 + int lay_zmask 100 4 + int lay_exclude 104 4 + int layflag 108 4 + int passflag 112 4 + int pass_xor 116 4 + int samples 120 4 + int pad 124 4 + FreestyleConfig freestyleConfig 128 56 + +ImageFormatData 248 + + char imtype 0 1 + char depth 1 1 + char planes 2 1 + char flag 3 1 + char quality 4 1 + char compress 5 1 + char exr_codec 6 1 + char cineon_flag 7 1 + short cineon_white 8 2 + short cineon_black 10 2 + float cineon_gamma 12 4 + char jp2_flag 16 1 + char jp2_codec 17 1 + char pad 18 6 + ColorManagedViewSettings view_settings 24 160 + ColorManagedDisplaySettings display_settings 184 64 + +RenderData 2608 + + ImageFormatData im_format 0 248 + AviCodecData *avicodecdata 248 8 + QuicktimeCodecData *qtcodecdata 256 8 + QuicktimeCodecSettings qtcodecsettings 264 64 + FFMpegCodecData ffcodecdata 328 72 + int cfra 400 4 + int sfra 404 4 + int efra 408 4 + float subframe 412 4 + int psfra 416 4 + int pefra 420 4 + int images 424 4 + int framapto 428 4 + short flag 432 2 + short threads 434 2 + float framelen 436 4 + float blurfac 440 4 + float edgeR 444 4 + float edgeG 448 4 + float edgeB 452 4 + short fullscreen 456 2 + short xplay 458 2 + short yplay 460 2 + short freqplay 462 2 + short depth 464 2 + short attrib 466 2 + int frame_step 468 4 + short stereomode 472 2 + short dimensionspreset 474 2 + short filtertype 476 2 + short size 478 2 + short maximsize 480 2 + short pad6 482 2 + int xsch 484 4 + int ysch 488 4 + short xparts 492 2 + short yparts 494 2 + int tilex 496 4 + int tiley 500 4 + short planes 504 2 + short imtype 506 2 + short subimtype 508 2 + short quality 510 2 + short displaymode 512 2 + short pad7 514 2 + int scemode 516 4 + int mode 520 4 + int raytrace_options 524 4 + short raytrace_structure 528 2 + short pad1 530 2 + short ocres 532 2 + short pad4 534 2 + short alphamode 536 2 + short osa 538 2 + short frs_sec 540 2 + short edgeint 542 2 + rctf safety 544 16 + rctf border 560 16 + rcti disprect 576 16 + ListBase layers 592 16 + short actlay 608 2 + short mblur_samples 610 2 + float xasp 612 4 + float yasp 616 4 + float frs_sec_base 620 4 + float gauss 624 4 + int color_mgt_flag 628 4 + float postgamma 632 4 + float posthue 636 4 + float postsat 640 4 + float dither_intensity 644 4 + short bake_osa 648 2 + short bake_filter 650 2 + short bake_mode 652 2 + short bake_flag 654 2 + short bake_normal_space 656 2 + short bake_quad_split 658 2 + float bake_maxdist 660 4 + float bake_biasdist 664 4 + short bake_samples 668 2 + short bake_pad 670 2 + char pic 672 1024 + int stamp 1696 4 + short stamp_font_id 1700 2 + short pad3 1702 2 + char stamp_udata 1704 768 + float fg_stamp 2472 16 + float bg_stamp 2488 16 + char seq_prev_type 2504 1 + char seq_rend_type 2505 1 + char seq_flag 2506 1 + char pad5 2507 5 + int simplify_flag 2512 4 + short simplify_subsurf 2516 2 + short simplify_shadowsamples 2518 2 + float simplify_particles 2520 4 + float simplify_aosss 2524 4 + short cineonwhite 2528 2 + short cineonblack 2530 2 + float cineongamma 2532 4 + short jp2_preset 2536 2 + short jp2_depth 2538 2 + int rpad3 2540 4 + short domeres 2544 2 + short domemode 2546 2 + short domeangle 2548 2 + short dometilt 2550 2 + float domeresbuf 2552 4 + float pad2 2556 4 + Text *dometext 2560 8 + int line_thickness_mode 2568 4 + float unit_line_thickness 2572 4 + char engine 2576 32 + +RenderProfile 64 + + RenderProfile *next 0 8 + RenderProfile *prev 8 8 + char name 16 32 + short particle_perc 48 2 + short subsurf_max 50 2 + short shadbufsample_max 52 2 + short pad1 54 2 + float ao_error 56 4 + float pad2 60 4 + +GameDome 24 + + short res 0 2 + short mode 2 2 + short angle 4 2 + short tilt 6 2 + float resbuf 8 4 + float pad2 12 4 + Text *warptext 16 8 + +GameFraming 16 + + float col 0 12 + char type 12 1 + char pad1 13 1 + char pad2 14 1 + char pad3 15 1 + +RecastData 56 + + float cellsize 0 4 + float cellheight 4 4 + float agentmaxslope 8 4 + float agentmaxclimb 12 4 + float agentheight 16 4 + float agentradius 20 4 + float edgemaxlen 24 4 + float edgemaxerror 28 4 + float regionminsize 32 4 + float regionmergesize 36 4 + int vertsperpoly 40 4 + float detailsampledist 44 4 + float detailsamplemaxerror 48 4 + short pad1 52 2 + short pad2 54 2 + +GameData 184 + + GameFraming framing 0 16 + short playerflag 16 2 + short xplay 18 2 + short yplay 20 2 + short freqplay 22 2 + short depth 24 2 + short attrib 26 2 + short rt1 28 2 + short rt2 30 2 + short aasamples 32 2 + short pad4 34 6 + GameDome dome 40 24 + short stereoflag 64 2 + short stereomode 66 2 + float eyeseparation 68 4 + RecastData recastData 72 56 + float gravity 128 4 + float activityBoxRadius 132 4 + int flag 136 4 + short mode 140 2 + short matmode 142 2 + short occlusionRes 144 2 + short physicsEngine 146 2 + short exitkey 148 2 + short vsync 150 2 + short ticrate 152 2 + short maxlogicstep 154 2 + short physubstep 156 2 + short maxphystep 158 2 + short obstacleSimulation 160 2 + short raster_storage 162 2 + float levelHeight 164 4 + float deactivationtime 168 4 + float lineardeactthreshold 172 4 + float angulardeactthreshold 176 4 + float pad2 180 4 + +TimeMarker 96 + + TimeMarker *next 0 8 + TimeMarker *prev 8 8 + int frame 16 4 + char name 20 64 + int flag 84 4 + Object *camera 88 8 + +Paint 32 + + Brush *brush 0 8 + void *paint_cursor 8 8 + char paint_cursor_col 16 4 + int flags 20 4 + int num_input_samples 24 4 + int pad 28 4 + +ImagePaintSettings 56 + + Paint paint 0 32 + short flag 32 2 + short pad 34 2 + short seam_bleed 36 2 + short normal_angle 38 2 + short screen_grab_size 40 4 + int pad1 44 4 + void *paintcursor 48 8 + +ParticleBrushData 16 + + short size 0 2 + short step 2 2 + short invert 4 2 + short count 6 2 + int flag 8 4 + float strength 12 4 + +ParticleEditSettings 168 + + short flag 0 2 + short totrekey 2 2 + short totaddkey 4 2 + short brushtype 6 2 + ParticleBrushData brush 8 112 + void *paintcursor 120 8 + float emitterdist 128 4 + float rt 132 4 + int selectmode 136 4 + int edittype 140 4 + int draw_step 144 4 + int fade_frames 148 4 + Scene *scene 152 8 + Object *object 160 8 + +Sculpt 56 + + Paint paint 0 32 + int flags 32 4 + int radial_symm 36 12 + int detail_size 48 4 + int symmetrize_direction 52 4 + +UvSculpt 32 + + Paint paint 0 32 + +VPaint 64 + + Paint paint 0 32 + short flag 32 2 + short pad 34 2 + int tot 36 4 + int *vpaint_prev 40 8 + MDeformVert *wpaint_prev 48 8 + void *paintcursor 56 8 + +TransformOrientation 120 + + TransformOrientation *next 0 8 + TransformOrientation *prev 8 8 + char name 16 64 + float mat 80 36 + int pad 116 4 + +UnifiedPaintSettings 80 + + int size 0 4 + float unprojected_radius 4 4 + float alpha 8 4 + float weight 12 4 + int flag 16 4 + float last_rake 20 8 + int pad 28 4 + float brush_rotation 32 4 + int draw_anchored 36 4 + int anchored_size 40 4 + float anchored_initial_mouse 44 8 + int draw_pressure 52 4 + float pressure_value 56 4 + float tex_mouse 60 8 + float mask_tex_mouse 68 8 + float pixel_radius 76 4 + +MeshStatVis 40 + + char type 0 1 + char _pad1 1 2 + char overhang_axis 3 1 + float overhang_min 4 4 + float overhang_max 8 4 + float thickness_min 12 4 + float thickness_max 16 4 + char thickness_samples 20 1 + char _pad2 21 3 + float distort_min 24 4 + float distort_max 28 4 + float sharp_min 32 4 + float sharp_max 36 4 + +ToolSettings 600 + + VPaint *vpaint 0 8 + VPaint *wpaint 8 8 + Sculpt *sculpt 16 8 + UvSculpt *uvsculpt 24 8 + float vgroup_weight 32 4 + short cornertype 36 2 + short pad1 38 2 + float jointrilimit 40 4 + float degr 44 4 + short step 48 2 + short turn 50 2 + float extr_offs 52 4 + float doublimit 56 4 + float normalsize 60 4 + short automerge 64 2 + short selectmode 66 2 + short segments 68 2 + short rings 70 2 + short vertices 72 2 + short unwrapper 74 2 + float uvcalc_radius 76 4 + float uvcalc_cubesize 80 4 + float uvcalc_margin 84 4 + short uvcalc_mapdir 88 2 + short uvcalc_mapalign 90 2 + short uvcalc_flag 92 2 + short uv_flag 94 2 + short uv_selectmode 96 2 + short pad2 98 2 + short gpencil_flags 100 2 + short autoik_chainlen 102 2 + ImagePaintSettings imapaint 104 56 + ParticleEditSettings particle 160 168 + float proportional_size 328 4 + float select_thresh 332 4 + float clean_thresh 336 4 + short autokey_mode 340 2 + short autokey_flag 342 2 + char multires_subdiv_type 344 1 + char pad3 345 5 + short skgen_resolution 350 2 + float skgen_threshold_internal 352 4 + float skgen_threshold_external 356 4 + float skgen_length_ratio 360 4 + float skgen_length_limit 364 4 + float skgen_angle_limit 368 4 + float skgen_correlation_limit 372 4 + float skgen_symmetry_limit 376 4 + float skgen_retarget_angle_weight 380 4 + float skgen_retarget_length_weight 384 4 + float skgen_retarget_distance_weight 388 4 + short skgen_options 392 2 + char skgen_postpro 394 1 + char skgen_postpro_passes 395 1 + char skgen_subdivisions 396 3 + char skgen_multi_level 399 1 + Object *skgen_template 400 8 + char bone_sketching 408 1 + char bone_sketching_convert 409 1 + char skgen_subdivision_number 410 1 + char skgen_retarget_options 411 1 + char skgen_retarget_roll 412 1 + char skgen_side_string 413 8 + char skgen_num_string 421 8 + char edge_mode 429 1 + char edge_mode_live_unwrap 430 1 + char snap_mode 431 1 + char snap_node_mode 432 1 + char snap_uv_mode 433 1 + short snap_flag 434 2 + short snap_target 436 2 + short proportional 438 2 + short prop_mode 440 2 + char proportional_objects 442 1 + char proportional_mask 443 1 + char auto_normalize 444 1 + char multipaint 445 1 + char weightuser 446 1 + char vgroupsubset 447 1 + int use_uv_sculpt 448 4 + int uv_sculpt_settings 452 4 + int uv_sculpt_tool 456 4 + int uv_relax_method 460 4 + short sculpt_paint_settings 464 2 + short pad5 466 2 + int sculpt_paint_unified_size 468 4 + float sculpt_paint_unified_unprojected_radius 472 4 + float sculpt_paint_unified_alpha 476 4 + UnifiedPaintSettings unified_paint_settings 480 80 + MeshStatVis statvis 560 40 + +bStats 32 + + int totobj 0 4 + int totlamp 4 4 + int totobjsel 8 4 + int totcurve 12 4 + int totmesh 16 4 + int totarmature 20 4 + int totvert 24 4 + int totface 28 4 + +UnitSettings 8 + + float scale_length 0 4 + char system 4 1 + char system_rotation 5 1 + short flag 6 2 + +PhysicsSettings 24 + + float gravity 0 12 + int flag 12 4 + int quick_cache_step 16 4 + int rt 20 4 + +Scene 3584 + + ID id 0 120 + AnimData *adt 120 8 + Object *camera 128 8 + World *world 136 8 + Scene *set 144 8 + ListBase base 152 16 + Base *basact 168 8 + Object *obedit 176 8 + float cursor 184 12 + float twcent 196 12 + float twmin 208 12 + float twmax 220 12 + int lay 232 4 + int layact 236 4 + int lay_updated 240 4 + short flag 244 2 + short use_nodes 246 2 + bNodeTree *nodetree 248 8 + Editing *ed 256 8 + ToolSettings *toolsettings 264 8 + SceneStats *stats 272 8 + RenderData r 280 2608 + AudioData audio 2888 32 + ListBase markers 2920 16 + ListBase transform_spaces 2936 16 + void *sound_scene 2952 8 + void *sound_scene_handle 2960 8 + void *sound_scrub_handle 2968 8 + void *speaker_handles 2976 8 + void *fps_info 2984 8 + DagForest *theDag 2992 8 + short dagflags 3000 2 + short recalc 3002 2 + int active_keyingset 3004 4 + ListBase keyingsets 3008 16 + GameFraming framing 3024 16 + GameData gm 3040 184 + UnitSettings unit 3224 8 + bGPdata *gpd 3232 8 + PhysicsSettings physics_settings 3240 24 + MovieClip *clip 3264 8 + uint64_t customdata_mask 3272 8 + uint64_t customdata_mask_modal 3280 8 + ColorManagedViewSettings view_settings 3288 160 + ColorManagedDisplaySettings display_settings 3448 64 + ColorManagedColorspaceSettings sequencer_colorspace_settings 3512 64 + RigidBodyWorld *rigidbody_world 3576 8 + +BGpic 104 + + BGpic *next 0 8 + BGpic *prev 8 8 + Image *ima 16 8 + ImageUser iuser 24 40 + MovieClip *clip 64 8 + MovieClipUser cuser 72 8 + float xof 80 4 + float yof 84 4 + float size 88 4 + float blend 92 4 + short view 96 2 + short flag 98 2 + short source 100 2 + short pad 102 2 + +RegionView3D 896 + + float winmat 0 64 + float viewmat 64 64 + float viewinv 128 64 + float persmat 192 64 + float persinv 256 64 + float viewmatob 320 64 + float persmatob 384 64 + float clip 448 96 + float clip_local 544 96 + BoundBox *clipbb 640 8 + bGPdata *gpd 648 8 + RegionView3D *localvd 656 8 + RenderInfo *ri 664 8 + RenderEngine *render_engine 672 8 + ViewDepths *depths 680 8 + void *gpuoffscreen 688 8 + SmoothView3DStore *sms 696 8 + wmTimer *smooth_timer 704 8 + float twmat 712 64 + float viewquat 776 16 + float dist 792 4 + float camdx 796 4 + float camdy 800 4 + float pixsize 804 4 + float ofs 808 12 + float camzoom 820 4 + char is_persp 824 1 + char persp 825 1 + char view 826 1 + char viewlock 827 1 + char viewlock_quad 828 1 + char pad 829 3 + float ofs_lock 832 8 + short twdrawflag 840 2 + short rflag 842 2 + float lviewquat 844 16 + short lpersp 860 2 + short lview 862 2 + float gridview 864 4 + float twangle 868 12 + float rot_angle 880 4 + float rot_axis 884 12 + +View3D 376 + + SpaceLink *next 0 8 + SpaceLink *prev 8 8 + ListBase regionbase 16 16 + int spacetype 32 4 + float blockscale 36 4 + short blockhandler 40 16 + float viewquat 56 16 + float dist 72 4 + float bundle_size 76 4 + char bundle_drawtype 80 1 + char pad 81 3 + int lay_prev 84 4 + int lay_used 88 4 + short persp 92 2 + short view 94 2 + Object *camera 96 8 + Object *ob_centre 104 8 + rctf render_border 112 16 + ListBase bgpicbase 128 16 + BGpic *bgpic 144 8 + View3D *localvd 152 8 + char ob_centre_bone 160 64 + int lay 224 4 + int layact 228 4 + short drawtype 232 2 + short ob_centre_cursor 234 2 + short scenelock 236 2 + short around 238 2 + short flag 240 2 + short flag2 242 2 + float lens 244 4 + float grid 248 4 + float near 252 4 + float far 256 4 + float ofs 260 12 + float cursor 272 12 + short matcap_icon 284 2 + short gridlines 286 2 + short gridsubdiv 288 2 + char gridflag 290 1 + char twtype 291 1 + char twmode 292 1 + char twflag 293 1 + char pad2 294 2 + ListBase afterdraw_transp 296 16 + ListBase afterdraw_xray 312 16 + ListBase afterdraw_xraytransp 328 16 + char zbuf 344 1 + char transp 345 1 + char xray 346 1 + char pad3 347 5 + void *properties_storage 352 8 + Material *defmaterial 360 8 + bGPdata *gpd 368 8 + +View2D 160 + + rctf tot 0 16 + rctf cur 16 16 + rcti vert 32 16 + rcti hor 48 16 + rcti mask 64 16 + float min 80 8 + float max 88 8 + float minzoom 96 4 + float maxzoom 100 4 + short scroll 104 2 + short scroll_ui 106 2 + short keeptot 108 2 + short keepzoom 110 2 + short keepofs 112 2 + short flag 114 2 + short align 116 2 + short winx 118 2 + short winy 120 2 + short oldwinx 122 2 + short oldwiny 124 2 + short around 126 2 + float *tab_offset 128 8 + int tab_num 136 4 + int tab_cur 140 4 + SmoothView2DStore *sms 144 8 + wmTimer *smooth_timer 152 8 + +SpaceLink 56 + + SpaceLink *next 0 8 + SpaceLink *prev 8 8 + ListBase regionbase 16 16 + int spacetype 32 4 + float blockscale 36 4 + short blockhandler 40 16 + +SpaceInfo 64 + + SpaceLink *next 0 8 + SpaceLink *prev 8 8 + ListBase regionbase 16 16 + int spacetype 32 4 + float blockscale 36 4 + short blockhandler 40 16 + char rpt_mask 56 1 + char pad 57 7 + +SpaceButs 272 + + SpaceLink *next 0 8 + SpaceLink *prev 8 8 + ListBase regionbase 16 16 + int spacetype 32 4 + float blockscale 36 4 + short blockhandler 40 16 + View2D v2d 56 160 + short mainb 216 2 + short mainbo 218 2 + short mainbuser 220 2 + short re_align 222 2 + short align 224 2 + short preview 226 2 + short texture_context 228 2 + short texture_context_prev 230 2 + char flag 232 1 + char pad 233 7 + void *path 240 8 + int pathflag 248 4 + int dataicon 252 4 + ID *pinid 256 8 + void *texuser 264 8 + +SpaceOops 304 + + SpaceLink *next 0 8 + SpaceLink *prev 8 8 + ListBase regionbase 16 16 + int spacetype 32 4 + float blockscale 36 4 + short blockhandler 40 16 + View2D v2d 56 160 + ListBase tree 216 16 + BLI_mempool *treestore 232 8 + char search_string 240 32 + TreeStoreElem search_tse 272 16 + short flag 288 2 + short outlinevis 290 2 + short storeflag 292 2 + short search_flags 294 2 + void *treehash 296 8 + +SpaceIpo 256 + + SpaceLink *next 0 8 + SpaceLink *prev 8 8 + ListBase regionbase 16 16 + int spacetype 32 4 + float blockscale 36 4 + short blockhandler 40 16 + View2D v2d 56 160 + bDopeSheet *ads 216 8 + ListBase ghostCurves 224 16 + short mode 240 2 + short autosnap 242 2 + int flag 244 4 + float cursorVal 248 4 + int around 252 4 + +SpaceNla 232 + + SpaceLink *next 0 8 + SpaceLink *prev 8 8 + ListBase regionbase 16 16 + int spacetype 32 4 + float blockscale 36 4 + short blockhandler 40 16 + short autosnap 56 2 + short flag 58 2 + int pad 60 4 + bDopeSheet *ads 64 8 + View2D v2d 72 160 + +SpaceTimeCache 24 + + SpaceTimeCache *next 0 8 + SpaceTimeCache *prev 8 8 + float *array 16 8 + +SpaceTime 224 + + SpaceLink *next 0 8 + SpaceLink *prev 8 8 + ListBase regionbase 16 16 + int spacetype 32 4 + float blockscale 36 4 + View2D v2d 40 160 + ListBase caches 200 16 + int cache_display 216 4 + int flag 220 4 + +SpaceSeq 304 + + SpaceLink *next 0 8 + SpaceLink *prev 8 8 + ListBase regionbase 16 16 + int spacetype 32 4 + float blockscale 36 4 + short blockhandler 40 16 + View2D v2d 56 160 + float xof 216 4 + float yof 220 4 + short mainb 224 2 + short render_size 226 2 + short chanshown 228 2 + short zebra 230 2 + int flag 232 4 + float zoom 236 4 + int view 240 4 + int overlay_type 244 4 + bGPdata *gpd 248 8 + SequencerScopes scopes 256 48 + +MaskSpaceInfo 16 + + Mask *mask 0 8 + char draw_flag 8 1 + char draw_type 9 1 + char pad3 10 6 + +FileSelectParams 2016 + + char title 0 96 + char dir 96 1056 + char file 1152 256 + char renamefile 1408 256 + char renameedit 1664 256 + char filter_glob 1920 64 + int active_file 1984 4 + int sel_first 1988 4 + int sel_last 1992 4 + short type 1996 2 + short flag 1998 2 + short sort 2000 2 + short display 2002 2 + short filter 2004 2 + short f_fp 2006 2 + char fp_str 2008 8 + +SpaceFile 104 + + SpaceLink *next 0 8 + SpaceLink *prev 8 8 + ListBase regionbase 16 16 + int spacetype 32 4 + int scroll_offset 36 4 + FileSelectParams *params 40 8 + FileList *files 48 8 + ListBase *folders_prev 56 8 + ListBase *folders_next 64 8 + wmOperator *op 72 8 + wmTimer *smoothscroll_timer 80 8 + FileLayout *layout 88 8 + short recentnr 96 2 + short bookmarknr 98 2 + short systemnr 100 2 + short pad2 102 2 + +SpaceImage 10584 + + SpaceLink *next 0 8 + SpaceLink *prev 8 8 + ListBase regionbase 16 16 + int spacetype 32 4 + int flag 36 4 + Image *image 40 8 + ImageUser iuser 48 40 + CurveMapping *cumap 88 8 + Scopes scopes 96 5264 + Histogram sample_line_hist 5360 5160 + bGPdata *gpd 10520 8 + float cursor 10528 8 + float xof 10536 4 + float yof 10540 4 + float zoom 10544 4 + float centx 10548 4 + float centy 10552 4 + char mode 10556 1 + char pin 10557 1 + short pad 10558 2 + short curtile 10560 2 + short lock 10562 2 + char dt_uv 10564 1 + char sticky 10565 1 + char dt_uvstretch 10566 1 + char around 10567 1 + MaskSpaceInfo mask_info 10568 16 + +SpaceText 672 + + SpaceLink *next 0 8 + SpaceLink *prev 8 8 + ListBase regionbase 16 16 + int spacetype 32 4 + float blockscale 36 4 + short blockhandler 40 16 + Text *text 56 8 + int top 64 4 + int viewlines 68 4 + short flags 72 2 + short menunr 74 2 + short lheight 76 2 + char cwidth 78 1 + char linenrs_tot 79 1 + int left 80 4 + int showlinenrs 84 4 + int tabnumber 88 4 + short showsyntax 92 2 + short line_hlight 94 2 + short overwrite 96 2 + short live_edit 98 2 + float pix_per_line 100 4 + rcti txtscroll 104 16 + rcti txtbar 120 16 + int wordwrap 136 4 + int doplugins 140 4 + char findstr 144 256 + char replacestr 400 256 + short margin_column 656 2 + short lheight_dpi 658 2 + char pad 660 4 + void *drawcache 664 8 + +Script 1448 + + ID id 0 120 + void *py_draw 120 8 + void *py_event 128 8 + void *py_button 136 8 + void *py_browsercallback 144 8 + void *py_globaldict 152 8 + int flags 160 4 + int lastspace 164 4 + char scriptname 168 1024 + char scriptarg 1192 256 + +SpaceScript 64 + + SpaceLink *next 0 8 + SpaceLink *prev 8 8 + ListBase regionbase 16 16 + int spacetype 32 4 + float blockscale 36 4 + Script *script 40 8 + short flags 48 2 + short menunr 50 2 + int pad1 52 4 + void *but_refs 56 8 + +bNodeTreePath 104 + + bNodeTreePath *next 0 8 + bNodeTreePath *prev 8 8 + bNodeTree *nodetree 16 8 + bNodeInstanceKey parent_key 24 4 + int pad 28 4 + float view_center 32 8 + char node_name 40 64 + +SpaceNode 400 + + SpaceLink *next 0 8 + SpaceLink *prev 8 8 + ListBase regionbase 16 16 + int spacetype 32 4 + float blockscale 36 4 + short blockhandler 40 16 + View2D v2d 56 160 + ID *id 216 8 + ID *from 224 8 + short flag 232 2 + short pad1 234 2 + float aspect 236 4 + float pad2 240 4 + float xof 244 4 + float yof 248 4 + float zoom 252 4 + float cursor 256 8 + ListBase treepath 264 16 + bNodeTree *nodetree 280 8 + bNodeTree *edittree 288 8 + char tree_idname 296 64 + int treetype 360 4 + int pad3 364 4 + short texfrom 368 2 + short shaderfrom 370 2 + short recalc 372 2 + short pad4 374 2 + ListBase linkdrag 376 16 + bGPdata *gpd 392 8 + +SpaceLogic 72 + + SpaceLink *next 0 8 + SpaceLink *prev 8 8 + ListBase regionbase 16 16 + int spacetype 32 4 + float blockscale 36 4 + short blockhandler 40 16 + short flag 56 2 + short scaflag 58 2 + int pad 60 4 + bGPdata *gpd 64 8 + +ConsoleLine 40 + + ConsoleLine *next 0 8 + ConsoleLine *prev 8 8 + int len_alloc 16 4 + int len 20 4 + char *line 24 8 + int cursor 32 4 + int type 36 4 + +SpaceConsole 392 + + SpaceLink *next 0 8 + SpaceLink *prev 8 8 + ListBase regionbase 16 16 + int spacetype 32 4 + float blockscale 36 4 + short blockhandler 40 16 + int lheight 56 4 + int pad 60 4 + ListBase scrollback 64 16 + ListBase history 80 16 + char prompt 96 256 + char language 352 32 + int sel_start 384 4 + int sel_end 388 4 + +SpaceUserPref 104 + + SpaceLink *next 0 8 + SpaceLink *prev 8 8 + ListBase regionbase 16 16 + int spacetype 32 4 + char pad 36 3 + char filter_type 39 1 + char filter 40 64 + +SpaceClip 408 + + SpaceLink *next 0 8 + SpaceLink *prev 8 8 + ListBase regionbase 16 16 + int spacetype 32 4 + float xof 36 4 + float yof 40 4 + float xlockof 44 4 + float ylockof 48 4 + float zoom 52 4 + MovieClipUser user 56 8 + MovieClip *clip 64 8 + MovieClipScopes scopes 72 136 + int flag 208 4 + short mode 212 2 + short view 214 2 + int path_length 216 4 + float loc 220 8 + float scale 228 4 + float angle 232 4 + int pad 236 4 + float stabmat 240 64 + float unistabmat 304 64 + int postproc_flag 368 4 + short gpencil_src 372 2 + short pad2 374 2 + int around 376 4 + int pad4 380 4 + float cursor 384 8 + MaskSpaceInfo mask_info 392 16 + +uiFont 1048 + + uiFont *next 0 8 + uiFont *prev 8 8 + char filename 16 1024 + short blf_id 1040 2 + short uifont_id 1042 2 + short r_to_l 1044 2 + short hinting 1046 2 + +uiFontStyle 32 + + short uifont_id 0 2 + short points 2 2 + short kerning 4 2 + char pad 6 6 + short italic 12 2 + short bold 14 2 + short shadow 16 2 + short shadx 18 2 + short shady 20 2 + short align 22 2 + float shadowalpha 24 4 + float shadowcolor 28 4 + +uiStyle 232 + + uiStyle *next 0 8 + uiStyle *prev 8 8 + char name 16 64 + uiFontStyle paneltitle 80 32 + uiFontStyle grouplabel 112 32 + uiFontStyle widgetlabel 144 32 + uiFontStyle widget 176 32 + float panelzoom 208 4 + short minlabelchars 212 2 + short minwidgetchars 214 2 + short columnspace 216 2 + short templatespace 218 2 + short boxspace 220 2 + short buttonspacex 222 2 + short buttonspacey 224 2 + short panelspace 226 2 + short panelouter 228 2 + short pad 230 2 + +uiWidgetColors 32 + + char outline 0 4 + char inner 4 4 + char inner_sel 8 4 + char item 12 4 + char text 16 4 + char text_sel 20 4 + short shaded 24 2 + short shadetop 26 2 + short shadedown 28 2 + short alpha_check 30 2 + +uiWidgetStateColors 32 + + char inner_anim 0 4 + char inner_anim_sel 4 4 + char inner_key 8 4 + char inner_key_sel 12 4 + char inner_driven 16 4 + char inner_driven_sel 20 4 + float blend 24 4 + float pad 28 4 + +uiPanelColors 16 + + char header 0 4 + char back 4 4 + short show_header 8 2 + short show_back 10 2 + int pad 12 4 + +uiGradientColors 16 + + char gradient 0 4 + char high_gradient 4 4 + int show_grad 8 4 + int pad2 12 4 + +ThemeUI 872 + + uiWidgetColors wcol_regular 0 32 + uiWidgetColors wcol_tool 32 32 + uiWidgetColors wcol_text 64 32 + uiWidgetColors wcol_radio 96 32 + uiWidgetColors wcol_option 128 32 + uiWidgetColors wcol_toggle 160 32 + uiWidgetColors wcol_num 192 32 + uiWidgetColors wcol_numslider 224 32 + uiWidgetColors wcol_menu 256 32 + uiWidgetColors wcol_pulldown 288 32 + uiWidgetColors wcol_menu_back 320 32 + uiWidgetColors wcol_menu_item 352 32 + uiWidgetColors wcol_tooltip 384 32 + uiWidgetColors wcol_box 416 32 + uiWidgetColors wcol_scroll 448 32 + uiWidgetColors wcol_progress 480 32 + uiWidgetColors wcol_list_item 512 32 + uiWidgetStateColors wcol_state 544 32 + uiPanelColors panel 576 16 + float menu_shadow_fac 592 4 + short menu_shadow_width 596 2 + short pad 598 2 + char iconfile 600 256 + float icon_alpha 856 4 + char xaxis 860 4 + char yaxis 864 4 + char zaxis 868 4 + +ThemeSpace 584 + + char back 0 4 + char title 4 4 + char text 8 4 + char text_hi 12 4 + char header 16 4 + char header_title 20 4 + char header_text 24 4 + char header_text_hi 28 4 + char button 32 4 + char button_title 36 4 + char button_text 40 4 + char button_text_hi 44 4 + char list 48 4 + char list_title 52 4 + char list_text 56 4 + char list_text_hi 60 4 + uiPanelColors panelcolors 64 16 + uiGradientColors gradients 80 16 + char shade1 96 4 + char shade2 100 4 + char hilite 104 4 + char grid 108 4 + char wire 112 4 + char wire_edit 116 4 + char select 120 4 + char lamp 124 4 + char speaker 128 4 + char empty 132 4 + char camera 136 4 + char pad 140 4 + char active 144 4 + char group 148 4 + char group_active 152 4 + char transform 156 4 + char vertex 160 4 + char vertex_select 164 4 + char vertex_unreferenced 168 4 + char edge 172 4 + char edge_select 176 4 + char edge_seam 180 4 + char edge_sharp 184 4 + char edge_facesel 188 4 + char edge_crease 192 4 + char face 196 4 + char face_select 200 4 + char face_dot 204 4 + char extra_edge_len 208 4 + char extra_edge_angle 212 4 + char extra_face_angle 216 4 + char extra_face_area 220 4 + char normal 224 4 + char vertex_normal 228 4 + char bone_solid 232 4 + char bone_pose 236 4 + char bone_pose_active 240 4 + char strip 244 4 + char strip_select 248 4 + char cframe 252 4 + char freestyle_edge_mark 256 4 + char freestyle_face_mark 260 4 + char nurb_uline 264 4 + char nurb_vline 268 4 + char act_spline 272 4 + char nurb_sel_uline 276 4 + char nurb_sel_vline 280 4 + char lastsel_point 284 4 + char handle_free 288 4 + char handle_auto 292 4 + char handle_vect 296 4 + char handle_align 300 4 + char handle_auto_clamped 304 4 + char handle_sel_free 308 4 + char handle_sel_auto 312 4 + char handle_sel_vect 316 4 + char handle_sel_align 320 4 + char handle_sel_auto_clamped 324 4 + char ds_channel 328 4 + char ds_subchannel 332 4 + char console_output 336 4 + char console_input 340 4 + char console_info 344 4 + char console_error 348 4 + char console_cursor 352 4 + char console_select 356 4 + char pad1 360 4 + char vertex_size 364 1 + char outline_width 365 1 + char facedot_size 366 1 + char noodle_curving 367 1 + char syntaxl 368 4 + char syntaxs 372 4 + char syntaxb 376 4 + char syntaxn 380 4 + char syntaxv 384 4 + char syntaxc 388 4 + char syntaxd 392 4 + char syntaxr 396 4 + char movie 400 4 + char movieclip 404 4 + char mask 408 4 + char image 412 4 + char scene 416 4 + char audio 420 4 + char effect 424 4 + char transition 428 4 + char meta 432 4 + char editmesh_active 436 4 + char handle_vertex 440 4 + char handle_vertex_select 444 4 + char pad2 448 4 + char handle_vertex_size 452 1 + char marker_outline 453 4 + char marker 457 4 + char act_marker 461 4 + char sel_marker 465 4 + char dis_marker 469 4 + char lock_marker 473 4 + char bundle_solid 477 4 + char path_before 481 4 + char path_after 485 4 + char camera_path 489 4 + char hpad 493 3 + char preview_back 496 4 + char preview_stitch_face 500 4 + char preview_stitch_edge 504 4 + char preview_stitch_vert 508 4 + char preview_stitch_stitchable 512 4 + char preview_stitch_unstitchable 516 4 + char preview_stitch_active 520 4 + char uv_shadow 524 4 + char uv_others 528 4 + char match 532 4 + char selected_highlight 536 4 + char skin_root 540 4 + char anim_active 544 4 + char anim_non_active 548 4 + char nla_tweaking 552 4 + char nla_tweakdupli 556 4 + char nla_transition 560 4 + char nla_transition_sel 564 4 + char nla_meta 568 4 + char nla_meta_sel 572 4 + char nla_sound 576 4 + char nla_sound_sel 580 4 + +ThemeWireColor 16 + + char solid 0 4 + char select 4 4 + char active 8 4 + short flag 12 2 + short pad 14 2 + +bTheme 11176 + + bTheme *next 0 8 + bTheme *prev 8 8 + char name 16 32 + ThemeUI tui 48 872 + ThemeSpace tbuts 920 584 + ThemeSpace tv3d 1504 584 + ThemeSpace tfile 2088 584 + ThemeSpace tipo 2672 584 + ThemeSpace tinfo 3256 584 + ThemeSpace tact 3840 584 + ThemeSpace tnla 4424 584 + ThemeSpace tseq 5008 584 + ThemeSpace tima 5592 584 + ThemeSpace text 6176 584 + ThemeSpace toops 6760 584 + ThemeSpace ttime 7344 584 + ThemeSpace tnode 7928 584 + ThemeSpace tlogic 8512 584 + ThemeSpace tuserpref 9096 584 + ThemeSpace tconsole 9680 584 + ThemeSpace tclip 10264 584 + ThemeWireColor tarm 10848 320 + int active_theme_area 11168 4 + int pad 11172 4 + +bAddon 88 + + bAddon *next 0 8 + bAddon *prev 8 8 + char module 16 64 + IDProperty *prop 80 8 + +bPathCompare 792 + + bPathCompare *next 0 8 + bPathCompare *prev 8 8 + char path 16 768 + char flag 784 1 + char pad 785 7 + +SolidLight 56 + + int flag 0 4 + int pad 4 4 + float col 8 16 + float spec 24 16 + float vec 40 16 + +UserDef 9104 + + int versionfile 0 4 + int subversionfile 4 4 + int flag 8 4 + int dupflag 12 4 + int savetime 16 4 + char tempdir 20 768 + char fontdir 788 768 + char renderdir 1556 1024 + char textudir 2580 768 + char pythondir 3348 768 + char sounddir 4116 768 + char i18ndir 4884 768 + char image_editor 5652 1024 + char anim_player 6676 1024 + int anim_player_preset 7700 4 + short v2d_min_gridsize 7704 2 + short timecode_style 7706 2 + short versions 7708 2 + short dbl_click_time 7710 2 + short gameflags 7712 2 + short wheellinescroll 7714 2 + int uiflag 7716 4 + int uiflag2 7720 4 + int language 7724 4 + short userpref 7728 2 + short viewzoom 7730 2 + int mixbufsize 7732 4 + int audiodevice 7736 4 + int audiorate 7740 4 + int audioformat 7744 4 + int audiochannels 7748 4 + int scrollback 7752 4 + int dpi 7756 4 + short encoding 7760 2 + short transopts 7762 2 + short menuthreshold1 7764 2 + short menuthreshold2 7766 2 + ListBase themes 7768 16 + ListBase uifonts 7784 16 + ListBase uistyles 7800 16 + ListBase keymaps 7816 16 + ListBase user_keymaps 7832 16 + ListBase addons 7848 16 + ListBase autoexec_paths 7864 16 + char keyconfigstr 7880 64 + short undosteps 7944 2 + short undomemory 7946 2 + short gp_manhattendist 7948 2 + short gp_euclideandist 7950 2 + short gp_eraser 7952 2 + short gp_settings 7954 2 + short tb_leftmouse 7956 2 + short tb_rightmouse 7958 2 + SolidLight light 7960 168 + short tw_hotspot 8128 2 + short tw_flag 8130 2 + short tw_handlesize 8132 2 + short tw_size 8134 2 + short textimeout 8136 2 + short texcollectrate 8138 2 + short wmdrawmethod 8140 2 + short dragthreshold 8142 2 + int memcachelimit 8144 4 + int prefetchframes 8148 4 + short frameserverport 8152 2 + short pad_rot_angle 8154 2 + short obcenter_dia 8156 2 + short rvisize 8158 2 + short rvibright 8160 2 + short recent_files 8162 2 + short smooth_viewtx 8164 2 + short glreslimit 8166 2 + short curssize 8168 2 + short color_picker_type 8170 2 + short ipo_new 8172 2 + short keyhandles_new 8174 2 + short scrcastfps 8176 2 + short scrcastwait 8178 2 + short widget_unit 8180 2 + short anisotropic_filter 8182 2 + short use_16bit_textures 8184 2 + short use_gpu_mipmap 8186 2 + float ndof_sensitivity 8188 4 + float ndof_orbit_sensitivity 8192 4 + int ndof_flag 8196 4 + short ogl_multisamples 8200 2 + short image_draw_method 8202 2 + float glalphaclip 8204 4 + short autokey_mode 8208 2 + short autokey_flag 8210 2 + short text_render 8212 2 + short pad9 8214 2 + ColorBand coba_weight 8216 776 + float sculpt_paint_overlay_col 8992 12 + short tweak_threshold 9004 2 + short pad3 9006 2 + char author 9008 80 + int compute_device_type 9088 4 + int compute_device_id 9092 4 + float fcu_inactive_alpha 9096 4 + float pixelsize 9100 4 + +bScreen 248 + + ID id 0 120 + ListBase vertbase 120 16 + ListBase edgebase 136 16 + ListBase areabase 152 16 + ListBase regionbase 168 16 + Scene *scene 184 8 + Scene *newscene 192 8 + int redraws_flag 200 4 + int pad1 204 4 + short full 208 2 + short temp 210 2 + short winid 212 2 + short do_draw 214 2 + short do_refresh 216 2 + short do_draw_gesture 218 2 + short do_draw_paintcursor 220 2 + short do_draw_drag 222 2 + short swap 224 2 + short mainwin 226 2 + short subwinactive 228 2 + short pad 230 2 + wmTimer *animtimer 232 8 + void *context 240 8 + +ScrVert 32 + + ScrVert *next 0 8 + ScrVert *prev 8 8 + ScrVert *newv 16 8 + vec2s vec 24 4 + short flag 28 2 + short editflag 30 2 + +ScrEdge 40 + + ScrEdge *next 0 8 + ScrEdge *prev 8 8 + ScrVert *v1 16 8 + ScrVert *v2 24 8 + short border 32 2 + short flag 34 2 + int pad 36 4 + +Panel 272 + + Panel *next 0 8 + Panel *prev 8 8 + PanelType *type 16 8 + uiLayout *layout 24 8 + char panelname 32 64 + char tabname 96 64 + char drawname 160 64 + int ofsx 224 4 + int ofsy 228 4 + int sizex 232 4 + int sizey 236 4 + short labelofs 240 2 + short pad 242 2 + short flag 244 2 + short runtime_flag 246 2 + short control 248 2 + short snap 250 2 + int sortorder 252 4 + Panel *paneltab 256 8 + void *activedata 264 8 + +uiList 200 + + uiList *next 0 8 + uiList *prev 8 8 + uiListType *type 16 8 + char list_id 24 64 + int layout_type 88 4 + int flag 92 4 + int list_scroll 96 4 + int list_grip 100 4 + int list_last_len 104 4 + int padi1 108 4 + char filter_byname 112 64 + int filter_flag 176 4 + int filter_sort_flag 180 4 + IDProperty *properties 184 8 + uiListDyn *dyn_data 192 8 + +ScrArea 160 + + ScrArea *next 0 8 + ScrArea *prev 8 8 + ScrVert *v1 16 8 + ScrVert *v2 24 8 + ScrVert *v3 32 8 + ScrVert *v4 40 8 + bScreen *full 48 8 + rcti totrct 56 16 + char spacetype 72 1 + char butspacetype 73 1 + short winx 74 2 + short winy 76 2 + short headertype 78 2 + short do_refresh 80 2 + short flag 82 2 + short region_active_win 84 2 + short pad 86 2 + SpaceType *type 88 8 + ListBase spacedata 96 16 + ListBase regionbase 112 16 + ListBase handlers 128 16 + ListBase actionzones 144 16 + +ARegion 336 + + ARegion *next 0 8 + ARegion *prev 8 8 + View2D v2d 16 160 + rcti winrct 176 16 + rcti drawrct 192 16 + short winx 208 2 + short winy 210 2 + short swinid 212 2 + short regiontype 214 2 + short alignment 216 2 + short flag 218 2 + float fsize 220 4 + short sizex 224 2 + short sizey 226 2 + short do_draw 228 2 + short do_draw_overlay 230 2 + short swap 232 2 + short overlap 234 2 + short pad 236 4 + ARegionType *type 240 8 + ListBase uiblocks 248 16 + ListBase panels 264 16 + ListBase ui_lists 280 16 + ListBase handlers 296 16 + wmTimer *regiontimer 312 8 + char *headerstr 320 8 + void *regiondata 328 8 + +FileGlobal 1072 + + char subvstr 0 4 + short subversion 4 2 + short pads 6 2 + short minversion 8 2 + short minsubversion 10 2 + short displaymode 12 2 + short winpos 14 2 + bScreen *curscreen 16 8 + Scene *curscene 24 8 + int fileflags 32 4 + int globalf 36 4 + int revision 40 4 + int pad 44 4 + char filename 48 1024 + +StripElem 264 + + char name 0 256 + int orig_width 256 4 + int orig_height 260 4 + +StripCrop 16 + + int top 0 4 + int bottom 4 4 + int left 8 4 + int right 12 4 + +StripTransform 8 + + int xofs 0 4 + int yofs 4 4 + +StripColorBalance 44 + + float lift 0 12 + float gamma 12 12 + float gain 24 12 + int flag 36 4 + int pad 40 4 + +StripProxy 1040 + + char dir 0 768 + char file 768 256 + anim *anim 1024 8 + short tc 1032 2 + short quality 1034 2 + short build_size_flags 1036 2 + short build_tc_flags 1038 2 + +Strip 904 + + Strip *next 0 8 + Strip *prev 8 8 + int us 16 4 + int done 20 4 + int startstill 24 4 + int endstill 28 4 + StripElem *stripdata 32 8 + char dir 40 768 + StripProxy *proxy 808 8 + StripCrop *crop 816 8 + StripTransform *transform 824 8 + StripColorBalance *color_balance 832 8 + ColorManagedColorspaceSettings colorspace_settings 840 64 + +Sequence 352 + + Sequence *next 0 8 + Sequence *prev 8 8 + void *tmp 16 8 + void *lib 24 8 + char name 32 64 + int flag 96 4 + int type 100 4 + int len 104 4 + int start 108 4 + int startofs 112 4 + int endofs 116 4 + int startstill 120 4 + int endstill 124 4 + int machine 128 4 + int depth 132 4 + int startdisp 136 4 + int enddisp 140 4 + float sat 144 4 + float mul 148 4 + float handsize 152 4 + short anim_preseek 156 2 + short streamindex 158 2 + int multicam_source 160 4 + int clip_flag 164 4 + Strip *strip 168 8 + Ipo *ipo 176 8 + Scene *scene 184 8 + Object *scene_camera 192 8 + MovieClip *clip 200 8 + Mask *mask 208 8 + anim *anim 216 8 + float effect_fader 224 4 + float speed_fader 228 4 + Sequence *seq1 232 8 + Sequence *seq2 240 8 + Sequence *seq3 248 8 + ListBase seqbase 256 16 + bSound *sound 272 8 + void *scene_sound 280 8 + float volume 288 4 + float pitch 292 4 + float pan 296 4 + float strobe 300 4 + void *effectdata 304 8 + int anim_startofs 312 4 + int anim_endofs 316 4 + int blend_mode 320 4 + float blend_opacity 324 4 + int sfra 328 4 + char alpha_mode 332 1 + char pad 333 3 + ListBase modifiers 336 16 + +MetaStack 32 + + MetaStack *next 0 8 + MetaStack *prev 8 8 + ListBase *oldbasep 16 8 + Sequence *parseq 24 8 + +Editing 2128 + + ListBase *seqbasep 0 8 + ListBase seqbase 8 16 + ListBase metastack 24 16 + Sequence *act_seq 40 8 + char act_imagedir 48 1024 + char act_sounddir 1072 1024 + int over_ofs 2096 4 + int over_cfra 2100 4 + int over_flag 2104 4 + int pad 2108 4 + rctf over_border 2112 16 + +WipeVars 12 + + float edgeWidth 0 4 + float angle 4 4 + short forward 8 2 + short wipetype 10 2 + +GlowVars 24 + + float fMini 0 4 + float fClamp 4 4 + float fBoost 8 4 + float dDist 12 4 + int dQuality 16 4 + int bNoComp 20 4 + +TransformVars 32 + + float ScalexIni 0 4 + float ScaleyIni 4 4 + float xIni 8 4 + float yIni 12 4 + float rotIni 16 4 + int percent 20 4 + int interpolation 24 4 + int uniform_scale 28 4 + +SolidColorVars 16 + + float col 0 12 + float pad 12 4 + +SpeedControlVars 24 + + float *frameMap 0 8 + float globalSpeed 8 4 + int flags 12 4 + int length 16 4 + int lastValidFrame 20 4 + +SequenceModifierData 112 + + SequenceModifierData *next 0 8 + SequenceModifierData *prev 8 8 + int type 16 4 + int flag 20 4 + char name 24 64 + int mask_input_type 88 4 + int pad 92 4 + Sequence *mask_sequence 96 8 + Mask *mask_id 104 8 + +ColorBalanceModifierData 160 + + SequenceModifierData modifier 0 112 + StripColorBalance color_balance 112 44 + float color_multiply 156 4 + +CurvesModifierData 432 + + SequenceModifierData modifier 0 112 + CurveMapping curve_mapping 112 320 + +HueCorrectModifierData 432 + + SequenceModifierData modifier 0 112 + CurveMapping curve_mapping 112 320 + +BrightContrastModifierData 120 + + SequenceModifierData modifier 0 112 + float bright 112 4 + float contrast 116 4 + +SequencerMaskModifierData 112 + + SequenceModifierData modifier 0 112 + +SequencerScopes 48 + + ImBuf *reference_ibuf 0 8 + ImBuf *zebra_ibuf 8 8 + ImBuf *waveform_ibuf 16 8 + ImBuf *sep_waveform_ibuf 24 8 + ImBuf *vector_ibuf 32 8 + ImBuf *histogram_ibuf 40 8 + +Effect 24 + + Effect *next 0 8 + Effect *prev 8 8 + short type 16 2 + short flag 18 2 + short buttype 20 2 + short rt 22 2 + +BuildEff 32 + + BuildEff *next 0 8 + BuildEff *prev 8 8 + short type 16 2 + short flag 18 2 + short buttype 20 2 + short rt 22 2 + float len 24 4 + float sfra 28 4 + +PartEff 392 + + PartEff *next 0 8 + PartEff *prev 8 8 + short type 16 2 + short flag 18 2 + short buttype 20 2 + short stype 22 2 + short vertgroup 24 2 + short userjit 26 2 + float sta 28 4 + float end 32 4 + float lifetime 36 4 + int totpart 40 4 + int totkey 44 4 + int seed 48 4 + float normfac 52 4 + float obfac 56 4 + float randfac 60 4 + float texfac 64 4 + float randlife 68 4 + float force 72 12 + float damp 84 4 + float nabla 88 4 + float vectsize 92 4 + float maxlen 96 4 + float pad 100 4 + float defvec 104 12 + float mult 116 16 + float life 132 16 + short child 148 8 + short mat 156 8 + short texmap 164 2 + short curmult 166 2 + short staticstep 168 2 + short omat 170 2 + short timetex 172 2 + short speedtex 174 2 + short flag2 176 2 + short flag2neg 178 2 + short disp 180 2 + short vertgroup_v 182 2 + char vgroupname 184 64 + char vgroupname_v 248 64 + float imat 312 64 + Particle *keys 376 8 + Group *group 384 8 + +WaveEff 64 + + WaveEff *next 0 8 + WaveEff *prev 8 8 + short type 16 2 + short flag 18 2 + short buttype 20 2 + short stype 22 2 + float startx 24 4 + float starty 28 4 + float height 32 4 + float width 36 4 + float narrow 40 4 + float speed 44 4 + float minfac 48 4 + float damp 52 4 + float timeoffs 56 4 + float lifetime 60 4 + +TreeStoreElem 16 + + short type 0 2 + short nr 2 2 + short flag 4 2 + short used 6 2 + ID *id 8 8 + +TreeStore 16 + + int totelem 0 4 + int usedelem 4 4 + TreeStoreElem *data 8 8 + +bProperty 96 + + bProperty *next 0 8 + bProperty *prev 8 8 + char name 16 64 + short type 80 2 + short flag 82 2 + int data 84 4 + void *poin 88 8 + +bNearSensor 80 + + char name 0 64 + float dist 64 4 + float resetdist 68 4 + int lastval 72 4 + int pad 76 4 + +bMouseSensor 8 + + short type 0 2 + short flag 2 2 + short pad1 4 2 + short pad2 6 2 + +bTouchSensor 80 + + char name 0 64 + Material *ma 64 8 + float dist 72 4 + float pad 76 4 + +bKeyboardSensor 136 + + short key 0 2 + short qual 2 2 + short type 4 2 + short qual2 6 2 + char targetName 8 64 + char toggleName 72 64 + +bPropertySensor 200 + + int type 0 4 + int pad 4 4 + char name 8 64 + char value 72 64 + char maxvalue 136 64 + +bActuatorSensor 72 + + int type 0 4 + int pad 4 4 + char name 8 64 + +bDelaySensor 8 + + short delay 0 2 + short duration 2 2 + short flag 4 2 + short pad 6 2 + +bCollisionSensor 136 + + char name 0 64 + char materialName 64 64 + short damptimer 128 2 + short damp 130 2 + short mode 132 2 + short pad2 134 2 + +bRadarSensor 76 + + char name 0 64 + float angle 64 4 + float range 68 4 + short flag 72 2 + short axis 74 2 + +bRandomSensor 72 + + char name 0 64 + int seed 64 4 + int delay 68 4 + +bRaySensor 204 + + char name 0 64 + float range 64 4 + char propname 68 64 + char matname 132 64 + short mode 196 2 + short pad1 198 2 + int axisflag 200 4 + +bArmatureSensor 136 + + char posechannel 0 64 + char constraint 64 64 + int type 128 4 + float value 132 4 + +bMessageSensor 136 + + Object *fromObject 0 8 + char subject 8 64 + char body 72 64 + +bSensor 128 + + bSensor *next 0 8 + bSensor *prev 8 8 + short type 16 2 + short otype 18 2 + short flag 20 2 + short pulse 22 2 + short freq 24 2 + short totlinks 26 2 + short pad1 28 2 + short pad2 30 2 + char name 32 64 + void *data 96 8 + bController **links 104 8 + Object *ob 112 8 + short invert 120 2 + short level 122 2 + short tap 124 2 + short pad 126 2 + +bJoystickSensor 92 + + char name 0 64 + char type 64 1 + char joyindex 65 1 + short flag 66 2 + short axis 68 2 + short axis_single 70 2 + int axisf 72 4 + int button 76 4 + int hat 80 4 + int hatf 84 4 + int precision 88 4 + +bExpressionCont 128 + + char str 0 128 + +bPythonCont 80 + + Text *text 0 8 + char module 8 64 + int mode 72 4 + int flag 76 4 + +bController 136 + + bController *next 0 8 + bController *prev 8 8 + bController *mynew 16 8 + short type 24 2 + short flag 26 2 + short inputs 28 2 + short totlinks 30 2 + short otype 32 2 + short totslinks 34 2 + short pad2 36 2 + short pad3 38 2 + char name 40 64 + void *data 104 8 + bActuator **links 112 8 + bSensor **slinks 120 8 + short val 128 2 + short valo 130 2 + int state_mask 132 4 + +bAddObjectActuator 16 + + int time 0 4 + int pad 4 4 + Object *ob 8 8 + +bActionActuator 168 + + bAction *act 0 8 + short type 8 2 + short flag 10 2 + float sta 12 4 + float end 16 4 + char name 20 64 + char frameProp 84 64 + short blendin 148 2 + short priority 150 2 + short layer 152 2 + short end_reset 154 2 + short strideaxis 156 2 + short blend_mode 158 2 + float stridelength 160 4 + float layer_weight 164 4 + +Sound3D 32 + + float min_gain 0 4 + float max_gain 4 4 + float reference_distance 8 4 + float max_distance 12 4 + float rolloff_factor 16 4 + float cone_inner_angle 20 4 + float cone_outer_angle 24 4 + float cone_outer_gain 28 4 + +bSoundActuator 72 + + short flag 0 2 + short sndnr 2 2 + int pad1 4 4 + int pad2 8 4 + short pad3 12 4 + float volume 16 4 + float pitch 20 4 + bSound *sound 24 8 + Sound3D sound3D 32 32 + short type 64 2 + short pad4 66 2 + short pad5 68 2 + short pad6 70 2 + +bEditObjectActuator 120 + + int time 0 4 + short type 4 2 + short flag 6 2 + Object *ob 8 8 + Mesh *me 16 8 + char name 24 64 + float linVelocity 88 12 + float angVelocity 100 12 + float mass 112 4 + short localflag 116 2 + short dyn_operation 118 2 + +bSceneActuator 24 + + short type 0 2 + short pad1 2 2 + int pad 4 4 + Scene *scene 8 8 + Object *camera 16 8 + +bPropertyActuator 144 + + int pad 0 4 + int type 4 4 + char name 8 64 + char value 72 64 + Object *ob 136 8 + +bObjectActuator 112 + + short flag 0 2 + short type 2 2 + short otype 4 2 + short damping 6 2 + float forceloc 8 12 + float forcerot 20 12 + float pad 32 12 + float pad1 44 12 + float dloc 56 12 + float drot 68 12 + float linearvelocity 80 12 + float angularvelocity 92 12 + Object *reference 104 8 + +bIpoActuator 148 + + short flag 0 2 + short type 2 2 + float sta 4 4 + float end 8 4 + char name 12 64 + char frameProp 76 64 + short pad1 140 2 + short pad2 142 2 + short pad3 144 2 + short pad4 146 2 + +bCameraActuator 32 + + Object *ob 0 8 + float height 8 4 + float min 12 4 + float max 16 4 + float damping 20 4 + short pad1 24 2 + short axis 26 2 + float pad2 28 4 + +bConstraintActuator 128 + + short type 0 2 + short mode 2 2 + short flag 4 2 + short damp 6 2 + short time 8 2 + short rotdamp 10 2 + int pad 12 4 + float minloc 16 12 + float maxloc 28 12 + float minrot 40 12 + float maxrot 52 12 + char matprop 64 64 + +bGroupActuator 88 + + short flag 0 2 + short type 2 2 + int sta 4 4 + int end 8 4 + char name 12 64 + short pad 76 6 + short cur 82 2 + short butsta 84 2 + short butend 86 2 + +bRandomActuator 88 + + int seed 0 4 + int distribution 4 4 + int int_arg_1 8 4 + int int_arg_2 12 4 + float float_arg_1 16 4 + float float_arg_2 20 4 + char propname 24 64 + +bMessageActuator 208 + + char toPropName 0 64 + Object *toObject 64 8 + char subject 72 64 + short bodyType 136 2 + short pad1 138 2 + int pad2 140 4 + char body 144 64 + +bGameActuator 140 + + short flag 0 2 + short type 2 2 + int sta 4 4 + int end 8 4 + char filename 12 64 + char loadaniname 76 64 + +bVisibilityActuator 4 + + int flag 0 4 + +bTwoDFilterActuator 24 + + char pad 0 4 + short type 4 2 + short flag 6 2 + int int_arg 8 4 + float float_arg 12 4 + Text *text 16 8 + +bParentActuator 16 + + char pad 0 2 + short flag 2 2 + int type 4 4 + Object *ob 8 8 + +bStateActuator 8 + + int type 0 4 + int mask 4 4 + +bArmatureActuator 160 + + char posechannel 0 64 + char constraint 64 64 + int type 128 4 + float weight 132 4 + float influence 136 4 + float pad 140 4 + Object *target 144 8 + Object *subtarget 152 8 + +bSteeringActuator 48 + + char pad 0 5 + char flag 5 1 + short facingaxis 6 2 + int type 8 4 + float dist 12 4 + float velocity 16 4 + float acceleration 20 4 + float turnspeed 24 4 + int updateTime 28 4 + Object *target 32 8 + Object *navmesh 40 8 + +bActuator 112 + + bActuator *next 0 8 + bActuator *prev 8 8 + bActuator *mynew 16 8 + short type 24 2 + short flag 26 2 + short otype 28 2 + short go 30 2 + char name 32 64 + void *data 96 8 + Object *ob 104 8 + +bSound 1232 + + ID id 0 120 + char name 120 1024 + PackedFile *packedfile 1144 8 + void *handle 1152 8 + PackedFile *newpackedfile 1160 8 + Ipo *ipo 1168 8 + float volume 1176 4 + float attenuation 1180 4 + float pitch 1184 4 + float min_gain 1188 4 + float max_gain 1192 4 + float distance 1196 4 + int flags 1200 4 + int pad 1204 4 + void *cache 1208 8 + void *waveform 1216 8 + void *playback_handle 1224 8 + +GroupObject 40 + + GroupObject *next 0 8 + GroupObject *prev 8 8 + Object *ob 16 8 + void *lampren 24 8 + short recalc 32 2 + char pad 34 6 + +Group 152 + + ID id 0 120 + ListBase gobject 120 16 + int layer 136 4 + float dupli_ofs 140 12 + +Bone 328 + + Bone *next 0 8 + Bone *prev 8 8 + IDProperty *prop 16 8 + Bone *parent 24 8 + ListBase childbase 32 16 + char name 48 64 + float roll 112 4 + float head 116 12 + float tail 128 12 + float bone_mat 140 36 + int flag 176 4 + float arm_head 180 12 + float arm_tail 192 12 + float arm_mat 204 64 + float arm_roll 268 4 + float dist 272 4 + float weight 276 4 + float xwidth 280 4 + float length 284 4 + float zwidth 288 4 + float ease1 292 4 + float ease2 296 4 + float rad_head 300 4 + float rad_tail 304 4 + float size 308 12 + int layer 320 4 + short segments 324 2 + short pad 326 2 + +bArmature 256 + + ID id 0 120 + AnimData *adt 120 8 + ListBase bonebase 128 16 + ListBase chainbase 144 16 + ListBase *edbo 160 8 + Bone *act_bone 168 8 + EditBone *act_edbone 176 8 + void *sketch 184 8 + int flag 192 4 + int drawtype 196 4 + int gevertdeformer 200 4 + int pad 204 4 + short deformflag 208 2 + short pathflag 210 2 + int layer_used 212 4 + int layer 216 4 + int layer_protected 220 4 + short ghostep 224 2 + short ghostsize 226 2 + short ghosttype 228 2 + short pathsize 230 2 + int ghostsf 232 4 + int ghostef 236 4 + int pathsf 240 4 + int pathef 244 4 + int pathbc 248 4 + int pathac 252 4 + +bMotionPathVert 16 + + float co 0 12 + int flag 12 4 + +bMotionPath 24 + + bMotionPathVert *points 0 8 + int length 8 4 + int start_frame 12 4 + int end_frame 16 4 + int flag 20 4 + +bAnimVizSettings 48 + + int ghost_sf 0 4 + int ghost_ef 4 4 + int ghost_bc 8 4 + int ghost_ac 12 4 + short ghost_type 16 2 + short ghost_step 18 2 + short ghost_flag 20 2 + short recalc 22 2 + short path_type 24 2 + short path_step 26 2 + short path_viewflag 28 2 + short path_bakeflag 30 2 + int path_sf 32 4 + int path_ef 36 4 + int path_bc 40 4 + int path_ac 44 4 + +bPoseChannel 544 + + bPoseChannel *next 0 8 + bPoseChannel *prev 8 8 + IDProperty *prop 16 8 + ListBase constraints 24 16 + char name 40 64 + short flag 104 2 + short ikflag 106 2 + short protectflag 108 2 + short agrp_index 110 2 + char constflag 112 1 + char selectflag 113 1 + char pad0 114 6 + Bone *bone 120 8 + bPoseChannel *parent 128 8 + bPoseChannel *child 136 8 + ListBase iktree 144 16 + ListBase siktree 160 16 + bMotionPath *mpath 176 8 + Object *custom 184 8 + bPoseChannel *custom_tx 192 8 + float loc 200 12 + float size 212 12 + float eul 224 12 + float quat 236 16 + float rotAxis 252 12 + float rotAngle 264 4 + short rotmode 268 2 + short pad 270 2 + float chan_mat 272 64 + float pose_mat 336 64 + float constinv 400 64 + float pose_head 464 12 + float pose_tail 476 12 + float limitmin 488 12 + float limitmax 500 12 + float stiffness 512 12 + float ikstretch 524 4 + float ikrotweight 528 4 + float iklinweight 532 4 + void *temp 536 8 + +bPose 216 + + ListBase chanbase 0 16 + GHash *chanhash 16 8 + short flag 24 2 + short pad 26 2 + int proxy_layer 28 4 + int pad1 32 4 + float ctime 36 4 + float stride_offset 40 12 + float cyclic_offset 52 12 + ListBase agroups 64 16 + int active_group 80 4 + int iksolver 84 4 + void *ikdata 88 8 + void *ikparam 96 8 + bAnimVizSettings avs 104 48 + char proxy_act_bone 152 64 + +bIKParam 4 + + int iksolver 0 4 + +bItasc 40 + + int iksolver 0 4 + float precision 4 4 + short numiter 8 2 + short numstep 10 2 + float minstep 12 4 + float maxstep 16 4 + short solver 20 2 + short flag 22 2 + float feedback 24 4 + float maxvel 28 4 + float dampmax 32 4 + float dampeps 36 4 + +bActionGroup 120 + + bActionGroup *next 0 8 + bActionGroup *prev 8 8 + ListBase channels 16 16 + int flag 32 4 + int customCol 36 4 + char name 40 64 + ThemeWireColor cs 104 16 + +bAction 200 + + ID id 0 120 + ListBase curves 120 16 + ListBase chanbase 136 16 + ListBase groups 152 16 + ListBase markers 168 16 + int flag 184 4 + int active_marker 188 4 + int idroot 192 4 + int pad 196 4 + +bDopeSheet 112 + + ID *source 0 8 + ListBase chanbase 8 16 + Group *filter_grp 24 8 + char searchstr 32 64 + int filterflag 96 4 + int flag 100 4 + int renameIndex 104 4 + int pad 108 4 + +SpaceAction 344 + + SpaceLink *next 0 8 + SpaceLink *prev 8 8 + ListBase regionbase 16 16 + int spacetype 32 4 + float blockscale 36 4 + short blockhandler 40 16 + View2D v2d 56 160 + bAction *action 216 8 + bDopeSheet ads 224 112 + char mode 336 1 + char autosnap 337 1 + short flag 338 2 + float timeslide 340 4 + +bActionChannel 120 + + bActionChannel *next 0 8 + bActionChannel *prev 8 8 + bActionGroup *grp 16 8 + Ipo *ipo 24 8 + ListBase constraintChannels 32 16 + int flag 48 4 + char name 52 64 + int temp 116 4 + +bConstraintChannel 56 + + bConstraintChannel *next 0 8 + bConstraintChannel *prev 8 8 + Ipo *ipo 16 8 + short flag 24 2 + char name 26 30 + +bConstraint 120 + + bConstraint *next 0 8 + bConstraint *prev 8 8 + void *data 16 8 + short type 24 2 + short flag 26 2 + char ownspace 28 1 + char tarspace 29 1 + char name 30 64 + short pad 94 2 + float enforce 96 4 + float headtail 100 4 + Ipo *ipo 104 8 + float lin_error 112 4 + float rot_error 116 4 + +bConstraintTarget 160 + + bConstraintTarget *next 0 8 + bConstraintTarget *prev 8 8 + Object *tar 16 8 + char subtarget 24 64 + float matrix 88 64 + short space 152 2 + short flag 154 2 + short type 156 2 + short rotOrder 158 2 + +bPythonConstraint 112 + + Text *text 0 8 + IDProperty *prop 8 8 + int flag 16 4 + int tarnum 20 4 + ListBase targets 24 16 + Object *tar 40 8 + char subtarget 48 64 + +bKinematicConstraint 184 + + Object *tar 0 8 + short iterations 8 2 + short flag 10 2 + short rootbone 12 2 + short max_rootbone 14 2 + char subtarget 16 64 + Object *poletar 80 8 + char polesubtarget 88 64 + float poleangle 152 4 + float weight 156 4 + float orientweight 160 4 + float grabtarget 164 12 + short type 176 2 + short mode 178 2 + float dist 180 4 + +bSplineIKConstraint 24 + + Object *tar 0 8 + float *points 8 8 + short numpoints 16 2 + short chainlen 18 2 + short flag 20 2 + short xzScaleMode 22 2 + +bTrackToConstraint 88 + + Object *tar 0 8 + int reserved1 8 4 + int reserved2 12 4 + int flags 16 4 + int pad 20 4 + char subtarget 24 64 + +bRotateLikeConstraint 80 + + Object *tar 0 8 + int flag 8 4 + int reserved1 12 4 + char subtarget 16 64 + +bLocateLikeConstraint 80 + + Object *tar 0 8 + int flag 8 4 + int reserved1 12 4 + char subtarget 16 64 + +bSizeLikeConstraint 80 + + Object *tar 0 8 + int flag 8 4 + int reserved1 12 4 + char subtarget 16 64 + +bSameVolumeConstraint 8 + + int flag 0 4 + float volume 4 4 + +bTransLikeConstraint 72 + + Object *tar 0 8 + char subtarget 8 64 + +bMinMaxConstraint 104 + + Object *tar 0 8 + int minmaxflag 8 4 + float offset 12 4 + int flag 16 4 + short sticky 20 2 + short stuck 22 2 + short pad1 24 2 + short pad2 26 2 + float cache 28 12 + char subtarget 40 64 + +bActionConstraint 104 + + Object *tar 0 8 + short type 8 2 + short local 10 2 + int start 12 4 + int end 16 4 + float min 20 4 + float max 24 4 + int flag 28 4 + bAction *act 32 8 + char subtarget 40 64 + +bLockTrackConstraint 80 + + Object *tar 0 8 + int trackflag 8 4 + int lockflag 12 4 + char subtarget 16 64 + +bDampTrackConstraint 80 + + Object *tar 0 8 + int trackflag 8 4 + int pad 12 4 + char subtarget 16 64 + +bFollowPathConstraint 24 + + Object *tar 0 8 + float offset 8 4 + float offset_fac 12 4 + int followflag 16 4 + short trackflag 20 2 + short upflag 22 2 + +bStretchToConstraint 88 + + Object *tar 0 8 + int volmode 8 4 + int plane 12 4 + float orglength 16 4 + float bulge 20 4 + char subtarget 24 64 + +bRigidBodyJointConstraint 104 + + Object *tar 0 8 + Object *child 8 8 + int type 16 4 + float pivX 20 4 + float pivY 24 4 + float pivZ 28 4 + float axX 32 4 + float axY 36 4 + float axZ 40 4 + float minLimit 44 24 + float maxLimit 68 24 + float extraFz 92 4 + short flag 96 2 + short pad 98 2 + short pad1 100 2 + short pad2 102 2 + +bClampToConstraint 16 + + Object *tar 0 8 + int flag 8 4 + int flag2 12 4 + +bChildOfConstraint 144 + + Object *tar 0 8 + int flag 8 4 + int pad 12 4 + float invmat 16 64 + char subtarget 80 64 + +bTransformConstraint 128 + + Object *tar 0 8 + char subtarget 8 64 + short from 72 2 + short to 74 2 + char map 76 3 + char expo 79 1 + float from_min 80 12 + float from_max 92 12 + float to_min 104 12 + float to_max 116 12 + +bPivotConstraint 88 + + Object *tar 0 8 + char subtarget 8 64 + float offset 72 12 + short rotAxis 84 2 + short flag 86 2 + +bLocLimitConstraint 28 + + float xmin 0 4 + float xmax 4 4 + float ymin 8 4 + float ymax 12 4 + float zmin 16 4 + float zmax 20 4 + short flag 24 2 + short flag2 26 2 + +bRotLimitConstraint 28 + + float xmin 0 4 + float xmax 4 4 + float ymin 8 4 + float ymax 12 4 + float zmin 16 4 + float zmax 20 4 + short flag 24 2 + short flag2 26 2 + +bSizeLimitConstraint 28 + + float xmin 0 4 + float xmax 4 4 + float ymin 8 4 + float ymax 12 4 + float zmin 16 4 + float zmax 20 4 + short flag 24 2 + short flag2 26 2 + +bDistLimitConstraint 88 + + Object *tar 0 8 + char subtarget 8 64 + float dist 72 4 + float soft 76 4 + short flag 80 2 + short mode 82 2 + int pad 84 4 + +bShrinkwrapConstraint 24 + + Object *target 0 8 + float dist 8 4 + short shrinkType 12 2 + char projAxis 14 1 + char projAxisSpace 15 1 + float projLimit 16 4 + char pad 20 4 + +bFollowTrackConstraint 160 + + MovieClip *clip 0 8 + char track 8 64 + int flag 72 4 + int frame_method 76 4 + char object 80 64 + Object *camera 144 8 + Object *depth_ob 152 8 + +bCameraSolverConstraint 16 + + MovieClip *clip 0 8 + int flag 8 4 + int pad 12 4 + +bObjectSolverConstraint 152 + + MovieClip *clip 0 8 + int flag 8 4 + int pad 12 4 + char object 16 64 + float invmat 80 64 + Object *camera 144 8 + +bActionModifier 72 + + bActionModifier *next 0 8 + bActionModifier *prev 8 8 + short type 16 2 + short flag 18 2 + char channel 20 32 + float noisesize 52 4 + float turbul 56 4 + short channels 60 2 + short no_rot_axis 62 2 + Object *ob 64 8 + +bActionStrip 168 + + bActionStrip *next 0 8 + bActionStrip *prev 8 8 + short flag 16 2 + short mode 18 2 + short stride_axis 20 2 + short curmod 22 2 + Ipo *ipo 24 8 + bAction *act 32 8 + Object *object 40 8 + float start 48 4 + float end 52 4 + float actstart 56 4 + float actend 60 4 + float actoffs 64 4 + float stridelen 68 4 + float repeat 72 4 + float scale 76 4 + float blendin 80 4 + float blendout 84 4 + char stridechannel 88 32 + char offs_bone 120 32 + ListBase modifiers 152 16 + +bNodeStack 48 + + float vec 0 16 + float min 16 4 + float max 20 4 + void *data 24 8 + short hasinput 32 2 + short hasoutput 34 2 + short datatype 36 2 + short sockettype 38 2 + short is_copy 40 2 + short external 42 2 + short pad 44 4 + +bNodeSocket 352 + + bNodeSocket *next 0 8 + bNodeSocket *prev 8 8 + bNodeSocket *new_sock 16 8 + IDProperty *prop 24 8 + char identifier 32 64 + char name 96 64 + void *storage 160 8 + short type 168 2 + short flag 170 2 + short limit 172 2 + short in_out 174 2 + bNodeSocketType *typeinfo 176 8 + char idname 184 64 + float locx 248 4 + float locy 252 4 + void *default_value 256 8 + short stack_index 264 2 + short stack_type 266 2 + int resizemode 268 4 + void *cache 272 8 + int own_index 280 4 + int to_index 284 4 + bNodeSocket *groupsock 288 8 + bNodeLink *link 296 8 + bNodeStack ns 304 48 + +bNode 464 + + bNode *next 0 8 + bNode *prev 8 8 + bNode *new_node 16 8 + IDProperty *prop 24 8 + bNodeType *typeinfo 32 8 + char idname 40 64 + char name 104 64 + int flag 168 4 + short type 172 2 + short pad 174 2 + short done 176 2 + short level 178 2 + short lasty 180 2 + short menunr 182 2 + short stack_index 184 2 + short nr 186 2 + float color 188 12 + ListBase inputs 200 16 + ListBase outputs 216 16 + bNode *parent 232 8 + ID *id 240 8 + void *storage 248 8 + bNode *original 256 8 + ListBase internal_links 264 16 + float locx 280 4 + float locy 284 4 + float width 288 4 + float height 292 4 + float miniwidth 296 4 + float offsetx 300 4 + float offsety 304 4 + int update 308 4 + char label 312 64 + short custom1 376 2 + short custom2 378 2 + float custom3 380 4 + float custom4 384 4 + short need_exec 388 2 + short exec 390 2 + void *threaddata 392 8 + rctf totr 400 16 + rctf butr 416 16 + rctf prvr 432 16 + short preview_xsize 448 2 + short preview_ysize 450 2 + int pad2 452 4 + uiBlock *block 456 8 + +bNodeInstanceKey 4 + + int value 0 4 + +bNodeInstanceHashEntry 8 + + bNodeInstanceKey key 0 4 + short tag 4 2 + short pad 6 2 + +bNodePreview 24 + + bNodeInstanceHashEntry hash_entry 0 8 + char *rect 8 8 + short xsize 16 2 + short ysize 18 2 + int pad 20 4 + +bNodeLink 56 + + bNodeLink *next 0 8 + bNodeLink *prev 8 8 + bNode *fromnode 16 8 + bNode *tonode 24 8 + bNodeSocket *fromsock 32 8 + bNodeSocket *tosock 40 8 + int flag 48 4 + int pad 52 4 + +bNodeTree 404 + + ID id 0 120 + AnimData *adt 120 8 + bNodeTreeType *typeinfo 128 8 + char idname 136 64 + StructRNA *interface_type 200 8 + bGPdata *gpd 208 8 + float view_center 216 8 + ListBase nodes 224 16 + ListBase links 240 16 + int type 256 4 + int init 260 4 + int cur_index 264 4 + int flag 268 4 + int update 272 4 + short is_updating 276 2 + short done 278 2 + int pad2 280 4 + int nodetype 284 4 + short edit_quality 288 2 + short render_quality 290 2 + int chunksize 292 4 + rctf viewer_border 296 16 + ListBase inputs 312 16 + ListBase outputs 328 16 + bNodeInstanceHash *previews 344 8 + bNodeInstanceKey active_viewer_key 352 4 + int pad 356 4 + bNodeTreeExec *execdata 360 8 + void (*progress)() 368 0 + void (*stats_draw)() 368 0 + int (*test_break)() 368 4 + void (*update_draw)() 372 0 + void *tbh 372 8 + void *prh 380 8 + void *sdh 388 8 + void *udh 396 8 + +bNodeSocketValueInt 16 + + int subtype 0 4 + int value 4 4 + int min 8 4 + int max 12 4 + +bNodeSocketValueFloat 16 + + int subtype 0 4 + float value 4 4 + float min 8 4 + float max 12 4 + +bNodeSocketValueBoolean 4 + + char value 0 1 + char pad 1 3 + +bNodeSocketValueVector 24 + + int subtype 0 4 + float value 4 12 + float min 16 4 + float max 20 4 + +bNodeSocketValueRGBA 16 + + float value 0 16 + +bNodeSocketValueString 1032 + + int subtype 0 4 + int pad 4 4 + char value 8 1024 + +NodeFrame 4 + + short flag 0 2 + short label_size 2 2 + +NodeImageAnim 16 + + int frames 0 4 + int sfra 4 4 + int nr 8 4 + char cyclic 12 1 + char movie 13 1 + short pad 14 2 + +ColorCorrectionData 24 + + float saturation 0 4 + float contrast 4 4 + float gamma 8 4 + float gain 12 4 + float lift 16 4 + int pad 20 4 + +NodeColorCorrection 104 + + ColorCorrectionData master 0 24 + ColorCorrectionData shadows 24 24 + ColorCorrectionData midtones 48 24 + ColorCorrectionData highlights 72 24 + float startmidtones 96 4 + float endmidtones 100 4 + +NodeBokehImage 20 + + float angle 0 4 + int flaps 4 4 + float rounding 8 4 + float catadioptric 12 4 + float lensshift 16 4 + +NodeBoxMask 24 + + float x 0 4 + float y 4 4 + float rotation 8 4 + float height 12 4 + float width 16 4 + int pad 20 4 + +NodeEllipseMask 24 + + float x 0 4 + float y 4 4 + float rotation 8 4 + float height 12 4 + float width 16 4 + int pad 20 4 + +NodeImageLayer 8 + + int pass_index 0 4 + int pass_flag 4 4 + +NodeBlurData 40 + + short sizex 0 2 + short sizey 2 2 + short samples 4 2 + short maxspeed 6 2 + short minspeed 8 2 + short relative 10 2 + short aspect 12 2 + short curved 14 2 + float fac 16 4 + float percentx 20 4 + float percenty 24 4 + short filtertype 28 2 + char bokeh 30 1 + char gamma 31 1 + int image_in_width 32 4 + int image_in_height 36 4 + +NodeDBlurData 28 + + float center_x 0 4 + float center_y 4 4 + float distance 8 4 + float angle 12 4 + float spin 16 4 + float zoom 20 4 + short iter 24 2 + char wrap 26 1 + char pad 27 1 + +NodeBilateralBlurData 12 + + float sigma_color 0 4 + float sigma_space 4 4 + short iter 8 2 + short pad 10 2 + +NodeHueSat 12 + + float hue 0 4 + float sat 4 4 + float val 8 4 + +NodeImageFile 1280 + + char name 0 1024 + ImageFormatData im_format 1024 248 + int sfra 1272 4 + int efra 1276 4 + +NodeImageMultiFile 1288 + + char base_path 0 1024 + ImageFormatData format 1024 248 + int sfra 1272 4 + int efra 1276 4 + int active_input 1280 4 + int pad 1284 4 + +NodeImageMultiFileSocket 1312 + + short use_render_format 0 2 + short use_node_format 2 2 + int pad1 4 4 + char path 8 1024 + ImageFormatData format 1032 248 + char layer 1280 30 + char pad2 1310 2 + +NodeChroma 44 + + float t1 0 4 + float t2 4 4 + float t3 8 4 + float fsize 12 4 + float fstrength 16 4 + float falpha 20 4 + float key 24 16 + short algorithm 40 2 + short channel 42 2 + +NodeTwoXYs 24 + + short x1 0 2 + short x2 2 2 + short y1 4 2 + short y2 6 2 + float fac_x1 8 4 + float fac_x2 12 4 + float fac_y1 16 4 + float fac_y2 20 4 + +NodeTwoFloats 8 + + float x 0 4 + float y 4 4 + +NodeGeometry 128 + + char uvname 0 64 + char colname 64 64 + +NodeVertexCol 64 + + char name 0 64 + +NodeDefocus 32 + + char bktype 0 1 + char pad_c1 1 1 + char preview 2 1 + char gamco 3 1 + short samples 4 2 + short no_zbuf 6 2 + float fstop 8 4 + float maxblur 12 4 + float bthresh 16 4 + float scale 20 4 + float rotation 24 4 + float pad_f1 28 4 + +NodeScriptDict 16 + + void *dict 0 8 + void *node 8 8 + +NodeGlare 32 + + char quality 0 1 + char type 1 1 + char iter 2 1 + char angle 3 1 + char pad_c1 4 1 + char size 5 1 + char pad 6 2 + float colmod 8 4 + float mix 12 4 + float threshold 16 4 + float fade 20 4 + float angle_ofs 24 4 + float pad_f1 28 4 + +NodeTonemap 32 + + float key 0 4 + float offset 4 4 + float gamma 8 4 + float f 12 4 + float m 16 4 + float a 20 4 + float c 24 4 + int type 28 4 + +NodeLensDist 8 + + short jit 0 2 + short proj 2 2 + short fit 4 2 + short pad 6 2 + +NodeColorBalance 96 + + float slope 0 12 + float offset 12 12 + float power 24 12 + float lift 36 12 + float gamma 48 12 + float gain 60 12 + float lift_lgg 72 12 + float gamma_inv 84 12 + +NodeColorspill 20 + + short limchan 0 2 + short unspill 2 2 + float limscale 4 4 + float uspillr 8 4 + float uspillg 12 4 + float uspillb 16 4 + +NodeDilateErode 8 + + char falloff 0 1 + char pad 1 7 + +NodeMask 8 + + int size_x 0 4 + int size_y 4 4 + +NodeTexBase 968 + + TexMapping tex_mapping 0 144 + ColorMapping color_mapping 144 824 + +NodeTexSky 992 + + NodeTexBase base 0 968 + int sky_model 968 4 + float sun_direction 972 12 + float turbidity 984 4 + float ground_albedo 988 4 + +NodeTexImage 1024 + + NodeTexBase base 0 968 + ImageUser iuser 968 40 + int color_space 1008 4 + int projection 1012 4 + float projection_blend 1016 4 + int pad 1020 4 + +NodeTexChecker 968 + + NodeTexBase base 0 968 + +NodeTexBrick 984 + + NodeTexBase base 0 968 + int offset_freq 968 4 + int squash_freq 972 4 + float offset 976 4 + float squash 980 4 + +NodeTexEnvironment 1016 + + NodeTexBase base 0 968 + ImageUser iuser 968 40 + int color_space 1008 4 + int projection 1012 4 + +NodeTexGradient 976 + + NodeTexBase base 0 968 + int gradient_type 968 4 + int pad 972 4 + +NodeTexNoise 968 + + NodeTexBase base 0 968 + +NodeTexVoronoi 976 + + NodeTexBase base 0 968 + int coloring 968 4 + int pad 972 4 + +NodeTexMusgrave 976 + + NodeTexBase base 0 968 + int musgrave_type 968 4 + int pad 972 4 + +NodeTexWave 976 + + NodeTexBase base 0 968 + int wave_type 968 4 + int pad 972 4 + +NodeTexMagic 976 + + NodeTexBase base 0 968 + int depth 968 4 + int pad 972 4 + +NodeShaderAttribute 64 + + char name 0 64 + +NodeShaderVectTransform 16 + + int type 0 4 + int convert_from 4 4 + int convert_to 8 4 + int pad 12 4 + +TexNodeOutput 64 + + char name 0 64 + +NodeKeyingScreenData 64 + + char tracking_object 0 64 + +NodeKeyingData 48 + + float screen_balance 0 4 + float despill_factor 4 4 + float despill_balance 8 4 + int edge_kernel_radius 12 4 + float edge_kernel_tolerance 16 4 + float clip_black 20 4 + float clip_white 24 4 + int dilate_distance 28 4 + int feather_distance 32 4 + int feather_falloff 36 4 + int blur_pre 40 4 + int blur_post 44 4 + +NodeTrackPosData 128 + + char tracking_object 0 64 + char track_name 64 64 + +NodeTranslateData 8 + + char wrap_axis 0 1 + char relative 1 1 + char pad 2 6 + +NodePlaneTrackDeformData 128 + + char tracking_object 0 64 + char plane_track_name 64 64 + +NodeShaderScript 1104 + + int mode 0 4 + int flag 4 4 + char filepath 8 1024 + char bytecode_hash 1032 64 + char *bytecode 1096 8 + +NodeShaderTangent 72 + + int direction_type 0 4 + int axis 4 4 + char uv_map 8 64 + +NodeShaderNormalMap 68 + + int space 0 4 + char uv_map 4 64 + +CurveMapPoint 12 + + float x 0 4 + float y 4 4 + short flag 8 2 + short shorty 10 2 + +CurveMap 56 + + short totpoint 0 2 + short flag 2 2 + float range 4 4 + float mintable 8 4 + float maxtable 12 4 + float ext_in 16 8 + float ext_out 24 8 + CurveMapPoint *curve 32 8 + CurveMapPoint *table 40 8 + CurveMapPoint *premultable 48 8 + +CurveMapping 320 + + int flag 0 4 + int cur 4 4 + int preset 8 4 + int changed_timestamp 12 4 + rctf curr 16 16 + rctf clipr 32 16 + CurveMap cm 48 224 + float black 272 12 + float white 284 12 + float bwmul 296 12 + float sample 308 12 + +Histogram 5160 + + int channels 0 4 + int x_resolution 4 4 + float data_luma 8 1024 + float data_r 1032 1024 + float data_g 2056 1024 + float data_b 3080 1024 + float data_a 4104 1024 + float xmax 5128 4 + float ymax 5132 4 + short mode 5136 2 + short flag 5138 2 + int height 5140 4 + float co 5144 16 + +Scopes 5264 + + int ok 0 4 + int sample_full 4 4 + int sample_lines 8 4 + float accuracy 12 4 + int wavefrm_mode 16 4 + float wavefrm_alpha 20 4 + float wavefrm_yfac 24 4 + int wavefrm_height 28 4 + float vecscope_alpha 32 4 + int vecscope_height 36 4 + float minmax 40 24 + Histogram hist 64 5160 + float *waveform_1 5224 8 + float *waveform_2 5232 8 + float *waveform_3 5240 8 + float *vecscope 5248 8 + int waveform_tot 5256 4 + int pad 5260 4 + +ColorManagedViewSettings 160 + + int flag 0 4 + int pad 4 4 + char look 8 64 + char view_transform 72 64 + float exposure 136 4 + float gamma 140 4 + CurveMapping *curve_mapping 144 8 + void *pad2 152 8 + +ColorManagedDisplaySettings 64 + + char display_device 0 64 + +ColorManagedColorspaceSettings 64 + + char name 0 64 + +BrushClone 24 + + Image *image 0 8 + float offset 8 8 + float alpha 16 4 + float pad 20 4 + +Brush 1992 + + ID id 0 120 + BrushClone clone 120 24 + CurveMapping *curve 144 8 + MTex mtex 152 312 + MTex mask_mtex 464 312 + Brush *toggle_brush 776 8 + ImBuf *icon_imbuf 784 8 + PreviewImage *preview 792 8 + char icon_filepath 800 1024 + float normal_weight 1824 4 + short blend 1828 2 + short ob_mode 1830 2 + float weight 1832 4 + int size 1836 4 + int flag 1840 4 + float jitter 1844 4 + int jitter_absolute 1848 4 + int overlay_flags 1852 4 + int spacing 1856 4 + int smooth_stroke_radius 1860 4 + float smooth_stroke_factor 1864 4 + float rate 1868 4 + float rgb 1872 12 + float alpha 1884 4 + int sculpt_plane 1888 4 + float plane_offset 1892 4 + char sculpt_tool 1896 1 + char vertexpaint_tool 1897 1 + char imagepaint_tool 1898 1 + char mask_tool 1899 1 + float autosmooth_factor 1900 4 + float crease_pinch_factor 1904 4 + float plane_trim 1908 4 + float height 1912 4 + float texture_sample_bias 1916 4 + int texture_overlay_alpha 1920 4 + int mask_overlay_alpha 1924 4 + int cursor_overlay_alpha 1928 4 + float unprojected_radius 1932 4 + float add_col 1936 12 + float sub_col 1948 12 + float stencil_pos 1960 8 + float stencil_dimension 1968 8 + float mask_stencil_pos 1976 8 + float mask_stencil_dimension 1984 8 + +CustomDataLayer 104 + + int type 0 4 + int offset 4 4 + int flag 8 4 + int active 12 4 + int active_rnd 16 4 + int active_clone 20 4 + int active_mask 24 4 + int uid 28 4 + char name 32 64 + void *data 96 8 + +CustomDataExternal 1024 + + char filename 0 1024 + +CustomData 192 + + CustomDataLayer *layers 0 8 + int typemap 8 156 + int totlayer 164 4 + int maxlayer 168 4 + int totsize 172 4 + void *pool 176 8 + CustomDataExternal *external 184 8 + +HairKey 24 + + float co 0 12 + float time 12 4 + float weight 16 4 + short editflag 20 2 + short pad 22 2 + +ParticleKey 56 + + float co 0 12 + float vel 12 12 + float rot 24 16 + float ave 40 12 + float time 52 4 + +BoidParticle 56 + + Object *ground 0 8 + BoidData data 8 20 + float gravity 28 12 + float wander 40 12 + float rt 52 4 + +ParticleSpring 16 + + float rest_length 0 4 + int particle_index 4 8 + int delete_flag 12 4 + +ChildParticle 64 + + int num 0 4 + int parent 4 4 + int pa 8 16 + float w 24 16 + float fuv 40 16 + float foffset 56 4 + float rt 60 4 + +ParticleTarget 40 + + ParticleTarget *next 0 8 + ParticleTarget *prev 8 8 + Object *ob 16 8 + int psys 24 4 + short flag 28 2 + short mode 30 2 + float time 32 4 + float duration 36 4 + +ParticleDupliWeight 32 + + ParticleDupliWeight *next 0 8 + ParticleDupliWeight *prev 8 8 + Object *ob 16 8 + short count 24 2 + short flag 26 2 + short index 28 2 + short rt 30 2 + +ParticleData 200 + + ParticleKey state 0 56 + ParticleKey prev_state 56 56 + HairKey *hair 112 8 + ParticleKey *keys 120 8 + BoidParticle *boid 128 8 + int totkey 136 4 + float time 140 4 + float lifetime 144 4 + float dietime 148 4 + int num 152 4 + int num_dmcache 156 4 + float fuv 160 16 + float foffset 176 4 + float size 180 4 + float sphdensity 184 4 + int pad 188 4 + int hair_index 192 4 + short flag 196 2 + short alive 198 2 + +SPHFluidSettings 68 + + float radius 0 4 + float spring_k 4 4 + float rest_length 8 4 + float plasticity_constant 12 4 + float yield_ratio 16 4 + float plasticity_balance 20 4 + float yield_balance 24 4 + float viscosity_omega 28 4 + float viscosity_beta 32 4 + float stiffness_k 36 4 + float stiffness_knear 40 4 + float rest_density 44 4 + float buoyancy 48 4 + int flag 52 4 + int spring_frames 56 4 + short solver 60 2 + short pad 62 6 + +ParticleSettings 800 + + ID id 0 120 + AnimData *adt 120 8 + BoidSettings *boids 128 8 + SPHFluidSettings *fluid 136 8 + EffectorWeights *effector_weights 144 8 + int flag 152 4 + int rt 156 4 + short type 160 2 + short from 162 2 + short distr 164 2 + short texact 166 2 + short phystype 168 2 + short rotmode 170 2 + short avemode 172 2 + short reactevent 174 2 + int draw 176 4 + int pad1 180 4 + short draw_as 184 2 + short draw_size 186 2 + short childtype 188 2 + short pad2 190 2 + short ren_as 192 2 + short subframes 194 2 + short draw_col 196 2 + short draw_step 198 2 + short ren_step 200 2 + short hair_step 202 2 + short keys_step 204 2 + short adapt_angle 206 2 + short adapt_pix 208 2 + short disp 210 2 + short omat 212 2 + short interpolation 214 2 + short integrator 216 2 + short rotfrom 218 2 + short kink 220 2 + short kink_axis 222 2 + short bb_align 224 2 + short bb_uv_split 226 2 + short bb_anim 228 2 + short bb_split_offset 230 2 + float bb_tilt 232 4 + float bb_rand_tilt 236 4 + float bb_offset 240 8 + float bb_size 248 8 + float bb_vel_head 256 4 + float bb_vel_tail 260 4 + float color_vec_max 264 4 + short simplify_flag 268 2 + short simplify_refsize 270 2 + float simplify_rate 272 4 + float simplify_transition 276 4 + float simplify_viewport 280 4 + float sta 284 4 + float end 288 4 + float lifetime 292 4 + float randlife 296 4 + float timetweak 300 4 + float courant_target 304 4 + float jitfac 308 4 + float eff_hair 312 4 + float grid_rand 316 4 + float ps_offset 320 4 + int totpart 324 4 + int userjit 328 4 + int grid_res 332 4 + int effector_amount 336 4 + short time_flag 340 2 + short time_pad 342 6 + float normfac 348 4 + float obfac 352 4 + float randfac 356 4 + float partfac 360 4 + float tanfac 364 4 + float tanphase 368 4 + float reactfac 372 4 + float ob_vel 376 12 + float avefac 388 4 + float phasefac 392 4 + float randrotfac 396 4 + float randphasefac 400 4 + float mass 404 4 + float size 408 4 + float randsize 412 4 + float acc 416 12 + float dragfac 428 4 + float brownfac 432 4 + float dampfac 436 4 + float randlength 440 4 + int child_nbr 444 4 + int ren_child_nbr 448 4 + float parents 452 4 + float childsize 456 4 + float childrandsize 460 4 + float childrad 464 4 + float childflat 468 4 + float clumpfac 472 4 + float clumppow 476 4 + float kink_amp 480 4 + float kink_freq 484 4 + float kink_shape 488 4 + float kink_flat 492 4 + float kink_amp_clump 496 4 + float rough1 500 4 + float rough1_size 504 4 + float rough2 508 4 + float rough2_size 512 4 + float rough2_thres 516 4 + float rough_end 520 4 + float rough_end_shape 524 4 + float clength 528 4 + float clength_thres 532 4 + float parting_fac 536 4 + float parting_min 540 4 + float parting_max 544 4 + float branch_thres 548 4 + float draw_line 552 8 + float path_start 560 4 + float path_end 564 4 + int trail_count 568 4 + int keyed_loops 572 4 + MTex *mtex 576 144 + Group *dup_group 720 8 + ListBase dupliweights 728 16 + Group *eff_group 744 8 + Object *dup_ob 752 8 + Object *bb_ob 760 8 + Ipo *ipo 768 8 + PartDeflect *pd 776 8 + PartDeflect *pd2 784 8 + short use_modifier_stack 792 2 + short pad 794 6 + +ParticleSystem 656 + + ParticleSystem *next 0 8 + ParticleSystem *prev 8 8 + ParticleSettings *part 16 8 + ParticleData *particles 24 8 + ChildParticle *child 32 8 + PTCacheEdit *edit 40 8 + void (*free_edit)() 48 0 + ParticleCacheKey **pathcache 48 8 + ParticleCacheKey **childcache 56 8 + ListBase pathcachebufs 64 16 + ListBase childcachebufs 80 16 + ClothModifierData *clmd 96 8 + DerivedMesh *hair_in_dm 104 8 + DerivedMesh *hair_out_dm 112 8 + Object *target_ob 120 8 + LatticeDeformData *lattice_deform_data 128 8 + Object *parent 136 8 + ListBase targets 144 16 + char name 160 64 + float imat 224 64 + float cfra 288 4 + float tree_frame 292 4 + float bvhtree_frame 296 4 + int seed 300 4 + int child_seed 304 4 + int flag 308 4 + int totpart 312 4 + int totunexist 316 4 + int totchild 320 4 + int totcached 324 4 + int totchildcache 328 4 + short recalc 332 2 + short target_psys 334 2 + short totkeyed 336 2 + short bakespace 338 2 + char bb_uvname 340 192 + short vgroup 532 24 + short vg_neg 556 2 + short rt3 558 2 + void *renderdata 560 8 + PointCache *pointcache 568 8 + ListBase ptcaches 576 16 + ListBase *effectors 592 8 + ParticleSpring *fluid_springs 600 8 + int tot_fluidsprings 608 4 + int alloc_fluidsprings 612 4 + KDTree *tree 616 8 + BVHTree *bvhtree 624 8 + ParticleDrawData *pdd 632 8 + float *frand 640 8 + float dt_frac 648 4 + float _pad 652 4 + +ClothSimSettings 152 + + LinkNode *cache 0 8 + float mingoal 8 4 + float Cdis 12 4 + float Cvi 16 4 + float gravity 20 12 + float dt 32 4 + float mass 36 4 + float structural 40 4 + float shear 44 4 + float bending 48 4 + float max_bend 52 4 + float max_struct 56 4 + float max_shear 60 4 + float avg_spring_len 64 4 + float timescale 68 4 + float maxgoal 72 4 + float eff_force_scale 76 4 + float eff_wind_scale 80 4 + float sim_time_old 84 4 + float defgoal 88 4 + float goalspring 92 4 + float goalfrict 96 4 + float velocity_smooth 100 4 + float collider_friction 104 4 + float vel_damping 108 4 + int stepsPerFrame 112 4 + int flags 116 4 + int preroll 120 4 + int maxspringlen 124 4 + short solver_type 128 2 + short vgroup_bend 130 2 + short vgroup_mass 132 2 + short vgroup_struct 134 2 + short shapekey_rest 136 2 + short presets 138 2 + short reset 140 2 + short pad 142 2 + EffectorWeights *effector_weights 144 8 + +ClothCollSettings 56 + + LinkNode *collision_list 0 8 + float epsilon 8 4 + float self_friction 12 4 + float friction 16 4 + float selfepsilon 20 4 + float repel_force 24 4 + float distance_repel 28 4 + int flags 32 4 + short self_loop_count 36 2 + short loop_count 38 2 + Group *group 40 8 + short vgroup_selfcol 48 2 + short pad 50 2 + int pad2 52 4 + +bGPDspoint 20 + + float x 0 4 + float y 4 4 + float z 8 4 + float pressure 12 4 + float time 16 4 + +bGPDstroke 48 + + bGPDstroke *next 0 8 + bGPDstroke *prev 8 8 + bGPDspoint *points 16 8 + void *pad 24 8 + int totpoints 32 4 + short thickness 36 2 + short flag 38 2 + double inittime 40 8 + +bGPDframe 40 + + bGPDframe *next 0 8 + bGPDframe *prev 8 8 + ListBase strokes 16 16 + int framenum 32 4 + int flag 36 4 + +bGPDlayer 192 + + bGPDlayer *next 0 8 + bGPDlayer *prev 8 8 + ListBase frames 16 16 + bGPDframe *actframe 32 8 + int flag 40 4 + short thickness 44 2 + short gstep 46 2 + float color 48 16 + char info 64 128 + +bGPdata 152 + + ID id 0 120 + ListBase layers 120 16 + int flag 136 4 + short sbuffer_size 140 2 + short sbuffer_sflag 142 2 + void *sbuffer 144 8 + +ReportList 40 + + ListBase list 0 16 + int printlevel 16 4 + int storelevel 20 4 + int flag 24 4 + int pad 28 4 + wmTimer *reporttimer 32 8 + +wmWindowManager 344 + + ID id 0 120 + wmWindow *windrawable 120 8 + wmWindow *winactive 128 8 + ListBase windows 136 16 + int initialized 152 4 + short file_saved 156 2 + short op_undo_depth 158 2 + ListBase operators 160 16 + ListBase queue 176 16 + ReportList reports 192 40 + ListBase jobs 232 16 + ListBase paintcursors 248 16 + ListBase drags 264 16 + ListBase keyconfigs 280 16 + wmKeyConfig *defaultconf 296 8 + wmKeyConfig *addonconf 304 8 + wmKeyConfig *userconf 312 8 + ListBase timers 320 16 + wmTimer *autosavetimer 336 8 + +wmWindow 256 + + wmWindow *next 0 8 + wmWindow *prev 8 8 + void *ghostwin 16 8 + int winid 24 4 + short grabcursor 28 2 + short pad 30 2 + bScreen *screen 32 8 + bScreen *newscreen 40 8 + char screenname 48 64 + short posx 112 2 + short posy 114 2 + short sizex 116 2 + short sizey 118 2 + short windowstate 120 2 + short monitor 122 2 + short active 124 2 + short cursor 126 2 + short lastcursor 128 2 + short modalcursor 130 2 + short addmousemove 132 2 + short pad2 134 2 + wmEvent *eventstate 136 8 + wmSubWindow *curswin 144 8 + wmGesture *tweak 152 8 + int drawmethod 160 4 + int drawfail 164 4 + void *drawdata 168 8 + ListBase queue 176 16 + ListBase handlers 192 16 + ListBase modalhandlers 208 16 + ListBase subwindows 224 16 + ListBase gesture 240 16 + +wmKeyMapItem 184 + + wmKeyMapItem *next 0 8 + wmKeyMapItem *prev 8 8 + char idname 16 64 + IDProperty *properties 80 8 + char propvalue_str 88 64 + short propvalue 152 2 + short type 154 2 + short val 156 2 + short shift 158 2 + short ctrl 160 2 + short alt 162 2 + short oskey 164 2 + short keymodifier 166 2 + short flag 168 2 + short maptype 170 2 + short id 172 2 + short pad 174 2 + PointerRNA *ptr 176 8 + +wmKeyMapDiffItem 32 + + wmKeyMapDiffItem *next 0 8 + wmKeyMapDiffItem *prev 8 8 + wmKeyMapItem *remove_item 16 8 + wmKeyMapItem *add_item 24 8 + +wmKeyMap 132 + + wmKeyMap *next 0 8 + wmKeyMap *prev 8 8 + ListBase items 16 16 + ListBase diff_items 32 16 + char idname 48 64 + short spaceid 112 2 + short regionid 114 2 + short flag 116 2 + short kmi_id 118 2 + int (*poll)() 120 4 + void *modal_items 124 8 + +wmKeyConfig 168 + + wmKeyConfig *next 0 8 + wmKeyConfig *prev 8 8 + char idname 16 64 + char basename 80 64 + ListBase keymaps 144 16 + int actkeymap 160 4 + int flag 164 4 + +wmOperator 168 + + wmOperator *next 0 8 + wmOperator *prev 8 8 + char idname 16 64 + IDProperty *properties 80 8 + wmOperatorType *type 88 8 + void *customdata 96 8 + void *py_instance 104 8 + PointerRNA *ptr 112 8 + ReportList *reports 120 8 + ListBase macro 128 16 + wmOperator *opm 144 8 + uiLayout *layout 152 8 + short flag 160 2 + short pad 162 6 + +FModifier 120 + + FModifier *next 0 8 + FModifier *prev 8 8 + void *data 16 8 + void *edata 24 8 + char name 32 64 + short type 96 2 + short flag 98 2 + float influence 100 4 + float sfra 104 4 + float efra 108 4 + float blendin 112 4 + float blendout 116 4 + +FMod_Generator 24 + + float *coefficients 0 8 + int arraysize 8 4 + int poly_order 12 4 + int mode 16 4 + int flag 20 4 + +FMod_FunctionGenerator 24 + + float amplitude 0 4 + float phase_multiplier 4 4 + float phase_offset 8 4 + float value_offset 12 4 + int type 16 4 + int flag 20 4 + +FCM_EnvelopeData 16 + + float min 0 4 + float max 4 4 + float time 8 4 + short f1 12 2 + short f2 14 2 + +FMod_Envelope 24 + + FCM_EnvelopeData *data 0 8 + int totvert 8 4 + float midval 12 4 + float min 16 4 + float max 20 4 + +FMod_Cycles 8 + + short before_mode 0 2 + short after_mode 2 2 + short before_cycles 4 2 + short after_cycles 6 2 + +FMod_Python 16 + + Text *script 0 8 + IDProperty *prop 8 8 + +FMod_Limits 24 + + rctf rect 0 16 + int flag 16 4 + int pad 20 4 + +FMod_Noise 20 + + float size 0 4 + float strength 4 4 + float phase 8 4 + float pad 12 4 + short depth 16 2 + short modification 18 2 + +FMod_Stepped 20 + + float step_size 0 4 + float offset 4 4 + float start_frame 8 4 + float end_frame 12 4 + int flag 16 4 + +DriverTarget 56 + + ID *id 0 8 + char *rna_path 8 8 + char pchan_name 16 32 + short transChan 48 2 + short flag 50 2 + int idtype 52 4 + +DriverVar 536 + + DriverVar *next 0 8 + DriverVar *prev 8 8 + char name 16 64 + DriverTarget targets 80 448 + short num_targets 528 2 + short type 530 2 + float curval 532 4 + +ChannelDriver 296 + + ListBase variables 0 16 + char expression 16 256 + void *expr_comp 272 8 + float curval 280 4 + float influence 284 4 + int type 288 4 + int flag 292 4 + +FPoint 16 + + float vec 0 8 + int flag 8 4 + int pad 12 4 + +FCurve 104 + + FCurve *next 0 8 + FCurve *prev 8 8 + bActionGroup *grp 16 8 + ChannelDriver *driver 24 8 + ListBase modifiers 32 16 + BezTriple *bezt 48 8 + FPoint *fpt 56 8 + int totvert 64 4 + float curval 68 4 + short flag 72 2 + short extend 74 2 + int array_index 76 4 + char *rna_path 80 8 + int color_mode 88 4 + float color 92 12 + +AnimMapPair 256 + + char from 0 128 + char to 128 128 + +AnimMapper 40 + + AnimMapper *next 0 8 + AnimMapper *prev 8 8 + bAction *target 16 8 + ListBase mappings 24 16 + +NlaStrip 208 + + NlaStrip *next 0 8 + NlaStrip *prev 8 8 + ListBase strips 16 16 + bAction *act 32 8 + AnimMapper *remap 40 8 + ListBase fcurves 48 16 + ListBase modifiers 64 16 + char name 80 64 + float influence 144 4 + float strip_time 148 4 + float start 152 4 + float end 156 4 + float actstart 160 4 + float actend 164 4 + float repeat 168 4 + float scale 172 4 + float blendin 176 4 + float blendout 180 4 + short blendmode 184 2 + short extendmode 186 2 + short pad1 188 2 + short type 190 2 + void *speaker_handle 192 8 + int flag 200 4 + int pad2 204 4 + +NlaTrack 104 + + NlaTrack *next 0 8 + NlaTrack *prev 8 8 + ListBase strips 16 16 + int flag 32 4 + int index 36 4 + char name 40 64 + +KS_Path 112 + + KS_Path *next 0 8 + KS_Path *prev 8 8 + ID *id 16 8 + char group 24 64 + int idtype 88 4 + short groupmode 92 2 + short pad 94 2 + char *rna_path 96 8 + int array_index 104 4 + short flag 108 2 + short keyingflag 110 2 + +KeyingSet 472 + + KeyingSet *next 0 8 + KeyingSet *prev 8 8 + ListBase paths 16 16 + char idname 32 64 + char name 96 64 + char description 160 240 + char typeinfo 400 64 + short flag 464 2 + short keyingflag 466 2 + int active_path 468 4 + +AnimOverride 32 + + AnimOverride *next 0 8 + AnimOverride *prev 8 8 + char *rna_path 16 8 + int array_index 24 4 + float value 28 4 + +AnimData 96 + + bAction *action 0 8 + bAction *tmpact 8 8 + AnimMapper *remap 16 8 + ListBase nla_tracks 24 16 + NlaStrip *actstrip 40 8 + ListBase drivers 48 16 + ListBase overrides 64 16 + int flag 80 4 + int recalc 84 4 + short act_blendmode 88 2 + short act_extendmode 90 2 + float act_influence 92 4 + +IdAdtTemplate 128 + + ID id 0 120 + AnimData *adt 120 8 + +BoidRule 56 + + BoidRule *next 0 8 + BoidRule *prev 8 8 + int type 16 4 + int flag 20 4 + char name 24 32 + +BoidRuleGoalAvoid 80 + + BoidRule rule 0 56 + Object *ob 56 8 + int options 64 4 + float fear_factor 68 4 + int signal_id 72 4 + int channels 76 4 + +BoidRuleAvoidCollision 64 + + BoidRule rule 0 56 + int options 56 4 + float look_ahead 60 4 + +BoidRuleFollowLeader 104 + + BoidRule rule 0 56 + Object *ob 56 8 + float loc 64 12 + float oloc 76 12 + float cfra 88 4 + float distance 92 4 + int options 96 4 + int queue_size 100 4 + +BoidRuleAverageSpeed 72 + + BoidRule rule 0 56 + float wander 56 4 + float level 60 4 + float speed 64 4 + float rt 68 4 + +BoidRuleFight 64 + + BoidRule rule 0 56 + float distance 56 4 + float flee_distance 60 4 + +BoidData 20 + + float health 0 4 + float acc 4 12 + short state_id 16 2 + short mode 18 2 + +BoidState 128 + + BoidState *next 0 8 + BoidState *prev 8 8 + ListBase rules 16 16 + ListBase conditions 32 16 + ListBase actions 48 16 + char name 64 32 + int id 96 4 + int flag 100 4 + int ruleset_type 104 4 + float rule_fuzziness 108 4 + int signal_id 112 4 + int channels 116 4 + float volume 120 4 + float falloff 124 4 + +BoidSettings 104 + + int options 0 4 + int last_state_id 4 4 + float landing_smoothness 8 4 + float height 12 4 + float banking 16 4 + float pitch 20 4 + float health 24 4 + float aggression 28 4 + float strength 32 4 + float accuracy 36 4 + float range 40 4 + float air_min_speed 44 4 + float air_max_speed 48 4 + float air_max_acc 52 4 + float air_max_ave 56 4 + float air_personal_space 60 4 + float land_jump_speed 64 4 + float land_max_speed 68 4 + float land_max_acc 72 4 + float land_max_ave 76 4 + float land_personal_space 80 4 + float land_stick_force 84 4 + ListBase states 88 16 + +SmokeDomainSettings 584 + + SmokeModifierData *smd 0 8 + FLUID_3D *fluid 8 8 + void *fluid_mutex 16 8 + Group *fluid_group 24 8 + Group *eff_group 32 8 + Group *coll_group 40 8 + WTURBULENCE *wt 48 8 + GPUTexture *tex 56 8 + GPUTexture *tex_wt 64 8 + GPUTexture *tex_shadow 72 8 + GPUTexture *tex_flame 80 8 + float *shadow 88 8 + float p0 96 12 + float p1 108 12 + float dp0 120 12 + float cell_size 132 12 + float global_size 144 12 + float prev_loc 156 12 + int shift 168 12 + float shift_f 180 12 + float obj_shift_f 192 12 + float imat 204 64 + float obmat 268 64 + int base_res 332 12 + int res_min 344 12 + int res_max 356 12 + int res 368 12 + int total_cells 380 4 + float dx 384 4 + float scale 388 4 + int adapt_margin 392 4 + int adapt_res 396 4 + float adapt_threshold 400 4 + float alpha 404 4 + float beta 408 4 + int amplify 412 4 + int maxres 416 4 + int flags 420 4 + int viewsettings 424 4 + short noise 428 2 + short diss_percent 430 2 + int diss_speed 432 4 + float strength 436 4 + int res_wt 440 12 + float dx_wt 452 4 + int cache_comp 456 4 + int cache_high_comp 460 4 + PointCache *point_cache 464 16 + ListBase ptcaches 480 32 + EffectorWeights *effector_weights 512 8 + int border_collisions 520 4 + float time_scale 524 4 + float vorticity 528 4 + int active_fields 532 4 + float active_color 536 12 + int highres_sampling 548 4 + float burning_rate 552 4 + float flame_smoke 556 4 + float flame_vorticity 560 4 + float flame_ignition 564 4 + float flame_max_temp 568 4 + float flame_smoke_color 572 12 + +SmokeFlowSettings 184 + + SmokeModifierData *smd 0 8 + DerivedMesh *dm 8 8 + ParticleSystem *psys 16 8 + Tex *noise_texture 24 8 + float *verts_old 32 8 + int numverts 40 4 + float vel_multi 44 4 + float vel_normal 48 4 + float vel_random 52 4 + float density 56 4 + float color 60 12 + float fuel_amount 72 4 + float temp 76 4 + float volume_density 80 4 + float surface_distance 84 4 + float particle_size 88 4 + int subframes 92 4 + float texture_size 96 4 + float texture_offset 100 4 + int pad 104 4 + char uvlayer_name 108 64 + short vgroup_density 172 2 + short type 174 2 + short source 176 2 + short texture_type 178 2 + int flags 180 4 + +SmokeCollSettings 32 + + SmokeModifierData *smd 0 8 + DerivedMesh *dm 8 8 + float *verts_old 16 8 + int numverts 24 4 + short type 28 2 + short pad 30 2 + +Speaker 184 + + ID id 0 120 + AnimData *adt 120 8 + bSound *sound 128 8 + float volume_max 136 4 + float volume_min 140 4 + float distance_max 144 4 + float distance_reference 148 4 + float attenuation 152 4 + float cone_angle_outer 156 4 + float cone_angle_inner 160 4 + float cone_volume_outer 164 4 + float volume 168 4 + float pitch 172 4 + short flag 176 2 + short pad1 178 6 + +MovieClipUser 8 + + int framenr 0 4 + short render_size 4 2 + short render_flag 6 2 + +MovieClipProxy 776 + + char dir 0 768 + short tc 768 2 + short quality 770 2 + short build_size_flag 772 2 + short build_tc_flag 774 2 + +MovieClip 2384 + + ID id 0 120 + AnimData *adt 120 8 + char name 128 1024 + int source 1152 4 + int lastframe 1156 4 + int lastsize 1160 8 + float aspx 1168 4 + float aspy 1172 4 + anim *anim 1176 8 + MovieClipCache *cache 1184 8 + bGPdata *gpd 1192 8 + MovieTracking tracking 1200 320 + void *tracking_context 1520 8 + MovieClipProxy proxy 1528 776 + int flag 2304 4 + int len 2308 4 + int start_frame 2312 4 + int frame_offset 2316 4 + ColorManagedColorspaceSettings colorspace_settings 2320 64 + +MovieClipScopes 136 + + short ok 0 2 + short use_track_mask 2 2 + int track_preview_height 4 4 + int frame_width 8 4 + int frame_height 12 4 + MovieTrackingMarker undist_marker 16 64 + ImBuf *track_search 80 8 + ImBuf *track_preview 88 8 + float track_pos 96 8 + short track_disabled 104 2 + short track_locked 106 2 + int framenr 108 4 + MovieTrackingTrack *track 112 8 + MovieTrackingMarker *marker 120 8 + float slide_scale 128 8 + +MovieReconstructedCamera 72 + + int framenr 0 4 + float error 4 4 + float mat 8 64 + +MovieTrackingCamera 48 + + void *intrinsics 0 8 + float sensor_width 8 4 + float pixel_aspect 12 4 + float pad 16 4 + float focal 20 4 + short units 24 2 + short pad1 26 2 + float principal 28 8 + float k1 36 4 + float k2 40 4 + float k3 44 4 + +MovieTrackingMarker 64 + + float pos 0 8 + float pattern_corners 8 32 + float search_min 40 8 + float search_max 48 8 + int framenr 56 4 + int flag 60 4 + +MovieTrackingTrack 200 + + MovieTrackingTrack *next 0 8 + MovieTrackingTrack *prev 8 8 + char name 16 64 + float pat_min 80 8 + float pat_max 88 8 + float search_min 96 8 + float search_max 104 8 + float offset 112 8 + int markersnr 120 4 + int last_marker 124 4 + MovieTrackingMarker *markers 128 8 + float bundle_pos 136 12 + float error 148 4 + int flag 152 4 + int pat_flag 156 4 + int search_flag 160 4 + float color 164 12 + short frames_limit 176 2 + short margin 178 2 + short pattern_match 180 2 + short motion_model 182 2 + int algorithm_flag 184 4 + float minimum_correlation 188 4 + bGPdata *gpd 192 8 + +MovieTrackingPlaneMarker 40 + + float corners 0 32 + int framenr 32 4 + int flag 36 4 + +MovieTrackingPlaneTrack 120 + + MovieTrackingPlaneTrack *next 0 8 + MovieTrackingPlaneTrack *prev 8 8 + char name 16 64 + MovieTrackingTrack **point_tracks 80 8 + int point_tracksnr 88 4 + int pad 92 4 + MovieTrackingPlaneMarker *markers 96 8 + int markersnr 104 4 + int flag 108 4 + int last_marker 112 4 + int pad2 116 4 + +MovieTrackingSettings 72 + + int flag 0 4 + short default_motion_model 4 2 + short default_algorithm_flag 6 2 + float default_minimum_correlation 8 4 + short default_pattern_size 12 2 + short default_search_size 14 2 + short default_frames_limit 16 2 + short default_margin 18 2 + short default_pattern_match 20 2 + short default_flag 22 2 + short motion_flag 24 2 + short speed 26 2 + int keyframe1 28 4 + int keyframe2 32 4 + float reconstruction_success_threshold 36 4 + int reconstruction_flag 40 4 + short refine_camera_intrinsics 44 2 + short pad2 46 2 + float dist 48 4 + int clean_frames 52 4 + int clean_action 56 4 + float clean_error 60 4 + float object_distance 64 4 + int pad3 68 4 + +MovieTrackingStabilization 48 + + int flag 0 4 + int tot_track 4 4 + int act_track 8 4 + float maxscale 12 4 + MovieTrackingTrack *rot_track 16 8 + float locinf 24 4 + float scaleinf 28 4 + float rotinf 32 4 + int filter 36 4 + int ok 40 4 + float scale 44 4 + +MovieTrackingReconstruction 24 + + int flag 0 4 + float error 4 4 + int last_camera 8 4 + int camnr 12 4 + MovieReconstructedCamera *cameras 16 8 + +MovieTrackingObject 152 + + MovieTrackingObject *next 0 8 + MovieTrackingObject *prev 8 8 + char name 16 64 + int flag 80 4 + float scale 84 4 + ListBase tracks 88 16 + ListBase plane_tracks 104 16 + MovieTrackingReconstruction reconstruction 120 24 + int keyframe1 144 4 + int keyframe2 148 4 + +MovieTrackingStats 256 + + char message 0 256 + +MovieTrackingDopesheetChannel 112 + + MovieTrackingDopesheetChannel *next 0 8 + MovieTrackingDopesheetChannel *prev 8 8 + MovieTrackingTrack *track 16 8 + int pad 24 4 + char name 28 64 + int tot_segment 92 4 + int *segments 96 8 + int max_segment 104 4 + int total_frames 108 4 + +MovieTrackingDopesheetCoverageSegment 32 + + MovieTrackingDopesheetCoverageSegment *next 0 8 + MovieTrackingDopesheetCoverageSegment *prev 8 8 + int coverage 16 4 + int start_frame 20 4 + int end_frame 24 4 + int pad 28 4 + +MovieTrackingDopesheet 48 + + int ok 0 4 + short sort_method 4 2 + short flag 6 2 + ListBase coverage_segments 8 16 + ListBase channels 24 16 + int tot_channel 40 4 + int pad 44 4 + +MovieTracking 320 + + MovieTrackingSettings settings 0 72 + MovieTrackingCamera camera 72 48 + ListBase tracks 120 16 + ListBase plane_tracks 136 16 + MovieTrackingReconstruction reconstruction 152 24 + MovieTrackingStabilization stabilization 176 48 + MovieTrackingTrack *act_track 224 8 + MovieTrackingPlaneTrack *act_plane_track 232 8 + ListBase objects 240 16 + int objectnr 256 4 + int tot_object 260 4 + MovieTrackingStats *stats 264 8 + MovieTrackingDopesheet dopesheet 272 48 + +DynamicPaintSurface 1560 + + DynamicPaintSurface *next 0 8 + DynamicPaintSurface *prev 8 8 + DynamicPaintCanvasSettings *canvas 16 8 + PaintSurfaceData *data 24 8 + Group *brush_group 32 8 + EffectorWeights *effector_weights 40 8 + PointCache *pointcache 48 8 + ListBase ptcaches 56 16 + int current_frame 72 4 + char name 76 64 + short format 140 2 + short type 142 2 + short disp_type 144 2 + short image_fileformat 146 2 + short effect_ui 148 2 + short preview_id 150 2 + short init_color_type 152 2 + short pad_s 154 2 + int flags 156 4 + int effect 160 4 + int image_resolution 164 4 + int substeps 168 4 + int start_frame 172 4 + int end_frame 176 4 + int pad 180 4 + float init_color 184 16 + Tex *init_texture 200 8 + char init_layername 208 64 + int dry_speed 272 4 + int diss_speed 276 4 + float color_dry_threshold 280 4 + float depth_clamp 284 4 + float disp_factor 288 4 + float spread_speed 292 4 + float color_spread_speed 296 4 + float shrink_speed 300 4 + float drip_vel 304 4 + float drip_acc 308 4 + float influence_scale 312 4 + float radius_scale 316 4 + float wave_damping 320 4 + float wave_speed 324 4 + float wave_timescale 328 4 + float wave_spring 332 4 + float wave_smoothness 336 4 + int pad2 340 4 + char uvlayer_name 344 64 + char image_output_path 408 1024 + char output_name 1432 64 + char output_name2 1496 64 + +DynamicPaintCanvasSettings 104 + + DynamicPaintModifierData *pmd 0 8 + DerivedMesh *dm 8 8 + ListBase surfaces 16 16 + short active_sur 32 2 + short flags 34 2 + int pad 36 4 + char error 40 64 + +DynamicPaintBrushSettings 112 + + DynamicPaintModifierData *pmd 0 8 + DerivedMesh *dm 8 8 + ParticleSystem *psys 16 8 + Material *mat 24 8 + int flags 32 4 + int collision 36 4 + float r 40 4 + float g 44 4 + float b 48 4 + float alpha 52 4 + float wetness 56 4 + float particle_radius 60 4 + float particle_smooth 64 4 + float paint_distance 68 4 + ColorBand *paint_ramp 72 8 + ColorBand *vel_ramp 80 8 + short proximity_falloff 88 2 + short wave_type 90 2 + short ray_dir 92 2 + short pad 94 2 + float wave_factor 96 4 + float wave_clamp 100 4 + float max_velocity 104 4 + float smudge_strength 108 4 + +Mask 168 + + ID id 0 120 + AnimData *adt 120 8 + ListBase masklayers 128 16 + int masklay_act 144 4 + int masklay_tot 148 4 + int sfra 152 4 + int efra 156 4 + int flag 160 4 + int pad 164 4 + +MaskParent 184 + + int id_type 0 4 + int type 4 4 + ID *id 8 8 + char parent 16 64 + char sub_parent 80 64 + float parent_orig 144 8 + float parent_corners_orig 152 32 + +MaskSplinePointUW 12 + + float u 0 4 + float w 4 4 + int flag 8 4 + +MaskSplinePoint 256 + + BezTriple bezt 0 56 + int pad 56 4 + int tot_uw 60 4 + MaskSplinePointUW *uw 64 8 + MaskParent parent 72 184 + +MaskSpline 224 + + MaskSpline *next 0 8 + MaskSpline *prev 8 8 + short flag 16 2 + char offset_mode 18 1 + char weight_interp 19 1 + int tot_point 20 4 + MaskSplinePoint *points 24 8 + MaskParent parent 32 184 + MaskSplinePoint *points_deform 216 8 + +MaskLayerShape 40 + + MaskLayerShape *next 0 8 + MaskLayerShape *prev 8 8 + float *data 16 8 + int tot_vert 24 4 + int frame 28 4 + char flag 32 1 + char pad 33 7 + +MaskLayer 144 + + MaskLayer *next 0 8 + MaskLayer *prev 8 8 + char name 16 64 + ListBase splines 80 16 + ListBase splines_shapes 96 16 + MaskSpline *act_spline 112 8 + MaskSplinePoint *act_point 120 8 + float alpha 128 4 + char blend 132 1 + char blend_flag 133 1 + char falloff 134 1 + char pad 135 7 + char flag 142 1 + char restrictflag 143 1 + +RigidBodyWorld 88 + + EffectorWeights *effector_weights 0 8 + Group *group 8 8 + Object **objects 16 8 + Group *constraints 24 8 + int pad 32 4 + float ltime 36 4 + PointCache *pointcache 40 8 + ListBase ptcaches 48 16 + int numbodies 64 4 + short steps_per_second 68 2 + short num_solver_iterations 70 2 + int flag 72 4 + float time_scale 76 4 + void *physics_world 80 8 + +RigidBodyOb 96 + + void *physics_object 0 8 + void *physics_shape 8 8 + short type 16 2 + short shape 18 2 + int flag 20 4 + int col_groups 24 4 + int pad 28 4 + float mass 32 4 + float friction 36 4 + float restitution 40 4 + float margin 44 4 + float lin_damping 48 4 + float ang_damping 52 4 + float lin_sleep_thresh 56 4 + float ang_sleep_thresh 60 4 + float orn 64 16 + float pos 80 12 + float pad1 92 4 + +RigidBodyCon 128 + + Object *ob1 0 8 + Object *ob2 8 8 + short type 16 2 + short num_solver_iterations 18 2 + int flag 20 4 + float breaking_threshold 24 4 + float pad 28 4 + float limit_lin_x_lower 32 4 + float limit_lin_x_upper 36 4 + float limit_lin_y_lower 40 4 + float limit_lin_y_upper 44 4 + float limit_lin_z_lower 48 4 + float limit_lin_z_upper 52 4 + float limit_ang_x_lower 56 4 + float limit_ang_x_upper 60 4 + float limit_ang_y_lower 64 4 + float limit_ang_y_upper 68 4 + float limit_ang_z_lower 72 4 + float limit_ang_z_upper 76 4 + float spring_stiffness_x 80 4 + float spring_stiffness_y 84 4 + float spring_stiffness_z 88 4 + float spring_damping_x 92 4 + float spring_damping_y 96 4 + float spring_damping_z 100 4 + float motor_lin_target_velocity 104 4 + float motor_ang_target_velocity 108 4 + float motor_lin_max_impulse 112 4 + float motor_ang_max_impulse 116 4 + void *physics_constraint 120 8 + +FreestyleLineSet 128 + + FreestyleLineSet *next 0 8 + FreestyleLineSet *prev 8 8 + char name 16 64 + int flags 80 4 + int selection 84 4 + short qi 88 2 + short pad1 90 2 + int qi_start 92 4 + int qi_end 96 4 + int edge_types 100 4 + int exclude_edge_types 104 4 + int pad2 108 4 + Group *group 112 8 + FreestyleLineStyle *linestyle 120 8 + +FreestyleModuleConfig 32 + + FreestyleModuleConfig *next 0 8 + FreestyleModuleConfig *prev 8 8 + Text *script 16 8 + short is_displayed 24 2 + short pad 26 6 + +FreestyleConfig 56 + + ListBase modules 0 16 + int mode 16 4 + int raycasting_algorithm 20 4 + int flags 24 4 + float sphere_radius 28 4 + float dkr_epsilon 32 4 + float crease_angle 36 4 + ListBase linesets 40 16 + +LineStyleModifier 96 + + LineStyleModifier *next 0 8 + LineStyleModifier *prev 8 8 + char name 16 64 + int type 80 4 + float influence 84 4 + int flags 88 4 + int blend 92 4 + +LineStyleColorModifier_AlongStroke 104 + + LineStyleModifier modifier 0 96 + ColorBand *color_ramp 96 8 + +LineStyleAlphaModifier_AlongStroke 112 + + LineStyleModifier modifier 0 96 + CurveMapping *curve 96 8 + int flags 104 4 + int pad 108 4 + +LineStyleThicknessModifier_AlongStroke 120 + + LineStyleModifier modifier 0 96 + CurveMapping *curve 96 8 + int flags 104 4 + float value_min 108 4 + float value_max 112 4 + int pad 116 4 + +LineStyleColorModifier_DistanceFromCamera 112 + + LineStyleModifier modifier 0 96 + ColorBand *color_ramp 96 8 + float range_min 104 4 + float range_max 108 4 + +LineStyleAlphaModifier_DistanceFromCamera 120 + + LineStyleModifier modifier 0 96 + CurveMapping *curve 96 8 + int flags 104 4 + float range_min 108 4 + float range_max 112 4 + int pad 116 4 + +LineStyleThicknessModifier_DistanceFromCamera 128 + + LineStyleModifier modifier 0 96 + CurveMapping *curve 96 8 + int flags 104 4 + float range_min 108 4 + float range_max 112 4 + float value_min 116 4 + float value_max 120 4 + int pad 124 4 + +LineStyleColorModifier_DistanceFromObject 120 + + LineStyleModifier modifier 0 96 + Object *target 96 8 + ColorBand *color_ramp 104 8 + float range_min 112 4 + float range_max 116 4 + +LineStyleAlphaModifier_DistanceFromObject 128 + + LineStyleModifier modifier 0 96 + Object *target 96 8 + CurveMapping *curve 104 8 + int flags 112 4 + float range_min 116 4 + float range_max 120 4 + int pad 124 4 + +LineStyleThicknessModifier_DistanceFromObject 136 + + LineStyleModifier modifier 0 96 + Object *target 96 8 + CurveMapping *curve 104 8 + int flags 112 4 + float range_min 116 4 + float range_max 120 4 + float value_min 124 4 + float value_max 128 4 + int pad 132 4 + +LineStyleColorModifier_Material 112 + + LineStyleModifier modifier 0 96 + ColorBand *color_ramp 96 8 + int flags 104 4 + int mat_attr 108 4 + +LineStyleAlphaModifier_Material 112 + + LineStyleModifier modifier 0 96 + CurveMapping *curve 96 8 + int flags 104 4 + int mat_attr 108 4 + +LineStyleThicknessModifier_Material 120 + + LineStyleModifier modifier 0 96 + CurveMapping *curve 96 8 + int flags 104 4 + float value_min 108 4 + float value_max 112 4 + int mat_attr 116 4 + +LineStyleGeometryModifier_Sampling 104 + + LineStyleModifier modifier 0 96 + float sampling 96 4 + int pad 100 4 + +LineStyleGeometryModifier_BezierCurve 104 + + LineStyleModifier modifier 0 96 + float error 96 4 + int pad 100 4 + +LineStyleGeometryModifier_SinusDisplacement 112 + + LineStyleModifier modifier 0 96 + float wavelength 96 4 + float amplitude 100 4 + float phase 104 4 + int pad 108 4 + +LineStyleGeometryModifier_SpatialNoise 112 + + LineStyleModifier modifier 0 96 + float amplitude 96 4 + float scale 100 4 + int octaves 104 4 + int flags 108 4 + +LineStyleGeometryModifier_PerlinNoise1D 120 + + LineStyleModifier modifier 0 96 + float frequency 96 4 + float amplitude 100 4 + float angle 104 4 + int octaves 108 4 + int seed 112 4 + int pad1 116 4 + +LineStyleGeometryModifier_PerlinNoise2D 120 + + LineStyleModifier modifier 0 96 + float frequency 96 4 + float amplitude 100 4 + float angle 104 4 + int octaves 108 4 + int seed 112 4 + int pad1 116 4 + +LineStyleGeometryModifier_BackboneStretcher 104 + + LineStyleModifier modifier 0 96 + float backbone_length 96 4 + int pad 100 4 + +LineStyleGeometryModifier_TipRemover 104 + + LineStyleModifier modifier 0 96 + float tip_length 96 4 + int pad 100 4 + +LineStyleGeometryModifier_Polygonalization 104 + + LineStyleModifier modifier 0 96 + float error 96 4 + int pad 100 4 + +LineStyleGeometryModifier_GuidingLines 104 + + LineStyleModifier modifier 0 96 + float offset 96 4 + int pad 100 4 + +LineStyleGeometryModifier_Blueprint 120 + + LineStyleModifier modifier 0 96 + int flags 96 4 + int rounds 100 4 + float backbone_length 104 4 + int random_radius 108 4 + int random_center 112 4 + int random_backbone 116 4 + +LineStyleGeometryModifier_2DOffset 112 + + LineStyleModifier modifier 0 96 + float start 96 4 + float end 100 4 + float x 104 4 + float y 108 4 + +LineStyleGeometryModifier_2DTransform 128 + + LineStyleModifier modifier 0 96 + int pivot 96 4 + float scale_x 100 4 + float scale_y 104 4 + float angle 108 4 + float pivot_u 112 4 + float pivot_x 116 4 + float pivot_y 120 4 + int pad 124 4 + +LineStyleThicknessModifier_Calligraphy 112 + + LineStyleModifier modifier 0 96 + float min_thickness 96 4 + float max_thickness 100 4 + float orientation 104 4 + int pad 108 4 + +FreestyleLineStyle 288 + + ID id 0 120 + AnimData *adt 120 8 + float r 128 4 + float g 132 4 + float b 136 4 + float alpha 140 4 + float thickness 144 4 + int thickness_position 148 4 + float thickness_ratio 152 4 + int flag 156 4 + int caps 160 4 + int chaining 164 4 + int rounds 168 4 + float split_length 172 4 + float min_angle 176 4 + float max_angle 180 4 + float min_length 184 4 + float max_length 188 4 + short split_dash1 192 2 + short split_gap1 194 2 + short split_dash2 196 2 + short split_gap2 198 2 + short split_dash3 200 2 + short split_gap3 202 2 + int pad 204 4 + short dash1 208 2 + short gap1 210 2 + short dash2 212 2 + short gap2 214 2 + short dash3 216 2 + short gap3 218 2 + int panel 220 4 + ListBase color_modifiers 224 16 + ListBase alpha_modifiers 240 16 + ListBase thickness_modifiers 256 16 + ListBase geometry_modifiers 272 16 + diff --git a/test/spiderExport.stl b/test/spiderExport.stl new file mode 100644 index 000000000..f9eb2d538 --- /dev/null +++ b/test/spiderExport.stl @@ -0,0 +1,10946 @@ +solid AssimpScene + facet normal 0.468281955 -0.863497853 -0.187305972 + outer loop + vertex 0.907127976 0.646165013 0.795193017 + vertex 1.65540099 1.11156702 0.520398021 + vertex 0.766146004 0.680482984 0.284518987 + endloop + endfacet + + facet normal 0.373240978 -0.860315859 -0.347199947 + outer loop + vertex 1.85664499 0.887426019 1.21811604 + vertex 0.907127976 0.646165013 0.795193017 + vertex 1.31394804 0.554956019 1.45853198 + endloop + endfacet + + facet normal -0.148454979 -0.953196883 -0.263394982 + outer loop + vertex 1.65540099 1.11156702 0.520398021 + vertex 1.85664499 0.887426019 1.21811604 + vertex 2.33923101 0.936287999 0.769291997 + endloop + endfacet + + facet normal 0.383431226 -0.84137249 -0.38088423 + outer loop + vertex 1.85664499 0.887426019 1.21811604 + vertex 1.65540099 1.11156702 0.520398021 + vertex 0.907127976 0.646165013 0.795193017 + endloop + endfacet + + facet normal 0.562677324 -0.799798489 -0.209085122 + outer loop + vertex 0.451615006 0.391451001 0.543682992 + vertex 0.907127976 0.646165013 0.795193017 + vertex 0.766146004 0.680482984 0.284518987 + endloop + endfacet + + facet normal 0.807133079 -0.364618987 -0.46431601 + outer loop + vertex 0.681457996 0.234028995 1.06684601 + vertex 0.451615006 0.391451001 0.543682992 + vertex 0.382212013 -0.0206840001 0.746681988 + endloop + endfacet + + facet normal 0.612119138 -0.640684068 -0.463502079 + outer loop + vertex 0.907127976 0.646165013 0.795193017 + vertex 0.681457996 0.234028995 1.06684601 + vertex 1.31394804 0.554956019 1.45853198 + endloop + endfacet + + facet normal 0.613333642 -0.640477657 -0.462179691 + outer loop + vertex 0.681457996 0.234028995 1.06684601 + vertex 0.907127976 0.646165013 0.795193017 + vertex 0.451615006 0.391451001 0.543682992 + endloop + endfacet + + facet normal 0.730570912 0 -0.68283695 + outer loop + vertex 0.681457996 -0.275397986 1.06684601 + vertex 0.681457996 0.234028995 1.06684601 + vertex 0.382212013 -0.0206840001 0.746681988 + endloop + endfacet + + facet normal 0.622255981 0.257773995 -0.739154994 + outer loop + vertex 1.25399899 -0.0620529987 1.62324095 + vertex 0.681457996 -0.275397986 1.06684601 + vertex 1.31394804 -0.679062009 1.45853198 + endloop + endfacet + + facet normal 0.600341976 -0.260203004 -0.756230056 + outer loop + vertex 0.681457996 0.234028995 1.06684601 + vertex 1.25399899 -0.0620529987 1.62324095 + vertex 1.31394804 0.554956019 1.45853198 + endloop + endfacet + + facet normal 0.696921945 0 -0.717146993 + outer loop + vertex 1.25399899 -0.0620529987 1.62324095 + vertex 0.681457996 0.234028995 1.06684601 + vertex 0.681457996 -0.275397986 1.06684601 + endloop + endfacet + + facet normal 0.168448105 -0.269475162 -0.948160529 + outer loop + vertex 1.25399899 -0.0620529987 1.62324095 + vertex 1.98102093 0.300615013 1.64932895 + vertex 1.31394804 0.554956019 1.45853198 + endloop + endfacet + + facet normal 0.168448105 0.269475162 -0.948160529 + outer loop + vertex 1.98102093 -0.424721986 1.64932895 + vertex 1.25399899 -0.0620529987 1.62324095 + vertex 1.31394804 -0.679062009 1.45853198 + endloop + endfacet + + facet normal -0.248866931 0 -0.968537748 + outer loop + vertex 1.98102093 0.300615013 1.64932895 + vertex 1.98102093 -0.424721986 1.64932895 + vertex 2.55083203 -0.0620529987 1.50291502 + endloop + endfacet + + facet normal 0.0358599909 0 -0.999356866 + outer loop + vertex 1.98102093 -0.424721986 1.64932895 + vertex 1.98102093 0.300615013 1.64932895 + vertex 1.25399899 -0.0620529987 1.62324095 + endloop + endfacet + + facet normal 0.00512400037 -0.591441095 -0.806332052 + outer loop + vertex 1.98102093 0.300615013 1.64932895 + vertex 1.85664499 0.887426019 1.21811604 + vertex 1.31394804 0.554956019 1.45853198 + endloop + endfacet + + facet normal -0.432703078 -0.343308091 -0.83361119 + outer loop + vertex 2.58366799 0.524757028 1.24420404 + vertex 1.98102093 0.300615013 1.64932895 + vertex 2.55083203 -0.0620529987 1.50291502 + endloop + endfacet + + facet normal -0.375216067 -0.787361085 -0.489158064 + outer loop + vertex 1.85664499 0.887426019 1.21811604 + vertex 2.58366799 0.524757028 1.24420404 + vertex 2.33923101 0.936287999 0.769291997 + endloop + endfacet + + facet normal -0.275769085 -0.606463194 -0.745757163 + outer loop + vertex 2.58366799 0.524757028 1.24420404 + vertex 1.85664499 0.887426019 1.21811604 + vertex 1.98102093 0.300615013 1.64932895 + endloop + endfacet + + facet normal -0.241510943 -0.3801229 -0.892848849 + outer loop + vertex 3.11489511 -0.0620529987 1.35033894 + vertex 2.58366799 0.524757028 1.24420404 + vertex 2.55083203 -0.0620529987 1.50291502 + endloop + endfacet + + facet normal -0.950931132 -0.278835028 0.134094015 + outer loop + vertex 2.83174801 0.524757028 0.562609017 + vertex 3.11489511 -0.0620529987 1.35033894 + vertex 2.97289085 -0.0620529987 0.343317986 + endloop + endfacet + + facet normal -0.680309772 -0.68983078 -0.247612908 + outer loop + vertex 2.58366799 0.524757028 1.24420404 + vertex 2.83174801 0.524757028 0.562609017 + vertex 2.33923101 0.936287999 0.769291997 + endloop + endfacet + + facet normal -0.69412154 -0.67406857 -0.25263983 + outer loop + vertex 2.83174801 0.524757028 0.562609017 + vertex 2.58366799 0.524757028 1.24420404 + vertex 3.11489511 -0.0620529987 1.35033894 + endloop + endfacet + + facet normal -0.241510943 0.3801229 -0.892848849 + outer loop + vertex 2.58366799 -0.648863018 1.24420404 + vertex 3.11489511 -0.0620529987 1.35033894 + vertex 2.55083203 -0.0620529987 1.50291502 + endloop + endfacet + + facet normal -0.680310249 0.689830244 -0.247613087 + outer loop + vertex 2.83174801 -0.648863018 0.562609017 + vertex 2.58366799 -0.648863018 1.24420404 + vertex 2.33923101 -1.060395 0.769291997 + endloop + endfacet + + facet normal -0.950931132 0.278835028 0.134094015 + outer loop + vertex 3.11489511 -0.0620529987 1.35033894 + vertex 2.83174801 -0.648863018 0.562609017 + vertex 2.97289085 -0.0620529987 0.343317986 + endloop + endfacet + + facet normal -0.69412154 0.67406857 -0.25263983 + outer loop + vertex 2.83174801 -0.648863018 0.562609017 + vertex 3.11489511 -0.0620529987 1.35033894 + vertex 2.58366799 -0.648863018 1.24420404 + endloop + endfacet + + facet normal 0.00512400037 0.591441095 -0.806332052 + outer loop + vertex 1.85664499 -1.01153195 1.21811604 + vertex 1.98102093 -0.424721986 1.64932895 + vertex 1.31394804 -0.679062009 1.45853198 + endloop + endfacet + + facet normal -0.375215858 0.787360668 -0.489158779 + outer loop + vertex 2.58366799 -0.648863018 1.24420404 + vertex 1.85664499 -1.01153195 1.21811604 + vertex 2.33923101 -1.060395 0.769291997 + endloop + endfacet + + facet normal -0.432703078 0.343308091 -0.83361119 + outer loop + vertex 1.98102093 -0.424721986 1.64932895 + vertex 2.58366799 -0.648863018 1.24420404 + vertex 2.55083203 -0.0620529987 1.50291502 + endloop + endfacet + + facet normal -0.275769085 0.606463194 -0.745757163 + outer loop + vertex 2.58366799 -0.648863018 1.24420404 + vertex 1.98102093 -0.424721986 1.64932895 + vertex 1.85664499 -1.01153195 1.21811604 + endloop + endfacet + + facet normal 0.415251076 0.870015144 -0.265782058 + outer loop + vertex 0.907127976 -0.687533975 0.795193017 + vertex 1.85664499 -1.01153195 1.21811604 + vertex 1.31394804 -0.679062009 1.45853198 + endloop + endfacet + + facet normal 0.529567182 0.823971212 -0.201569065 + outer loop + vertex 1.65540099 -1.23567402 0.520398021 + vertex 0.907127976 -0.687533975 0.795193017 + vertex 0.766146004 -0.721850991 0.284518987 + endloop + endfacet + + facet normal -0.148454979 0.953196883 -0.263394982 + outer loop + vertex 1.85664499 -1.01153195 1.21811604 + vertex 1.65540099 -1.23567402 0.520398021 + vertex 2.33923101 -1.060395 0.769291997 + endloop + endfacet + + facet normal 0.447698891 0.805645823 -0.387943923 + outer loop + vertex 1.65540099 -1.23567402 0.520398021 + vertex 1.85664499 -1.01153195 1.21811604 + vertex 0.907127976 -0.687533975 0.795193017 + endloop + endfacet + + facet normal 0.410494119 0.855419278 0.315836102 + outer loop + vertex 1.04242694 -0.687533975 -0.167512998 + vertex 1.65540099 -1.23567402 0.520398021 + vertex 0.766146004 -0.721850991 0.284518987 + endloop + endfacet + + facet normal 0.11889904 0.867570281 0.482892126 + outer loop + vertex 2.2580471 -1.01153195 0.115272999 + vertex 1.04242694 -0.687533975 -0.167512998 + vertex 1.99685407 -0.679062009 -0.417735994 + endloop + endfacet + + facet normal -0.283029974 0.953196943 0.106346995 + outer loop + vertex 1.65540099 -1.23567402 0.520398021 + vertex 2.2580471 -1.01153195 0.115272999 + vertex 2.33923101 -1.060395 0.769291997 + endloop + endfacet + + facet normal 0.0833719745 0.8142398 0.574510813 + outer loop + vertex 2.2580471 -1.01153195 0.115272999 + vertex 1.65540099 -1.23567402 0.520398021 + vertex 1.04242694 -0.687533975 -0.167512998 + endloop + endfacet + + facet normal -0.514374912 0.591440976 0.620979965 + outer loop + vertex 2.63050294 -0.424721986 -0.135107994 + vertex 2.2580471 -1.01153195 0.115272999 + vertex 1.99685407 -0.679062009 -0.417735994 + endloop + endfacet + + facet normal -0.867304265 0.343309104 0.360447109 + outer loop + vertex 2.83174801 -0.648863018 0.562609017 + vertex 2.63050294 -0.424721986 -0.135107994 + vertex 2.97289085 -0.0620529987 0.343317986 + endloop + endfacet + + facet normal -0.601856828 0.787360728 0.133533955 + outer loop + vertex 2.2580471 -1.01153195 0.115272999 + vertex 2.83174801 -0.648863018 0.562609017 + vertex 2.33923101 -1.060395 0.769291997 + endloop + endfacet + + facet normal -0.690615118 0.606463134 0.394022048 + outer loop + vertex 2.83174801 -0.648863018 0.562609017 + vertex 2.2580471 -1.01153195 0.115272999 + vertex 2.63050294 -0.424721986 -0.135107994 + endloop + endfacet + + facet normal -0.480426788 0.269474924 0.834609628 + outer loop + vertex 2.05680299 -0.0620529987 -0.582445025 + vertex 2.63050294 -0.424721986 -0.135107994 + vertex 1.99685407 -0.679062009 -0.417735994 + endloop + endfacet + + facet normal -0.480426788 -0.269474924 0.834609628 + outer loop + vertex 2.63050294 0.300615013 -0.135107994 + vertex 2.05680299 -0.0620529987 -0.582445025 + vertex 1.99685407 0.554956019 -0.417735994 + endloop + endfacet + + facet normal -0.813206792 0 0.581974864 + outer loop + vertex 2.63050294 -0.424721986 -0.135107994 + vertex 2.63050294 0.300615013 -0.135107994 + vertex 2.97289085 -0.0620529987 0.343317986 + endloop + endfacet + + facet normal -0.614903986 0 0.788601995 + outer loop + vertex 2.63050294 0.300615013 -0.135107994 + vertex 2.63050294 -0.424721986 -0.135107994 + vertex 2.05680299 -0.0620529987 -0.582445025 + endloop + endfacet + + facet normal 0.0294839889 0.255127907 0.966457665 + outer loop + vertex 0.900376976 -0.275397986 -0.490844995 + vertex 2.05680299 -0.0620529987 -0.582445025 + vertex 1.99685407 -0.679062009 -0.417735994 + endloop + endfacet + + facet normal 0.514054716 0 0.857757449 + outer loop + vertex 0.900376976 0.234028995 -0.490844995 + vertex 0.900376976 -0.275397986 -0.490844995 + vertex 0.524474978 -0.0206840001 -0.265567005 + endloop + endfacet + + facet normal 0.0107639972 -0.256923914 0.966371715 + outer loop + vertex 2.05680299 -0.0620529987 -0.582445025 + vertex 0.900376976 0.234028995 -0.490844995 + vertex 1.99685407 0.554956019 -0.417735994 + endloop + endfacet + + facet normal 0.0789619684 0 0.996877551 + outer loop + vertex 0.900376976 0.234028995 -0.490844995 + vertex 2.05680299 -0.0620529987 -0.582445025 + vertex 0.900376976 -0.275397986 -0.490844995 + endloop + endfacet + + facet normal 0.647883713 -0.364618838 0.668804705 + outer loop + vertex 0.535234988 0.391451001 -0.0513020009 + vertex 0.900376976 0.234028995 -0.490844995 + vertex 0.524474978 -0.0206840001 -0.265567005 + endloop + endfacet + + facet normal 0.483248174 -0.79979831 0.356081158 + outer loop + vertex 1.04242694 0.646165013 -0.167512998 + vertex 0.535234988 0.391451001 -0.0513020009 + vertex 0.766146004 0.680482984 0.284518987 + endloop + endfacet + + facet normal 0.136986926 -0.640222669 0.755876541 + outer loop + vertex 0.900376976 0.234028995 -0.490844995 + vertex 1.04242694 0.646165013 -0.167512998 + vertex 1.99685407 0.554956019 -0.417735994 + endloop + endfacet + + facet normal 0.462180048 -0.640478075 0.613333046 + outer loop + vertex 1.04242694 0.646165013 -0.167512998 + vertex 0.900376976 0.234028995 -0.490844995 + vertex 0.535234988 0.391451001 -0.0513020009 + endloop + endfacet + + facet normal 0.355838954 -0.890011907 0.285056978 + outer loop + vertex 1.65540099 1.11156702 0.520398021 + vertex 1.04242694 0.646165013 -0.167512998 + vertex 0.766146004 0.680482984 0.284518987 + endloop + endfacet + + facet normal -0.283029974 -0.953196943 0.106346995 + outer loop + vertex 2.2580471 0.887426019 0.115272999 + vertex 1.65540099 1.11156702 0.520398021 + vertex 2.33923101 0.936287999 0.769291997 + endloop + endfacet + + facet normal 0.0517080128 -0.85851717 0.510171115 + outer loop + vertex 1.04242694 0.646165013 -0.167512998 + vertex 2.2580471 0.887426019 0.115272999 + vertex 1.99685407 0.554956019 -0.417735994 + endloop + endfacet + + facet normal 0.0438020192 -0.845141351 0.532745183 + outer loop + vertex 2.2580471 0.887426019 0.115272999 + vertex 1.04242694 0.646165013 -0.167512998 + vertex 1.65540099 1.11156702 0.520398021 + endloop + endfacet + + facet normal -0.514374912 -0.591440976 0.620979965 + outer loop + vertex 2.2580471 0.887426019 0.115272999 + vertex 2.63050294 0.300615013 -0.135107994 + vertex 1.99685407 0.554956019 -0.417735994 + endloop + endfacet + + facet normal -0.601856947 -0.787360847 0.133532986 + outer loop + vertex 2.83174801 0.524757028 0.562609017 + vertex 2.2580471 0.887426019 0.115272999 + vertex 2.33923101 0.936287999 0.769291997 + endloop + endfacet + + facet normal -0.867304265 -0.343309104 0.360447109 + outer loop + vertex 2.63050294 0.300615013 -0.135107994 + vertex 2.83174801 0.524757028 0.562609017 + vertex 2.97289085 -0.0620529987 0.343317986 + endloop + endfacet + + facet normal -0.690614581 -0.606463671 0.394021749 + outer loop + vertex 2.83174801 0.524757028 0.562609017 + vertex 2.63050294 0.300615013 -0.135107994 + vertex 2.2580471 0.887426019 0.115272999 + endloop + endfacet + + facet normal 0.964517593 -0.227995917 -0.133129939 + outer loop + vertex 0.451615006 0.391451001 0.543682992 + vertex 0.309563994 -0.0206840001 0.220350996 + vertex 0.382212013 -0.0206840001 0.746681988 + endloop + endfacet + + facet normal 0.716925919 -0.689829946 0.100756995 + outer loop + vertex 0.535234988 0.391451001 -0.0513020009 + vertex 0.451615006 0.391451001 0.543682992 + vertex 0.766146004 0.680482984 0.284518987 + endloop + endfacet + + facet normal 0.890458107 -0.227996066 0.393830061 + outer loop + vertex 0.309563994 -0.0206840001 0.220350996 + vertex 0.535234988 0.391451001 -0.0513020009 + vertex 0.524474978 -0.0206840001 -0.265567005 + endloop + endfacet + + facet normal 0.90287751 -0.41074425 0.126891062 + outer loop + vertex 0.535234988 0.391451001 -0.0513020009 + vertex 0.309563994 -0.0206840001 0.220350996 + vertex 0.451615006 0.391451001 0.543682992 + endloop + endfacet + + facet normal 0.964517593 0.227995917 -0.133129939 + outer loop + vertex 0.309563994 -0.0206840001 0.220350996 + vertex 0.451615006 -0.432819992 0.543682992 + vertex 0.382212013 -0.0206840001 0.746681988 + endloop + endfacet + + facet normal 0.890458107 0.227996066 0.393830061 + outer loop + vertex 0.535234988 -0.432819992 -0.0513020009 + vertex 0.309563994 -0.0206840001 0.220350996 + vertex 0.524474978 -0.0206840001 -0.265567005 + endloop + endfacet + + facet normal 0.716925919 0.689829946 0.100756995 + outer loop + vertex 0.451615006 -0.432819992 0.543682992 + vertex 0.535234988 -0.432819992 -0.0513020009 + vertex 0.766146004 -0.721850991 0.284518987 + endloop + endfacet + + facet normal 0.90287751 0.41074425 0.126891062 + outer loop + vertex 0.535234988 -0.432819992 -0.0513020009 + vertex 0.451615006 -0.432819992 0.543682992 + vertex 0.309563994 -0.0206840001 0.220350996 + endloop + endfacet + + facet normal 0.807133079 0.364618987 -0.46431601 + outer loop + vertex 0.451615006 -0.432819992 0.543682992 + vertex 0.681457996 -0.275397986 1.06684601 + vertex 0.382212013 -0.0206840001 0.746681988 + endloop + endfacet + + facet normal 0.562677264 0.79979831 -0.209086075 + outer loop + vertex 0.907127976 -0.687533975 0.795193017 + vertex 0.451615006 -0.432819992 0.543682992 + vertex 0.766146004 -0.721850991 0.284518987 + endloop + endfacet + + facet normal 0.657568097 0.631190121 -0.411343098 + outer loop + vertex 0.681457996 -0.275397986 1.06684601 + vertex 0.907127976 -0.687533975 0.795193017 + vertex 1.31394804 -0.679062009 1.45853198 + endloop + endfacet + + facet normal 0.613333642 0.640477657 -0.462179691 + outer loop + vertex 0.907127976 -0.687533975 0.795193017 + vertex 0.681457996 -0.275397986 1.06684601 + vertex 0.451615006 -0.432819992 0.543682992 + endloop + endfacet + + facet normal 0.188311026 0.645559072 0.740130126 + outer loop + vertex 1.04242694 -0.687533975 -0.167512998 + vertex 0.900376976 -0.275397986 -0.490844995 + vertex 1.99685407 -0.679062009 -0.417735994 + endloop + endfacet + + facet normal 0.483248174 0.79979831 0.356081158 + outer loop + vertex 0.535234988 -0.432819992 -0.0513020009 + vertex 1.04242694 -0.687533975 -0.167512998 + vertex 0.766146004 -0.721850991 0.284518987 + endloop + endfacet + + facet normal 0.647883177 0.364619046 0.668805122 + outer loop + vertex 0.900376976 -0.275397986 -0.490844995 + vertex 0.535234988 -0.432819992 -0.0513020009 + vertex 0.524474978 -0.0206840001 -0.265567005 + endloop + endfacet + + facet normal 0.462180048 0.640478075 0.613333046 + outer loop + vertex 0.535234988 -0.432819992 -0.0513020009 + vertex 0.900376976 -0.275397986 -0.490844995 + vertex 1.04242694 -0.687533975 -0.167512998 + endloop + endfacet + + facet normal -0.432697058 -0.859040201 0.273538053 + outer loop + vertex -0.631551027 0.764486015 0.547107995 + vertex -0.438125014 0.541163027 0.151739001 + vertex -1.01341605 0.803828001 0.0666079968 + endloop + endfacet + + facet normal -0.541726649 -0.466254741 -0.69938457 + outer loop + vertex -0.0606839992 0.437810004 0.322710991 + vertex -0.631551027 0.764486015 0.547107995 + vertex -0.291319996 0.284505993 0.603558004 + endloop + endfacet + + facet normal -0.175982982 -0.964926958 -0.194796994 + outer loop + vertex -0.438125014 0.541163027 0.151739001 + vertex -0.0606839992 0.437810004 0.322710991 + vertex 0.253931999 0.460341007 -0.0731239989 + endloop + endfacet + + facet normal -0.37848711 -0.872899234 0.307887077 + outer loop + vertex -0.0606839992 0.437810004 0.322710991 + vertex -0.438125014 0.541163027 0.151739001 + vertex -0.631551027 0.764486015 0.547107995 + endloop + endfacet + + facet normal 0.429853022 -0.805670023 -0.407581031 + outer loop + vertex -1.13857305 0.472478002 0.589594007 + vertex -0.631551027 0.764486015 0.547107995 + vertex -1.01341605 0.803828001 0.0666079968 + endloop + endfacet + + facet normal 0.334310085 -0.263623089 -0.904842317 + outer loop + vertex -0.75335598 0.292008013 0.78449899 + vertex -1.13857305 0.472478002 0.589594007 + vertex -1.13703001 0 0.727819026 + endloop + endfacet + + facet normal -0.34672612 -0.348206103 -0.870938241 + outer loop + vertex -0.631551027 0.764486015 0.547107995 + vertex -0.75335598 0.292008013 0.78449899 + vertex -0.291319996 0.284505993 0.603558004 + endloop + endfacet + + facet normal 0.205720052 -0.481182128 -0.852140188 + outer loop + vertex -0.75335598 0.292008013 0.78449899 + vertex -0.631551027 0.764486015 0.547107995 + vertex -1.13857305 0.472478002 0.589594007 + endloop + endfacet + + facet normal 0.146141946 0 -0.989263594 + outer loop + vertex -0.75335598 -0.292008013 0.78449899 + vertex -0.75335598 0.292008013 0.78449899 + vertex -1.13703001 0 0.727819026 + endloop + endfacet + + facet normal -0.353928059 0.280805022 -0.892123103 + outer loop + vertex -0.265545011 0 0.682883978 + vertex -0.75335598 -0.292008013 0.78449899 + vertex -0.291319996 -0.284505993 0.603558004 + endloop + endfacet + + facet normal -0.353928059 -0.280805022 -0.892123103 + outer loop + vertex -0.75335598 0.292008013 0.78449899 + vertex -0.265545011 0 0.682883978 + vertex -0.291319996 0.284505993 0.603558004 + endloop + endfacet + + facet normal -0.203930095 0 -0.978985429 + outer loop + vertex -0.265545011 0 0.682883978 + vertex -0.75335598 0.292008013 0.78449899 + vertex -0.75335598 -0.292008013 0.78449899 + endloop + endfacet + + facet normal -0.370298028 -0.280459017 -0.885563135 + outer loop + vertex -0.265545011 0 0.682883978 + vertex 0.369307995 0.167228997 0.364459008 + vertex -0.291319996 0.284505993 0.603558004 + endloop + endfacet + + facet normal -0.370298028 0.280459017 -0.885563135 + outer loop + vertex 0.369307995 -0.167228997 0.364459008 + vertex -0.265545011 0 0.682883978 + vertex -0.291319996 -0.284505993 0.603558004 + endloop + endfacet + + facet normal -0.60111922 0 -0.799159288 + outer loop + vertex 0.369307995 0.167228997 0.364459008 + vertex 0.369307995 -0.167228997 0.364459008 + vertex 0.662828028 0 0.143675998 + endloop + endfacet + + facet normal -0.448338032 0 -0.893864095 + outer loop + vertex 0.369307995 -0.167228997 0.364459008 + vertex 0.369307995 0.167228997 0.364459008 + vertex -0.265545011 0 0.682883978 + endloop + endfacet + + facet normal -0.355542064 -0.666160047 -0.655607045 + outer loop + vertex 0.369307995 0.167228997 0.364459008 + vertex -0.0606839992 0.437810004 0.322710991 + vertex -0.291319996 0.284505993 0.603558004 + endloop + endfacet + + facet normal -0.681211829 -0.339001894 -0.648866773 + outer loop + vertex 0.581242025 0.389086008 0.0260499995 + vertex 0.369307995 0.167228997 0.364459008 + vertex 0.662828028 0 0.143675998 + endloop + endfacet + + facet normal -0.156875014 -0.971084058 -0.179961026 + outer loop + vertex -0.0606839992 0.437810004 0.322710991 + vertex 0.581242025 0.389086008 0.0260499995 + vertex 0.253931999 0.460341007 -0.0731239989 + endloop + endfacet + + facet normal -0.354409099 -0.664684117 -0.657715142 + outer loop + vertex 0.581242025 0.389086008 0.0260499995 + vertex -0.0606839992 0.437810004 0.322710991 + vertex 0.369307995 0.167228997 0.364459008 + endloop + endfacet + + facet normal -0.967071533 -0.233435884 -0.101391949 + outer loop + vertex 0.700780988 0 -0.218314007 + vertex 0.581242025 0.389086008 0.0260499995 + vertex 0.662828028 0 0.143675998 + endloop + endfacet + + facet normal -0.722780406 -0.23343581 0.650458395 + outer loop + vertex 0.460438013 0.389086008 -0.345746011 + vertex 0.700780988 0 -0.218314007 + vertex 0.457304001 0 -0.488862008 + endloop + endfacet + + facet normal -0.234045014 -0.969247162 0.0760460123 + outer loop + vertex 0.581242025 0.389086008 0.0260499995 + vertex 0.460438013 0.389086008 -0.345746011 + vertex 0.253931999 0.460341007 -0.0731239989 + endloop + endfacet + + facet normal -0.855295599 -0.437309831 0.277901888 + outer loop + vertex 0.460438013 0.389086008 -0.345746011 + vertex 0.581242025 0.389086008 0.0260499995 + vertex 0.700780988 0 -0.218314007 + endloop + endfacet + + facet normal -0.967071533 0.233435884 -0.101391949 + outer loop + vertex 0.581242025 -0.389086008 0.0260499995 + vertex 0.700780988 0 -0.218314007 + vertex 0.662828028 0 0.143675998 + endloop + endfacet + + facet normal -0.234045014 0.969247162 0.0760460123 + outer loop + vertex 0.460438013 -0.389086008 -0.345746011 + vertex 0.581242025 -0.389086008 0.0260499995 + vertex 0.253931999 -0.460341007 -0.0731239989 + endloop + endfacet + + facet normal -0.722780406 0.233436137 0.650458336 + outer loop + vertex 0.700780988 0 -0.218314007 + vertex 0.460438013 -0.389086008 -0.345746011 + vertex 0.457304001 0 -0.488862008 + endloop + endfacet + + facet normal -0.855295599 0.437309831 0.277901888 + outer loop + vertex 0.460438013 -0.389086008 -0.345746011 + vertex 0.700780988 0 -0.218314007 + vertex 0.581242025 -0.389086008 0.0260499995 + endloop + endfacet + + facet normal -0.355541825 0.66615957 -0.655607641 + outer loop + vertex -0.0606839992 -0.437810004 0.322710991 + vertex 0.369307995 -0.167228997 0.364459008 + vertex -0.291319996 -0.284505993 0.603558004 + endloop + endfacet + + facet normal -0.156875029 0.971084177 -0.179960027 + outer loop + vertex 0.581242025 -0.389086008 0.0260499995 + vertex -0.0606839992 -0.437810004 0.322710991 + vertex 0.253931999 -0.460341007 -0.0731239989 + endloop + endfacet + + facet normal -0.681211829 0.339001894 -0.648866773 + outer loop + vertex 0.369307995 -0.167228997 0.364459008 + vertex 0.581242025 -0.389086008 0.0260499995 + vertex 0.662828028 0 0.143675998 + endloop + endfacet + + facet normal -0.354409099 0.664684117 -0.657715142 + outer loop + vertex 0.581242025 -0.389086008 0.0260499995 + vertex 0.369307995 -0.167228997 0.364459008 + vertex -0.0606839992 -0.437810004 0.322710991 + endloop + endfacet + + facet normal -0.541726649 0.466254741 -0.69938457 + outer loop + vertex -0.631551027 -0.764486015 0.547107995 + vertex -0.0606839992 -0.437810004 0.322710991 + vertex -0.291319996 -0.284505993 0.603558004 + endloop + endfacet + + facet normal -0.432697058 0.859040201 0.273538053 + outer loop + vertex -0.438125014 -0.541163027 0.151739001 + vertex -0.631551027 -0.764486015 0.547107995 + vertex -1.01341605 -0.803828001 0.0666079968 + endloop + endfacet + + facet normal -0.175982982 0.964926958 -0.194796994 + outer loop + vertex -0.0606839992 -0.437810004 0.322710991 + vertex -0.438125014 -0.541163027 0.151739001 + vertex 0.253931999 -0.460341007 -0.0731239989 + endloop + endfacet + + facet normal -0.37848711 0.872899234 0.307887077 + outer loop + vertex -0.438125014 -0.541163027 0.151739001 + vertex -0.0606839992 -0.437810004 0.322710991 + vertex -0.631551027 -0.764486015 0.547107995 + endloop + endfacet + + facet normal -0.411905199 0.910833538 -0.0267650131 + outer loop + vertex -0.903297007 -0.764486015 -0.289241999 + vertex -0.438125014 -0.541163027 0.151739001 + vertex -1.01341605 -0.803828001 0.0666079968 + endloop + endfacet + + facet normal -0.188856944 0.147191957 0.970910728 + outer loop + vertex -0.233263001 -0.437810004 -0.208434999 + vertex -0.903297007 -0.764486015 -0.289241999 + vertex -0.584930003 -0.284505993 -0.300080001 + endloop + endfacet + + facet normal -0.0278740041 0.964927137 0.261034042 + outer loop + vertex -0.438125014 -0.541163027 0.151739001 + vertex -0.233263001 -0.437810004 -0.208434999 + vertex 0.253931999 -0.460341007 -0.0731239989 + endloop + endfacet + + facet normal -0.439015031 0.898443103 0.00810600072 + outer loop + vertex -0.233263001 -0.437810004 -0.208434999 + vertex -0.438125014 -0.541163027 0.151739001 + vertex -0.903297007 -0.764486015 -0.289241999 + endloop + endfacet + + facet normal -0.021137001 0.971084058 0.237800032 + outer loop + vertex -0.233263001 -0.437810004 -0.208434999 + vertex 0.460438013 -0.389086008 -0.345746011 + vertex 0.253931999 -0.460341007 -0.0731239989 + endloop + endfacet + + facet normal 0.645767212 -0.710955262 0.278437078 + outer loop + vertex -0.903297007 0.764486015 -0.289241999 + vertex -1.33845997 0.472478002 -0.0255929995 + vertex -1.01341605 0.803828001 0.0666079968 + endloop + endfacet + + facet normal -0.411905199 -0.910833538 -0.0267650131 + outer loop + vertex -0.438125014 0.541163027 0.151739001 + vertex -0.903297007 0.764486015 -0.289241999 + vertex -1.01341605 0.803828001 0.0666079968 + endloop + endfacet + + facet normal -0.0278739929 -0.964926779 0.261034936 + outer loop + vertex -0.233263001 0.437810004 -0.208434999 + vertex -0.438125014 0.541163027 0.151739001 + vertex 0.253931999 0.460341007 -0.0731239989 + endloop + endfacet + + facet normal -0.188856944 -0.147191957 0.970910728 + outer loop + vertex -0.903297007 0.764486015 -0.289241999 + vertex -0.233263001 0.437810004 -0.208434999 + vertex -0.584930003 0.284505993 -0.300080001 + endloop + endfacet + + facet normal -0.439015031 -0.898443103 0.00810600072 + outer loop + vertex -0.233263001 0.437810004 -0.208434999 + vertex -0.903297007 0.764486015 -0.289241999 + vertex -0.438125014 0.541163027 0.151739001 + endloop + endfacet + + facet normal -0.021137001 -0.971084058 0.237800032 + outer loop + vertex 0.460438013 0.389086008 -0.345746011 + vertex -0.233263001 0.437810004 -0.208434999 + vertex 0.253931999 0.460341007 -0.0731239989 + endloop + endfacet + + facet normal 0.719865859 -0.192728966 -0.66681987 + outer loop + vertex -1.13857305 0.472478002 0.589594007 + vertex -1.37665105 0 0.469136 + vertex -1.13703001 0 0.727819026 + endloop + endfacet + + facet normal 0.725731134 -0.646305084 -0.23580502 + outer loop + vertex -1.33845997 0.472478002 -0.0255929995 + vertex -1.13857305 0.472478002 0.589594007 + vertex -1.01341605 0.803828001 0.0666079968 + endloop + endfacet + + facet normal 0.986233354 -0.150788054 -0.0678730309 + outer loop + vertex -1.37665105 0 0.469136 + vertex -1.33845997 0.472478002 -0.0255929995 + vertex -1.41845703 0 -0.138327003 + endloop + endfacet + + facet normal 0.882898092 -0.371747017 -0.286871016 + outer loop + vertex -1.33845997 0.472478002 -0.0255929995 + vertex -1.37665105 0 0.469136 + vertex -1.13857305 0.472478002 0.589594007 + endloop + endfacet + + facet normal 0.719865859 0.192728966 -0.66681987 + outer loop + vertex -1.37665105 0 0.469136 + vertex -1.13857305 -0.472478002 0.589594007 + vertex -1.13703001 0 0.727819026 + endloop + endfacet + + facet normal 0.986233175 0.150789022 -0.067873016 + outer loop + vertex -1.33845997 -0.472478002 -0.0255929995 + vertex -1.37665105 0 0.469136 + vertex -1.41845703 0 -0.138327003 + endloop + endfacet + + facet normal 0.725731134 0.646305084 -0.23580502 + outer loop + vertex -1.13857305 -0.472478002 0.589594007 + vertex -1.33845997 -0.472478002 -0.0255929995 + vertex -1.01341605 -0.803828001 0.0666079968 + endloop + endfacet + + facet normal 0.882897794 0.371747911 -0.286870927 + outer loop + vertex -1.33845997 -0.472478002 -0.0255929995 + vertex -1.13857305 -0.472478002 0.589594007 + vertex -1.37665105 0 0.469136 + endloop + endfacet + + facet normal 0.334310085 0.263623089 -0.904842317 + outer loop + vertex -1.13857305 -0.472478002 0.589594007 + vertex -0.75335598 -0.292008013 0.78449899 + vertex -1.13703001 0 0.727819026 + endloop + endfacet + + facet normal 0.429853022 0.805670023 -0.407581031 + outer loop + vertex -0.631551027 -0.764486015 0.547107995 + vertex -1.13857305 -0.472478002 0.589594007 + vertex -1.01341605 -0.803828001 0.0666079968 + endloop + endfacet + + facet normal -0.34672612 0.348206103 -0.870938241 + outer loop + vertex -0.75335598 -0.292008013 0.78449899 + vertex -0.631551027 -0.764486015 0.547107995 + vertex -0.291319996 -0.284505993 0.603558004 + endloop + endfacet + + facet normal 0.205720052 0.481182128 -0.852140188 + outer loop + vertex -0.631551027 -0.764486015 0.547107995 + vertex -0.75335598 -0.292008013 0.78449899 + vertex -1.13857305 -0.472478002 0.589594007 + endloop + endfacet + + facet normal 0.645767868 0.710954785 0.278436899 + outer loop + vertex -1.33845997 -0.472478002 -0.0255929995 + vertex -0.903297007 -0.764486015 -0.289241999 + vertex -1.01341605 -0.803828001 0.0666079968 + endloop + endfacet + + facet normal -0.0213829875 -0.399431795 0.916513443 + outer loop + vertex -1.12877297 -0.45877099 -0.0446330011 + vertex -1.01783097 -0.351024002 0.00491400016 + vertex -0.964388013 -0.470328987 -0.0458349995 + endloop + endfacet + + facet normal 0.781845033 0.449936032 0.431597054 + outer loop + vertex -2.92025304 -3.89943194 -1.53437805 + vertex -2.91697907 -3.88280702 -1.55764103 + vertex -2.90600705 -3.90321898 -1.55623698 + endloop + endfacet + + facet normal -0.945810378 0.167999059 -0.277883112 + outer loop + vertex -1.23226094 -1.14515603 0.457924992 + vertex -1.23026299 -1.22389793 0.403519988 + vertex -0.964388013 -0.470328987 -0.0458349995 + endloop + endfacet + + facet normal -0.805882096 -0.113844007 -0.581028044 + outer loop + vertex -1.01783097 -0.351024002 0.00491400016 + vertex -1.23226094 -1.14515603 0.457924992 + vertex -0.964388013 -0.470328987 -0.0458349995 + endloop + endfacet + + facet normal -0.942644656 0.173225939 -0.285330921 + outer loop + vertex -1.23226094 -1.14515603 0.457924992 + vertex -1.57549906 -1.69697499 1.256863 + vertex -1.23026299 -1.22389793 0.403519988 + endloop + endfacet + + facet normal -0.941975474 0.154292092 -0.298121184 + outer loop + vertex -1.23226094 -1.14515603 0.457924992 + vertex -1.59044802 -1.66966891 1.31822896 + vertex -1.57549906 -1.69697499 1.256863 + endloop + endfacet + + facet normal -0.747970819 0.285769939 -0.599061906 + outer loop + vertex -1.67847705 -1.86322808 1.30613101 + vertex -1.61559892 -1.845137 1.23625302 + vertex -1.57549906 -1.69697499 1.256863 + endloop + endfacet + + facet normal -0.832128823 0.402304918 -0.381722927 + outer loop + vertex -1.59044802 -1.66966891 1.31822896 + vertex -1.67847705 -1.86322808 1.30613101 + vertex -1.57549906 -1.69697499 1.256863 + endloop + endfacet + + facet normal -0.673662126 0.581969082 -0.455512017 + outer loop + vertex -1.67847705 -1.86322808 1.30613101 + vertex -2.09431696 -2.85528994 0.653648019 + vertex -1.61559892 -1.845137 1.23625302 + endloop + endfacet + + facet normal -0.55654794 0.606640875 -0.567662954 + outer loop + vertex -1.67847705 -1.86322808 1.30613101 + vertex -2.17327309 -2.90178013 0.681376994 + vertex -2.09431696 -2.85528994 0.653648019 + endloop + endfacet + + facet normal -0.405423969 0.861933947 -0.30446896 + outer loop + vertex -2.70630598 -3.77558994 -1.13675594 + vertex -2.66079307 -3.77662301 -1.20028496 + vertex -2.09431696 -2.85528994 0.653648019 + endloop + endfacet + + facet normal -0.551747799 0.803281784 -0.224304914 + outer loop + vertex -2.17327309 -2.90178013 0.681376994 + vertex -2.70630598 -3.77558994 -1.13675594 + vertex -2.09431696 -2.85528994 0.653648019 + endloop + endfacet + + facet normal -0.23088105 0.956007123 -0.180954024 + outer loop + vertex -2.70630598 -3.77558994 -1.13675594 + vertex -2.90600705 -3.90321898 -1.55623698 + vertex -2.66079307 -3.77662301 -1.20028496 + endloop + endfacet + + facet normal -0.112019025 0.964274228 -0.240056053 + outer loop + vertex -2.70630598 -3.77558994 -1.13675594 + vertex -2.92025304 -3.89943194 -1.53437805 + vertex -2.90600705 -3.90321898 -1.55623698 + endloop + endfacet + + facet normal -0.0213859938 -0.399428874 0.916514754 + outer loop + vertex -1.12877297 -0.45877099 -0.0446330011 + vertex -1.15481496 -0.312853992 0.018352 + vertex -1.01783097 -0.351024002 0.00491400016 + endloop + endfacet + + facet normal 0.781792939 0.449993968 0.431630969 + outer loop + vertex -2.93203402 -3.88312697 -1.530038 + vertex -2.91697907 -3.88280702 -1.55764103 + vertex -2.92025304 -3.89943194 -1.53437805 + endloop + endfacet + + facet normal -0.208629057 -0.441484153 -0.872677267 + outer loop + vertex -1.15481496 -0.312853992 0.018352 + vertex -1.23226094 -1.14515603 0.457924992 + vertex -1.01783097 -0.351024002 0.00491400016 + endloop + endfacet + + facet normal -0.460987806 -0.38050884 -0.801687777 + outer loop + vertex -1.15481496 -0.312853992 0.018352 + vertex -1.30323696 -1.08182395 0.468677998 + vertex -1.23226094 -1.14515603 0.457924992 + endloop + endfacet + + facet normal -0.578610063 -0.566799045 -0.586471975 + outer loop + vertex -1.63677692 -1.63637698 1.331761 + vertex -1.59044802 -1.66966891 1.31822896 + vertex -1.23226094 -1.14515603 0.457924992 + endloop + endfacet + + facet normal -0.587184072 -0.558590055 -0.585826039 + outer loop + vertex -1.30323696 -1.08182395 0.468677998 + vertex -1.63677692 -1.63637698 1.331761 + vertex -1.23226094 -1.14515603 0.457924992 + endloop + endfacet + + facet normal -0.181254938 0.14324595 -0.972947657 + outer loop + vertex -1.63677692 -1.63637698 1.331761 + vertex -1.67847705 -1.86322808 1.30613101 + vertex -1.59044802 -1.66966891 1.31822896 + endloop + endfacet + + facet normal -0.106968015 0.131019011 -0.985592186 + outer loop + vertex -1.63677692 -1.63637698 1.331761 + vertex -1.76449001 -1.82381392 1.32070494 + vertex -1.67847705 -1.86322808 1.30613101 + endloop + endfacet + + facet normal 0.127293006 0.466052979 -0.875551939 + outer loop + vertex -2.26325607 -2.86937809 0.685541987 + vertex -2.17327309 -2.90178013 0.681376994 + vertex -1.67847705 -1.86322808 1.30613101 + endloop + endfacet + + facet normal 0.0775939971 0.490328014 -0.86807698 + outer loop + vertex -1.76449001 -1.82381392 1.32070494 + vertex -2.26325607 -2.86937809 0.685541987 + vertex -1.67847705 -1.86322808 1.30613101 + endloop + endfacet + + facet normal 0.277229041 0.831728101 -0.481012046 + outer loop + vertex -2.26325607 -2.86937809 0.685541987 + vertex -2.70630598 -3.77558994 -1.13675594 + vertex -2.17327309 -2.90178013 0.681376994 + endloop + endfacet + + facet normal 0.55742979 0.681361735 -0.474360824 + outer loop + vertex -2.26325607 -2.86937809 0.685541987 + vertex -2.73510885 -3.74266911 -1.12331498 + vertex -2.70630598 -3.77558994 -1.13675594 + endloop + endfacet + + facet normal 0.622135937 0.587329924 -0.517677963 + outer loop + vertex -2.93203402 -3.88312697 -1.530038 + vertex -2.92025304 -3.89943194 -1.53437805 + vertex -2.70630598 -3.77558994 -1.13675594 + endloop + endfacet + + facet normal 0.542568088 0.677461982 -0.496654004 + outer loop + vertex -2.73510885 -3.74266911 -1.12331498 + vertex -2.93203402 -3.88312697 -1.530038 + vertex -2.70630598 -3.77558994 -1.13675594 + endloop + endfacet + + facet normal -0.0213840026 -0.39942807 0.916515112 + outer loop + vertex -1.12877297 -0.45877099 -0.0446330011 + vertex -1.27218902 -0.384564996 -0.0156389996 + vertex -1.15481496 -0.312853992 0.018352 + endloop + endfacet + + facet normal 0.781800985 0.449976027 0.431634992 + outer loop + vertex -2.93247795 -3.86658096 -1.54648209 + vertex -2.91697907 -3.88280702 -1.55764103 + vertex -2.93203402 -3.88312697 -1.530038 + endloop + endfacet + + facet normal 0.36412704 -0.521987081 -0.771324098 + outer loop + vertex -1.38974404 -1.08159196 0.427682996 + vertex -1.30323696 -1.08182395 0.468677998 + vertex -1.15481496 -0.312853992 0.018352 + endloop + endfacet + + facet normal 0.515085042 -0.520164073 -0.681261063 + outer loop + vertex -1.27218902 -0.384564996 -0.0156389996 + vertex -1.38974404 -1.08159196 0.427682996 + vertex -1.15481496 -0.312853992 0.018352 + endloop + endfacet + + facet normal 0.218663126 -0.857194424 -0.466266215 + outer loop + vertex -1.38974404 -1.08159196 0.427682996 + vertex -1.63677692 -1.63637698 1.331761 + vertex -1.30323696 -1.08182395 0.468677998 + endloop + endfacet + + facet normal 0.204399109 -0.858234406 -0.470802218 + outer loop + vertex -1.38974404 -1.08159196 0.427682996 + vertex -1.6796 -1.62216997 1.28727102 + vertex -1.63677692 -1.63637698 1.331761 + endloop + endfacet + + facet normal 0.50200671 -0.29407683 -0.813331544 + outer loop + vertex -1.80886698 -1.75657201 1.26900196 + vertex -1.76449001 -1.82381392 1.32070494 + vertex -1.63677692 -1.63637698 1.331761 + endloop + endfacet + + facet normal 0.566596866 -0.451231897 -0.689461887 + outer loop + vertex -1.6796 -1.62216997 1.28727102 + vertex -1.80886698 -1.75657201 1.26900196 + vertex -1.63677692 -1.63637698 1.331761 + endloop + endfacet + + facet normal 0.770831048 0.019108003 -0.636753082 + outer loop + vertex -1.80886698 -1.75657201 1.26900196 + vertex -2.26325607 -2.86937809 0.685541987 + vertex -1.76449001 -1.82381392 1.32070494 + endloop + endfacet + + facet normal 0.701269805 0.0847809836 -0.707836807 + outer loop + vertex -1.80886698 -1.75657201 1.26900196 + vertex -2.29650688 -2.78248215 0.663007021 + vertex -2.26325607 -2.86937809 0.685541987 + endloop + endfacet + + facet normal 0.940782428 -0.327590138 -0.0872530341 + outer loop + vertex -2.72551107 -3.70265102 -1.17007899 + vertex -2.73510885 -3.74266911 -1.12331498 + vertex -2.26325607 -2.86937809 0.685541987 + endloop + endfacet + + facet normal 0.904131114 0.257567018 -0.34089005 + outer loop + vertex -2.29650688 -2.78248215 0.663007021 + vertex -2.72551107 -3.70265102 -1.17007899 + vertex -2.26325607 -2.86937809 0.685541987 + endloop + endfacet + + facet normal 0.841782868 -0.483176947 -0.240710974 + outer loop + vertex -2.72551107 -3.70265102 -1.17007899 + vertex -2.93203402 -3.88312697 -1.530038 + vertex -2.73510885 -3.74266911 -1.12331498 + endloop + endfacet + + facet normal 0.88212204 -0.319912046 -0.345712066 + outer loop + vertex -2.72551107 -3.70265102 -1.17007899 + vertex -2.93247795 -3.86658096 -1.54648209 + vertex -2.93203402 -3.88312697 -1.530038 + endloop + endfacet + + facet normal -0.0213889908 -0.399435818 0.916511655 + outer loop + vertex -1.12877297 -0.45877099 -0.0446330011 + vertex -1.28156805 -0.512152016 -0.0714629963 + vertex -1.27218902 -0.384564996 -0.0156389996 + endloop + endfacet + + facet normal 0.781811774 0.450063884 0.43152386 + outer loop + vertex -2.92125297 -3.86225605 -1.57133007 + vertex -2.91697907 -3.88280702 -1.55764103 + vertex -2.93247795 -3.86658096 -1.54648209 + endloop + endfacet + + facet normal 0.989923298 -0.111154035 0.0877310187 + outer loop + vertex -1.28156805 -0.512152016 -0.0714629963 + vertex -1.38974404 -1.08159196 0.427682996 + vertex -1.27218902 -0.384564996 -0.0156389996 + endloop + endfacet + + facet normal 0.917860508 -0.345539778 -0.195279896 + outer loop + vertex -1.28156805 -0.512152016 -0.0714629963 + vertex -1.42664099 -1.14463401 0.365808994 + vertex -1.38974404 -1.08159196 0.427682996 + endloop + endfacet + + facet normal 0.889153957 -0.457446933 0.0121459989 + outer loop + vertex -1.68667006 -1.63774502 1.21826005 + vertex -1.6796 -1.62216997 1.28727102 + vertex -1.38974404 -1.08159196 0.427682996 + endloop + endfacet + + facet normal 0.870758057 -0.491359055 -0.0186190028 + outer loop + vertex -1.42664099 -1.14463401 0.365808994 + vertex -1.68667006 -1.63774502 1.21826005 + vertex -1.38974404 -1.08159196 0.427682996 + endloop + endfacet + + facet normal 0.712433815 -0.69666481 0.0842389762 + outer loop + vertex -1.68667006 -1.63774502 1.21826005 + vertex -1.80886698 -1.75657201 1.26900196 + vertex -1.6796 -1.62216997 1.28727102 + endloop + endfacet + + facet normal 0.652189136 -0.740312099 -0.163056031 + outer loop + vertex -1.68667006 -1.63774502 1.21826005 + vertex -1.77819002 -1.71213603 1.18995297 + vertex -1.80886698 -1.75657201 1.26900196 + endloop + endfacet + + facet normal 0.860250354 -0.490703166 0.138491064 + outer loop + vertex -2.24798799 -2.70652914 0.630741 + vertex -2.29650688 -2.78248215 0.663007021 + vertex -1.80886698 -1.75657201 1.26900196 + endloop + endfacet + + facet normal 0.882484198 -0.463115126 0.0821340159 + outer loop + vertex -1.77819002 -1.71213603 1.18995297 + vertex -2.24798799 -2.70652914 0.630741 + vertex -1.80886698 -1.75657201 1.26900196 + endloop + endfacet + + facet normal 0.852697134 -0.518845081 0.0608890019 + outer loop + vertex -2.24798799 -2.70652914 0.630741 + vertex -2.72551107 -3.70265102 -1.17007899 + vertex -2.29650688 -2.78248215 0.663007021 + endloop + endfacet + + facet normal 0.671965122 -0.708992124 0.213993043 + outer loop + vertex -2.24798799 -2.70652914 0.630741 + vertex -2.68474102 -3.68566895 -1.24183702 + vertex -2.72551107 -3.70265102 -1.17007899 + endloop + endfacet + + facet normal 0.517961979 -0.851083994 0.0858569965 + outer loop + vertex -2.92125297 -3.86225605 -1.57133007 + vertex -2.93247795 -3.86658096 -1.54648209 + vertex -2.72551107 -3.70265102 -1.17007899 + endloop + endfacet + + facet normal 0.512933195 -0.853763223 0.0893750265 + outer loop + vertex -2.68474102 -3.68566895 -1.24183702 + vertex -2.92125297 -3.86225605 -1.57133007 + vertex -2.72551107 -3.70265102 -1.17007899 + endloop + endfacet + + facet normal -0.0213880036 -0.399439096 0.916510165 + outer loop + vertex -1.12877297 -0.45877099 -0.0446330011 + vertex -1.17588902 -0.599543989 -0.107084997 + vertex -1.28156805 -0.512152016 -0.0714629963 + endloop + endfacet + + facet normal 0.781846225 0.450043142 0.43148312 + outer loop + vertex -2.906811 -3.87340689 -1.58586907 + vertex -2.91697907 -3.88280702 -1.55764103 + vertex -2.92125297 -3.86225605 -1.57133007 + endloop + endfacet + + facet normal 0.826284051 0.179565996 0.533864021 + outer loop + vertex -1.38614297 -1.22347903 0.329647988 + vertex -1.42664099 -1.14463401 0.365808994 + vertex -1.28156805 -0.512152016 -0.0714629963 + endloop + endfacet + + facet normal 0.545636952 0.349325001 0.761742949 + outer loop + vertex -1.17588902 -0.599543989 -0.107084997 + vertex -1.38614297 -1.22347903 0.329647988 + vertex -1.28156805 -0.512152016 -0.0714629963 + endloop + endfacet + + facet normal 0.872462928 0.257705957 0.415205985 + outer loop + vertex -1.38614297 -1.22347903 0.329647988 + vertex -1.68667006 -1.63774502 1.21826005 + vertex -1.42664099 -1.14463401 0.365808994 + endloop + endfacet + + facet normal 0.843502939 0.317459971 0.433268964 + outer loop + vertex -1.38614297 -1.22347903 0.329647988 + vertex -1.65266299 -1.67137408 1.17669499 + vertex -1.68667006 -1.63774502 1.21826005 + endloop + endfacet + + facet normal 0.305910945 -0.643379867 0.701769829 + outer loop + vertex -1.69556093 -1.72396898 1.14308596 + vertex -1.77819002 -1.71213603 1.18995297 + vertex -1.68667006 -1.63774502 1.21826005 + endloop + endfacet + + facet normal 0.23541902 -0.652366102 0.720414102 + outer loop + vertex -1.65266299 -1.67137408 1.17669499 + vertex -1.69556093 -1.72396898 1.14308596 + vertex -1.68667006 -1.63774502 1.21826005 + endloop + endfacet + + facet normal 0.338568926 -0.577655792 0.742754817 + outer loop + vertex -1.69556093 -1.72396898 1.14308596 + vertex -2.24798799 -2.70652914 0.630741 + vertex -1.77819002 -1.71213603 1.18995297 + endloop + endfacet + + facet normal 0.199457005 -0.538895965 0.818417966 + outer loop + vertex -1.69556093 -1.72396898 1.14308596 + vertex -2.15423298 -2.69871092 0.61303997 + vertex -2.24798799 -2.70652914 0.630741 + endloop + endfacet + + facet normal 0.0607170016 -0.890278995 0.451350033 + outer loop + vertex -2.64349794 -3.70451212 -1.28455305 + vertex -2.68474102 -3.68566895 -1.24183702 + vertex -2.24798799 -2.70652914 0.630741 + endloop + endfacet + + facet normal 0.155501023 -0.888839185 0.431027114 + outer loop + vertex -2.15423298 -2.69871092 0.61303997 + vertex -2.64349794 -3.70451212 -1.28455305 + vertex -2.24798799 -2.70652914 0.630741 + endloop + endfacet + + facet normal 0.0504009835 -0.894892812 0.443425894 + outer loop + vertex -2.64349794 -3.70451212 -1.28455305 + vertex -2.92125297 -3.86225605 -1.57133007 + vertex -2.68474102 -3.68566895 -1.24183702 + endloop + endfacet + + facet normal -0.0920129791 -0.832157791 0.546851873 + outer loop + vertex -2.64349794 -3.70451212 -1.28455305 + vertex -2.906811 -3.87340689 -1.58586907 + vertex -2.92125297 -3.86225605 -1.57133007 + endloop + endfacet + + facet normal -0.0213840008 -0.399439991 0.916509986 + outer loop + vertex -1.12877297 -0.45877099 -0.0446330011 + vertex -1.03473103 -0.580929995 -0.095679 + vertex -1.17588902 -0.599543989 -0.107084997 + endloop + endfacet + + facet normal 0.78186512 0.450013101 0.43148011 + outer loop + vertex -2.90002489 -3.89163709 -1.57915306 + vertex -2.91697907 -3.88280702 -1.55764103 + vertex -2.906811 -3.87340689 -1.58586907 + endloop + endfacet + + facet normal -0.14268291 0.599396646 0.787632525 + outer loop + vertex -1.03473103 -0.580929995 -0.095679 + vertex -1.38614297 -1.22347903 0.329647988 + vertex -1.17588902 -0.599543989 -0.107084997 + endloop + endfacet + + facet normal 0.052053012 0.53126812 0.845603168 + outer loop + vertex -1.03473103 -0.580929995 -0.095679 + vertex -1.29874599 -1.25875497 0.346430987 + vertex -1.38614297 -1.22347903 0.329647988 + endloop + endfacet + + facet normal 0.257589042 0.818346024 0.513768077 + outer loop + vertex -1.60318708 -1.69773293 1.19387603 + vertex -1.65266299 -1.67137408 1.17669499 + vertex -1.38614297 -1.22347903 0.329647988 + endloop + endfacet + + facet normal 0.235024914 0.826015711 0.51230979 + outer loop + vertex -1.29874599 -1.25875497 0.346430987 + vertex -1.60318708 -1.69773293 1.19387603 + vertex -1.38614297 -1.22347903 0.329647988 + endloop + endfacet + + facet normal -0.421861917 -0.218249947 0.879999757 + outer loop + vertex -1.60318708 -1.69773293 1.19387603 + vertex -1.69556093 -1.72396898 1.14308596 + vertex -1.65266299 -1.67137408 1.17669499 + endloop + endfacet + + facet normal -0.423999131 -0.211792082 0.880550325 + outer loop + vertex -1.60318708 -1.69773293 1.19387603 + vertex -1.62320209 -1.78316092 1.16369104 + vertex -1.69556093 -1.72396898 1.14308596 + endloop + endfacet + + facet normal -0.403747976 -0.283086956 0.869970918 + outer loop + vertex -2.08584404 -2.76491594 0.623236001 + vertex -2.15423298 -2.69871092 0.61303997 + vertex -1.69556093 -1.72396898 1.14308596 + endloop + endfacet + + facet normal -0.45299387 -0.256548941 0.8538028 + outer loop + vertex -1.62320209 -1.78316092 1.16369104 + vertex -2.08584404 -2.76491594 0.623236001 + vertex -1.69556093 -1.72396898 1.14308596 + endloop + endfacet + + facet normal -0.645806909 -0.593053877 0.480853915 + outer loop + vertex -2.08584404 -2.76491594 0.623236001 + vertex -2.64349794 -3.70451212 -1.28455305 + vertex -2.15423298 -2.69871092 0.61303997 + endloop + endfacet + + facet normal -0.940769851 -0.0996529832 0.324069977 + outer loop + vertex -2.08584404 -2.76491594 0.623236001 + vertex -2.63283992 -3.74498892 -1.26605999 + vertex -2.64349794 -3.70451212 -1.28455305 + endloop + endfacet + + facet normal -0.743959308 -0.0310040135 0.667505264 + outer loop + vertex -2.90002489 -3.89163709 -1.57915306 + vertex -2.906811 -3.87340689 -1.58586907 + vertex -2.64349794 -3.70451212 -1.28455305 + endloop + endfacet + + facet normal -0.776583195 0.0809680223 0.624790192 + outer loop + vertex -2.63283992 -3.74498892 -1.26605999 + vertex -2.90002489 -3.89163709 -1.57915306 + vertex -2.64349794 -3.70451212 -1.28455305 + endloop + endfacet + + facet normal -0.021383008 -0.399439186 0.916510284 + outer loop + vertex -1.12877297 -0.45877099 -0.0446330011 + vertex -0.964388013 -0.470328987 -0.0458349995 + vertex -1.03473103 -0.580929995 -0.095679 + endloop + endfacet + + facet normal 0.781880677 0.449949831 0.43151781 + outer loop + vertex -2.90600705 -3.90321898 -1.55623698 + vertex -2.91697907 -3.88280702 -1.55764103 + vertex -2.90002489 -3.89163709 -1.57915306 + endloop + endfacet + + facet normal -0.677965045 0.568237007 0.466337025 + outer loop + vertex -1.23026299 -1.22389793 0.403519988 + vertex -1.29874599 -1.25875497 0.346430987 + vertex -1.03473103 -0.580929995 -0.095679 + endloop + endfacet + + facet normal -0.864686787 0.444541872 0.23387894 + outer loop + vertex -0.964388013 -0.470328987 -0.0458349995 + vertex -1.23026299 -1.22389793 0.403519988 + vertex -1.03473103 -0.580929995 -0.095679 + endloop + endfacet + + facet normal -0.574035645 0.792883575 0.204495862 + outer loop + vertex -1.23026299 -1.22389793 0.403519988 + vertex -1.60318708 -1.69773293 1.19387603 + vertex -1.29874599 -1.25875497 0.346430987 + endloop + endfacet + + facet normal -0.542411327 0.808382392 0.228709131 + outer loop + vertex -1.23026299 -1.22389793 0.403519988 + vertex -1.57549906 -1.69697499 1.256863 + vertex -1.60318708 -1.69773293 1.19387603 + endloop + endfacet + + facet normal -0.963225067 0.145960003 0.225594997 + outer loop + vertex -1.61559892 -1.845137 1.23625302 + vertex -1.62320209 -1.78316092 1.16369104 + vertex -1.60318708 -1.69773293 1.19387603 + endloop + endfacet + + facet normal -0.899821401 0.188828096 0.393275172 + outer loop + vertex -1.57549906 -1.69697499 1.256863 + vertex -1.61559892 -1.845137 1.23625302 + vertex -1.60318708 -1.69773293 1.19387603 + endloop + endfacet + + facet normal -0.913563967 0.256980956 0.315216959 + outer loop + vertex -1.61559892 -1.845137 1.23625302 + vertex -2.08584404 -2.76491594 0.623236001 + vertex -1.62320209 -1.78316092 1.16369104 + endloop + endfacet + + facet normal -0.902424276 0.211018071 0.375635087 + outer loop + vertex -1.61559892 -1.845137 1.23625302 + vertex -2.09431696 -2.85528994 0.653648019 + vertex -2.08584404 -2.76491594 0.623236001 + endloop + endfacet + + facet normal -0.811797917 0.580202937 -0.065944992 + outer loop + vertex -2.66079307 -3.77662301 -1.20028496 + vertex -2.63283992 -3.74498892 -1.26605999 + vertex -2.08584404 -2.76491594 0.623236001 + endloop + endfacet + + facet normal -0.963328302 0.162240043 0.213721067 + outer loop + vertex -2.09431696 -2.85528994 0.653648019 + vertex -2.66079307 -3.77662301 -1.20028496 + vertex -2.08584404 -2.76491594 0.623236001 + endloop + endfacet + + facet normal -0.591019154 0.795851231 0.131594047 + outer loop + vertex -2.66079307 -3.77662301 -1.20028496 + vertex -2.90002489 -3.89163709 -1.57915306 + vertex -2.63283992 -3.74498892 -1.26605999 + endloop + endfacet + + facet normal -0.658654928 0.72665596 0.195306972 + outer loop + vertex -2.66079307 -3.77662301 -1.20028496 + vertex -2.90600705 -3.90321898 -1.55623698 + vertex -2.90002489 -3.89163709 -1.57915306 + endloop + endfacet + + facet normal -0.0767649934 0.424314976 0.902254939 + outer loop + vertex -1.01769805 0.488880992 -0.0326699987 + vertex -1.05401599 0.365146995 0.0224300008 + vertex -1.17887402 0.454620987 -0.0302719995 + endloop + endfacet + + facet normal 0.760863423 -0.570059299 0.310031176 + outer loop + vertex -3.08611298 2.79408097 -1.54586196 + vertex -3.0986321 2.77501702 -1.55019093 + vertex -3.09980297 2.787534 -1.52430201 + endloop + endfacet + + facet normal -0.885644376 -0.0708120316 -0.458933175 + outer loop + vertex -1.01769805 0.488880992 -0.0326699987 + vertex -1.31704998 1.29457402 0.420700014 + vertex -1.32415795 1.20324898 0.448507994 + endloop + endfacet + + facet normal -0.840063334 0.0050070025 -0.54246521 + outer loop + vertex -1.01769805 0.488880992 -0.0326699987 + vertex -1.32415795 1.20324898 0.448507994 + vertex -1.05401599 0.365146995 0.0224300008 + endloop + endfacet + + facet normal -0.96798718 -0.00108800025 -0.250997066 + outer loop + vertex -1.31704998 1.29457402 0.420700014 + vertex -1.52630997 1.67332196 1.22608101 + vertex -1.32415795 1.20324898 0.448507994 + endloop + endfacet + + facet normal -0.909262836 0.206635967 -0.361306936 + outer loop + vertex -1.52630997 1.67332196 1.22608101 + vertex -1.55407095 1.62852693 1.27032602 + vertex -1.32415795 1.20324898 0.448507994 + endloop + endfacet + + facet normal -0.512467682 -0.124688931 -0.849605441 + outer loop + vertex -1.52630997 1.67332196 1.22608101 + vertex -1.57726693 1.81927204 1.23539805 + vertex -1.65870309 1.81342292 1.28537703 + endloop + endfacet + + facet normal -0.638087928 -0.303487957 -0.707629025 + outer loop + vertex -1.52630997 1.67332196 1.22608101 + vertex -1.65870309 1.81342292 1.28537703 + vertex -1.55407095 1.62852693 1.27032602 + endloop + endfacet + + facet normal -0.160780042 -0.915375292 -0.369104087 + outer loop + vertex -1.57726693 1.81927204 1.23539805 + vertex -2.07294297 2.21308303 0.474665999 + vertex -1.65870309 1.81342292 1.28537703 + endloop + endfacet + + facet normal -0.373682201 -0.893377483 -0.249476135 + outer loop + vertex -2.07294297 2.21308303 0.474665999 + vertex -2.16162205 2.24618006 0.488976002 + vertex -1.65870309 1.81342292 1.28537703 + endloop + endfacet + + facet normal -0.0730490163 -0.98006922 -0.18473804 + outer loop + vertex -2.07294297 2.21308303 0.474665999 + vertex -2.83839607 2.59127903 -1.22905695 + vertex -2.881531 2.58232498 -1.16449904 + endloop + endfacet + + facet normal -0.354477853 -0.934385598 -0.0356209837 + outer loop + vertex -2.07294297 2.21308303 0.474665999 + vertex -2.881531 2.58232498 -1.16449904 + vertex -2.16162205 2.24618006 0.488976002 + endloop + endfacet + + facet normal -0.308526933 -0.892181814 -0.32988295 + outer loop + vertex -2.83839607 2.59127903 -1.22905695 + vertex -3.08611298 2.79408097 -1.54586196 + vertex -2.881531 2.58232498 -1.16449904 + endloop + endfacet + + facet normal -0.192876935 -0.898110747 -0.395215869 + outer loop + vertex -3.08611298 2.79408097 -1.54586196 + vertex -3.09980297 2.787534 -1.52430201 + vertex -2.881531 2.58232498 -1.16449904 + endloop + endfacet + + facet normal -0.0767690316 0.424311161 0.90225637 + outer loop + vertex -1.05401599 0.365146995 0.0224300008 + vertex -1.18435502 0.308786988 0.0378439985 + vertex -1.17887402 0.454620987 -0.0302719995 + endloop + endfacet + + facet normal 0.760849178 -0.570075095 0.310037047 + outer loop + vertex -3.09980297 2.787534 -1.52430201 + vertex -3.0986321 2.77501702 -1.55019093 + vertex -3.11261201 2.77155995 -1.52223897 + endloop + endfacet + + facet normal -0.264522016 0.367929041 -0.891435027 + outer loop + vertex -1.05401599 0.365146995 0.0224300008 + vertex -1.32415795 1.20324898 0.448507994 + vertex -1.18435502 0.308786988 0.0378439985 + endloop + endfacet + + facet normal -0.0232820045 0.414123029 -0.909923077 + outer loop + vertex -1.32415795 1.20324898 0.448507994 + vertex -1.38690901 1.13685596 0.41989699 + vertex -1.18435502 0.308786988 0.0378439985 + endloop + endfacet + + facet normal -0.489267647 0.711135507 -0.504879713 + outer loop + vertex -1.32415795 1.20324898 0.448507994 + vertex -1.55407095 1.62852693 1.27032602 + vertex -1.59782505 1.59094405 1.25979102 + endloop + endfacet + + facet normal -0.508115053 0.69780314 -0.504866123 + outer loop + vertex -1.32415795 1.20324898 0.448507994 + vertex -1.59782505 1.59094405 1.25979102 + vertex -1.38690901 1.13685596 0.41989699 + endloop + endfacet + + facet normal 0.113028012 0.14399001 -0.983103096 + outer loop + vertex -1.55407095 1.62852693 1.27032602 + vertex -1.65870309 1.81342292 1.28537703 + vertex -1.59782505 1.59094405 1.25979102 + endloop + endfacet + + facet normal 0.185814962 0.162295938 -0.969088614 + outer loop + vertex -1.65870309 1.81342292 1.28537703 + vertex -1.73981202 1.76814294 1.26224196 + vertex -1.59782505 1.59094405 1.25979102 + endloop + endfacet + + facet normal 0.498544723 -0.589779615 -0.635305643 + outer loop + vertex -1.65870309 1.81342292 1.28537703 + vertex -2.16162205 2.24618006 0.488976002 + vertex -2.244519 2.21051693 0.457031012 + endloop + endfacet + + facet normal 0.506276369 -0.581512392 -0.636810482 + outer loop + vertex -1.65870309 1.81342292 1.28537703 + vertex -2.244519 2.21051693 0.457031012 + vertex -1.73981202 1.76814294 1.26224196 + endloop + endfacet + + facet normal 0.484050065 -0.79212904 -0.371789068 + outer loop + vertex -2.16162205 2.24618006 0.488976002 + vertex -2.881531 2.58232498 -1.16449904 + vertex -2.244519 2.21051693 0.457031012 + endloop + endfacet + + facet normal 0.612227142 -0.683636189 -0.397265077 + outer loop + vertex -2.881531 2.58232498 -1.16449904 + vertex -2.91222191 2.54960108 -1.15548396 + vertex -2.244519 2.21051693 0.457031012 + endloop + endfacet + + facet normal 0.557069063 -0.529381037 -0.639867067 + outer loop + vertex -2.881531 2.58232498 -1.16449904 + vertex -3.09980297 2.787534 -1.52430201 + vertex -3.11261201 2.77155995 -1.52223897 + endloop + endfacet + + facet normal 0.471753895 -0.616191864 -0.630678833 + outer loop + vertex -2.881531 2.58232498 -1.16449904 + vertex -3.11261201 2.77155995 -1.52223897 + vertex -2.91222191 2.54960108 -1.15548396 + endloop + endfacet + + facet normal -0.0767669678 0.424310833 0.902256668 + outer loop + vertex -1.18435502 0.308786988 0.0378439985 + vertex -1.31056702 0.362244993 0.00196600007 + vertex -1.17887402 0.454620987 -0.0302719995 + endloop + endfacet + + facet normal 0.760856092 -0.570063055 0.310042024 + outer loop + vertex -3.11261201 2.77155995 -1.52223897 + vertex -3.0986321 2.77501702 -1.55019093 + vertex -3.11489511 2.75818896 -1.54122198 + endloop + endfacet + + facet normal 0.624605119 0.447931021 -0.639708042 + outer loop + vertex -1.18435502 0.308786988 0.0378439985 + vertex -1.38690901 1.13685596 0.41989699 + vertex -1.45804906 1.14539194 0.356413007 + endloop + endfacet + + facet normal 0.412785053 0.438942075 -0.798084199 + outer loop + vertex -1.18435502 0.308786988 0.0378439985 + vertex -1.45804906 1.14539194 0.356413007 + vertex -1.31056702 0.362244993 0.00196600007 + endloop + endfacet + + facet normal 0.413569063 0.840320051 -0.350461036 + outer loop + vertex -1.38690901 1.13685596 0.41989699 + vertex -1.59782505 1.59094405 1.25979102 + vertex -1.45804906 1.14539194 0.356413007 + endloop + endfacet + + facet normal 0.566476703 0.770475626 -0.292354852 + outer loop + vertex -1.59782505 1.59094405 1.25979102 + vertex -1.62462497 1.58887506 1.20240998 + vertex -1.45804906 1.14539194 0.356413007 + endloop + endfacet + + facet normal 0.666042805 0.540796876 -0.513736904 + outer loop + vertex -1.59782505 1.59094405 1.25979102 + vertex -1.73981202 1.76814294 1.26224196 + vertex -1.759516 1.71752691 1.18341398 + endloop + endfacet + + facet normal 0.673963785 0.656674743 -0.338453889 + outer loop + vertex -1.59782505 1.59094405 1.25979102 + vertex -1.759516 1.71752691 1.18341398 + vertex -1.62462497 1.58887506 1.20240998 + endloop + endfacet + + facet normal 0.87528342 0.276827157 -0.396542192 + outer loop + vertex -1.73981202 1.76814294 1.26224196 + vertex -2.244519 2.21051693 0.457031012 + vertex -1.759516 1.71752691 1.18341398 + endloop + endfacet + + facet normal 0.868040264 0.162955061 -0.468990147 + outer loop + vertex -2.244519 2.21051693 0.457031012 + vertex -2.25921011 2.13294888 0.402886987 + vertex -1.759516 1.71752691 1.18341398 + endloop + endfacet + + facet normal 0.824457109 0.515765071 -0.232931033 + outer loop + vertex -2.244519 2.21051693 0.457031012 + vertex -2.91222191 2.54960108 -1.15548396 + vertex -2.90735912 2.51775002 -1.20879805 + endloop + endfacet + + facet normal 0.931220233 0.0728830248 -0.357095122 + outer loop + vertex -2.244519 2.21051693 0.457031012 + vertex -2.90735912 2.51775002 -1.20879805 + vertex -2.25921011 2.13294888 0.402886987 + endloop + endfacet + + facet normal 0.866475642 0.459383816 -0.195413932 + outer loop + vertex -2.91222191 2.54960108 -1.15548396 + vertex -3.11261201 2.77155995 -1.52223897 + vertex -2.90735912 2.51775002 -1.20879805 + endloop + endfacet + + facet normal 0.89059633 0.314460129 -0.328562111 + outer loop + vertex -3.11261201 2.77155995 -1.52223897 + vertex -3.11489511 2.75818896 -1.54122198 + vertex -2.90735912 2.51775002 -1.20879805 + endloop + endfacet + + facet normal -0.0767720193 0.424317122 0.90225327 + outer loop + vertex -1.31056702 0.362244993 0.00196600007 + vertex -1.33761096 0.485262007 -0.0581879988 + vertex -1.17887402 0.454620987 -0.0302719995 + endloop + endfacet + + facet normal 0.760856211 -0.570104182 0.309966087 + outer loop + vertex -3.11489511 2.75818896 -1.54122198 + vertex -3.0986321 2.77501702 -1.55019093 + vertex -3.10493207 2.75749207 -1.56695998 + endloop + endfacet + + facet normal 0.979394317 0.199278057 -0.0327850096 + outer loop + vertex -1.31056702 0.362244993 0.00196600007 + vertex -1.45804906 1.14539194 0.356413007 + vertex -1.33761096 0.485262007 -0.0581879988 + endloop + endfacet + + facet normal 0.961778104 0.24816604 -0.115743019 + outer loop + vertex -1.45804906 1.14539194 0.356413007 + vertex -1.48400998 1.22242701 0.305858999 + vertex -1.33761096 0.485262007 -0.0581879988 + endloop + endfacet + + facet normal 0.983233452 0.0246459842 0.180677876 + outer loop + vertex -1.45804906 1.14539194 0.356413007 + vertex -1.62462497 1.58887506 1.20240998 + vertex -1.61429 1.623878 1.14139199 + endloop + endfacet + + facet normal 0.94848156 0.316802889 -0.004323998 + outer loop + vertex -1.45804906 1.14539194 0.356413007 + vertex -1.61429 1.623878 1.14139199 + vertex -1.48400998 1.22242701 0.305858999 + endloop + endfacet + + facet normal 0.569355786 0.667770743 0.479495794 + outer loop + vertex -1.62462497 1.58887506 1.20240998 + vertex -1.759516 1.71752691 1.18341398 + vertex -1.61429 1.623878 1.14139199 + endloop + endfacet + + facet normal 0.574625909 0.780223846 0.247094944 + outer loop + vertex -1.759516 1.71752691 1.18341398 + vertex -1.70297694 1.69968998 1.10825098 + vertex -1.61429 1.623878 1.14139199 + endloop + endfacet + + facet normal 0.67483896 0.736889005 -0.0398359969 + outer loop + vertex -1.759516 1.71752691 1.18341398 + vertex -2.25921011 2.13294888 0.402886987 + vertex -2.19463396 2.07188797 0.367314011 + endloop + endfacet + + facet normal 0.45869714 0.878017306 0.136684045 + outer loop + vertex -1.759516 1.71752691 1.18341398 + vertex -2.19463396 2.07188797 0.367314011 + vertex -1.70297694 1.69968998 1.10825098 + endloop + endfacet + + facet normal 0.658887684 0.747243702 -0.0865659639 + outer loop + vertex -2.25921011 2.13294888 0.402886987 + vertex -2.90735912 2.51775002 -1.20879805 + vertex -2.19463396 2.07188797 0.367314011 + endloop + endfacet + + facet normal 0.369675994 0.924360991 0.0943209976 + outer loop + vertex -2.90735912 2.51775002 -1.20879805 + vertex -2.87060595 2.51075506 -1.28429699 + vertex -2.19463396 2.07188797 0.367314011 + endloop + endfacet + + facet normal 0.583255053 0.786133051 0.204471037 + outer loop + vertex -2.90735912 2.51775002 -1.20879805 + vertex -3.11489511 2.75818896 -1.54122198 + vertex -3.10493207 2.75749207 -1.56695998 + endloop + endfacet + + facet normal 0.578596771 0.78848362 0.208612904 + outer loop + vertex -2.90735912 2.51775002 -1.20879805 + vertex -3.10493207 2.75749207 -1.56695998 + vertex -2.87060595 2.51075506 -1.28429699 + endloop + endfacet + + facet normal -0.0767699778 0.424322903 0.902250767 + outer loop + vertex -1.33761096 0.485262007 -0.0581879988 + vertex -1.24512303 0.585205019 -0.0973210037 + vertex -1.17887402 0.454620987 -0.0302719995 + endloop + endfacet + + facet normal 0.760858178 -0.570103168 0.309963077 + outer loop + vertex -3.10493207 2.75749207 -1.56695998 + vertex -3.0986321 2.77501702 -1.55019093 + vertex -3.09022689 2.76998997 -1.58006907 + endloop + endfacet + + facet normal 0.584711671 -0.262888819 0.767464519 + outer loop + vertex -1.33761096 0.485262007 -0.0581879988 + vertex -1.48400998 1.22242701 0.305858999 + vertex -1.44524097 1.309955 0.306304008 + endloop + endfacet + + facet normal 0.59877497 -0.25710398 0.758529007 + outer loop + vertex -1.33761096 0.485262007 -0.0581879988 + vertex -1.44524097 1.309955 0.306304008 + vertex -1.24512303 0.585205019 -0.0973210037 + endloop + endfacet + + facet normal 0.86564827 -0.385051161 0.319982111 + outer loop + vertex -1.48400998 1.22242701 0.305858999 + vertex -1.61429 1.623878 1.14139199 + vertex -1.44524097 1.309955 0.306304008 + endloop + endfacet + + facet normal 0.773064494 -0.526166677 0.354287803 + outer loop + vertex -1.61429 1.623878 1.14139199 + vertex -1.57460201 1.66959405 1.12268603 + vertex -1.44524097 1.309955 0.306304008 + endloop + endfacet + + facet normal 0.0183549952 0.418431848 0.908062696 + outer loop + vertex -1.61429 1.623878 1.14139199 + vertex -1.70297694 1.69968998 1.10825098 + vertex -1.61277103 1.72806406 1.09335303 + endloop + endfacet + + facet normal -0.0551219992 0.418749988 0.906427085 + outer loop + vertex -1.61429 1.623878 1.14139199 + vertex -1.61277103 1.72806406 1.09335303 + vertex -1.57460201 1.66959405 1.12268603 + endloop + endfacet + + facet normal -0.173069939 0.829250693 0.53140384 + outer loop + vertex -1.70297694 1.69968998 1.10825098 + vertex -2.19463396 2.07188797 0.367314011 + vertex -1.61277103 1.72806406 1.09335303 + endloop + endfacet + + facet normal -0.0611630008 0.882239044 0.466812015 + outer loop + vertex -2.19463396 2.07188797 0.367314011 + vertex -2.09941506 2.07331204 0.377099007 + vertex -1.61277103 1.72806406 1.09335303 + endloop + endfacet + + facet normal -0.196834937 0.924567699 0.326236904 + outer loop + vertex -2.19463396 2.07188797 0.367314011 + vertex -2.87060595 2.51075506 -1.28429699 + vertex -2.82963514 2.533885 -1.32512701 + endloop + endfacet + + facet normal -0.0429229848 0.959605694 0.278054923 + outer loop + vertex -2.19463396 2.07188797 0.367314011 + vertex -2.82963514 2.533885 -1.32512701 + vertex -2.09941506 2.07331204 0.377099007 + endloop + endfacet + + facet normal 0.133578047 0.798819304 0.586553216 + outer loop + vertex -2.87060595 2.51075506 -1.28429699 + vertex -3.10493207 2.75749207 -1.56695998 + vertex -2.82963514 2.533885 -1.32512701 + endloop + endfacet + + facet normal -0.00927600171 0.728916168 0.684540093 + outer loop + vertex -3.10493207 2.75749207 -1.56695998 + vertex -3.09022689 2.76998997 -1.58006907 + vertex -2.82963514 2.533885 -1.32512701 + endloop + endfacet + + facet normal -0.0767689869 0.424322903 0.902250767 + outer loop + vertex -1.24512303 0.585205019 -0.0973210037 + vertex -1.10274899 0.586816013 -0.0859650001 + vertex -1.17887402 0.454620987 -0.0302719995 + endloop + endfacet + + facet normal 0.760912299 -0.570029259 0.309966117 + outer loop + vertex -3.09022689 2.76998997 -1.58006907 + vertex -3.0986321 2.77501702 -1.55019093 + vertex -3.08185196 2.78627396 -1.57068002 + endloop + endfacet + + facet normal -0.0633039996 -0.498861015 0.864367008 + outer loop + vertex -1.24512303 0.585205019 -0.0973210037 + vertex -1.44524097 1.309955 0.306304008 + vertex -1.10274899 0.586816013 -0.0859650001 + endloop + endfacet + + facet normal -0.292043954 -0.559201956 0.77588886 + outer loop + vertex -1.44524097 1.309955 0.306304008 + vertex -1.37093699 1.34206295 0.357414007 + vertex -1.10274899 0.586816013 -0.0859650001 + endloop + endfacet + + facet normal 0.108098 -0.903342009 0.415075988 + outer loop + vertex -1.44524097 1.309955 0.306304008 + vertex -1.57460201 1.66959405 1.12268603 + vertex -1.53544593 1.69159794 1.16037703 + endloop + endfacet + + facet normal 0.105116993 -0.903753936 0.414944977 + outer loop + vertex -1.44524097 1.309955 0.306304008 + vertex -1.53544593 1.69159794 1.16037703 + vertex -1.37093699 1.34206295 0.357414007 + endloop + endfacet + + facet normal -0.671668947 -0.0683799908 0.737688959 + outer loop + vertex -1.57460201 1.66959405 1.12268603 + vertex -1.61277103 1.72806406 1.09335303 + vertex -1.53544593 1.69159794 1.16037703 + endloop + endfacet + + facet normal -0.673058808 -0.074808985 0.735795796 + outer loop + vertex -1.61277103 1.72806406 1.09335303 + vertex -1.55682492 1.78128397 1.14994001 + vertex -1.53544593 1.69159794 1.16037703 + endloop + endfacet + + facet normal -0.765665054 0.19344902 0.613461077 + outer loop + vertex -1.61277103 1.72806406 1.09335303 + vertex -2.09941506 2.07331204 0.377099007 + vertex -2.045259 2.13614988 0.424876004 + endloop + endfacet + + facet normal -0.776329041 0.171037018 0.606679142 + outer loop + vertex -1.61277103 1.72806406 1.09335303 + vertex -2.045259 2.13614988 0.424876004 + vertex -1.55682492 1.78128397 1.14994001 + endloop + endfacet + + facet normal -0.816772759 0.362870902 0.448560894 + outer loop + vertex -2.09941506 2.07331204 0.377099007 + vertex -2.82963514 2.533885 -1.32512701 + vertex -2.045259 2.13614988 0.424876004 + endloop + endfacet + + facet normal -0.903895736 0.0723669752 0.421586841 + outer loop + vertex -2.82963514 2.533885 -1.32512701 + vertex -2.81529999 2.56972098 -1.30054402 + vertex -2.045259 2.13614988 0.424876004 + endloop + endfacet + + facet normal -0.714901567 -0.0349769779 0.698349595 + outer loop + vertex -2.82963514 2.533885 -1.32512701 + vertex -3.09022689 2.76998997 -1.58006907 + vertex -3.08185196 2.78627396 -1.57068002 + endloop + endfacet + + facet normal -0.757348955 -0.135290995 0.638841927 + outer loop + vertex -2.82963514 2.533885 -1.32512701 + vertex -3.08185196 2.78627396 -1.57068002 + vertex -2.81529999 2.56972098 -1.30054402 + endloop + endfacet + + facet normal -0.0767659619 0.4243218 0.902251601 + outer loop + vertex -1.10274899 0.586816013 -0.0859650001 + vertex -1.01769805 0.488880992 -0.0326699987 + vertex -1.17887402 0.454620987 -0.0302719995 + endloop + endfacet + + facet normal 0.760899127 -0.570062101 0.309938073 + outer loop + vertex -3.08185196 2.78627396 -1.57068002 + vertex -3.0986321 2.77501702 -1.55019093 + vertex -3.08611298 2.79408097 -1.54586196 + endloop + endfacet + + facet normal -0.81185782 -0.481850892 0.329706907 + outer loop + vertex -1.10274899 0.586816013 -0.0859650001 + vertex -1.37093699 1.34206295 0.357414007 + vertex -1.31704998 1.29457402 0.420700014 + endloop + endfacet + + facet normal -0.792782009 -0.494814992 0.355886042 + outer loop + vertex -1.10274899 0.586816013 -0.0859650001 + vertex -1.31704998 1.29457402 0.420700014 + vertex -1.01769805 0.488880992 -0.0326699987 + endloop + endfacet + + facet normal -0.739785016 -0.659090936 0.135341004 + outer loop + vertex -1.37093699 1.34206295 0.357414007 + vertex -1.53544593 1.69159794 1.16037703 + vertex -1.31704998 1.29457402 0.420700014 + endloop + endfacet + + facet normal -0.882458806 -0.470319867 -0.00810799841 + outer loop + vertex -1.53544593 1.69159794 1.16037703 + vertex -1.52630997 1.67332196 1.22608101 + vertex -1.31704998 1.29457402 0.420700014 + endloop + endfacet + + facet normal -0.962198138 -0.243543014 -0.121908017 + outer loop + vertex -1.53544593 1.69159794 1.16037703 + vertex -1.55682492 1.78128397 1.14994001 + vertex -1.57726693 1.81927204 1.23539805 + endloop + endfacet + + facet normal -0.942623854 -0.331589013 0.0388449952 + outer loop + vertex -1.53544593 1.69159794 1.16037703 + vertex -1.57726693 1.81927204 1.23539805 + vertex -1.52630997 1.67332196 1.22608101 + endloop + endfacet + + facet normal -0.708266914 -0.692263961 0.138305992 + outer loop + vertex -1.55682492 1.78128397 1.14994001 + vertex -2.045259 2.13614988 0.424876004 + vertex -1.57726693 1.81927204 1.23539805 + endloop + endfacet + + facet normal -0.826425612 -0.483796358 0.28803125 + outer loop + vertex -2.045259 2.13614988 0.424876004 + vertex -2.07294297 2.21308303 0.474665999 + vertex -1.57726693 1.81927204 1.23539805 + endloop + endfacet + + facet normal -0.582625031 -0.810790002 0.0562819988 + outer loop + vertex -2.045259 2.13614988 0.424876004 + vertex -2.81529999 2.56972098 -1.30054402 + vertex -2.83839607 2.59127903 -1.22905695 + endloop + endfacet + + facet normal -0.836838305 -0.476067185 0.270299107 + outer loop + vertex -2.045259 2.13614988 0.424876004 + vertex -2.83839607 2.59127903 -1.22905695 + vertex -2.07294297 2.21308303 0.474665999 + endloop + endfacet + + facet normal -0.644049883 -0.764651895 0.0225229971 + outer loop + vertex -2.81529999 2.56972098 -1.30054402 + vertex -3.08185196 2.78627396 -1.57068002 + vertex -2.83839607 2.59127903 -1.22905695 + endloop + endfacet + + facet normal -0.70394212 -0.703137159 0.100317024 + outer loop + vertex -3.08185196 2.78627396 -1.57068002 + vertex -3.08611298 2.79408097 -1.54586196 + vertex -2.83839607 2.59127903 -1.22905695 + endloop + endfacet + + facet normal 0.0729879588 -0.424979746 0.902255476 + outer loop + vertex -0.790997982 -0.401975006 -0.0302719995 + vertex -0.704271972 -0.275191993 0.0224300008 + vertex -0.627824008 -0.379043013 -0.0326699987 + endloop + endfacet + + facet normal 0.288141072 0.809048176 0.512265146 + outer loop + vertex -1.08943605 -3.53432393 -1.60113692 + vertex -1.09849703 -3.51748109 -1.62264204 + vertex -1.07667601 -3.52539802 -1.62241101 + endloop + endfacet + + facet normal -0.722976804 -0.26038295 -0.639925897 + outer loop + vertex -0.785679996 -1.12330306 0.448507994 + vertex -0.733861029 -1.19883895 0.420700014 + vertex -0.627824008 -0.379043013 -0.0326699987 + endloop + endfacet + + facet normal -0.757175207 -0.233753055 -0.609955132 + outer loop + vertex -0.704271972 -0.275191993 0.0224300008 + vertex -0.785679996 -1.12330306 0.448507994 + vertex -0.627824008 -0.379043013 -0.0326699987 + endloop + endfacet + + facet normal -0.822168887 -0.424315959 -0.379465908 + outer loop + vertex -0.785679996 -1.12330306 0.448507994 + vertex -0.835425019 -1.72230005 1.22608101 + vertex -0.733861029 -1.19883895 0.420700014 + endloop + endfacet + + facet normal -0.716561258 -0.529666245 -0.453865141 + outer loop + vertex -0.785679996 -1.12330306 0.448507994 + vertex -0.881864011 -1.69738698 1.27032602 + vertex -0.835425019 -1.72230005 1.22608101 + endloop + endfacet + + facet normal -0.506153941 -0.148249999 -0.849605918 + outer loop + vertex -0.880029976 -1.90982795 1.28537703 + vertex -0.806580007 -1.87417507 1.23539805 + vertex -0.835425019 -1.72230005 1.22608101 + endloop + endfacet + + facet normal -0.704344094 -0.0562160127 -0.707629204 + outer loop + vertex -0.881864011 -1.69738698 1.27032602 + vertex -0.880029976 -1.90982795 1.28537703 + vertex -0.835425019 -1.72230005 1.22608101 + endloop + endfacet + + facet normal -0.632492721 0.574023724 -0.520047784 + outer loop + vertex -0.880029976 -1.90982795 1.28537703 + vertex -0.908545017 -2.86312103 0.267821014 + vertex -0.806580007 -1.87417507 1.23539805 + endloop + endfacet + + facet normal -0.727581859 0.510695934 -0.4580549 + outer loop + vertex -0.880029976 -1.90982795 1.28537703 + vertex -0.968795002 -2.93612289 0.282130986 + vertex -0.908545017 -2.86312103 0.267821014 + endloop + endfacet + + facet normal -0.823960662 0.55237174 -0.126388937 + outer loop + vertex -1.00637496 -3.34099007 -1.18288505 + vertex -0.972894013 -3.30494308 -1.24361598 + vertex -0.908545017 -2.86312103 0.267821014 + endloop + endfacet + + facet normal -0.776781023 0.611850977 -0.149162993 + outer loop + vertex -0.968795002 -2.93612289 0.282130986 + vertex -1.00637496 -3.34099007 -1.18288505 + vertex -0.908545017 -2.86312103 0.267821014 + endloop + endfacet + + facet normal -0.813524306 0.571128249 -0.10950204 + outer loop + vertex -1.00637496 -3.34099007 -1.18288505 + vertex -1.07667601 -3.52539802 -1.62241101 + vertex -0.972894013 -3.30494308 -1.24361598 + endloop + endfacet + + facet normal -0.731632829 0.66243583 -0.160909951 + outer loop + vertex -1.00637496 -3.34099007 -1.18288505 + vertex -1.08943605 -3.53432393 -1.60113692 + vertex -1.07667601 -3.52539802 -1.62241101 + endloop + endfacet + + facet normal 0.0729849711 -0.424977809 0.902256668 + outer loop + vertex -0.790997982 -0.401975006 -0.0302719995 + vertex -0.846026003 -0.26681 0.0378439985 + vertex -0.704271972 -0.275191993 0.0224300008 + endloop + endfacet + + facet normal 0.288122952 0.809049785 0.512272894 + outer loop + vertex -1.10902095 -3.53056598 -1.59605598 + vertex -1.09849703 -3.51748109 -1.62264204 + vertex -1.08943605 -3.53432393 -1.60113692 + endloop + endfacet + + facet normal -0.122729048 -0.436093181 -0.89149332 + outer loop + vertex -0.846026003 -0.26681 0.0378439985 + vertex -0.785679996 -1.12330306 0.448507994 + vertex -0.704271972 -0.275191993 0.0224300008 + endloop + endfacet + + facet normal 0.167711034 -0.416587085 -0.893492162 + outer loop + vertex -0.846026003 -0.26681 0.0378439985 + vertex -0.873220026 -1.09718001 0.41989699 + vertex -0.785679996 -1.12330306 0.448507994 + endloop + endfacet + + facet normal -0.0466770045 -0.816322029 -0.575708032 + outer loop + vertex -0.938548028 -1.68671608 1.25979102 + vertex -0.881864011 -1.69738698 1.27032602 + vertex -0.785679996 -1.12330306 0.448507994 + endloop + endfacet + + facet normal -0.0548560023 -0.815250099 -0.576505065 + outer loop + vertex -0.873220026 -1.09718001 0.41989699 + vertex -0.938548028 -1.68671608 1.25979102 + vertex -0.785679996 -1.12330306 0.448507994 + endloop + endfacet + + facet normal 0.169880018 -0.0681860074 -0.983103096 + outer loop + vertex -0.938548028 -1.68671608 1.25979102 + vertex -0.880029976 -1.90982795 1.28537703 + vertex -0.881864011 -1.69738698 1.27032602 + endloop + endfacet + + facet normal 0.242068902 -0.0476449765 -0.969088554 + outer loop + vertex -0.938548028 -1.68671608 1.25979102 + vertex -0.972913027 -1.91116905 1.26224196 + vertex -0.880029976 -1.90982795 1.28537703 + endloop + endfacet + + facet normal 0.173446015 0.680724084 -0.711710036 + outer loop + vertex -1.05841696 -2.94668603 0.25018701 + vertex -0.968795002 -2.93612289 0.282130986 + vertex -0.880029976 -1.90982795 1.28537703 + endloop + endfacet + + facet normal 0.167480066 0.681978166 -0.711938262 + outer loop + vertex -0.972913027 -1.91116905 1.26224196 + vertex -1.05841696 -2.94668603 0.25018701 + vertex -0.880029976 -1.90982795 1.28537703 + endloop + endfacet + + facet normal -0.0188320037 0.963823199 -0.265876055 + outer loop + vertex -1.05841696 -2.94668603 0.25018701 + vertex -1.00637496 -3.34099007 -1.18288505 + vertex -0.968795002 -2.93612289 0.282130986 + endloop + endfacet + + facet normal -0.0057739974 0.964099646 -0.265477866 + outer loop + vertex -1.05841696 -2.94668603 0.25018701 + vertex -1.04957104 -3.33721995 -1.16825497 + vertex -1.00637496 -3.34099007 -1.18288505 + endloop + endfacet + + facet normal 0.0616750047 0.901266992 -0.428852022 + outer loop + vertex -1.10902095 -3.53056598 -1.59605598 + vertex -1.08943605 -3.53432393 -1.60113692 + vertex -1.00637496 -3.34099007 -1.18288505 + endloop + endfacet + + facet normal -0.057355985 0.912721753 -0.404535919 + outer loop + vertex -1.04957104 -3.33721995 -1.16825497 + vertex -1.10902095 -3.53056598 -1.59605598 + vertex -1.00637496 -3.34099007 -1.18288505 + endloop + endfacet + + facet normal 0.0729859918 -0.424976945 0.902256966 + outer loop + vertex -0.790997982 -0.401975006 -0.0302719995 + vertex -0.946343005 -0.360211015 0.00196600007 + vertex -0.846026003 -0.26681 0.0378439985 + endloop + endfacet + + facet normal 0.288119972 0.809050918 0.512272954 + outer loop + vertex -1.12068105 -3.5169549 -1.61099398 + vertex -1.09849703 -3.51748109 -1.62264204 + vertex -1.10902095 -3.53056598 -1.59605598 + endloop + endfacet + + facet normal 0.798518658 -0.272996873 -0.536507785 + outer loop + vertex -0.930561006 -1.14014304 0.356413007 + vertex -0.873220026 -1.09718001 0.41989699 + vertex -0.846026003 -0.26681 0.0378439985 + endloop + endfacet + + facet normal 0.574401855 -0.329012901 -0.749541819 + outer loop + vertex -0.946343005 -0.360211015 0.00196600007 + vertex -0.930561006 -1.14014304 0.356413007 + vertex -0.846026003 -0.26681 0.0378439985 + endloop + endfacet + + facet normal 0.770561874 -0.548315942 -0.324936926 + outer loop + vertex -0.930561006 -1.14014304 0.356413007 + vertex -0.938548028 -1.68671608 1.25979102 + vertex -0.873220026 -1.09718001 0.41989699 + endloop + endfacet + + facet normal 0.849924028 -0.454108 -0.267235994 + outer loop + vertex -0.930561006 -1.14014304 0.356413007 + vertex -0.962791979 -1.69832397 1.20240998 + vertex -0.938548028 -1.68671608 1.25979102 + endloop + endfacet + + facet normal 0.847210824 -0.135322973 -0.51373291 + outer loop + vertex -1.01528394 -1.87718606 1.18341398 + vertex -0.972913027 -1.91116905 1.26224196 + vertex -0.938548028 -1.68671608 1.25979102 + endloop + endfacet + + facet normal 0.912006497 -0.231711864 -0.338457793 + outer loop + vertex -0.962791979 -1.69832397 1.20240998 + vertex -1.01528394 -1.87718606 1.18341398 + vertex -0.938548028 -1.68671608 1.25979102 + endloop + endfacet + + facet normal 0.891000748 0.277561903 -0.359272897 + outer loop + vertex -1.01528394 -1.87718606 1.18341398 + vertex -1.05841696 -2.94668603 0.25018701 + vertex -0.972913027 -1.91116905 1.26224196 + endloop + endfacet + + facet normal 0.839562833 0.337562919 -0.425658911 + outer loop + vertex -1.01528394 -1.87718606 1.18341398 + vertex -1.10992396 -2.88685608 0.196043 + vertex -1.05841696 -2.94668603 0.25018701 + endloop + endfacet + + facet normal 0.926300108 0.364708066 -0.0946370065 + outer loop + vertex -1.06995296 -3.29647708 -1.21073997 + vertex -1.04957104 -3.33721995 -1.16825497 + vertex -1.05841696 -2.94668603 0.25018701 + endloop + endfacet + + facet normal 0.810221374 0.568532228 -0.142522067 + outer loop + vertex -1.10992396 -2.88685608 0.196043 + vertex -1.06995296 -3.29647708 -1.21073997 + vertex -1.05841696 -2.94668603 0.25018701 + endloop + endfacet + + facet normal 0.94495213 0.228233024 -0.234468028 + outer loop + vertex -1.06995296 -3.29647708 -1.21073997 + vertex -1.10902095 -3.53056598 -1.59605598 + vertex -1.04957104 -3.33721995 -1.16825497 + endloop + endfacet + + facet normal 0.864126801 0.386452913 -0.322395921 + outer loop + vertex -1.06995296 -3.29647708 -1.21073997 + vertex -1.12068105 -3.5169549 -1.61099398 + vertex -1.10902095 -3.53056598 -1.59605598 + endloop + endfacet + + facet normal 0.0729829893 -0.424985975 0.902252913 + outer loop + vertex -0.790997982 -0.401975006 -0.0302719995 + vertex -0.929682016 -0.485058993 -0.0581879988 + vertex -0.946343005 -0.360211015 0.00196600007 + endloop + endfacet + + facet normal 0.288078994 0.809116066 0.512193084 + outer loop + vertex -1.11563802 -3.50374198 -1.63470399 + vertex -1.09849703 -3.51748109 -1.62264204 + vertex -1.12068105 -3.5169549 -1.61099398 + endloop + endfacet + + facet normal 0.990013659 0.0744389743 0.119714953 + outer loop + vertex -0.929682016 -0.485058993 -0.0581879988 + vertex -0.930561006 -1.14014304 0.356413007 + vertex -0.946343005 -0.360211015 0.00196600007 + endloop + endfacet + + facet normal 0.98270458 0.0980890542 0.157068089 + outer loop + vertex -0.929682016 -0.485058993 -0.0581879988 + vertex -0.914526999 -1.21983802 0.305858999 + vertex -0.930561006 -1.14014304 0.356413007 + endloop + endfacet + + facet normal 0.90709877 0.334623903 0.25533995 + outer loop + vertex -0.936339974 -1.72346997 1.14139199 + vertex -0.962791979 -1.69832397 1.20240998 + vertex -0.930561006 -1.14014304 0.356413007 + endloop + endfacet + + facet normal 0.985707998 0.131661996 0.105094999 + outer loop + vertex -0.914526999 -1.21983802 0.305858999 + vertex -0.936339974 -1.72346997 1.14139199 + vertex -0.930561006 -1.14014304 0.356413007 + endloop + endfacet + + facet normal 0.826960862 -0.293624967 0.479499906 + outer loop + vertex -0.936339974 -1.72346997 1.14139199 + vertex -1.01528394 -1.87718606 1.18341398 + vertex -0.962791979 -1.69832397 1.20240998 + endloop + endfacet + + facet normal 0.887753963 -0.388381988 0.247087985 + outer loop + vertex -0.936339974 -1.72346997 1.14139199 + vertex -0.975238979 -1.83346891 1.10825098 + vertex -1.01528394 -1.87718606 1.18341398 + endloop + endfacet + + facet normal 0.963315189 -0.228283063 0.141105026 + outer loop + vertex -1.08453 -2.80168796 0.160469994 + vertex -1.10992396 -2.88685608 0.196043 + vertex -1.01528394 -1.87718606 1.18341398 + endloop + endfacet + + facet normal 0.893704414 -0.361284137 0.266019106 + outer loop + vertex -0.975238979 -1.83346891 1.10825098 + vertex -1.08453 -2.80168796 0.160469994 + vertex -1.01528394 -1.87718606 1.18341398 + endloop + endfacet + + facet normal 0.964169204 -0.246102065 0.0990530252 + outer loop + vertex -1.08453 -2.80168796 0.160469994 + vertex -1.06995296 -3.29647708 -1.21073997 + vertex -1.10992396 -2.88685608 0.196043 + endloop + endfacet + + facet normal 0.968371511 -0.231236875 0.0937339589 + outer loop + vertex -1.08453 -2.80168796 0.160469994 + vertex -1.052176 -3.24943709 -1.27835 + vertex -1.06995296 -3.29647708 -1.21073997 + endloop + endfacet + + facet normal 0.955379665 -0.292643875 0.0401169844 + outer loop + vertex -1.11563802 -3.50374198 -1.63470399 + vertex -1.12068105 -3.5169549 -1.61099398 + vertex -1.06995296 -3.29647708 -1.21073997 + endloop + endfacet + + facet normal 0.953478754 -0.298360944 0.0431159884 + outer loop + vertex -1.052176 -3.24943709 -1.27835 + vertex -1.11563802 -3.50374198 -1.63470399 + vertex -1.06995296 -3.29647708 -1.21073997 + endloop + endfacet + + facet normal 0.0729859844 -0.424989969 0.902250886 + outer loop + vertex -0.790997982 -0.401975006 -0.0302719995 + vertex -0.808588982 -0.547342002 -0.0973210037 + vertex -0.929682016 -0.485058993 -0.0581879988 + endloop + endfacet + + facet normal 0.288051158 0.809107423 0.51222229 + outer loop + vertex -1.09768903 -3.50087404 -1.64932895 + vertex -1.09849703 -3.51748109 -1.62264204 + vertex -1.11563802 -3.50374198 -1.63470399 + endloop + endfacet + + facet normal 0.307018965 0.427585959 0.850240946 + outer loop + vertex -0.837188005 -1.27625394 0.306304008 + vertex -0.914526999 -1.21983802 0.305858999 + vertex -0.929682016 -0.485058993 -0.0581879988 + endloop + endfacet + + facet normal 0.465866804 0.414588839 0.781718731 + outer loop + vertex -0.808588982 -0.547342002 -0.0973210037 + vertex -0.837188005 -1.27625394 0.306304008 + vertex -0.929682016 -0.485058993 -0.0581879988 + endloop + endfacet + + facet normal 0.524762154 0.722922206 0.449453115 + outer loop + vertex -0.837188005 -1.27625394 0.306304008 + vertex -0.936339974 -1.72346997 1.14139199 + vertex -0.914526999 -1.21983802 0.305858999 + endloop + endfacet + + facet normal 0.420921862 0.777905762 0.466569841 + outer loop + vertex -0.837188005 -1.27625394 0.306304008 + vertex -0.879109979 -1.74321699 1.12268603 + vertex -0.936339974 -1.72346997 1.14139199 + endloop + endfacet + + facet normal 0.225110888 -0.353195846 0.908062637 + outer loop + vertex -0.882930994 -1.81293797 1.09335303 + vertex -0.975238979 -1.83346891 1.10825098 + vertex -0.936339974 -1.72346997 1.14139199 + endloop + endfacet + + facet normal 0.161638066 -0.390208155 0.906427443 + outer loop + vertex -0.879109979 -1.74321699 1.12268603 + vertex -0.882930994 -1.81293797 1.09335303 + vertex -0.936339974 -1.72346997 1.14139199 + endloop + endfacet + + facet normal 0.262340188 -0.689983487 0.67461139 + outer loop + vertex -0.882930994 -1.81293797 1.09335303 + vertex -1.08453 -2.80168796 0.160469994 + vertex -0.975238979 -1.83346891 1.10825098 + endloop + endfacet + + facet normal 0.304611921 -0.685768843 0.661008835 + outer loop + vertex -0.882930994 -1.81293797 1.09335303 + vertex -1.00135601 -2.75531101 0.170255005 + vertex -1.08453 -2.80168796 0.160469994 + endloop + endfacet + + facet normal 0.576873899 -0.77617085 0.254509926 + outer loop + vertex -1.009624 -3.23152399 -1.32017207 + vertex -1.052176 -3.24943709 -1.27835 + vertex -1.08453 -2.80168796 0.160469994 + endloop + endfacet + + facet normal 0.444392174 -0.854044378 0.270414114 + outer loop + vertex -1.00135601 -2.75531101 0.170255005 + vertex -1.009624 -3.23152399 -1.32017207 + vertex -1.08453 -2.80168796 0.160469994 + endloop + endfacet + + facet normal 0.642835021 -0.673016012 0.365804017 + outer loop + vertex -1.009624 -3.23152399 -1.32017207 + vertex -1.11563802 -3.50374198 -1.63470399 + vertex -1.052176 -3.24943709 -1.27835 + endloop + endfacet + + facet normal 0.496770978 -0.732070029 0.466145962 + outer loop + vertex -1.009624 -3.23152399 -1.32017207 + vertex -1.09768903 -3.50087404 -1.64932895 + vertex -1.11563802 -3.50374198 -1.63470399 + endloop + endfacet + + facet normal 0.0729879811 -0.424989879 0.902250707 + outer loop + vertex -0.790997982 -0.401975006 -0.0302719995 + vertex -0.674250007 -0.500160992 -0.0859650001 + vertex -0.808588982 -0.547342002 -0.0973210037 + endloop + endfacet + + facet normal 0.288186014 0.809071004 0.512204051 + outer loop + vertex -1.08034897 -3.51051307 -1.64385903 + vertex -1.09849703 -3.51748109 -1.62264204 + vertex -1.09768903 -3.50087404 -1.64932895 + endloop + endfacet + + facet normal -0.239184961 0.477530897 0.845431745 + outer loop + vertex -0.674250007 -0.500160992 -0.0859650001 + vertex -0.837188005 -1.27625394 0.306304008 + vertex -0.808588982 -0.547342002 -0.0973210037 + endloop + endfacet + + facet normal -0.511622965 0.470719934 0.718793988 + outer loop + vertex -0.674250007 -0.500160992 -0.0859650001 + vertex -0.756784022 -1.266909 0.357414007 + vertex -0.837188005 -1.27625394 0.306304008 + endloop + endfacet + + facet normal -0.382025898 0.810512841 0.443987906 + outer loop + vertex -0.834199011 -1.74269497 1.16037703 + vertex -0.879109979 -1.74321699 1.12268603 + vertex -0.837188005 -1.27625394 0.306304008 + endloop + endfacet + + facet normal -0.377227038 0.812248111 0.44491905 + outer loop + vertex -0.756784022 -1.266909 0.357414007 + vertex -0.834199011 -1.74269497 1.16037703 + vertex -0.837188005 -1.27625394 0.306304008 + endloop + endfacet + + facet normal -0.615875244 -0.27661112 0.737688243 + outer loop + vertex -0.834199011 -1.74269497 1.16037703 + vertex -0.882930994 -1.81293797 1.09335303 + vertex -0.879109979 -1.74321699 1.12268603 + endloop + endfacet + + facet normal -0.620292902 -0.27173996 0.735794902 + outer loop + vertex -0.834199011 -1.74269497 1.16037703 + vertex -0.807870984 -1.83105493 1.14994001 + vertex -0.882930994 -1.81293797 1.09335303 + endloop + endfacet + + facet normal -0.567791045 -0.538507044 0.622594059 + outer loop + vertex -0.923036993 -2.78265285 0.218032002 + vertex -1.00135601 -2.75531101 0.170255005 + vertex -0.882930994 -1.81293797 1.09335303 + endloop + endfacet + + facet normal -0.588796079 -0.528039038 0.61195904 + outer loop + vertex -0.807870984 -1.83105493 1.14994001 + vertex -0.923036993 -2.78265285 0.218032002 + vertex -0.882930994 -1.81293797 1.09335303 + endloop + endfacet + + facet normal -0.461007148 -0.844552219 0.272404075 + outer loop + vertex -0.923036993 -2.78265285 0.218032002 + vertex -1.009624 -3.23152399 -1.32017207 + vertex -1.00135601 -2.75531101 0.170255005 + endloop + endfacet + + facet normal -0.626499951 -0.73802501 0.250632972 + outer loop + vertex -0.923036993 -2.78265285 0.218032002 + vertex -0.974340975 -3.25622606 -1.30471396 + vertex -1.009624 -3.23152399 -1.32017207 + endloop + endfacet + + facet normal -0.521524727 -0.586632669 0.61957562 + outer loop + vertex -1.08034897 -3.51051307 -1.64385903 + vertex -1.09768903 -3.50087404 -1.64932895 + vertex -1.009624 -3.23152399 -1.32017207 + endloop + endfacet + + facet normal -0.621271908 -0.521286964 0.58504796 + outer loop + vertex -0.974340975 -3.25622606 -1.30471396 + vertex -1.08034897 -3.51051307 -1.64385903 + vertex -1.009624 -3.23152399 -1.32017207 + endloop + endfacet + + facet normal 0.0729890391 -0.42498818 0.902251422 + outer loop + vertex -0.790997982 -0.401975006 -0.0302719995 + vertex -0.627824008 -0.379043013 -0.0326699987 + vertex -0.674250007 -0.500160992 -0.0859650001 + endloop + endfacet + + facet normal 0.288157016 0.809092045 0.512187064 + outer loop + vertex -1.07667601 -3.52539802 -1.62241101 + vertex -1.09849703 -3.51748109 -1.62264204 + vertex -1.08034897 -3.51051307 -1.64385903 + endloop + endfacet + + facet normal -0.970688343 0.189770058 0.147484049 + outer loop + vertex -0.733861029 -1.19883895 0.420700014 + vertex -0.756784022 -1.266909 0.357414007 + vertex -0.674250007 -0.500160992 -0.0859650001 + endloop + endfacet + + facet normal -0.937109649 0.253748894 0.239659891 + outer loop + vertex -0.627824008 -0.379043013 -0.0326699987 + vertex -0.733861029 -1.19883895 0.420700014 + vertex -0.674250007 -0.500160992 -0.0859650001 + endloop + endfacet + + facet normal -0.962245107 0.26455602 0.0639880076 + outer loop + vertex -0.733861029 -1.19883895 0.420700014 + vertex -0.834199011 -1.74269497 1.16037703 + vertex -0.756784022 -1.266909 0.357414007 + endloop + endfacet + + facet normal -0.992402613 0.11104396 -0.0529739819 + outer loop + vertex -0.733861029 -1.19883895 0.420700014 + vertex -0.835425019 -1.72230005 1.22608101 + vertex -0.834199011 -1.74269497 1.16037703 + endloop + endfacet + + facet normal -0.955060899 -0.270181984 -0.121902987 + outer loop + vertex -0.806580007 -1.87417507 1.23539805 + vertex -0.807870984 -1.83105493 1.14994001 + vertex -0.834199011 -1.74269497 1.16037703 + endloop + endfacet + + facet normal -0.982130885 -0.184146971 0.0388449915 + outer loop + vertex -0.835425019 -1.72230005 1.22608101 + vertex -0.806580007 -1.87417507 1.23539805 + vertex -0.834199011 -1.74269497 1.16037703 + endloop + endfacet + + facet normal -0.99619478 0.0708289742 0.0507849865 + outer loop + vertex -0.806580007 -1.87417507 1.23539805 + vertex -0.923036993 -2.78265285 0.218032002 + vertex -0.807870984 -1.83105493 1.14994001 + endloop + endfacet + + facet normal -0.982285738 -0.0691299886 0.174171969 + outer loop + vertex -0.806580007 -1.87417507 1.23539805 + vertex -0.908545017 -2.86312103 0.267821014 + vertex -0.923036993 -2.78265285 0.218032002 + endloop + endfacet + + facet normal -0.999482691 0.00903299823 0.0308649931 + outer loop + vertex -0.972894013 -3.30494308 -1.24361598 + vertex -0.974340975 -3.25622606 -1.30471396 + vertex -0.923036993 -2.78265285 0.218032002 + endloop + endfacet + + facet normal -0.98847574 -0.128696963 0.0797049776 + outer loop + vertex -0.908545017 -2.86312103 0.267821014 + vertex -0.972894013 -3.30494308 -1.24361598 + vertex -0.923036993 -2.78265285 0.218032002 + endloop + endfacet + + facet normal -0.969163716 0.180971935 0.167244941 + outer loop + vertex -0.972894013 -3.30494308 -1.24361598 + vertex -1.08034897 -3.51051307 -1.64385903 + vertex -0.974340975 -3.25622606 -1.30471396 + endloop + endfacet + + facet normal -0.972173095 0.078271009 0.220801026 + outer loop + vertex -0.972894013 -3.30494308 -1.24361598 + vertex -1.07667601 -3.52539802 -1.62241101 + vertex -1.08034897 -3.51051307 -1.64385903 + endloop + endfacet + + facet normal -0.0414419994 0.415021986 0.908867061 + outer loop + vertex -0.614050984 0.300803006 -0.0098240003 + vertex -0.709182978 0.205512002 0.029352 + vertex -0.771920979 0.340665013 -0.0352250002 + endloop + endfacet + + facet normal 0.109958999 -0.990333974 0.084544003 + outer loop + vertex -0.45371899 3.54508805 -1.52100492 + vertex -0.47586599 3.54209304 -1.52728605 + vertex -0.468991995 3.54522705 -1.49951506 + endloop + endfacet + + facet normal -0.570165336 0.334529191 -0.750334442 + outer loop + vertex -0.614050984 0.300803006 -0.0098240003 + vertex -0.712240994 1.12719202 0.433227003 + vertex -0.77559799 1.05748606 0.450291991 + endloop + endfacet + + facet normal -0.611606061 0.311619043 -0.727208078 + outer loop + vertex -0.614050984 0.300803006 -0.0098240003 + vertex -0.77559799 1.05748606 0.450291991 + vertex -0.709182978 0.205512002 0.029352 + endloop + endfacet + + facet normal -0.695668578 0.507932723 -0.507985711 + outer loop + vertex -0.712240994 1.12719202 0.433227003 + vertex -0.897680998 1.65840197 1.21833503 + vertex -0.77559799 1.05748606 0.450291991 + endloop + endfacet + + facet normal -0.568472028 0.601665974 -0.561104 + outer loop + vertex -0.897680998 1.65840197 1.21833503 + vertex -0.953411996 1.63847899 1.25343502 + vertex -0.77559799 1.05748606 0.450291991 + endloop + endfacet + + facet normal -0.332945973 0.200343996 -0.921416938 + outer loop + vertex -0.897680998 1.65840197 1.21833503 + vertex -0.855413973 1.80642998 1.23524797 + vertex -0.932361007 1.84956503 1.27243102 + endloop + endfacet + + facet normal -0.561175942 0.129531994 -0.817498028 + outer loop + vertex -0.897680998 1.65840197 1.21833503 + vertex -0.932361007 1.84956503 1.27243102 + vertex -0.953411996 1.63847899 1.25343502 + endloop + endfacet + + facet normal -0.57141304 -0.403153062 -0.714811087 + outer loop + vertex -0.855413973 1.80642998 1.23524797 + vertex -0.710753024 2.34805298 0.814131975 + vertex -0.932361007 1.84956503 1.27243102 + endloop + endfacet + + facet normal -0.60945785 -0.37249589 -0.699862778 + outer loop + vertex -0.710753024 2.34805298 0.814131975 + vertex -0.764732003 2.42695308 0.819145024 + vertex -0.932361007 1.84956503 1.27243102 + endloop + endfacet + + facet normal -0.704304576 -0.627007663 -0.332890779 + outer loop + vertex -0.710753024 2.34805298 0.814131975 + vertex -0.440544993 3.16025901 -1.28735995 + vertex -0.480170995 3.16933012 -1.220608 + endloop + endfacet + + facet normal -0.79560107 -0.52511704 -0.302111 + outer loop + vertex -0.710753024 2.34805298 0.814131975 + vertex -0.480170995 3.16933012 -1.220608 + vertex -0.764732003 2.42695308 0.819145024 + endloop + endfacet + + facet normal -0.836549938 -0.30498296 -0.455159009 + outer loop + vertex -0.440544993 3.16025901 -1.28735995 + vertex -0.45371899 3.54508805 -1.52100492 + vertex -0.480170995 3.16933012 -1.220608 + endloop + endfacet + + facet normal -0.756833076 -0.374799043 -0.535471082 + outer loop + vertex -0.45371899 3.54508805 -1.52100492 + vertex -0.468991995 3.54522705 -1.49951506 + vertex -0.480170995 3.16933012 -1.220608 + endloop + endfacet + + facet normal -0.0414459892 0.4150199 0.908867776 + outer loop + vertex -0.709182978 0.205512002 0.029352 + vertex -0.851558983 0.211992994 0.0198999997 + vertex -0.771920979 0.340665013 -0.0352250002 + endloop + endfacet + + facet normal 0.10995701 -0.990334094 0.0845450088 + outer loop + vertex -0.468991995 3.54522705 -1.49951506 + vertex -0.47586599 3.54209304 -1.52728605 + vertex -0.489443004 3.54300499 -1.49893701 + endloop + endfacet + + facet normal 0.0794950202 0.44653213 -0.891229272 + outer loop + vertex -0.709182978 0.205512002 0.029352 + vertex -0.77559799 1.05748606 0.450291991 + vertex -0.851558983 0.211992994 0.0198999997 + endloop + endfacet + + facet normal 0.362293869 0.396774888 -0.843393683 + outer loop + vertex -0.77559799 1.05748606 0.450291991 + vertex -0.859055996 1.04065704 0.406524003 + vertex -0.851558983 0.211992994 0.0198999997 + endloop + endfacet + + facet normal 0.13829203 0.816729188 -0.560204148 + outer loop + vertex -0.77559799 1.05748606 0.450291991 + vertex -0.953411996 1.63847899 1.25343502 + vertex -1.00819802 1.63379192 1.23307705 + endloop + endfacet + + facet normal 0.130304053 0.816518307 -0.562422156 + outer loop + vertex -0.77559799 1.05748606 0.450291991 + vertex -1.00819802 1.63379192 1.23307705 + vertex -0.859055996 1.04065704 0.406524003 + endloop + endfacet + + facet normal 0.344124049 0.0500540063 -0.937589109 + outer loop + vertex -0.953411996 1.63847899 1.25343502 + vertex -0.932361007 1.84956503 1.27243102 + vertex -1.00819802 1.63379192 1.23307705 + endloop + endfacet + + facet normal 0.410272747 0.0220819861 -0.91169548 + outer loop + vertex -0.932361007 1.84956503 1.27243102 + vertex -1.01917601 1.86060798 1.23362994 + vertex -1.00819802 1.63379192 1.23307705 + endloop + endfacet + + facet normal 0.258674949 -0.641833842 -0.72189784 + outer loop + vertex -0.932361007 1.84956503 1.27243102 + vertex -0.764732003 2.42695308 0.819145024 + vertex -0.845875025 2.44682598 0.772400022 + endloop + endfacet + + facet normal 0.242763117 -0.643158317 -0.72623229 + outer loop + vertex -0.932361007 1.84956503 1.27243102 + vertex -0.845875025 2.44682598 0.772400022 + vertex -1.01917601 1.86060798 1.23362994 + endloop + endfacet + + facet normal -0.0306019913 -0.937877655 -0.345613897 + outer loop + vertex -0.764732003 2.42695308 0.819145024 + vertex -0.480170995 3.16933012 -1.220608 + vertex -0.845875025 2.44682598 0.772400022 + endloop + endfacet + + facet normal 0.0047039995 -0.940395951 -0.340048969 + outer loop + vertex -0.480170995 3.16933012 -1.220608 + vertex -0.525406003 3.1667769 -1.21417403 + vertex -0.845875025 2.44682598 0.772400022 + endloop + endfacet + + facet normal 0.0421100184 -0.596148252 -0.801769316 + outer loop + vertex -0.480170995 3.16933012 -1.220608 + vertex -0.468991995 3.54522705 -1.49951506 + vertex -0.489443004 3.54300499 -1.49893701 + endloop + endfacet + + facet normal -0.0798940063 -0.596715033 -0.798466146 + outer loop + vertex -0.480170995 3.16933012 -1.220608 + vertex -0.489443004 3.54300499 -1.49893701 + vertex -0.525406003 3.1667769 -1.21417403 + endloop + endfacet + + facet normal -0.0414439961 0.415019959 0.908867955 + outer loop + vertex -0.851558983 0.211992994 0.0198999997 + vertex -0.933965981 0.31536901 -0.0310629997 + vertex -0.771920979 0.340665013 -0.0352250002 + endloop + endfacet + + facet normal 0.109933957 -0.99033761 0.0845339671 + outer loop + vertex -0.489443004 3.54300499 -1.49893701 + vertex -0.47586599 3.54209304 -1.52728605 + vertex -0.499669999 3.540097 -1.51970506 + endloop + endfacet + + facet normal 0.903346419 0.188033089 -0.385498166 + outer loop + vertex -0.851558983 0.211992994 0.0198999997 + vertex -0.859055996 1.04065704 0.406524003 + vertex -0.899770975 1.089378 0.334881008 + endloop + endfacet + + facet normal 0.726601124 0.267169058 -0.632986128 + outer loop + vertex -0.851558983 0.211992994 0.0198999997 + vertex -0.899770975 1.089378 0.334881008 + vertex -0.933965981 0.31536901 -0.0310629997 + endloop + endfacet + + facet normal 0.867568493 0.464764267 -0.176972091 + outer loop + vertex -0.859055996 1.04065704 0.406524003 + vertex -1.00819802 1.63379192 1.23307705 + vertex -0.899770975 1.089378 0.334881008 + endloop + endfacet + + facet normal 0.925577819 0.362778932 -0.108153984 + outer loop + vertex -1.00819802 1.63379192 1.23307705 + vertex -1.02078402 1.64787102 1.17259204 + vertex -0.899770975 1.089378 0.334881008 + endloop + endfacet + + facet normal 0.932907045 0.0460240059 -0.357164025 + outer loop + vertex -1.00819802 1.63379192 1.23307705 + vertex -1.01917601 1.86060798 1.23362994 + vertex -1.05048597 1.83123994 1.14806604 + endloop + endfacet + + facet normal 0.975855947 0.135112986 -0.171608999 + outer loop + vertex -1.00819802 1.63379192 1.23307705 + vertex -1.05048597 1.83123994 1.14806604 + vertex -1.02078402 1.64787102 1.17259204 + endloop + endfacet + + facet normal 0.89283818 -0.410217047 -0.185908034 + outer loop + vertex -1.01917601 1.86060798 1.23362994 + vertex -0.845875025 2.44682598 0.772400022 + vertex -1.05048597 1.83123994 1.14806604 + endloop + endfacet + + facet normal 0.85756886 -0.443764925 -0.260092974 + outer loop + vertex -0.845875025 2.44682598 0.772400022 + vertex -0.893078029 2.39270806 0.709097981 + vertex -1.05048597 1.83123994 1.14806604 + endloop + endfacet + + facet normal 0.783792257 -0.613570154 -0.0959240273 + outer loop + vertex -0.845875025 2.44682598 0.772400022 + vertex -0.525406003 3.1667769 -1.21417403 + vertex -0.542185009 3.15452409 -1.27289903 + endloop + endfacet + + facet normal 0.799032509 -0.594929636 -0.0872109383 + outer loop + vertex -0.845875025 2.44682598 0.772400022 + vertex -0.542185009 3.15452409 -1.27289903 + vertex -0.893078029 2.39270806 0.709097981 + endloop + endfacet + + facet normal 0.942718029 -0.253892004 -0.21638301 + outer loop + vertex -0.525406003 3.1667769 -1.21417403 + vertex -0.489443004 3.54300499 -1.49893701 + vertex -0.542185009 3.15452409 -1.27289903 + endloop + endfacet + + facet normal 0.862533689 -0.336815894 -0.377611876 + outer loop + vertex -0.489443004 3.54300499 -1.49893701 + vertex -0.499669999 3.540097 -1.51970506 + vertex -0.542185009 3.15452409 -1.27289903 + endloop + endfacet + + facet normal -0.0414459854 0.41502884 0.908863723 + outer loop + vertex -0.933965981 0.31536901 -0.0310629997 + vertex -0.894349992 0.43779099 -0.0851600021 + vertex -0.771920979 0.340665013 -0.0352250002 + endloop + endfacet + + facet normal 0.109905973 -0.990348756 0.0844399855 + outer loop + vertex -0.499669999 3.540097 -1.51970506 + vertex -0.47586599 3.54209304 -1.52728605 + vertex -0.491973996 3.5386939 -1.54618096 + endloop + endfacet + + facet normal 0.941180944 -0.177515998 0.287517965 + outer loop + vertex -0.933965981 0.31536901 -0.0310629997 + vertex -0.899770975 1.089378 0.334881008 + vertex -0.894349992 0.43779099 -0.0851600021 + endloop + endfacet + + facet normal 0.925101757 -0.200271949 0.322610945 + outer loop + vertex -0.899770975 1.089378 0.334881008 + vertex -0.867084026 1.16696095 0.289312005 + vertex -0.894349992 0.43779099 -0.0851600021 + endloop + endfacet + + facet normal 0.80964005 -0.427607 0.402038991 + outer loop + vertex -0.899770975 1.089378 0.334881008 + vertex -1.02078402 1.64787102 1.17259204 + vertex -0.981692016 1.67011404 1.11752605 + endloop + endfacet + + facet normal 0.933612227 -0.233975038 0.271337062 + outer loop + vertex -0.899770975 1.089378 0.334881008 + vertex -0.981692016 1.67011404 1.11752605 + vertex -0.867084026 1.16696095 0.289312005 + endloop + endfacet + + facet normal 0.756896913 0.205576986 0.620359004 + outer loop + vertex -1.02078402 1.64787102 1.17259204 + vertex -1.05048597 1.83123994 1.14806604 + vertex -0.981692016 1.67011404 1.11752605 + endloop + endfacet + + facet normal 0.866550148 0.293460041 0.403698057 + outer loop + vertex -1.05048597 1.83123994 1.14806604 + vertex -1.00271201 1.78357601 1.08016706 + vertex -0.981692016 1.67011404 1.11752605 + endloop + endfacet + + facet normal 0.904024541 0.0755139664 0.4207578 + outer loop + vertex -1.05048597 1.83123994 1.14806604 + vertex -0.893078029 2.39270806 0.709097981 + vertex -0.870796978 2.30535197 0.676904976 + endloop + endfacet + + facet normal 0.856916606 0.161637917 0.48945576 + outer loop + vertex -1.05048597 1.83123994 1.14806604 + vertex -0.870796978 2.30535197 0.676904976 + vertex -1.00271201 1.78357601 1.08016706 + endloop + endfacet + + facet normal 0.959722757 0.15956296 0.231239945 + outer loop + vertex -0.893078029 2.39270806 0.709097981 + vertex -0.542185009 3.15452409 -1.27289903 + vertex -0.870796978 2.30535197 0.676904976 + endloop + endfacet + + facet normal 0.942926586 0.216352895 0.25314188 + outer loop + vertex -0.542185009 3.15452409 -1.27289903 + vertex -0.517876983 3.14179301 -1.35256696 + vertex -0.870796978 2.30535197 0.676904976 + endloop + endfacet + + facet normal 0.958869755 0.070299983 0.275002927 + outer loop + vertex -0.542185009 3.15452409 -1.27289903 + vertex -0.499669999 3.540097 -1.51970506 + vertex -0.491973996 3.5386939 -1.54618096 + endloop + endfacet + + facet normal 0.957076609 0.07421197 0.280172884 + outer loop + vertex -0.542185009 3.15452409 -1.27289903 + vertex -0.491973996 3.5386939 -1.54618096 + vertex -0.517876983 3.14179301 -1.35256696 + endloop + endfacet + + facet normal -0.0414430015 0.415033042 0.908861995 + outer loop + vertex -0.894349992 0.43779099 -0.0851600021 + vertex -0.762543023 0.487076014 -0.101654999 + vertex -0.771920979 0.340665013 -0.0352250002 + endloop + endfacet + + facet normal 0.109901994 -0.990348935 0.0844429955 + outer loop + vertex -0.491973996 3.5386939 -1.54618096 + vertex -0.47586599 3.54209304 -1.52728605 + vertex -0.472149014 3.53985 -1.55842602 + endloop + endfacet + + facet normal 0.109039977 -0.457335919 0.882583737 + outer loop + vertex -0.894349992 0.43779099 -0.0851600021 + vertex -0.867084026 1.16696095 0.289312005 + vertex -0.78560698 1.21498394 0.304129988 + endloop + endfacet + + facet normal 0.277854979 -0.461013973 0.842770934 + outer loop + vertex -0.894349992 0.43779099 -0.0851600021 + vertex -0.78560698 1.21498394 0.304129988 + vertex -0.762543023 0.487076014 -0.101654999 + endloop + endfacet + + facet normal 0.361492991 -0.773815036 0.520128012 + outer loop + vertex -0.867084026 1.16696095 0.289312005 + vertex -0.981692016 1.67011404 1.11752605 + vertex -0.78560698 1.21498394 0.304129988 + endloop + endfacet + + facet normal 0.251163065 -0.817641199 0.518054128 + outer loop + vertex -0.981692016 1.67011404 1.11752605 + vertex -0.920360029 1.68377101 1.10934496 + vertex -0.78560698 1.21498394 0.304129988 + endloop + endfacet + + facet normal 0.0991519839 0.327727944 0.93955487 + outer loop + vertex -0.981692016 1.67011404 1.11752605 + vertex -1.00271201 1.78357601 1.08016706 + vertex -0.911831021 1.75351 1.08106399 + endloop + endfacet + + facet normal 0.0410849974 0.371169001 0.927655935 + outer loop + vertex -0.981692016 1.67011404 1.11752605 + vertex -0.911831021 1.75351 1.08106399 + vertex -0.920360029 1.68377101 1.10934496 + endloop + endfacet + + facet normal 0.181426108 0.57224232 0.799764454 + outer loop + vertex -1.00271201 1.78357601 1.08016706 + vertex -0.870796978 2.30535197 0.676904976 + vertex -0.911831021 1.75351 1.08106399 + endloop + endfacet + + facet normal 0.172062024 0.573693037 0.800794065 + outer loop + vertex -0.870796978 2.30535197 0.676904976 + vertex -0.795808017 2.25053501 0.70006299 + vertex -0.911831021 1.75351 1.08106399 + endloop + endfacet + + facet normal 0.41394791 0.814008892 0.407475978 + outer loop + vertex -0.870796978 2.30535197 0.676904976 + vertex -0.517876983 3.14179301 -1.35256696 + vertex -0.470782012 3.13817596 -1.39318299 + endloop + endfacet + + facet normal 0.453948915 0.792807758 0.406676918 + outer loop + vertex -0.870796978 2.30535197 0.676904976 + vertex -0.470782012 3.13817596 -1.39318299 + vertex -0.795808017 2.25053501 0.70006299 + endloop + endfacet + + facet normal 0.635360062 0.304674029 0.709571123 + outer loop + vertex -0.517876983 3.14179301 -1.35256696 + vertex -0.491973996 3.5386939 -1.54618096 + vertex -0.470782012 3.13817596 -1.39318299 + endloop + endfacet + + facet normal 0.480959207 0.334958136 0.810235322 + outer loop + vertex -0.491973996 3.5386939 -1.54618096 + vertex -0.472149014 3.53985 -1.55842602 + vertex -0.470782012 3.13817596 -1.39318299 + endloop + endfacet + + facet normal -0.0414400063 0.415033072 0.908862054 + outer loop + vertex -0.762543023 0.487076014 -0.101654999 + vertex -0.637798011 0.426109999 -0.0681279972 + vertex -0.771920979 0.340665013 -0.0352250002 + endloop + endfacet + + facet normal 0.110049009 -0.990331113 0.0844600126 + outer loop + vertex -0.472149014 3.53985 -1.55842602 + vertex -0.47586599 3.54209304 -1.52728605 + vertex -0.455123991 3.54269695 -1.54722202 + endloop + endfacet + + facet normal -0.430224836 -0.44991383 0.782613695 + outer loop + vertex -0.762543023 0.487076014 -0.101654999 + vertex -0.78560698 1.21498394 0.304129988 + vertex -0.637798011 0.426109999 -0.0681279972 + endloop + endfacet + + facet normal -0.674363017 -0.414663017 0.610974014 + outer loop + vertex -0.78560698 1.21498394 0.304129988 + vertex -0.716696024 1.19728494 0.368178993 + vertex -0.637798011 0.426109999 -0.0681279972 + endloop + endfacet + + facet normal -0.534693182 -0.766140282 0.356556147 + outer loop + vertex -0.78560698 1.21498394 0.304129988 + vertex -0.920360029 1.68377101 1.10934496 + vertex -0.882972002 1.67855692 1.15420997 + endloop + endfacet + + facet normal -0.530332863 -0.768367827 0.358270913 + outer loop + vertex -0.78560698 1.21498394 0.304129988 + vertex -0.882972002 1.67855692 1.15420997 + vertex -0.716696024 1.19728494 0.368178993 + endloop + endfacet + + facet normal -0.702823222 0.339469105 0.62514019 + outer loop + vertex -0.920360029 1.68377101 1.10934496 + vertex -0.911831021 1.75351 1.08106399 + vertex -0.882972002 1.67855692 1.15420997 + endloop + endfacet + + facet normal -0.707317054 0.335090995 0.62242806 + outer loop + vertex -0.911831021 1.75351 1.08106399 + vertex -0.846276999 1.76368093 1.15008199 + vertex -0.882972002 1.67855692 1.15420997 + endloop + endfacet + + facet normal -0.619192243 0.563640237 0.54672724 + outer loop + vertex -0.911831021 1.75351 1.08106399 + vertex -0.795808017 2.25053501 0.70006299 + vertex -0.72458303 2.26954007 0.761135995 + endloop + endfacet + + facet normal -0.640845776 0.558843732 0.526317775 + outer loop + vertex -0.911831021 1.75351 1.08106399 + vertex -0.72458303 2.26954007 0.761135995 + vertex -0.846276999 1.76368093 1.15008199 + endloop + endfacet + + facet normal -0.466304839 0.838133693 0.28300491 + outer loop + vertex -0.795808017 2.25053501 0.70006299 + vertex -0.470782012 3.13817596 -1.39318299 + vertex -0.72458303 2.26954007 0.761135995 + endloop + endfacet + + facet normal -0.445953965 0.847108006 0.289020956 + outer loop + vertex -0.470782012 3.13817596 -1.39318299 + vertex -0.436367005 3.14639306 -1.36416399 + vertex -0.72458303 2.26954007 0.761135995 + endloop + endfacet + + facet normal -0.558003902 0.314087898 0.768101811 + outer loop + vertex -0.470782012 3.13817596 -1.39318299 + vertex -0.472149014 3.53985 -1.55842602 + vertex -0.455123991 3.54269695 -1.54722202 + endloop + endfacet + + facet normal -0.656410813 0.290523916 0.696218848 + outer loop + vertex -0.470782012 3.13817596 -1.39318299 + vertex -0.455123991 3.54269695 -1.54722202 + vertex -0.436367005 3.14639306 -1.36416399 + endloop + endfacet + + facet normal -0.041439008 0.415031046 0.908863068 + outer loop + vertex -0.637798011 0.426109999 -0.0681279972 + vertex -0.614050984 0.300803006 -0.0098240003 + vertex -0.771920979 0.340665013 -0.0352250002 + endloop + endfacet + + facet normal 0.109999016 -0.990341127 0.0844070092 + outer loop + vertex -0.455123991 3.54269695 -1.54722202 + vertex -0.47586599 3.54209304 -1.52728605 + vertex -0.45371899 3.54508805 -1.52100492 + endloop + endfacet + + facet normal -0.995849967 -0.0872669965 -0.0258349981 + outer loop + vertex -0.637798011 0.426109999 -0.0681279972 + vertex -0.716696024 1.19728494 0.368178993 + vertex -0.712240994 1.12719202 0.433227003 + endloop + endfacet + + facet normal -0.985554934 -0.154403999 0.0695760027 + outer loop + vertex -0.637798011 0.426109999 -0.0681279972 + vertex -0.712240994 1.12719202 0.433227003 + vertex -0.614050984 0.300803006 -0.0098240003 + endloop + endfacet + + facet normal -0.980780065 -0.162526011 -0.107962005 + outer loop + vertex -0.716696024 1.19728494 0.368178993 + vertex -0.882972002 1.67855692 1.15420997 + vertex -0.712240994 1.12719202 0.433227003 + endloop + endfacet + + facet normal -0.974204123 -0.00670300052 -0.22556901 + outer loop + vertex -0.882972002 1.67855692 1.15420997 + vertex -0.897680998 1.65840197 1.21833503 + vertex -0.712240994 1.12719202 0.433227003 + endloop + endfacet + + facet normal -0.886417329 0.368533105 -0.280085117 + outer loop + vertex -0.882972002 1.67855692 1.15420997 + vertex -0.846276999 1.76368093 1.15008199 + vertex -0.855413973 1.80642998 1.23524797 + endloop + endfacet + + facet normal -0.949701548 0.285797149 -0.128012076 + outer loop + vertex -0.882972002 1.67855692 1.15420997 + vertex -0.855413973 1.80642998 1.23524797 + vertex -0.897680998 1.65840197 1.21833503 + endloop + endfacet + + facet normal -0.980542064 0.111841008 -0.161335021 + outer loop + vertex -0.846276999 1.76368093 1.15008199 + vertex -0.72458303 2.26954007 0.761135995 + vertex -0.855413973 1.80642998 1.23524797 + endloop + endfacet + + facet normal -0.975146174 0.212986052 -0.0610480122 + outer loop + vertex -0.72458303 2.26954007 0.761135995 + vertex -0.710753024 2.34805298 0.814131975 + vertex -0.855413973 1.80642998 1.23524797 + endloop + endfacet + + facet normal -0.987725139 0.135249019 -0.0781470165 + outer loop + vertex -0.72458303 2.26954007 0.761135995 + vertex -0.436367005 3.14639306 -1.36416399 + vertex -0.440544993 3.16025901 -1.28735995 + endloop + endfacet + + facet normal -0.977861404 0.203926086 -0.0469170213 + outer loop + vertex -0.72458303 2.26954007 0.761135995 + vertex -0.440544993 3.16025901 -1.28735995 + vertex -0.710753024 2.34805298 0.814131975 + endloop + endfacet + + facet normal -0.996882498 -0.0666720271 -0.0421910174 + outer loop + vertex -0.436367005 3.14639306 -1.36416399 + vertex -0.455123991 3.54269695 -1.54722202 + vertex -0.440544993 3.16025901 -1.28735995 + endloop + endfacet + + facet normal -0.998558164 -0.00160700036 0.0536570102 + outer loop + vertex -0.455123991 3.54269695 -1.54722202 + vertex -0.45371899 3.54508805 -1.52100492 + vertex -0.440544993 3.16025901 -1.28735995 + endloop + endfacet + + facet normal 0.272473931 0.292851925 0.916512847 + outer loop + vertex -0.253796995 0.214561 -0.0458349995 + vertex -0.376740992 0.170128003 0.00491400016 + vertex -0.376302004 0.324781001 -0.0446330011 + endloop + endfacet + + facet normal -0.247445986 -0.867469966 0.431585968 + outer loop + vertex 0.933724999 3.97960711 -1.55623698 + vertex 0.911266029 3.98531508 -1.55764103 + vertex 0.926603973 3.99251413 -1.53437805 + endloop + endfacet + + facet normal -0.777862728 0.563657761 -0.277883887 + outer loop + vertex -0.253796995 0.214561 -0.0458349995 + vertex 0.103583999 0.929287016 0.403519988 + vertex 0.0455540009 0.876025975 0.457924992 + endloop + endfacet + + facet normal -0.477919996 0.658785999 -0.581027985 + outer loop + vertex -0.253796995 0.214561 -0.0458349995 + vertex 0.0455540009 0.876025975 0.457924992 + vertex -0.376740992 0.170128003 0.00491400016 + endloop + endfacet + + facet normal -0.779423177 0.557750106 -0.285332054 + outer loop + vertex 0.103583999 0.929287016 0.403519988 + vertex 0.204065993 1.50625706 1.256863 + vertex 0.0455540009 0.876025975 0.457924992 + endloop + endfacet + + facet normal -0.765340209 0.570419192 -0.298121095 + outer loop + vertex 0.204065993 1.50625706 1.256863 + vertex 0.174039006 1.49804199 1.31822896 + vertex 0.0455540009 0.876025975 0.457924992 + endloop + endfacet + + facet normal -0.725149751 0.339532942 -0.599061906 + outer loop + vertex 0.204065993 1.50625706 1.256863 + vertex 0.282788992 1.63802505 1.23625302 + vertex 0.252124012 1.695822 1.30613101 + endloop + endfacet + + facet normal -0.867439389 0.319119155 -0.381722182 + outer loop + vertex 0.204065993 1.50625706 1.256863 + vertex 0.252124012 1.695822 1.30613101 + vertex 0.174039006 1.49804199 1.31822896 + endloop + endfacet + + facet normal -0.88659811 0.0803210139 -0.45551309 + outer loop + vertex 0.282788992 1.63802505 1.23625302 + vertex 0.676886976 2.68409705 0.653648019 + vertex 0.252124012 1.695822 1.30613101 + endloop + endfacet + + facet normal -0.822990119 -0.0210630018 -0.56766504 + outer loop + vertex 0.676886976 2.68409705 0.653648019 + vertex 0.655480981 2.77318811 0.681376994 + vertex 0.252124012 1.695822 1.30613101 + endloop + endfacet + + facet normal -0.987821043 -0.06729801 -0.140287012 + outer loop + vertex 0.676886976 2.68409705 0.653648019 + vertex 0.870212972 3.71105099 -1.20028496 + vertex 0.858199 3.75496197 -1.13675594 + endloop + endfacet + + facet normal -0.965101719 -0.169848949 -0.199323922 + outer loop + vertex 0.676886976 2.68409705 0.653648019 + vertex 0.858199 3.75496197 -1.13675594 + vertex 0.655480981 2.77318811 0.681376994 + endloop + endfacet + + facet normal -0.983463764 -0.00726499874 -0.180958956 + outer loop + vertex 0.870212972 3.71105099 -1.20028496 + vertex 0.933724999 3.97960711 -1.55623698 + vertex 0.858199 3.75496197 -1.13675594 + endloop + endfacet + + facet normal -0.962732613 -0.124580957 -0.240052924 + outer loop + vertex 0.933724999 3.97960711 -1.55623698 + vertex 0.926603973 3.99251413 -1.53437805 + vertex 0.858199 3.75496197 -1.13675594 + endloop + endfacet + + facet normal 0.272466928 0.292851955 0.916514874 + outer loop + vertex -0.376740992 0.170128003 0.00491400016 + vertex -0.499356002 0.242149994 0.018352 + vertex -0.376302004 0.324781001 -0.0446330011 + endloop + endfacet + + facet normal -0.247477978 -0.86745286 0.431601971 + outer loop + vertex 0.926603973 3.99251413 -1.53437805 + vertex 0.911266029 3.98531508 -1.55764103 + vertex 0.907932997 4 -1.530038 + endloop + endfacet + + facet normal 0.172648042 0.456757128 -0.872677267 + outer loop + vertex -0.376740992 0.170128003 0.00491400016 + vertex 0.0455540009 0.876025975 0.457924992 + vertex -0.499356002 0.242149994 0.018352 + endloop + endfacet + + facet normal -0.0465159826 0.595931768 -0.801686645 + outer loop + vertex 0.0455540009 0.876025975 0.457924992 + vertex -0.0493079983 0.883087993 0.468677998 + vertex -0.499356002 0.242149994 0.018352 + endloop + endfacet + + facet normal 0.00578699959 0.809948981 -0.586471915 + outer loop + vertex 0.0455540009 0.876025975 0.457924992 + vertex 0.174039006 1.49804199 1.31822896 + vertex 0.117908001 1.50824094 1.331761 + endloop + endfacet + + facet normal -0.00608000066 0.810414016 -0.585826039 + outer loop + vertex 0.0455540009 0.876025975 0.457924992 + vertex 0.117908001 1.50824094 1.331761 + vertex -0.0493079983 0.883087993 0.468677998 + endloop + endfacet + + facet normal -0.228951991 0.0308769979 -0.972947955 + outer loop + vertex 0.174039006 1.49804199 1.31822896 + vertex 0.252124012 1.695822 1.30613101 + vertex 0.117908001 1.50824094 1.331761 + endloop + endfacet + + facet normal -0.16855301 -0.0140670007 -0.985592186 + outer loop + vertex 0.252124012 1.695822 1.30613101 + vertex 0.164021999 1.73031497 1.32070494 + vertex 0.117908001 1.50824094 1.331761 + endloop + endfacet + + facet normal -0.246820986 -0.415316969 -0.875551939 + outer loop + vertex 0.252124012 1.695822 1.30613101 + vertex 0.655480981 2.77318811 0.681376994 + vertex 0.569666028 2.81540704 0.685541987 + endloop + endfacet + + facet normal -0.298811018 -0.396427006 -0.86807698 + outer loop + vertex 0.252124012 1.695822 1.30613101 + vertex 0.569666028 2.81540704 0.685541987 + vertex 0.164021999 1.73031497 1.32070494 + endloop + endfacet + + facet normal -0.408190787 -0.78346467 -0.468575805 + outer loop + vertex 0.655480981 2.77318811 0.681376994 + vertex 0.858199 3.75496197 -1.13675594 + vertex 0.569666028 2.81540704 0.685541987 + endloop + endfacet + + facet normal -0.526139736 -0.719045639 -0.454037786 + outer loop + vertex 0.858199 3.75496197 -1.13675594 + vertex 0.819289029 3.77494597 -1.12331498 + vertex 0.569666028 2.81540704 0.685541987 + endloop + endfacet + + facet normal -0.419349998 -0.74575603 -0.517681003 + outer loop + vertex 0.858199 3.75496197 -1.13675594 + vertex 0.926603973 3.99251413 -1.53437805 + vertex 0.907932997 4 -1.530038 + endloop + endfacet + + facet normal -0.526108801 -0.690327764 -0.496645838 + outer loop + vertex 0.858199 3.75496197 -1.13675594 + vertex 0.907932997 4 -1.530038 + vertex 0.819289029 3.77494597 -1.12331498 + endloop + endfacet + + facet normal 0.272470057 0.292848051 0.916515231 + outer loop + vertex -0.499356002 0.242149994 0.018352 + vertex -0.529305995 0.376396 -0.0156389996 + vertex -0.376302004 0.324781001 -0.0446330011 + endloop + endfacet + + facet normal -0.24746196 -0.8674559 0.431604952 + outer loop + vertex 0.907932997 4 -1.530038 + vertex 0.911266029 3.98531508 -1.55764103 + vertex 0.891771019 3.99642801 -1.54648209 + endloop + endfacet + + facet normal 0.628429115 0.100671016 -0.771325052 + outer loop + vertex -0.499356002 0.242149994 0.018352 + vertex -0.0493079983 0.883087993 0.468677998 + vertex -0.109567001 0.945155025 0.427682996 + endloop + endfacet + + facet normal 0.73198396 -0.0091859987 -0.68125993 + outer loop + vertex -0.499356002 0.242149994 0.018352 + vertex -0.109567001 0.945155025 0.427682996 + vertex -0.529305995 0.376396 -0.0156389996 + endloop + endfacet + + facet normal 0.76850915 0.438165128 -0.466267079 + outer loop + vertex -0.0493079983 0.883087993 0.468677998 + vertex 0.117908001 1.50824094 1.331761 + vertex -0.109567001 0.945155025 0.427682996 + endloop + endfacet + + facet normal 0.759350061 0.449147046 -0.470802039 + outer loop + vertex 0.117908001 1.50824094 1.331761 + vertex 0.0779410005 1.52917695 1.28727102 + vertex -0.109567001 0.945155025 0.427682996 + endloop + endfacet + + facet normal 0.560264945 -0.156830981 -0.813330948 + outer loop + vertex 0.117908001 1.50824094 1.331761 + vertex 0.164021999 1.73031497 1.32070494 + vertex 0.0848250017 1.71552598 1.26900196 + endloop + endfacet + + facet normal 0.71818012 -0.0941240117 -0.689462125 + outer loop + vertex 0.117908001 1.50824094 1.331761 + vertex 0.0848250017 1.71552598 1.26900196 + vertex 0.0779410005 1.52917695 1.28727102 + endloop + endfacet + + facet normal 0.5217219 -0.567762852 -0.636750877 + outer loop + vertex 0.164021999 1.73031497 1.32070494 + vertex 0.569666028 2.81540704 0.685541987 + vertex 0.0848250017 1.71552598 1.26900196 + endloop + endfacet + + facet normal 0.426157176 -0.563345253 -0.70783627 + outer loop + vertex 0.569666028 2.81540704 0.685541987 + vertex 0.484059989 2.77896309 0.663007021 + vertex 0.0848250017 1.71552598 1.26900196 + endloop + endfacet + + facet normal 0.688727021 -0.67552197 -0.263296992 + outer loop + vertex 0.569666028 2.81540704 0.685541987 + vertex 0.819289029 3.77494597 -1.12331498 + vertex 0.782781005 3.75595093 -1.17007899 + endloop + endfacet + + facet normal 0.444303989 -0.81863606 -0.363907993 + outer loop + vertex 0.569666028 2.81540704 0.685541987 + vertex 0.782781005 3.75595093 -1.17007899 + vertex 0.484059989 2.77896309 0.663007021 + endloop + endfacet + + facet normal 0.672470868 -0.699886858 -0.24070996 + outer loop + vertex 0.819289029 3.77494597 -1.12331498 + vertex 0.907932997 4 -1.530038 + vertex 0.782781005 3.75595093 -1.17007899 + endloop + endfacet + + facet normal 0.523807168 -0.778528273 -0.345716119 + outer loop + vertex 0.907932997 4 -1.530038 + vertex 0.891771019 3.99642801 -1.54648209 + vertex 0.782781005 3.75595093 -1.17007899 + endloop + endfacet + + facet normal 0.272472084 0.292858124 0.916511357 + outer loop + vertex -0.529305995 0.376396 -0.0156389996 + vertex -0.44404301 0.471772999 -0.0714629963 + vertex -0.376302004 0.324781001 -0.0446330011 + endloop + endfacet + + facet normal -0.247544035 -0.867487133 0.43149507 + outer loop + vertex 0.891771019 3.99642801 -1.54648209 + vertex 0.911266029 3.98531508 -1.55764103 + vertex 0.890290976 3.98449087 -1.57133007 + endloop + endfacet + + facet normal 0.767617583 -0.634875655 0.087727949 + outer loop + vertex -0.529305995 0.376396 -0.0156389996 + vertex -0.109567001 0.945155025 0.427682996 + vertex -0.44404301 0.471772999 -0.0714629963 + endloop + endfacet + + facet normal 0.886159658 -0.420221835 -0.19528091 + outer loop + vertex -0.109567001 0.945155025 0.427682996 + vertex -0.0898490027 1.01548898 0.365808994 + vertex -0.44404301 0.471772999 -0.0714629963 + endloop + endfacet + + facet normal 0.946716785 -0.321837902 0.0121489968 + outer loop + vertex -0.109567001 0.945155025 0.427682996 + vertex 0.0779410005 1.52917695 1.28727102 + vertex 0.0842330009 1.54508102 1.21826005 + endloop + endfacet + + facet normal 0.958333194 -0.285045058 -0.0186190046 + outer loop + vertex -0.109567001 0.945155025 0.427682996 + vertex 0.0842330009 1.54508102 1.21826005 + vertex -0.0898490027 1.01548898 0.365808994 + endloop + endfacet + + facet normal 0.996036589 -0.028538987 0.0842419714 + outer loop + vertex 0.0779410005 1.52917695 1.28727102 + vertex 0.0848250017 1.71552598 1.26900196 + vertex 0.0842330009 1.54508102 1.21826005 + endloop + endfacet + + facet normal 0.985584736 0.0451189876 -0.163054958 + outer loop + vertex 0.0848250017 1.71552598 1.26900196 + vertex 0.0741709992 1.66259193 1.18995297 + vertex 0.0842330009 1.54508102 1.21826005 + endloop + endfacet + + facet normal 0.950562179 -0.277941048 0.138492033 + outer loop + vertex 0.0848250017 1.71552598 1.26900196 + vertex 0.484059989 2.77896309 0.663007021 + vertex 0.463129014 2.69129992 0.630741 + endloop + endfacet + + facet normal 0.946162283 -0.313099086 0.0821340233 + outer loop + vertex 0.0848250017 1.71552598 1.26900196 + vertex 0.463129014 2.69129992 0.630741 + vertex 0.0741709992 1.66259193 1.18995297 + endloop + endfacet + + facet normal 0.96979171 -0.242210925 0.0289449915 + outer loop + vertex 0.484059989 2.77896309 0.663007021 + vertex 0.782781005 3.75595093 -1.17007899 + vertex 0.463129014 2.69129992 0.630741 + endloop + endfacet + + facet normal 0.974733412 -0.219097078 0.0434880182 + outer loop + vertex 0.782781005 3.75595093 -1.17007899 + vertex 0.776166975 3.71228409 -1.24183702 + vertex 0.463129014 2.69129992 0.630741 + endloop + endfacet + + facet normal 0.951111138 -0.296674043 0.0858610049 + outer loop + vertex 0.782781005 3.75595093 -1.17007899 + vertex 0.891771019 3.99642801 -1.54648209 + vertex 0.890290976 3.98449087 -1.57133007 + endloop + endfacet + + facet normal 0.95249474 -0.291143954 0.0893819779 + outer loop + vertex 0.782781005 3.75595093 -1.17007899 + vertex 0.890290976 3.98449087 -1.57133007 + vertex 0.776166975 3.71228409 -1.24183702 + endloop + endfacet + + facet normal 0.272473961 0.292857975 0.91651094 + outer loop + vertex -0.44404301 0.471772999 -0.0714629963 + vertex -0.307767004 0.456461996 -0.107084997 + vertex -0.376302004 0.324781001 -0.0446330011 + endloop + endfacet + + facet normal -0.247539937 -0.867491841 0.431487888 + outer loop + vertex 0.890290976 3.98449087 -1.57133007 + vertex 0.911266029 3.98531508 -1.55764103 + vertex 0.904604971 3.97317505 -1.58586907 + endloop + endfacet + + facet normal 0.444817066 -0.719116151 0.533863127 + outer loop + vertex -0.44404301 0.471772999 -0.0714629963 + vertex -0.0898490027 1.01548898 0.365808994 + vertex -0.00500100013 1.04112697 0.329647988 + endloop + endfacet + + facet normal 0.127748966 -0.63515991 0.76174283 + outer loop + vertex -0.44404301 0.471772999 -0.0714629963 + vertex -0.00500100013 1.04112697 0.329647988 + vertex -0.307767004 0.456461996 -0.107084997 + endloop + endfacet + + facet normal 0.420685858 -0.806614757 0.415205866 + outer loop + vertex -0.0898490027 1.01548898 0.365808994 + vertex 0.0842330009 1.54508102 1.21826005 + vertex -0.00500100013 1.04112697 0.329647988 + endloop + endfacet + + facet normal 0.35758391 -0.827291846 0.433268875 + outer loop + vertex 0.0842330009 1.54508102 1.21826005 + vertex 0.132046998 1.54398 1.17669499 + vertex -0.00500100013 1.04112697 0.329647988 + endloop + endfacet + + facet normal 0.675312042 0.226875037 0.701770067 + outer loop + vertex 0.0842330009 1.54508102 1.21826005 + vertex 0.0741709992 1.66259193 1.18995297 + vertex 0.140081003 1.61137402 1.14308596 + endloop + endfacet + + facet normal 0.632811844 0.283820927 0.72041285 + outer loop + vertex 0.0842330009 1.54508102 1.21826005 + vertex 0.140081003 1.61137402 1.14308596 + vertex 0.132046998 1.54398 1.17669499 + endloop + endfacet + + facet normal 0.650720775 0.157726958 0.742754757 + outer loop + vertex 0.0741709992 1.66259193 1.18995297 + vertex 0.463129014 2.69129992 0.630741 + vertex 0.140081003 1.61137402 1.14308596 + endloop + endfacet + + facet normal 0.52620393 0.230870992 0.818417966 + outer loop + vertex 0.463129014 2.69129992 0.630741 + vertex 0.522632003 2.61842799 0.61303997 + vertex 0.140081003 1.61137402 1.14308596 + endloop + endfacet + + facet normal 0.890468955 0.320121974 0.323398978 + outer loop + vertex 0.463129014 2.69129992 0.630741 + vertex 0.776166975 3.71228409 -1.24183702 + vertex 0.804427981 3.67682505 -1.28455305 + endloop + endfacet + + facet normal 0.754780054 0.51879406 0.401447028 + outer loop + vertex 0.463129014 2.69129992 0.630741 + vertex 0.804427981 3.67682505 -1.28455305 + vertex 0.522632003 2.61842799 0.61303997 + endloop + endfacet + + facet normal 0.880504668 0.167588919 0.443424821 + outer loop + vertex 0.776166975 3.71228409 -1.24183702 + vertex 0.890290976 3.98449087 -1.57133007 + vertex 0.804427981 3.67682505 -1.28455305 + endloop + endfacet + + facet normal 0.785179079 0.290597022 0.546852052 + outer loop + vertex 0.890290976 3.98449087 -1.57133007 + vertex 0.904604971 3.97317505 -1.58586907 + vertex 0.804427981 3.67682505 -1.28455305 + endloop + endfacet + + facet normal 0.272477061 0.292857081 0.916510224 + outer loop + vertex -0.307767004 0.456461996 -0.107084997 + vertex -0.223100007 0.341991007 -0.095679 + vertex -0.376302004 0.324781001 -0.0446330011 + endloop + endfacet + + facet normal -0.247493982 -0.867506921 0.431483984 + outer loop + vertex 0.904604971 3.97317505 -1.58586907 + vertex 0.911266029 3.98531508 -1.55764103 + vertex 0.923933983 3.97100115 -1.57915306 + endloop + endfacet + + facet normal -0.530284762 -0.313739836 0.787632704 + outer loop + vertex -0.307767004 0.456461996 -0.107084997 + vertex -0.00500100013 1.04112697 0.329647988 + vertex -0.223100007 0.341991007 -0.095679 + endloop + endfacet + + facet normal -0.346002042 -0.406495035 0.845602989 + outer loop + vertex -0.00500100013 1.04112697 0.329647988 + vertex 0.0810849965 1.00276399 0.346430987 + vertex -0.223100007 0.341991007 -0.095679 + endloop + endfacet + + facet normal -0.409732103 -0.753765106 0.513768137 + outer loop + vertex -0.00500100013 1.04112697 0.329647988 + vertex 0.132046998 1.54398 1.17669499 + vertex 0.185377002 1.52670002 1.19387603 + endloop + endfacet + + facet normal -0.430921048 -0.742863119 0.512310088 + outer loop + vertex -0.00500100013 1.04112697 0.329647988 + vertex 0.185377002 1.52670002 1.19387603 + vertex 0.0810849965 1.00276399 0.346430987 + endloop + endfacet + + facet normal -0.136054054 0.455070168 0.880000293 + outer loop + vertex 0.132046998 1.54398 1.17669499 + vertex 0.140081003 1.61137402 1.14308596 + vertex 0.185377002 1.52670002 1.19387603 + endloop + endfacet + + facet normal -0.142184019 0.452121019 0.8805511 + outer loop + vertex 0.140081003 1.61137402 1.14308596 + vertex 0.232925996 1.60044098 1.16369104 + vertex 0.185377002 1.52670002 1.19387603 + endloop + endfacet + + facet normal -0.0768329725 0.487080812 0.869970679 + outer loop + vertex 0.140081003 1.61137402 1.14308596 + vertex 0.522632003 2.61842799 0.61303997 + vertex 0.617762983 2.61522293 0.623236001 + endloop + endfacet + + facet normal -0.130130991 0.504069924 0.853802919 + outer loop + vertex 0.140081003 1.61137402 1.14308596 + vertex 0.617762983 2.61522293 0.623236001 + vertex 0.232925996 1.60044098 1.16369104 + endloop + endfacet + + facet normal -0.0224629845 0.874534488 0.484442741 + outer loop + vertex 0.522632003 2.61842799 0.61303997 + vertex 0.804427981 3.67682505 -1.28455305 + vertex 0.617762983 2.61522293 0.623236001 + endloop + endfacet + + facet normal -0.19305791 0.865288615 0.462605834 + outer loop + vertex 0.804427981 3.67682505 -1.28455305 + vertex 0.846280992 3.67627597 -1.26605999 + vertex 0.617762983 2.61522293 0.623236001 + endloop + endfacet + + facet normal -0.149908945 0.729361773 0.667501807 + outer loop + vertex 0.804427981 3.67682505 -1.28455305 + vertex 0.904604971 3.97317505 -1.58586907 + vertex 0.923933983 3.97100115 -1.57915306 + endloop + endfacet + + facet normal -0.266439945 0.733926833 0.62478888 + outer loop + vertex 0.804427981 3.67682505 -1.28455305 + vertex 0.923933983 3.97100115 -1.57915306 + vertex 0.846280992 3.67627597 -1.26605999 + endloop + endfacet + + facet normal 0.272476882 0.292855889 0.916510582 + outer loop + vertex -0.223100007 0.341991007 -0.095679 + vertex -0.253796995 0.214561 -0.0458349995 + vertex -0.376302004 0.324781001 -0.0446330011 + endloop + endfacet + + facet normal -0.247450024 -0.8675071 0.431509018 + outer loop + vertex 0.923933983 3.97100115 -1.57915306 + vertex 0.911266029 3.98531508 -1.55764103 + vertex 0.933724999 3.97960711 -1.55623698 + endloop + endfacet + + facet normal -0.879711032 0.0929580033 0.466333985 + outer loop + vertex -0.223100007 0.341991007 -0.095679 + vertex 0.0810849965 1.00276399 0.346430987 + vertex 0.103583999 0.929287016 0.403519988 + endloop + endfacet + + facet normal -0.920438349 0.313202113 0.233875081 + outer loop + vertex -0.223100007 0.341991007 -0.095679 + vertex 0.103583999 0.929287016 0.403519988 + vertex -0.253796995 0.214561 -0.0458349995 + endloop + endfacet + + facet normal -0.969112396 -0.137853056 0.204494074 + outer loop + vertex 0.0810849965 1.00276399 0.346430987 + vertex 0.185377002 1.52670002 1.19387603 + vertex 0.103583999 0.929287016 0.403519988 + endloop + endfacet + + facet normal -0.958291411 -0.17137289 0.22871086 + outer loop + vertex 0.185377002 1.52670002 1.19387603 + vertex 0.204065993 1.50625706 1.256863 + vertex 0.103583999 0.929287016 0.403519988 + endloop + endfacet + + facet normal -0.774107695 0.591492772 0.225595891 + outer loop + vertex 0.185377002 1.52670002 1.19387603 + vertex 0.232925996 1.60044098 1.16369104 + vertex 0.282788992 1.63802505 1.23625302 + endloop + endfacet + + facet normal -0.76090014 0.516107082 0.393274039 + outer loop + vertex 0.185377002 1.52670002 1.19387603 + vertex 0.282788992 1.63802505 1.23625302 + vertex 0.204065993 1.50625706 1.256863 + endloop + endfacet + + facet normal -0.8194713 0.47864911 0.315217078 + outer loop + vertex 0.232925996 1.60044098 1.16369104 + vertex 0.617762983 2.61522293 0.623236001 + vertex 0.282788992 1.63802505 1.23625302 + endloop + endfacet + + facet normal -0.778669 0.502564967 0.375636965 + outer loop + vertex 0.617762983 2.61522293 0.623236001 + vertex 0.676886976 2.68409705 0.653648019 + vertex 0.282788992 1.63802505 1.23625302 + endloop + endfacet + + facet normal -0.907485962 0.403503984 0.11684899 + outer loop + vertex 0.617762983 2.61522293 0.623236001 + vertex 0.846280992 3.67627597 -1.26605999 + vertex 0.870212972 3.71105099 -1.20028496 + endloop + endfacet + + facet normal -0.786379635 0.571479738 0.234558895 + outer loop + vertex 0.617762983 2.61522293 0.623236001 + vertex 0.870212972 3.71105099 -1.20028496 + vertex 0.676886976 2.68409705 0.653648019 + endloop + endfacet + + facet normal -0.915194154 0.380924076 0.131592035 + outer loop + vertex 0.846280992 3.67627597 -1.26605999 + vertex 0.923933983 3.97100115 -1.57915306 + vertex 0.870212972 3.71105099 -1.20028496 + endloop + endfacet + + facet normal -0.864411891 0.463298917 0.195310965 + outer loop + vertex 0.923933983 3.97100115 -1.57915306 + vertex 0.933724999 3.97960711 -1.55623698 + vertex 0.870212972 3.71105099 -1.20028496 + endloop + endfacet + + facet normal 0.272473931 -0.292851925 0.916512847 + outer loop + vertex -0.376302004 -0.324781001 -0.0446330011 + vertex -0.376740992 -0.170128003 0.00491400016 + vertex -0.253796995 -0.214561 -0.0458349995 + endloop + endfacet + + facet normal -0.247445986 0.867469966 0.431585968 + outer loop + vertex 0.926603973 -3.99251413 -1.53437805 + vertex 0.911266029 -3.98531508 -1.55764103 + vertex 0.933724999 -3.97960711 -1.55623698 + endloop + endfacet + + facet normal -0.777862728 -0.563657761 -0.277883887 + outer loop + vertex 0.0455540009 -0.876025975 0.457924992 + vertex 0.103583999 -0.929287016 0.403519988 + vertex -0.253796995 -0.214561 -0.0458349995 + endloop + endfacet + + facet normal -0.477919996 -0.658785999 -0.581027985 + outer loop + vertex -0.376740992 -0.170128003 0.00491400016 + vertex 0.0455540009 -0.876025975 0.457924992 + vertex -0.253796995 -0.214561 -0.0458349995 + endloop + endfacet + + facet normal -0.779423177 -0.557750106 -0.285332054 + outer loop + vertex 0.0455540009 -0.876025975 0.457924992 + vertex 0.204065993 -1.50625706 1.256863 + vertex 0.103583999 -0.929287016 0.403519988 + endloop + endfacet + + facet normal -0.76534009 -0.570420086 -0.298120022 + outer loop + vertex 0.0455540009 -0.876025975 0.457924992 + vertex 0.174039006 -1.49804103 1.31822896 + vertex 0.204065993 -1.50625706 1.256863 + endloop + endfacet + + facet normal -0.725148976 -0.339533001 -0.599062979 + outer loop + vertex 0.252124012 -1.695822 1.30613101 + vertex 0.282788992 -1.63802397 1.23625302 + vertex 0.204065993 -1.50625706 1.256863 + endloop + endfacet + + facet normal -0.867439389 -0.319119155 -0.381722182 + outer loop + vertex 0.174039006 -1.49804103 1.31822896 + vertex 0.252124012 -1.695822 1.30613101 + vertex 0.204065993 -1.50625706 1.256863 + endloop + endfacet + + facet normal -0.88659811 -0.0803210139 -0.45551309 + outer loop + vertex 0.252124012 -1.695822 1.30613101 + vertex 0.676886976 -2.68409705 0.653648019 + vertex 0.282788992 -1.63802397 1.23625302 + endloop + endfacet + + facet normal -0.822990119 0.0210630018 -0.56766504 + outer loop + vertex 0.252124012 -1.695822 1.30613101 + vertex 0.655480981 -2.77318811 0.681376994 + vertex 0.676886976 -2.68409705 0.653648019 + endloop + endfacet + + facet normal -0.987821043 0.06729801 -0.140287012 + outer loop + vertex 0.858199 -3.75496197 -1.13675594 + vertex 0.870212972 -3.71105099 -1.20028496 + vertex 0.676886976 -2.68409705 0.653648019 + endloop + endfacet + + facet normal -0.965101719 0.169848949 -0.199323922 + outer loop + vertex 0.655480981 -2.77318811 0.681376994 + vertex 0.858199 -3.75496197 -1.13675594 + vertex 0.676886976 -2.68409705 0.653648019 + endloop + endfacet + + facet normal -0.983463764 0.00726499874 -0.180958956 + outer loop + vertex 0.858199 -3.75496197 -1.13675594 + vertex 0.933724999 -3.97960711 -1.55623698 + vertex 0.870212972 -3.71105099 -1.20028496 + endloop + endfacet + + facet normal -0.962732613 0.124580957 -0.240052924 + outer loop + vertex 0.858199 -3.75496197 -1.13675594 + vertex 0.926603973 -3.99251413 -1.53437805 + vertex 0.933724999 -3.97960711 -1.55623698 + endloop + endfacet + + facet normal 0.272466928 -0.292851955 0.916514874 + outer loop + vertex -0.376302004 -0.324781001 -0.0446330011 + vertex -0.499356002 -0.242149994 0.018352 + vertex -0.376740992 -0.170128003 0.00491400016 + endloop + endfacet + + facet normal -0.247477025 0.867453039 0.431602061 + outer loop + vertex 0.907932997 -4 -1.530038 + vertex 0.911266029 -3.98531508 -1.55764103 + vertex 0.926603973 -3.99251413 -1.53437805 + endloop + endfacet + + facet normal 0.172648042 -0.456757128 -0.872677267 + outer loop + vertex -0.499356002 -0.242149994 0.018352 + vertex 0.0455540009 -0.876025975 0.457924992 + vertex -0.376740992 -0.170128003 0.00491400016 + endloop + endfacet + + facet normal -0.0465160124 -0.595931113 -0.801687181 + outer loop + vertex -0.499356002 -0.242149994 0.018352 + vertex -0.0493079983 -0.883087993 0.468677998 + vertex 0.0455540009 -0.876025975 0.457924992 + endloop + endfacet + + facet normal 0.00578899914 -0.809948862 -0.586471856 + outer loop + vertex 0.117908001 -1.50824094 1.331761 + vertex 0.174039006 -1.49804103 1.31822896 + vertex 0.0455540009 -0.876025975 0.457924992 + endloop + endfacet + + facet normal -0.00607900089 -0.810414076 -0.585826099 + outer loop + vertex -0.0493079983 -0.883087993 0.468677998 + vertex 0.117908001 -1.50824094 1.331761 + vertex 0.0455540009 -0.876025975 0.457924992 + endloop + endfacet + + facet normal -0.228951037 -0.0308770034 -0.972948134 + outer loop + vertex 0.117908001 -1.50824094 1.331761 + vertex 0.252124012 -1.695822 1.30613101 + vertex 0.174039006 -1.49804103 1.31822896 + endloop + endfacet + + facet normal -0.16855301 0.0140670007 -0.985592186 + outer loop + vertex 0.117908001 -1.50824094 1.331761 + vertex 0.164021999 -1.73031497 1.32070494 + vertex 0.252124012 -1.695822 1.30613101 + endloop + endfacet + + facet normal -0.246820047 0.415317029 -0.875552118 + outer loop + vertex 0.569666028 -2.81540704 0.685541987 + vertex 0.655480981 -2.77318811 0.681376994 + vertex 0.252124012 -1.695822 1.30613101 + endloop + endfacet + + facet normal -0.298811018 0.396427006 -0.86807698 + outer loop + vertex 0.164021999 -1.73031497 1.32070494 + vertex 0.569666028 -2.81540704 0.685541987 + vertex 0.252124012 -1.695822 1.30613101 + endloop + endfacet + + facet normal -0.40818882 0.783465683 -0.468575835 + outer loop + vertex 0.569666028 -2.81540704 0.685541987 + vertex 0.858199 -3.75496197 -1.13675594 + vertex 0.655480981 -2.77318811 0.681376994 + endloop + endfacet + + facet normal -0.526139736 0.719045639 -0.454037786 + outer loop + vertex 0.569666028 -2.81540704 0.685541987 + vertex 0.819289029 -3.77494597 -1.12331498 + vertex 0.858199 -3.75496197 -1.13675594 + endloop + endfacet + + facet normal -0.419348896 0.745756686 -0.517680824 + outer loop + vertex 0.907932997 -4 -1.530038 + vertex 0.926603973 -3.99251413 -1.53437805 + vertex 0.858199 -3.75496197 -1.13675594 + endloop + endfacet + + facet normal -0.526108801 0.690327764 -0.496645838 + outer loop + vertex 0.819289029 -3.77494597 -1.12331498 + vertex 0.907932997 -4 -1.530038 + vertex 0.858199 -3.75496197 -1.13675594 + endloop + endfacet + + facet normal 0.272470057 -0.292848051 0.916515231 + outer loop + vertex -0.376302004 -0.324781001 -0.0446330011 + vertex -0.529305995 -0.376397014 -0.0156389996 + vertex -0.499356002 -0.242149994 0.018352 + endloop + endfacet + + facet normal -0.247462898 0.867455661 0.431604832 + outer loop + vertex 0.891771019 -3.99642801 -1.54648209 + vertex 0.911266029 -3.98531508 -1.55764103 + vertex 0.907932997 -4 -1.530038 + endloop + endfacet + + facet normal 0.628429115 -0.100671016 -0.771325052 + outer loop + vertex -0.109567001 -0.945155025 0.427682996 + vertex -0.0493079983 -0.883087993 0.468677998 + vertex -0.499356002 -0.242149994 0.018352 + endloop + endfacet + + facet normal 0.73198396 0.0091859987 -0.68125993 + outer loop + vertex -0.529305995 -0.376397014 -0.0156389996 + vertex -0.109567001 -0.945155025 0.427682996 + vertex -0.499356002 -0.242149994 0.018352 + endloop + endfacet + + facet normal 0.768508792 -0.438165873 -0.46626687 + outer loop + vertex -0.109567001 -0.945155025 0.427682996 + vertex 0.117908001 -1.50824094 1.331761 + vertex -0.0493079983 -0.883087993 0.468677998 + endloop + endfacet + + facet normal 0.759350061 -0.449147046 -0.470802039 + outer loop + vertex -0.109567001 -0.945155025 0.427682996 + vertex 0.0779410005 -1.52917695 1.28727102 + vertex 0.117908001 -1.50824094 1.331761 + endloop + endfacet + + facet normal 0.560264945 0.156830981 -0.813330948 + outer loop + vertex 0.0848250017 -1.71552598 1.26900196 + vertex 0.164021999 -1.73031497 1.32070494 + vertex 0.117908001 -1.50824094 1.331761 + endloop + endfacet + + facet normal 0.71818012 0.0941240117 -0.689462125 + outer loop + vertex 0.0779410005 -1.52917695 1.28727102 + vertex 0.0848250017 -1.71552598 1.26900196 + vertex 0.117908001 -1.50824094 1.331761 + endloop + endfacet + + facet normal 0.521720767 0.567762733 -0.636751771 + outer loop + vertex 0.0848250017 -1.71552598 1.26900196 + vertex 0.569666028 -2.81540704 0.685541987 + vertex 0.164021999 -1.73031497 1.32070494 + endloop + endfacet + + facet normal 0.426156938 0.563345969 -0.707835913 + outer loop + vertex 0.0848250017 -1.71552598 1.26900196 + vertex 0.484059989 -2.77896309 0.663007021 + vertex 0.569666028 -2.81540704 0.685541987 + endloop + endfacet + + facet normal 0.688727021 0.67552197 -0.263296992 + outer loop + vertex 0.782781005 -3.75595093 -1.17007899 + vertex 0.819289029 -3.77494597 -1.12331498 + vertex 0.569666028 -2.81540704 0.685541987 + endloop + endfacet + + facet normal 0.444303989 0.81863606 -0.363907993 + outer loop + vertex 0.484059989 -2.77896309 0.663007021 + vertex 0.782781005 -3.75595093 -1.17007899 + vertex 0.569666028 -2.81540704 0.685541987 + endloop + endfacet + + facet normal 0.672470868 0.699886858 -0.24070996 + outer loop + vertex 0.782781005 -3.75595093 -1.17007899 + vertex 0.907932997 -4 -1.530038 + vertex 0.819289029 -3.77494597 -1.12331498 + endloop + endfacet + + facet normal 0.523808122 0.778528154 -0.345715046 + outer loop + vertex 0.782781005 -3.75595093 -1.17007899 + vertex 0.891771019 -3.99642801 -1.54648209 + vertex 0.907932997 -4 -1.530038 + endloop + endfacet + + facet normal 0.272471815 -0.292857826 0.916511476 + outer loop + vertex -0.376302004 -0.324781001 -0.0446330011 + vertex -0.44404301 -0.471772999 -0.0714629963 + vertex -0.529305995 -0.376397014 -0.0156389996 + endloop + endfacet + + facet normal -0.247543931 0.867487788 0.431493849 + outer loop + vertex 0.890290976 -3.98449087 -1.57133007 + vertex 0.911266029 -3.98531508 -1.55764103 + vertex 0.891771019 -3.99642801 -1.54648209 + endloop + endfacet + + facet normal 0.76761812 0.634875059 0.0877270103 + outer loop + vertex -0.44404301 -0.471772999 -0.0714629963 + vertex -0.109567001 -0.945155025 0.427682996 + vertex -0.529305995 -0.376397014 -0.0156389996 + endloop + endfacet + + facet normal 0.886159658 0.420221835 -0.19528091 + outer loop + vertex -0.44404301 -0.471772999 -0.0714629963 + vertex -0.0898490027 -1.01548898 0.365808994 + vertex -0.109567001 -0.945155025 0.427682996 + endloop + endfacet + + facet normal 0.946716785 0.321837902 0.0121489968 + outer loop + vertex 0.0842330009 -1.54508102 1.21826005 + vertex 0.0779410005 -1.52917695 1.28727102 + vertex -0.109567001 -0.945155025 0.427682996 + endloop + endfacet + + facet normal 0.958333194 0.285045058 -0.0186190046 + outer loop + vertex -0.0898490027 -1.01548898 0.365808994 + vertex 0.0842330009 -1.54508102 1.21826005 + vertex -0.109567001 -0.945155025 0.427682996 + endloop + endfacet + + facet normal 0.996036589 0.028538987 0.0842419714 + outer loop + vertex 0.0842330009 -1.54508102 1.21826005 + vertex 0.0848250017 -1.71552598 1.26900196 + vertex 0.0779410005 -1.52917695 1.28727102 + endloop + endfacet + + facet normal 0.985584736 -0.0451189876 -0.163054958 + outer loop + vertex 0.0842330009 -1.54508102 1.21826005 + vertex 0.0741709992 -1.66259193 1.18995297 + vertex 0.0848250017 -1.71552598 1.26900196 + endloop + endfacet + + facet normal 0.950562179 0.277941048 0.138492033 + outer loop + vertex 0.463129014 -2.69129992 0.630741 + vertex 0.484059989 -2.77896309 0.663007021 + vertex 0.0848250017 -1.71552598 1.26900196 + endloop + endfacet + + facet normal 0.946162283 0.313099086 0.0821340233 + outer loop + vertex 0.0741709992 -1.66259193 1.18995297 + vertex 0.463129014 -2.69129992 0.630741 + vertex 0.0848250017 -1.71552598 1.26900196 + endloop + endfacet + + facet normal 0.96979171 0.242210925 0.0289449915 + outer loop + vertex 0.463129014 -2.69129992 0.630741 + vertex 0.782781005 -3.75595093 -1.17007899 + vertex 0.484059989 -2.77896309 0.663007021 + endloop + endfacet + + facet normal 0.974733412 0.219097078 0.0434880182 + outer loop + vertex 0.463129014 -2.69129992 0.630741 + vertex 0.776166975 -3.71228409 -1.24183702 + vertex 0.782781005 -3.75595093 -1.17007899 + endloop + endfacet + + facet normal 0.951111257 0.296673089 0.0858620256 + outer loop + vertex 0.890290976 -3.98449087 -1.57133007 + vertex 0.891771019 -3.99642801 -1.54648209 + vertex 0.782781005 -3.75595093 -1.17007899 + endloop + endfacet + + facet normal 0.95249474 0.291143954 0.0893819779 + outer loop + vertex 0.776166975 -3.71228409 -1.24183702 + vertex 0.890290976 -3.98449087 -1.57133007 + vertex 0.782781005 -3.75595093 -1.17007899 + endloop + endfacet + + facet normal 0.272473961 -0.292857975 0.91651094 + outer loop + vertex -0.376302004 -0.324781001 -0.0446330011 + vertex -0.307767004 -0.456461996 -0.107084997 + vertex -0.44404301 -0.471772999 -0.0714629963 + endloop + endfacet + + facet normal -0.247540042 0.867492199 0.431487083 + outer loop + vertex 0.904604971 -3.97317505 -1.58586907 + vertex 0.911266029 -3.98531508 -1.55764103 + vertex 0.890290976 -3.98449087 -1.57133007 + endloop + endfacet + + facet normal 0.444816053 0.719116032 0.533864081 + outer loop + vertex -0.00500100013 -1.04112697 0.329647988 + vertex -0.0898490027 -1.01548898 0.365808994 + vertex -0.44404301 -0.471772999 -0.0714629963 + endloop + endfacet + + facet normal 0.127748966 0.63515991 0.76174283 + outer loop + vertex -0.307767004 -0.456461996 -0.107084997 + vertex -0.00500100013 -1.04112697 0.329647988 + vertex -0.44404301 -0.471772999 -0.0714629963 + endloop + endfacet + + facet normal 0.420685023 0.806615114 0.415206045 + outer loop + vertex -0.00500100013 -1.04112697 0.329647988 + vertex 0.0842330009 -1.54508102 1.21826005 + vertex -0.0898490027 -1.01548898 0.365808994 + endloop + endfacet + + facet normal 0.357582062 0.827292144 0.433270067 + outer loop + vertex -0.00500100013 -1.04112697 0.329647988 + vertex 0.132046998 -1.54397893 1.17669499 + vertex 0.0842330009 -1.54508102 1.21826005 + endloop + endfacet + + facet normal 0.675312042 -0.226875037 0.701770067 + outer loop + vertex 0.140081003 -1.61137402 1.14308596 + vertex 0.0741709992 -1.66259193 1.18995297 + vertex 0.0842330009 -1.54508102 1.21826005 + endloop + endfacet + + facet normal 0.632813096 -0.283820063 0.720412195 + outer loop + vertex 0.132046998 -1.54397893 1.17669499 + vertex 0.140081003 -1.61137402 1.14308596 + vertex 0.0842330009 -1.54508102 1.21826005 + endloop + endfacet + + facet normal 0.650720775 -0.157726958 0.742754757 + outer loop + vertex 0.140081003 -1.61137402 1.14308596 + vertex 0.463129014 -2.69129992 0.630741 + vertex 0.0741709992 -1.66259193 1.18995297 + endloop + endfacet + + facet normal 0.52620393 -0.230870992 0.818417966 + outer loop + vertex 0.140081003 -1.61137402 1.14308596 + vertex 0.522632003 -2.61842704 0.61303997 + vertex 0.463129014 -2.69129992 0.630741 + endloop + endfacet + + facet normal 0.890468955 -0.320121974 0.323398978 + outer loop + vertex 0.804427981 -3.67682505 -1.28455305 + vertex 0.776166975 -3.71228409 -1.24183702 + vertex 0.463129014 -2.69129992 0.630741 + endloop + endfacet + + facet normal 0.754779816 -0.518793881 0.401447922 + outer loop + vertex 0.522632003 -2.61842704 0.61303997 + vertex 0.804427981 -3.67682505 -1.28455305 + vertex 0.463129014 -2.69129992 0.630741 + endloop + endfacet + + facet normal 0.880504668 -0.167588919 0.443424821 + outer loop + vertex 0.804427981 -3.67682505 -1.28455305 + vertex 0.890290976 -3.98449087 -1.57133007 + vertex 0.776166975 -3.71228409 -1.24183702 + endloop + endfacet + + facet normal 0.785177827 -0.290598929 0.546852827 + outer loop + vertex 0.804427981 -3.67682505 -1.28455305 + vertex 0.904604971 -3.97317505 -1.58586907 + vertex 0.890290976 -3.98449087 -1.57133007 + endloop + endfacet + + facet normal 0.272477061 -0.292857081 0.916510224 + outer loop + vertex -0.376302004 -0.324781001 -0.0446330011 + vertex -0.223100007 -0.341991007 -0.095679 + vertex -0.307767004 -0.456461996 -0.107084997 + endloop + endfacet + + facet normal -0.247495025 0.8675071 0.43148303 + outer loop + vertex 0.923933983 -3.97100115 -1.57915306 + vertex 0.911266029 -3.98531508 -1.55764103 + vertex 0.904604971 -3.97317505 -1.58586907 + endloop + endfacet + + facet normal -0.530284762 0.313739836 0.787632704 + outer loop + vertex -0.223100007 -0.341991007 -0.095679 + vertex -0.00500100013 -1.04112697 0.329647988 + vertex -0.307767004 -0.456461996 -0.107084997 + endloop + endfacet + + facet normal -0.346002042 0.406495035 0.845602989 + outer loop + vertex -0.223100007 -0.341991007 -0.095679 + vertex 0.0810849965 -1.00276399 0.346430987 + vertex -0.00500100013 -1.04112697 0.329647988 + endloop + endfacet + + facet normal -0.409730107 0.753766179 0.513768196 + outer loop + vertex 0.185377002 -1.52670002 1.19387603 + vertex 0.132046998 -1.54397893 1.17669499 + vertex -0.00500100013 -1.04112697 0.329647988 + endloop + endfacet + + facet normal -0.430921048 0.742863119 0.512310088 + outer loop + vertex 0.0810849965 -1.00276399 0.346430987 + vertex 0.185377002 -1.52670002 1.19387603 + vertex -0.00500100013 -1.04112697 0.329647988 + endloop + endfacet + + facet normal -0.136055022 -0.455070078 0.880000114 + outer loop + vertex 0.185377002 -1.52670002 1.19387603 + vertex 0.140081003 -1.61137402 1.14308596 + vertex 0.132046998 -1.54397893 1.17669499 + endloop + endfacet + + facet normal -0.142182976 -0.452121913 0.880550802 + outer loop + vertex 0.185377002 -1.52670002 1.19387603 + vertex 0.232925996 -1.60044098 1.16369104 + vertex 0.140081003 -1.61137402 1.14308596 + endloop + endfacet + + facet normal -0.0768340006 -0.487081975 0.869969964 + outer loop + vertex 0.617762983 -2.61522293 0.623236001 + vertex 0.522632003 -2.61842704 0.61303997 + vertex 0.140081003 -1.61137402 1.14308596 + endloop + endfacet + + facet normal -0.130130008 -0.504070044 0.853803098 + outer loop + vertex 0.232925996 -1.60044098 1.16369104 + vertex 0.617762983 -2.61522293 0.623236001 + vertex 0.140081003 -1.61137402 1.14308596 + endloop + endfacet + + facet normal -0.0224649999 -0.874534965 0.484441966 + outer loop + vertex 0.617762983 -2.61522293 0.623236001 + vertex 0.804427981 -3.67682505 -1.28455305 + vertex 0.522632003 -2.61842704 0.61303997 + endloop + endfacet + + facet normal -0.19305791 -0.865288615 0.462605834 + outer loop + vertex 0.617762983 -2.61522293 0.623236001 + vertex 0.846280992 -3.67627597 -1.26605999 + vertex 0.804427981 -3.67682505 -1.28455305 + endloop + endfacet + + facet normal -0.149909943 -0.729361713 0.667501748 + outer loop + vertex 0.923933983 -3.97100115 -1.57915306 + vertex 0.904604971 -3.97317505 -1.58586907 + vertex 0.804427981 -3.67682505 -1.28455305 + endloop + endfacet + + facet normal -0.266439945 -0.733926833 0.62478888 + outer loop + vertex 0.846280992 -3.67627597 -1.26605999 + vertex 0.923933983 -3.97100115 -1.57915306 + vertex 0.804427981 -3.67682505 -1.28455305 + endloop + endfacet + + facet normal 0.272477865 -0.292855829 0.916510463 + outer loop + vertex -0.376302004 -0.324781001 -0.0446330011 + vertex -0.253796995 -0.214561 -0.0458349995 + vertex -0.223100007 -0.341991007 -0.095679 + endloop + endfacet + + facet normal -0.247450024 0.8675071 0.431509018 + outer loop + vertex 0.933724999 -3.97960711 -1.55623698 + vertex 0.911266029 -3.98531508 -1.55764103 + vertex 0.923933983 -3.97100115 -1.57915306 + endloop + endfacet + + facet normal -0.879710913 -0.0929590017 0.466333956 + outer loop + vertex 0.103583999 -0.929287016 0.403519988 + vertex 0.0810849965 -1.00276399 0.346430987 + vertex -0.223100007 -0.341991007 -0.095679 + endloop + endfacet + + facet normal -0.920438349 -0.313202113 0.233875081 + outer loop + vertex -0.253796995 -0.214561 -0.0458349995 + vertex 0.103583999 -0.929287016 0.403519988 + vertex -0.223100007 -0.341991007 -0.095679 + endloop + endfacet + + facet normal -0.969112217 0.137854025 0.204494044 + outer loop + vertex 0.103583999 -0.929287016 0.403519988 + vertex 0.185377002 -1.52670002 1.19387603 + vertex 0.0810849965 -1.00276399 0.346430987 + endloop + endfacet + + facet normal -0.958291709 0.171372935 0.228709921 + outer loop + vertex 0.103583999 -0.929287016 0.403519988 + vertex 0.204065993 -1.50625706 1.256863 + vertex 0.185377002 -1.52670002 1.19387603 + endloop + endfacet + + facet normal -0.774107099 -0.591494083 0.225595027 + outer loop + vertex 0.282788992 -1.63802397 1.23625302 + vertex 0.232925996 -1.60044098 1.16369104 + vertex 0.185377002 -1.52670002 1.19387603 + endloop + endfacet + + facet normal -0.760899842 -0.516106904 0.393274903 + outer loop + vertex 0.204065993 -1.50625706 1.256863 + vertex 0.282788992 -1.63802397 1.23625302 + vertex 0.185377002 -1.52670002 1.19387603 + endloop + endfacet + + facet normal -0.819471061 -0.47864899 0.315218031 + outer loop + vertex 0.282788992 -1.63802397 1.23625302 + vertex 0.617762983 -2.61522293 0.623236001 + vertex 0.232925996 -1.60044098 1.16369104 + endloop + endfacet + + facet normal -0.778668165 -0.502565145 0.375638098 + outer loop + vertex 0.282788992 -1.63802397 1.23625302 + vertex 0.676886976 -2.68409705 0.653648019 + vertex 0.617762983 -2.61522293 0.623236001 + endloop + endfacet + + facet normal -0.907485962 -0.403503984 0.11684899 + outer loop + vertex 0.870212972 -3.71105099 -1.20028496 + vertex 0.846280992 -3.67627597 -1.26605999 + vertex 0.617762983 -2.61522293 0.623236001 + endloop + endfacet + + facet normal -0.78637892 -0.57148093 0.23455897 + outer loop + vertex 0.676886976 -2.68409705 0.653648019 + vertex 0.870212972 -3.71105099 -1.20028496 + vertex 0.617762983 -2.61522293 0.623236001 + endloop + endfacet + + facet normal -0.915194154 -0.380924076 0.131592035 + outer loop + vertex 0.870212972 -3.71105099 -1.20028496 + vertex 0.923933983 -3.97100115 -1.57915306 + vertex 0.846280992 -3.67627597 -1.26605999 + endloop + endfacet + + facet normal -0.864411891 -0.463298917 0.195310965 + outer loop + vertex 0.870212972 -3.71105099 -1.20028496 + vertex 0.933724999 -3.97960711 -1.55623698 + vertex 0.923933983 -3.97100115 -1.57915306 + endloop + endfacet + + facet normal 0.287100911 0.22565192 0.930942714 + outer loop + vertex 0.254595995 0.172016993 -0.203339994 + vertex 0.134731993 0.118957996 -0.153512999 + vertex 0.128368005 0.27710101 -0.189882994 + endloop + endfacet + + facet normal -0.184374034 -0.887741089 0.421808064 + outer loop + vertex 1.54126191 3.22838593 -1.48060107 + vertex 1.51856101 3.23323202 -1.48032296 + vertex 1.53517103 3.24041605 -1.45794499 + endloop + endfacet + + facet normal -0.475138009 0.667518914 -0.573290944 + outer loop + vertex 0.254595995 0.172016993 -0.203339994 + vertex 0.599097013 0.871927977 0.326090991 + vertex 0.515124023 0.832623005 0.349920988 + endloop + endfacet + + facet normal -0.518250883 0.659948826 -0.543951869 + outer loop + vertex 0.254595995 0.172016993 -0.203339994 + vertex 0.515124023 0.832623005 0.349920988 + vertex 0.134731993 0.118957996 -0.153512999 + endloop + endfacet + + facet normal -0.472386926 0.830829799 -0.294231981 + outer loop + vertex 0.599097013 0.871927977 0.326090991 + vertex 0.79035902 1.28275597 1.17908895 + vertex 0.515124023 0.832623005 0.349920988 + endloop + endfacet + + facet normal -0.327001125 0.87180227 -0.364734113 + outer loop + vertex 0.79035902 1.28275597 1.17908895 + vertex 0.737774014 1.28159404 1.22345495 + vertex 0.515124023 0.832623005 0.349920988 + endloop + endfacet + + facet normal -0.350684077 0.480596095 -0.803771198 + outer loop + vertex 0.79035902 1.28275597 1.17908895 + vertex 0.895301998 1.39467299 1.20021999 + vertex 0.851906002 1.45822704 1.25715399 + endloop + endfacet + + facet normal -0.56752795 0.492581934 -0.659753859 + outer loop + vertex 0.79035902 1.28275597 1.17908895 + vertex 0.851906002 1.45822704 1.25715399 + vertex 0.737774014 1.28159404 1.22345495 + endloop + endfacet + + facet normal -0.840571404 -0.0964380503 -0.533047318 + outer loop + vertex 0.895301998 1.39467299 1.20021999 + vertex 1.33289194 2.38362789 0.331256986 + vertex 0.851906002 1.45822704 1.25715399 + endloop + endfacet + + facet normal -0.887653232 0.000607000198 -0.460512102 + outer loop + vertex 1.33289194 2.38362789 0.331256986 + vertex 1.32048297 2.47545505 0.355296999 + vertex 0.851906002 1.45822704 1.25715399 + endloop + endfacet + + facet normal -0.991470754 -0.0184189957 -0.129020974 + outer loop + vertex 1.33289194 2.38362789 0.331256986 + vertex 1.51245093 2.94816399 -1.12917101 + vertex 1.50315905 2.98986292 -1.06372297 + endloop + endfacet + + facet normal -0.982979894 -0.0910649896 -0.159554973 + outer loop + vertex 1.33289194 2.38362789 0.331256986 + vertex 1.50315905 2.98986292 -1.06372297 + vertex 1.32048297 2.47545505 0.355296999 + endloop + endfacet + + facet normal -0.992559552 -0.0415029824 -0.114467941 + outer loop + vertex 1.51245093 2.94816399 -1.12917101 + vertex 1.54126191 3.22838593 -1.48060107 + vertex 1.50315905 2.98986292 -1.06372297 + endloop + endfacet + + facet normal -0.971499264 -0.156291023 -0.178220034 + outer loop + vertex 1.54126191 3.22838593 -1.48060107 + vertex 1.53517103 3.24041605 -1.45794499 + vertex 1.50315905 2.98986292 -1.06372297 + endloop + endfacet + + facet normal 0.28709814 0.225652128 0.930943549 + outer loop + vertex 0.134731993 0.118957996 -0.153512999 + vertex 0.0100760004 0.184983999 -0.131073996 + vertex 0.128368005 0.27710101 -0.189882994 + endloop + endfacet + + facet normal -0.184354961 -0.887750745 0.421795905 + outer loop + vertex 1.53517103 3.24041605 -1.45794499 + vertex 1.51856101 3.23323202 -1.48032296 + vertex 1.51657104 3.24703598 -1.45213997 + endloop + endfacet + + facet normal 0.127014041 0.525668204 -0.841154218 + outer loop + vertex 0.134731993 0.118957996 -0.153512999 + vertex 0.515124023 0.832623005 0.349920988 + vertex 0.0100760004 0.184983999 -0.131073996 + endloop + endfacet + + facet normal 0.362982959 0.356360972 -0.860958934 + outer loop + vertex 0.515124023 0.832623005 0.349920988 + vertex 0.427042991 0.859715998 0.324000001 + vertex 0.0100760004 0.184983999 -0.131073996 + endloop + endfacet + + facet normal 0.393000096 0.773265183 -0.497606099 + outer loop + vertex 0.515124023 0.832623005 0.349920988 + vertex 0.737774014 1.28159404 1.22345495 + vertex 0.68404901 1.30356801 1.21517205 + endloop + endfacet + + facet normal 0.385495007 0.776755929 -0.498039961 + outer loop + vertex 0.515124023 0.832623005 0.349920988 + vertex 0.68404901 1.30356801 1.21517205 + vertex 0.427042991 0.859715998 0.324000001 + endloop + endfacet + + facet normal 0.180199996 0.0707409903 -0.981082916 + outer loop + vertex 0.737774014 1.28159404 1.22345495 + vertex 0.851906002 1.45822704 1.25715399 + vertex 0.68404901 1.30356801 1.21517205 + endloop + endfacet + + facet normal 0.230535865 0.013906992 -0.972964406 + outer loop + vertex 0.851906002 1.45822704 1.25715399 + vertex 0.773847997 1.51072693 1.23941004 + vertex 0.68404901 1.30356801 1.21517205 + endloop + endfacet + + facet normal -0.213641912 -0.591137767 -0.777761698 + outer loop + vertex 0.851906002 1.45822704 1.25715399 + vertex 1.32048297 2.47545505 0.355296999 + vertex 1.25007606 2.53493595 0.329427987 + endloop + endfacet + + facet normal -0.219361112 -0.589029253 -0.777769387 + outer loop + vertex 0.851906002 1.45822704 1.25715399 + vertex 1.25007606 2.53493595 0.329427987 + vertex 0.773847997 1.51072693 1.23941004 + endloop + endfacet + + facet normal -0.526722312 -0.775172412 -0.348814219 + outer loop + vertex 1.32048297 2.47545505 0.355296999 + vertex 1.50315905 2.98986292 -1.06372297 + vertex 1.25007606 2.53493595 0.329427987 + endloop + endfacet + + facet normal -0.515794992 -0.782329023 -0.349166006 + outer loop + vertex 1.50315905 2.98986292 -1.06372297 + vertex 1.46452999 3.00791907 -1.04711497 + vertex 1.25007606 2.53493595 0.329427987 + endloop + endfacet + + facet normal -0.425265014 -0.747819066 -0.509819984 + outer loop + vertex 1.50315905 2.98986292 -1.06372297 + vertex 1.53517103 3.24041605 -1.45794499 + vertex 1.51657104 3.24703598 -1.45213997 + endloop + endfacet + + facet normal -0.532313049 -0.697276056 -0.48005107 + outer loop + vertex 1.50315905 2.98986292 -1.06372297 + vertex 1.51657104 3.24703598 -1.45213997 + vertex 1.46452999 3.00791907 -1.04711497 + endloop + endfacet + + facet normal 0.287100106 0.225650087 0.93094337 + outer loop + vertex 0.0100760004 0.184983999 -0.131073996 + vertex -0.0255030002 0.320378006 -0.152918994 + vertex 0.128368005 0.27710101 -0.189882994 + endloop + endfacet + + facet normal -0.184421048 -0.887741208 0.421787083 + outer loop + vertex 1.51657104 3.24703598 -1.45213997 + vertex 1.51856101 3.23323202 -1.48032296 + vertex 1.49947 3.24326396 -1.46755695 + endloop + endfacet + + facet normal 0.821849883 -0.134506986 -0.553597927 + outer loop + vertex 0.0100760004 0.184983999 -0.131073996 + vertex 0.427042991 0.859715998 0.324000001 + vertex 0.401181996 0.932807028 0.267848015 + endloop + endfacet + + facet normal 0.661470056 0.0531199984 -0.748088062 + outer loop + vertex 0.0100760004 0.184983999 -0.131073996 + vertex 0.401181996 0.932807028 0.267848015 + vertex -0.0255030002 0.320378006 -0.152918994 + endloop + endfacet + + facet normal 0.944037497 0.0903129503 -0.317232847 + outer loop + vertex 0.427042991 0.859715998 0.324000001 + vertex 0.68404901 1.30356801 1.21517205 + vertex 0.401181996 0.932807028 0.267848015 + endloop + endfacet + + facet normal 0.961416662 -0.0369919837 -0.272597879 + outer loop + vertex 0.68404901 1.30356801 1.21517205 + vertex 0.669640005 1.33213401 1.16047704 + vertex 0.401181996 0.932807028 0.267848015 + endloop + endfacet + + facet normal 0.79018414 -0.278662026 -0.545854032 + outer loop + vertex 0.68404901 1.30356801 1.21517205 + vertex 0.773847997 1.51072693 1.23941004 + vertex 0.719905972 1.51263702 1.16034794 + endloop + endfacet + + facet normal 0.896213293 -0.249837101 -0.366583139 + outer loop + vertex 0.68404901 1.30356801 1.21517205 + vertex 0.719905972 1.51263702 1.16034794 + vertex 0.669640005 1.33213401 1.16047704 + endloop + endfacet + + facet normal 0.608522832 -0.666114748 -0.431266844 + outer loop + vertex 0.773847997 1.51072693 1.23941004 + vertex 1.25007606 2.53493595 0.329427987 + vertex 0.719905972 1.51263702 1.16034794 + endloop + endfacet + + facet normal 0.533110023 -0.68267101 -0.499754041 + outer loop + vertex 1.25007606 2.53493595 0.329427987 + vertex 1.17469096 2.5172801 0.273131013 + vertex 0.719905972 1.51263702 1.16034794 + endloop + endfacet + + facet normal 0.592279136 -0.785880089 -0.177758023 + outer loop + vertex 1.25007606 2.53493595 0.329427987 + vertex 1.46452999 3.00791907 -1.04711497 + vertex 1.42565393 2.98873901 -1.09184897 + endloop + endfacet + + facet normal 0.385834247 -0.891602457 -0.237017125 + outer loop + vertex 1.25007606 2.53493595 0.329427987 + vertex 1.42565393 2.98873901 -1.09184897 + vertex 1.17469096 2.5172801 0.273131013 + endloop + endfacet + + facet normal 0.680419087 -0.665989041 -0.305759013 + outer loop + vertex 1.46452999 3.00791907 -1.04711497 + vertex 1.51657104 3.24703598 -1.45213997 + vertex 1.42565393 2.98873901 -1.09184897 + endloop + endfacet + + facet normal 0.528014839 -0.747651756 -0.402761877 + outer loop + vertex 1.51657104 3.24703598 -1.45213997 + vertex 1.49947 3.24326396 -1.46755695 + vertex 1.42565393 2.98873901 -1.09184897 + endloop + endfacet + + facet normal 0.287102133 0.225660101 0.93094033 + outer loop + vertex -0.0255030002 0.320378006 -0.152918994 + vertex 0.0547849983 0.423180997 -0.202600002 + vertex 0.128368005 0.27710101 -0.189882994 + endloop + endfacet + + facet normal -0.184494004 -0.887767971 0.421698987 + outer loop + vertex 1.49947 3.24326396 -1.46755695 + vertex 1.51856101 3.23323202 -1.48032296 + vertex 1.49674499 3.23194098 -1.49258697 + endloop + endfacet + + facet normal 0.800132632 -0.597048759 0.0576229766 + outer loop + vertex -0.0255030002 0.320378006 -0.152918994 + vertex 0.401181996 0.932807028 0.267848015 + vertex 0.0547849983 0.423180997 -0.202600002 + endloop + endfacet + + facet normal 0.781402111 -0.617047131 0.0930780172 + outer loop + vertex 0.401181996 0.932807028 0.267848015 + vertex 0.457011998 0.996855974 0.223748997 + vertex 0.0547849983 0.423180997 -0.202600002 + endloop + endfacet + + facet normal 0.591941774 -0.786967635 0.17403093 + outer loop + vertex 0.401181996 0.932807028 0.267848015 + vertex 0.669640005 1.33213401 1.16047704 + vertex 0.705398023 1.34577894 1.10055697 + endloop + endfacet + + facet normal 0.766158044 -0.64151305 0.0382480025 + outer loop + vertex 0.401181996 0.932807028 0.267848015 + vertex 0.705398023 1.34577894 1.10055697 + vertex 0.457011998 0.996855974 0.223748997 + endloop + endfacet + + facet normal 0.856900215 -0.238298073 0.457095116 + outer loop + vertex 0.669640005 1.33213401 1.16047704 + vertex 0.719905972 1.51263702 1.16034794 + vertex 0.705398023 1.34577894 1.10055697 + endloop + endfacet + + facet normal 0.958668172 -0.166126028 0.230992049 + outer loop + vertex 0.719905972 1.51263702 1.16034794 + vertex 0.730700016 1.46251702 1.07950306 + vertex 0.705398023 1.34577894 1.10055697 + endloop + endfacet + + facet normal 0.937909067 -0.32989803 0.107209012 + outer loop + vertex 0.719905972 1.51263702 1.16034794 + vertex 1.17469096 2.5172801 0.273131013 + vertex 1.15109396 2.43578506 0.228796005 + endloop + endfacet + + facet normal 0.949355602 -0.194093928 0.247085899 + outer loop + vertex 0.719905972 1.51263702 1.16034794 + vertex 1.15109396 2.43578506 0.228796005 + vertex 0.730700016 1.46251702 1.07950306 + endloop + endfacet + + facet normal 0.94807595 -0.310923964 0.0669199899 + outer loop + vertex 1.17469096 2.5172801 0.273131013 + vertex 1.42565393 2.98873901 -1.09184897 + vertex 1.15109396 2.43578506 0.228796005 + endloop + endfacet + + facet normal 0.943761826 -0.325121939 0.0600779913 + outer loop + vertex 1.42565393 2.98873901 -1.09184897 + vertex 1.415802 2.94676304 -1.16424298 + vertex 1.15109396 2.43578506 0.228796005 + endloop + endfacet + + facet normal 0.965287447 -0.260869861 0.012922992 + outer loop + vertex 1.42565393 2.98873901 -1.09184897 + vertex 1.49947 3.24326396 -1.46755695 + vertex 1.49674499 3.23194098 -1.49258697 + endloop + endfacet + + facet normal 0.966702342 -0.25537008 0.0165120047 + outer loop + vertex 1.42565393 2.98873901 -1.09184897 + vertex 1.49674499 3.23194098 -1.49258697 + vertex 1.415802 2.94676304 -1.16424298 + endloop + endfacet + + facet normal 0.287104994 0.225661978 0.930938959 + outer loop + vertex 0.0547849983 0.423180997 -0.202600002 + vertex 0.190484002 0.415984005 -0.242705002 + vertex 0.128368005 0.27710101 -0.189882994 + endloop + endfacet + + facet normal -0.184529021 -0.887732148 0.421759069 + outer loop + vertex 1.49674499 3.23194098 -1.49258697 + vertex 1.51856101 3.23323202 -1.48032296 + vertex 1.51044703 3.22158909 -1.50838101 + endloop + endfacet + + facet normal 0.0337819979 -0.611306012 0.790672958 + outer loop + vertex 0.0547849983 0.423180997 -0.202600002 + vertex 0.457011998 0.996855974 0.223748997 + vertex 0.552495003 1.00363195 0.224907994 + endloop + endfacet + + facet normal 0.175379902 -0.676896572 0.714879572 + outer loop + vertex 0.0547849983 0.423180997 -0.202600002 + vertex 0.552495003 1.00363195 0.224907994 + vertex 0.190484002 0.415984005 -0.242705002 + endloop + endfacet + + facet normal 0.0619340129 -0.933253109 0.353840023 + outer loop + vertex 0.457011998 0.996855974 0.223748997 + vertex 0.705398023 1.34577894 1.10055697 + vertex 0.552495003 1.00363195 0.224907994 + endloop + endfacet + + facet normal -0.055268988 -0.926689804 0.371740907 + outer loop + vertex 0.705398023 1.34577894 1.10055697 + vertex 0.764394999 1.33422804 1.08053398 + vertex 0.552495003 1.00363195 0.224907994 + endloop + endfacet + + facet normal 0.378070056 0.0843310058 0.921928108 + outer loop + vertex 0.705398023 1.34577894 1.10055697 + vertex 0.730700016 1.46251702 1.07950306 + vertex 0.798102021 1.39811099 1.05775404 + endloop + endfacet + + facet normal 0.343855888 0.149168938 0.927098632 + outer loop + vertex 0.705398023 1.34577894 1.10055697 + vertex 0.798102021 1.39811099 1.05775404 + vertex 0.764394999 1.33422804 1.08053398 + endloop + endfacet + + facet normal 0.588112831 0.373159915 0.717547894 + outer loop + vertex 0.730700016 1.46251702 1.07950306 + vertex 1.15109396 2.43578506 0.228796005 + vertex 0.798102021 1.39811099 1.05775404 + endloop + endfacet + + facet normal 0.621727347 0.348749191 0.701305389 + outer loop + vertex 1.15109396 2.43578506 0.228796005 + vertex 1.19705403 2.35181308 0.229809001 + vertex 0.798102021 1.39811099 1.05775404 + endloop + endfacet + + facet normal 0.900523543 0.324001819 0.289965838 + outer loop + vertex 1.15109396 2.43578506 0.228796005 + vertex 1.415802 2.94676304 -1.16424298 + vertex 1.44239593 2.91360092 -1.20978105 + endloop + endfacet + + facet normal 0.829440415 0.457835257 0.320024192 + outer loop + vertex 1.15109396 2.43578506 0.228796005 + vertex 1.44239593 2.91360092 -1.20978105 + vertex 1.19705403 2.35181308 0.229809001 + endloop + endfacet + + facet normal 0.90179795 0.19060199 0.387854964 + outer loop + vertex 1.415802 2.94676304 -1.16424298 + vertex 1.49674499 3.23194098 -1.49258697 + vertex 1.44239593 2.91360092 -1.20978105 + endloop + endfacet + + facet normal 0.809248447 0.306871176 0.500946224 + outer loop + vertex 1.49674499 3.23194098 -1.49258697 + vertex 1.51044703 3.22158909 -1.50838101 + vertex 1.44239593 2.91360092 -1.20978105 + endloop + endfacet + + facet normal 0.287105978 0.22566098 0.930938959 + outer loop + vertex 0.190484002 0.415984005 -0.242705002 + vertex 0.279406995 0.304206014 -0.243034005 + vertex 0.128368005 0.27710101 -0.189882994 + endloop + endfacet + + facet normal -0.184329927 -0.887789667 0.421724826 + outer loop + vertex 1.51044703 3.22158909 -1.50838101 + vertex 1.51856101 3.23323202 -1.48032296 + vertex 1.53025901 3.22000909 -1.50304699 + endloop + endfacet + + facet normal -0.455893964 -0.365067989 0.811717927 + outer loop + vertex 0.190484002 0.415984005 -0.242705002 + vertex 0.552495003 1.00363195 0.224907994 + vertex 0.279406995 0.304206014 -0.243034005 + endloop + endfacet + + facet normal -0.683324039 -0.202508017 0.701469064 + outer loop + vertex 0.552495003 1.00363195 0.224907994 + vertex 0.615728974 0.948033988 0.270455003 + vertex 0.279406995 0.304206014 -0.243034005 + endloop + endfacet + + facet normal -0.75348109 -0.528666079 0.390869021 + outer loop + vertex 0.552495003 1.00363195 0.224907994 + vertex 0.764394999 1.33422804 1.08053398 + vertex 0.802205026 1.30618 1.115484 + endloop + endfacet + + facet normal -0.750331104 -0.532756031 0.391375035 + outer loop + vertex 0.552495003 1.00363195 0.224907994 + vertex 0.802205026 1.30618 1.115484 + vertex 0.615728974 0.948033988 0.270455003 + endloop + endfacet + + facet normal -0.375712037 0.480761021 0.792281091 + outer loop + vertex 0.764394999 1.33422804 1.08053398 + vertex 0.798102021 1.39811099 1.05775404 + vertex 0.802205026 1.30618 1.115484 + endloop + endfacet + + facet normal -0.38203603 0.479180038 0.790212035 + outer loop + vertex 0.798102021 1.39811099 1.05775404 + vertex 0.871357024 1.36791801 1.11147904 + vertex 0.802205026 1.30618 1.115484 + endloop + endfacet + + facet normal -0.196147874 0.688334584 0.698370576 + outer loop + vertex 0.798102021 1.39811099 1.05775404 + vertex 1.19705403 2.35181308 0.229809001 + vertex 1.27796102 2.32860303 0.275409013 + endloop + endfacet + + facet normal -0.219507039 0.691687107 0.688030124 + outer loop + vertex 0.798102021 1.39811099 1.05775404 + vertex 1.27796102 2.32860303 0.275409013 + vertex 0.871357024 1.36791801 1.11147904 + endloop + endfacet + + facet normal 0.0565909743 0.926781595 0.371312857 + outer loop + vertex 1.19705403 2.35181308 0.229809001 + vertex 1.44239593 2.91360092 -1.20978105 + vertex 1.27796102 2.32860303 0.275409013 + endloop + endfacet + + facet normal -0.140208036 0.926429152 0.349386096 + outer loop + vertex 1.44239593 2.91360092 -1.20978105 + vertex 1.48540699 2.91422415 -1.19417298 + vertex 1.27796102 2.32860303 0.275409013 + endloop + endfacet + + facet normal -0.131446019 0.704857111 0.697064102 + outer loop + vertex 1.44239593 2.91360092 -1.20978105 + vertex 1.51044703 3.22158909 -1.50838101 + vertex 1.53025901 3.22000909 -1.50304699 + endloop + endfacet + + facet normal -0.250628948 0.705919802 0.662466824 + outer loop + vertex 1.44239593 2.91360092 -1.20978105 + vertex 1.53025901 3.22000909 -1.50304699 + vertex 1.48540699 2.91422415 -1.19417298 + endloop + endfacet + + facet normal 0.287107021 0.225659013 0.930939138 + outer loop + vertex 0.279406995 0.304206014 -0.243034005 + vertex 0.254595995 0.172016993 -0.203339994 + vertex 0.128368005 0.27710101 -0.189882994 + endloop + endfacet + + facet normal -0.184386045 -0.887791276 0.42169711 + outer loop + vertex 1.53025901 3.22000909 -1.50304699 + vertex 1.51856101 3.23323202 -1.48032296 + vertex 1.54126191 3.22838593 -1.48060107 + endloop + endfacet + + facet normal -0.923753202 0.336100101 0.183621049 + outer loop + vertex 0.279406995 0.304206014 -0.243034005 + vertex 0.615728974 0.948033988 0.270455003 + vertex 0.599097013 0.871927977 0.326090991 + endloop + endfacet + + facet normal -0.929180562 0.254806876 0.26776287 + outer loop + vertex 0.279406995 0.304206014 -0.243034005 + vertex 0.599097013 0.871927977 0.326090991 + vertex 0.254595995 0.172016993 -0.203339994 + endloop + endfacet + + facet normal -0.95622313 0.277304024 0.093486011 + outer loop + vertex 0.615728974 0.948033988 0.270455003 + vertex 0.802205026 1.30618 1.115484 + vertex 0.599097013 0.871927977 0.326090991 + endloop + endfacet + + facet normal -0.900449932 0.434893966 -0.00755599979 + outer loop + vertex 0.802205026 1.30618 1.115484 + vertex 0.79035902 1.28275597 1.17908895 + vertex 0.599097013 0.871927977 0.326090991 + endloop + endfacet + + facet normal -0.666760981 0.743948996 -0.0443829969 + outer loop + vertex 0.802205026 1.30618 1.115484 + vertex 0.871357024 1.36791801 1.11147904 + vertex 0.895301998 1.39467299 1.20021999 + endloop + endfacet + + facet normal -0.735310256 0.66884923 0.109359048 + outer loop + vertex 0.802205026 1.30618 1.115484 + vertex 0.895301998 1.39467299 1.20021999 + vertex 0.79035902 1.28275597 1.17908895 + endloop + endfacet + + facet normal -0.882354856 0.459964931 0.0994089916 + outer loop + vertex 0.871357024 1.36791801 1.11147904 + vertex 1.27796102 2.32860303 0.275409013 + vertex 0.895301998 1.39467299 1.20021999 + endloop + endfacet + + facet normal -0.796392024 0.557778955 0.233756989 + outer loop + vertex 1.27796102 2.32860303 0.275409013 + vertex 1.33289194 2.38362789 0.331256986 + vertex 0.895301998 1.39467299 1.20021999 + endloop + endfacet + + facet normal -0.852394104 0.515902042 0.0852600113 + outer loop + vertex 1.27796102 2.32860303 0.275409013 + vertex 1.48540699 2.91422415 -1.19417298 + vertex 1.51245093 2.94816399 -1.12917101 + endloop + endfacet + + facet normal -0.770075798 0.621154904 0.145429969 + outer loop + vertex 1.27796102 2.32860303 0.275409013 + vertex 1.51245093 2.94816399 -1.12917101 + vertex 1.33289194 2.38362789 0.331256986 + endloop + endfacet + + facet normal -0.917796791 0.340652943 0.203971952 + outer loop + vertex 1.48540699 2.91422415 -1.19417298 + vertex 1.53025901 3.22000909 -1.50304699 + vertex 1.51245093 2.94816399 -1.12917101 + endloop + endfacet + + facet normal -0.865918994 0.42326802 0.266512007 + outer loop + vertex 1.53025901 3.22000909 -1.50304699 + vertex 1.54126191 3.22838593 -1.48060107 + vertex 1.51245093 2.94816399 -1.12917101 + endloop + endfacet + + facet normal 0.287100911 -0.22565192 0.930942714 + outer loop + vertex 0.073210001 -0.249522001 -0.189882994 + vertex 0.0795739964 -0.0913780034 -0.153512999 + vertex 0.199438006 -0.144437 -0.203339994 + endloop + endfacet + + facet normal -0.184375003 0.887741923 0.421805978 + outer loop + vertex 1.48001194 -3.21283698 -1.45794499 + vertex 1.46340299 -3.20565391 -1.48032296 + vertex 1.48610401 -3.20080709 -1.48060107 + endloop + endfacet + + facet normal -0.475138009 -0.667518914 -0.573290944 + outer loop + vertex 0.459966004 -0.805042982 0.349920988 + vertex 0.543938994 -0.844349027 0.326090991 + vertex 0.199438006 -0.144437 -0.203339994 + endloop + endfacet + + facet normal -0.518250108 -0.659949124 -0.543952107 + outer loop + vertex 0.0795739964 -0.0913780034 -0.153512999 + vertex 0.459966004 -0.805042982 0.349920988 + vertex 0.199438006 -0.144437 -0.203339994 + endloop + endfacet + + facet normal -0.472386926 -0.830829799 -0.294231981 + outer loop + vertex 0.459966004 -0.805042982 0.349920988 + vertex 0.735199988 -1.25517702 1.17908895 + vertex 0.543938994 -0.844349027 0.326090991 + endloop + endfacet + + facet normal -0.327001125 -0.87180227 -0.364734113 + outer loop + vertex 0.459966004 -0.805042982 0.349920988 + vertex 0.682615995 -1.25401497 1.22345495 + vertex 0.735199988 -1.25517702 1.17908895 + endloop + endfacet + + facet normal -0.35068512 -0.480595142 -0.803771257 + outer loop + vertex 0.796747029 -1.43064797 1.25715399 + vertex 0.840143979 -1.36709404 1.20021999 + vertex 0.735199988 -1.25517702 1.17908895 + endloop + endfacet + + facet normal -0.56752795 -0.492581934 -0.659753859 + outer loop + vertex 0.682615995 -1.25401497 1.22345495 + vertex 0.796747029 -1.43064797 1.25715399 + vertex 0.735199988 -1.25517702 1.17908895 + endloop + endfacet + + facet normal -0.840571702 0.0964379609 -0.533046842 + outer loop + vertex 0.796747029 -1.43064797 1.25715399 + vertex 1.27773297 -2.35605001 0.331256986 + vertex 0.840143979 -1.36709404 1.20021999 + endloop + endfacet + + facet normal -0.887653232 -0.000607000198 -0.460512102 + outer loop + vertex 0.796747029 -1.43064797 1.25715399 + vertex 1.265324 -2.44787598 0.355296999 + vertex 1.27773297 -2.35605001 0.331256986 + endloop + endfacet + + facet normal -0.991470754 0.0184189957 -0.129020974 + outer loop + vertex 1.44800103 -2.96228409 -1.06372297 + vertex 1.45729196 -2.92058492 -1.12917101 + vertex 1.27773297 -2.35605001 0.331256986 + endloop + endfacet + + facet normal -0.982979894 0.0910649896 -0.159554973 + outer loop + vertex 1.265324 -2.44787598 0.355296999 + vertex 1.44800103 -2.96228409 -1.06372297 + vertex 1.27773297 -2.35605001 0.331256986 + endloop + endfacet + + facet normal -0.992559552 0.0415029824 -0.114467941 + outer loop + vertex 1.44800103 -2.96228409 -1.06372297 + vertex 1.48610401 -3.20080709 -1.48060107 + vertex 1.45729196 -2.92058492 -1.12917101 + endloop + endfacet + + facet normal -0.971497953 0.156295985 -0.178222984 + outer loop + vertex 1.44800103 -2.96228409 -1.06372297 + vertex 1.48001194 -3.21283698 -1.45794499 + vertex 1.48610401 -3.20080709 -1.48060107 + endloop + endfacet + + facet normal 0.28709814 -0.225652128 0.930943549 + outer loop + vertex 0.073210001 -0.249522001 -0.189882994 + vertex -0.0450830013 -0.157405004 -0.131073996 + vertex 0.0795739964 -0.0913780034 -0.153512999 + endloop + endfacet + + facet normal -0.184356034 0.887751043 0.42179507 + outer loop + vertex 1.46141195 -3.2194581 -1.45213997 + vertex 1.46340299 -3.20565391 -1.48032296 + vertex 1.48001194 -3.21283698 -1.45794499 + endloop + endfacet + + facet normal 0.127014041 -0.525668204 -0.841154218 + outer loop + vertex -0.0450830013 -0.157405004 -0.131073996 + vertex 0.459966004 -0.805042982 0.349920988 + vertex 0.0795739964 -0.0913780034 -0.153512999 + endloop + endfacet + + facet normal 0.362984002 -0.356359988 -0.860958934 + outer loop + vertex -0.0450830013 -0.157405004 -0.131073996 + vertex 0.371885002 -0.832136989 0.324000001 + vertex 0.459966004 -0.805042982 0.349920988 + endloop + endfacet + + facet normal 0.393002957 -0.773263991 -0.497605979 + outer loop + vertex 0.628889978 -1.27599001 1.21517205 + vertex 0.682615995 -1.25401497 1.22345495 + vertex 0.459966004 -0.805042982 0.349920988 + endloop + endfacet + + facet normal 0.38549611 -0.776755273 -0.49804014 + outer loop + vertex 0.371885002 -0.832136989 0.324000001 + vertex 0.628889978 -1.27599001 1.21517205 + vertex 0.459966004 -0.805042982 0.349920988 + endloop + endfacet + + facet normal 0.180200949 -0.0707409829 -0.981082737 + outer loop + vertex 0.628889978 -1.27599001 1.21517205 + vertex 0.796747029 -1.43064797 1.25715399 + vertex 0.682615995 -1.25401497 1.22345495 + endloop + endfacet + + facet normal 0.230534911 -0.0139069958 -0.972964704 + outer loop + vertex 0.628889978 -1.27599001 1.21517205 + vertex 0.718689024 -1.48314798 1.23941004 + vertex 0.796747029 -1.43064797 1.25715399 + endloop + endfacet + + facet normal -0.213641912 0.591137767 -0.777761698 + outer loop + vertex 1.19491804 -2.50735712 0.329427987 + vertex 1.265324 -2.44787598 0.355296999 + vertex 0.796747029 -1.43064797 1.25715399 + endloop + endfacet + + facet normal -0.219361112 0.589029253 -0.777769387 + outer loop + vertex 0.718689024 -1.48314798 1.23941004 + vertex 1.19491804 -2.50735712 0.329427987 + vertex 0.796747029 -1.43064797 1.25715399 + endloop + endfacet + + facet normal -0.526722312 0.775172412 -0.348814219 + outer loop + vertex 1.19491804 -2.50735712 0.329427987 + vertex 1.44800103 -2.96228409 -1.06372297 + vertex 1.265324 -2.44787598 0.355296999 + endloop + endfacet + + facet normal -0.515791953 0.78233093 -0.349165976 + outer loop + vertex 1.19491804 -2.50735712 0.329427987 + vertex 1.40937102 -2.98034 -1.04711497 + vertex 1.44800103 -2.96228409 -1.06372297 + endloop + endfacet + + facet normal -0.425266892 0.747818768 -0.509818792 + outer loop + vertex 1.46141195 -3.2194581 -1.45213997 + vertex 1.48001194 -3.21283698 -1.45794499 + vertex 1.44800103 -2.96228409 -1.06372297 + endloop + endfacet + + facet normal -0.53230989 0.697277844 -0.480051875 + outer loop + vertex 1.40937102 -2.98034 -1.04711497 + vertex 1.46141195 -3.2194581 -1.45213997 + vertex 1.44800103 -2.96228409 -1.06372297 + endloop + endfacet + + facet normal 0.287100106 -0.225650087 0.93094337 + outer loop + vertex 0.073210001 -0.249522001 -0.189882994 + vertex -0.0806619972 -0.292798996 -0.152918994 + vertex -0.0450830013 -0.157405004 -0.131073996 + endloop + endfacet + + facet normal -0.184420034 0.887742043 0.42178604 + outer loop + vertex 1.44431102 -3.21568489 -1.46755695 + vertex 1.46340299 -3.20565391 -1.48032296 + vertex 1.46141195 -3.2194581 -1.45213997 + endloop + endfacet + + facet normal 0.821849883 0.134506986 -0.553597927 + outer loop + vertex 0.346022993 -0.905228019 0.267848015 + vertex 0.371885002 -0.832136989 0.324000001 + vertex -0.0450830013 -0.157405004 -0.131073996 + endloop + endfacet + + facet normal 0.661470056 -0.0531199984 -0.748088062 + outer loop + vertex -0.0806619972 -0.292798996 -0.152918994 + vertex 0.346022993 -0.905228019 0.267848015 + vertex -0.0450830013 -0.157405004 -0.131073996 + endloop + endfacet + + facet normal 0.944037497 -0.0903129503 -0.317232847 + outer loop + vertex 0.346022993 -0.905228019 0.267848015 + vertex 0.628889978 -1.27599001 1.21517205 + vertex 0.371885002 -0.832136989 0.324000001 + endloop + endfacet + + facet normal 0.961416781 0.0369929932 -0.272596925 + outer loop + vertex 0.346022993 -0.905228019 0.267848015 + vertex 0.614481986 -1.30455494 1.16047704 + vertex 0.628889978 -1.27599001 1.21517205 + endloop + endfacet + + facet normal 0.790184855 0.278661966 -0.545852959 + outer loop + vertex 0.664747 -1.485057 1.16034794 + vertex 0.718689024 -1.48314798 1.23941004 + vertex 0.628889978 -1.27599001 1.21517205 + endloop + endfacet + + facet normal 0.89621377 0.249836937 -0.366581917 + outer loop + vertex 0.614481986 -1.30455494 1.16047704 + vertex 0.664747 -1.485057 1.16034794 + vertex 0.628889978 -1.27599001 1.21517205 + endloop + endfacet + + facet normal 0.608522832 0.666114748 -0.431266844 + outer loop + vertex 0.664747 -1.485057 1.16034794 + vertex 1.19491804 -2.50735712 0.329427987 + vertex 0.718689024 -1.48314798 1.23941004 + endloop + endfacet + + facet normal 0.53311193 0.682670951 -0.499751985 + outer loop + vertex 0.664747 -1.485057 1.16034794 + vertex 1.11953306 -2.48970103 0.273131013 + vertex 1.19491804 -2.50735712 0.329427987 + endloop + endfacet + + facet normal 0.592273772 0.785883725 -0.17775993 + outer loop + vertex 1.37049496 -2.96115994 -1.09184897 + vertex 1.40937102 -2.98034 -1.04711497 + vertex 1.19491804 -2.50735712 0.329427987 + endloop + endfacet + + facet normal 0.385835916 0.891601741 -0.237016946 + outer loop + vertex 1.11953306 -2.48970103 0.273131013 + vertex 1.37049496 -2.96115994 -1.09184897 + vertex 1.19491804 -2.50735712 0.329427987 + endloop + endfacet + + facet normal 0.680415094 0.665992081 -0.305761069 + outer loop + vertex 1.37049496 -2.96115994 -1.09184897 + vertex 1.46141195 -3.2194581 -1.45213997 + vertex 1.40937102 -2.98034 -1.04711497 + endloop + endfacet + + facet normal 0.528015077 0.747652054 -0.402761072 + outer loop + vertex 1.37049496 -2.96115994 -1.09184897 + vertex 1.44431102 -3.21568489 -1.46755695 + vertex 1.46141195 -3.2194581 -1.45213997 + endloop + endfacet + + facet normal 0.287102133 -0.225660101 0.93094033 + outer loop + vertex 0.073210001 -0.249522001 -0.189882994 + vertex -0.000372999988 -0.395601988 -0.202600002 + vertex -0.0806619972 -0.292798996 -0.152918994 + endloop + endfacet + + facet normal -0.184492007 0.887767911 0.421699971 + outer loop + vertex 1.44158602 -3.20436192 -1.49258697 + vertex 1.46340299 -3.20565391 -1.48032296 + vertex 1.44431102 -3.21568489 -1.46755695 + endloop + endfacet + + facet normal 0.800132632 0.597048759 0.0576229766 + outer loop + vertex -0.000372999988 -0.395601988 -0.202600002 + vertex 0.346022993 -0.905228019 0.267848015 + vertex -0.0806619972 -0.292798996 -0.152918994 + endloop + endfacet + + facet normal 0.781401157 0.617048144 0.0930790231 + outer loop + vertex -0.000372999988 -0.395601988 -0.202600002 + vertex 0.401854008 -0.969277024 0.223748997 + vertex 0.346022993 -0.905228019 0.267848015 + endloop + endfacet + + facet normal 0.591942251 0.786967278 0.174031064 + outer loop + vertex 0.650238991 -1.31819999 1.10055697 + vertex 0.614481986 -1.30455494 1.16047704 + vertex 0.346022993 -0.905228019 0.267848015 + endloop + endfacet + + facet normal 0.766158044 0.64151305 0.0382480025 + outer loop + vertex 0.401854008 -0.969277024 0.223748997 + vertex 0.650238991 -1.31819999 1.10055697 + vertex 0.346022993 -0.905228019 0.267848015 + endloop + endfacet + + facet normal 0.856900215 0.238298073 0.457095116 + outer loop + vertex 0.650238991 -1.31819999 1.10055697 + vertex 0.664747 -1.485057 1.16034794 + vertex 0.614481986 -1.30455494 1.16047704 + endloop + endfacet + + facet normal 0.958668351 0.166127056 0.230991095 + outer loop + vertex 0.650238991 -1.31819999 1.10055697 + vertex 0.675541997 -1.43493795 1.07950306 + vertex 0.664747 -1.485057 1.16034794 + endloop + endfacet + + facet normal 0.937909067 0.329899043 0.107207015 + outer loop + vertex 1.09593499 -2.40820599 0.228796005 + vertex 1.11953306 -2.48970103 0.273131013 + vertex 0.664747 -1.485057 1.16034794 + endloop + endfacet + + facet normal 0.949355602 0.194093928 0.247085899 + outer loop + vertex 0.675541997 -1.43493795 1.07950306 + vertex 1.09593499 -2.40820599 0.228796005 + vertex 0.664747 -1.485057 1.16034794 + endloop + endfacet + + facet normal 0.948075712 0.310924917 0.0669189766 + outer loop + vertex 1.09593499 -2.40820599 0.228796005 + vertex 1.37049496 -2.96115994 -1.09184897 + vertex 1.11953306 -2.48970103 0.273131013 + endloop + endfacet + + facet normal 0.943762779 0.325118899 0.0600789823 + outer loop + vertex 1.09593499 -2.40820599 0.228796005 + vertex 1.36064398 -2.91918492 -1.16424298 + vertex 1.37049496 -2.96115994 -1.09184897 + endloop + endfacet + + facet normal 0.965285242 0.260878086 0.0129180038 + outer loop + vertex 1.44158602 -3.20436192 -1.49258697 + vertex 1.44431102 -3.21568489 -1.46755695 + vertex 1.37049496 -2.96115994 -1.09184897 + endloop + endfacet + + facet normal 0.966703117 0.255367041 0.0165140014 + outer loop + vertex 1.36064398 -2.91918492 -1.16424298 + vertex 1.44158602 -3.20436192 -1.49258697 + vertex 1.37049496 -2.96115994 -1.09184897 + endloop + endfacet + + facet normal 0.287104994 -0.225661978 0.930938959 + outer loop + vertex 0.073210001 -0.249522001 -0.189882994 + vertex 0.135325 -0.388404995 -0.242705002 + vertex -0.000372999988 -0.395601988 -0.202600002 + endloop + endfacet + + facet normal -0.184526041 0.887733161 0.421758115 + outer loop + vertex 1.45528793 -3.19401002 -1.50838101 + vertex 1.46340299 -3.20565391 -1.48032296 + vertex 1.44158602 -3.20436192 -1.49258697 + endloop + endfacet + + facet normal 0.0337819979 0.611306012 0.790672958 + outer loop + vertex 0.497337013 -0.976052999 0.224907994 + vertex 0.401854008 -0.969277024 0.223748997 + vertex -0.000372999988 -0.395601988 -0.202600002 + endloop + endfacet + + facet normal 0.175379902 0.676896572 0.714879572 + outer loop + vertex 0.135325 -0.388404995 -0.242705002 + vertex 0.497337013 -0.976052999 0.224907994 + vertex -0.000372999988 -0.395601988 -0.202600002 + endloop + endfacet + + facet normal 0.0619340129 0.933253109 0.353840023 + outer loop + vertex 0.497337013 -0.976052999 0.224907994 + vertex 0.650238991 -1.31819999 1.10055697 + vertex 0.401854008 -0.969277024 0.223748997 + endloop + endfacet + + facet normal -0.0552679971 0.926689863 0.371740967 + outer loop + vertex 0.497337013 -0.976052999 0.224907994 + vertex 0.709236979 -1.30664897 1.08053398 + vertex 0.650238991 -1.31819999 1.10055697 + endloop + endfacet + + facet normal 0.378070056 -0.0843310058 0.921928108 + outer loop + vertex 0.742944002 -1.37053204 1.05775404 + vertex 0.675541997 -1.43493795 1.07950306 + vertex 0.650238991 -1.31819999 1.10055697 + endloop + endfacet + + facet normal 0.343855888 -0.149168938 0.927098632 + outer loop + vertex 0.709236979 -1.30664897 1.08053398 + vertex 0.742944002 -1.37053204 1.05775404 + vertex 0.650238991 -1.31819999 1.10055697 + endloop + endfacet + + facet normal 0.588112831 -0.373159915 0.717547894 + outer loop + vertex 0.742944002 -1.37053204 1.05775404 + vertex 1.09593499 -2.40820599 0.228796005 + vertex 0.675541997 -1.43493795 1.07950306 + endloop + endfacet + + facet normal 0.621727943 -0.348748952 0.701304913 + outer loop + vertex 0.742944002 -1.37053204 1.05775404 + vertex 1.14189506 -2.32423401 0.229809001 + vertex 1.09593499 -2.40820599 0.228796005 + endloop + endfacet + + facet normal 0.900525928 -0.323995978 0.289964974 + outer loop + vertex 1.38723803 -2.88602304 -1.20978105 + vertex 1.36064398 -2.91918492 -1.16424298 + vertex 1.09593499 -2.40820599 0.228796005 + endloop + endfacet + + facet normal 0.829440713 -0.45783484 0.320023894 + outer loop + vertex 1.14189506 -2.32423401 0.229809001 + vertex 1.38723803 -2.88602304 -1.20978105 + vertex 1.09593499 -2.40820599 0.228796005 + endloop + endfacet + + facet normal 0.901799619 -0.190599918 0.387851864 + outer loop + vertex 1.38723803 -2.88602304 -1.20978105 + vertex 1.44158602 -3.20436192 -1.49258697 + vertex 1.36064398 -2.91918492 -1.16424298 + endloop + endfacet + + facet normal 0.809241116 -0.306879073 0.500953138 + outer loop + vertex 1.38723803 -2.88602304 -1.20978105 + vertex 1.45528793 -3.19401002 -1.50838101 + vertex 1.44158602 -3.20436192 -1.49258697 + endloop + endfacet + + facet normal 0.287105978 -0.22566098 0.930938959 + outer loop + vertex 0.073210001 -0.249522001 -0.189882994 + vertex 0.224249005 -0.276625991 -0.243034005 + vertex 0.135325 -0.388404995 -0.242705002 + endloop + endfacet + + facet normal -0.184329927 0.887789667 0.421724826 + outer loop + vertex 1.47510099 -3.19243002 -1.50304699 + vertex 1.46340299 -3.20565391 -1.48032296 + vertex 1.45528793 -3.19401002 -1.50838101 + endloop + endfacet + + facet normal -0.455893964 0.365067989 0.811717927 + outer loop + vertex 0.224249005 -0.276625991 -0.243034005 + vertex 0.497337013 -0.976052999 0.224907994 + vertex 0.135325 -0.388404995 -0.242705002 + endloop + endfacet + + facet normal -0.683324039 0.202508017 0.701469064 + outer loop + vertex 0.224249005 -0.276625991 -0.243034005 + vertex 0.560570002 -0.920454979 0.270455003 + vertex 0.497337013 -0.976052999 0.224907994 + endloop + endfacet + + facet normal -0.753481925 0.528664887 0.390868932 + outer loop + vertex 0.747047007 -1.27860093 1.115484 + vertex 0.709236979 -1.30664897 1.08053398 + vertex 0.497337013 -0.976052999 0.224907994 + endloop + endfacet + + facet normal -0.750331104 0.532756031 0.391375035 + outer loop + vertex 0.560570002 -0.920454979 0.270455003 + vertex 0.747047007 -1.27860093 1.115484 + vertex 0.497337013 -0.976052999 0.224907994 + endloop + endfacet + + facet normal -0.375710905 -0.480760843 0.792281747 + outer loop + vertex 0.747047007 -1.27860093 1.115484 + vertex 0.742944002 -1.37053204 1.05775404 + vertex 0.709236979 -1.30664897 1.08053398 + endloop + endfacet + + facet normal -0.382036179 -0.479179204 0.790212393 + outer loop + vertex 0.747047007 -1.27860093 1.115484 + vertex 0.816199005 -1.34033895 1.11147904 + vertex 0.742944002 -1.37053204 1.05775404 + endloop + endfacet + + facet normal -0.196147874 -0.688334584 0.698370576 + outer loop + vertex 1.22280204 -2.30102396 0.275409013 + vertex 1.14189506 -2.32423401 0.229809001 + vertex 0.742944002 -1.37053204 1.05775404 + endloop + endfacet + + facet normal -0.219507039 -0.691687107 0.688030124 + outer loop + vertex 0.816199005 -1.34033895 1.11147904 + vertex 1.22280204 -2.30102396 0.275409013 + vertex 0.742944002 -1.37053204 1.05775404 + endloop + endfacet + + facet normal 0.0565900318 -0.926781535 0.371313214 + outer loop + vertex 1.22280204 -2.30102396 0.275409013 + vertex 1.38723803 -2.88602304 -1.20978105 + vertex 1.14189506 -2.32423401 0.229809001 + endloop + endfacet + + facet normal -0.140208036 -0.926429152 0.349386096 + outer loop + vertex 1.22280204 -2.30102396 0.275409013 + vertex 1.43024898 -2.88664603 -1.19417298 + vertex 1.38723803 -2.88602304 -1.20978105 + endloop + endfacet + + facet normal -0.131445929 -0.704857588 0.697063565 + outer loop + vertex 1.47510099 -3.19243002 -1.50304699 + vertex 1.45528793 -3.19401002 -1.50838101 + vertex 1.38723803 -2.88602304 -1.20978105 + endloop + endfacet + + facet normal -0.250627995 -0.705919981 0.662467003 + outer loop + vertex 1.43024898 -2.88664603 -1.19417298 + vertex 1.47510099 -3.19243002 -1.50304699 + vertex 1.38723803 -2.88602304 -1.20978105 + endloop + endfacet + + facet normal 0.287107021 -0.225659013 0.930939138 + outer loop + vertex 0.073210001 -0.249522001 -0.189882994 + vertex 0.199438006 -0.144437 -0.203339994 + vertex 0.224249005 -0.276625991 -0.243034005 + endloop + endfacet + + facet normal -0.184387118 0.887791514 0.421696275 + outer loop + vertex 1.48610401 -3.20080709 -1.48060107 + vertex 1.46340299 -3.20565391 -1.48032296 + vertex 1.47510099 -3.19243002 -1.50304699 + endloop + endfacet + + facet normal -0.923753202 -0.336100101 0.183621049 + outer loop + vertex 0.543938994 -0.844349027 0.326090991 + vertex 0.560570002 -0.920454979 0.270455003 + vertex 0.224249005 -0.276625991 -0.243034005 + endloop + endfacet + + facet normal -0.929180801 -0.254806966 0.267761976 + outer loop + vertex 0.199438006 -0.144437 -0.203339994 + vertex 0.543938994 -0.844349027 0.326090991 + vertex 0.224249005 -0.276625991 -0.243034005 + endloop + endfacet + + facet normal -0.95622313 -0.277304024 0.093486011 + outer loop + vertex 0.543938994 -0.844349027 0.326090991 + vertex 0.747047007 -1.27860093 1.115484 + vertex 0.560570002 -0.920454979 0.270455003 + endloop + endfacet + + facet normal -0.900449932 -0.434893966 -0.00755599979 + outer loop + vertex 0.543938994 -0.844349027 0.326090991 + vertex 0.735199988 -1.25517702 1.17908895 + vertex 0.747047007 -1.27860093 1.115484 + endloop + endfacet + + facet normal -0.666762054 -0.743947983 -0.0443830006 + outer loop + vertex 0.840143979 -1.36709404 1.20021999 + vertex 0.816199005 -1.34033895 1.11147904 + vertex 0.747047007 -1.27860093 1.115484 + endloop + endfacet + + facet normal -0.735310256 -0.66884923 0.109359048 + outer loop + vertex 0.735199988 -1.25517702 1.17908895 + vertex 0.840143979 -1.36709404 1.20021999 + vertex 0.747047007 -1.27860093 1.115484 + endloop + endfacet + + facet normal -0.882354856 -0.459964931 0.0994089916 + outer loop + vertex 0.840143979 -1.36709404 1.20021999 + vertex 1.22280204 -2.30102396 0.275409013 + vertex 0.816199005 -1.34033895 1.11147904 + endloop + endfacet + + facet normal -0.796393156 -0.55777812 0.233755067 + outer loop + vertex 0.840143979 -1.36709404 1.20021999 + vertex 1.27773297 -2.35605001 0.331256986 + vertex 1.22280204 -2.30102396 0.275409013 + endloop + endfacet + + facet normal -0.852396131 -0.515899062 0.0852590129 + outer loop + vertex 1.45729196 -2.92058492 -1.12917101 + vertex 1.43024898 -2.88664603 -1.19417298 + vertex 1.22280204 -2.30102396 0.275409013 + endloop + endfacet + + facet normal -0.770077705 -0.621152759 0.145428941 + outer loop + vertex 1.27773297 -2.35605001 0.331256986 + vertex 1.45729196 -2.92058492 -1.12917101 + vertex 1.22280204 -2.30102396 0.275409013 + endloop + endfacet + + facet normal -0.917797387 -0.34065178 0.203970864 + outer loop + vertex 1.45729196 -2.92058492 -1.12917101 + vertex 1.47510099 -3.19243002 -1.50304699 + vertex 1.43024898 -2.88664603 -1.19417298 + endloop + endfacet + + facet normal -0.865921021 -0.42326504 0.26651001 + outer loop + vertex 1.45729196 -2.92058492 -1.12917101 + vertex 1.48610401 -3.20080709 -1.48060107 + vertex 1.47510099 -3.19243002 -1.50304699 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04417706 0.405290008 -0.393858999 + vertex -2.04417706 0.405290008 -0.393858999 + vertex -2.04417706 0.405290008 -0.393858999 + endloop + endfacet + + facet normal -0.891946077 0.305186033 -0.333607048 + outer loop + vertex -1.82387102 0.262814999 0.436648995 + vertex -1.88754892 0.152713999 0.506179988 + vertex -1.82589507 0.173241004 0.360118985 + endloop + endfacet + + facet normal -0.885285795 -0.462163895 0.0517069884 + outer loop + vertex -1.96445894 0.327820987 0.278562993 + vertex -1.92826998 0.255225986 0.249314994 + vertex -2.04417706 0.405290008 -0.393858999 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04417706 0.405290008 -0.393858999 + vertex -1.96445894 0.327820987 0.278562993 + vertex -2.04417706 0.405290008 -0.393858999 + endloop + endfacet + + facet normal -0.782588899 -0.524955928 0.334627926 + outer loop + vertex -1.96445894 0.327820987 0.278562993 + vertex -1.82589507 0.173241004 0.360118985 + vertex -1.92826998 0.255225986 0.249314994 + endloop + endfacet + + facet normal -0.758137107 -0.413600057 0.504146039 + outer loop + vertex -1.96445894 0.327820987 0.278562993 + vertex -1.82387102 0.262814999 0.436648995 + vertex -1.82589507 0.173241004 0.360118985 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04417706 0.405290008 -0.393858999 + vertex -2.04417706 0.405290008 -0.393858999 + vertex -2.04417706 0.405290008 -0.393858999 + endloop + endfacet + + facet normal -0.891943216 0.305170089 -0.333629102 + outer loop + vertex -1.86979997 0.26947999 0.565533996 + vertex -1.88754892 0.152713999 0.506179988 + vertex -1.82387102 0.262814999 0.436648995 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04417706 0.405290008 -0.393858999 + vertex -1.96445894 0.327820987 0.278562993 + vertex -2.04417706 0.405290008 -0.393858999 + endloop + endfacet + + facet normal -0.239093035 -0.967433095 -0.0831130072 + outer loop + vertex -2.04417706 0.405290008 -0.393858999 + vertex -2.0592401 0.350344002 0.289045006 + vertex -1.96445894 0.327820987 0.278562993 + endloop + endfacet + + facet normal -0.347355902 -0.934693694 -0.0754429772 + outer loop + vertex -1.86979997 0.26947999 0.565533996 + vertex -1.82387102 0.262814999 0.436648995 + vertex -1.96445894 0.327820987 0.278562993 + endloop + endfacet + + facet normal -0.241774052 -0.963362157 -0.116098031 + outer loop + vertex -2.0592401 0.350344002 0.289045006 + vertex -1.86979997 0.26947999 0.565533996 + vertex -1.96445894 0.327820987 0.278562993 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04417706 0.405290008 -0.393858999 + vertex -2.04417706 0.405290008 -0.393858999 + vertex -2.04417706 0.405290008 -0.393858999 + endloop + endfacet + + facet normal -0.891948104 0.305166036 -0.333620042 + outer loop + vertex -1.929093 0.188217998 0.649725974 + vertex -1.88754892 0.152713999 0.506179988 + vertex -1.86979997 0.26947999 0.565533996 + endloop + endfacet + + facet normal 0.485254169 -0.872347355 -0.059485022 + outer loop + vertex -2.14123702 0.305835992 0.272870004 + vertex -2.0592401 0.350344002 0.289045006 + vertex -2.04417706 0.405290008 -0.393858999 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04417706 0.405290008 -0.393858999 + vertex -2.14123702 0.305835992 0.272870004 + vertex -2.04417706 0.405290008 -0.393858999 + endloop + endfacet + + facet normal 0.482345223 -0.694664299 -0.533652186 + outer loop + vertex -2.14123702 0.305835992 0.272870004 + vertex -1.86979997 0.26947999 0.565533996 + vertex -2.0592401 0.350344002 0.289045006 + endloop + endfacet + + facet normal 0.403894991 -0.783667028 -0.471947998 + outer loop + vertex -2.14123702 0.305835992 0.272870004 + vertex -1.929093 0.188217998 0.649725974 + vertex -1.86979997 0.26947999 0.565533996 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04417706 0.405290008 -0.393858999 + vertex -2.04417706 0.405290008 -0.393858999 + vertex -2.04417706 0.405290008 -0.393858999 + endloop + endfacet + + facet normal -0.891944408 0.305174798 -0.3336218 + outer loop + vertex -1.95710492 0.080219999 0.625826001 + vertex -1.88754892 0.152713999 0.506179988 + vertex -1.929093 0.188217998 0.649725974 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04417706 0.405290008 -0.393858999 + vertex -2.14123702 0.305835992 0.272870004 + vertex -2.04417706 0.405290008 -0.393858999 + endloop + endfacet + + facet normal 0.98234576 -0.14196597 0.121829979 + outer loop + vertex -2.04417706 0.405290008 -0.393858999 + vertex -2.14871097 0.227810994 0.242217004 + vertex -2.14123702 0.305835992 0.272870004 + endloop + endfacet + + facet normal 0.851677775 -0.107379965 -0.512946844 + outer loop + vertex -1.95710492 0.080219999 0.625826001 + vertex -1.929093 0.188217998 0.649725974 + vertex -2.14123702 0.305835992 0.272870004 + endloop + endfacet + + facet normal 0.903621852 0.0788339823 -0.421013921 + outer loop + vertex -2.14871097 0.227810994 0.242217004 + vertex -1.95710492 0.080219999 0.625826001 + vertex -2.14123702 0.305835992 0.272870004 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04417706 0.405290008 -0.393858999 + vertex -2.04417706 0.405290008 -0.393858999 + vertex -2.04417706 0.405290008 -0.393858999 + endloop + endfacet + + facet normal -0.891943157 0.30518806 -0.333613068 + outer loop + vertex -1.93274093 0.026811 0.511829019 + vertex -1.88754892 0.152713999 0.506179988 + vertex -1.95710492 0.080219999 0.625826001 + endloop + endfacet + + facet normal 0.618495345 0.72466743 0.303843141 + outer loop + vertex -2.07603097 0.175025001 0.220166996 + vertex -2.14871097 0.227810994 0.242217004 + vertex -2.04417706 0.405290008 -0.393858999 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04417706 0.405290008 -0.393858999 + vertex -2.07603097 0.175025001 0.220166996 + vertex -2.04417706 0.405290008 -0.393858999 + endloop + endfacet + + facet normal 0.590632141 0.806794107 0.0153980022 + outer loop + vertex -2.07603097 0.175025001 0.220166996 + vertex -1.95710492 0.080219999 0.625826001 + vertex -2.14871097 0.227810994 0.242217004 + endloop + endfacet + + facet normal 0.806724787 0.582333744 -0.100411966 + outer loop + vertex -2.07603097 0.175025001 0.220166996 + vertex -1.93274093 0.026811 0.511829019 + vertex -1.95710492 0.080219999 0.625826001 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04417706 0.405290008 -0.393858999 + vertex -2.04417706 0.405290008 -0.393858999 + vertex -2.04417706 0.405290008 -0.393858999 + endloop + endfacet + + facet normal -0.891946197 0.305190057 -0.333603054 + outer loop + vertex -1.874349 0.0682099983 0.393579006 + vertex -1.88754892 0.152713999 0.506179988 + vertex -1.93274093 0.026811 0.511829019 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04417706 0.405290008 -0.393858999 + vertex -2.07603097 0.175025001 0.220166996 + vertex -2.04417706 0.405290008 -0.393858999 + endloop + endfacet + + facet normal -0.126801014 0.93091315 0.342523098 + outer loop + vertex -2.04417706 0.405290008 -0.393858999 + vertex -1.97792399 0.187224999 0.223326996 + vertex -2.07603097 0.175025001 0.220166996 + endloop + endfacet + + facet normal 0.14587602 0.909062088 0.390289992 + outer loop + vertex -1.874349 0.0682099983 0.393579006 + vertex -1.93274093 0.026811 0.511829019 + vertex -2.07603097 0.175025001 0.220166996 + endloop + endfacet + + facet normal -0.116740949 0.779241621 0.615754724 + outer loop + vertex -1.97792399 0.187224999 0.223326996 + vertex -1.874349 0.0682099983 0.393579006 + vertex -2.07603097 0.175025001 0.220166996 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04417706 0.405290008 -0.393858999 + vertex -2.04417706 0.405290008 -0.393858999 + vertex -2.04417706 0.405290008 -0.393858999 + endloop + endfacet + + facet normal -0.89194411 0.30519402 -0.333605021 + outer loop + vertex -1.82589507 0.173241004 0.360118985 + vertex -1.88754892 0.152713999 0.506179988 + vertex -1.874349 0.0682099983 0.393579006 + endloop + endfacet + + facet normal -0.824144065 0.500415027 0.265276015 + outer loop + vertex -1.92826998 0.255225986 0.249314994 + vertex -1.97792399 0.187224999 0.223326996 + vertex -2.04417706 0.405290008 -0.393858999 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04417706 0.405290008 -0.393858999 + vertex -1.92826998 0.255225986 0.249314994 + vertex -2.04417706 0.405290008 -0.393858999 + endloop + endfacet + + facet normal -0.719097137 0.282446057 0.63492012 + outer loop + vertex -1.92826998 0.255225986 0.249314994 + vertex -1.874349 0.0682099983 0.393579006 + vertex -1.97792399 0.187224999 0.223326996 + endloop + endfacet + + facet normal -0.460380882 0.455178916 0.762142777 + outer loop + vertex -1.92826998 0.255225986 0.249314994 + vertex -1.82589507 0.173241004 0.360118985 + vertex -1.874349 0.0682099983 0.393579006 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 0.828594983 0.146647006 + vertex -2.04218507 0.828594983 0.146647006 + vertex -2.04218507 0.828594983 0.146647006 + endloop + endfacet + + facet normal -0.417208999 0.680008054 -0.602931023 + outer loop + vertex -1.57823491 0.290569007 0.523093998 + vertex -1.56890297 0.382055014 0.619817972 + vertex -1.69004607 0.288253009 0.597850978 + endloop + endfacet + + facet normal 0.390951902 0.768584847 0.506392896 + outer loop + vertex -1.67776895 0.494724005 0.372043014 + vertex -1.75114 0.490709007 0.434781998 + vertex -2.04218507 0.828594983 0.146647006 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 0.828594983 0.146647006 + vertex -1.67776895 0.494724005 0.372043014 + vertex -2.04218507 0.828594983 0.146647006 + endloop + endfacet + + facet normal 0.482774973 0.633068919 0.605104983 + outer loop + vertex -1.67776895 0.494724005 0.372043014 + vertex -1.69004607 0.288253009 0.597850978 + vertex -1.75114 0.490709007 0.434781998 + endloop + endfacet + + facet normal 0.40646106 0.663174093 0.628482044 + outer loop + vertex -1.67776895 0.494724005 0.372043014 + vertex -1.57823491 0.290569007 0.523093998 + vertex -1.69004607 0.288253009 0.597850978 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 0.828594983 0.146647006 + vertex -2.04218507 0.828594983 0.146647006 + vertex -2.04218507 0.828594983 0.146647006 + endloop + endfacet + + facet normal -0.417207867 0.680008829 -0.602930844 + outer loop + vertex -1.45939696 0.361775994 0.521173 + vertex -1.56890297 0.382055014 0.619817972 + vertex -1.57823491 0.290569007 0.523093998 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 0.828594983 0.146647006 + vertex -1.67776895 0.494724005 0.372043014 + vertex -2.04218507 0.828594983 0.146647006 + endloop + endfacet + + facet normal -0.141779974 0.442905903 0.885286808 + outer loop + vertex -2.04218507 0.828594983 0.146647006 + vertex -1.61079192 0.552542984 0.353843004 + vertex -1.67776895 0.494724005 0.372043014 + endloop + endfacet + + facet normal -0.275092959 0.481556952 0.832121909 + outer loop + vertex -1.45939696 0.361775994 0.521173 + vertex -1.57823491 0.290569007 0.523093998 + vertex -1.67776895 0.494724005 0.372043014 + endloop + endfacet + + facet normal -0.234951034 0.528878033 0.815528095 + outer loop + vertex -1.61079192 0.552542984 0.353843004 + vertex -1.45939696 0.361775994 0.521173 + vertex -1.67776895 0.494724005 0.372043014 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 0.828594983 0.146647006 + vertex -2.04218507 0.828594983 0.146647006 + vertex -2.04218507 0.828594983 0.146647006 + endloop + endfacet + + facet normal -0.417209029 0.680007041 -0.602932036 + outer loop + vertex -1.42301798 0.448253989 0.593532026 + vertex -1.56890297 0.382055014 0.619817972 + vertex -1.45939696 0.361775994 0.521173 + endloop + endfacet + + facet normal -0.577504694 -0.348285824 0.738366604 + outer loop + vertex -1.60065103 0.620625973 0.39388901 + vertex -1.61079192 0.552542984 0.353843004 + vertex -2.04218507 0.828594983 0.146647006 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 0.828594983 0.146647006 + vertex -1.60065103 0.620625973 0.39388901 + vertex -2.04218507 0.828594983 0.146647006 + endloop + endfacet + + facet normal -0.825805068 -0.189439997 0.531185985 + outer loop + vertex -1.60065103 0.620625973 0.39388901 + vertex -1.45939696 0.361775994 0.521173 + vertex -1.61079192 0.552542984 0.353843004 + endloop + endfacet + + facet normal -0.799487293 -0.150301054 0.581575215 + outer loop + vertex -1.60065103 0.620625973 0.39388901 + vertex -1.42301798 0.448253989 0.593532026 + vertex -1.45939696 0.361775994 0.521173 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 0.828594983 0.146647006 + vertex -2.04218507 0.828594983 0.146647006 + vertex -2.04218507 0.828594983 0.146647006 + endloop + endfacet + + facet normal -0.417208076 0.680003166 -0.602937102 + outer loop + vertex -1.49649501 0.484885007 0.685687006 + vertex -1.56890297 0.382055014 0.619817972 + vertex -1.42301798 0.448253989 0.593532026 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 0.828594983 0.146647006 + vertex -1.60065103 0.620625973 0.39388901 + vertex -2.04218507 0.828594983 0.146647006 + endloop + endfacet + + facet normal -0.432327896 -0.901612759 0.0136729963 + outer loop + vertex -2.04218507 0.828594983 0.146647006 + vertex -1.65497303 0.647706985 0.462024987 + vertex -1.60065103 0.620625973 0.39388901 + endloop + endfacet + + facet normal -0.591237962 -0.791076005 -0.156959981 + outer loop + vertex -1.49649501 0.484885007 0.685687006 + vertex -1.42301798 0.448253989 0.593532026 + vertex -1.60065103 0.620625973 0.39388901 + endloop + endfacet + + facet normal -0.591241717 -0.791073561 -0.156957924 + outer loop + vertex -1.65497303 0.647706985 0.462024987 + vertex -1.49649501 0.484885007 0.685687006 + vertex -1.60065103 0.620625973 0.39388901 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 0.828594983 0.146647006 + vertex -2.04218507 0.828594983 0.146647006 + vertex -2.04218507 0.828594983 0.146647006 + endloop + endfacet + + facet normal -0.417217761 0.680004597 -0.602928638 + outer loop + vertex -1.62449408 0.444081008 0.728241026 + vertex -1.56890297 0.382055014 0.619817972 + vertex -1.49649501 0.484885007 0.685687006 + endloop + endfacet + + facet normal 0.0537490025 -0.836287022 -0.545651078 + outer loop + vertex -1.73286009 0.613394976 0.50694102 + vertex -1.65497303 0.647706985 0.462024987 + vertex -2.04218507 0.828594983 0.146647006 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 0.828594983 0.146647006 + vertex -1.73286009 0.613394976 0.50694102 + vertex -2.04218507 0.828594983 0.146647006 + endloop + endfacet + + facet normal 0.0118340012 -0.804398 -0.593973041 + outer loop + vertex -1.73286009 0.613394976 0.50694102 + vertex -1.49649501 0.484885007 0.685687006 + vertex -1.65497303 0.647706985 0.462024987 + endloop + endfacet + + facet normal 0.043387007 -0.783097148 -0.620384037 + outer loop + vertex -1.73286009 0.613394976 0.50694102 + vertex -1.62449408 0.444081008 0.728241026 + vertex -1.49649501 0.484885007 0.685687006 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 0.828594983 0.146647006 + vertex -2.04218507 0.828594983 0.146647006 + vertex -2.04218507 0.828594983 0.146647006 + endloop + endfacet + + facet normal -0.417210042 0.680010021 -0.602928042 + outer loop + vertex -1.71063101 0.356572986 0.689149976 + vertex -1.56890297 0.382055014 0.619817972 + vertex -1.62449408 0.444081008 0.728241026 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 0.828594983 0.146647006 + vertex -1.73286009 0.613394976 0.50694102 + vertex -2.04218507 0.828594983 0.146647006 + endloop + endfacet + + facet normal 0.644525826 -0.270723939 -0.71504885 + outer loop + vertex -2.04218507 0.828594983 0.146647006 + vertex -1.77565801 0.543522 0.494818002 + vertex -1.73286009 0.613394976 0.50694102 + endloop + endfacet + + facet normal 0.676752985 -0.386139005 -0.626819015 + outer loop + vertex -1.71063101 0.356572986 0.689149976 + vertex -1.62449408 0.444081008 0.728241026 + vertex -1.73286009 0.613394976 0.50694102 + endloop + endfacet + + facet normal 0.734697938 -0.349096984 -0.581678927 + outer loop + vertex -1.77565801 0.543522 0.494818002 + vertex -1.71063101 0.356572986 0.689149976 + vertex -1.73286009 0.613394976 0.50694102 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 0.828594983 0.146647006 + vertex -2.04218507 0.828594983 0.146647006 + vertex -2.04218507 0.828594983 0.146647006 + endloop + endfacet + + facet normal -0.417209774 0.680009604 -0.602928638 + outer loop + vertex -1.69004607 0.288253009 0.597850978 + vertex -1.56890297 0.382055014 0.619817972 + vertex -1.71063101 0.356572986 0.689149976 + endloop + endfacet + + facet normal 0.811764777 0.561054826 -0.162034959 + outer loop + vertex -1.75114 0.490709007 0.434781998 + vertex -1.77565801 0.543522 0.494818002 + vertex -2.04218507 0.828594983 0.146647006 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 0.828594983 0.146647006 + vertex -1.75114 0.490709007 0.434781998 + vertex -2.04218507 0.828594983 0.146647006 + endloop + endfacet + + facet normal 0.926234186 0.373678088 0.0495470129 + outer loop + vertex -1.75114 0.490709007 0.434781998 + vertex -1.71063101 0.356572986 0.689149976 + vertex -1.77565801 0.543522 0.494818002 + endloop + endfacet + + facet normal 0.957405746 0.28874594 -0.000203999953 + outer loop + vertex -1.75114 0.490709007 0.434781998 + vertex -1.69004607 0.288253009 0.597850978 + vertex -1.71063101 0.356572986 0.689149976 + endloop + endfacet + + facet normal -0.188931033 -0.0582350083 -0.980262101 + outer loop + vertex -1.06898999 0.00229800004 1.10110295 + vertex -1.10224402 0.110184997 1.10110295 + vertex -1.51577306 0.00229800004 1.18721402 + endloop + endfacet + + facet normal -0.184715971 -0.0741749927 -0.979988813 + outer loop + vertex -1.10224402 0.110184997 1.10110295 + vertex -1.21148205 0.382218003 1.10110295 + vertex -1.51577306 0.00229800004 1.18721402 + endloop + endfacet + + facet normal -0.0691840202 -0.258199126 0.963611364 + outer loop + vertex -1.51577306 0.250510991 0.26087001 + vertex -1.39166701 0.217256993 0.26087001 + vertex -1.51577306 0.00229800004 0.194362 + endloop + endfacet + + facet normal 0.0497390032 -0.258499026 -0.964730144 + outer loop + vertex -1.21148205 0.382218003 1.10110295 + vertex -1.51577306 0.250510991 1.12070501 + vertex -1.51577306 0.00229800004 1.18721402 + endloop + endfacet + + facet normal 0.86519593 -0.129780978 0.484347939 + outer loop + vertex -1.59023798 0.217256993 0.384977013 + vertex -1.51577306 0.250510991 0.26087001 + vertex -1.51577306 0.00229800004 0.194362 + endloop + endfacet + + facet normal 0.0691840202 -0.258199126 -0.963611364 + outer loop + vertex -1.51577306 0.250510991 1.12070501 + vertex -1.63987899 0.217256993 1.12070501 + vertex -1.51577306 0.00229800004 1.18721402 + endloop + endfacet + + facet normal 0.202385992 -0.60955894 0.766471028 + outer loop + vertex -1.68109 0.126405001 0.336712986 + vertex -1.59023798 0.217256993 0.384977013 + vertex -1.51577306 0.00229800004 0.194362 + endloop + endfacet + + facet normal 0.189015046 -0.189015046 -0.963611245 + outer loop + vertex -1.63987899 0.217256993 1.12070501 + vertex -1.73073196 0.126405001 1.12070501 + vertex -1.51577306 0.00229800004 1.18721402 + endloop + endfacet + + facet normal 0.57565999 -0.154247016 0.803008974 + outer loop + vertex -1.71434402 0.00229800004 0.336712986 + vertex -1.68109 0.126405001 0.336712986 + vertex -1.51577306 0.00229800004 0.194362 + endloop + endfacet + + facet normal 0.258199096 -0.069185026 -0.963611245 + outer loop + vertex -1.73073196 0.126405001 1.12070501 + vertex -1.76398599 0.00229800004 1.12070501 + vertex -1.51577306 0.00229800004 1.18721402 + endloop + endfacet + + facet normal 0.202413991 -0.788102925 0.581310928 + outer loop + vertex -1.39166701 0.217256993 0.26087001 + vertex -1.30081499 0.37461701 0.442575008 + vertex -1.16183996 0.410311013 0.442575008 + endloop + endfacet + + facet normal -0.0857829452 -0.731517494 0.676404655 + outer loop + vertex -1.51577306 0.489080012 0.539102018 + vertex -1.30081499 0.37461701 0.442575008 + vertex -1.39166701 0.217256993 0.26087001 + endloop + endfacet + + facet normal -0.199330047 -0.743908167 0.637862206 + outer loop + vertex -1.51577306 0.250510991 0.26087001 + vertex -1.51577306 0.489080012 0.539102018 + vertex -1.39166701 0.217256993 0.26087001 + endloop + endfacet + + facet normal 0.818344295 -0.436300159 0.374105155 + outer loop + vertex -1.59023798 0.217256993 0.384977013 + vertex -1.51577306 0.489080012 0.539102018 + vertex -1.51577306 0.250510991 0.26087001 + endloop + endfacet + + facet normal -0.151083976 -0.455930918 0.877097785 + outer loop + vertex -1.59023798 0.217256993 0.384977013 + vertex -1.73073196 0.37461701 0.442575008 + vertex -1.51577306 0.489080012 0.539102018 + endloop + endfacet + + facet normal 0.186527073 -0.186527073 0.964580357 + outer loop + vertex -1.88809204 0.217256993 0.442575008 + vertex -1.73073196 0.37461701 0.442575008 + vertex -1.59023798 0.217256993 0.384977013 + endloop + endfacet + + facet normal 0.154704109 -0.579695344 0.800012469 + outer loop + vertex -1.68109 0.126405001 0.336712986 + vertex -1.88809204 0.217256993 0.442575008 + vertex -1.59023798 0.217256993 0.384977013 + endloop + endfacet + + facet normal 0.413534015 -0.110806011 0.903721035 + outer loop + vertex -1.71434402 0.00229800004 0.336712986 + vertex -1.88809204 0.217256993 0.442575008 + vertex -1.68109 0.126405001 0.336712986 + endloop + endfacet + + facet normal 0.30303511 -0.212556094 0.928972363 + outer loop + vertex -1.71434402 0.00229800004 0.336712986 + vertex -1.93741703 0.00229800004 0.409480006 + vertex -1.88809204 0.217256993 0.442575008 + endloop + endfacet + + facet normal -0.839146435 -0.156273901 0.520971715 + outer loop + vertex -1.16183996 0.410311013 0.442575008 + vertex -0.820776999 0.00229800004 0.869545996 + vertex -1.08585596 0.00229800004 0.442575008 + endloop + endfacet + + facet normal -0.844083071 -0.17527701 0.506756067 + outer loop + vertex -1.16183996 0.410311013 0.442575008 + vertex -0.905672014 0.411127001 0.869545996 + vertex -0.820776999 0.00229800004 0.869545996 + endloop + endfacet + + facet normal -0.610179067 -0.701910973 0.367426991 + outer loop + vertex -1.087376 0.569083989 0.869545996 + vertex -0.905672014 0.411127001 0.869545996 + vertex -1.16183996 0.410311013 0.442575008 + endloop + endfacet + + facet normal 0.237148076 -0.923342288 0.301993102 + outer loop + vertex -1.30081499 0.37461701 0.442575008 + vertex -1.087376 0.569083989 0.869545996 + vertex -1.16183996 0.410311013 0.442575008 + endloop + endfacet + + facet normal -0.22484003 -0.839457035 0.494731098 + outer loop + vertex -1.51577306 0.489080012 0.539102018 + vertex -1.087376 0.569083989 0.869545996 + vertex -1.30081499 0.37461701 0.442575008 + endloop + endfacet + + facet normal 0.136153921 -0.988691449 0.0628579706 + outer loop + vertex -1.51577306 0.489080012 0.539102018 + vertex -1.51577306 0.498724014 0.690787971 + vertex -1.087376 0.569083989 0.869545996 + endloop + endfacet + + facet normal 0.379295141 -0.923411429 0.0587080233 + outer loop + vertex -1.82231998 0.370554 0.655328989 + vertex -1.51577306 0.498724014 0.690787971 + vertex -1.51577306 0.489080012 0.539102018 + endloop + endfacet + + facet normal 0.40773806 -0.899263144 0.158352017 + outer loop + vertex -1.73073196 0.37461701 0.442575008 + vertex -1.82231998 0.370554 0.655328989 + vertex -1.51577306 0.489080012 0.539102018 + endloop + endfacet + + facet normal 0.678963184 -0.678963184 0.279317051 + outer loop + vertex -1.88809204 0.217256993 0.442575008 + vertex -1.82231998 0.370554 0.655328989 + vertex -1.73073196 0.37461701 0.442575008 + endloop + endfacet + + facet normal 0.852383077 -0.512161076 0.105518013 + outer loop + vertex -1.88809204 0.217256993 0.442575008 + vertex -1.95466805 0.150288999 0.655328989 + vertex -1.82231998 0.370554 0.655328989 + endloop + endfacet + + facet normal 0.871156156 -0.475356132 0.122977026 + outer loop + vertex -2.02874589 0.00229800004 0.608051002 + vertex -1.95466805 0.150288999 0.655328989 + vertex -1.88809204 0.217256993 0.442575008 + endloop + endfacet + + facet normal 0.876484156 -0.263182074 0.403126091 + outer loop + vertex -1.93741703 0.00229800004 0.409480006 + vertex -2.02874589 0.00229800004 0.608051002 + vertex -1.88809204 0.217256993 0.442575008 + endloop + endfacet + + facet normal -0.881306171 -0.176261023 -0.438442081 + outer loop + vertex -0.963268995 0.382218003 1.00323403 + vertex -0.887284994 0.00229800004 1.00323403 + vertex -0.820776999 0.00229800004 0.869545996 + endloop + endfacet + + facet normal -0.887549043 -0.184303015 -0.422242999 + outer loop + vertex -0.905672014 0.411127001 0.869545996 + vertex -0.963268995 0.382218003 1.00323403 + vertex -0.820776999 0.00229800004 0.869545996 + endloop + endfacet + + facet normal -0.599210024 -0.689293087 -0.407213032 + outer loop + vertex -1.087376 0.569083989 0.869545996 + vertex -0.963268995 0.382218003 1.00323403 + vertex -0.905672014 0.411127001 0.869545996 + endloop + endfacet + + facet normal -0.599209309 -0.689293444 -0.407213241 + outer loop + vertex -1.087376 0.569083989 0.869545996 + vertex -1.12063003 0.519014001 1.00323403 + vertex -0.963268995 0.382218003 1.00323403 + endloop + endfacet + + facet normal 0.249579027 -0.925593197 -0.284583032 + outer loop + vertex -1.51577306 0.432215005 0.939001024 + vertex -1.12063003 0.519014001 1.00323403 + vertex -1.087376 0.569083989 0.869545996 + endloop + endfacet + + facet normal 0.257640004 -0.933317065 -0.250081986 + outer loop + vertex -1.51577306 0.498724014 0.690787971 + vertex -1.51577306 0.432215005 0.939001024 + vertex -1.087376 0.569083989 0.869545996 + endloop + endfacet + + facet normal 0.397967875 -0.88613981 -0.23743996 + outer loop + vertex -1.82231998 0.370554 0.655328989 + vertex -1.51577306 0.432215005 0.939001024 + vertex -1.51577306 0.498724014 0.690787971 + endloop + endfacet + + facet normal 0.404057056 -0.881292224 -0.245075062 + outer loop + vertex -1.82231998 0.370554 0.655328989 + vertex -1.77611899 0.322712004 0.903541982 + vertex -1.51577306 0.432215005 0.939001024 + endloop + endfacet + + facet normal 0.894504726 -0.365525872 -0.25739491 + outer loop + vertex -2.02874589 0.00229800004 0.608051002 + vertex -1.89073491 0.131956995 0.903541982 + vertex -1.95466805 0.150288999 0.655328989 + endloop + endfacet + + facet normal 0.875202715 -0.431018859 -0.219642922 + outer loop + vertex -2.02874589 0.00229800004 0.608051002 + vertex -1.94569099 0.00229800004 0.939001024 + vertex -1.89073491 0.131956995 0.903541982 + endloop + endfacet + + facet normal -0.472085088 -0.0944170132 -0.876482189 + outer loop + vertex -0.963268995 0.382218003 1.00323403 + vertex -1.06898999 0.00229800004 1.10110295 + vertex -0.887284994 0.00229800004 1.00323403 + endloop + endfacet + + facet normal -0.399085015 -0.123011015 -0.908625066 + outer loop + vertex -0.963268995 0.382218003 1.00323403 + vertex -1.10224402 0.110184997 1.10110295 + vertex -1.06898999 0.00229800004 1.10110295 + endloop + endfacet + + facet normal -0.362892926 -0.145723984 -0.92036581 + outer loop + vertex -1.21148205 0.382218003 1.10110295 + vertex -1.10224402 0.110184997 1.10110295 + vertex -0.963268995 0.382218003 1.00323403 + endloop + endfacet + + facet normal -0.337955117 -0.388762146 -0.857117355 + outer loop + vertex -1.12063003 0.519014001 1.00323403 + vertex -1.21148205 0.382218003 1.10110295 + vertex -0.963268995 0.382218003 1.00323403 + endloop + endfacet + + facet normal 0.260202944 -0.670137882 -0.695132792 + outer loop + vertex -1.51577306 0.432215005 0.939001024 + vertex -1.21148205 0.382218003 1.10110295 + vertex -1.12063003 0.519014001 1.00323403 + endloop + endfacet + + facet normal 0.25209403 -0.68426919 -0.68426919 + outer loop + vertex -1.51577306 0.432215005 0.939001024 + vertex -1.51577306 0.250510991 1.12070501 + vertex -1.21148205 0.382218003 1.10110295 + endloop + endfacet + + facet normal 0.186157033 -0.694746077 -0.694747031 + outer loop + vertex -1.63987899 0.217256993 1.12070501 + vertex -1.51577306 0.250510991 1.12070501 + vertex -1.51577306 0.432215005 0.939001024 + endloop + endfacet + + facet normal 0.380661011 -0.715228021 -0.586127996 + outer loop + vertex -1.77611899 0.322712004 0.903541982 + vertex -1.63987899 0.217256993 1.12070501 + vertex -1.51577306 0.432215005 0.939001024 + endloop + endfacet + + facet normal 0.673306763 -0.404560834 -0.618860781 + outer loop + vertex -1.89073491 0.131956995 0.903541982 + vertex -1.63987899 0.217256993 1.12070501 + vertex -1.77611899 0.322712004 0.903541982 + endloop + endfacet + + facet normal 0.622429729 -0.622429729 -0.474512786 + outer loop + vertex -1.89073491 0.131956995 0.903541982 + vertex -1.73073196 0.126405001 1.12070501 + vertex -1.63987899 0.217256993 1.12070501 + endloop + endfacet + + facet normal 0.784513175 -0.210212052 -0.583395123 + outer loop + vertex -1.76398599 0.00229800004 1.12070501 + vertex -1.73073196 0.126405001 1.12070501 + vertex -1.89073491 0.131956995 0.903541982 + endloop + endfacet + + facet normal 0.634200931 -0.442243934 -0.634199917 + outer loop + vertex -1.94569099 0.00229800004 0.939001024 + vertex -1.76398599 0.00229800004 1.12070501 + vertex -1.89073491 0.131956995 0.903541982 + endloop + endfacet + + facet normal -0.521937072 0.820092142 0.23458603 + outer loop + vertex -1.86046696 0.186704993 0.72092402 + vertex -1.861866 0.181822002 0.734879017 + vertex -1.90136802 0.159713998 0.72427702 + endloop + endfacet + + facet normal 0.561335981 0.0371459983 -0.826753974 + outer loop + vertex -1.86046696 0.186704993 0.72092402 + vertex -1.88625693 0.190698996 0.703593016 + vertex -1.95466805 0.150288999 0.655328989 + endloop + endfacet + + facet normal 0.511620045 -0.810681105 -0.284677029 + outer loop + vertex -1.95466805 0.150288999 0.655328989 + vertex -1.90136802 0.159713998 0.72427702 + vertex -1.86046696 0.186704993 0.72092402 + endloop + endfacet + + facet normal 0.675249338 -0.414224237 -0.610292375 + outer loop + vertex -1.95466805 0.150288999 0.655328989 + vertex -1.88625693 0.190698996 0.703593016 + vertex -1.862077 0.240274996 0.69669801 + endloop + endfacet + + facet normal 0.672121286 -0.413267165 -0.6143803 + outer loop + vertex -1.84992504 0.223467007 0.721297979 + vertex -1.862077 0.240274996 0.69669801 + vertex -1.88625693 0.190698996 0.703593016 + endloop + endfacet + + facet normal 0.536241055 -0.145309016 -0.831463099 + outer loop + vertex -1.88625693 0.190698996 0.703593016 + vertex -1.86046696 0.186704993 0.72092402 + vertex -1.84992504 0.223467007 0.721297979 + endloop + endfacet + + facet normal 0.895058095 -0.00553400069 -0.445915073 + outer loop + vertex -1.862077 0.240274996 0.69669801 + vertex -1.84992504 0.223467007 0.721297979 + vertex -1.83589005 0.272417009 0.748862982 + endloop + endfacet + + facet normal 0.914936006 -0.290188998 -0.280503988 + outer loop + vertex -1.83589005 0.272417009 0.748862982 + vertex -1.83185291 0.302246004 0.731172025 + vertex -1.862077 0.240274996 0.69669801 + endloop + endfacet + + facet normal 0.968834221 -0.246922046 -0.0197400041 + outer loop + vertex -1.82278597 0.320836991 0.786329985 + vertex -1.83589005 0.272417009 0.748862982 + vertex -1.82824898 0.299062014 0.790558994 + endloop + endfacet + + facet normal 0.977088094 -0.189592034 -0.0967150182 + outer loop + vertex -1.83589005 0.272417009 0.748862982 + vertex -1.82278597 0.320836991 0.786329985 + vertex -1.83185291 0.302246004 0.731172025 + endloop + endfacet + + facet normal 0.997900486 -0.059398029 0.0258140136 + outer loop + vertex -1.82883 0.30844301 0.834594011 + vertex -1.82824898 0.299062014 0.790558994 + vertex -1.82945108 0.294871002 0.827350974 + endloop + endfacet + + facet normal 0.970842361 -0.231529072 0.0621240176 + outer loop + vertex -1.82883 0.30844301 0.834594011 + vertex -1.82278597 0.320836991 0.786329985 + vertex -1.82824898 0.299062014 0.790558994 + endloop + endfacet + + facet normal 0.980895996 -0.124577008 0.149410993 + outer loop + vertex -1.82945108 0.294871002 0.827350974 + vertex -1.83534694 0.274305999 0.848914027 + vertex -1.82883 0.30844301 0.834594011 + endloop + endfacet + + facet normal 0.919196248 -0.0103330025 0.393664122 + outer loop + vertex -1.83534694 0.274305999 0.848914027 + vertex -1.84091997 0.283654988 0.862173021 + vertex -1.82883 0.30844301 0.834594011 + endloop + endfacet + + facet normal 0.793512285 -0.287576079 0.536319196 + outer loop + vertex -1.83534694 0.274305999 0.848914027 + vertex -1.859056 0.246472001 0.869068027 + vertex -1.84091997 0.283654988 0.862173021 + endloop + endfacet + + facet normal 0.804793894 -0.326704979 0.495550931 + outer loop + vertex -1.859056 0.246472001 0.869068027 + vertex -1.83534694 0.274305999 0.848914027 + vertex -1.84234202 0.249915004 0.844193995 + endloop + endfacet + + facet normal 0.715217113 -0.572144091 0.401392102 + outer loop + vertex -1.84234202 0.249915004 0.844193995 + vertex -1.87719107 0.209290996 0.848384023 + vertex -1.859056 0.246472001 0.869068027 + endloop + endfacet + + facet normal 0.653585851 -0.502279878 0.566162825 + outer loop + vertex -1.87719107 0.209290996 0.848384023 + vertex -1.84234202 0.249915004 0.844193995 + vertex -1.84963393 0.224481001 0.830048978 + endloop + endfacet + + facet normal 0.553774893 -0.8182289 0.154384971 + outer loop + vertex -1.87719107 0.209290996 0.848384023 + vertex -1.84963393 0.224481001 0.830048978 + vertex -1.851632 0.217520997 0.800320983 + endloop + endfacet + + facet normal 0.600953341 -0.777222455 0.18649511 + outer loop + vertex -1.851632 0.217520997 0.800320983 + vertex -1.88625693 0.190698996 0.800119996 + vertex -1.87719107 0.209290996 0.848384023 + endloop + endfacet + + facet normal 0.597519338 -0.772989392 0.213209108 + outer loop + vertex -1.88625693 0.190698996 0.800119996 + vertex -1.851632 0.217520997 0.800320983 + vertex -1.85507607 0.205509007 0.766424 + endloop + endfacet + + facet normal 0.664247036 -0.676876068 0.317198038 + outer loop + vertex -1.85507607 0.205509007 0.766424 + vertex -1.89834702 0.165911004 0.772539973 + vertex -1.88625693 0.190698996 0.800119996 + endloop + endfacet + + facet normal 0.657633066 -0.663550079 0.356679052 + outer loop + vertex -1.89834702 0.165911004 0.772539973 + vertex -1.85507607 0.205509007 0.766424 + vertex -1.861866 0.181822002 0.734879017 + endloop + endfacet + + facet normal 0.469525933 -0.87896502 0.0834619924 + outer loop + vertex -1.861866 0.181822002 0.734879017 + vertex -1.90136802 0.159713998 0.72427702 + vertex -1.89834702 0.165911004 0.772539973 + endloop + endfacet + + facet normal 0.373062909 -0.913278759 -0.163541958 + outer loop + vertex -1.95466805 0.150288999 0.655328989 + vertex -1.89073491 0.131956995 0.903541982 + vertex -1.90136802 0.159713998 0.72427702 + endloop + endfacet + + facet normal 0.998070359 -0.0116050038 -0.0609980263 + outer loop + vertex -1.89073491 0.131956995 0.903541982 + vertex -1.89834702 0.165911004 0.772539973 + vertex -1.90136802 0.159713998 0.72427702 + endloop + endfacet + + facet normal 0.941413999 -0.30913201 -0.13482298 + outer loop + vertex -1.89073491 0.131956995 0.903541982 + vertex -1.88625693 0.190698996 0.800119996 + vertex -1.89834702 0.165911004 0.772539973 + endloop + endfacet + + facet normal 0.967765391 -0.234683871 -0.0913969502 + outer loop + vertex -1.89073491 0.131956995 0.903541982 + vertex -1.87719107 0.209290996 0.848384023 + vertex -1.88625693 0.190698996 0.800119996 + endloop + endfacet + + facet normal 0.919147134 -0.322406024 -0.22632502 + outer loop + vertex -1.89073491 0.131956995 0.903541982 + vertex -1.859056 0.246472001 0.869068027 + vertex -1.87719107 0.209290996 0.848384023 + endloop + endfacet + + facet normal 0.629824102 -0.378435016 -0.678313076 + outer loop + vertex -1.859056 0.246472001 0.869068027 + vertex -1.89073491 0.131956995 0.903541982 + vertex -1.77611899 0.322712004 0.903541982 + endloop + endfacet + + facet normal 0.655910254 -0.434390157 -0.617322266 + outer loop + vertex -1.77611899 0.322712004 0.903541982 + vertex -1.84091997 0.283654988 0.862173021 + vertex -1.859056 0.246472001 0.869068027 + endloop + endfacet + + facet normal 0.635727286 -0.691575289 -0.342891186 + outer loop + vertex -1.77611899 0.322712004 0.903541982 + vertex -1.82883 0.30844301 0.834594011 + vertex -1.84091997 0.283654988 0.862173021 + endloop + endfacet + + facet normal 0.669934034 -0.402535051 -0.623822033 + outer loop + vertex -1.82231998 0.370554 0.655328989 + vertex -1.95466805 0.150288999 0.655328989 + vertex -1.862077 0.240274996 0.69669801 + endloop + endfacet + + facet normal 0.919927955 -0.341761976 -0.192174986 + outer loop + vertex -1.82231998 0.370554 0.655328989 + vertex -1.862077 0.240274996 0.69669801 + vertex -1.83185291 0.302246004 0.731172025 + endloop + endfacet + + facet normal 0.970269501 -0.227404878 -0.082849957 + outer loop + vertex -1.82231998 0.370554 0.655328989 + vertex -1.83185291 0.302246004 0.731172025 + vertex -1.82278597 0.320836991 0.786329985 + endloop + endfacet + + facet normal 0.549644947 -0.795333922 -0.255605966 + outer loop + vertex -1.82883 0.30844301 0.834594011 + vertex -1.77611899 0.322712004 0.903541982 + vertex -1.82231998 0.370554 0.655328989 + endloop + endfacet + + facet normal -0.685612202 -0.679787159 -0.260433048 + outer loop + vertex -1.82231998 0.370554 0.655328989 + vertex -1.82883 0.30844301 0.834594011 + vertex -1.82278597 0.320836991 0.786329985 + endloop + endfacet + + facet normal 0.0977169722 0.666159749 0.739379823 + outer loop + vertex 0.0900690034 -0.167228997 -0.494951993 + vertex -0.233263001 -0.437810004 -0.208434999 + vertex -0.584930003 -0.284505993 -0.300080001 + endloop + endfacet + + facet normal -0.169718057 0.339002132 0.925350368 + outer loop + vertex 0.460438013 -0.389086008 -0.345746011 + vertex 0.0900690034 -0.167228997 -0.494951993 + vertex 0.457304001 0 -0.488862008 + endloop + endfacet + + facet normal 0.0998720229 0.664684117 0.740419149 + outer loop + vertex 0.460438013 -0.389086008 -0.345746011 + vertex -0.233263001 -0.437810004 -0.208434999 + vertex 0.0900690034 -0.167228997 -0.494951993 + endloop + endfacet + + facet normal 0.220944032 0.28046003 0.934091032 + outer loop + vertex -0.610705018 0 -0.379406005 + vertex 0.0900690034 -0.167228997 -0.494951993 + vertex -0.584930003 -0.284505993 -0.300080001 + endloop + endfacet + + facet normal 0.220944032 -0.28046003 0.934091032 + outer loop + vertex 0.0900690034 0.167228997 -0.494951993 + vertex -0.610705018 0 -0.379406005 + vertex -0.584930003 0.284505993 -0.300080001 + endloop + endfacet + + facet normal -0.0165820085 0 0.999862492 + outer loop + vertex 0.0900690034 -0.167228997 -0.494951993 + vertex 0.0900690034 0.167228997 -0.494951993 + vertex 0.457304001 0 -0.488862008 + endloop + endfacet + + facet normal 0.162686959 0 0.986677706 + outer loop + vertex 0.0900690034 0.167228997 -0.494951993 + vertex 0.0900690034 -0.167228997 -0.494951993 + vertex -0.610705018 0 -0.379406005 + endloop + endfacet + + facet normal -0.190478116 0.247609138 0.94995153 + outer loop + vertex -1.14137506 -0.292008013 -0.409700006 + vertex -0.610705018 0 -0.379406005 + vertex -0.584930003 -0.284505993 -0.300080001 + endloop + endfacet + + facet normal 0.699706972 0 0.714430034 + outer loop + vertex -1.14137506 0.292008013 -0.409700006 + vertex -1.14137506 -0.292008013 -0.409700006 + vertex -1.41845703 0 -0.138327003 + endloop + endfacet + + facet normal -0.190478116 -0.247609138 0.94995153 + outer loop + vertex -0.610705018 0 -0.379406005 + vertex -1.14137506 0.292008013 -0.409700006 + vertex -0.584930003 0.284505993 -0.300080001 + endloop + endfacet + + facet normal -0.0569919795 0 0.998374581 + outer loop + vertex -1.14137506 0.292008013 -0.409700006 + vertex -0.610705018 0 -0.379406005 + vertex -1.14137506 -0.292008013 -0.409700006 + endloop + endfacet + + facet normal 0.802316129 -0.263622016 0.535530031 + outer loop + vertex -1.33845997 0.472478002 -0.0255929995 + vertex -1.14137506 0.292008013 -0.409700006 + vertex -1.41845703 0 -0.138327003 + endloop + endfacet + + facet normal -0.193044886 -0.149941906 0.969665408 + outer loop + vertex -1.14137506 0.292008013 -0.409700006 + vertex -0.903297007 0.764486015 -0.289241999 + vertex -0.584930003 0.284505993 -0.300080001 + endloop + endfacet + + facet normal 0.667306721 -0.481181771 0.568476737 + outer loop + vertex -0.903297007 0.764486015 -0.289241999 + vertex -1.14137506 0.292008013 -0.409700006 + vertex -1.33845997 0.472478002 -0.0255929995 + endloop + endfacet + + facet normal 0.0977169722 -0.666159749 0.739379823 + outer loop + vertex -0.233263001 0.437810004 -0.208434999 + vertex 0.0900690034 0.167228997 -0.494951993 + vertex -0.584930003 0.284505993 -0.300080001 + endloop + endfacet + + facet normal -0.169718057 -0.339002132 0.925350368 + outer loop + vertex 0.0900690034 0.167228997 -0.494951993 + vertex 0.460438013 0.389086008 -0.345746011 + vertex 0.457304001 0 -0.488862008 + endloop + endfacet + + facet normal 0.0998720229 -0.664684117 0.740419149 + outer loop + vertex 0.460438013 0.389086008 -0.345746011 + vertex 0.0900690034 0.167228997 -0.494951993 + vertex -0.233263001 0.437810004 -0.208434999 + endloop + endfacet + + facet normal -0.193044886 0.149941906 0.969665408 + outer loop + vertex -0.903297007 -0.764486015 -0.289241999 + vertex -1.14137506 -0.292008013 -0.409700006 + vertex -0.584930003 -0.284505993 -0.300080001 + endloop + endfacet + + facet normal 0.802316129 0.263622016 0.535530031 + outer loop + vertex -1.14137506 -0.292008013 -0.409700006 + vertex -1.33845997 -0.472478002 -0.0255929995 + vertex -1.41845703 0 -0.138327003 + endloop + endfacet + + facet normal 0.667307138 0.481182069 0.568476081 + outer loop + vertex -1.33845997 -0.472478002 -0.0255929995 + vertex -1.14137506 -0.292008013 -0.409700006 + vertex -0.903297007 -0.764486015 -0.289241999 + endloop + endfacet + + facet normal -0.188931033 0.0582350083 -0.980262101 + outer loop + vertex -1.51577306 0.00046000001 1.18721402 + vertex -1.10224402 -0.107427001 1.10110295 + vertex -1.06898999 0.00046000001 1.10110295 + endloop + endfacet + + facet normal -0.184715971 0.0741749927 -0.979988813 + outer loop + vertex -1.51577306 0.00046000001 1.18721402 + vertex -1.21148205 -0.379460007 1.10110295 + vertex -1.10224402 -0.107427001 1.10110295 + endloop + endfacet + + facet normal -0.0691840202 0.258199126 0.963611364 + outer loop + vertex -1.51577306 0.00046000001 0.194362 + vertex -1.39166701 -0.214498997 0.26087001 + vertex -1.51577306 -0.247752994 0.26087001 + endloop + endfacet + + facet normal 0.0497390032 0.258499026 -0.964730144 + outer loop + vertex -1.51577306 0.00046000001 1.18721402 + vertex -1.51577306 -0.247752994 1.12070501 + vertex -1.21148205 -0.379460007 1.10110295 + endloop + endfacet + + facet normal 0.86519593 0.129780978 0.484347939 + outer loop + vertex -1.51577306 0.00046000001 0.194362 + vertex -1.51577306 -0.247752994 0.26087001 + vertex -1.59023798 -0.214498997 0.384977013 + endloop + endfacet + + facet normal 0.0691840202 0.258199126 -0.963611364 + outer loop + vertex -1.51577306 0.00046000001 1.18721402 + vertex -1.63987899 -0.214498997 1.12070501 + vertex -1.51577306 -0.247752994 1.12070501 + endloop + endfacet + + facet normal 0.202385992 0.60955894 0.766471028 + outer loop + vertex -1.51577306 0.00046000001 0.194362 + vertex -1.59023798 -0.214498997 0.384977013 + vertex -1.68109 -0.123646997 0.336712986 + endloop + endfacet + + facet normal 0.189015046 0.189015046 -0.963611245 + outer loop + vertex -1.51577306 0.00046000001 1.18721402 + vertex -1.73073196 -0.123646997 1.12070501 + vertex -1.63987899 -0.214498997 1.12070501 + endloop + endfacet + + facet normal 0.57565999 0.154247016 0.803008974 + outer loop + vertex -1.51577306 0.00046000001 0.194362 + vertex -1.68109 -0.123646997 0.336712986 + vertex -1.71434402 0.00046000001 0.336712986 + endloop + endfacet + + facet normal 0.258199096 0.069185026 -0.963611245 + outer loop + vertex -1.51577306 0.00046000001 1.18721402 + vertex -1.76398599 0.00046000001 1.12070501 + vertex -1.73073196 -0.123646997 1.12070501 + endloop + endfacet + + facet normal 0.202413991 0.788102925 0.581310928 + outer loop + vertex -1.16183996 -0.407552987 0.442575008 + vertex -1.30081499 -0.371859998 0.442575008 + vertex -1.39166701 -0.214498997 0.26087001 + endloop + endfacet + + facet normal -0.0857829452 0.731517494 0.676404655 + outer loop + vertex -1.39166701 -0.214498997 0.26087001 + vertex -1.30081499 -0.371859998 0.442575008 + vertex -1.51577306 -0.486321986 0.539102018 + endloop + endfacet + + facet normal -0.199329078 0.743908286 0.637862265 + outer loop + vertex -1.39166701 -0.214498997 0.26087001 + vertex -1.51577306 -0.486321986 0.539102018 + vertex -1.51577306 -0.247752994 0.26087001 + endloop + endfacet + + facet normal 0.818344295 0.436300159 0.374105155 + outer loop + vertex -1.51577306 -0.247752994 0.26087001 + vertex -1.51577306 -0.486321986 0.539102018 + vertex -1.59023798 -0.214498997 0.384977013 + endloop + endfacet + + facet normal -0.151083976 0.455930918 0.877097785 + outer loop + vertex -1.51577306 -0.486321986 0.539102018 + vertex -1.73073196 -0.371859998 0.442575008 + vertex -1.59023798 -0.214498997 0.384977013 + endloop + endfacet + + facet normal 0.186527073 0.186527073 0.964580357 + outer loop + vertex -1.59023798 -0.214498997 0.384977013 + vertex -1.73073196 -0.371859998 0.442575008 + vertex -1.88809204 -0.214498997 0.442575008 + endloop + endfacet + + facet normal 0.154704019 0.579696059 0.800012052 + outer loop + vertex -1.59023798 -0.214498997 0.384977013 + vertex -1.88809204 -0.214498997 0.442575008 + vertex -1.68109 -0.123646997 0.336712986 + endloop + endfacet + + facet normal 0.413534015 0.110806011 0.903721035 + outer loop + vertex -1.68109 -0.123646997 0.336712986 + vertex -1.88809204 -0.214498997 0.442575008 + vertex -1.71434402 0.00046000001 0.336712986 + endloop + endfacet + + facet normal 0.30303511 0.212556094 0.928972363 + outer loop + vertex -1.88809204 -0.214498997 0.442575008 + vertex -1.93741703 0.00046000001 0.409480006 + vertex -1.71434402 0.00046000001 0.336712986 + endloop + endfacet + + facet normal -0.839146435 0.156273901 0.520971715 + outer loop + vertex -1.08585596 0.00046000001 0.442575008 + vertex -0.820776999 0.00046000001 0.869545996 + vertex -1.16183996 -0.407552987 0.442575008 + endloop + endfacet + + facet normal -0.844083071 0.17527701 0.506756067 + outer loop + vertex -0.820776999 0.00046000001 0.869545996 + vertex -0.905672014 -0.408369005 0.869545996 + vertex -1.16183996 -0.407552987 0.442575008 + endloop + endfacet + + facet normal -0.610179067 0.701910973 0.367426991 + outer loop + vertex -1.16183996 -0.407552987 0.442575008 + vertex -0.905672014 -0.408369005 0.869545996 + vertex -1.087376 -0.566326022 0.869545996 + endloop + endfacet + + facet normal 0.237148017 0.92334199 0.301994026 + outer loop + vertex -1.16183996 -0.407552987 0.442575008 + vertex -1.087376 -0.566326022 0.869545996 + vertex -1.30081499 -0.371859998 0.442575008 + endloop + endfacet + + facet normal -0.22484003 0.839457035 0.494731098 + outer loop + vertex -1.30081499 -0.371859998 0.442575008 + vertex -1.087376 -0.566326022 0.869545996 + vertex -1.51577306 -0.486321986 0.539102018 + endloop + endfacet + + facet normal 0.136153921 0.988691449 0.0628579706 + outer loop + vertex -1.087376 -0.566326022 0.869545996 + vertex -1.51577306 -0.495965987 0.690787971 + vertex -1.51577306 -0.486321986 0.539102018 + endloop + endfacet + + facet normal 0.379295141 0.923411429 0.0587080233 + outer loop + vertex -1.51577306 -0.486321986 0.539102018 + vertex -1.51577306 -0.495965987 0.690787971 + vertex -1.82231998 -0.367796004 0.655328989 + endloop + endfacet + + facet normal 0.40773806 0.899263144 0.158352017 + outer loop + vertex -1.51577306 -0.486321986 0.539102018 + vertex -1.82231998 -0.367796004 0.655328989 + vertex -1.73073196 -0.371859998 0.442575008 + endloop + endfacet + + facet normal 0.678963006 0.678963006 0.279318005 + outer loop + vertex -1.73073196 -0.371859998 0.442575008 + vertex -1.82231998 -0.367796004 0.655328989 + vertex -1.88809204 -0.214498997 0.442575008 + endloop + endfacet + + facet normal 0.852383077 0.512161076 0.105518013 + outer loop + vertex -1.82231998 -0.367796004 0.655328989 + vertex -1.95466805 -0.147531003 0.655328989 + vertex -1.88809204 -0.214498997 0.442575008 + endloop + endfacet + + facet normal 0.871156156 0.475356132 0.122977026 + outer loop + vertex -1.88809204 -0.214498997 0.442575008 + vertex -1.95466805 -0.147531003 0.655328989 + vertex -2.02874589 0.00046000001 0.608051002 + endloop + endfacet + + facet normal 0.876483917 0.263182968 0.403125972 + outer loop + vertex -1.88809204 -0.214498997 0.442575008 + vertex -2.02874589 0.00046000001 0.608051002 + vertex -1.93741703 0.00046000001 0.409480006 + endloop + endfacet + + facet normal -0.881306171 0.176261023 -0.438442081 + outer loop + vertex -0.820776999 0.00046000001 0.869545996 + vertex -0.887284994 0.00046000001 1.00323403 + vertex -0.963268995 -0.379460007 1.00323403 + endloop + endfacet + + facet normal -0.887549043 0.184303015 -0.422242999 + outer loop + vertex -0.820776999 0.00046000001 0.869545996 + vertex -0.963268995 -0.379460007 1.00323403 + vertex -0.905672014 -0.408369005 0.869545996 + endloop + endfacet + + facet normal -0.599210024 0.689293087 -0.407213032 + outer loop + vertex -0.905672014 -0.408369005 0.869545996 + vertex -0.963268995 -0.379460007 1.00323403 + vertex -1.087376 -0.566326022 0.869545996 + endloop + endfacet + + facet normal -0.59920913 0.689293206 -0.407214105 + outer loop + vertex -0.963268995 -0.379460007 1.00323403 + vertex -1.12063003 -0.516255975 1.00323403 + vertex -1.087376 -0.566326022 0.869545996 + endloop + endfacet + + facet normal 0.249578983 0.925593019 -0.284583956 + outer loop + vertex -1.087376 -0.566326022 0.869545996 + vertex -1.12063003 -0.516255975 1.00323403 + vertex -1.51577306 -0.429457009 0.939001024 + endloop + endfacet + + facet normal 0.257640928 0.933316827 -0.250081927 + outer loop + vertex -1.087376 -0.566326022 0.869545996 + vertex -1.51577306 -0.429457009 0.939001024 + vertex -1.51577306 -0.495965987 0.690787971 + endloop + endfacet + + facet normal 0.397967786 0.886139572 -0.237440884 + outer loop + vertex -1.51577306 -0.495965987 0.690787971 + vertex -1.51577306 -0.429457009 0.939001024 + vertex -1.82231998 -0.367796004 0.655328989 + endloop + endfacet + + facet normal 0.404057056 0.881292224 -0.245075062 + outer loop + vertex -1.51577306 -0.429457009 0.939001024 + vertex -1.77611899 -0.319954008 0.903541982 + vertex -1.82231998 -0.367796004 0.655328989 + endloop + endfacet + + facet normal 0.894504726 0.365525872 -0.25739491 + outer loop + vertex -1.95466805 -0.147531003 0.655328989 + vertex -1.89073491 -0.129198998 0.903541982 + vertex -2.02874589 0.00046000001 0.608051002 + endloop + endfacet + + facet normal 0.875202715 0.431018859 -0.219642922 + outer loop + vertex -1.89073491 -0.129198998 0.903541982 + vertex -1.94569099 0.00046000001 0.939001024 + vertex -2.02874589 0.00046000001 0.608051002 + endloop + endfacet + + facet normal -0.472085088 0.0944170132 -0.876482189 + outer loop + vertex -0.887284994 0.00046000001 1.00323403 + vertex -1.06898999 0.00046000001 1.10110295 + vertex -0.963268995 -0.379460007 1.00323403 + endloop + endfacet + + facet normal -0.399085015 0.123011015 -0.908625066 + outer loop + vertex -1.06898999 0.00046000001 1.10110295 + vertex -1.10224402 -0.107427001 1.10110295 + vertex -0.963268995 -0.379460007 1.00323403 + endloop + endfacet + + facet normal -0.362892926 0.145723984 -0.92036581 + outer loop + vertex -0.963268995 -0.379460007 1.00323403 + vertex -1.10224402 -0.107427001 1.10110295 + vertex -1.21148205 -0.379460007 1.10110295 + endloop + endfacet + + facet normal -0.337954879 0.388761878 -0.857117712 + outer loop + vertex -0.963268995 -0.379460007 1.00323403 + vertex -1.21148205 -0.379460007 1.10110295 + vertex -1.12063003 -0.516255975 1.00323403 + endloop + endfacet + + facet normal 0.260202944 0.670137882 -0.695132792 + outer loop + vertex -1.12063003 -0.516255975 1.00323403 + vertex -1.21148205 -0.379460007 1.10110295 + vertex -1.51577306 -0.429457009 0.939001024 + endloop + endfacet + + facet normal 0.25209403 0.68426919 -0.68426919 + outer loop + vertex -1.21148205 -0.379460007 1.10110295 + vertex -1.51577306 -0.247752994 1.12070501 + vertex -1.51577306 -0.429457009 0.939001024 + endloop + endfacet + + facet normal 0.186156049 0.694746196 -0.69474715 + outer loop + vertex -1.51577306 -0.429457009 0.939001024 + vertex -1.51577306 -0.247752994 1.12070501 + vertex -1.63987899 -0.214498997 1.12070501 + endloop + endfacet + + facet normal 0.380661011 0.715228021 -0.586127996 + outer loop + vertex -1.51577306 -0.429457009 0.939001024 + vertex -1.63987899 -0.214498997 1.12070501 + vertex -1.77611899 -0.319954008 0.903541982 + endloop + endfacet + + facet normal 0.673306763 0.404560834 -0.618860781 + outer loop + vertex -1.77611899 -0.319954008 0.903541982 + vertex -1.63987899 -0.214498997 1.12070501 + vertex -1.89073491 -0.129198998 0.903541982 + endloop + endfacet + + facet normal 0.622429729 0.622429729 -0.474512786 + outer loop + vertex -1.63987899 -0.214498997 1.12070501 + vertex -1.73073196 -0.123646997 1.12070501 + vertex -1.89073491 -0.129198998 0.903541982 + endloop + endfacet + + facet normal 0.784513354 0.210211098 -0.583395302 + outer loop + vertex -1.89073491 -0.129198998 0.903541982 + vertex -1.73073196 -0.123646997 1.12070501 + vertex -1.76398599 0.00046000001 1.12070501 + endloop + endfacet + + facet normal 0.634200931 0.442243934 -0.634199917 + outer loop + vertex -1.89073491 -0.129198998 0.903541982 + vertex -1.76398599 0.00046000001 1.12070501 + vertex -1.94569099 0.00046000001 0.939001024 + endloop + endfacet + + facet normal -0.521937132 -0.820093155 0.234582052 + outer loop + vertex -1.90136802 -0.156956002 0.72427702 + vertex -1.861866 -0.179064006 0.734879017 + vertex -1.86046696 -0.183946997 0.72092402 + endloop + endfacet + + facet normal 0.561335981 -0.0371459983 -0.826753974 + outer loop + vertex -1.95466805 -0.147531003 0.655328989 + vertex -1.88625693 -0.187941998 0.703593016 + vertex -1.86046696 -0.183946997 0.72092402 + endloop + endfacet + + facet normal 0.511620224 0.810681403 -0.284676135 + outer loop + vertex -1.86046696 -0.183946997 0.72092402 + vertex -1.90136802 -0.156956002 0.72427702 + vertex -1.95466805 -0.147531003 0.655328989 + endloop + endfacet + + facet normal 0.67524904 0.414225072 -0.610292137 + outer loop + vertex -1.862077 -0.237517998 0.69669801 + vertex -1.88625693 -0.187941998 0.703593016 + vertex -1.95466805 -0.147531003 0.655328989 + endloop + endfacet + + facet normal 0.672120929 0.413266957 -0.614380956 + outer loop + vertex -1.88625693 -0.187941998 0.703593016 + vertex -1.862077 -0.237517998 0.69669801 + vertex -1.84992504 -0.220708996 0.721297979 + endloop + endfacet + + facet normal 0.536240935 0.14531 -0.831462979 + outer loop + vertex -1.84992504 -0.220708996 0.721297979 + vertex -1.86046696 -0.183946997 0.72092402 + vertex -1.88625693 -0.187941998 0.703593016 + endloop + endfacet + + facet normal 0.895058095 0.00553400069 -0.445915073 + outer loop + vertex -1.83589005 -0.269659013 0.748862982 + vertex -1.84992504 -0.220708996 0.721297979 + vertex -1.862077 -0.237517998 0.69669801 + endloop + endfacet + + facet normal 0.914935708 0.290189922 -0.280503899 + outer loop + vertex -1.862077 -0.237517998 0.69669801 + vertex -1.83185291 -0.299488008 0.731172025 + vertex -1.83589005 -0.269659013 0.748862982 + endloop + endfacet + + facet normal 0.968834102 0.246923044 -0.019739002 + outer loop + vertex -1.82824898 -0.296305001 0.790558994 + vertex -1.83589005 -0.269659013 0.748862982 + vertex -1.82278597 -0.318078995 0.786329985 + endloop + endfacet + + facet normal 0.977088094 0.189592034 -0.0967150182 + outer loop + vertex -1.83185291 -0.299488008 0.731172025 + vertex -1.82278597 -0.318078995 0.786329985 + vertex -1.83589005 -0.269659013 0.748862982 + endloop + endfacet + + facet normal 0.997900426 0.0593979657 0.0258139856 + outer loop + vertex -1.82945108 -0.292113006 0.827350974 + vertex -1.82824898 -0.296305001 0.790558994 + vertex -1.82883 -0.305685014 0.834594011 + endloop + endfacet + + facet normal 0.970842183 0.231530026 0.0621240065 + outer loop + vertex -1.82824898 -0.296305001 0.790558994 + vertex -1.82278597 -0.318078995 0.786329985 + vertex -1.82883 -0.305685014 0.834594011 + endloop + endfacet + + facet normal 0.980895877 0.124576986 0.149411991 + outer loop + vertex -1.82883 -0.305685014 0.834594011 + vertex -1.83534694 -0.271548003 0.848914027 + vertex -1.82945108 -0.292113006 0.827350974 + endloop + endfacet + + facet normal 0.919196248 0.0103330025 0.393664122 + outer loop + vertex -1.82883 -0.305685014 0.834594011 + vertex -1.84091997 -0.280896991 0.862173021 + vertex -1.83534694 -0.271548003 0.848914027 + endloop + endfacet + + facet normal 0.793512464 0.287575155 0.536319315 + outer loop + vertex -1.84091997 -0.280896991 0.862173021 + vertex -1.859056 -0.243714005 0.869068027 + vertex -1.83534694 -0.271548003 0.848914027 + endloop + endfacet + + facet normal 0.804794014 0.326705992 0.495550007 + outer loop + vertex -1.84234202 -0.247156993 0.844193995 + vertex -1.83534694 -0.271548003 0.848914027 + vertex -1.859056 -0.243714005 0.869068027 + endloop + endfacet + + facet normal 0.715218186 0.572144151 0.401390105 + outer loop + vertex -1.859056 -0.243714005 0.869068027 + vertex -1.87719107 -0.206533 0.848384023 + vertex -1.84234202 -0.247156993 0.844193995 + endloop + endfacet + + facet normal 0.653584957 0.502278864 0.566164911 + outer loop + vertex -1.84963393 -0.221723005 0.830048978 + vertex -1.84234202 -0.247156993 0.844193995 + vertex -1.87719107 -0.206533 0.848384023 + endloop + endfacet + + facet normal 0.55377394 0.818229973 0.154382989 + outer loop + vertex -1.851632 -0.214763001 0.800320983 + vertex -1.84963393 -0.221723005 0.830048978 + vertex -1.87719107 -0.206533 0.848384023 + endloop + endfacet + + facet normal 0.600952983 0.777222991 0.186493993 + outer loop + vertex -1.87719107 -0.206533 0.848384023 + vertex -1.88625693 -0.187941998 0.800119996 + vertex -1.851632 -0.214763001 0.800320983 + endloop + endfacet + + facet normal 0.597518981 0.772989929 0.21320799 + outer loop + vertex -1.85507607 -0.202750996 0.766424 + vertex -1.851632 -0.214763001 0.800320983 + vertex -1.88625693 -0.187941998 0.800119996 + endloop + endfacet + + facet normal 0.664247036 0.676876068 0.317198038 + outer loop + vertex -1.88625693 -0.187941998 0.800119996 + vertex -1.89834702 -0.163152993 0.772539973 + vertex -1.85507607 -0.202750996 0.766424 + endloop + endfacet + + facet normal 0.657633364 0.663550377 0.356678218 + outer loop + vertex -1.861866 -0.179064006 0.734879017 + vertex -1.85507607 -0.202750996 0.766424 + vertex -1.89834702 -0.163152993 0.772539973 + endloop + endfacet + + facet normal 0.469525933 0.87896502 0.0834619924 + outer loop + vertex -1.89834702 -0.163152993 0.772539973 + vertex -1.90136802 -0.156956002 0.72427702 + vertex -1.861866 -0.179064006 0.734879017 + endloop + endfacet + + facet normal 0.373062909 0.913278759 -0.163541958 + outer loop + vertex -1.90136802 -0.156956002 0.72427702 + vertex -1.89073491 -0.129198998 0.903541982 + vertex -1.95466805 -0.147531003 0.655328989 + endloop + endfacet + + facet normal 0.998070359 0.0116050038 -0.0609980263 + outer loop + vertex -1.90136802 -0.156956002 0.72427702 + vertex -1.89834702 -0.163152993 0.772539973 + vertex -1.89073491 -0.129198998 0.903541982 + endloop + endfacet + + facet normal 0.941414237 0.309131056 -0.134823024 + outer loop + vertex -1.89834702 -0.163152993 0.772539973 + vertex -1.88625693 -0.187941998 0.800119996 + vertex -1.89073491 -0.129198998 0.903541982 + endloop + endfacet + + facet normal 0.967765391 0.234684095 -0.0913970396 + outer loop + vertex -1.88625693 -0.187941998 0.800119996 + vertex -1.87719107 -0.206533 0.848384023 + vertex -1.89073491 -0.129198998 0.903541982 + endloop + endfacet + + facet normal 0.919146776 0.322406918 -0.226324946 + outer loop + vertex -1.87719107 -0.206533 0.848384023 + vertex -1.859056 -0.243714005 0.869068027 + vertex -1.89073491 -0.129198998 0.903541982 + endloop + endfacet + + facet normal 0.629824638 0.378434747 -0.6783126 + outer loop + vertex -1.77611899 -0.319954008 0.903541982 + vertex -1.89073491 -0.129198998 0.903541982 + vertex -1.859056 -0.243714005 0.869068027 + endloop + endfacet + + facet normal 0.655910134 0.434389085 -0.617323101 + outer loop + vertex -1.859056 -0.243714005 0.869068027 + vertex -1.84091997 -0.280896991 0.862173021 + vertex -1.77611899 -0.319954008 0.903541982 + endloop + endfacet + + facet normal 0.635726869 0.691575825 -0.342890948 + outer loop + vertex -1.84091997 -0.280896991 0.862173021 + vertex -1.82883 -0.305685014 0.834594011 + vertex -1.77611899 -0.319954008 0.903541982 + endloop + endfacet + + facet normal 0.669934034 0.402535051 -0.623822033 + outer loop + vertex -1.862077 -0.237517998 0.69669801 + vertex -1.95466805 -0.147531003 0.655328989 + vertex -1.82231998 -0.367796004 0.655328989 + endloop + endfacet + + facet normal 0.919927955 0.341761976 -0.192174986 + outer loop + vertex -1.83185291 -0.299488008 0.731172025 + vertex -1.862077 -0.237517998 0.69669801 + vertex -1.82231998 -0.367796004 0.655328989 + endloop + endfacet + + facet normal 0.970269859 0.227403969 -0.0828489959 + outer loop + vertex -1.82278597 -0.318078995 0.786329985 + vertex -1.83185291 -0.299488008 0.731172025 + vertex -1.82231998 -0.367796004 0.655328989 + endloop + endfacet + + facet normal 0.549644947 0.795333922 -0.255605966 + outer loop + vertex -1.82231998 -0.367796004 0.655328989 + vertex -1.77611899 -0.319954008 0.903541982 + vertex -1.82883 -0.305685014 0.834594011 + endloop + endfacet + + facet normal -0.685613155 0.679786205 -0.260433048 + outer loop + vertex -1.82278597 -0.318078995 0.786329985 + vertex -1.82883 -0.305685014 0.834594011 + vertex -1.82231998 -0.367796004 0.655328989 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.03665495 -0.418327987 -0.397619992 + vertex -2.03665495 -0.418327987 -0.397619992 + vertex -2.03665495 -0.418327987 -0.397619992 + endloop + endfacet + + facet normal -0.891945779 -0.305186927 -0.333606929 + outer loop + vertex -1.81837392 -0.186278999 0.356357992 + vertex -1.88002801 -0.165750995 0.502418995 + vertex -1.81634998 -0.275851995 0.432888001 + endloop + endfacet + + facet normal -0.885285795 0.462163895 0.0517069884 + outer loop + vertex -2.03665495 -0.418327987 -0.397619992 + vertex -1.92074704 -0.268263996 0.245554 + vertex -1.95693707 -0.340858012 0.274801999 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.03665495 -0.418327987 -0.397619992 + vertex -1.95693707 -0.340858012 0.274801999 + vertex -2.03665495 -0.418327987 -0.397619992 + endloop + endfacet + + facet normal -0.782590985 0.524955928 0.334623009 + outer loop + vertex -1.92074704 -0.268263996 0.245554 + vertex -1.81837392 -0.186278999 0.356357992 + vertex -1.95693707 -0.340858012 0.274801999 + endloop + endfacet + + facet normal -0.758138955 0.413597971 0.504144967 + outer loop + vertex -1.81837392 -0.186278999 0.356357992 + vertex -1.81634998 -0.275851995 0.432888001 + vertex -1.95693707 -0.340858012 0.274801999 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.03665495 -0.418327987 -0.397619992 + vertex -2.03665495 -0.418327987 -0.397619992 + vertex -2.03665495 -0.418327987 -0.397619992 + endloop + endfacet + + facet normal -0.89194268 -0.305170923 -0.333629906 + outer loop + vertex -1.81634998 -0.275851995 0.432888001 + vertex -1.88002801 -0.165750995 0.502418995 + vertex -1.86227798 -0.282516986 0.561774015 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.03665495 -0.418327987 -0.397619992 + vertex -1.95693707 -0.340858012 0.274801999 + vertex -2.03665495 -0.418327987 -0.397619992 + endloop + endfacet + + facet normal -0.239093035 0.967433095 -0.0831130072 + outer loop + vertex -1.95693707 -0.340858012 0.274801999 + vertex -2.051718 -0.363382012 0.285284013 + vertex -2.03665495 -0.418327987 -0.397619992 + endloop + endfacet + + facet normal -0.347357035 0.934693158 -0.0754440054 + outer loop + vertex -1.95693707 -0.340858012 0.274801999 + vertex -1.81634998 -0.275851995 0.432888001 + vertex -1.86227798 -0.282516986 0.561774015 + endloop + endfacet + + facet normal -0.241774037 0.963362098 -0.116099015 + outer loop + vertex -1.95693707 -0.340858012 0.274801999 + vertex -1.86227798 -0.282516986 0.561774015 + vertex -2.051718 -0.363382012 0.285284013 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.03665495 -0.418327987 -0.397619992 + vertex -2.03665495 -0.418327987 -0.397619992 + vertex -2.03665495 -0.418327987 -0.397619992 + endloop + endfacet + + facet normal -0.89195025 -0.305165082 -0.333615094 + outer loop + vertex -1.86227798 -0.282516986 0.561774015 + vertex -1.88002801 -0.165750995 0.502418995 + vertex -1.92157102 -0.201254994 0.645965993 + endloop + endfacet + + facet normal 0.485254169 0.872347355 -0.059485022 + outer loop + vertex -2.03665495 -0.418327987 -0.397619992 + vertex -2.051718 -0.363382012 0.285284013 + vertex -2.13371491 -0.318872988 0.269109011 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.03665495 -0.418327987 -0.397619992 + vertex -2.13371491 -0.318872988 0.269109011 + vertex -2.03665495 -0.418327987 -0.397619992 + endloop + endfacet + + facet normal 0.482345134 0.694665194 -0.533651114 + outer loop + vertex -2.051718 -0.363382012 0.285284013 + vertex -1.86227798 -0.282516986 0.561774015 + vertex -2.13371491 -0.318872988 0.269109011 + endloop + endfacet + + facet normal 0.403897047 0.78366518 -0.47194913 + outer loop + vertex -1.86227798 -0.282516986 0.561774015 + vertex -1.92157102 -0.201254994 0.645965993 + vertex -2.13371491 -0.318872988 0.269109011 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.03665495 -0.418327987 -0.397619992 + vertex -2.03665495 -0.418327987 -0.397619992 + vertex -2.03665495 -0.418327987 -0.397619992 + endloop + endfacet + + facet normal -0.891946554 -0.305174887 -0.333615839 + outer loop + vertex -1.92157102 -0.201254994 0.645965993 + vertex -1.88002801 -0.165750995 0.502418995 + vertex -1.94958198 -0.0932570025 0.622065008 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.03665495 -0.418327987 -0.397619992 + vertex -2.13371491 -0.318872988 0.269109011 + vertex -2.03665495 -0.418327987 -0.397619992 + endloop + endfacet + + facet normal 0.98234576 0.14196597 0.121829979 + outer loop + vertex -2.13371491 -0.318872988 0.269109011 + vertex -2.1411891 -0.240848005 0.238456994 + vertex -2.03665495 -0.418327987 -0.397619992 + endloop + endfacet + + facet normal 0.851677418 0.107381061 -0.512947261 + outer loop + vertex -2.13371491 -0.318872988 0.269109011 + vertex -1.92157102 -0.201254994 0.645965993 + vertex -1.94958198 -0.0932570025 0.622065008 + endloop + endfacet + + facet normal 0.903621852 -0.0788339823 -0.421013921 + outer loop + vertex -2.13371491 -0.318872988 0.269109011 + vertex -1.94958198 -0.0932570025 0.622065008 + vertex -2.1411891 -0.240848005 0.238456994 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.03665495 -0.418327987 -0.397619992 + vertex -2.03665495 -0.418327987 -0.397619992 + vertex -2.03665495 -0.418327987 -0.397619992 + endloop + endfacet + + facet normal -0.89194572 -0.305181921 -0.333611906 + outer loop + vertex -1.94958198 -0.0932570025 0.622065008 + vertex -1.88002801 -0.165750995 0.502418995 + vertex -1.92521799 -0.0398489982 0.508068025 + endloop + endfacet + + facet normal 0.618494272 -0.724668384 0.303843141 + outer loop + vertex -2.03665495 -0.418327987 -0.397619992 + vertex -2.1411891 -0.240848005 0.238456994 + vertex -2.0685091 -0.188061997 0.216406003 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.03665495 -0.418327987 -0.397619992 + vertex -2.0685091 -0.188061997 0.216406003 + vertex -2.03665495 -0.418327987 -0.397619992 + endloop + endfacet + + facet normal 0.590630949 -0.806794941 0.0153989978 + outer loop + vertex -2.1411891 -0.240848005 0.238456994 + vertex -1.94958198 -0.0932570025 0.622065008 + vertex -2.0685091 -0.188061997 0.216406003 + endloop + endfacet + + facet normal 0.806724012 -0.582334995 -0.100410998 + outer loop + vertex -1.94958198 -0.0932570025 0.622065008 + vertex -1.92521799 -0.0398489982 0.508068025 + vertex -2.0685091 -0.188061997 0.216406003 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.03665495 -0.418327987 -0.397619992 + vertex -2.03665495 -0.418327987 -0.397619992 + vertex -2.03665495 -0.418327987 -0.397619992 + endloop + endfacet + + facet normal -0.891949534 -0.305183858 -0.333599836 + outer loop + vertex -1.92521799 -0.0398489982 0.508068025 + vertex -1.88002801 -0.165750995 0.502418995 + vertex -1.86682701 -0.0812470019 0.389818996 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.03665495 -0.418327987 -0.397619992 + vertex -2.0685091 -0.188061997 0.216406003 + vertex -2.03665495 -0.418327987 -0.397619992 + endloop + endfacet + + facet normal -0.126801014 -0.93091315 0.342523098 + outer loop + vertex -2.0685091 -0.188061997 0.216406003 + vertex -1.970402 -0.200262994 0.219567001 + vertex -2.03665495 -0.418327987 -0.397619992 + endloop + endfacet + + facet normal 0.145876989 -0.909062028 0.390289962 + outer loop + vertex -2.0685091 -0.188061997 0.216406003 + vertex -1.92521799 -0.0398489982 0.508068025 + vertex -1.86682701 -0.0812470019 0.389818996 + endloop + endfacet + + facet normal -0.116740949 -0.779241621 0.615754724 + outer loop + vertex -2.0685091 -0.188061997 0.216406003 + vertex -1.86682701 -0.0812470019 0.389818996 + vertex -1.970402 -0.200262994 0.219567001 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.03665495 -0.418327987 -0.397619992 + vertex -2.03665495 -0.418327987 -0.397619992 + vertex -2.03665495 -0.418327987 -0.397619992 + endloop + endfacet + + facet normal -0.891944349 -0.305192113 -0.333606124 + outer loop + vertex -1.86682701 -0.0812470019 0.389818996 + vertex -1.88002801 -0.165750995 0.502418995 + vertex -1.81837392 -0.186278999 0.356357992 + endloop + endfacet + + facet normal -0.824144065 -0.500415027 0.265276015 + outer loop + vertex -2.03665495 -0.418327987 -0.397619992 + vertex -1.970402 -0.200262994 0.219567001 + vertex -1.92074704 -0.268263996 0.245554 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.03665495 -0.418327987 -0.397619992 + vertex -1.92074704 -0.268263996 0.245554 + vertex -2.03665495 -0.418327987 -0.397619992 + endloop + endfacet + + facet normal -0.71909833 -0.282447189 0.634918332 + outer loop + vertex -1.970402 -0.200262994 0.219567001 + vertex -1.86682701 -0.0812470019 0.389818996 + vertex -1.92074704 -0.268263996 0.245554 + endloop + endfacet + + facet normal -0.460383147 -0.455179155 0.762141228 + outer loop + vertex -1.86682701 -0.0812470019 0.389818996 + vertex -1.81837392 -0.186278999 0.356357992 + vertex -1.92074704 -0.268263996 0.245554 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 -0.77343601 0.148902997 + vertex -2.04218507 -0.77343601 0.148902997 + vertex -2.04218507 -0.77343601 0.148902997 + endloop + endfacet + + facet normal -0.417207867 -0.680008829 -0.602930844 + outer loop + vertex -1.69004607 -0.233094007 0.600108027 + vertex -1.56890297 -0.326896995 0.622075021 + vertex -1.57823491 -0.235411003 0.525350988 + endloop + endfacet + + facet normal 0.39095211 -0.768585265 0.506392181 + outer loop + vertex -2.04218507 -0.77343601 0.148902997 + vertex -1.75114 -0.435550988 0.437038004 + vertex -1.67776895 -0.439565986 0.37429899 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 -0.77343601 0.148902997 + vertex -1.67776895 -0.439565986 0.37429899 + vertex -2.04218507 -0.77343601 0.148902997 + endloop + endfacet + + facet normal 0.482775718 -0.633069634 0.605103672 + outer loop + vertex -1.75114 -0.435550988 0.437038004 + vertex -1.69004607 -0.233094007 0.600108027 + vertex -1.67776895 -0.439565986 0.37429899 + endloop + endfacet + + facet normal 0.40646106 -0.663174093 0.628482044 + outer loop + vertex -1.69004607 -0.233094007 0.600108027 + vertex -1.57823491 -0.235411003 0.525350988 + vertex -1.67776895 -0.439565986 0.37429899 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 -0.77343601 0.148902997 + vertex -2.04218507 -0.77343601 0.148902997 + vertex -2.04218507 -0.77343601 0.148902997 + endloop + endfacet + + facet normal -0.417207867 -0.680008829 -0.602930844 + outer loop + vertex -1.57823491 -0.235411003 0.525350988 + vertex -1.56890297 -0.326896995 0.622075021 + vertex -1.45939696 -0.306618005 0.523428977 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 -0.77343601 0.148902997 + vertex -1.67776895 -0.439565986 0.37429899 + vertex -2.04218507 -0.77343601 0.148902997 + endloop + endfacet + + facet normal -0.141780034 -0.442905128 0.885287166 + outer loop + vertex -1.67776895 -0.439565986 0.37429899 + vertex -1.61079192 -0.497384012 0.35609901 + vertex -2.04218507 -0.77343601 0.148902997 + endloop + endfacet + + facet normal -0.275092959 -0.481556952 0.832121909 + outer loop + vertex -1.67776895 -0.439565986 0.37429899 + vertex -1.57823491 -0.235411003 0.525350988 + vertex -1.45939696 -0.306618005 0.523428977 + endloop + endfacet + + facet normal -0.234952107 -0.528877199 0.815528333 + outer loop + vertex -1.67776895 -0.439565986 0.37429899 + vertex -1.45939696 -0.306618005 0.523428977 + vertex -1.61079192 -0.497384012 0.35609901 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 -0.77343601 0.148902997 + vertex -2.04218507 -0.77343601 0.148902997 + vertex -2.04218507 -0.77343601 0.148902997 + endloop + endfacet + + facet normal -0.417209029 -0.680007041 -0.602932036 + outer loop + vertex -1.45939696 -0.306618005 0.523428977 + vertex -1.56890297 -0.326896995 0.622075021 + vertex -1.42301798 -0.393096 0.595789015 + endloop + endfacet + + facet normal -0.577504694 0.348285824 0.738366604 + outer loop + vertex -2.04218507 -0.77343601 0.148902997 + vertex -1.61079192 -0.497384012 0.35609901 + vertex -1.60065103 -0.565468013 0.396145999 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 -0.77343601 0.148902997 + vertex -1.60065103 -0.565468013 0.396145999 + vertex -2.04218507 -0.77343601 0.148902997 + endloop + endfacet + + facet normal -0.825805068 0.189439997 0.531185985 + outer loop + vertex -1.61079192 -0.497384012 0.35609901 + vertex -1.45939696 -0.306618005 0.523428977 + vertex -1.60065103 -0.565468013 0.396145999 + endloop + endfacet + + facet normal -0.799487293 0.150301054 0.581575215 + outer loop + vertex -1.45939696 -0.306618005 0.523428977 + vertex -1.42301798 -0.393096 0.595789015 + vertex -1.60065103 -0.565468013 0.396145999 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 -0.77343601 0.148902997 + vertex -2.04218507 -0.77343601 0.148902997 + vertex -2.04218507 -0.77343601 0.148902997 + endloop + endfacet + + facet normal -0.417208076 -0.680003166 -0.602937102 + outer loop + vertex -1.42301798 -0.393096 0.595789015 + vertex -1.56890297 -0.326896995 0.622075021 + vertex -1.49649501 -0.429726005 0.687943995 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 -0.77343601 0.148902997 + vertex -1.60065103 -0.565468013 0.396145999 + vertex -2.04218507 -0.77343601 0.148902997 + endloop + endfacet + + facet normal -0.432327896 0.901612759 0.0136719961 + outer loop + vertex -1.60065103 -0.565468013 0.396145999 + vertex -1.65497303 -0.592549026 0.464280993 + vertex -2.04218507 -0.77343601 0.148902997 + endloop + endfacet + + facet normal -0.591237903 0.791075826 -0.156960949 + outer loop + vertex -1.60065103 -0.565468013 0.396145999 + vertex -1.42301798 -0.393096 0.595789015 + vertex -1.49649501 -0.429726005 0.687943995 + endloop + endfacet + + facet normal -0.591241062 0.791074038 -0.156958029 + outer loop + vertex -1.60065103 -0.565468013 0.396145999 + vertex -1.49649501 -0.429726005 0.687943995 + vertex -1.65497303 -0.592549026 0.464280993 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 -0.77343601 0.148902997 + vertex -2.04218507 -0.77343601 0.148902997 + vertex -2.04218507 -0.77343601 0.148902997 + endloop + endfacet + + facet normal -0.417216957 -0.680004954 -0.602928936 + outer loop + vertex -1.49649501 -0.429726005 0.687943995 + vertex -1.56890297 -0.326896995 0.622075021 + vertex -1.62449408 -0.388922006 0.730498016 + endloop + endfacet + + facet normal 0.0537480377 0.836287558 -0.545650363 + outer loop + vertex -2.04218507 -0.77343601 0.148902997 + vertex -1.65497303 -0.592549026 0.464280993 + vertex -1.73286009 -0.558236003 0.50919801 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 -0.77343601 0.148902997 + vertex -1.73286009 -0.558236003 0.50919801 + vertex -2.04218507 -0.77343601 0.148902997 + endloop + endfacet + + facet normal 0.011833 0.804398 -0.593973041 + outer loop + vertex -1.65497303 -0.592549026 0.464280993 + vertex -1.49649501 -0.429726005 0.687943995 + vertex -1.73286009 -0.558236003 0.50919801 + endloop + endfacet + + facet normal 0.043387007 0.783097148 -0.620384037 + outer loop + vertex -1.49649501 -0.429726005 0.687943995 + vertex -1.62449408 -0.388922006 0.730498016 + vertex -1.73286009 -0.558236003 0.50919801 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 -0.77343601 0.148902997 + vertex -2.04218507 -0.77343601 0.148902997 + vertex -2.04218507 -0.77343601 0.148902997 + endloop + endfacet + + facet normal -0.417210042 -0.680010021 -0.602928042 + outer loop + vertex -1.62449408 -0.388922006 0.730498016 + vertex -1.56890297 -0.326896995 0.622075021 + vertex -1.71063101 -0.301414013 0.691406012 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 -0.77343601 0.148902997 + vertex -1.73286009 -0.558236003 0.50919801 + vertex -2.04218507 -0.77343601 0.148902997 + endloop + endfacet + + facet normal 0.644526064 0.270723015 -0.715049088 + outer loop + vertex -1.73286009 -0.558236003 0.50919801 + vertex -1.77565801 -0.488364011 0.497074008 + vertex -2.04218507 -0.77343601 0.148902997 + endloop + endfacet + + facet normal 0.676753223 0.386138141 -0.626819253 + outer loop + vertex -1.73286009 -0.558236003 0.50919801 + vertex -1.62449408 -0.388922006 0.730498016 + vertex -1.71063101 -0.301414013 0.691406012 + endloop + endfacet + + facet normal 0.734698236 0.349097162 -0.581678212 + outer loop + vertex -1.73286009 -0.558236003 0.50919801 + vertex -1.71063101 -0.301414013 0.691406012 + vertex -1.77565801 -0.488364011 0.497074008 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 -0.77343601 0.148902997 + vertex -2.04218507 -0.77343601 0.148902997 + vertex -2.04218507 -0.77343601 0.148902997 + endloop + endfacet + + facet normal -0.417209774 -0.680009604 -0.602928638 + outer loop + vertex -1.71063101 -0.301414013 0.691406012 + vertex -1.56890297 -0.326896995 0.622075021 + vertex -1.69004607 -0.233094007 0.600108027 + endloop + endfacet + + facet normal 0.811764777 -0.561054826 -0.162034959 + outer loop + vertex -2.04218507 -0.77343601 0.148902997 + vertex -1.77565801 -0.488364011 0.497074008 + vertex -1.75114 -0.435550988 0.437038004 + endloop + endfacet + + facet normal 0 0 0 + outer loop + vertex -2.04218507 -0.77343601 0.148902997 + vertex -1.75114 -0.435550988 0.437038004 + vertex -2.04218507 -0.77343601 0.148902997 + endloop + endfacet + + facet normal 0.926234186 -0.373678088 0.0495470129 + outer loop + vertex -1.77565801 -0.488364011 0.497074008 + vertex -1.71063101 -0.301414013 0.691406012 + vertex -1.75114 -0.435550988 0.437038004 + endloop + endfacet + + facet normal 0.957405746 -0.28874594 -0.000203999953 + outer loop + vertex -1.71063101 -0.301414013 0.691406012 + vertex -1.69004607 -0.233094007 0.600108027 + vertex -1.75114 -0.435550988 0.437038004 + endloop + endfacet + + facet normal -0.416388899 -0.719635844 -0.55564785 + outer loop + vertex -1.74193907 0.30865401 0.859300017 + vertex -1.71201098 0.319709986 0.822552979 + vertex -1.75035691 0.337029994 0.828857005 + endloop + endfacet + + facet normal 0.0543019809 -0.722881734 -0.688834786 + outer loop + vertex -1.79036307 0.316971004 0.846754014 + vertex -1.74193907 0.30865401 0.859300017 + vertex -1.75035691 0.337029994 0.828857005 + endloop + endfacet + + facet normal 0.418186873 -0.411096871 -0.810011804 + outer loop + vertex -1.78778303 0.271717012 0.87105298 + vertex -1.79036307 0.316971004 0.846754014 + vertex -1.82835197 0.274188995 0.848854005 + endloop + endfacet + + facet normal 0.147420973 -0.461364925 -0.874876797 + outer loop + vertex -1.78778303 0.271717012 0.87105298 + vertex -1.74193907 0.30865401 0.859300017 + vertex -1.79036307 0.316971004 0.846754014 + endloop + endfacet + + facet normal 0.471917987 -0.110359997 -0.874708056 + outer loop + vertex -1.83203197 0.223009005 0.853326023 + vertex -1.78778303 0.271717012 0.87105298 + vertex -1.82835197 0.274188995 0.848854005 + endloop + endfacet + + facet normal 0.488865733 0.668644667 -0.560289621 + outer loop + vertex -1.85778403 0.181133002 0.812886 + vertex -1.82527602 0.146935999 0.800439 + vertex -1.82124805 0.177171007 0.840036988 + endloop + endfacet + + facet normal 0.901277065 0.41694206 -0.117724016 + outer loop + vertex -1.85520399 0.162085995 0.765182972 + vertex -1.85778403 0.181133002 0.812886 + vertex -1.87216401 0.202947006 0.780054986 + endloop + endfacet + + facet normal 0.416387886 0.908436775 0.0369279943 + outer loop + vertex -1.82527602 0.146935999 0.800439 + vertex -1.85520399 0.162085995 0.765182972 + vertex -1.81685805 0.144767001 0.758879006 + endloop + endfacet + + facet normal 0.654929042 0.713351011 -0.249396011 + outer loop + vertex -1.85520399 0.162085995 0.765182972 + vertex -1.82527602 0.146935999 0.800439 + vertex -1.85778403 0.181133002 0.812886 + endloop + endfacet + + facet normal 0.901276946 0.243723989 0.358187973 + outer loop + vertex -1.85778403 0.207340002 0.740882993 + vertex -1.85520399 0.162085995 0.765182972 + vertex -1.87216401 0.202947006 0.780054986 + endloop + endfacet + + facet normal 0.488877177 0.152067065 0.858996332 + outer loop + vertex -1.82527602 0.173142999 0.72843498 + vertex -1.85778403 0.207340002 0.740882993 + vertex -1.82124805 0.221757993 0.717536986 + endloop + endfacet + + facet normal 0.416390866 0.719640791 0.555639863 + outer loop + vertex -1.85520399 0.162085995 0.765182972 + vertex -1.82527602 0.173142999 0.72843498 + vertex -1.81685805 0.144767001 0.758879006 + endloop + endfacet + + facet normal 0.654934824 0.386147916 0.649576843 + outer loop + vertex -1.82527602 0.173142999 0.72843498 + vertex -1.85520399 0.162085995 0.765182972 + vertex -1.85778403 0.207340002 0.740882993 + endloop + endfacet + + facet normal 0.289381891 -0.326072842 0.899963617 + outer loop + vertex -1.83203197 0.265412986 0.736820996 + vertex -1.78360796 0.267105997 0.721863985 + vertex -1.82124805 0.221757993 0.717536986 + endloop + endfacet + + facet normal 0.471929044 -0.646786034 0.599125087 + outer loop + vertex -1.78778303 0.314121008 0.75454998 + vertex -1.83203197 0.265412986 0.736820996 + vertex -1.82835197 0.301744998 0.77314502 + endloop + endfacet + + facet normal 0.00501300022 -0.570511997 0.821273983 + outer loop + vertex -1.78360796 0.267105997 0.721863985 + vertex -1.78778303 0.314121008 0.75454998 + vertex -1.74596691 0.304625988 0.747698009 + endloop + endfacet + + facet normal 0.266493112 -0.534111202 0.802313328 + outer loop + vertex -1.78778303 0.314121008 0.75454998 + vertex -1.78360796 0.267105997 0.721863985 + vertex -1.83203197 0.265412986 0.736820996 + endloop + endfacet + + facet normal 0.418194056 -0.835585117 0.356246054 + outer loop + vertex -1.79036307 0.333168 0.802253008 + vertex -1.78778303 0.314121008 0.75454998 + vertex -1.82835197 0.301744998 0.77314502 + endloop + endfacet + + facet normal 0.0543040037 -0.996534169 0.0630150065 + outer loop + vertex -1.74193907 0.33486101 0.787295997 + vertex -1.79036307 0.333168 0.802253008 + vertex -1.75035691 0.337029994 0.828857005 + endloop + endfacet + + facet normal -0.0790780187 -0.788413107 0.610042095 + outer loop + vertex -1.78778303 0.314121008 0.75454998 + vertex -1.74193907 0.33486101 0.787295997 + vertex -1.74596691 0.304625988 0.747698009 + endloop + endfacet + + facet normal 0.147421986 -0.915789962 0.37362498 + outer loop + vertex -1.74193907 0.33486101 0.787295997 + vertex -1.78778303 0.314121008 0.75454998 + vertex -1.79036307 0.333168 0.802253008 + endloop + endfacet + + facet normal -0.416385919 -0.908437848 -0.036924988 + outer loop + vertex -1.71201098 0.319709986 0.822552979 + vertex -1.74193907 0.33486101 0.787295997 + vertex -1.75035691 0.337029994 0.828857005 + endloop + endfacet + + facet normal 0.629848003 -0.584398985 -0.511633992 + outer loop + vertex -1.79036307 0.316971004 0.846754014 + vertex -1.83620691 0.296231002 0.814006984 + vertex -1.82835197 0.274188995 0.848854005 + endloop + endfacet + + facet normal 0.303186983 -0.895461917 -0.325922996 + outer loop + vertex -1.79036307 0.333168 0.802253008 + vertex -1.79036307 0.316971004 0.846754014 + vertex -1.75035691 0.337029994 0.828857005 + endloop + endfacet + + facet normal 0.629847765 -0.77654773 0.0162899923 + outer loop + vertex -1.83620691 0.296231002 0.814006984 + vertex -1.79036307 0.333168 0.802253008 + vertex -1.82835197 0.301744998 0.77314502 + endloop + endfacet + + facet normal 0.556288183 -0.780874252 -0.284216076 + outer loop + vertex -1.79036307 0.333168 0.802253008 + vertex -1.83620691 0.296231002 0.814006984 + vertex -1.79036307 0.316971004 0.846754014 + endloop + endfacet + + facet normal 0.813501 -0.390870959 -0.430622935 + outer loop + vertex -1.83620691 0.296231002 0.814006984 + vertex -1.86195993 0.238159001 0.818068981 + vertex -1.82835197 0.274188995 0.848854005 + endloop + endfacet + + facet normal 0.813501239 -0.576223135 0.0786300153 + outer loop + vertex -1.86195993 0.254355997 0.773567975 + vertex -1.83620691 0.296231002 0.814006984 + vertex -1.82835197 0.301744998 0.77314502 + endloop + endfacet + + facet normal 0.976355553 -0.203133896 -0.0739349648 + outer loop + vertex -1.86195993 0.238159001 0.818068981 + vertex -1.86195993 0.254355997 0.773567975 + vertex -1.87216401 0.202947006 0.780054986 + endloop + endfacet + + facet normal 0.900026679 -0.409550846 -0.14906396 + outer loop + vertex -1.86195993 0.254355997 0.773567975 + vertex -1.86195993 0.238159001 0.818068981 + vertex -1.83620691 0.296231002 0.814006984 + endloop + endfacet + + facet normal 0.733524799 -0.111328974 -0.670482814 + outer loop + vertex -1.86195993 0.238159001 0.818068981 + vertex -1.83203197 0.223009005 0.853326023 + vertex -1.82835197 0.274188995 0.848854005 + endloop + endfacet + + facet normal 0.934058905 0.0995580032 -0.342960984 + outer loop + vertex -1.85778403 0.181133002 0.812886 + vertex -1.86195993 0.238159001 0.818068981 + vertex -1.87216401 0.202947006 0.780054986 + endloop + endfacet + + facet normal 0.582925737 0.34976086 -0.733392715 + outer loop + vertex -1.83203197 0.223009005 0.853326023 + vertex -1.85778403 0.181133002 0.812886 + vertex -1.82124805 0.177171007 0.840036988 + endloop + endfacet + + facet normal 0.78092432 0.113006048 -0.614318252 + outer loop + vertex -1.85778403 0.181133002 0.812886 + vertex -1.83203197 0.223009005 0.853326023 + vertex -1.86195993 0.238159001 0.818068981 + endloop + endfacet + + facet normal 0.582937837 -0.203489929 0.786622763 + outer loop + vertex -1.85778403 0.207340002 0.740882993 + vertex -1.83203197 0.265412986 0.736820996 + vertex -1.82124805 0.221757993 0.717536986 + endloop + endfacet + + facet normal 0.934058607 -0.144184947 0.326718837 + outer loop + vertex -1.86195993 0.254355997 0.773567975 + vertex -1.85778403 0.207340002 0.740882993 + vertex -1.87216401 0.202947006 0.780054986 + endloop + endfacet + + facet normal 0.733523011 -0.516260922 0.442061961 + outer loop + vertex -1.83203197 0.265412986 0.736820996 + vertex -1.86195993 0.254355997 0.773567975 + vertex -1.82835197 0.301744998 0.77314502 + endloop + endfacet + + facet normal 0.780922234 -0.308308095 0.54323715 + outer loop + vertex -1.86195993 0.254355997 0.773567975 + vertex -1.83203197 0.265412986 0.736820996 + vertex -1.85778403 0.207340002 0.740882993 + endloop + endfacet + + facet normal -0.416389078 0.719636202 -0.555647135 + outer loop + vertex -1.75035691 -0.325998992 0.817825019 + vertex -1.71201098 -0.308679014 0.811520994 + vertex -1.74193907 -0.297621995 0.848267972 + endloop + endfacet + + facet normal 0.0543030165 0.722881198 -0.688835204 + outer loop + vertex -1.75035691 -0.325998992 0.817825019 + vertex -1.74193907 -0.297621995 0.848267972 + vertex -1.79036307 -0.305938989 0.835722029 + endloop + endfacet + + facet normal 0.418186873 0.411096871 -0.810011804 + outer loop + vertex -1.82835197 -0.263157994 0.83782202 + vertex -1.79036307 -0.305938989 0.835722029 + vertex -1.78778303 -0.260684997 0.860022008 + endloop + endfacet + + facet normal 0.147422016 0.461364061 -0.874877095 + outer loop + vertex -1.79036307 -0.305938989 0.835722029 + vertex -1.74193907 -0.297621995 0.848267972 + vertex -1.78778303 -0.260684997 0.860022008 + endloop + endfacet + + facet normal 0.471917987 0.110359997 -0.874708056 + outer loop + vertex -1.82835197 -0.263157994 0.83782202 + vertex -1.78778303 -0.260684997 0.860022008 + vertex -1.83203197 -0.211977005 0.842293978 + endloop + endfacet + + facet normal 0.488864958 -0.668645024 -0.560289919 + outer loop + vertex -1.82124805 -0.166140005 0.829005003 + vertex -1.82527602 -0.135903999 0.789408028 + vertex -1.85778403 -0.170101002 0.801854014 + endloop + endfacet + + facet normal 0.901277483 -0.416941255 -0.117724068 + outer loop + vertex -1.87216401 -0.191915005 0.769023001 + vertex -1.85778403 -0.170101002 0.801854014 + vertex -1.85520399 -0.151054993 0.754150987 + endloop + endfacet + + facet normal 0.416387081 -0.908437192 0.0369280092 + outer loop + vertex -1.81685805 -0.133735001 0.747847021 + vertex -1.85520399 -0.151054993 0.754150987 + vertex -1.82527602 -0.135903999 0.789408028 + endloop + endfacet + + facet normal 0.654929042 -0.713351011 -0.249396011 + outer loop + vertex -1.85778403 -0.170101002 0.801854014 + vertex -1.82527602 -0.135903999 0.789408028 + vertex -1.85520399 -0.151054993 0.754150987 + endloop + endfacet + + facet normal 0.901276588 -0.243724898 0.358187854 + outer loop + vertex -1.87216401 -0.191915005 0.769023001 + vertex -1.85520399 -0.151054993 0.754150987 + vertex -1.85778403 -0.196308002 0.729851007 + endloop + endfacet + + facet normal 0.488875985 -0.152067006 0.858997047 + outer loop + vertex -1.82124805 -0.210725993 0.706505001 + vertex -1.85778403 -0.196308002 0.729851007 + vertex -1.82527602 -0.162110999 0.717404008 + endloop + endfacet + + facet normal 0.416390151 -0.719640255 0.555641174 + outer loop + vertex -1.81685805 -0.133735001 0.747847021 + vertex -1.82527602 -0.162110999 0.717404008 + vertex -1.85520399 -0.151054993 0.754150987 + endloop + endfacet + + facet normal 0.65493387 -0.386147916 0.649577796 + outer loop + vertex -1.85778403 -0.196308002 0.729851007 + vertex -1.85520399 -0.151054993 0.754150987 + vertex -1.82527602 -0.162110999 0.717404008 + endloop + endfacet + + facet normal 0.289381891 0.326072842 0.899963617 + outer loop + vertex -1.82124805 -0.210725993 0.706505001 + vertex -1.78360796 -0.256074011 0.710832 + vertex -1.83203197 -0.254381001 0.725790024 + endloop + endfacet + + facet normal 0.471929044 0.646786034 0.599125087 + outer loop + vertex -1.82835197 -0.290713996 0.762113988 + vertex -1.83203197 -0.254381001 0.725790024 + vertex -1.78778303 -0.303088993 0.743517995 + endloop + endfacet + + facet normal 0.00501300115 0.570513129 0.821273208 + outer loop + vertex -1.74596691 -0.293594003 0.736666024 + vertex -1.78778303 -0.303088993 0.743517995 + vertex -1.78360796 -0.256074011 0.710832 + endloop + endfacet + + facet normal 0.266492993 0.534111917 0.80231297 + outer loop + vertex -1.83203197 -0.254381001 0.725790024 + vertex -1.78360796 -0.256074011 0.710832 + vertex -1.78778303 -0.303088993 0.743517995 + endloop + endfacet + + facet normal 0.418193072 0.835586071 0.356245041 + outer loop + vertex -1.82835197 -0.290713996 0.762113988 + vertex -1.78778303 -0.303088993 0.743517995 + vertex -1.79036307 -0.322136015 0.791221976 + endloop + endfacet + + facet normal 0.0543069914 0.996533871 0.06301599 + outer loop + vertex -1.75035691 -0.325998992 0.817825019 + vertex -1.79036307 -0.322136015 0.791221976 + vertex -1.74193907 -0.323828995 0.776265025 + endloop + endfacet + + facet normal -0.0790780187 0.788413107 0.610042095 + outer loop + vertex -1.74596691 -0.293594003 0.736666024 + vertex -1.74193907 -0.323828995 0.776265025 + vertex -1.78778303 -0.303088993 0.743517995 + endloop + endfacet + + facet normal 0.14742294 0.915790558 0.373622864 + outer loop + vertex -1.79036307 -0.322136015 0.791221976 + vertex -1.78778303 -0.303088993 0.743517995 + vertex -1.74193907 -0.323828995 0.776265025 + endloop + endfacet + + facet normal -0.416385919 0.908437848 -0.036924988 + outer loop + vertex -1.75035691 -0.325998992 0.817825019 + vertex -1.74193907 -0.323828995 0.776265025 + vertex -1.71201098 -0.308679014 0.811520994 + endloop + endfacet + + facet normal 0.629849017 0.584397912 -0.511633933 + outer loop + vertex -1.82835197 -0.263157994 0.83782202 + vertex -1.83620691 -0.285198987 0.802974999 + vertex -1.79036307 -0.305938989 0.835722029 + endloop + endfacet + + facet normal 0.30318898 0.895461917 -0.325920969 + outer loop + vertex -1.75035691 -0.325998992 0.817825019 + vertex -1.79036307 -0.305938989 0.835722029 + vertex -1.79036307 -0.322136015 0.791221976 + endloop + endfacet + + facet normal 0.629846632 0.776548624 0.016288992 + outer loop + vertex -1.82835197 -0.290713996 0.762113988 + vertex -1.79036307 -0.322136015 0.791221976 + vertex -1.83620691 -0.285198987 0.802974999 + endloop + endfacet + + facet normal 0.556288064 0.780875146 -0.28421402 + outer loop + vertex -1.79036307 -0.305938989 0.835722029 + vertex -1.83620691 -0.285198987 0.802974999 + vertex -1.79036307 -0.322136015 0.791221976 + endloop + endfacet + + facet normal 0.813500583 0.390871763 -0.430622727 + outer loop + vertex -1.82835197 -0.263157994 0.83782202 + vertex -1.86195993 -0.227127001 0.807036996 + vertex -1.83620691 -0.285198987 0.802974999 + endloop + endfacet + + facet normal 0.813501239 0.576223135 0.0786290169 + outer loop + vertex -1.82835197 -0.290713996 0.762113988 + vertex -1.83620691 -0.285198987 0.802974999 + vertex -1.86195993 -0.243323997 0.762535989 + endloop + endfacet + + facet normal 0.976355791 0.203132957 -0.0739349872 + outer loop + vertex -1.87216401 -0.191915005 0.769023001 + vertex -1.86195993 -0.243323997 0.762535989 + vertex -1.86195993 -0.227127001 0.807036996 + endloop + endfacet + + facet normal 0.900026679 0.409550846 -0.14906396 + outer loop + vertex -1.83620691 -0.285198987 0.802974999 + vertex -1.86195993 -0.227127001 0.807036996 + vertex -1.86195993 -0.243323997 0.762535989 + endloop + endfacet + + facet normal 0.733524799 0.111328974 -0.670482814 + outer loop + vertex -1.82835197 -0.263157994 0.83782202 + vertex -1.83203197 -0.211977005 0.842293978 + vertex -1.86195993 -0.227127001 0.807036996 + endloop + endfacet + + facet normal 0.934059024 -0.0995570049 -0.342961013 + outer loop + vertex -1.87216401 -0.191915005 0.769023001 + vertex -1.86195993 -0.227127001 0.807036996 + vertex -1.85778403 -0.170101002 0.801854014 + endloop + endfacet + + facet normal 0.582925141 -0.349761069 -0.733393192 + outer loop + vertex -1.82124805 -0.166140005 0.829005003 + vertex -1.85778403 -0.170101002 0.801854014 + vertex -1.83203197 -0.211977005 0.842293978 + endloop + endfacet + + facet normal 0.78092432 -0.113006048 -0.614318252 + outer loop + vertex -1.86195993 -0.227127001 0.807036996 + vertex -1.83203197 -0.211977005 0.842293978 + vertex -1.85778403 -0.170101002 0.801854014 + endloop + endfacet + + facet normal 0.582937121 0.203490049 0.78662318 + outer loop + vertex -1.82124805 -0.210725993 0.706505001 + vertex -1.83203197 -0.254381001 0.725790024 + vertex -1.85778403 -0.196308002 0.729851007 + endloop + endfacet + + facet normal 0.934058905 0.144184992 0.326718003 + outer loop + vertex -1.87216401 -0.191915005 0.769023001 + vertex -1.85778403 -0.196308002 0.729851007 + vertex -1.86195993 -0.243323997 0.762535989 + endloop + endfacet + + facet normal 0.733523011 0.516260922 0.442061961 + outer loop + vertex -1.82835197 -0.290713996 0.762113988 + vertex -1.86195993 -0.243323997 0.762535989 + vertex -1.83203197 -0.254381001 0.725790024 + endloop + endfacet + + facet normal 0.780922592 0.308307827 0.543236673 + outer loop + vertex -1.85778403 -0.196308002 0.729851007 + vertex -1.83203197 -0.254381001 0.725790024 + vertex -1.86195993 -0.243323997 0.762535989 + endloop + endfacet + +endsolid AssimpScene diff --git a/test/test.3mf b/test/test.3mf new file mode 100644 index 0000000000000000000000000000000000000000..eb1c49e1b1a6e71d1ce6c95fec699be6d1357051 GIT binary patch literal 1143 zcmWIWW@Zs#U|`^2NG@F(^g2*&^>HB2kCA~v7)VDu=jWBA=9R>UR2HNb$Ldw&=Cn?@ zoyBA*;BfzM*M#evX6JG*6$zKW^^5fvqjTA}rjsfvn-|r;cVTrDkWViv;yY7R`^8Vz z;HhT7>ie(Vv{j^cb+(1Q-!}VI^!o7br{6>jwG%a0hrOI=`A=f<+YY%RS*1yPzA9EG zNM24*x?#(cVK?j9=^G9`M?}TD#qP>~Gq4W(?|yT7$wal1$kq3?qnlDW4R@Vn;!ftf zV!LEj^s;ID#nt+Q&*U>@q|Pg?d2wMc&(Ga|Hh*>~=AZUlrF><}e1J z_*D%Enm{@jXum`_lwF)VeR+)@q90j ze)%;?xrEa`&hLEFWA_|rt7DpYw@`lX@z|qV)mQaBJg~ub#&3P;d#^tj$S^r&J+(gT ze<-NeJ_pI2+}JeZ zYOvJ(%O37YcJ8|SCO%)GvFUZ+&6f1CyI+_4=s)5&%zwLDZtY3M{`N+ht|^C^t;0J@ zB6Kf3jd$#6kZI&$G~?9~)DVpDW!7f7#-X#kk*86nu}BIiqBP+w->x5-JWt#6G=uW2%2=}-8b)XcBt z8@p%kPX^RDbw9sr=@wvIvH|0j8%W0&rREgt>w(Br|C3yY398+^D6OMJ$-L#Z{jj z-SdBw;_`#8bF#hy6D%WBGz0Fe3iKfoz>;y%4M0!x2;Dz`O!ORpt{Xjc5xUPXp@emS SH!B-Rk{Jkh0O^&?ARYi{r_oaY literal 0 HcmV?d00001 diff --git a/test/testExport.stl b/test/testExport.stl new file mode 100644 index 000000000..e7d06cefc --- /dev/null +++ b/test/testExport.stl @@ -0,0 +1,33 @@ + solid Assimp_Pointcloud + facet normal 0 0 0 + vertex 0 0 0 + vertex 0 0 0 + vertex 0 0 0 + vertex 1 1 1 + vertex 1 1 1 + vertex 1 1 1 + vertex 2 2 2 + vertex 2 2 2 + vertex 2 2 2 + vertex 3 3 3 + vertex 3 3 3 + vertex 3 3 3 + vertex 4 4 4 + vertex 4 4 4 + vertex 4 4 4 + vertex 5 5 5 + vertex 5 5 5 + vertex 5 5 5 + vertex 6 6 6 + vertex 6 6 6 + vertex 6 6 6 + vertex 7 7 7 + vertex 7 7 7 + vertex 7 7 7 + vertex 8 8 8 + vertex 8 8 8 + vertex 8 8 8 + vertex 9 9 9 + vertex 9 9 9 + vertex 9 9 9 +endsolid Assimp_Pointcloud diff --git a/test/unit/utStringUtils.cpp b/test/unit/utStringUtils.cpp index 6a10cba24..08be82e9a 100644 --- a/test/unit/utStringUtils.cpp +++ b/test/unit/utStringUtils.cpp @@ -42,9 +42,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include class utStringUtils : public ::testing::Test { + // empty }; -TEST_F( utStringUtils, to_string_Test ) { +TEST_F(utStringUtils, to_string_Test ) { std::string res = ai_to_string( 1 ); EXPECT_EQ( res, "1" ); @@ -52,7 +53,7 @@ TEST_F( utStringUtils, to_string_Test ) { EXPECT_EQ( res, "1" ); } -TEST_F( utStringUtils, ai_strtofTest ) { +TEST_F(utStringUtils, ai_strtofTest ) { float res = ai_strtof( nullptr, nullptr ); EXPECT_FLOAT_EQ( res, 0.0f ); @@ -66,3 +67,11 @@ TEST_F( utStringUtils, ai_strtofTest ) { res = ai_strtof( begin, end ); EXPECT_FLOAT_EQ( res, 200.0f ); } + +TEST_F(utStringUtils, ai_rgba2hexTest) { + std::string result; + result = ai_rgba2hex(255, 255, 255, 255, true); + EXPECT_EQ(result, "#ffffffff"); + result = ai_rgba2hex(0, 0, 0, 0, false); + EXPECT_EQ(result, "00000000"); +} From 153b890b02efaea35f5a73d3bb10f7576bce57a1 Mon Sep 17 00:00:00 2001 From: "Max Vollmer (Microsoft Havok)" <60260460+ms-maxvollmer@users.noreply.github.com> Date: Wed, 5 May 2021 14:09:43 +0100 Subject: [PATCH 066/335] Prevent accessing nullpointers --- code/AssetLib/glTF2/glTF2Importer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index f34b1b451..e6265946b 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1263,7 +1263,7 @@ aiMeshMorphAnim *CreateMeshMorphAnim(glTF2::Asset&, Node &node, AnimationSampler static const float kMillisecondsFromSeconds = 1000.f; - if (nullptr != samplers.weight) { + if (samplers.weight && samplers.weight->input && samplers.weight->output) { float *times = nullptr; samplers.weight->input->ExtractData(times); float *values = nullptr; From 9395322e56dcfafa2eb457a4e67ba675b1b2ddd7 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 5 May 2021 15:10:22 +0200 Subject: [PATCH 067/335] Delete AssimpLog_C.txt --- test/AssimpLog_C.txt | 3184 ------------------------------------------ 1 file changed, 3184 deletions(-) delete mode 100644 test/AssimpLog_C.txt diff --git a/test/AssimpLog_C.txt b/test/AssimpLog_C.txt deleted file mode 100644 index b843b6f07..000000000 --- a/test/AssimpLog_C.txt +++ /dev/null @@ -1,3184 +0,0 @@ -Info, T34284: Load dae.dae -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Collada Importer. -Info, T34284: Import root directory is '.\' -Debug, T34284: Collada schema version is 1.4.n -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: START `t1` -Debug, T34284: END `t1`, dt= 0.0064414 s -Info, T34284: Load D:/projects/assimp/test/models/OBJ/spider.obj -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 5 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_post_linear.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 5 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_xz_bezier.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 5 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_xz_stepped.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 5 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_oldformat_56.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 2 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_post_offset_repeat.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 5 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_xz_hermite.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 5 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_y_pre_ofrep_post_osc.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 5 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Warn, T34284: Validation warning: aiNodeAnim::mPositionKeys[1].mTime (-72.00000) is smaller than aiAnimation::mPositionKeys[0] (which is 0.00000) -Warn, T34284: Validation warning: aiNodeAnim::mPositionKeys[5].mTime (-21.00000) is smaller than aiAnimation::mPositionKeys[4] (which is 0.00000) -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_oldformat_6.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 3 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_post_repeat.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 5 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_xz_linear.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 5 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_post_constant.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 5 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_post_reset.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 5 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_xz_spline.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 5 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/LWO/LWO2/boxuv.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWO/LWO2\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/LWO/LWO2/formatDetection -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWO/LWO2\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/SMD/triangle.smd -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Valve SMD Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/SMD\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/SMD/holy_grailref.smd -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Valve SMD Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/SMD\' -Error, T34284: [SMD/VTA] Bone index overflow. The bone index will be ignored, the weight will be assigned to the vertex' parent node -Skipping one or more lines with the same contents -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/glTF/TwoBoxes/TwoBoxes.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: glTF Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF/TwoBoxes\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF/IncorrectVertexArrays/Cube_v1.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: glTF Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF/IncorrectVertexArrays\' -Warn, T34284: The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped. -Warn, T34284: The number of vertices was not compatible with the LINES mode. Some vertices were dropped. -Warn, T34284: The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped. -Warn, T34284: The number of vertices was not compatible with the LINES mode. Some vertices were dropped. -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Warn, T34284: Validation warning: There are unreferenced vertices -Skipping one or more lines with the same contents -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF/TwoBoxes/TwoBoxes.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: glTF Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF/TwoBoxes\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxTextured-glTF/BoxTextured.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxTextured-glTF\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 1 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/2CylinderEngine-glTF-Binary/2CylinderEngine.glb -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Reading GLTF2 binary -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/2CylinderEngine-glTF-Binary\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Reading GLTF2 binary -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 34 materials -Debug, T34284: Importing 29 meshes -Debug, T34284: Importing 1 cameras -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxTextured-glTF/BoxTextured.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxTextured-glTF\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 1 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: GenVertexNormalsProcess begin -Debug, T34284: GenVertexNormalsProcess finished. Normals are already there -Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxTextured-glTF-Embedded/BoxTextured.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxTextured-glTF-Embedded\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 1 embedded textures -Debug, T34284: Importing 1 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: GenVertexNormalsProcess begin -Debug, T34284: GenVertexNormalsProcess finished. Normals are already there -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_00.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_01.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_02.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_03.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_04.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_05.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_06.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_07.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_08.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_09.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_10.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_11.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_12.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/simple_skin/simple_skin.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/simple_skin\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 1 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Debug, T34284: ScenePreprocessor: Dummy position track has been generated -Info, T34284: Load D:/projects/assimp/test/models/glTF2/cameras/Cameras.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/cameras\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing 2 cameras -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Warn, T34284: Validation warning: 0.000000 is not a valid value for aiCamera::mHorizontalFOV -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/IncorrectVertexArrays/Cube.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/IncorrectVertexArrays\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 8 meshes -Warn, T34284: The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped. -Warn, T34284: The number of vertices was not compatible with the LINES mode. Some vertices were dropped. -Warn, T34284: The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped. -Warn, T34284: The number of vertices was not compatible with the LINES mode. Some vertices were dropped. -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Warn, T34284: Validation warning: There are unreferenced vertices -Skipping one or more lines with the same contents -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/textureTransform/TextureTransformTest.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/textureTransform\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 9 materials -Debug, T34284: Importing 9 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxTextured-glTF/BoxTextured.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxTextured-glTF\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 1 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: TriangulateProcess begin -Debug, T34284: TriangulateProcess finished. There was nothing to be done. -Debug, T34284: SortByPTypeProcess begin -Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) -Debug, T34284: SortByPTypeProcess finished -Debug, T34284: JoinVerticesProcess begin -Debug, T34284: Mesh 0 (Mesh) | Verts in: 24 out: 24 | ~0% -Debug, T34284: JoinVerticesProcess finished -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Sample-Models/AnimatedMorphCube-glTF/AnimatedMorphCube.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Sample-Models/AnimatedMorphCube-glTF\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 1 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 1 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: TriangulateProcess begin -Debug, T34284: TriangulateProcess finished. There was nothing to be done. -Debug, T34284: SortByPTypeProcess begin -Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) -Debug, T34284: SortByPTypeProcess finished -Debug, T34284: JoinVerticesProcess begin -Debug, T34284: Mesh 0 (Cube) | Verts in: 24 out: 24 | ~0% -Debug, T34284: JoinVerticesProcess finished -Info, T34284: Load D:/projects/assimp/test/models/glTF2/MissingBin/BoxTextured.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxWithInfinites-glTF-Binary/BoxWithInfinites.glb -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Reading GLTF2 binary -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxWithInfinites-glTF-Binary\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Reading GLTF2 binary -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 1 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: TriangulateProcess begin -Debug, T34284: TriangulateProcess finished. There was nothing to be done. -Debug, T34284: SortByPTypeProcess begin -Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) -Debug, T34284: SortByPTypeProcess finished -Debug, T34284: JoinVerticesProcess begin -Debug, T34284: Mesh 0 (Mesh) | Verts in: 24 out: 10 | ~58.3333% -Info, T34284: JoinVerticesProcess finished | Verts in: 24 out: 10 | ~58.3333 -Debug, T34284: TriangulateProcess begin -Debug, T34284: TriangulateProcess finished. There was nothing to be done. -Debug, T34284: SortByPTypeProcess begin -Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) -Debug, T34284: SortByPTypeProcess finished -Debug, T34284: JoinVerticesProcess begin -Debug, T34284: Mesh 0 (Mesh) | Verts in: 24 out: 10 | ~58.3333% -Info, T34284: JoinVerticesProcess finished | Verts in: 24 out: 10 | ~58.3333 -Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxBadNormals-glTF-Binary/BoxBadNormals.glb -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Reading GLTF2 binary -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxBadNormals-glTF-Binary\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Reading GLTF2 binary -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 1 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: TriangulateProcess begin -Debug, T34284: TriangulateProcess finished. There was nothing to be done. -Debug, T34284: SortByPTypeProcess begin -Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) -Debug, T34284: SortByPTypeProcess finished -Debug, T34284: JoinVerticesProcess begin -Debug, T34284: Mesh 0 (Mesh) | Verts in: 24 out: 24 | ~0% -Debug, T34284: JoinVerticesProcess finished -Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxBadNormals-glTF-Binary/BoxBadNormals_out.glb -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: (Deleting previous scene) -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Reading GLTF2 binary -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxBadNormals-glTF-Binary\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Reading GLTF2 binary -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 1 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxTextured-glTF/BoxTextured.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxTextured-glTF\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 1 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxTexcoords-glTF\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 1 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/RecursiveNodes/RecursiveNodes.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Load D:/projects/assimp/test/models/glTF2/TestNoRootNode/NoScene.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/TestNoRootNode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 0 meshes -Error, T34284: GLTF: No scene -Info, T34284: Load D:/projects/assimp/test/models/glTF2/TestNoRootNode/SceneWithoutNodes.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/TestNoRootNode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 0 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/issue_3269/texcoord_crash.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Load D:/projects/assimp/test/models/glTF2/IndexOutOfRange/IndexOutOfRange.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/IndexOutOfRange\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 1 materials -Debug, T34284: Importing 1 meshes -Warn, T34284: Some faces had out-of-range indices. Those faces were dropped. -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Warn, T34284: Validation warning: There are unreferenced vertices -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/IndexOutOfRange/AllIndicesOutOfRange.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/IndexOutOfRange\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 1 materials -Debug, T34284: Importing 1 meshes -Warn, T34284: Some faces had out-of-range indices. Those faces were dropped. -Error, T34284: Mesh "Mesh" has no faces -Info, T34284: Load D:/projects/assimp/test/models/glTF2/draco/2CylinderEngine.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Load D:/projects/assimp/test/models/glTF2/wrongTypes/badArray.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Load D:/projects/assimp/test/models/glTF2/wrongTypes/badString.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Load D:/projects/assimp/test/models/glTF2/wrongTypes/badUint.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Load D:/projects/assimp/test/models/glTF2/wrongTypes/badNumber.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Load D:/projects/assimp/test/models/glTF2/wrongTypes/badObject.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Load D:/projects/assimp/test/models/glTF2/wrongTypes/badExtension.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Load D:/projects/assimp/test/models/HMP/terrain.hmp -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: 3D GameStudio Heightmap (HMP) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/HMP\' -Debug, T34284: HMP subtype: 3D GameStudio A7, magic word is HMP7 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/IFC/AC14-FZK-Haus.ifc -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Industry Foundation Classes (IFC) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/IFC\' -Debug, T34284: IFC: File schema is 'IFC2X3' -Debug, T34284: STEP: got 82226 object records with 5788 inverse index entries -Debug, T34284: IFC: got units used for lengths -Debug, T34284: IFC: got units used for angles -Debug, T34284: IFC: got world coordinate system -Debug, T34284: IFC: looking at spatial structure `Gelände` -Debug, T34284: IFC: selecting this spatial structure as root structure -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Skipping one or more lines with the same contents -Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline -Debug, T34284: IFC: removing duplicate vertices -Error, T34284: IFC: failed to generate all window caps -Skipping one or more lines with the same contents -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: removing degenerate faces -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Skipping one or more lines with the same contents -Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline -Debug, T34284: IFC: removing duplicate vertices -Error, T34284: IFC: failed to generate all window caps -Skipping one or more lines with the same contents -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: removing degenerate faces -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Skipping one or more lines with the same contents -Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline -Debug, T34284: IFC: removing duplicate vertices -Error, T34284: IFC: failed to generate all window caps -Skipping one or more lines with the same contents -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Skipping one or more lines with the same contents -Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline -Debug, T34284: IFC: removing duplicate vertices -Error, T34284: IFC: failed to generate all window caps -Skipping one or more lines with the same contents -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Skipping one or more lines with the same contents -Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline -Debug, T34284: IFC: removing duplicate vertices -Error, T34284: IFC: failed to generate all window caps -Skipping one or more lines with the same contents -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Skipping one or more lines with the same contents -Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline -Debug, T34284: IFC: removing duplicate vertices -Error, T34284: IFC: failed to generate all window caps -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: removing degenerate faces -Skipping one or more lines with the same contents -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: removing degenerate faces -Skipping one or more lines with the same contents -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: skipping IfcAnnotation entity due to importer settings -Skipping one or more lines with the same contents -Debug, T34284: IFC: skipping IfcSpace entity due to importer settings -Skipping one or more lines with the same contents -Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: generating CSG geometry by plane clipping (IfcBooleanClippingResult) -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline -Debug, T34284: IFC: removing duplicate vertices -Error, T34284: IFC: failed to generate all window caps -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: generating CSG geometry by plane clipping with polygonal bounding (IfcBooleanClippingResult) -Skipping one or more lines with the same contents -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: removing degenerate faces -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: removing degenerate faces -Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: generating CSG geometry by plane clipping (IfcBooleanClippingResult) -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline -Debug, T34284: IFC: removing duplicate vertices -Error, T34284: IFC: failed to generate all window caps -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: generating CSG geometry by plane clipping with polygonal bounding (IfcBooleanClippingResult) -Skipping one or more lines with the same contents -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: removing degenerate faces -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: generating CSG geometry by plane clipping (IfcBooleanClippingResult) -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: generating CSG geometry by plane clipping (IfcBooleanClippingResult) -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Skipping one or more lines with the same contents -Debug, T34284: IFC: generating CSG geometry by plane clipping (IfcBooleanClippingResult) -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: generating CSG geometry by plane clipping (IfcBooleanClippingResult) -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: removing duplicate vertices -Warn, T34284: IFC: failed to resolve all openings, presumably their topology is not supported by Assimp -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: removing degenerate faces -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: skipping IfcAnnotation entity due to importer settings -Skipping one or more lines with the same contents -Debug, T34284: IFC: skipping IfcSpace entity due to importer settings -Debug, T34284: IFC: STEP: evaluated 70094 object records -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load $$$___magic___$$$. -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Found positive match for header keyword: HEADER -Info, T34284: Found a matching importer for this file format: Drawing Interchange Format (DXF) Importer. -Info, T34284: Import root directory is '.\' -Warn, T34284: DXF: EOF reached, but did not encounter DXF EOF marker -Debug, T34284: DXF: Unexpanded polycount is 0, vertex count is 0 -Error, T34284: DXF: no data blocks loaded -Info, T34284: Load D:/projects/assimp/test/models/FBX/spider.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing binary FBX file -Debug, T34284: FBX version: 7400 -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7400 -Debug, T34284: UpdateImporterScale scale set: 0.01 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/box.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing binary FBX file -Debug, T34284: FBX version: 7400 -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7400 -Info, T34284: FBX: generating full transformation chain for node: root -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/cubes_nonames.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing ASCII FBX file -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7500 -Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless -Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 -Debug, T34284: UpdateImporterScale scale set: 0.01 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/cubes_with_names.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing ASCII FBX file -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7500 -Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless -Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 -Debug, T34284: UpdateImporterScale scale set: 0.01 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/cubes_with_mirroring_and_pivot.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing ASCII FBX file -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7500 -Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless -Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 -Info, T34284: FBX: generating full transformation chain for node: Cube1 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/close_to_identity_transforms.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing ASCII FBX file -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7500 -Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless -Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 -Info, T34284: FBX: generating full transformation chain for node: Cube1 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/phong_cube.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing binary FBX file -Debug, T34284: FBX version: 7400 -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7400 -Warn, T34284: FBX-DOM (TOK_KEY, offset 0x4190) source object for connection does not exist -Debug, T34284: UpdateImporterScale scale set: 0.01 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/global_settings.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing binary FBX file -Debug, T34284: FBX version: 7400 -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7400 -Error, T34284: FBX: no material assigned to mesh, setting default material -Debug, T34284: UpdateImporterScale scale set: 5 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/embedded_ascii/box.FBX -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX/embedded_ascii\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing ASCII FBX file -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7400 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Warn, T34284: Validation warning: A specular shading model is specified but the value of the AI_MATKEY_SHININESS_STRENGTH key is 0.0 -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/embedded_ascii/box_embedded_texture_fragmented.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX/embedded_ascii\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing ASCII FBX file -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7500 -Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless -Error, T34284: FBX: ignoring additional binormal layer -Error, T34284: FBX: ignoring additional tangent layer -Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/box_orphant_embedded_texture.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing ASCII FBX file -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7500 -Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless -Error, T34284: FBX: ignoring additional binormal layer -Error, T34284: FBX: ignoring additional tangent layer -Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/global_settings.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing binary FBX file -Debug, T34284: FBX version: 7400 -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7400 -Error, T34284: FBX: no material assigned to mesh, setting default material -Debug, T34284: UpdateImporterScale scale set: 5 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/cubes_with_outofrange_float.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing ASCII FBX file -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7500 -Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless -Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 -Debug, T34284: UpdateImporterScale scale set: 0.01 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/maxPbrMaterial_metalRough.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing ASCII FBX file -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7700 -Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless -Warn, T34284: FBX-DOM (TOK_KEY, line 679, col 13) shading mode not recognized: unknown -Info, T34284: FBX: generating full transformation chain for node: Box001 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/maxPbrMaterial_specGloss.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing ASCII FBX file -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7700 -Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless -Warn, T34284: FBX-DOM (TOK_KEY, line 679, col 13) shading mode not recognized: unknown -Info, T34284: FBX: generating full transformation chain for node: Box001 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load $$$___magic___$$$.3ds -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Discreet 3DS Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: 3DS file format version: 3 -Warn, T34284: 3DS: Skipping TCB animation info -Skipping one or more lines with the same contents -Error, T34284: 3DS: Skipping FOV animation track. This is not supported -Warn, T34284: 3DS: Skipping TCB animation info -Skipping one or more lines with the same contents -Info, T34284: 3DS: Generating default material -Debug, T34284: 3DS: Converting camera roll track ... -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Debug, T34284: ScenePreprocessor: Setting animation duration -Info, T34284: Entering post processing pipeline -Debug, T34284: RemoveRedundantMatsProcess begin -Debug, T34284: RemoveRedundantMatsProcess finished -Debug, T34284: GenUVCoordsProcess begin -Debug, T34284: GenUVCoordsProcess finished -Debug, T34284: TriangulateProcess begin -Debug, T34284: TriangulateProcess finished. There was nothing to be done. -Debug, T34284: FindDegeneratesProcess begin -Debug, T34284: FindDegeneratesProcess finished -Debug, T34284: SortByPTypeProcess begin -Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) -Debug, T34284: SortByPTypeProcess finished -Debug, T34284: FindInvalidDataProcess begin -Info, T34284: FindInvalidDataProcess finished. Found issues ... -Debug, T34284: SplitLargeMeshesProcess_Triangle begin -Debug, T34284: SplitLargeMeshesProcess_Triangle finished. There was nothing to do -Debug, T34284: Generate spatially-sorted vertex cache -Debug, T34284: GenVertexNormalsProcess begin -Debug, T34284: GenVertexNormalsProcess finished. Normals are already there -Debug, T34284: CalcTangentsProcess begin -Info, T34284: CalcTangentsProcess finished. Tangents have been calculated -Debug, T34284: JoinVerticesProcess begin -Debug, T34284: Mesh 0 (0) | Verts in: 36 out: 24 | ~33.3333% -Info, T34284: JoinVerticesProcess finished | Verts in: 36 out: 24 | ~33.3333 -Debug, T34284: SplitLargeMeshesProcess_Vertex begin -Debug, T34284: SplitLargeMeshesProcess_Vertex finished. There was nothing to do -Debug, T34284: LimitBoneWeightsProcess begin -Debug, T34284: LimitBoneWeightsProcess end -Debug, T34284: ImproveCacheLocalityProcess begin -Debug, T34284: Mesh %u | ACMR in: 0 out: 2 | ~20 -Info, T34284: Cache relevant are 1 meshes (12 faces). Average output ACMR is 2 -Debug, T34284: ImproveCacheLocalityProcess finished. -Info, T34284: Leaving post processing pipeline -Info, T34284: Registering custom importer for these file extensions: applelinuxmacwindows -Info, T34284: Unregistering custom importer: -Info, T34284: Load D:/projects/assimp/test/models/X/test.x -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Warn, T34284: Unknown data object in mesh in x file -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Entering post processing pipeline -Debug, T34284: RemoveRedundantMatsProcess begin -Debug, T34284: RemoveRedundantMatsProcess finished -Debug, T34284: OptimizeGraphProcess begin -Debug, T34284: OptimizeGraphProcess finished -Debug, T34284: GenUVCoordsProcess begin -Debug, T34284: GenUVCoordsProcess finished -Debug, T34284: TriangulateProcess begin -Debug, T34284: TriangulateProcess finished. There was nothing to be done. -Debug, T34284: FindDegeneratesProcess begin -Debug, T34284: FindDegeneratesProcess finished -Debug, T34284: SortByPTypeProcess begin -Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) -Debug, T34284: SortByPTypeProcess finished -Debug, T34284: FindInvalidDataProcess begin -Info, T34284: FindInvalidDataProcess finished. Found issues ... -Debug, T34284: Skipping OptimizeMeshesProcess -Debug, T34284: Generate spatially-sorted vertex cache -Debug, T34284: GenVertexNormalsProcess begin -Debug, T34284: GenVertexNormalsProcess finished. Normals are already there -Debug, T34284: JoinVerticesProcess begin -Debug, T34284: Mesh 0 (pCube1) | Verts in: 36 out: 24 | ~33.3333% -Info, T34284: JoinVerticesProcess finished | Verts in: 36 out: 24 | ~33.3333 -Info, T34284: Leaving post processing pipeline -Info, T34284: Load D:/projects/assimp/test/models/X/Testwuson.X -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: (Deleting previous scene) -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Warn, T34284: Unknown data object in animation of .x file -Skipping one or more lines with the same contents -Warn, T34284: Unknown data object in frame in x file -Skipping one or more lines with the same contents -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Entering post processing pipeline -Debug, T34284: RemoveRedundantMatsProcess begin -Debug, T34284: RemoveRedundantMatsProcess finished -Debug, T34284: OptimizeGraphProcess begin -Debug, T34284: OptimizeGraphProcess finished -Debug, T34284: GenUVCoordsProcess begin -Debug, T34284: GenUVCoordsProcess finished -Debug, T34284: TriangulateProcess begin -Debug, T34284: TriangulateProcess finished. There was nothing to be done. -Debug, T34284: FindDegeneratesProcess begin -Debug, T34284: FindDegeneratesProcess finished -Debug, T34284: SortByPTypeProcess begin -Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) -Debug, T34284: SortByPTypeProcess finished -Debug, T34284: FindInvalidDataProcess begin -Info, T34284: FindInvalidDataProcess finished. Found issues ... -Debug, T34284: Skipping OptimizeMeshesProcess -Debug, T34284: Generate spatially-sorted vertex cache -Debug, T34284: GenVertexNormalsProcess begin -Debug, T34284: GenVertexNormalsProcess finished. Normals are already there -Debug, T34284: JoinVerticesProcess begin -Debug, T34284: Mesh 0 (Wuson) | Verts in: 11196 out: 3205 | ~71.3737% -Info, T34284: JoinVerticesProcess finished | Verts in: 11196 out: 3205 | ~71.3737 -Info, T34284: Leaving post processing pipeline -Info, T34284: Load D:/projects/assimp/test/models/X/anim_test.x -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: (Deleting previous scene) -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Warn, T34284: Length of texture file name is zero. Skipping this texture. -Warn, T34284: Unknown data object in mesh in x file -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Entering post processing pipeline -Debug, T34284: RemoveRedundantMatsProcess begin -Debug, T34284: RemoveRedundantMatsProcess finished -Debug, T34284: OptimizeGraphProcess begin -Debug, T34284: OptimizeGraphProcess finished -Debug, T34284: GenUVCoordsProcess begin -Debug, T34284: GenUVCoordsProcess finished -Debug, T34284: TriangulateProcess begin -Debug, T34284: TriangulateProcess finished. There was nothing to be done. -Debug, T34284: FindDegeneratesProcess begin -Debug, T34284: FindDegeneratesProcess finished -Debug, T34284: SortByPTypeProcess begin -Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) -Debug, T34284: SortByPTypeProcess finished -Debug, T34284: FindInvalidDataProcess begin -Warn, T34284: Simplified dummy tracks with just one key -Skipping one or more lines with the same contents -Info, T34284: FindInvalidDataProcess finished. Found issues ... -Debug, T34284: Skipping OptimizeMeshesProcess -Debug, T34284: Generate spatially-sorted vertex cache -Debug, T34284: GenVertexNormalsProcess begin -Debug, T34284: GenVertexNormalsProcess finished. Normals are already there -Debug, T34284: JoinVerticesProcess begin -Debug, T34284: Mesh 0 (pCylinder1) | Verts in: 2520 out: 485 | ~80.754% -Info, T34284: JoinVerticesProcess finished | Verts in: 2520 out: 485 | ~80.754 -Info, T34284: Leaving post processing pipeline -Info, T34284: Load D:/projects/assimp/test/models/X/anim_test.x -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: (Deleting previous scene) -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Warn, T34284: Length of texture file name is zero. Skipping this texture. -Warn, T34284: Unknown data object in mesh in x file -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Entering post processing pipeline -Debug, T34284: RemoveRedundantMatsProcess begin -Debug, T34284: RemoveRedundantMatsProcess finished -Debug, T34284: OptimizeGraphProcess begin -Debug, T34284: OptimizeGraphProcess finished -Debug, T34284: GenUVCoordsProcess begin -Debug, T34284: GenUVCoordsProcess finished -Debug, T34284: TriangulateProcess begin -Debug, T34284: TriangulateProcess finished. There was nothing to be done. -Debug, T34284: FindDegeneratesProcess begin -Debug, T34284: FindDegeneratesProcess finished -Debug, T34284: SortByPTypeProcess begin -Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) -Debug, T34284: SortByPTypeProcess finished -Debug, T34284: FindInvalidDataProcess begin -Warn, T34284: Simplified dummy tracks with just one key -Skipping one or more lines with the same contents -Info, T34284: FindInvalidDataProcess finished. Found issues ... -Debug, T34284: Skipping OptimizeMeshesProcess -Debug, T34284: Generate spatially-sorted vertex cache -Debug, T34284: GenVertexNormalsProcess begin -Debug, T34284: GenVertexNormalsProcess finished. Normals are already there -Debug, T34284: JoinVerticesProcess begin -Debug, T34284: Mesh 0 (pCylinder1) | Verts in: 2520 out: 485 | ~80.754% -Info, T34284: JoinVerticesProcess finished | Verts in: 2520 out: 485 | ~80.754 -Info, T34284: Leaving post processing pipeline -Info, T34284: Load D:/projects/assimp/test/models/X/BCN_Epileptic.X -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: (Deleting previous scene) -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Warn, T34284: Unknown data object in animation of .x file -Skipping one or more lines with the same contents -Warn, T34284: Unknown data object in frame in x file -Skipping one or more lines with the same contents -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Entering post processing pipeline -Debug, T34284: RemoveRedundantMatsProcess begin -Debug, T34284: RemoveRedundantMatsProcess finished -Debug, T34284: OptimizeGraphProcess begin -Debug, T34284: OptimizeGraphProcess finished -Debug, T34284: GenUVCoordsProcess begin -Debug, T34284: GenUVCoordsProcess finished -Debug, T34284: TriangulateProcess begin -Debug, T34284: TriangulateProcess finished. There was nothing to be done. -Debug, T34284: FindDegeneratesProcess begin -Debug, T34284: FindDegeneratesProcess finished -Debug, T34284: SortByPTypeProcess begin -Info, T34284: Points: 0, Lines: 0, Triangles: 3, Polygons: 0 (Meshes, X = removed) -Debug, T34284: SortByPTypeProcess finished -Debug, T34284: FindInvalidDataProcess begin -Info, T34284: FindInvalidDataProcess finished. Found issues ... -Debug, T34284: OptimizeMeshesProcess begin -Debug, T34284: OptimizeMeshesProcess finished -Debug, T34284: Generate spatially-sorted vertex cache -Debug, T34284: GenVertexNormalsProcess begin -Debug, T34284: GenVertexNormalsProcess finished. Normals are already there -Debug, T34284: JoinVerticesProcess begin -Debug, T34284: Mesh 0 (Torso) | Verts in: 5898 out: 1170 | ~80.1628% -Debug, T34284: Mesh 1 (Head) | Verts in: 6108 out: 1196 | ~80.4191% -Debug, T34284: Mesh 2 (Legs) | Verts in: 3372 out: 648 | ~80.7829% -Info, T34284: JoinVerticesProcess finished | Verts in: 15378 out: 3014 | ~80.4006 -Info, T34284: Leaving post processing pipeline -Info, T34284: Registering custom importer for these file extensions: fail -Info, T34284: Load deadlyImportError.fail -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Failing importer. -Info, T34284: Import root directory is './' -Error, T34284: Deadly import error test. Details: 42 More Details: Failure -Info, T34284: Registering custom importer for these file extensions: fail -Info, T34284: Load stdException.fail -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Failing importer. -Info, T34284: Import root directory is './' -Error, T34284: std::exception test -Info, T34284: Registering custom importer for these file extensions: fail -Info, T34284: Load unexpectedException.fail -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Failing importer. -Info, T34284: Import root directory is './' -Info, T34284: Load D:/projects/assimp/test/models/3D/box_a.3d -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Unreal Mesh Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3D\' -Debug, T34284: UNREAL: data file is D:/projects/assimp/test/models/3D/box_d.3d -Debug, T34284: UNREAL: aniv file is D:/projects/assimp/test/models/3D/box_a.3d -Debug, T34284: UNREAL: uc file is D:/projects/assimp/test/models/3D/box.uc -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/3D/box_d.3d -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Unreal Mesh Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3D\' -Debug, T34284: UNREAL: data file is D:/projects/assimp/test/models/3D/box_d.3d -Debug, T34284: UNREAL: aniv file is D:/projects/assimp/test/models/3D/box_a.3d -Debug, T34284: UNREAL: uc file is D:/projects/assimp/test/models/3D/box.uc -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/3D/box.uc -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Unreal Mesh Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3D\' -Debug, T34284: UNREAL: data file is D:/projects/assimp/test/models/3D/box_d.3d -Debug, T34284: UNREAL: aniv file is D:/projects/assimp/test/models/3D/box_a.3d -Debug, T34284: UNREAL: uc file is D:/projects/assimp/test/models/3D/box.uc -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/3DS/fels.3ds -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Discreet 3DS Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3DS\' -Info, T34284: 3DS file format version: 3 -Warn, T34284: No hierarchy information has been found in the file. -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/3DS/testFormatDetection -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Discreet 3DS Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3DS\' -Info, T34284: 3DS file format version: 3 -Warn, T34284: 3DS: Skipping TCB animation info -Skipping one or more lines with the same contents -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/AC/closedLine.ac -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: AC3D Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' -Info, T34284: AC3D file format version: 11 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/AC/nosurfaces.ac -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: AC3D Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' -Info, T34284: AC3D file format version: 11 -Warn, T34284: AC3D: No material has been found -Info, T34284: AC3D: No surfaces defined in object definition, a point list is returned -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/AC/openLine.ac -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: AC3D Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' -Info, T34284: AC3D file format version: 11 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/AC/sample_subdiv.ac -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: AC3D Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' -Info, T34284: AC3D file format version: 11 -Info, T34284: AC3D: Evaluating subdivision surface: cylinder -Debug, T34284: Catmull-Clark Subdivider: got 130 bad edges touching only one face (totally 61505 edges). -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/AC/SphereWithLight.ac -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: AC3D Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' -Info, T34284: AC3D file format version: 11 -Debug, T34284: AC3D: Light source encountered -Info, T34284: AC3D: Evaluating subdivision surface: sphere -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/AC/SphereWithLight_UTF16LE.ac -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: AC3D Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' -Debug, T34284: Found UTF-16 BOM ... -Error, T34284: AC3D: No valid AC3D file, magic sequence not found -Info, T34284: Load D:/projects/assimp/test/models/AC/SphereWithLight_UTF8BOM.ac -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: AC3D Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' -Debug, T34284: Found UTF-8 BOM ... -Info, T34284: AC3D file format version: 11 -Debug, T34284: AC3D: Light source encountered -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/AC/SphereWithLightUvScaling4X.ac -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: AC3D Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' -Info, T34284: AC3D file format version: 11 -Debug, T34284: AC3D: Light source encountered -Info, T34284: AC3D: Evaluating subdivision surface: sphere -Debug, T34284: Catmull-Clark Subdivider: got 12 bad edges touching only one face (totally 17670 edges). -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/AC/Wuson.ac -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: AC3D Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' -Info, T34284: AC3D file format version: 11 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/AC/Wuson.acc -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: AC3D Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' -Info, T34284: AC3D file format version: 11 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/AC/TestFormatDetection -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: AC3D Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' -Info, T34284: AC3D file format version: 11 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/AMF/test1.amf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Additive manufacturing file format(AMF) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AMF\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Adding default material 'DefaultMaterial' -Info, T34284: Load D:/projects/assimp/test/models/AMF/test_with_mat.amf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Additive manufacturing file format(AMF) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AMF\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Adding default material 'DefaultMaterial' -Info, T34284: Load D:/projects/assimp/test/models/ASE/ThreeCubesGreen.ASE -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: ASE Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/ASE\' -Info, T34284: Line 1: Comment: AsciiExport-Version 2,00 - Tue May 06 17:21:38 2008 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/3MF/box.3mf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: 3mf Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3MF\' -Warn, T34284: Ignored file of unknown type: 3D/3dmodel.model -Warn, T34284: Ignored file of unsupported type CONTENT_TYPES_ARCHIVES[Content_Types].xml -Debug, T34284: 3D/3dmodel.model -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Adding default material 'DefaultMaterial' -Info, T34284: Load D:/projects/assimp/test/models/3MF/box.3mf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: 3mf Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3MF\' -Warn, T34284: Ignored file of unknown type: 3D/3dmodel.model -Warn, T34284: Ignored file of unsupported type CONTENT_TYPES_ARCHIVES[Content_Types].xml -Debug, T34284: 3D/3dmodel.model -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ScenePreprocessor: Adding default material 'DefaultMaterial' -Info, T34284: Load D:/projects/assimp/test/models/3MF/box.3mf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: 3mf Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3MF\' -Warn, T34284: Ignored file of unknown type: 3D/3dmodel.model -Warn, T34284: Ignored file of unsupported type CONTENT_TYPES_ARCHIVES[Content_Types].xml -Debug, T34284: 3D/3dmodel.model -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ScenePreprocessor: Adding default material 'DefaultMaterial' -Info, T34284: Load test.3mf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: 3mf Importer. -Info, T34284: Import root directory is '.\' -Warn, T34284: Ignored file of unknown type: 3D/3DModel.model -Warn, T34284: Ignored file of unsupported type CONTENT_TYPES_ARCHIVES[Content_Types].xml -Debug, T34284: 3D/3DModel.model -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: Load D:/projects/assimp/test/models/Q3D/earth.q3o -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Quick3D Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/Q3D\' -Info, T34284: Quick3D File format version: 30 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/STL/Spider_ascii.stl -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/STL/Spider_ascii.stl -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/STL/Spider_ascii.stl -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/STL/formatDetection -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Found positive match for header keyword: solid -Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/STL/triangle_with_two_solids.stl -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/STL/triangle_with_empty_solid.stl -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' -Warn, T34284: STL: mesh is empty or invalid; no data loaded -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: Load D:/projects/assimp/test/models/STL/triangle_with_empty_solid.stl -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: (Deleting previous scene) -Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' -Warn, T34284: STL: mesh is empty or invalid; no data loaded -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Error, T34284: Validation failed: The mesh emptySolid contains no vertices -Info, T34284: Load D:/projects/assimp/test/models/STL/Spider_ascii.stl -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: PretransformVerticesProcess begin -Debug, T34284: PretransformVerticesProcess finished -Info, T34284: Removed 2 nodes and 0 animation channels (1 output nodes) -Info, T34284: Kept 0 lights and 0 cameras. -Info, T34284: Moved 1 meshes to WCS (number of output meshes: 1) -Debug, T34284: TriangulateProcess begin -Debug, T34284: TriangulateProcess finished. There was nothing to be done. -Debug, T34284: GenFaceNormalsProcess begin -Debug, T34284: GenFaceNormalsProcess finished. Normals are already there -Info, T34284: Load spiderExport.stl -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: (Deleting previous scene) -Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. -Info, T34284: Import root directory is '.\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: TriangulateProcess begin -Debug, T34284: TriangulateProcess finished. There was nothing to be done. -Debug, T34284: GenFaceNormalsProcess begin -Info, T34284: Normal vectors are undefined for line and point meshes -Debug, T34284: GenFaceNormalsProcess finished. Normals are already there -Info, T34284: Load D:/projects/assimp/test/models/X/test.x -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Warn, T34284: Unknown data object in mesh in x file -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/X/OV_GetNextToken -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Info, T34284: Successfully decompressed MSZIP-compressed file -Error, T34284: Opening brace expected. -Info, T34284: Load D:/projects/assimp/test/models/X/anim_test.x -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Warn, T34284: Length of texture file name is zero. Skipping this texture. -Warn, T34284: Unknown data object in mesh in x file -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/X/BCN_Epileptic.X -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Warn, T34284: Unknown data object in animation of .x file -Skipping one or more lines with the same contents -Warn, T34284: Unknown data object in frame in x file -Skipping one or more lines with the same contents -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/X/fromtruespace_bin32.x -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Warn, T34284: Unknown data object in animation of .x file -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/X/kwxport_test_cubewithvcolors.x -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Warn, T34284: Unknown data object in animation of .x file -Skipping one or more lines with the same contents -Warn, T34284: Unknown data object in frame in x file -Warn, T34284: Unknown data object in mesh in x file -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/X/test_cube_binary.x -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/X/test_cube_compressed.x -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Info, T34284: Successfully decompressed MSZIP-compressed file -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/X/test_cube_text.x -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/X/Testwuson.X -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Warn, T34284: Unknown data object in animation of .x file -Skipping one or more lines with the same contents -Warn, T34284: Unknown data object in frame in x file -Skipping one or more lines with the same contents -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/X/TestFormatDetection -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Warn, T34284: Unknown data object in mesh in x file -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models-nonbsd/X/dwarf.x -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models-nonbsd/X\' -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Skipping one or more lines with the same contents -Info, T34284: Load D:/projects/assimp/test/models/X3D/ComputerKeyboard.x3d -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: File extension not known, trying signature-based detection -Info, T34284: Found a matching importer for this file format: Extensible 3D(X3D) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X3D\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Error, T34284: Validation failed: aiScene::mNumMeshes is 0. At least one mesh must be there -Info, T34284: Load D:/projects/assimp/test/models/DXF/PinkEggFromLW.dxf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Drawing Interchange Format (DXF) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/DXF\' -Debug, T34284: DXF: got 0 entries in BLOCKS -Debug, T34284: DXF: got 288 polylines and 0 inserted blocks in ENTITIES -Debug, T34284: DXF: Unexpanded polycount is 288, vertex count is 1152 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/DXF/lineTest -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Found positive match for header keyword: SECTION -Info, T34284: Found a matching importer for this file format: Drawing Interchange Format (DXF) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/DXF\' -Info, T34284: DXF Comment: VISION3D DXF -Debug, T34284: DXF: got 0 entries in BLOCKS -Debug, T34284: DXF: got 3 polylines and 0 inserted blocks in ENTITIES -Debug, T34284: DXF: Unexpanded polycount is 3, vertex count is 9 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/DXF/issue_2229.dxf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Drawing Interchange Format (DXF) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/DXF\' -Debug, T34284: DXF: skipped over control group (273 lines) -Debug, T34284: DXF: got 2 entries in BLOCKS -Warn, T34284: DXF: invalid vertex index, indices are one-based. -Skipping one or more lines with the same contents -Warn, T34284: DXF: unexpected face count in polymesh: 1173, expected 1174 -Warn, T34284: DXF: invalid vertex index, indices are one-based. -Skipping one or more lines with the same contents -Debug, T34284: DXF: got 4 polylines and 0 inserted blocks in ENTITIES -Debug, T34284: DXF: skipped over control group (3 lines) -Skipping one or more lines with the same contents -Debug, T34284: DXF: Unexpanded polycount is 1289, vertex count is 768 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/PLY/cube.ply -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' -Debug, T34284: PLY::DOM::ParseInstance() begin -Debug, T34284: PLY::DOM::ParseHeader() begin -Debug, T34284: PLY::DOM::ParseHeader() succeeded -Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin -Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded -Debug, T34284: PLY::DOM::ParseInstance() succeeded -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/PLY/cube.ply -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' -Debug, T34284: PLY::DOM::ParseInstance() begin -Debug, T34284: PLY::DOM::ParseHeader() begin -Debug, T34284: PLY::DOM::ParseHeader() succeeded -Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin -Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded -Debug, T34284: PLY::DOM::ParseInstance() succeeded -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: PretransformVerticesProcess begin -Debug, T34284: PretransformVerticesProcess finished -Info, T34284: Removed 1 nodes and 0 animation channels (1 output nodes) -Info, T34284: Kept 0 lights and 0 cameras. -Info, T34284: Moved 1 meshes to WCS (number of output meshes: 1) -Info, T34284: Load D:/projects/assimp/test/models/PLY/cube.ply -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' -Debug, T34284: PLY::DOM::ParseInstance() begin -Debug, T34284: PLY::DOM::ParseHeader() begin -Debug, T34284: PLY::DOM::ParseHeader() succeeded -Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin -Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded -Debug, T34284: PLY::DOM::ParseInstance() succeeded -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/PLY/cube.ply -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: (Deleting previous scene) -Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' -Debug, T34284: PLY::DOM::ParseInstance() begin -Debug, T34284: PLY::DOM::ParseHeader() begin -Debug, T34284: PLY::DOM::ParseHeader() succeeded -Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin -Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded -Debug, T34284: PLY::DOM::ParseInstance() succeeded -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/PLY/cube_uv.ply -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' -Debug, T34284: PLY::DOM::ParseInstance() begin -Debug, T34284: PLY::DOM::ParseHeader() begin -Debug, T34284: PLY::DOM::ParseHeader() succeeded -Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin -Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded -Debug, T34284: PLY::DOM::ParseInstance() succeeded -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/PLY/cube_binary.ply -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' -Debug, T34284: PLY::DOM::ParseInstanceBinary() begin -Debug, T34284: PLY::DOM::ParseHeader() begin -Debug, T34284: PLY::DOM::ParseHeader() succeeded -Debug, T34284: PLY::DOM::ParseElementInstanceListsBinary() begin -Debug, T34284: PLY::DOM::ParseElementInstanceListsBinary() succeeded -Debug, T34284: PLY::DOM::ParseInstanceBinary() succeeded -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/PLY/float-color.ply -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' -Debug, T34284: PLY::DOM::ParseInstance() begin -Debug, T34284: PLY::DOM::ParseHeader() begin -Debug, T34284: PLY::DOM::ParseHeader() succeeded -Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin -Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded -Debug, T34284: PLY::DOM::ParseInstance() succeeded -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/PLY/issue623.ply -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' -Debug, T34284: PLY::DOM::ParseInstance() begin -Debug, T34284: PLY::DOM::ParseHeader() begin -Debug, T34284: PLY::DOM::ParseHeader() succeeded -Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin -Warn, T34284: Unable to parse property instance. Skipping this element instance -Skipping one or more lines with the same contents -Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded -Debug, T34284: PLY::DOM::ParseInstance() succeeded -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: Load $$$___magic___$$$. -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Found positive match for header keyword: ply -Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. -Info, T34284: Import root directory is '.\' -Debug, T34284: PLY::DOM::ParseInstance() begin -Debug, T34284: PLY::DOM::ParseHeader() begin -Debug, T34284: PLY::DOM::ParseHeader() succeeded -Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin -Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded -Debug, T34284: PLY::DOM::ParseInstance() succeeded -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: Load D:/projects/assimp/test/models/OBJ/spider.obj -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/OBJ/spider.obj -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: GenVertexNormalsProcess begin -Debug, T34284: GenVertexNormalsProcess finished. Normals are already there -Debug, T34284: GenVertexNormalsProcess begin -Debug, T34284: GenVertexNormalsProcess finished. Normals are already there -Info, T34284: Load $$$___magic___$$$. -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: File extension not known, trying signature-based detection -Debug, T34284: Found positive match for header keyword: usemtl -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is '.\' -Error, T34284: OBJ: failed to locate material Default, creating new material -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: Load $$$___magic___$$$. -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: File extension not known, trying signature-based detection -Debug, T34284: Found positive match for header keyword: usemtl -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is '.\' -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: Load D:/projects/assimp/test/models/OBJ/cube_with_vertexcolors.obj -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: GenVertexNormalsProcess begin -Debug, T34284: GenVertexNormalsProcess finished. Normals are already there -Info, T34284: Load D:/projects/assimp/test/models/OBJ/cube_with_vertexcolors_uni.obj -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: GenVertexNormalsProcess begin -Debug, T34284: GenVertexNormalsProcess finished. Normals are already there -Info, T34284: Load $$$___magic___$$$. -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: File extension not known, trying signature-based detection -Debug, T34284: Found positive match for header keyword: mtllib -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is '.\' -Error, T34284: OBJ: Unable to locate material file $blobfile.mtl -Info, T34284: OBJ: Opening fallback material file $$$___magic___$mtl -Error, T34284: OBJ: Unable to locate fallback material file $$$___magic___$mtl -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load $$$___magic___$$$. -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: File extension not known, trying signature-based detection -Debug, T34284: Found positive match for header keyword: v -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is '.\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Error, T34284: Validation failed: Mesh contains no faces -Info, T34284: Load $$$___magic___$$$. -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: File extension not known, trying signature-based detection -Debug, T34284: Found positive match for header keyword: v -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is '.\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load $$$___magic___$$$. -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: File extension not known, trying signature-based detection -Debug, T34284: Found positive match for header keyword: v -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is '.\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load $$$___magic___$$$. -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: File extension not known, trying signature-based detection -Debug, T34284: Found positive match for header keyword: v -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is '.\' -Error, T34284: OBJ: Invalid component in homogeneous vector (Division by zero) -Info, T34284: Load $$$___magic___$$$. -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: File extension not known, trying signature-based detection -Debug, T34284: Found positive match for header keyword: v -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is '.\' -Error, T34284: OBJ: Invalid face indice -Info, T34284: Load $$$___magic___$$$. -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: File extension not known, trying signature-based detection -Debug, T34284: Found positive match for header keyword: v -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is '.\' -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: Load $$$___magic___$$$. -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: File extension not known, trying signature-based detection -Debug, T34284: Found positive match for header keyword: v -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is '.\' -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: Load D:/projects/assimp/test/models/OBJ/cube_mtllib_after_g.obj -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' -Error, T34284: OBJ: Unable to locate material file cube_mtllib_after_g.mat -Info, T34284: OBJ: Opening fallback material file D:/projects/assimp/test/models/OBJ/cube_mtllib_after_g.mtl -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/OBJ/point_cloud.obj -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ScenePreprocessor: Adding default material 'DefaultMaterial' -Info, T34284: Load D:/projects/assimp/test/models/OBJ/box_without_lineending.obj -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' -Error, T34284: OBJ: failed to locate material Default, creating new material -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: Load $$$___magic___$$$. -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: File extension not known, trying signature-based detection -Debug, T34284: Found positive match for header keyword: v -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is '.\' -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: Load D:/projects/assimp/test/models/OpenGEX/Example.ogex -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Open Game Engine Exchange. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OpenGEX\' -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: Load D:/projects/assimp/test/models/OpenGEX/light_issue1262.ogex -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Open Game Engine Exchange. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OpenGEX\' -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: Load D:/projects/assimp/test/models/OpenGEX/empty_camera.ogex -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Open Game Engine Exchange. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OpenGEX\' -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: Load D:/projects/assimp/test/models/SIB/heffalump.sib -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Silo SIB Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/SIB\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/BLEND/AreaLight_269.blend -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Blender 3D Importer (http://www.blender3d.org). -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/BLEND\' -Info, T34284: BLEND: Blender version is 2.69 (64bit: true, little endian: true) -Debug, T34284: REND -Debug, T34284: TEST -Debug, T34284: GLOB -Debug, T34284: WM -Debug, T34284: DATA -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SC -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: CA -Debug, T34284: LA -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: LA -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: LA -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: WO -Debug, T34284: DATA -Debug, T34284: OB -Skipping one or more lines with the same contents -Debug, T34284: DATA -Debug, T34284: OB -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: OB -Debug, T34284: MA -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: TE -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: ME -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: LS -Debug, T34284: DATA -Debug, T34284: DNA1 -Debug, T34284: BlenderDNA: Got 551 structures with totally 6353 fields -Info, T34284: BlenderDNA: Dumped dna to dna.txt -Debug, T34284: ENDB -Warn, T34284: -Skipping one or more lines with the same contents -Warn, T34284: BlendDNA: Did not find a field named `coeff_const` in structure `Lamp` -Warn, T34284: BlendDNA: Did not find a field named `coeff_lin` in structure `Lamp` -Warn, T34284: BlendDNA: Did not find a field named `coeff_quad` in structure `Lamp` -Warn, T34284: -Skipping one or more lines with the same contents -Warn, T34284: BlendDNA: Did not find a field named `coeff_const` in structure `Lamp` -Warn, T34284: BlendDNA: Did not find a field named `coeff_lin` in structure `Lamp` -Warn, T34284: BlendDNA: Did not find a field named `coeff_quad` in structure `Lamp` -Warn, T34284: -Skipping one or more lines with the same contents -Warn, T34284: BlendDNA: Did not find a field named `coeff_const` in structure `Lamp` -Warn, T34284: BlendDNA: Did not find a field named `coeff_lin` in structure `Lamp` -Warn, T34284: BlendDNA: Did not find a field named `coeff_quad` in structure `Lamp` -Info, T34284: (Stats) Fields read: 918, pointers resolved: 23, cache hits: 3, cached objects: 19 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Warn, T34284: Validation warning: aiLight::mAttenuationXXX - all are zero -Skipping one or more lines with the same contents -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/BLEND/box.blend -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Blender 3D Importer (http://www.blender3d.org). -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/BLEND\' -Info, T34284: BLEND: Blender version is 2.76 (64bit: true, little endian: true) -Debug, T34284: REND -Debug, T34284: TEST -Debug, T34284: GLOB -Debug, T34284: WM -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SC -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: CA -Debug, T34284: LA -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: WO -Debug, T34284: DATA -Debug, T34284: OB -Debug, T34284: DATA -Debug, T34284: OB -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: OB -Debug, T34284: DATA -Debug, T34284: MA -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: TE -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: ME -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: LS -Debug, T34284: DATA -Debug, T34284: DNA1 -Debug, T34284: BlenderDNA: Got 604 structures with totally 6955 fields -Info, T34284: BlenderDNA: Dumped dna to dna.txt -Debug, T34284: ENDB -Warn, T34284: -Skipping one or more lines with the same contents -Warn, T34284: BlendDNA: Did not find a field named `coeff_const` in structure `Lamp` -Warn, T34284: BlendDNA: Did not find a field named `coeff_lin` in structure `Lamp` -Warn, T34284: BlendDNA: Did not find a field named `coeff_quad` in structure `Lamp` -Info, T34284: (Stats) Fields read: 668, pointers resolved: 17, cache hits: 3, cached objects: 13 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Warn, T34284: Validation warning: aiLight::mAttenuationXXX - all are zero -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/BLEND/4Cubes4Mats_248.blend -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Blender 3D Importer (http://www.blender3d.org). -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/BLEND\' -Info, T34284: BLEND: Blender version is 2.48 (64bit: false, little endian: true) -Debug, T34284: REND -Debug, T34284: GLOB -Debug, T34284: SR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SC -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: CA -Debug, T34284: LA -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: WO -Debug, T34284: TX -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: OB -Skipping one or more lines with the same contents -Debug, T34284: DATA -Debug, T34284: OB -Debug, T34284: DATA -Debug, T34284: OB -Debug, T34284: DATA -Debug, T34284: OB -Debug, T34284: DATA -Debug, T34284: OB -Debug, T34284: MA -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: MA -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: MA -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: MA -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: TE -Debug, T34284: ME -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: ME -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: ME -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: ME -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: DNA1 -Debug, T34284: BlenderDNA: Got 310 structures with totally 3868 fields -Info, T34284: BlenderDNA: Dumped dna to dna.txt -Debug, T34284: ENDB -Warn, T34284: -Skipping one or more lines with the same contents -Warn, T34284: BlendDNA: Did not find a field named `sensor_x` in structure `Camera` -Warn, T34284: -Skipping one or more lines with the same contents -Warn, T34284: BlendDNA: Did not find a field named `totloop` in structure `Mesh` -Warn, T34284: BlendDNA: Did not find a field named `totpoly` in structure `Mesh` -Warn, T34284: BlendDNA: Did not find a field named `*mloop` in structure `Mesh` -Warn, T34284: BlendDNA: Did not find a field named `*mloopuv` in structure `Mesh` -Warn, T34284: BlendDNA: Did not find a field named `*mloopcol` in structure `Mesh` -Warn, T34284: BlendDNA: Did not find a field named `*mpoly` in structure `Mesh` -Warn, T34284: BlendDNA: Did not find a field named `*mtpoly` in structure `Mesh` -Warn, T34284: -Skipping one or more lines with the same contents -Warn, T34284: BlendDNA: Did not find a field named `rot` in structure `MTex` -Warn, T34284: BlendDNA: Did not find a field named `colspecfac` in structure `MTex` -Warn, T34284: BlendDNA: Did not find a field named `mirrfac` in structure `MTex` -Warn, T34284: BlendDNA: Did not find a field named `alphafac` in structure `MTex` -Warn, T34284: BlendDNA: Did not find a field named `difffac` in structure `MTex` -Warn, T34284: BlendDNA: Did not find a field named `specfac` in structure `MTex` -Warn, T34284: BlendDNA: Did not find a field named `emitfac` in structure `MTex` -Warn, T34284: BlendDNA: Did not find a field named `hardfac` in structure `MTex` -Warn, T34284: BlendDNA: Did not find a field named `material_type` in structure `Material` -Warn, T34284: BlendDNA: Did not find a field named `pr_texture` in structure `Material` -Warn, T34284: BlendDNA: Did not find a field named `shadowonly_flag` in structure `Material` -Warn, T34284: BlendDNA: Did not find a field named `index` in structure `Material` -Warn, T34284: BlendDNA: Did not find a field named `vcol_alpha` in structure `Material` -Warn, T34284: BlendDNA: Did not find a field named `typemap` in structure `CustomData` -Error, T34284: Constructing BlenderDNA Structure encountered an error -Info, T34284: Load D:/projects/assimp/test/models/BLEND/blender_269_regress1.blend -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Blender 3D Importer (http://www.blender3d.org). -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/BLEND\' -Info, T34284: BLEND: Blender version is 2.69 (64bit: true, little endian: true) -Debug, T34284: REND -Debug, T34284: TEST -Debug, T34284: GLOB -Debug, T34284: WM -Debug, T34284: DATA -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SC -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: CA -Debug, T34284: LA -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: KE -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: WO -Debug, T34284: DATA -Debug, T34284: OB -Debug, T34284: DATA -Debug, T34284: OB -Debug, T34284: DATA -Debug, T34284: OB -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: MA -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: TE -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: ME -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: LS -Debug, T34284: DATA -Debug, T34284: DNA1 -Debug, T34284: BlenderDNA: Got 551 structures with totally 6353 fields -Info, T34284: BlenderDNA: Dumped dna to dna.txt From e683c6eef9cc0ce5ef3da6dce79b58ec86a3f966 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 5 May 2021 15:10:30 +0200 Subject: [PATCH 068/335] Delete testExport.stl --- test/testExport.stl | 33 --------------------------------- 1 file changed, 33 deletions(-) delete mode 100644 test/testExport.stl diff --git a/test/testExport.stl b/test/testExport.stl deleted file mode 100644 index e7d06cefc..000000000 --- a/test/testExport.stl +++ /dev/null @@ -1,33 +0,0 @@ - solid Assimp_Pointcloud - facet normal 0 0 0 - vertex 0 0 0 - vertex 0 0 0 - vertex 0 0 0 - vertex 1 1 1 - vertex 1 1 1 - vertex 1 1 1 - vertex 2 2 2 - vertex 2 2 2 - vertex 2 2 2 - vertex 3 3 3 - vertex 3 3 3 - vertex 3 3 3 - vertex 4 4 4 - vertex 4 4 4 - vertex 4 4 4 - vertex 5 5 5 - vertex 5 5 5 - vertex 5 5 5 - vertex 6 6 6 - vertex 6 6 6 - vertex 6 6 6 - vertex 7 7 7 - vertex 7 7 7 - vertex 7 7 7 - vertex 8 8 8 - vertex 8 8 8 - vertex 8 8 8 - vertex 9 9 9 - vertex 9 9 9 - vertex 9 9 9 -endsolid Assimp_Pointcloud From d1991ad949e8bc45ff711d66dd8d8f1ea6376327 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 5 May 2021 15:10:50 +0200 Subject: [PATCH 069/335] Delete AssimpLog_Cpp.txt --- test/AssimpLog_Cpp.txt | 3184 ---------------------------------------- 1 file changed, 3184 deletions(-) delete mode 100644 test/AssimpLog_Cpp.txt diff --git a/test/AssimpLog_Cpp.txt b/test/AssimpLog_Cpp.txt deleted file mode 100644 index b843b6f07..000000000 --- a/test/AssimpLog_Cpp.txt +++ /dev/null @@ -1,3184 +0,0 @@ -Info, T34284: Load dae.dae -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Collada Importer. -Info, T34284: Import root directory is '.\' -Debug, T34284: Collada schema version is 1.4.n -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: START `t1` -Debug, T34284: END `t1`, dt= 0.0064414 s -Info, T34284: Load D:/projects/assimp/test/models/OBJ/spider.obj -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 5 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_post_linear.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 5 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_xz_bezier.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 5 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_xz_stepped.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 5 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_oldformat_56.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 2 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_post_offset_repeat.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 5 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_xz_hermite.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 5 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_y_pre_ofrep_post_osc.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 5 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Warn, T34284: Validation warning: aiNodeAnim::mPositionKeys[1].mTime (-72.00000) is smaller than aiAnimation::mPositionKeys[0] (which is 0.00000) -Warn, T34284: Validation warning: aiNodeAnim::mPositionKeys[5].mTime (-21.00000) is smaller than aiAnimation::mPositionKeys[4] (which is 0.00000) -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_oldformat_6.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 3 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_post_repeat.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 5 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_xz_linear.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 5 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_post_constant.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 5 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_x_post_reset.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 5 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/LWS/move_xz_spline.lws -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave Scene Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWS\' -Debug, T34284: LWS: Skipping over plugin-specific data -Skipping one or more lines with the same contents -Info, T34284: LWS file format version is 5 -Info, T34284: %%% BEGIN EXTERNAL FILE %%% -Info, T34284: File: simple_cube.lwo -Info, T34284: Load simple_cube.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: %%% END EXTERNAL FILE %%% -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy rotation track has been generated -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/LWO/LWO2/boxuv.lwo -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWO/LWO2\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/LWO/LWO2/formatDetection -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: LightWave/Modo Object Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/LWO/LWO2\' -Info, T34284: LWO file format: LWO2 (>= LightWave 6) -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/SMD/triangle.smd -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Valve SMD Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/SMD\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/SMD/holy_grailref.smd -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Valve SMD Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/SMD\' -Error, T34284: [SMD/VTA] Bone index overflow. The bone index will be ignored, the weight will be assigned to the vertex' parent node -Skipping one or more lines with the same contents -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Info, T34284: Load D:/projects/assimp/test/models/glTF/TwoBoxes/TwoBoxes.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: glTF Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF/TwoBoxes\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF/IncorrectVertexArrays/Cube_v1.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: glTF Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF/IncorrectVertexArrays\' -Warn, T34284: The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped. -Warn, T34284: The number of vertices was not compatible with the LINES mode. Some vertices were dropped. -Warn, T34284: The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped. -Warn, T34284: The number of vertices was not compatible with the LINES mode. Some vertices were dropped. -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Warn, T34284: Validation warning: There are unreferenced vertices -Skipping one or more lines with the same contents -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF/TwoBoxes/TwoBoxes.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: glTF Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF/TwoBoxes\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxTextured-glTF/BoxTextured.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxTextured-glTF\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 1 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/2CylinderEngine-glTF-Binary/2CylinderEngine.glb -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Reading GLTF2 binary -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/2CylinderEngine-glTF-Binary\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Reading GLTF2 binary -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 34 materials -Debug, T34284: Importing 29 meshes -Debug, T34284: Importing 1 cameras -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxTextured-glTF/BoxTextured.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxTextured-glTF\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 1 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: GenVertexNormalsProcess begin -Debug, T34284: GenVertexNormalsProcess finished. Normals are already there -Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxTextured-glTF-Embedded/BoxTextured.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxTextured-glTF-Embedded\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 1 embedded textures -Debug, T34284: Importing 1 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: GenVertexNormalsProcess begin -Debug, T34284: GenVertexNormalsProcess finished. Normals are already there -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_00.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_01.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_02.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_03.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_04.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_05.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_06.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_07.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_08.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_09.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_10.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_11.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode/Mesh_PrimitiveMode_12.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Asset-Generator/Mesh_PrimitiveMode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/simple_skin/simple_skin.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/simple_skin\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 1 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Debug, T34284: ScenePreprocessor: Dummy position track has been generated -Info, T34284: Load D:/projects/assimp/test/models/glTF2/cameras/Cameras.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/cameras\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing 2 cameras -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Warn, T34284: Validation warning: 0.000000 is not a valid value for aiCamera::mHorizontalFOV -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/IncorrectVertexArrays/Cube.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/IncorrectVertexArrays\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 8 meshes -Warn, T34284: The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped. -Warn, T34284: The number of vertices was not compatible with the LINES mode. Some vertices were dropped. -Warn, T34284: The number of vertices was not compatible with the TRIANGLES mode. Some vertices were dropped. -Warn, T34284: The number of vertices was not compatible with the LINES mode. Some vertices were dropped. -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Warn, T34284: Validation warning: There are unreferenced vertices -Skipping one or more lines with the same contents -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/textureTransform/TextureTransformTest.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/textureTransform\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 9 materials -Debug, T34284: Importing 9 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxTextured-glTF/BoxTextured.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxTextured-glTF\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 1 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: TriangulateProcess begin -Debug, T34284: TriangulateProcess finished. There was nothing to be done. -Debug, T34284: SortByPTypeProcess begin -Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) -Debug, T34284: SortByPTypeProcess finished -Debug, T34284: JoinVerticesProcess begin -Debug, T34284: Mesh 0 (Mesh) | Verts in: 24 out: 24 | ~0% -Debug, T34284: JoinVerticesProcess finished -Info, T34284: Load D:/projects/assimp/test/models/glTF2/glTF-Sample-Models/AnimatedMorphCube-glTF/AnimatedMorphCube.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/glTF-Sample-Models/AnimatedMorphCube-glTF\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 1 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 1 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: TriangulateProcess begin -Debug, T34284: TriangulateProcess finished. There was nothing to be done. -Debug, T34284: SortByPTypeProcess begin -Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) -Debug, T34284: SortByPTypeProcess finished -Debug, T34284: JoinVerticesProcess begin -Debug, T34284: Mesh 0 (Cube) | Verts in: 24 out: 24 | ~0% -Debug, T34284: JoinVerticesProcess finished -Info, T34284: Load D:/projects/assimp/test/models/glTF2/MissingBin/BoxTextured.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxWithInfinites-glTF-Binary/BoxWithInfinites.glb -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Reading GLTF2 binary -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxWithInfinites-glTF-Binary\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Reading GLTF2 binary -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 1 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: TriangulateProcess begin -Debug, T34284: TriangulateProcess finished. There was nothing to be done. -Debug, T34284: SortByPTypeProcess begin -Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) -Debug, T34284: SortByPTypeProcess finished -Debug, T34284: JoinVerticesProcess begin -Debug, T34284: Mesh 0 (Mesh) | Verts in: 24 out: 10 | ~58.3333% -Info, T34284: JoinVerticesProcess finished | Verts in: 24 out: 10 | ~58.3333 -Debug, T34284: TriangulateProcess begin -Debug, T34284: TriangulateProcess finished. There was nothing to be done. -Debug, T34284: SortByPTypeProcess begin -Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) -Debug, T34284: SortByPTypeProcess finished -Debug, T34284: JoinVerticesProcess begin -Debug, T34284: Mesh 0 (Mesh) | Verts in: 24 out: 10 | ~58.3333% -Info, T34284: JoinVerticesProcess finished | Verts in: 24 out: 10 | ~58.3333 -Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxBadNormals-glTF-Binary/BoxBadNormals.glb -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Reading GLTF2 binary -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxBadNormals-glTF-Binary\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Reading GLTF2 binary -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 1 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: TriangulateProcess begin -Debug, T34284: TriangulateProcess finished. There was nothing to be done. -Debug, T34284: SortByPTypeProcess begin -Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) -Debug, T34284: SortByPTypeProcess finished -Debug, T34284: JoinVerticesProcess begin -Debug, T34284: Mesh 0 (Mesh) | Verts in: 24 out: 24 | ~0% -Debug, T34284: JoinVerticesProcess finished -Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxBadNormals-glTF-Binary/BoxBadNormals_out.glb -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: (Deleting previous scene) -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Reading GLTF2 binary -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxBadNormals-glTF-Binary\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Reading GLTF2 binary -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 1 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxTextured-glTF/BoxTextured.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxTextured-glTF\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 1 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/BoxTexcoords-glTF\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 1 materials -Debug, T34284: Importing 1 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/RecursiveNodes/RecursiveNodes.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Load D:/projects/assimp/test/models/glTF2/TestNoRootNode/NoScene.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/TestNoRootNode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 0 meshes -Error, T34284: GLTF: No scene -Info, T34284: Load D:/projects/assimp/test/models/glTF2/TestNoRootNode/SceneWithoutNodes.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/TestNoRootNode\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 0 materials -Debug, T34284: Importing 0 meshes -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/issue_3269/texcoord_crash.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Load D:/projects/assimp/test/models/glTF2/IndexOutOfRange/IndexOutOfRange.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/IndexOutOfRange\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 1 materials -Debug, T34284: Importing 1 meshes -Warn, T34284: Some faces had out-of-range indices. Those faces were dropped. -Debug, T34284: Importing nodes -Debug, T34284: Importing 0 animations -Debug, T34284: Importing metadata -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Warn, T34284: Validation warning: There are unreferenced vertices -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/glTF2/IndexOutOfRange/AllIndicesOutOfRange.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Found a matching importer for this file format: glTF2 Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/glTF2/IndexOutOfRange\' -Debug, T34284: Reading GLTF2 file -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Debug, T34284: Importing 1 materials -Debug, T34284: Importing 1 meshes -Warn, T34284: Some faces had out-of-range indices. Those faces were dropped. -Error, T34284: Mesh "Mesh" has no faces -Info, T34284: Load D:/projects/assimp/test/models/glTF2/draco/2CylinderEngine.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Load D:/projects/assimp/test/models/glTF2/wrongTypes/badArray.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Load D:/projects/assimp/test/models/glTF2/wrongTypes/badString.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Load D:/projects/assimp/test/models/glTF2/wrongTypes/badUint.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Load D:/projects/assimp/test/models/glTF2/wrongTypes/badNumber.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Load D:/projects/assimp/test/models/glTF2/wrongTypes/badObject.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Load D:/projects/assimp/test/models/glTF2/wrongTypes/badExtension.gltf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Loading GLTF2 asset -Debug, T34284: Parsing GLTF2 JSON -Info, T34284: Load D:/projects/assimp/test/models/HMP/terrain.hmp -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: 3D GameStudio Heightmap (HMP) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/HMP\' -Debug, T34284: HMP subtype: 3D GameStudio A7, magic word is HMP7 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/IFC/AC14-FZK-Haus.ifc -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Industry Foundation Classes (IFC) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/IFC\' -Debug, T34284: IFC: File schema is 'IFC2X3' -Debug, T34284: STEP: got 82226 object records with 5788 inverse index entries -Debug, T34284: IFC: got units used for lengths -Debug, T34284: IFC: got units used for angles -Debug, T34284: IFC: got world coordinate system -Debug, T34284: IFC: looking at spatial structure `Gelände` -Debug, T34284: IFC: selecting this spatial structure as root structure -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Skipping one or more lines with the same contents -Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline -Debug, T34284: IFC: removing duplicate vertices -Error, T34284: IFC: failed to generate all window caps -Skipping one or more lines with the same contents -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: removing degenerate faces -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Skipping one or more lines with the same contents -Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline -Debug, T34284: IFC: removing duplicate vertices -Error, T34284: IFC: failed to generate all window caps -Skipping one or more lines with the same contents -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: removing degenerate faces -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Skipping one or more lines with the same contents -Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline -Debug, T34284: IFC: removing duplicate vertices -Error, T34284: IFC: failed to generate all window caps -Skipping one or more lines with the same contents -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Skipping one or more lines with the same contents -Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline -Debug, T34284: IFC: removing duplicate vertices -Error, T34284: IFC: failed to generate all window caps -Skipping one or more lines with the same contents -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Skipping one or more lines with the same contents -Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline -Debug, T34284: IFC: removing duplicate vertices -Error, T34284: IFC: failed to generate all window caps -Skipping one or more lines with the same contents -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Skipping one or more lines with the same contents -Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline -Debug, T34284: IFC: removing duplicate vertices -Error, T34284: IFC: failed to generate all window caps -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: removing degenerate faces -Skipping one or more lines with the same contents -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: removing degenerate faces -Skipping one or more lines with the same contents -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: skipping IfcAnnotation entity due to importer settings -Skipping one or more lines with the same contents -Debug, T34284: IFC: skipping IfcSpace entity due to importer settings -Skipping one or more lines with the same contents -Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: generating CSG geometry by plane clipping (IfcBooleanClippingResult) -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline -Debug, T34284: IFC: removing duplicate vertices -Error, T34284: IFC: failed to generate all window caps -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: generating CSG geometry by plane clipping with polygonal bounding (IfcBooleanClippingResult) -Skipping one or more lines with the same contents -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: removing degenerate faces -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: removing degenerate faces -Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: generating CSG geometry by plane clipping (IfcBooleanClippingResult) -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Warn, T34284: IFC: skipping unknown IfcGeometricRepresentationItem entity, type is IfcPolyline -Debug, T34284: IFC: removing duplicate vertices -Error, T34284: IFC: failed to generate all window caps -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: generating CSG geometry by plane clipping with polygonal bounding (IfcBooleanClippingResult) -Skipping one or more lines with the same contents -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: removing degenerate faces -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: generating CSG geometry by plane clipping (IfcBooleanClippingResult) -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: generating CSG geometry by plane clipping (IfcBooleanClippingResult) -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Skipping one or more lines with the same contents -Debug, T34284: IFC: generating CSG geometry by plane clipping (IfcBooleanClippingResult) -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: generating CSG geometry by plane clipping (IfcBooleanClippingResult) -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: removing duplicate vertices -Warn, T34284: IFC: failed to resolve all openings, presumably their topology is not supported by Assimp -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: removing degenerate faces -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: removing duplicate vertices -Debug, T34284: IFC: generate mesh procedurally by extrusion (IfcExtrudedAreaSolid) -Debug, T34284: IFC: skipping IfcAnnotation entity due to importer settings -Skipping one or more lines with the same contents -Debug, T34284: IFC: skipping IfcSpace entity due to importer settings -Debug, T34284: IFC: STEP: evaluated 70094 object records -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load $$$___magic___$$$. -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Found positive match for header keyword: HEADER -Info, T34284: Found a matching importer for this file format: Drawing Interchange Format (DXF) Importer. -Info, T34284: Import root directory is '.\' -Warn, T34284: DXF: EOF reached, but did not encounter DXF EOF marker -Debug, T34284: DXF: Unexpanded polycount is 0, vertex count is 0 -Error, T34284: DXF: no data blocks loaded -Info, T34284: Load D:/projects/assimp/test/models/FBX/spider.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing binary FBX file -Debug, T34284: FBX version: 7400 -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7400 -Debug, T34284: UpdateImporterScale scale set: 0.01 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/box.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing binary FBX file -Debug, T34284: FBX version: 7400 -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7400 -Info, T34284: FBX: generating full transformation chain for node: root -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/cubes_nonames.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing ASCII FBX file -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7500 -Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless -Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 -Debug, T34284: UpdateImporterScale scale set: 0.01 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/cubes_with_names.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing ASCII FBX file -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7500 -Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless -Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 -Debug, T34284: UpdateImporterScale scale set: 0.01 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/cubes_with_mirroring_and_pivot.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing ASCII FBX file -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7500 -Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless -Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 -Info, T34284: FBX: generating full transformation chain for node: Cube1 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/close_to_identity_transforms.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing ASCII FBX file -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7500 -Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless -Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 -Info, T34284: FBX: generating full transformation chain for node: Cube1 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/phong_cube.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing binary FBX file -Debug, T34284: FBX version: 7400 -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7400 -Warn, T34284: FBX-DOM (TOK_KEY, offset 0x4190) source object for connection does not exist -Debug, T34284: UpdateImporterScale scale set: 0.01 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/global_settings.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing binary FBX file -Debug, T34284: FBX version: 7400 -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7400 -Error, T34284: FBX: no material assigned to mesh, setting default material -Debug, T34284: UpdateImporterScale scale set: 5 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/embedded_ascii/box.FBX -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX/embedded_ascii\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing ASCII FBX file -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7400 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Warn, T34284: Validation warning: A specular shading model is specified but the value of the AI_MATKEY_SHININESS_STRENGTH key is 0.0 -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/embedded_ascii/box_embedded_texture_fragmented.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX/embedded_ascii\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing ASCII FBX file -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7500 -Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless -Error, T34284: FBX: ignoring additional binormal layer -Error, T34284: FBX: ignoring additional tangent layer -Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/box_orphant_embedded_texture.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing ASCII FBX file -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7500 -Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless -Error, T34284: FBX: ignoring additional binormal layer -Error, T34284: FBX: ignoring additional tangent layer -Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/global_settings.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing binary FBX file -Debug, T34284: FBX version: 7400 -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7400 -Error, T34284: FBX: no material assigned to mesh, setting default material -Debug, T34284: UpdateImporterScale scale set: 5 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/cubes_with_outofrange_float.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing ASCII FBX file -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7500 -Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless -Info, T34284: FBX: ignoring empty AnimationStack (using IK?): Take 001 -Debug, T34284: UpdateImporterScale scale set: 0.01 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/maxPbrMaterial_metalRough.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing ASCII FBX file -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7700 -Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless -Warn, T34284: FBX-DOM (TOK_KEY, line 679, col 13) shading mode not recognized: unknown -Info, T34284: FBX: generating full transformation chain for node: Box001 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/FBX/maxPbrMaterial_specGloss.fbx -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Autodesk FBX Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/FBX\' -Debug, T34284: Reading FBX file -Debug, T34284: Tokenizing ASCII FBX file -Debug, T34284: Parsing FBX tokens -Debug, T34284: Creating FBX Document -Debug, T34284: FBX Version: 7700 -Warn, T34284: FBX-DOM: unsupported, newer format version, supported are only FBX 2011, FBX 2012 and FBX 2013, trying to read it nevertheless -Warn, T34284: FBX-DOM (TOK_KEY, line 679, col 13) shading mode not recognized: unknown -Info, T34284: FBX: generating full transformation chain for node: Box001 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load $$$___magic___$$$.3ds -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Discreet 3DS Importer. -Info, T34284: Import root directory is '.\' -Info, T34284: 3DS file format version: 3 -Warn, T34284: 3DS: Skipping TCB animation info -Skipping one or more lines with the same contents -Error, T34284: 3DS: Skipping FOV animation track. This is not supported -Warn, T34284: 3DS: Skipping TCB animation info -Skipping one or more lines with the same contents -Info, T34284: 3DS: Generating default material -Debug, T34284: 3DS: Converting camera roll track ... -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Debug, T34284: ScenePreprocessor: Setting animation duration -Info, T34284: Entering post processing pipeline -Debug, T34284: RemoveRedundantMatsProcess begin -Debug, T34284: RemoveRedundantMatsProcess finished -Debug, T34284: GenUVCoordsProcess begin -Debug, T34284: GenUVCoordsProcess finished -Debug, T34284: TriangulateProcess begin -Debug, T34284: TriangulateProcess finished. There was nothing to be done. -Debug, T34284: FindDegeneratesProcess begin -Debug, T34284: FindDegeneratesProcess finished -Debug, T34284: SortByPTypeProcess begin -Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) -Debug, T34284: SortByPTypeProcess finished -Debug, T34284: FindInvalidDataProcess begin -Info, T34284: FindInvalidDataProcess finished. Found issues ... -Debug, T34284: SplitLargeMeshesProcess_Triangle begin -Debug, T34284: SplitLargeMeshesProcess_Triangle finished. There was nothing to do -Debug, T34284: Generate spatially-sorted vertex cache -Debug, T34284: GenVertexNormalsProcess begin -Debug, T34284: GenVertexNormalsProcess finished. Normals are already there -Debug, T34284: CalcTangentsProcess begin -Info, T34284: CalcTangentsProcess finished. Tangents have been calculated -Debug, T34284: JoinVerticesProcess begin -Debug, T34284: Mesh 0 (0) | Verts in: 36 out: 24 | ~33.3333% -Info, T34284: JoinVerticesProcess finished | Verts in: 36 out: 24 | ~33.3333 -Debug, T34284: SplitLargeMeshesProcess_Vertex begin -Debug, T34284: SplitLargeMeshesProcess_Vertex finished. There was nothing to do -Debug, T34284: LimitBoneWeightsProcess begin -Debug, T34284: LimitBoneWeightsProcess end -Debug, T34284: ImproveCacheLocalityProcess begin -Debug, T34284: Mesh %u | ACMR in: 0 out: 2 | ~20 -Info, T34284: Cache relevant are 1 meshes (12 faces). Average output ACMR is 2 -Debug, T34284: ImproveCacheLocalityProcess finished. -Info, T34284: Leaving post processing pipeline -Info, T34284: Registering custom importer for these file extensions: applelinuxmacwindows -Info, T34284: Unregistering custom importer: -Info, T34284: Load D:/projects/assimp/test/models/X/test.x -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Warn, T34284: Unknown data object in mesh in x file -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Entering post processing pipeline -Debug, T34284: RemoveRedundantMatsProcess begin -Debug, T34284: RemoveRedundantMatsProcess finished -Debug, T34284: OptimizeGraphProcess begin -Debug, T34284: OptimizeGraphProcess finished -Debug, T34284: GenUVCoordsProcess begin -Debug, T34284: GenUVCoordsProcess finished -Debug, T34284: TriangulateProcess begin -Debug, T34284: TriangulateProcess finished. There was nothing to be done. -Debug, T34284: FindDegeneratesProcess begin -Debug, T34284: FindDegeneratesProcess finished -Debug, T34284: SortByPTypeProcess begin -Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) -Debug, T34284: SortByPTypeProcess finished -Debug, T34284: FindInvalidDataProcess begin -Info, T34284: FindInvalidDataProcess finished. Found issues ... -Debug, T34284: Skipping OptimizeMeshesProcess -Debug, T34284: Generate spatially-sorted vertex cache -Debug, T34284: GenVertexNormalsProcess begin -Debug, T34284: GenVertexNormalsProcess finished. Normals are already there -Debug, T34284: JoinVerticesProcess begin -Debug, T34284: Mesh 0 (pCube1) | Verts in: 36 out: 24 | ~33.3333% -Info, T34284: JoinVerticesProcess finished | Verts in: 36 out: 24 | ~33.3333 -Info, T34284: Leaving post processing pipeline -Info, T34284: Load D:/projects/assimp/test/models/X/Testwuson.X -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: (Deleting previous scene) -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Warn, T34284: Unknown data object in animation of .x file -Skipping one or more lines with the same contents -Warn, T34284: Unknown data object in frame in x file -Skipping one or more lines with the same contents -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Entering post processing pipeline -Debug, T34284: RemoveRedundantMatsProcess begin -Debug, T34284: RemoveRedundantMatsProcess finished -Debug, T34284: OptimizeGraphProcess begin -Debug, T34284: OptimizeGraphProcess finished -Debug, T34284: GenUVCoordsProcess begin -Debug, T34284: GenUVCoordsProcess finished -Debug, T34284: TriangulateProcess begin -Debug, T34284: TriangulateProcess finished. There was nothing to be done. -Debug, T34284: FindDegeneratesProcess begin -Debug, T34284: FindDegeneratesProcess finished -Debug, T34284: SortByPTypeProcess begin -Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) -Debug, T34284: SortByPTypeProcess finished -Debug, T34284: FindInvalidDataProcess begin -Info, T34284: FindInvalidDataProcess finished. Found issues ... -Debug, T34284: Skipping OptimizeMeshesProcess -Debug, T34284: Generate spatially-sorted vertex cache -Debug, T34284: GenVertexNormalsProcess begin -Debug, T34284: GenVertexNormalsProcess finished. Normals are already there -Debug, T34284: JoinVerticesProcess begin -Debug, T34284: Mesh 0 (Wuson) | Verts in: 11196 out: 3205 | ~71.3737% -Info, T34284: JoinVerticesProcess finished | Verts in: 11196 out: 3205 | ~71.3737 -Info, T34284: Leaving post processing pipeline -Info, T34284: Load D:/projects/assimp/test/models/X/anim_test.x -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: (Deleting previous scene) -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Warn, T34284: Length of texture file name is zero. Skipping this texture. -Warn, T34284: Unknown data object in mesh in x file -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Entering post processing pipeline -Debug, T34284: RemoveRedundantMatsProcess begin -Debug, T34284: RemoveRedundantMatsProcess finished -Debug, T34284: OptimizeGraphProcess begin -Debug, T34284: OptimizeGraphProcess finished -Debug, T34284: GenUVCoordsProcess begin -Debug, T34284: GenUVCoordsProcess finished -Debug, T34284: TriangulateProcess begin -Debug, T34284: TriangulateProcess finished. There was nothing to be done. -Debug, T34284: FindDegeneratesProcess begin -Debug, T34284: FindDegeneratesProcess finished -Debug, T34284: SortByPTypeProcess begin -Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) -Debug, T34284: SortByPTypeProcess finished -Debug, T34284: FindInvalidDataProcess begin -Warn, T34284: Simplified dummy tracks with just one key -Skipping one or more lines with the same contents -Info, T34284: FindInvalidDataProcess finished. Found issues ... -Debug, T34284: Skipping OptimizeMeshesProcess -Debug, T34284: Generate spatially-sorted vertex cache -Debug, T34284: GenVertexNormalsProcess begin -Debug, T34284: GenVertexNormalsProcess finished. Normals are already there -Debug, T34284: JoinVerticesProcess begin -Debug, T34284: Mesh 0 (pCylinder1) | Verts in: 2520 out: 485 | ~80.754% -Info, T34284: JoinVerticesProcess finished | Verts in: 2520 out: 485 | ~80.754 -Info, T34284: Leaving post processing pipeline -Info, T34284: Load D:/projects/assimp/test/models/X/anim_test.x -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: (Deleting previous scene) -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Warn, T34284: Length of texture file name is zero. Skipping this texture. -Warn, T34284: Unknown data object in mesh in x file -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Entering post processing pipeline -Debug, T34284: RemoveRedundantMatsProcess begin -Debug, T34284: RemoveRedundantMatsProcess finished -Debug, T34284: OptimizeGraphProcess begin -Debug, T34284: OptimizeGraphProcess finished -Debug, T34284: GenUVCoordsProcess begin -Debug, T34284: GenUVCoordsProcess finished -Debug, T34284: TriangulateProcess begin -Debug, T34284: TriangulateProcess finished. There was nothing to be done. -Debug, T34284: FindDegeneratesProcess begin -Debug, T34284: FindDegeneratesProcess finished -Debug, T34284: SortByPTypeProcess begin -Info, T34284: Points: 0, Lines: 0, Triangles: 1, Polygons: 0 (Meshes, X = removed) -Debug, T34284: SortByPTypeProcess finished -Debug, T34284: FindInvalidDataProcess begin -Warn, T34284: Simplified dummy tracks with just one key -Skipping one or more lines with the same contents -Info, T34284: FindInvalidDataProcess finished. Found issues ... -Debug, T34284: Skipping OptimizeMeshesProcess -Debug, T34284: Generate spatially-sorted vertex cache -Debug, T34284: GenVertexNormalsProcess begin -Debug, T34284: GenVertexNormalsProcess finished. Normals are already there -Debug, T34284: JoinVerticesProcess begin -Debug, T34284: Mesh 0 (pCylinder1) | Verts in: 2520 out: 485 | ~80.754% -Info, T34284: JoinVerticesProcess finished | Verts in: 2520 out: 485 | ~80.754 -Info, T34284: Leaving post processing pipeline -Info, T34284: Load D:/projects/assimp/test/models/X/BCN_Epileptic.X -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: (Deleting previous scene) -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Warn, T34284: Unknown data object in animation of .x file -Skipping one or more lines with the same contents -Warn, T34284: Unknown data object in frame in x file -Skipping one or more lines with the same contents -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Entering post processing pipeline -Debug, T34284: RemoveRedundantMatsProcess begin -Debug, T34284: RemoveRedundantMatsProcess finished -Debug, T34284: OptimizeGraphProcess begin -Debug, T34284: OptimizeGraphProcess finished -Debug, T34284: GenUVCoordsProcess begin -Debug, T34284: GenUVCoordsProcess finished -Debug, T34284: TriangulateProcess begin -Debug, T34284: TriangulateProcess finished. There was nothing to be done. -Debug, T34284: FindDegeneratesProcess begin -Debug, T34284: FindDegeneratesProcess finished -Debug, T34284: SortByPTypeProcess begin -Info, T34284: Points: 0, Lines: 0, Triangles: 3, Polygons: 0 (Meshes, X = removed) -Debug, T34284: SortByPTypeProcess finished -Debug, T34284: FindInvalidDataProcess begin -Info, T34284: FindInvalidDataProcess finished. Found issues ... -Debug, T34284: OptimizeMeshesProcess begin -Debug, T34284: OptimizeMeshesProcess finished -Debug, T34284: Generate spatially-sorted vertex cache -Debug, T34284: GenVertexNormalsProcess begin -Debug, T34284: GenVertexNormalsProcess finished. Normals are already there -Debug, T34284: JoinVerticesProcess begin -Debug, T34284: Mesh 0 (Torso) | Verts in: 5898 out: 1170 | ~80.1628% -Debug, T34284: Mesh 1 (Head) | Verts in: 6108 out: 1196 | ~80.4191% -Debug, T34284: Mesh 2 (Legs) | Verts in: 3372 out: 648 | ~80.7829% -Info, T34284: JoinVerticesProcess finished | Verts in: 15378 out: 3014 | ~80.4006 -Info, T34284: Leaving post processing pipeline -Info, T34284: Registering custom importer for these file extensions: fail -Info, T34284: Load deadlyImportError.fail -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Failing importer. -Info, T34284: Import root directory is './' -Error, T34284: Deadly import error test. Details: 42 More Details: Failure -Info, T34284: Registering custom importer for these file extensions: fail -Info, T34284: Load stdException.fail -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Failing importer. -Info, T34284: Import root directory is './' -Error, T34284: std::exception test -Info, T34284: Registering custom importer for these file extensions: fail -Info, T34284: Load unexpectedException.fail -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Failing importer. -Info, T34284: Import root directory is './' -Info, T34284: Load D:/projects/assimp/test/models/3D/box_a.3d -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Unreal Mesh Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3D\' -Debug, T34284: UNREAL: data file is D:/projects/assimp/test/models/3D/box_d.3d -Debug, T34284: UNREAL: aniv file is D:/projects/assimp/test/models/3D/box_a.3d -Debug, T34284: UNREAL: uc file is D:/projects/assimp/test/models/3D/box.uc -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/3D/box_d.3d -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Unreal Mesh Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3D\' -Debug, T34284: UNREAL: data file is D:/projects/assimp/test/models/3D/box_d.3d -Debug, T34284: UNREAL: aniv file is D:/projects/assimp/test/models/3D/box_a.3d -Debug, T34284: UNREAL: uc file is D:/projects/assimp/test/models/3D/box.uc -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/3D/box.uc -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Unreal Mesh Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3D\' -Debug, T34284: UNREAL: data file is D:/projects/assimp/test/models/3D/box_d.3d -Debug, T34284: UNREAL: aniv file is D:/projects/assimp/test/models/3D/box_a.3d -Debug, T34284: UNREAL: uc file is D:/projects/assimp/test/models/3D/box.uc -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/3DS/fels.3ds -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Discreet 3DS Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3DS\' -Info, T34284: 3DS file format version: 3 -Warn, T34284: No hierarchy information has been found in the file. -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/3DS/testFormatDetection -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Discreet 3DS Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3DS\' -Info, T34284: 3DS file format version: 3 -Warn, T34284: 3DS: Skipping TCB animation info -Skipping one or more lines with the same contents -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/AC/closedLine.ac -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: AC3D Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' -Info, T34284: AC3D file format version: 11 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/AC/nosurfaces.ac -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: AC3D Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' -Info, T34284: AC3D file format version: 11 -Warn, T34284: AC3D: No material has been found -Info, T34284: AC3D: No surfaces defined in object definition, a point list is returned -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/AC/openLine.ac -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: AC3D Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' -Info, T34284: AC3D file format version: 11 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/AC/sample_subdiv.ac -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: AC3D Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' -Info, T34284: AC3D file format version: 11 -Info, T34284: AC3D: Evaluating subdivision surface: cylinder -Debug, T34284: Catmull-Clark Subdivider: got 130 bad edges touching only one face (totally 61505 edges). -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/AC/SphereWithLight.ac -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: AC3D Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' -Info, T34284: AC3D file format version: 11 -Debug, T34284: AC3D: Light source encountered -Info, T34284: AC3D: Evaluating subdivision surface: sphere -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/AC/SphereWithLight_UTF16LE.ac -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: AC3D Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' -Debug, T34284: Found UTF-16 BOM ... -Error, T34284: AC3D: No valid AC3D file, magic sequence not found -Info, T34284: Load D:/projects/assimp/test/models/AC/SphereWithLight_UTF8BOM.ac -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: AC3D Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' -Debug, T34284: Found UTF-8 BOM ... -Info, T34284: AC3D file format version: 11 -Debug, T34284: AC3D: Light source encountered -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/AC/SphereWithLightUvScaling4X.ac -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: AC3D Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' -Info, T34284: AC3D file format version: 11 -Debug, T34284: AC3D: Light source encountered -Info, T34284: AC3D: Evaluating subdivision surface: sphere -Debug, T34284: Catmull-Clark Subdivider: got 12 bad edges touching only one face (totally 17670 edges). -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/AC/Wuson.ac -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: AC3D Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' -Info, T34284: AC3D file format version: 11 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/AC/Wuson.acc -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: AC3D Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' -Info, T34284: AC3D file format version: 11 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/AC/TestFormatDetection -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: AC3D Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AC\' -Info, T34284: AC3D file format version: 11 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/AMF/test1.amf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Additive manufacturing file format(AMF) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AMF\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Adding default material 'DefaultMaterial' -Info, T34284: Load D:/projects/assimp/test/models/AMF/test_with_mat.amf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Additive manufacturing file format(AMF) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/AMF\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Adding default material 'DefaultMaterial' -Info, T34284: Load D:/projects/assimp/test/models/ASE/ThreeCubesGreen.ASE -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: ASE Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/ASE\' -Info, T34284: Line 1: Comment: AsciiExport-Version 2,00 - Tue May 06 17:21:38 2008 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/3MF/box.3mf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: 3mf Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3MF\' -Warn, T34284: Ignored file of unknown type: 3D/3dmodel.model -Warn, T34284: Ignored file of unsupported type CONTENT_TYPES_ARCHIVES[Content_Types].xml -Debug, T34284: 3D/3dmodel.model -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Adding default material 'DefaultMaterial' -Info, T34284: Load D:/projects/assimp/test/models/3MF/box.3mf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: 3mf Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3MF\' -Warn, T34284: Ignored file of unknown type: 3D/3dmodel.model -Warn, T34284: Ignored file of unsupported type CONTENT_TYPES_ARCHIVES[Content_Types].xml -Debug, T34284: 3D/3dmodel.model -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ScenePreprocessor: Adding default material 'DefaultMaterial' -Info, T34284: Load D:/projects/assimp/test/models/3MF/box.3mf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: 3mf Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/3MF\' -Warn, T34284: Ignored file of unknown type: 3D/3dmodel.model -Warn, T34284: Ignored file of unsupported type CONTENT_TYPES_ARCHIVES[Content_Types].xml -Debug, T34284: 3D/3dmodel.model -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ScenePreprocessor: Adding default material 'DefaultMaterial' -Info, T34284: Load test.3mf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: 3mf Importer. -Info, T34284: Import root directory is '.\' -Warn, T34284: Ignored file of unknown type: 3D/3DModel.model -Warn, T34284: Ignored file of unsupported type CONTENT_TYPES_ARCHIVES[Content_Types].xml -Debug, T34284: 3D/3DModel.model -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: Load D:/projects/assimp/test/models/Q3D/earth.q3o -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Quick3D Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/Q3D\' -Info, T34284: Quick3D File format version: 30 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/STL/Spider_ascii.stl -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/STL/Spider_ascii.stl -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/STL/Spider_ascii.stl -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/STL/formatDetection -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Found positive match for header keyword: solid -Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/STL/triangle_with_two_solids.stl -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/STL/triangle_with_empty_solid.stl -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' -Warn, T34284: STL: mesh is empty or invalid; no data loaded -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: Load D:/projects/assimp/test/models/STL/triangle_with_empty_solid.stl -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: (Deleting previous scene) -Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' -Warn, T34284: STL: mesh is empty or invalid; no data loaded -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Error, T34284: Validation failed: The mesh emptySolid contains no vertices -Info, T34284: Load D:/projects/assimp/test/models/STL/Spider_ascii.stl -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/STL\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: PretransformVerticesProcess begin -Debug, T34284: PretransformVerticesProcess finished -Info, T34284: Removed 2 nodes and 0 animation channels (1 output nodes) -Info, T34284: Kept 0 lights and 0 cameras. -Info, T34284: Moved 1 meshes to WCS (number of output meshes: 1) -Debug, T34284: TriangulateProcess begin -Debug, T34284: TriangulateProcess finished. There was nothing to be done. -Debug, T34284: GenFaceNormalsProcess begin -Debug, T34284: GenFaceNormalsProcess finished. Normals are already there -Info, T34284: Load spiderExport.stl -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: (Deleting previous scene) -Info, T34284: Found a matching importer for this file format: Stereolithography (STL) Importer. -Info, T34284: Import root directory is '.\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: TriangulateProcess begin -Debug, T34284: TriangulateProcess finished. There was nothing to be done. -Debug, T34284: GenFaceNormalsProcess begin -Info, T34284: Normal vectors are undefined for line and point meshes -Debug, T34284: GenFaceNormalsProcess finished. Normals are already there -Info, T34284: Load D:/projects/assimp/test/models/X/test.x -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Warn, T34284: Unknown data object in mesh in x file -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/X/OV_GetNextToken -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Info, T34284: Successfully decompressed MSZIP-compressed file -Error, T34284: Opening brace expected. -Info, T34284: Load D:/projects/assimp/test/models/X/anim_test.x -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Warn, T34284: Length of texture file name is zero. Skipping this texture. -Warn, T34284: Unknown data object in mesh in x file -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/X/BCN_Epileptic.X -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Warn, T34284: Unknown data object in animation of .x file -Skipping one or more lines with the same contents -Warn, T34284: Unknown data object in frame in x file -Skipping one or more lines with the same contents -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/X/fromtruespace_bin32.x -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Warn, T34284: Unknown data object in animation of .x file -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/X/kwxport_test_cubewithvcolors.x -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Warn, T34284: Unknown data object in animation of .x file -Skipping one or more lines with the same contents -Warn, T34284: Unknown data object in frame in x file -Warn, T34284: Unknown data object in mesh in x file -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/X/test_cube_binary.x -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/X/test_cube_compressed.x -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Info, T34284: Successfully decompressed MSZIP-compressed file -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/X/test_cube_text.x -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/X/Testwuson.X -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Warn, T34284: Unknown data object in animation of .x file -Skipping one or more lines with the same contents -Warn, T34284: Unknown data object in frame in x file -Skipping one or more lines with the same contents -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/X/TestFormatDetection -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X\' -Warn, T34284: Unknown data object in mesh in x file -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models-nonbsd/X/dwarf.x -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Direct3D XFile Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models-nonbsd/X\' -Debug, T34284: MakeLeftHandedProcess begin -Debug, T34284: MakeLeftHandedProcess finished -Debug, T34284: FlipWindingOrderProcess begin -Debug, T34284: FlipWindingOrderProcess finished -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: ScenePreprocessor: Dummy scaling track has been generated -Skipping one or more lines with the same contents -Info, T34284: Load D:/projects/assimp/test/models/X3D/ComputerKeyboard.x3d -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: File extension not known, trying signature-based detection -Info, T34284: Found a matching importer for this file format: Extensible 3D(X3D) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/X3D\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Error, T34284: Validation failed: aiScene::mNumMeshes is 0. At least one mesh must be there -Info, T34284: Load D:/projects/assimp/test/models/DXF/PinkEggFromLW.dxf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Drawing Interchange Format (DXF) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/DXF\' -Debug, T34284: DXF: got 0 entries in BLOCKS -Debug, T34284: DXF: got 288 polylines and 0 inserted blocks in ENTITIES -Debug, T34284: DXF: Unexpanded polycount is 288, vertex count is 1152 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/DXF/lineTest -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Found positive match for header keyword: SECTION -Info, T34284: Found a matching importer for this file format: Drawing Interchange Format (DXF) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/DXF\' -Info, T34284: DXF Comment: VISION3D DXF -Debug, T34284: DXF: got 0 entries in BLOCKS -Debug, T34284: DXF: got 3 polylines and 0 inserted blocks in ENTITIES -Debug, T34284: DXF: Unexpanded polycount is 3, vertex count is 9 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/DXF/issue_2229.dxf -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Drawing Interchange Format (DXF) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/DXF\' -Debug, T34284: DXF: skipped over control group (273 lines) -Debug, T34284: DXF: got 2 entries in BLOCKS -Warn, T34284: DXF: invalid vertex index, indices are one-based. -Skipping one or more lines with the same contents -Warn, T34284: DXF: unexpected face count in polymesh: 1173, expected 1174 -Warn, T34284: DXF: invalid vertex index, indices are one-based. -Skipping one or more lines with the same contents -Debug, T34284: DXF: got 4 polylines and 0 inserted blocks in ENTITIES -Debug, T34284: DXF: skipped over control group (3 lines) -Skipping one or more lines with the same contents -Debug, T34284: DXF: Unexpanded polycount is 1289, vertex count is 768 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/PLY/cube.ply -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' -Debug, T34284: PLY::DOM::ParseInstance() begin -Debug, T34284: PLY::DOM::ParseHeader() begin -Debug, T34284: PLY::DOM::ParseHeader() succeeded -Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin -Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded -Debug, T34284: PLY::DOM::ParseInstance() succeeded -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/PLY/cube.ply -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' -Debug, T34284: PLY::DOM::ParseInstance() begin -Debug, T34284: PLY::DOM::ParseHeader() begin -Debug, T34284: PLY::DOM::ParseHeader() succeeded -Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin -Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded -Debug, T34284: PLY::DOM::ParseInstance() succeeded -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: PretransformVerticesProcess begin -Debug, T34284: PretransformVerticesProcess finished -Info, T34284: Removed 1 nodes and 0 animation channels (1 output nodes) -Info, T34284: Kept 0 lights and 0 cameras. -Info, T34284: Moved 1 meshes to WCS (number of output meshes: 1) -Info, T34284: Load D:/projects/assimp/test/models/PLY/cube.ply -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' -Debug, T34284: PLY::DOM::ParseInstance() begin -Debug, T34284: PLY::DOM::ParseHeader() begin -Debug, T34284: PLY::DOM::ParseHeader() succeeded -Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin -Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded -Debug, T34284: PLY::DOM::ParseInstance() succeeded -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/PLY/cube.ply -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: (Deleting previous scene) -Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' -Debug, T34284: PLY::DOM::ParseInstance() begin -Debug, T34284: PLY::DOM::ParseHeader() begin -Debug, T34284: PLY::DOM::ParseHeader() succeeded -Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin -Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded -Debug, T34284: PLY::DOM::ParseInstance() succeeded -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/PLY/cube_uv.ply -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' -Debug, T34284: PLY::DOM::ParseInstance() begin -Debug, T34284: PLY::DOM::ParseHeader() begin -Debug, T34284: PLY::DOM::ParseHeader() succeeded -Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin -Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded -Debug, T34284: PLY::DOM::ParseInstance() succeeded -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/PLY/cube_binary.ply -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' -Debug, T34284: PLY::DOM::ParseInstanceBinary() begin -Debug, T34284: PLY::DOM::ParseHeader() begin -Debug, T34284: PLY::DOM::ParseHeader() succeeded -Debug, T34284: PLY::DOM::ParseElementInstanceListsBinary() begin -Debug, T34284: PLY::DOM::ParseElementInstanceListsBinary() succeeded -Debug, T34284: PLY::DOM::ParseInstanceBinary() succeeded -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/PLY/float-color.ply -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' -Debug, T34284: PLY::DOM::ParseInstance() begin -Debug, T34284: PLY::DOM::ParseHeader() begin -Debug, T34284: PLY::DOM::ParseHeader() succeeded -Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin -Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded -Debug, T34284: PLY::DOM::ParseInstance() succeeded -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/PLY/issue623.ply -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/PLY\' -Debug, T34284: PLY::DOM::ParseInstance() begin -Debug, T34284: PLY::DOM::ParseHeader() begin -Debug, T34284: PLY::DOM::ParseHeader() succeeded -Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin -Warn, T34284: Unable to parse property instance. Skipping this element instance -Skipping one or more lines with the same contents -Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded -Debug, T34284: PLY::DOM::ParseInstance() succeeded -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: Load $$$___magic___$$$. -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Debug, T34284: Found positive match for header keyword: ply -Info, T34284: Found a matching importer for this file format: Stanford Polygon Library (PLY) Importer. -Info, T34284: Import root directory is '.\' -Debug, T34284: PLY::DOM::ParseInstance() begin -Debug, T34284: PLY::DOM::ParseHeader() begin -Debug, T34284: PLY::DOM::ParseHeader() succeeded -Debug, T34284: PLY::DOM::ParseElementInstanceLists() begin -Debug, T34284: PLY::DOM::ParseElementInstanceLists() succeeded -Debug, T34284: PLY::DOM::ParseInstance() succeeded -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: Load D:/projects/assimp/test/models/OBJ/spider.obj -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/OBJ/spider.obj -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: GenVertexNormalsProcess begin -Debug, T34284: GenVertexNormalsProcess finished. Normals are already there -Debug, T34284: GenVertexNormalsProcess begin -Debug, T34284: GenVertexNormalsProcess finished. Normals are already there -Info, T34284: Load $$$___magic___$$$. -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: File extension not known, trying signature-based detection -Debug, T34284: Found positive match for header keyword: usemtl -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is '.\' -Error, T34284: OBJ: failed to locate material Default, creating new material -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: Load $$$___magic___$$$. -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: File extension not known, trying signature-based detection -Debug, T34284: Found positive match for header keyword: usemtl -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is '.\' -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: Load D:/projects/assimp/test/models/OBJ/cube_with_vertexcolors.obj -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: GenVertexNormalsProcess begin -Debug, T34284: GenVertexNormalsProcess finished. Normals are already there -Info, T34284: Load D:/projects/assimp/test/models/OBJ/cube_with_vertexcolors_uni.obj -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Debug, T34284: GenVertexNormalsProcess begin -Debug, T34284: GenVertexNormalsProcess finished. Normals are already there -Info, T34284: Load $$$___magic___$$$. -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: File extension not known, trying signature-based detection -Debug, T34284: Found positive match for header keyword: mtllib -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is '.\' -Error, T34284: OBJ: Unable to locate material file $blobfile.mtl -Info, T34284: OBJ: Opening fallback material file $$$___magic___$mtl -Error, T34284: OBJ: Unable to locate fallback material file $$$___magic___$mtl -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load $$$___magic___$$$. -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: File extension not known, trying signature-based detection -Debug, T34284: Found positive match for header keyword: v -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is '.\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Error, T34284: Validation failed: Mesh contains no faces -Info, T34284: Load $$$___magic___$$$. -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: File extension not known, trying signature-based detection -Debug, T34284: Found positive match for header keyword: v -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is '.\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load $$$___magic___$$$. -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: File extension not known, trying signature-based detection -Debug, T34284: Found positive match for header keyword: v -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is '.\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load $$$___magic___$$$. -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: File extension not known, trying signature-based detection -Debug, T34284: Found positive match for header keyword: v -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is '.\' -Error, T34284: OBJ: Invalid component in homogeneous vector (Division by zero) -Info, T34284: Load $$$___magic___$$$. -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: File extension not known, trying signature-based detection -Debug, T34284: Found positive match for header keyword: v -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is '.\' -Error, T34284: OBJ: Invalid face indice -Info, T34284: Load $$$___magic___$$$. -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: File extension not known, trying signature-based detection -Debug, T34284: Found positive match for header keyword: v -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is '.\' -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: Load $$$___magic___$$$. -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: File extension not known, trying signature-based detection -Debug, T34284: Found positive match for header keyword: v -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is '.\' -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: Load D:/projects/assimp/test/models/OBJ/cube_mtllib_after_g.obj -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' -Error, T34284: OBJ: Unable to locate material file cube_mtllib_after_g.mat -Info, T34284: OBJ: Opening fallback material file D:/projects/assimp/test/models/OBJ/cube_mtllib_after_g.mtl -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/OBJ/point_cloud.obj -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ScenePreprocessor: Adding default material 'DefaultMaterial' -Info, T34284: Load D:/projects/assimp/test/models/OBJ/box_without_lineending.obj -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OBJ\' -Error, T34284: OBJ: failed to locate material Default, creating new material -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: Load $$$___magic___$$$. -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: File extension not known, trying signature-based detection -Debug, T34284: Found positive match for header keyword: v -Info, T34284: Found a matching importer for this file format: Wavefront Object Importer. -Info, T34284: Import root directory is '.\' -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: Load D:/projects/assimp/test/models/OpenGEX/Example.ogex -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Open Game Engine Exchange. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OpenGEX\' -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: Load D:/projects/assimp/test/models/OpenGEX/light_issue1262.ogex -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Open Game Engine Exchange. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OpenGEX\' -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: Load D:/projects/assimp/test/models/OpenGEX/empty_camera.ogex -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Open Game Engine Exchange. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/OpenGEX\' -Debug, T34284: UpdateImporterScale scale set: 1 -Info, T34284: Load D:/projects/assimp/test/models/SIB/heffalump.sib -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Silo SIB Importer. -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/SIB\' -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/BLEND/AreaLight_269.blend -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Blender 3D Importer (http://www.blender3d.org). -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/BLEND\' -Info, T34284: BLEND: Blender version is 2.69 (64bit: true, little endian: true) -Debug, T34284: REND -Debug, T34284: TEST -Debug, T34284: GLOB -Debug, T34284: WM -Debug, T34284: DATA -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SC -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: CA -Debug, T34284: LA -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: LA -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: LA -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: WO -Debug, T34284: DATA -Debug, T34284: OB -Skipping one or more lines with the same contents -Debug, T34284: DATA -Debug, T34284: OB -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: OB -Debug, T34284: MA -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: TE -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: ME -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: LS -Debug, T34284: DATA -Debug, T34284: DNA1 -Debug, T34284: BlenderDNA: Got 551 structures with totally 6353 fields -Info, T34284: BlenderDNA: Dumped dna to dna.txt -Debug, T34284: ENDB -Warn, T34284: -Skipping one or more lines with the same contents -Warn, T34284: BlendDNA: Did not find a field named `coeff_const` in structure `Lamp` -Warn, T34284: BlendDNA: Did not find a field named `coeff_lin` in structure `Lamp` -Warn, T34284: BlendDNA: Did not find a field named `coeff_quad` in structure `Lamp` -Warn, T34284: -Skipping one or more lines with the same contents -Warn, T34284: BlendDNA: Did not find a field named `coeff_const` in structure `Lamp` -Warn, T34284: BlendDNA: Did not find a field named `coeff_lin` in structure `Lamp` -Warn, T34284: BlendDNA: Did not find a field named `coeff_quad` in structure `Lamp` -Warn, T34284: -Skipping one or more lines with the same contents -Warn, T34284: BlendDNA: Did not find a field named `coeff_const` in structure `Lamp` -Warn, T34284: BlendDNA: Did not find a field named `coeff_lin` in structure `Lamp` -Warn, T34284: BlendDNA: Did not find a field named `coeff_quad` in structure `Lamp` -Info, T34284: (Stats) Fields read: 918, pointers resolved: 23, cache hits: 3, cached objects: 19 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Warn, T34284: Validation warning: aiLight::mAttenuationXXX - all are zero -Skipping one or more lines with the same contents -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/BLEND/box.blend -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Blender 3D Importer (http://www.blender3d.org). -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/BLEND\' -Info, T34284: BLEND: Blender version is 2.76 (64bit: true, little endian: true) -Debug, T34284: REND -Debug, T34284: TEST -Debug, T34284: GLOB -Debug, T34284: WM -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SC -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: CA -Debug, T34284: LA -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: WO -Debug, T34284: DATA -Debug, T34284: OB -Debug, T34284: DATA -Debug, T34284: OB -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: OB -Debug, T34284: DATA -Debug, T34284: MA -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: TE -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: ME -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: LS -Debug, T34284: DATA -Debug, T34284: DNA1 -Debug, T34284: BlenderDNA: Got 604 structures with totally 6955 fields -Info, T34284: BlenderDNA: Dumped dna to dna.txt -Debug, T34284: ENDB -Warn, T34284: -Skipping one or more lines with the same contents -Warn, T34284: BlendDNA: Did not find a field named `coeff_const` in structure `Lamp` -Warn, T34284: BlendDNA: Did not find a field named `coeff_lin` in structure `Lamp` -Warn, T34284: BlendDNA: Did not find a field named `coeff_quad` in structure `Lamp` -Info, T34284: (Stats) Fields read: 668, pointers resolved: 17, cache hits: 3, cached objects: 13 -Debug, T34284: UpdateImporterScale scale set: 1 -Debug, T34284: ValidateDataStructureProcess begin -Warn, T34284: Validation warning: aiLight::mAttenuationXXX - all are zero -Debug, T34284: ValidateDataStructureProcess end -Info, T34284: Load D:/projects/assimp/test/models/BLEND/4Cubes4Mats_248.blend -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Blender 3D Importer (http://www.blender3d.org). -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/BLEND\' -Info, T34284: BLEND: Blender version is 2.48 (64bit: false, little endian: true) -Debug, T34284: REND -Debug, T34284: GLOB -Debug, T34284: SR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SC -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: CA -Debug, T34284: LA -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: WO -Debug, T34284: TX -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: OB -Skipping one or more lines with the same contents -Debug, T34284: DATA -Debug, T34284: OB -Debug, T34284: DATA -Debug, T34284: OB -Debug, T34284: DATA -Debug, T34284: OB -Debug, T34284: DATA -Debug, T34284: OB -Debug, T34284: MA -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: MA -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: MA -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: MA -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: TE -Debug, T34284: ME -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: ME -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: ME -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: ME -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: DNA1 -Debug, T34284: BlenderDNA: Got 310 structures with totally 3868 fields -Info, T34284: BlenderDNA: Dumped dna to dna.txt -Debug, T34284: ENDB -Warn, T34284: -Skipping one or more lines with the same contents -Warn, T34284: BlendDNA: Did not find a field named `sensor_x` in structure `Camera` -Warn, T34284: -Skipping one or more lines with the same contents -Warn, T34284: BlendDNA: Did not find a field named `totloop` in structure `Mesh` -Warn, T34284: BlendDNA: Did not find a field named `totpoly` in structure `Mesh` -Warn, T34284: BlendDNA: Did not find a field named `*mloop` in structure `Mesh` -Warn, T34284: BlendDNA: Did not find a field named `*mloopuv` in structure `Mesh` -Warn, T34284: BlendDNA: Did not find a field named `*mloopcol` in structure `Mesh` -Warn, T34284: BlendDNA: Did not find a field named `*mpoly` in structure `Mesh` -Warn, T34284: BlendDNA: Did not find a field named `*mtpoly` in structure `Mesh` -Warn, T34284: -Skipping one or more lines with the same contents -Warn, T34284: BlendDNA: Did not find a field named `rot` in structure `MTex` -Warn, T34284: BlendDNA: Did not find a field named `colspecfac` in structure `MTex` -Warn, T34284: BlendDNA: Did not find a field named `mirrfac` in structure `MTex` -Warn, T34284: BlendDNA: Did not find a field named `alphafac` in structure `MTex` -Warn, T34284: BlendDNA: Did not find a field named `difffac` in structure `MTex` -Warn, T34284: BlendDNA: Did not find a field named `specfac` in structure `MTex` -Warn, T34284: BlendDNA: Did not find a field named `emitfac` in structure `MTex` -Warn, T34284: BlendDNA: Did not find a field named `hardfac` in structure `MTex` -Warn, T34284: BlendDNA: Did not find a field named `material_type` in structure `Material` -Warn, T34284: BlendDNA: Did not find a field named `pr_texture` in structure `Material` -Warn, T34284: BlendDNA: Did not find a field named `shadowonly_flag` in structure `Material` -Warn, T34284: BlendDNA: Did not find a field named `index` in structure `Material` -Warn, T34284: BlendDNA: Did not find a field named `vcol_alpha` in structure `Material` -Warn, T34284: BlendDNA: Did not find a field named `typemap` in structure `CustomData` -Error, T34284: Constructing BlenderDNA Structure encountered an error -Info, T34284: Load D:/projects/assimp/test/models/BLEND/blender_269_regress1.blend -Debug, T34284: Assimp 5.0.3419303229 x86 msvc debug shared singlethreadedsingle : -Info, T34284: Found a matching importer for this file format: Blender 3D Importer (http://www.blender3d.org). -Info, T34284: Import root directory is 'D:/projects/assimp/test/models/BLEND\' -Info, T34284: BLEND: Blender version is 2.69 (64bit: true, little endian: true) -Debug, T34284: REND -Debug, T34284: TEST -Debug, T34284: GLOB -Debug, T34284: WM -Debug, T34284: DATA -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SN -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: SC -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: CA -Debug, T34284: LA -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: KE -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: WO -Debug, T34284: DATA -Debug, T34284: OB -Debug, T34284: DATA -Debug, T34284: OB -Debug, T34284: DATA -Debug, T34284: OB -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: MA -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: TE -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: ME -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: BR -Debug, T34284: DATA -Skipping one or more lines with the same contents -Debug, T34284: LS -Debug, T34284: DATA -Debug, T34284: DNA1 -Debug, T34284: BlenderDNA: Got 551 structures with totally 6353 fields -Info, T34284: BlenderDNA: Dumped dna to dna.txt From 0aadd8a3f98549bc6c39a7be047a63fb0c326544 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 5 May 2021 15:10:59 +0200 Subject: [PATCH 070/335] Delete spiderExport.stl --- test/spiderExport.stl | 10946 ---------------------------------------- 1 file changed, 10946 deletions(-) delete mode 100644 test/spiderExport.stl diff --git a/test/spiderExport.stl b/test/spiderExport.stl deleted file mode 100644 index f9eb2d538..000000000 --- a/test/spiderExport.stl +++ /dev/null @@ -1,10946 +0,0 @@ -solid AssimpScene - facet normal 0.468281955 -0.863497853 -0.187305972 - outer loop - vertex 0.907127976 0.646165013 0.795193017 - vertex 1.65540099 1.11156702 0.520398021 - vertex 0.766146004 0.680482984 0.284518987 - endloop - endfacet - - facet normal 0.373240978 -0.860315859 -0.347199947 - outer loop - vertex 1.85664499 0.887426019 1.21811604 - vertex 0.907127976 0.646165013 0.795193017 - vertex 1.31394804 0.554956019 1.45853198 - endloop - endfacet - - facet normal -0.148454979 -0.953196883 -0.263394982 - outer loop - vertex 1.65540099 1.11156702 0.520398021 - vertex 1.85664499 0.887426019 1.21811604 - vertex 2.33923101 0.936287999 0.769291997 - endloop - endfacet - - facet normal 0.383431226 -0.84137249 -0.38088423 - outer loop - vertex 1.85664499 0.887426019 1.21811604 - vertex 1.65540099 1.11156702 0.520398021 - vertex 0.907127976 0.646165013 0.795193017 - endloop - endfacet - - facet normal 0.562677324 -0.799798489 -0.209085122 - outer loop - vertex 0.451615006 0.391451001 0.543682992 - vertex 0.907127976 0.646165013 0.795193017 - vertex 0.766146004 0.680482984 0.284518987 - endloop - endfacet - - facet normal 0.807133079 -0.364618987 -0.46431601 - outer loop - vertex 0.681457996 0.234028995 1.06684601 - vertex 0.451615006 0.391451001 0.543682992 - vertex 0.382212013 -0.0206840001 0.746681988 - endloop - endfacet - - facet normal 0.612119138 -0.640684068 -0.463502079 - outer loop - vertex 0.907127976 0.646165013 0.795193017 - vertex 0.681457996 0.234028995 1.06684601 - vertex 1.31394804 0.554956019 1.45853198 - endloop - endfacet - - facet normal 0.613333642 -0.640477657 -0.462179691 - outer loop - vertex 0.681457996 0.234028995 1.06684601 - vertex 0.907127976 0.646165013 0.795193017 - vertex 0.451615006 0.391451001 0.543682992 - endloop - endfacet - - facet normal 0.730570912 0 -0.68283695 - outer loop - vertex 0.681457996 -0.275397986 1.06684601 - vertex 0.681457996 0.234028995 1.06684601 - vertex 0.382212013 -0.0206840001 0.746681988 - endloop - endfacet - - facet normal 0.622255981 0.257773995 -0.739154994 - outer loop - vertex 1.25399899 -0.0620529987 1.62324095 - vertex 0.681457996 -0.275397986 1.06684601 - vertex 1.31394804 -0.679062009 1.45853198 - endloop - endfacet - - facet normal 0.600341976 -0.260203004 -0.756230056 - outer loop - vertex 0.681457996 0.234028995 1.06684601 - vertex 1.25399899 -0.0620529987 1.62324095 - vertex 1.31394804 0.554956019 1.45853198 - endloop - endfacet - - facet normal 0.696921945 0 -0.717146993 - outer loop - vertex 1.25399899 -0.0620529987 1.62324095 - vertex 0.681457996 0.234028995 1.06684601 - vertex 0.681457996 -0.275397986 1.06684601 - endloop - endfacet - - facet normal 0.168448105 -0.269475162 -0.948160529 - outer loop - vertex 1.25399899 -0.0620529987 1.62324095 - vertex 1.98102093 0.300615013 1.64932895 - vertex 1.31394804 0.554956019 1.45853198 - endloop - endfacet - - facet normal 0.168448105 0.269475162 -0.948160529 - outer loop - vertex 1.98102093 -0.424721986 1.64932895 - vertex 1.25399899 -0.0620529987 1.62324095 - vertex 1.31394804 -0.679062009 1.45853198 - endloop - endfacet - - facet normal -0.248866931 0 -0.968537748 - outer loop - vertex 1.98102093 0.300615013 1.64932895 - vertex 1.98102093 -0.424721986 1.64932895 - vertex 2.55083203 -0.0620529987 1.50291502 - endloop - endfacet - - facet normal 0.0358599909 0 -0.999356866 - outer loop - vertex 1.98102093 -0.424721986 1.64932895 - vertex 1.98102093 0.300615013 1.64932895 - vertex 1.25399899 -0.0620529987 1.62324095 - endloop - endfacet - - facet normal 0.00512400037 -0.591441095 -0.806332052 - outer loop - vertex 1.98102093 0.300615013 1.64932895 - vertex 1.85664499 0.887426019 1.21811604 - vertex 1.31394804 0.554956019 1.45853198 - endloop - endfacet - - facet normal -0.432703078 -0.343308091 -0.83361119 - outer loop - vertex 2.58366799 0.524757028 1.24420404 - vertex 1.98102093 0.300615013 1.64932895 - vertex 2.55083203 -0.0620529987 1.50291502 - endloop - endfacet - - facet normal -0.375216067 -0.787361085 -0.489158064 - outer loop - vertex 1.85664499 0.887426019 1.21811604 - vertex 2.58366799 0.524757028 1.24420404 - vertex 2.33923101 0.936287999 0.769291997 - endloop - endfacet - - facet normal -0.275769085 -0.606463194 -0.745757163 - outer loop - vertex 2.58366799 0.524757028 1.24420404 - vertex 1.85664499 0.887426019 1.21811604 - vertex 1.98102093 0.300615013 1.64932895 - endloop - endfacet - - facet normal -0.241510943 -0.3801229 -0.892848849 - outer loop - vertex 3.11489511 -0.0620529987 1.35033894 - vertex 2.58366799 0.524757028 1.24420404 - vertex 2.55083203 -0.0620529987 1.50291502 - endloop - endfacet - - facet normal -0.950931132 -0.278835028 0.134094015 - outer loop - vertex 2.83174801 0.524757028 0.562609017 - vertex 3.11489511 -0.0620529987 1.35033894 - vertex 2.97289085 -0.0620529987 0.343317986 - endloop - endfacet - - facet normal -0.680309772 -0.68983078 -0.247612908 - outer loop - vertex 2.58366799 0.524757028 1.24420404 - vertex 2.83174801 0.524757028 0.562609017 - vertex 2.33923101 0.936287999 0.769291997 - endloop - endfacet - - facet normal -0.69412154 -0.67406857 -0.25263983 - outer loop - vertex 2.83174801 0.524757028 0.562609017 - vertex 2.58366799 0.524757028 1.24420404 - vertex 3.11489511 -0.0620529987 1.35033894 - endloop - endfacet - - facet normal -0.241510943 0.3801229 -0.892848849 - outer loop - vertex 2.58366799 -0.648863018 1.24420404 - vertex 3.11489511 -0.0620529987 1.35033894 - vertex 2.55083203 -0.0620529987 1.50291502 - endloop - endfacet - - facet normal -0.680310249 0.689830244 -0.247613087 - outer loop - vertex 2.83174801 -0.648863018 0.562609017 - vertex 2.58366799 -0.648863018 1.24420404 - vertex 2.33923101 -1.060395 0.769291997 - endloop - endfacet - - facet normal -0.950931132 0.278835028 0.134094015 - outer loop - vertex 3.11489511 -0.0620529987 1.35033894 - vertex 2.83174801 -0.648863018 0.562609017 - vertex 2.97289085 -0.0620529987 0.343317986 - endloop - endfacet - - facet normal -0.69412154 0.67406857 -0.25263983 - outer loop - vertex 2.83174801 -0.648863018 0.562609017 - vertex 3.11489511 -0.0620529987 1.35033894 - vertex 2.58366799 -0.648863018 1.24420404 - endloop - endfacet - - facet normal 0.00512400037 0.591441095 -0.806332052 - outer loop - vertex 1.85664499 -1.01153195 1.21811604 - vertex 1.98102093 -0.424721986 1.64932895 - vertex 1.31394804 -0.679062009 1.45853198 - endloop - endfacet - - facet normal -0.375215858 0.787360668 -0.489158779 - outer loop - vertex 2.58366799 -0.648863018 1.24420404 - vertex 1.85664499 -1.01153195 1.21811604 - vertex 2.33923101 -1.060395 0.769291997 - endloop - endfacet - - facet normal -0.432703078 0.343308091 -0.83361119 - outer loop - vertex 1.98102093 -0.424721986 1.64932895 - vertex 2.58366799 -0.648863018 1.24420404 - vertex 2.55083203 -0.0620529987 1.50291502 - endloop - endfacet - - facet normal -0.275769085 0.606463194 -0.745757163 - outer loop - vertex 2.58366799 -0.648863018 1.24420404 - vertex 1.98102093 -0.424721986 1.64932895 - vertex 1.85664499 -1.01153195 1.21811604 - endloop - endfacet - - facet normal 0.415251076 0.870015144 -0.265782058 - outer loop - vertex 0.907127976 -0.687533975 0.795193017 - vertex 1.85664499 -1.01153195 1.21811604 - vertex 1.31394804 -0.679062009 1.45853198 - endloop - endfacet - - facet normal 0.529567182 0.823971212 -0.201569065 - outer loop - vertex 1.65540099 -1.23567402 0.520398021 - vertex 0.907127976 -0.687533975 0.795193017 - vertex 0.766146004 -0.721850991 0.284518987 - endloop - endfacet - - facet normal -0.148454979 0.953196883 -0.263394982 - outer loop - vertex 1.85664499 -1.01153195 1.21811604 - vertex 1.65540099 -1.23567402 0.520398021 - vertex 2.33923101 -1.060395 0.769291997 - endloop - endfacet - - facet normal 0.447698891 0.805645823 -0.387943923 - outer loop - vertex 1.65540099 -1.23567402 0.520398021 - vertex 1.85664499 -1.01153195 1.21811604 - vertex 0.907127976 -0.687533975 0.795193017 - endloop - endfacet - - facet normal 0.410494119 0.855419278 0.315836102 - outer loop - vertex 1.04242694 -0.687533975 -0.167512998 - vertex 1.65540099 -1.23567402 0.520398021 - vertex 0.766146004 -0.721850991 0.284518987 - endloop - endfacet - - facet normal 0.11889904 0.867570281 0.482892126 - outer loop - vertex 2.2580471 -1.01153195 0.115272999 - vertex 1.04242694 -0.687533975 -0.167512998 - vertex 1.99685407 -0.679062009 -0.417735994 - endloop - endfacet - - facet normal -0.283029974 0.953196943 0.106346995 - outer loop - vertex 1.65540099 -1.23567402 0.520398021 - vertex 2.2580471 -1.01153195 0.115272999 - vertex 2.33923101 -1.060395 0.769291997 - endloop - endfacet - - facet normal 0.0833719745 0.8142398 0.574510813 - outer loop - vertex 2.2580471 -1.01153195 0.115272999 - vertex 1.65540099 -1.23567402 0.520398021 - vertex 1.04242694 -0.687533975 -0.167512998 - endloop - endfacet - - facet normal -0.514374912 0.591440976 0.620979965 - outer loop - vertex 2.63050294 -0.424721986 -0.135107994 - vertex 2.2580471 -1.01153195 0.115272999 - vertex 1.99685407 -0.679062009 -0.417735994 - endloop - endfacet - - facet normal -0.867304265 0.343309104 0.360447109 - outer loop - vertex 2.83174801 -0.648863018 0.562609017 - vertex 2.63050294 -0.424721986 -0.135107994 - vertex 2.97289085 -0.0620529987 0.343317986 - endloop - endfacet - - facet normal -0.601856828 0.787360728 0.133533955 - outer loop - vertex 2.2580471 -1.01153195 0.115272999 - vertex 2.83174801 -0.648863018 0.562609017 - vertex 2.33923101 -1.060395 0.769291997 - endloop - endfacet - - facet normal -0.690615118 0.606463134 0.394022048 - outer loop - vertex 2.83174801 -0.648863018 0.562609017 - vertex 2.2580471 -1.01153195 0.115272999 - vertex 2.63050294 -0.424721986 -0.135107994 - endloop - endfacet - - facet normal -0.480426788 0.269474924 0.834609628 - outer loop - vertex 2.05680299 -0.0620529987 -0.582445025 - vertex 2.63050294 -0.424721986 -0.135107994 - vertex 1.99685407 -0.679062009 -0.417735994 - endloop - endfacet - - facet normal -0.480426788 -0.269474924 0.834609628 - outer loop - vertex 2.63050294 0.300615013 -0.135107994 - vertex 2.05680299 -0.0620529987 -0.582445025 - vertex 1.99685407 0.554956019 -0.417735994 - endloop - endfacet - - facet normal -0.813206792 0 0.581974864 - outer loop - vertex 2.63050294 -0.424721986 -0.135107994 - vertex 2.63050294 0.300615013 -0.135107994 - vertex 2.97289085 -0.0620529987 0.343317986 - endloop - endfacet - - facet normal -0.614903986 0 0.788601995 - outer loop - vertex 2.63050294 0.300615013 -0.135107994 - vertex 2.63050294 -0.424721986 -0.135107994 - vertex 2.05680299 -0.0620529987 -0.582445025 - endloop - endfacet - - facet normal 0.0294839889 0.255127907 0.966457665 - outer loop - vertex 0.900376976 -0.275397986 -0.490844995 - vertex 2.05680299 -0.0620529987 -0.582445025 - vertex 1.99685407 -0.679062009 -0.417735994 - endloop - endfacet - - facet normal 0.514054716 0 0.857757449 - outer loop - vertex 0.900376976 0.234028995 -0.490844995 - vertex 0.900376976 -0.275397986 -0.490844995 - vertex 0.524474978 -0.0206840001 -0.265567005 - endloop - endfacet - - facet normal 0.0107639972 -0.256923914 0.966371715 - outer loop - vertex 2.05680299 -0.0620529987 -0.582445025 - vertex 0.900376976 0.234028995 -0.490844995 - vertex 1.99685407 0.554956019 -0.417735994 - endloop - endfacet - - facet normal 0.0789619684 0 0.996877551 - outer loop - vertex 0.900376976 0.234028995 -0.490844995 - vertex 2.05680299 -0.0620529987 -0.582445025 - vertex 0.900376976 -0.275397986 -0.490844995 - endloop - endfacet - - facet normal 0.647883713 -0.364618838 0.668804705 - outer loop - vertex 0.535234988 0.391451001 -0.0513020009 - vertex 0.900376976 0.234028995 -0.490844995 - vertex 0.524474978 -0.0206840001 -0.265567005 - endloop - endfacet - - facet normal 0.483248174 -0.79979831 0.356081158 - outer loop - vertex 1.04242694 0.646165013 -0.167512998 - vertex 0.535234988 0.391451001 -0.0513020009 - vertex 0.766146004 0.680482984 0.284518987 - endloop - endfacet - - facet normal 0.136986926 -0.640222669 0.755876541 - outer loop - vertex 0.900376976 0.234028995 -0.490844995 - vertex 1.04242694 0.646165013 -0.167512998 - vertex 1.99685407 0.554956019 -0.417735994 - endloop - endfacet - - facet normal 0.462180048 -0.640478075 0.613333046 - outer loop - vertex 1.04242694 0.646165013 -0.167512998 - vertex 0.900376976 0.234028995 -0.490844995 - vertex 0.535234988 0.391451001 -0.0513020009 - endloop - endfacet - - facet normal 0.355838954 -0.890011907 0.285056978 - outer loop - vertex 1.65540099 1.11156702 0.520398021 - vertex 1.04242694 0.646165013 -0.167512998 - vertex 0.766146004 0.680482984 0.284518987 - endloop - endfacet - - facet normal -0.283029974 -0.953196943 0.106346995 - outer loop - vertex 2.2580471 0.887426019 0.115272999 - vertex 1.65540099 1.11156702 0.520398021 - vertex 2.33923101 0.936287999 0.769291997 - endloop - endfacet - - facet normal 0.0517080128 -0.85851717 0.510171115 - outer loop - vertex 1.04242694 0.646165013 -0.167512998 - vertex 2.2580471 0.887426019 0.115272999 - vertex 1.99685407 0.554956019 -0.417735994 - endloop - endfacet - - facet normal 0.0438020192 -0.845141351 0.532745183 - outer loop - vertex 2.2580471 0.887426019 0.115272999 - vertex 1.04242694 0.646165013 -0.167512998 - vertex 1.65540099 1.11156702 0.520398021 - endloop - endfacet - - facet normal -0.514374912 -0.591440976 0.620979965 - outer loop - vertex 2.2580471 0.887426019 0.115272999 - vertex 2.63050294 0.300615013 -0.135107994 - vertex 1.99685407 0.554956019 -0.417735994 - endloop - endfacet - - facet normal -0.601856947 -0.787360847 0.133532986 - outer loop - vertex 2.83174801 0.524757028 0.562609017 - vertex 2.2580471 0.887426019 0.115272999 - vertex 2.33923101 0.936287999 0.769291997 - endloop - endfacet - - facet normal -0.867304265 -0.343309104 0.360447109 - outer loop - vertex 2.63050294 0.300615013 -0.135107994 - vertex 2.83174801 0.524757028 0.562609017 - vertex 2.97289085 -0.0620529987 0.343317986 - endloop - endfacet - - facet normal -0.690614581 -0.606463671 0.394021749 - outer loop - vertex 2.83174801 0.524757028 0.562609017 - vertex 2.63050294 0.300615013 -0.135107994 - vertex 2.2580471 0.887426019 0.115272999 - endloop - endfacet - - facet normal 0.964517593 -0.227995917 -0.133129939 - outer loop - vertex 0.451615006 0.391451001 0.543682992 - vertex 0.309563994 -0.0206840001 0.220350996 - vertex 0.382212013 -0.0206840001 0.746681988 - endloop - endfacet - - facet normal 0.716925919 -0.689829946 0.100756995 - outer loop - vertex 0.535234988 0.391451001 -0.0513020009 - vertex 0.451615006 0.391451001 0.543682992 - vertex 0.766146004 0.680482984 0.284518987 - endloop - endfacet - - facet normal 0.890458107 -0.227996066 0.393830061 - outer loop - vertex 0.309563994 -0.0206840001 0.220350996 - vertex 0.535234988 0.391451001 -0.0513020009 - vertex 0.524474978 -0.0206840001 -0.265567005 - endloop - endfacet - - facet normal 0.90287751 -0.41074425 0.126891062 - outer loop - vertex 0.535234988 0.391451001 -0.0513020009 - vertex 0.309563994 -0.0206840001 0.220350996 - vertex 0.451615006 0.391451001 0.543682992 - endloop - endfacet - - facet normal 0.964517593 0.227995917 -0.133129939 - outer loop - vertex 0.309563994 -0.0206840001 0.220350996 - vertex 0.451615006 -0.432819992 0.543682992 - vertex 0.382212013 -0.0206840001 0.746681988 - endloop - endfacet - - facet normal 0.890458107 0.227996066 0.393830061 - outer loop - vertex 0.535234988 -0.432819992 -0.0513020009 - vertex 0.309563994 -0.0206840001 0.220350996 - vertex 0.524474978 -0.0206840001 -0.265567005 - endloop - endfacet - - facet normal 0.716925919 0.689829946 0.100756995 - outer loop - vertex 0.451615006 -0.432819992 0.543682992 - vertex 0.535234988 -0.432819992 -0.0513020009 - vertex 0.766146004 -0.721850991 0.284518987 - endloop - endfacet - - facet normal 0.90287751 0.41074425 0.126891062 - outer loop - vertex 0.535234988 -0.432819992 -0.0513020009 - vertex 0.451615006 -0.432819992 0.543682992 - vertex 0.309563994 -0.0206840001 0.220350996 - endloop - endfacet - - facet normal 0.807133079 0.364618987 -0.46431601 - outer loop - vertex 0.451615006 -0.432819992 0.543682992 - vertex 0.681457996 -0.275397986 1.06684601 - vertex 0.382212013 -0.0206840001 0.746681988 - endloop - endfacet - - facet normal 0.562677264 0.79979831 -0.209086075 - outer loop - vertex 0.907127976 -0.687533975 0.795193017 - vertex 0.451615006 -0.432819992 0.543682992 - vertex 0.766146004 -0.721850991 0.284518987 - endloop - endfacet - - facet normal 0.657568097 0.631190121 -0.411343098 - outer loop - vertex 0.681457996 -0.275397986 1.06684601 - vertex 0.907127976 -0.687533975 0.795193017 - vertex 1.31394804 -0.679062009 1.45853198 - endloop - endfacet - - facet normal 0.613333642 0.640477657 -0.462179691 - outer loop - vertex 0.907127976 -0.687533975 0.795193017 - vertex 0.681457996 -0.275397986 1.06684601 - vertex 0.451615006 -0.432819992 0.543682992 - endloop - endfacet - - facet normal 0.188311026 0.645559072 0.740130126 - outer loop - vertex 1.04242694 -0.687533975 -0.167512998 - vertex 0.900376976 -0.275397986 -0.490844995 - vertex 1.99685407 -0.679062009 -0.417735994 - endloop - endfacet - - facet normal 0.483248174 0.79979831 0.356081158 - outer loop - vertex 0.535234988 -0.432819992 -0.0513020009 - vertex 1.04242694 -0.687533975 -0.167512998 - vertex 0.766146004 -0.721850991 0.284518987 - endloop - endfacet - - facet normal 0.647883177 0.364619046 0.668805122 - outer loop - vertex 0.900376976 -0.275397986 -0.490844995 - vertex 0.535234988 -0.432819992 -0.0513020009 - vertex 0.524474978 -0.0206840001 -0.265567005 - endloop - endfacet - - facet normal 0.462180048 0.640478075 0.613333046 - outer loop - vertex 0.535234988 -0.432819992 -0.0513020009 - vertex 0.900376976 -0.275397986 -0.490844995 - vertex 1.04242694 -0.687533975 -0.167512998 - endloop - endfacet - - facet normal -0.432697058 -0.859040201 0.273538053 - outer loop - vertex -0.631551027 0.764486015 0.547107995 - vertex -0.438125014 0.541163027 0.151739001 - vertex -1.01341605 0.803828001 0.0666079968 - endloop - endfacet - - facet normal -0.541726649 -0.466254741 -0.69938457 - outer loop - vertex -0.0606839992 0.437810004 0.322710991 - vertex -0.631551027 0.764486015 0.547107995 - vertex -0.291319996 0.284505993 0.603558004 - endloop - endfacet - - facet normal -0.175982982 -0.964926958 -0.194796994 - outer loop - vertex -0.438125014 0.541163027 0.151739001 - vertex -0.0606839992 0.437810004 0.322710991 - vertex 0.253931999 0.460341007 -0.0731239989 - endloop - endfacet - - facet normal -0.37848711 -0.872899234 0.307887077 - outer loop - vertex -0.0606839992 0.437810004 0.322710991 - vertex -0.438125014 0.541163027 0.151739001 - vertex -0.631551027 0.764486015 0.547107995 - endloop - endfacet - - facet normal 0.429853022 -0.805670023 -0.407581031 - outer loop - vertex -1.13857305 0.472478002 0.589594007 - vertex -0.631551027 0.764486015 0.547107995 - vertex -1.01341605 0.803828001 0.0666079968 - endloop - endfacet - - facet normal 0.334310085 -0.263623089 -0.904842317 - outer loop - vertex -0.75335598 0.292008013 0.78449899 - vertex -1.13857305 0.472478002 0.589594007 - vertex -1.13703001 0 0.727819026 - endloop - endfacet - - facet normal -0.34672612 -0.348206103 -0.870938241 - outer loop - vertex -0.631551027 0.764486015 0.547107995 - vertex -0.75335598 0.292008013 0.78449899 - vertex -0.291319996 0.284505993 0.603558004 - endloop - endfacet - - facet normal 0.205720052 -0.481182128 -0.852140188 - outer loop - vertex -0.75335598 0.292008013 0.78449899 - vertex -0.631551027 0.764486015 0.547107995 - vertex -1.13857305 0.472478002 0.589594007 - endloop - endfacet - - facet normal 0.146141946 0 -0.989263594 - outer loop - vertex -0.75335598 -0.292008013 0.78449899 - vertex -0.75335598 0.292008013 0.78449899 - vertex -1.13703001 0 0.727819026 - endloop - endfacet - - facet normal -0.353928059 0.280805022 -0.892123103 - outer loop - vertex -0.265545011 0 0.682883978 - vertex -0.75335598 -0.292008013 0.78449899 - vertex -0.291319996 -0.284505993 0.603558004 - endloop - endfacet - - facet normal -0.353928059 -0.280805022 -0.892123103 - outer loop - vertex -0.75335598 0.292008013 0.78449899 - vertex -0.265545011 0 0.682883978 - vertex -0.291319996 0.284505993 0.603558004 - endloop - endfacet - - facet normal -0.203930095 0 -0.978985429 - outer loop - vertex -0.265545011 0 0.682883978 - vertex -0.75335598 0.292008013 0.78449899 - vertex -0.75335598 -0.292008013 0.78449899 - endloop - endfacet - - facet normal -0.370298028 -0.280459017 -0.885563135 - outer loop - vertex -0.265545011 0 0.682883978 - vertex 0.369307995 0.167228997 0.364459008 - vertex -0.291319996 0.284505993 0.603558004 - endloop - endfacet - - facet normal -0.370298028 0.280459017 -0.885563135 - outer loop - vertex 0.369307995 -0.167228997 0.364459008 - vertex -0.265545011 0 0.682883978 - vertex -0.291319996 -0.284505993 0.603558004 - endloop - endfacet - - facet normal -0.60111922 0 -0.799159288 - outer loop - vertex 0.369307995 0.167228997 0.364459008 - vertex 0.369307995 -0.167228997 0.364459008 - vertex 0.662828028 0 0.143675998 - endloop - endfacet - - facet normal -0.448338032 0 -0.893864095 - outer loop - vertex 0.369307995 -0.167228997 0.364459008 - vertex 0.369307995 0.167228997 0.364459008 - vertex -0.265545011 0 0.682883978 - endloop - endfacet - - facet normal -0.355542064 -0.666160047 -0.655607045 - outer loop - vertex 0.369307995 0.167228997 0.364459008 - vertex -0.0606839992 0.437810004 0.322710991 - vertex -0.291319996 0.284505993 0.603558004 - endloop - endfacet - - facet normal -0.681211829 -0.339001894 -0.648866773 - outer loop - vertex 0.581242025 0.389086008 0.0260499995 - vertex 0.369307995 0.167228997 0.364459008 - vertex 0.662828028 0 0.143675998 - endloop - endfacet - - facet normal -0.156875014 -0.971084058 -0.179961026 - outer loop - vertex -0.0606839992 0.437810004 0.322710991 - vertex 0.581242025 0.389086008 0.0260499995 - vertex 0.253931999 0.460341007 -0.0731239989 - endloop - endfacet - - facet normal -0.354409099 -0.664684117 -0.657715142 - outer loop - vertex 0.581242025 0.389086008 0.0260499995 - vertex -0.0606839992 0.437810004 0.322710991 - vertex 0.369307995 0.167228997 0.364459008 - endloop - endfacet - - facet normal -0.967071533 -0.233435884 -0.101391949 - outer loop - vertex 0.700780988 0 -0.218314007 - vertex 0.581242025 0.389086008 0.0260499995 - vertex 0.662828028 0 0.143675998 - endloop - endfacet - - facet normal -0.722780406 -0.23343581 0.650458395 - outer loop - vertex 0.460438013 0.389086008 -0.345746011 - vertex 0.700780988 0 -0.218314007 - vertex 0.457304001 0 -0.488862008 - endloop - endfacet - - facet normal -0.234045014 -0.969247162 0.0760460123 - outer loop - vertex 0.581242025 0.389086008 0.0260499995 - vertex 0.460438013 0.389086008 -0.345746011 - vertex 0.253931999 0.460341007 -0.0731239989 - endloop - endfacet - - facet normal -0.855295599 -0.437309831 0.277901888 - outer loop - vertex 0.460438013 0.389086008 -0.345746011 - vertex 0.581242025 0.389086008 0.0260499995 - vertex 0.700780988 0 -0.218314007 - endloop - endfacet - - facet normal -0.967071533 0.233435884 -0.101391949 - outer loop - vertex 0.581242025 -0.389086008 0.0260499995 - vertex 0.700780988 0 -0.218314007 - vertex 0.662828028 0 0.143675998 - endloop - endfacet - - facet normal -0.234045014 0.969247162 0.0760460123 - outer loop - vertex 0.460438013 -0.389086008 -0.345746011 - vertex 0.581242025 -0.389086008 0.0260499995 - vertex 0.253931999 -0.460341007 -0.0731239989 - endloop - endfacet - - facet normal -0.722780406 0.233436137 0.650458336 - outer loop - vertex 0.700780988 0 -0.218314007 - vertex 0.460438013 -0.389086008 -0.345746011 - vertex 0.457304001 0 -0.488862008 - endloop - endfacet - - facet normal -0.855295599 0.437309831 0.277901888 - outer loop - vertex 0.460438013 -0.389086008 -0.345746011 - vertex 0.700780988 0 -0.218314007 - vertex 0.581242025 -0.389086008 0.0260499995 - endloop - endfacet - - facet normal -0.355541825 0.66615957 -0.655607641 - outer loop - vertex -0.0606839992 -0.437810004 0.322710991 - vertex 0.369307995 -0.167228997 0.364459008 - vertex -0.291319996 -0.284505993 0.603558004 - endloop - endfacet - - facet normal -0.156875029 0.971084177 -0.179960027 - outer loop - vertex 0.581242025 -0.389086008 0.0260499995 - vertex -0.0606839992 -0.437810004 0.322710991 - vertex 0.253931999 -0.460341007 -0.0731239989 - endloop - endfacet - - facet normal -0.681211829 0.339001894 -0.648866773 - outer loop - vertex 0.369307995 -0.167228997 0.364459008 - vertex 0.581242025 -0.389086008 0.0260499995 - vertex 0.662828028 0 0.143675998 - endloop - endfacet - - facet normal -0.354409099 0.664684117 -0.657715142 - outer loop - vertex 0.581242025 -0.389086008 0.0260499995 - vertex 0.369307995 -0.167228997 0.364459008 - vertex -0.0606839992 -0.437810004 0.322710991 - endloop - endfacet - - facet normal -0.541726649 0.466254741 -0.69938457 - outer loop - vertex -0.631551027 -0.764486015 0.547107995 - vertex -0.0606839992 -0.437810004 0.322710991 - vertex -0.291319996 -0.284505993 0.603558004 - endloop - endfacet - - facet normal -0.432697058 0.859040201 0.273538053 - outer loop - vertex -0.438125014 -0.541163027 0.151739001 - vertex -0.631551027 -0.764486015 0.547107995 - vertex -1.01341605 -0.803828001 0.0666079968 - endloop - endfacet - - facet normal -0.175982982 0.964926958 -0.194796994 - outer loop - vertex -0.0606839992 -0.437810004 0.322710991 - vertex -0.438125014 -0.541163027 0.151739001 - vertex 0.253931999 -0.460341007 -0.0731239989 - endloop - endfacet - - facet normal -0.37848711 0.872899234 0.307887077 - outer loop - vertex -0.438125014 -0.541163027 0.151739001 - vertex -0.0606839992 -0.437810004 0.322710991 - vertex -0.631551027 -0.764486015 0.547107995 - endloop - endfacet - - facet normal -0.411905199 0.910833538 -0.0267650131 - outer loop - vertex -0.903297007 -0.764486015 -0.289241999 - vertex -0.438125014 -0.541163027 0.151739001 - vertex -1.01341605 -0.803828001 0.0666079968 - endloop - endfacet - - facet normal -0.188856944 0.147191957 0.970910728 - outer loop - vertex -0.233263001 -0.437810004 -0.208434999 - vertex -0.903297007 -0.764486015 -0.289241999 - vertex -0.584930003 -0.284505993 -0.300080001 - endloop - endfacet - - facet normal -0.0278740041 0.964927137 0.261034042 - outer loop - vertex -0.438125014 -0.541163027 0.151739001 - vertex -0.233263001 -0.437810004 -0.208434999 - vertex 0.253931999 -0.460341007 -0.0731239989 - endloop - endfacet - - facet normal -0.439015031 0.898443103 0.00810600072 - outer loop - vertex -0.233263001 -0.437810004 -0.208434999 - vertex -0.438125014 -0.541163027 0.151739001 - vertex -0.903297007 -0.764486015 -0.289241999 - endloop - endfacet - - facet normal -0.021137001 0.971084058 0.237800032 - outer loop - vertex -0.233263001 -0.437810004 -0.208434999 - vertex 0.460438013 -0.389086008 -0.345746011 - vertex 0.253931999 -0.460341007 -0.0731239989 - endloop - endfacet - - facet normal 0.645767212 -0.710955262 0.278437078 - outer loop - vertex -0.903297007 0.764486015 -0.289241999 - vertex -1.33845997 0.472478002 -0.0255929995 - vertex -1.01341605 0.803828001 0.0666079968 - endloop - endfacet - - facet normal -0.411905199 -0.910833538 -0.0267650131 - outer loop - vertex -0.438125014 0.541163027 0.151739001 - vertex -0.903297007 0.764486015 -0.289241999 - vertex -1.01341605 0.803828001 0.0666079968 - endloop - endfacet - - facet normal -0.0278739929 -0.964926779 0.261034936 - outer loop - vertex -0.233263001 0.437810004 -0.208434999 - vertex -0.438125014 0.541163027 0.151739001 - vertex 0.253931999 0.460341007 -0.0731239989 - endloop - endfacet - - facet normal -0.188856944 -0.147191957 0.970910728 - outer loop - vertex -0.903297007 0.764486015 -0.289241999 - vertex -0.233263001 0.437810004 -0.208434999 - vertex -0.584930003 0.284505993 -0.300080001 - endloop - endfacet - - facet normal -0.439015031 -0.898443103 0.00810600072 - outer loop - vertex -0.233263001 0.437810004 -0.208434999 - vertex -0.903297007 0.764486015 -0.289241999 - vertex -0.438125014 0.541163027 0.151739001 - endloop - endfacet - - facet normal -0.021137001 -0.971084058 0.237800032 - outer loop - vertex 0.460438013 0.389086008 -0.345746011 - vertex -0.233263001 0.437810004 -0.208434999 - vertex 0.253931999 0.460341007 -0.0731239989 - endloop - endfacet - - facet normal 0.719865859 -0.192728966 -0.66681987 - outer loop - vertex -1.13857305 0.472478002 0.589594007 - vertex -1.37665105 0 0.469136 - vertex -1.13703001 0 0.727819026 - endloop - endfacet - - facet normal 0.725731134 -0.646305084 -0.23580502 - outer loop - vertex -1.33845997 0.472478002 -0.0255929995 - vertex -1.13857305 0.472478002 0.589594007 - vertex -1.01341605 0.803828001 0.0666079968 - endloop - endfacet - - facet normal 0.986233354 -0.150788054 -0.0678730309 - outer loop - vertex -1.37665105 0 0.469136 - vertex -1.33845997 0.472478002 -0.0255929995 - vertex -1.41845703 0 -0.138327003 - endloop - endfacet - - facet normal 0.882898092 -0.371747017 -0.286871016 - outer loop - vertex -1.33845997 0.472478002 -0.0255929995 - vertex -1.37665105 0 0.469136 - vertex -1.13857305 0.472478002 0.589594007 - endloop - endfacet - - facet normal 0.719865859 0.192728966 -0.66681987 - outer loop - vertex -1.37665105 0 0.469136 - vertex -1.13857305 -0.472478002 0.589594007 - vertex -1.13703001 0 0.727819026 - endloop - endfacet - - facet normal 0.986233175 0.150789022 -0.067873016 - outer loop - vertex -1.33845997 -0.472478002 -0.0255929995 - vertex -1.37665105 0 0.469136 - vertex -1.41845703 0 -0.138327003 - endloop - endfacet - - facet normal 0.725731134 0.646305084 -0.23580502 - outer loop - vertex -1.13857305 -0.472478002 0.589594007 - vertex -1.33845997 -0.472478002 -0.0255929995 - vertex -1.01341605 -0.803828001 0.0666079968 - endloop - endfacet - - facet normal 0.882897794 0.371747911 -0.286870927 - outer loop - vertex -1.33845997 -0.472478002 -0.0255929995 - vertex -1.13857305 -0.472478002 0.589594007 - vertex -1.37665105 0 0.469136 - endloop - endfacet - - facet normal 0.334310085 0.263623089 -0.904842317 - outer loop - vertex -1.13857305 -0.472478002 0.589594007 - vertex -0.75335598 -0.292008013 0.78449899 - vertex -1.13703001 0 0.727819026 - endloop - endfacet - - facet normal 0.429853022 0.805670023 -0.407581031 - outer loop - vertex -0.631551027 -0.764486015 0.547107995 - vertex -1.13857305 -0.472478002 0.589594007 - vertex -1.01341605 -0.803828001 0.0666079968 - endloop - endfacet - - facet normal -0.34672612 0.348206103 -0.870938241 - outer loop - vertex -0.75335598 -0.292008013 0.78449899 - vertex -0.631551027 -0.764486015 0.547107995 - vertex -0.291319996 -0.284505993 0.603558004 - endloop - endfacet - - facet normal 0.205720052 0.481182128 -0.852140188 - outer loop - vertex -0.631551027 -0.764486015 0.547107995 - vertex -0.75335598 -0.292008013 0.78449899 - vertex -1.13857305 -0.472478002 0.589594007 - endloop - endfacet - - facet normal 0.645767868 0.710954785 0.278436899 - outer loop - vertex -1.33845997 -0.472478002 -0.0255929995 - vertex -0.903297007 -0.764486015 -0.289241999 - vertex -1.01341605 -0.803828001 0.0666079968 - endloop - endfacet - - facet normal -0.0213829875 -0.399431795 0.916513443 - outer loop - vertex -1.12877297 -0.45877099 -0.0446330011 - vertex -1.01783097 -0.351024002 0.00491400016 - vertex -0.964388013 -0.470328987 -0.0458349995 - endloop - endfacet - - facet normal 0.781845033 0.449936032 0.431597054 - outer loop - vertex -2.92025304 -3.89943194 -1.53437805 - vertex -2.91697907 -3.88280702 -1.55764103 - vertex -2.90600705 -3.90321898 -1.55623698 - endloop - endfacet - - facet normal -0.945810378 0.167999059 -0.277883112 - outer loop - vertex -1.23226094 -1.14515603 0.457924992 - vertex -1.23026299 -1.22389793 0.403519988 - vertex -0.964388013 -0.470328987 -0.0458349995 - endloop - endfacet - - facet normal -0.805882096 -0.113844007 -0.581028044 - outer loop - vertex -1.01783097 -0.351024002 0.00491400016 - vertex -1.23226094 -1.14515603 0.457924992 - vertex -0.964388013 -0.470328987 -0.0458349995 - endloop - endfacet - - facet normal -0.942644656 0.173225939 -0.285330921 - outer loop - vertex -1.23226094 -1.14515603 0.457924992 - vertex -1.57549906 -1.69697499 1.256863 - vertex -1.23026299 -1.22389793 0.403519988 - endloop - endfacet - - facet normal -0.941975474 0.154292092 -0.298121184 - outer loop - vertex -1.23226094 -1.14515603 0.457924992 - vertex -1.59044802 -1.66966891 1.31822896 - vertex -1.57549906 -1.69697499 1.256863 - endloop - endfacet - - facet normal -0.747970819 0.285769939 -0.599061906 - outer loop - vertex -1.67847705 -1.86322808 1.30613101 - vertex -1.61559892 -1.845137 1.23625302 - vertex -1.57549906 -1.69697499 1.256863 - endloop - endfacet - - facet normal -0.832128823 0.402304918 -0.381722927 - outer loop - vertex -1.59044802 -1.66966891 1.31822896 - vertex -1.67847705 -1.86322808 1.30613101 - vertex -1.57549906 -1.69697499 1.256863 - endloop - endfacet - - facet normal -0.673662126 0.581969082 -0.455512017 - outer loop - vertex -1.67847705 -1.86322808 1.30613101 - vertex -2.09431696 -2.85528994 0.653648019 - vertex -1.61559892 -1.845137 1.23625302 - endloop - endfacet - - facet normal -0.55654794 0.606640875 -0.567662954 - outer loop - vertex -1.67847705 -1.86322808 1.30613101 - vertex -2.17327309 -2.90178013 0.681376994 - vertex -2.09431696 -2.85528994 0.653648019 - endloop - endfacet - - facet normal -0.405423969 0.861933947 -0.30446896 - outer loop - vertex -2.70630598 -3.77558994 -1.13675594 - vertex -2.66079307 -3.77662301 -1.20028496 - vertex -2.09431696 -2.85528994 0.653648019 - endloop - endfacet - - facet normal -0.551747799 0.803281784 -0.224304914 - outer loop - vertex -2.17327309 -2.90178013 0.681376994 - vertex -2.70630598 -3.77558994 -1.13675594 - vertex -2.09431696 -2.85528994 0.653648019 - endloop - endfacet - - facet normal -0.23088105 0.956007123 -0.180954024 - outer loop - vertex -2.70630598 -3.77558994 -1.13675594 - vertex -2.90600705 -3.90321898 -1.55623698 - vertex -2.66079307 -3.77662301 -1.20028496 - endloop - endfacet - - facet normal -0.112019025 0.964274228 -0.240056053 - outer loop - vertex -2.70630598 -3.77558994 -1.13675594 - vertex -2.92025304 -3.89943194 -1.53437805 - vertex -2.90600705 -3.90321898 -1.55623698 - endloop - endfacet - - facet normal -0.0213859938 -0.399428874 0.916514754 - outer loop - vertex -1.12877297 -0.45877099 -0.0446330011 - vertex -1.15481496 -0.312853992 0.018352 - vertex -1.01783097 -0.351024002 0.00491400016 - endloop - endfacet - - facet normal 0.781792939 0.449993968 0.431630969 - outer loop - vertex -2.93203402 -3.88312697 -1.530038 - vertex -2.91697907 -3.88280702 -1.55764103 - vertex -2.92025304 -3.89943194 -1.53437805 - endloop - endfacet - - facet normal -0.208629057 -0.441484153 -0.872677267 - outer loop - vertex -1.15481496 -0.312853992 0.018352 - vertex -1.23226094 -1.14515603 0.457924992 - vertex -1.01783097 -0.351024002 0.00491400016 - endloop - endfacet - - facet normal -0.460987806 -0.38050884 -0.801687777 - outer loop - vertex -1.15481496 -0.312853992 0.018352 - vertex -1.30323696 -1.08182395 0.468677998 - vertex -1.23226094 -1.14515603 0.457924992 - endloop - endfacet - - facet normal -0.578610063 -0.566799045 -0.586471975 - outer loop - vertex -1.63677692 -1.63637698 1.331761 - vertex -1.59044802 -1.66966891 1.31822896 - vertex -1.23226094 -1.14515603 0.457924992 - endloop - endfacet - - facet normal -0.587184072 -0.558590055 -0.585826039 - outer loop - vertex -1.30323696 -1.08182395 0.468677998 - vertex -1.63677692 -1.63637698 1.331761 - vertex -1.23226094 -1.14515603 0.457924992 - endloop - endfacet - - facet normal -0.181254938 0.14324595 -0.972947657 - outer loop - vertex -1.63677692 -1.63637698 1.331761 - vertex -1.67847705 -1.86322808 1.30613101 - vertex -1.59044802 -1.66966891 1.31822896 - endloop - endfacet - - facet normal -0.106968015 0.131019011 -0.985592186 - outer loop - vertex -1.63677692 -1.63637698 1.331761 - vertex -1.76449001 -1.82381392 1.32070494 - vertex -1.67847705 -1.86322808 1.30613101 - endloop - endfacet - - facet normal 0.127293006 0.466052979 -0.875551939 - outer loop - vertex -2.26325607 -2.86937809 0.685541987 - vertex -2.17327309 -2.90178013 0.681376994 - vertex -1.67847705 -1.86322808 1.30613101 - endloop - endfacet - - facet normal 0.0775939971 0.490328014 -0.86807698 - outer loop - vertex -1.76449001 -1.82381392 1.32070494 - vertex -2.26325607 -2.86937809 0.685541987 - vertex -1.67847705 -1.86322808 1.30613101 - endloop - endfacet - - facet normal 0.277229041 0.831728101 -0.481012046 - outer loop - vertex -2.26325607 -2.86937809 0.685541987 - vertex -2.70630598 -3.77558994 -1.13675594 - vertex -2.17327309 -2.90178013 0.681376994 - endloop - endfacet - - facet normal 0.55742979 0.681361735 -0.474360824 - outer loop - vertex -2.26325607 -2.86937809 0.685541987 - vertex -2.73510885 -3.74266911 -1.12331498 - vertex -2.70630598 -3.77558994 -1.13675594 - endloop - endfacet - - facet normal 0.622135937 0.587329924 -0.517677963 - outer loop - vertex -2.93203402 -3.88312697 -1.530038 - vertex -2.92025304 -3.89943194 -1.53437805 - vertex -2.70630598 -3.77558994 -1.13675594 - endloop - endfacet - - facet normal 0.542568088 0.677461982 -0.496654004 - outer loop - vertex -2.73510885 -3.74266911 -1.12331498 - vertex -2.93203402 -3.88312697 -1.530038 - vertex -2.70630598 -3.77558994 -1.13675594 - endloop - endfacet - - facet normal -0.0213840026 -0.39942807 0.916515112 - outer loop - vertex -1.12877297 -0.45877099 -0.0446330011 - vertex -1.27218902 -0.384564996 -0.0156389996 - vertex -1.15481496 -0.312853992 0.018352 - endloop - endfacet - - facet normal 0.781800985 0.449976027 0.431634992 - outer loop - vertex -2.93247795 -3.86658096 -1.54648209 - vertex -2.91697907 -3.88280702 -1.55764103 - vertex -2.93203402 -3.88312697 -1.530038 - endloop - endfacet - - facet normal 0.36412704 -0.521987081 -0.771324098 - outer loop - vertex -1.38974404 -1.08159196 0.427682996 - vertex -1.30323696 -1.08182395 0.468677998 - vertex -1.15481496 -0.312853992 0.018352 - endloop - endfacet - - facet normal 0.515085042 -0.520164073 -0.681261063 - outer loop - vertex -1.27218902 -0.384564996 -0.0156389996 - vertex -1.38974404 -1.08159196 0.427682996 - vertex -1.15481496 -0.312853992 0.018352 - endloop - endfacet - - facet normal 0.218663126 -0.857194424 -0.466266215 - outer loop - vertex -1.38974404 -1.08159196 0.427682996 - vertex -1.63677692 -1.63637698 1.331761 - vertex -1.30323696 -1.08182395 0.468677998 - endloop - endfacet - - facet normal 0.204399109 -0.858234406 -0.470802218 - outer loop - vertex -1.38974404 -1.08159196 0.427682996 - vertex -1.6796 -1.62216997 1.28727102 - vertex -1.63677692 -1.63637698 1.331761 - endloop - endfacet - - facet normal 0.50200671 -0.29407683 -0.813331544 - outer loop - vertex -1.80886698 -1.75657201 1.26900196 - vertex -1.76449001 -1.82381392 1.32070494 - vertex -1.63677692 -1.63637698 1.331761 - endloop - endfacet - - facet normal 0.566596866 -0.451231897 -0.689461887 - outer loop - vertex -1.6796 -1.62216997 1.28727102 - vertex -1.80886698 -1.75657201 1.26900196 - vertex -1.63677692 -1.63637698 1.331761 - endloop - endfacet - - facet normal 0.770831048 0.019108003 -0.636753082 - outer loop - vertex -1.80886698 -1.75657201 1.26900196 - vertex -2.26325607 -2.86937809 0.685541987 - vertex -1.76449001 -1.82381392 1.32070494 - endloop - endfacet - - facet normal 0.701269805 0.0847809836 -0.707836807 - outer loop - vertex -1.80886698 -1.75657201 1.26900196 - vertex -2.29650688 -2.78248215 0.663007021 - vertex -2.26325607 -2.86937809 0.685541987 - endloop - endfacet - - facet normal 0.940782428 -0.327590138 -0.0872530341 - outer loop - vertex -2.72551107 -3.70265102 -1.17007899 - vertex -2.73510885 -3.74266911 -1.12331498 - vertex -2.26325607 -2.86937809 0.685541987 - endloop - endfacet - - facet normal 0.904131114 0.257567018 -0.34089005 - outer loop - vertex -2.29650688 -2.78248215 0.663007021 - vertex -2.72551107 -3.70265102 -1.17007899 - vertex -2.26325607 -2.86937809 0.685541987 - endloop - endfacet - - facet normal 0.841782868 -0.483176947 -0.240710974 - outer loop - vertex -2.72551107 -3.70265102 -1.17007899 - vertex -2.93203402 -3.88312697 -1.530038 - vertex -2.73510885 -3.74266911 -1.12331498 - endloop - endfacet - - facet normal 0.88212204 -0.319912046 -0.345712066 - outer loop - vertex -2.72551107 -3.70265102 -1.17007899 - vertex -2.93247795 -3.86658096 -1.54648209 - vertex -2.93203402 -3.88312697 -1.530038 - endloop - endfacet - - facet normal -0.0213889908 -0.399435818 0.916511655 - outer loop - vertex -1.12877297 -0.45877099 -0.0446330011 - vertex -1.28156805 -0.512152016 -0.0714629963 - vertex -1.27218902 -0.384564996 -0.0156389996 - endloop - endfacet - - facet normal 0.781811774 0.450063884 0.43152386 - outer loop - vertex -2.92125297 -3.86225605 -1.57133007 - vertex -2.91697907 -3.88280702 -1.55764103 - vertex -2.93247795 -3.86658096 -1.54648209 - endloop - endfacet - - facet normal 0.989923298 -0.111154035 0.0877310187 - outer loop - vertex -1.28156805 -0.512152016 -0.0714629963 - vertex -1.38974404 -1.08159196 0.427682996 - vertex -1.27218902 -0.384564996 -0.0156389996 - endloop - endfacet - - facet normal 0.917860508 -0.345539778 -0.195279896 - outer loop - vertex -1.28156805 -0.512152016 -0.0714629963 - vertex -1.42664099 -1.14463401 0.365808994 - vertex -1.38974404 -1.08159196 0.427682996 - endloop - endfacet - - facet normal 0.889153957 -0.457446933 0.0121459989 - outer loop - vertex -1.68667006 -1.63774502 1.21826005 - vertex -1.6796 -1.62216997 1.28727102 - vertex -1.38974404 -1.08159196 0.427682996 - endloop - endfacet - - facet normal 0.870758057 -0.491359055 -0.0186190028 - outer loop - vertex -1.42664099 -1.14463401 0.365808994 - vertex -1.68667006 -1.63774502 1.21826005 - vertex -1.38974404 -1.08159196 0.427682996 - endloop - endfacet - - facet normal 0.712433815 -0.69666481 0.0842389762 - outer loop - vertex -1.68667006 -1.63774502 1.21826005 - vertex -1.80886698 -1.75657201 1.26900196 - vertex -1.6796 -1.62216997 1.28727102 - endloop - endfacet - - facet normal 0.652189136 -0.740312099 -0.163056031 - outer loop - vertex -1.68667006 -1.63774502 1.21826005 - vertex -1.77819002 -1.71213603 1.18995297 - vertex -1.80886698 -1.75657201 1.26900196 - endloop - endfacet - - facet normal 0.860250354 -0.490703166 0.138491064 - outer loop - vertex -2.24798799 -2.70652914 0.630741 - vertex -2.29650688 -2.78248215 0.663007021 - vertex -1.80886698 -1.75657201 1.26900196 - endloop - endfacet - - facet normal 0.882484198 -0.463115126 0.0821340159 - outer loop - vertex -1.77819002 -1.71213603 1.18995297 - vertex -2.24798799 -2.70652914 0.630741 - vertex -1.80886698 -1.75657201 1.26900196 - endloop - endfacet - - facet normal 0.852697134 -0.518845081 0.0608890019 - outer loop - vertex -2.24798799 -2.70652914 0.630741 - vertex -2.72551107 -3.70265102 -1.17007899 - vertex -2.29650688 -2.78248215 0.663007021 - endloop - endfacet - - facet normal 0.671965122 -0.708992124 0.213993043 - outer loop - vertex -2.24798799 -2.70652914 0.630741 - vertex -2.68474102 -3.68566895 -1.24183702 - vertex -2.72551107 -3.70265102 -1.17007899 - endloop - endfacet - - facet normal 0.517961979 -0.851083994 0.0858569965 - outer loop - vertex -2.92125297 -3.86225605 -1.57133007 - vertex -2.93247795 -3.86658096 -1.54648209 - vertex -2.72551107 -3.70265102 -1.17007899 - endloop - endfacet - - facet normal 0.512933195 -0.853763223 0.0893750265 - outer loop - vertex -2.68474102 -3.68566895 -1.24183702 - vertex -2.92125297 -3.86225605 -1.57133007 - vertex -2.72551107 -3.70265102 -1.17007899 - endloop - endfacet - - facet normal -0.0213880036 -0.399439096 0.916510165 - outer loop - vertex -1.12877297 -0.45877099 -0.0446330011 - vertex -1.17588902 -0.599543989 -0.107084997 - vertex -1.28156805 -0.512152016 -0.0714629963 - endloop - endfacet - - facet normal 0.781846225 0.450043142 0.43148312 - outer loop - vertex -2.906811 -3.87340689 -1.58586907 - vertex -2.91697907 -3.88280702 -1.55764103 - vertex -2.92125297 -3.86225605 -1.57133007 - endloop - endfacet - - facet normal 0.826284051 0.179565996 0.533864021 - outer loop - vertex -1.38614297 -1.22347903 0.329647988 - vertex -1.42664099 -1.14463401 0.365808994 - vertex -1.28156805 -0.512152016 -0.0714629963 - endloop - endfacet - - facet normal 0.545636952 0.349325001 0.761742949 - outer loop - vertex -1.17588902 -0.599543989 -0.107084997 - vertex -1.38614297 -1.22347903 0.329647988 - vertex -1.28156805 -0.512152016 -0.0714629963 - endloop - endfacet - - facet normal 0.872462928 0.257705957 0.415205985 - outer loop - vertex -1.38614297 -1.22347903 0.329647988 - vertex -1.68667006 -1.63774502 1.21826005 - vertex -1.42664099 -1.14463401 0.365808994 - endloop - endfacet - - facet normal 0.843502939 0.317459971 0.433268964 - outer loop - vertex -1.38614297 -1.22347903 0.329647988 - vertex -1.65266299 -1.67137408 1.17669499 - vertex -1.68667006 -1.63774502 1.21826005 - endloop - endfacet - - facet normal 0.305910945 -0.643379867 0.701769829 - outer loop - vertex -1.69556093 -1.72396898 1.14308596 - vertex -1.77819002 -1.71213603 1.18995297 - vertex -1.68667006 -1.63774502 1.21826005 - endloop - endfacet - - facet normal 0.23541902 -0.652366102 0.720414102 - outer loop - vertex -1.65266299 -1.67137408 1.17669499 - vertex -1.69556093 -1.72396898 1.14308596 - vertex -1.68667006 -1.63774502 1.21826005 - endloop - endfacet - - facet normal 0.338568926 -0.577655792 0.742754817 - outer loop - vertex -1.69556093 -1.72396898 1.14308596 - vertex -2.24798799 -2.70652914 0.630741 - vertex -1.77819002 -1.71213603 1.18995297 - endloop - endfacet - - facet normal 0.199457005 -0.538895965 0.818417966 - outer loop - vertex -1.69556093 -1.72396898 1.14308596 - vertex -2.15423298 -2.69871092 0.61303997 - vertex -2.24798799 -2.70652914 0.630741 - endloop - endfacet - - facet normal 0.0607170016 -0.890278995 0.451350033 - outer loop - vertex -2.64349794 -3.70451212 -1.28455305 - vertex -2.68474102 -3.68566895 -1.24183702 - vertex -2.24798799 -2.70652914 0.630741 - endloop - endfacet - - facet normal 0.155501023 -0.888839185 0.431027114 - outer loop - vertex -2.15423298 -2.69871092 0.61303997 - vertex -2.64349794 -3.70451212 -1.28455305 - vertex -2.24798799 -2.70652914 0.630741 - endloop - endfacet - - facet normal 0.0504009835 -0.894892812 0.443425894 - outer loop - vertex -2.64349794 -3.70451212 -1.28455305 - vertex -2.92125297 -3.86225605 -1.57133007 - vertex -2.68474102 -3.68566895 -1.24183702 - endloop - endfacet - - facet normal -0.0920129791 -0.832157791 0.546851873 - outer loop - vertex -2.64349794 -3.70451212 -1.28455305 - vertex -2.906811 -3.87340689 -1.58586907 - vertex -2.92125297 -3.86225605 -1.57133007 - endloop - endfacet - - facet normal -0.0213840008 -0.399439991 0.916509986 - outer loop - vertex -1.12877297 -0.45877099 -0.0446330011 - vertex -1.03473103 -0.580929995 -0.095679 - vertex -1.17588902 -0.599543989 -0.107084997 - endloop - endfacet - - facet normal 0.78186512 0.450013101 0.43148011 - outer loop - vertex -2.90002489 -3.89163709 -1.57915306 - vertex -2.91697907 -3.88280702 -1.55764103 - vertex -2.906811 -3.87340689 -1.58586907 - endloop - endfacet - - facet normal -0.14268291 0.599396646 0.787632525 - outer loop - vertex -1.03473103 -0.580929995 -0.095679 - vertex -1.38614297 -1.22347903 0.329647988 - vertex -1.17588902 -0.599543989 -0.107084997 - endloop - endfacet - - facet normal 0.052053012 0.53126812 0.845603168 - outer loop - vertex -1.03473103 -0.580929995 -0.095679 - vertex -1.29874599 -1.25875497 0.346430987 - vertex -1.38614297 -1.22347903 0.329647988 - endloop - endfacet - - facet normal 0.257589042 0.818346024 0.513768077 - outer loop - vertex -1.60318708 -1.69773293 1.19387603 - vertex -1.65266299 -1.67137408 1.17669499 - vertex -1.38614297 -1.22347903 0.329647988 - endloop - endfacet - - facet normal 0.235024914 0.826015711 0.51230979 - outer loop - vertex -1.29874599 -1.25875497 0.346430987 - vertex -1.60318708 -1.69773293 1.19387603 - vertex -1.38614297 -1.22347903 0.329647988 - endloop - endfacet - - facet normal -0.421861917 -0.218249947 0.879999757 - outer loop - vertex -1.60318708 -1.69773293 1.19387603 - vertex -1.69556093 -1.72396898 1.14308596 - vertex -1.65266299 -1.67137408 1.17669499 - endloop - endfacet - - facet normal -0.423999131 -0.211792082 0.880550325 - outer loop - vertex -1.60318708 -1.69773293 1.19387603 - vertex -1.62320209 -1.78316092 1.16369104 - vertex -1.69556093 -1.72396898 1.14308596 - endloop - endfacet - - facet normal -0.403747976 -0.283086956 0.869970918 - outer loop - vertex -2.08584404 -2.76491594 0.623236001 - vertex -2.15423298 -2.69871092 0.61303997 - vertex -1.69556093 -1.72396898 1.14308596 - endloop - endfacet - - facet normal -0.45299387 -0.256548941 0.8538028 - outer loop - vertex -1.62320209 -1.78316092 1.16369104 - vertex -2.08584404 -2.76491594 0.623236001 - vertex -1.69556093 -1.72396898 1.14308596 - endloop - endfacet - - facet normal -0.645806909 -0.593053877 0.480853915 - outer loop - vertex -2.08584404 -2.76491594 0.623236001 - vertex -2.64349794 -3.70451212 -1.28455305 - vertex -2.15423298 -2.69871092 0.61303997 - endloop - endfacet - - facet normal -0.940769851 -0.0996529832 0.324069977 - outer loop - vertex -2.08584404 -2.76491594 0.623236001 - vertex -2.63283992 -3.74498892 -1.26605999 - vertex -2.64349794 -3.70451212 -1.28455305 - endloop - endfacet - - facet normal -0.743959308 -0.0310040135 0.667505264 - outer loop - vertex -2.90002489 -3.89163709 -1.57915306 - vertex -2.906811 -3.87340689 -1.58586907 - vertex -2.64349794 -3.70451212 -1.28455305 - endloop - endfacet - - facet normal -0.776583195 0.0809680223 0.624790192 - outer loop - vertex -2.63283992 -3.74498892 -1.26605999 - vertex -2.90002489 -3.89163709 -1.57915306 - vertex -2.64349794 -3.70451212 -1.28455305 - endloop - endfacet - - facet normal -0.021383008 -0.399439186 0.916510284 - outer loop - vertex -1.12877297 -0.45877099 -0.0446330011 - vertex -0.964388013 -0.470328987 -0.0458349995 - vertex -1.03473103 -0.580929995 -0.095679 - endloop - endfacet - - facet normal 0.781880677 0.449949831 0.43151781 - outer loop - vertex -2.90600705 -3.90321898 -1.55623698 - vertex -2.91697907 -3.88280702 -1.55764103 - vertex -2.90002489 -3.89163709 -1.57915306 - endloop - endfacet - - facet normal -0.677965045 0.568237007 0.466337025 - outer loop - vertex -1.23026299 -1.22389793 0.403519988 - vertex -1.29874599 -1.25875497 0.346430987 - vertex -1.03473103 -0.580929995 -0.095679 - endloop - endfacet - - facet normal -0.864686787 0.444541872 0.23387894 - outer loop - vertex -0.964388013 -0.470328987 -0.0458349995 - vertex -1.23026299 -1.22389793 0.403519988 - vertex -1.03473103 -0.580929995 -0.095679 - endloop - endfacet - - facet normal -0.574035645 0.792883575 0.204495862 - outer loop - vertex -1.23026299 -1.22389793 0.403519988 - vertex -1.60318708 -1.69773293 1.19387603 - vertex -1.29874599 -1.25875497 0.346430987 - endloop - endfacet - - facet normal -0.542411327 0.808382392 0.228709131 - outer loop - vertex -1.23026299 -1.22389793 0.403519988 - vertex -1.57549906 -1.69697499 1.256863 - vertex -1.60318708 -1.69773293 1.19387603 - endloop - endfacet - - facet normal -0.963225067 0.145960003 0.225594997 - outer loop - vertex -1.61559892 -1.845137 1.23625302 - vertex -1.62320209 -1.78316092 1.16369104 - vertex -1.60318708 -1.69773293 1.19387603 - endloop - endfacet - - facet normal -0.899821401 0.188828096 0.393275172 - outer loop - vertex -1.57549906 -1.69697499 1.256863 - vertex -1.61559892 -1.845137 1.23625302 - vertex -1.60318708 -1.69773293 1.19387603 - endloop - endfacet - - facet normal -0.913563967 0.256980956 0.315216959 - outer loop - vertex -1.61559892 -1.845137 1.23625302 - vertex -2.08584404 -2.76491594 0.623236001 - vertex -1.62320209 -1.78316092 1.16369104 - endloop - endfacet - - facet normal -0.902424276 0.211018071 0.375635087 - outer loop - vertex -1.61559892 -1.845137 1.23625302 - vertex -2.09431696 -2.85528994 0.653648019 - vertex -2.08584404 -2.76491594 0.623236001 - endloop - endfacet - - facet normal -0.811797917 0.580202937 -0.065944992 - outer loop - vertex -2.66079307 -3.77662301 -1.20028496 - vertex -2.63283992 -3.74498892 -1.26605999 - vertex -2.08584404 -2.76491594 0.623236001 - endloop - endfacet - - facet normal -0.963328302 0.162240043 0.213721067 - outer loop - vertex -2.09431696 -2.85528994 0.653648019 - vertex -2.66079307 -3.77662301 -1.20028496 - vertex -2.08584404 -2.76491594 0.623236001 - endloop - endfacet - - facet normal -0.591019154 0.795851231 0.131594047 - outer loop - vertex -2.66079307 -3.77662301 -1.20028496 - vertex -2.90002489 -3.89163709 -1.57915306 - vertex -2.63283992 -3.74498892 -1.26605999 - endloop - endfacet - - facet normal -0.658654928 0.72665596 0.195306972 - outer loop - vertex -2.66079307 -3.77662301 -1.20028496 - vertex -2.90600705 -3.90321898 -1.55623698 - vertex -2.90002489 -3.89163709 -1.57915306 - endloop - endfacet - - facet normal -0.0767649934 0.424314976 0.902254939 - outer loop - vertex -1.01769805 0.488880992 -0.0326699987 - vertex -1.05401599 0.365146995 0.0224300008 - vertex -1.17887402 0.454620987 -0.0302719995 - endloop - endfacet - - facet normal 0.760863423 -0.570059299 0.310031176 - outer loop - vertex -3.08611298 2.79408097 -1.54586196 - vertex -3.0986321 2.77501702 -1.55019093 - vertex -3.09980297 2.787534 -1.52430201 - endloop - endfacet - - facet normal -0.885644376 -0.0708120316 -0.458933175 - outer loop - vertex -1.01769805 0.488880992 -0.0326699987 - vertex -1.31704998 1.29457402 0.420700014 - vertex -1.32415795 1.20324898 0.448507994 - endloop - endfacet - - facet normal -0.840063334 0.0050070025 -0.54246521 - outer loop - vertex -1.01769805 0.488880992 -0.0326699987 - vertex -1.32415795 1.20324898 0.448507994 - vertex -1.05401599 0.365146995 0.0224300008 - endloop - endfacet - - facet normal -0.96798718 -0.00108800025 -0.250997066 - outer loop - vertex -1.31704998 1.29457402 0.420700014 - vertex -1.52630997 1.67332196 1.22608101 - vertex -1.32415795 1.20324898 0.448507994 - endloop - endfacet - - facet normal -0.909262836 0.206635967 -0.361306936 - outer loop - vertex -1.52630997 1.67332196 1.22608101 - vertex -1.55407095 1.62852693 1.27032602 - vertex -1.32415795 1.20324898 0.448507994 - endloop - endfacet - - facet normal -0.512467682 -0.124688931 -0.849605441 - outer loop - vertex -1.52630997 1.67332196 1.22608101 - vertex -1.57726693 1.81927204 1.23539805 - vertex -1.65870309 1.81342292 1.28537703 - endloop - endfacet - - facet normal -0.638087928 -0.303487957 -0.707629025 - outer loop - vertex -1.52630997 1.67332196 1.22608101 - vertex -1.65870309 1.81342292 1.28537703 - vertex -1.55407095 1.62852693 1.27032602 - endloop - endfacet - - facet normal -0.160780042 -0.915375292 -0.369104087 - outer loop - vertex -1.57726693 1.81927204 1.23539805 - vertex -2.07294297 2.21308303 0.474665999 - vertex -1.65870309 1.81342292 1.28537703 - endloop - endfacet - - facet normal -0.373682201 -0.893377483 -0.249476135 - outer loop - vertex -2.07294297 2.21308303 0.474665999 - vertex -2.16162205 2.24618006 0.488976002 - vertex -1.65870309 1.81342292 1.28537703 - endloop - endfacet - - facet normal -0.0730490163 -0.98006922 -0.18473804 - outer loop - vertex -2.07294297 2.21308303 0.474665999 - vertex -2.83839607 2.59127903 -1.22905695 - vertex -2.881531 2.58232498 -1.16449904 - endloop - endfacet - - facet normal -0.354477853 -0.934385598 -0.0356209837 - outer loop - vertex -2.07294297 2.21308303 0.474665999 - vertex -2.881531 2.58232498 -1.16449904 - vertex -2.16162205 2.24618006 0.488976002 - endloop - endfacet - - facet normal -0.308526933 -0.892181814 -0.32988295 - outer loop - vertex -2.83839607 2.59127903 -1.22905695 - vertex -3.08611298 2.79408097 -1.54586196 - vertex -2.881531 2.58232498 -1.16449904 - endloop - endfacet - - facet normal -0.192876935 -0.898110747 -0.395215869 - outer loop - vertex -3.08611298 2.79408097 -1.54586196 - vertex -3.09980297 2.787534 -1.52430201 - vertex -2.881531 2.58232498 -1.16449904 - endloop - endfacet - - facet normal -0.0767690316 0.424311161 0.90225637 - outer loop - vertex -1.05401599 0.365146995 0.0224300008 - vertex -1.18435502 0.308786988 0.0378439985 - vertex -1.17887402 0.454620987 -0.0302719995 - endloop - endfacet - - facet normal 0.760849178 -0.570075095 0.310037047 - outer loop - vertex -3.09980297 2.787534 -1.52430201 - vertex -3.0986321 2.77501702 -1.55019093 - vertex -3.11261201 2.77155995 -1.52223897 - endloop - endfacet - - facet normal -0.264522016 0.367929041 -0.891435027 - outer loop - vertex -1.05401599 0.365146995 0.0224300008 - vertex -1.32415795 1.20324898 0.448507994 - vertex -1.18435502 0.308786988 0.0378439985 - endloop - endfacet - - facet normal -0.0232820045 0.414123029 -0.909923077 - outer loop - vertex -1.32415795 1.20324898 0.448507994 - vertex -1.38690901 1.13685596 0.41989699 - vertex -1.18435502 0.308786988 0.0378439985 - endloop - endfacet - - facet normal -0.489267647 0.711135507 -0.504879713 - outer loop - vertex -1.32415795 1.20324898 0.448507994 - vertex -1.55407095 1.62852693 1.27032602 - vertex -1.59782505 1.59094405 1.25979102 - endloop - endfacet - - facet normal -0.508115053 0.69780314 -0.504866123 - outer loop - vertex -1.32415795 1.20324898 0.448507994 - vertex -1.59782505 1.59094405 1.25979102 - vertex -1.38690901 1.13685596 0.41989699 - endloop - endfacet - - facet normal 0.113028012 0.14399001 -0.983103096 - outer loop - vertex -1.55407095 1.62852693 1.27032602 - vertex -1.65870309 1.81342292 1.28537703 - vertex -1.59782505 1.59094405 1.25979102 - endloop - endfacet - - facet normal 0.185814962 0.162295938 -0.969088614 - outer loop - vertex -1.65870309 1.81342292 1.28537703 - vertex -1.73981202 1.76814294 1.26224196 - vertex -1.59782505 1.59094405 1.25979102 - endloop - endfacet - - facet normal 0.498544723 -0.589779615 -0.635305643 - outer loop - vertex -1.65870309 1.81342292 1.28537703 - vertex -2.16162205 2.24618006 0.488976002 - vertex -2.244519 2.21051693 0.457031012 - endloop - endfacet - - facet normal 0.506276369 -0.581512392 -0.636810482 - outer loop - vertex -1.65870309 1.81342292 1.28537703 - vertex -2.244519 2.21051693 0.457031012 - vertex -1.73981202 1.76814294 1.26224196 - endloop - endfacet - - facet normal 0.484050065 -0.79212904 -0.371789068 - outer loop - vertex -2.16162205 2.24618006 0.488976002 - vertex -2.881531 2.58232498 -1.16449904 - vertex -2.244519 2.21051693 0.457031012 - endloop - endfacet - - facet normal 0.612227142 -0.683636189 -0.397265077 - outer loop - vertex -2.881531 2.58232498 -1.16449904 - vertex -2.91222191 2.54960108 -1.15548396 - vertex -2.244519 2.21051693 0.457031012 - endloop - endfacet - - facet normal 0.557069063 -0.529381037 -0.639867067 - outer loop - vertex -2.881531 2.58232498 -1.16449904 - vertex -3.09980297 2.787534 -1.52430201 - vertex -3.11261201 2.77155995 -1.52223897 - endloop - endfacet - - facet normal 0.471753895 -0.616191864 -0.630678833 - outer loop - vertex -2.881531 2.58232498 -1.16449904 - vertex -3.11261201 2.77155995 -1.52223897 - vertex -2.91222191 2.54960108 -1.15548396 - endloop - endfacet - - facet normal -0.0767669678 0.424310833 0.902256668 - outer loop - vertex -1.18435502 0.308786988 0.0378439985 - vertex -1.31056702 0.362244993 0.00196600007 - vertex -1.17887402 0.454620987 -0.0302719995 - endloop - endfacet - - facet normal 0.760856092 -0.570063055 0.310042024 - outer loop - vertex -3.11261201 2.77155995 -1.52223897 - vertex -3.0986321 2.77501702 -1.55019093 - vertex -3.11489511 2.75818896 -1.54122198 - endloop - endfacet - - facet normal 0.624605119 0.447931021 -0.639708042 - outer loop - vertex -1.18435502 0.308786988 0.0378439985 - vertex -1.38690901 1.13685596 0.41989699 - vertex -1.45804906 1.14539194 0.356413007 - endloop - endfacet - - facet normal 0.412785053 0.438942075 -0.798084199 - outer loop - vertex -1.18435502 0.308786988 0.0378439985 - vertex -1.45804906 1.14539194 0.356413007 - vertex -1.31056702 0.362244993 0.00196600007 - endloop - endfacet - - facet normal 0.413569063 0.840320051 -0.350461036 - outer loop - vertex -1.38690901 1.13685596 0.41989699 - vertex -1.59782505 1.59094405 1.25979102 - vertex -1.45804906 1.14539194 0.356413007 - endloop - endfacet - - facet normal 0.566476703 0.770475626 -0.292354852 - outer loop - vertex -1.59782505 1.59094405 1.25979102 - vertex -1.62462497 1.58887506 1.20240998 - vertex -1.45804906 1.14539194 0.356413007 - endloop - endfacet - - facet normal 0.666042805 0.540796876 -0.513736904 - outer loop - vertex -1.59782505 1.59094405 1.25979102 - vertex -1.73981202 1.76814294 1.26224196 - vertex -1.759516 1.71752691 1.18341398 - endloop - endfacet - - facet normal 0.673963785 0.656674743 -0.338453889 - outer loop - vertex -1.59782505 1.59094405 1.25979102 - vertex -1.759516 1.71752691 1.18341398 - vertex -1.62462497 1.58887506 1.20240998 - endloop - endfacet - - facet normal 0.87528342 0.276827157 -0.396542192 - outer loop - vertex -1.73981202 1.76814294 1.26224196 - vertex -2.244519 2.21051693 0.457031012 - vertex -1.759516 1.71752691 1.18341398 - endloop - endfacet - - facet normal 0.868040264 0.162955061 -0.468990147 - outer loop - vertex -2.244519 2.21051693 0.457031012 - vertex -2.25921011 2.13294888 0.402886987 - vertex -1.759516 1.71752691 1.18341398 - endloop - endfacet - - facet normal 0.824457109 0.515765071 -0.232931033 - outer loop - vertex -2.244519 2.21051693 0.457031012 - vertex -2.91222191 2.54960108 -1.15548396 - vertex -2.90735912 2.51775002 -1.20879805 - endloop - endfacet - - facet normal 0.931220233 0.0728830248 -0.357095122 - outer loop - vertex -2.244519 2.21051693 0.457031012 - vertex -2.90735912 2.51775002 -1.20879805 - vertex -2.25921011 2.13294888 0.402886987 - endloop - endfacet - - facet normal 0.866475642 0.459383816 -0.195413932 - outer loop - vertex -2.91222191 2.54960108 -1.15548396 - vertex -3.11261201 2.77155995 -1.52223897 - vertex -2.90735912 2.51775002 -1.20879805 - endloop - endfacet - - facet normal 0.89059633 0.314460129 -0.328562111 - outer loop - vertex -3.11261201 2.77155995 -1.52223897 - vertex -3.11489511 2.75818896 -1.54122198 - vertex -2.90735912 2.51775002 -1.20879805 - endloop - endfacet - - facet normal -0.0767720193 0.424317122 0.90225327 - outer loop - vertex -1.31056702 0.362244993 0.00196600007 - vertex -1.33761096 0.485262007 -0.0581879988 - vertex -1.17887402 0.454620987 -0.0302719995 - endloop - endfacet - - facet normal 0.760856211 -0.570104182 0.309966087 - outer loop - vertex -3.11489511 2.75818896 -1.54122198 - vertex -3.0986321 2.77501702 -1.55019093 - vertex -3.10493207 2.75749207 -1.56695998 - endloop - endfacet - - facet normal 0.979394317 0.199278057 -0.0327850096 - outer loop - vertex -1.31056702 0.362244993 0.00196600007 - vertex -1.45804906 1.14539194 0.356413007 - vertex -1.33761096 0.485262007 -0.0581879988 - endloop - endfacet - - facet normal 0.961778104 0.24816604 -0.115743019 - outer loop - vertex -1.45804906 1.14539194 0.356413007 - vertex -1.48400998 1.22242701 0.305858999 - vertex -1.33761096 0.485262007 -0.0581879988 - endloop - endfacet - - facet normal 0.983233452 0.0246459842 0.180677876 - outer loop - vertex -1.45804906 1.14539194 0.356413007 - vertex -1.62462497 1.58887506 1.20240998 - vertex -1.61429 1.623878 1.14139199 - endloop - endfacet - - facet normal 0.94848156 0.316802889 -0.004323998 - outer loop - vertex -1.45804906 1.14539194 0.356413007 - vertex -1.61429 1.623878 1.14139199 - vertex -1.48400998 1.22242701 0.305858999 - endloop - endfacet - - facet normal 0.569355786 0.667770743 0.479495794 - outer loop - vertex -1.62462497 1.58887506 1.20240998 - vertex -1.759516 1.71752691 1.18341398 - vertex -1.61429 1.623878 1.14139199 - endloop - endfacet - - facet normal 0.574625909 0.780223846 0.247094944 - outer loop - vertex -1.759516 1.71752691 1.18341398 - vertex -1.70297694 1.69968998 1.10825098 - vertex -1.61429 1.623878 1.14139199 - endloop - endfacet - - facet normal 0.67483896 0.736889005 -0.0398359969 - outer loop - vertex -1.759516 1.71752691 1.18341398 - vertex -2.25921011 2.13294888 0.402886987 - vertex -2.19463396 2.07188797 0.367314011 - endloop - endfacet - - facet normal 0.45869714 0.878017306 0.136684045 - outer loop - vertex -1.759516 1.71752691 1.18341398 - vertex -2.19463396 2.07188797 0.367314011 - vertex -1.70297694 1.69968998 1.10825098 - endloop - endfacet - - facet normal 0.658887684 0.747243702 -0.0865659639 - outer loop - vertex -2.25921011 2.13294888 0.402886987 - vertex -2.90735912 2.51775002 -1.20879805 - vertex -2.19463396 2.07188797 0.367314011 - endloop - endfacet - - facet normal 0.369675994 0.924360991 0.0943209976 - outer loop - vertex -2.90735912 2.51775002 -1.20879805 - vertex -2.87060595 2.51075506 -1.28429699 - vertex -2.19463396 2.07188797 0.367314011 - endloop - endfacet - - facet normal 0.583255053 0.786133051 0.204471037 - outer loop - vertex -2.90735912 2.51775002 -1.20879805 - vertex -3.11489511 2.75818896 -1.54122198 - vertex -3.10493207 2.75749207 -1.56695998 - endloop - endfacet - - facet normal 0.578596771 0.78848362 0.208612904 - outer loop - vertex -2.90735912 2.51775002 -1.20879805 - vertex -3.10493207 2.75749207 -1.56695998 - vertex -2.87060595 2.51075506 -1.28429699 - endloop - endfacet - - facet normal -0.0767699778 0.424322903 0.902250767 - outer loop - vertex -1.33761096 0.485262007 -0.0581879988 - vertex -1.24512303 0.585205019 -0.0973210037 - vertex -1.17887402 0.454620987 -0.0302719995 - endloop - endfacet - - facet normal 0.760858178 -0.570103168 0.309963077 - outer loop - vertex -3.10493207 2.75749207 -1.56695998 - vertex -3.0986321 2.77501702 -1.55019093 - vertex -3.09022689 2.76998997 -1.58006907 - endloop - endfacet - - facet normal 0.584711671 -0.262888819 0.767464519 - outer loop - vertex -1.33761096 0.485262007 -0.0581879988 - vertex -1.48400998 1.22242701 0.305858999 - vertex -1.44524097 1.309955 0.306304008 - endloop - endfacet - - facet normal 0.59877497 -0.25710398 0.758529007 - outer loop - vertex -1.33761096 0.485262007 -0.0581879988 - vertex -1.44524097 1.309955 0.306304008 - vertex -1.24512303 0.585205019 -0.0973210037 - endloop - endfacet - - facet normal 0.86564827 -0.385051161 0.319982111 - outer loop - vertex -1.48400998 1.22242701 0.305858999 - vertex -1.61429 1.623878 1.14139199 - vertex -1.44524097 1.309955 0.306304008 - endloop - endfacet - - facet normal 0.773064494 -0.526166677 0.354287803 - outer loop - vertex -1.61429 1.623878 1.14139199 - vertex -1.57460201 1.66959405 1.12268603 - vertex -1.44524097 1.309955 0.306304008 - endloop - endfacet - - facet normal 0.0183549952 0.418431848 0.908062696 - outer loop - vertex -1.61429 1.623878 1.14139199 - vertex -1.70297694 1.69968998 1.10825098 - vertex -1.61277103 1.72806406 1.09335303 - endloop - endfacet - - facet normal -0.0551219992 0.418749988 0.906427085 - outer loop - vertex -1.61429 1.623878 1.14139199 - vertex -1.61277103 1.72806406 1.09335303 - vertex -1.57460201 1.66959405 1.12268603 - endloop - endfacet - - facet normal -0.173069939 0.829250693 0.53140384 - outer loop - vertex -1.70297694 1.69968998 1.10825098 - vertex -2.19463396 2.07188797 0.367314011 - vertex -1.61277103 1.72806406 1.09335303 - endloop - endfacet - - facet normal -0.0611630008 0.882239044 0.466812015 - outer loop - vertex -2.19463396 2.07188797 0.367314011 - vertex -2.09941506 2.07331204 0.377099007 - vertex -1.61277103 1.72806406 1.09335303 - endloop - endfacet - - facet normal -0.196834937 0.924567699 0.326236904 - outer loop - vertex -2.19463396 2.07188797 0.367314011 - vertex -2.87060595 2.51075506 -1.28429699 - vertex -2.82963514 2.533885 -1.32512701 - endloop - endfacet - - facet normal -0.0429229848 0.959605694 0.278054923 - outer loop - vertex -2.19463396 2.07188797 0.367314011 - vertex -2.82963514 2.533885 -1.32512701 - vertex -2.09941506 2.07331204 0.377099007 - endloop - endfacet - - facet normal 0.133578047 0.798819304 0.586553216 - outer loop - vertex -2.87060595 2.51075506 -1.28429699 - vertex -3.10493207 2.75749207 -1.56695998 - vertex -2.82963514 2.533885 -1.32512701 - endloop - endfacet - - facet normal -0.00927600171 0.728916168 0.684540093 - outer loop - vertex -3.10493207 2.75749207 -1.56695998 - vertex -3.09022689 2.76998997 -1.58006907 - vertex -2.82963514 2.533885 -1.32512701 - endloop - endfacet - - facet normal -0.0767689869 0.424322903 0.902250767 - outer loop - vertex -1.24512303 0.585205019 -0.0973210037 - vertex -1.10274899 0.586816013 -0.0859650001 - vertex -1.17887402 0.454620987 -0.0302719995 - endloop - endfacet - - facet normal 0.760912299 -0.570029259 0.309966117 - outer loop - vertex -3.09022689 2.76998997 -1.58006907 - vertex -3.0986321 2.77501702 -1.55019093 - vertex -3.08185196 2.78627396 -1.57068002 - endloop - endfacet - - facet normal -0.0633039996 -0.498861015 0.864367008 - outer loop - vertex -1.24512303 0.585205019 -0.0973210037 - vertex -1.44524097 1.309955 0.306304008 - vertex -1.10274899 0.586816013 -0.0859650001 - endloop - endfacet - - facet normal -0.292043954 -0.559201956 0.77588886 - outer loop - vertex -1.44524097 1.309955 0.306304008 - vertex -1.37093699 1.34206295 0.357414007 - vertex -1.10274899 0.586816013 -0.0859650001 - endloop - endfacet - - facet normal 0.108098 -0.903342009 0.415075988 - outer loop - vertex -1.44524097 1.309955 0.306304008 - vertex -1.57460201 1.66959405 1.12268603 - vertex -1.53544593 1.69159794 1.16037703 - endloop - endfacet - - facet normal 0.105116993 -0.903753936 0.414944977 - outer loop - vertex -1.44524097 1.309955 0.306304008 - vertex -1.53544593 1.69159794 1.16037703 - vertex -1.37093699 1.34206295 0.357414007 - endloop - endfacet - - facet normal -0.671668947 -0.0683799908 0.737688959 - outer loop - vertex -1.57460201 1.66959405 1.12268603 - vertex -1.61277103 1.72806406 1.09335303 - vertex -1.53544593 1.69159794 1.16037703 - endloop - endfacet - - facet normal -0.673058808 -0.074808985 0.735795796 - outer loop - vertex -1.61277103 1.72806406 1.09335303 - vertex -1.55682492 1.78128397 1.14994001 - vertex -1.53544593 1.69159794 1.16037703 - endloop - endfacet - - facet normal -0.765665054 0.19344902 0.613461077 - outer loop - vertex -1.61277103 1.72806406 1.09335303 - vertex -2.09941506 2.07331204 0.377099007 - vertex -2.045259 2.13614988 0.424876004 - endloop - endfacet - - facet normal -0.776329041 0.171037018 0.606679142 - outer loop - vertex -1.61277103 1.72806406 1.09335303 - vertex -2.045259 2.13614988 0.424876004 - vertex -1.55682492 1.78128397 1.14994001 - endloop - endfacet - - facet normal -0.816772759 0.362870902 0.448560894 - outer loop - vertex -2.09941506 2.07331204 0.377099007 - vertex -2.82963514 2.533885 -1.32512701 - vertex -2.045259 2.13614988 0.424876004 - endloop - endfacet - - facet normal -0.903895736 0.0723669752 0.421586841 - outer loop - vertex -2.82963514 2.533885 -1.32512701 - vertex -2.81529999 2.56972098 -1.30054402 - vertex -2.045259 2.13614988 0.424876004 - endloop - endfacet - - facet normal -0.714901567 -0.0349769779 0.698349595 - outer loop - vertex -2.82963514 2.533885 -1.32512701 - vertex -3.09022689 2.76998997 -1.58006907 - vertex -3.08185196 2.78627396 -1.57068002 - endloop - endfacet - - facet normal -0.757348955 -0.135290995 0.638841927 - outer loop - vertex -2.82963514 2.533885 -1.32512701 - vertex -3.08185196 2.78627396 -1.57068002 - vertex -2.81529999 2.56972098 -1.30054402 - endloop - endfacet - - facet normal -0.0767659619 0.4243218 0.902251601 - outer loop - vertex -1.10274899 0.586816013 -0.0859650001 - vertex -1.01769805 0.488880992 -0.0326699987 - vertex -1.17887402 0.454620987 -0.0302719995 - endloop - endfacet - - facet normal 0.760899127 -0.570062101 0.309938073 - outer loop - vertex -3.08185196 2.78627396 -1.57068002 - vertex -3.0986321 2.77501702 -1.55019093 - vertex -3.08611298 2.79408097 -1.54586196 - endloop - endfacet - - facet normal -0.81185782 -0.481850892 0.329706907 - outer loop - vertex -1.10274899 0.586816013 -0.0859650001 - vertex -1.37093699 1.34206295 0.357414007 - vertex -1.31704998 1.29457402 0.420700014 - endloop - endfacet - - facet normal -0.792782009 -0.494814992 0.355886042 - outer loop - vertex -1.10274899 0.586816013 -0.0859650001 - vertex -1.31704998 1.29457402 0.420700014 - vertex -1.01769805 0.488880992 -0.0326699987 - endloop - endfacet - - facet normal -0.739785016 -0.659090936 0.135341004 - outer loop - vertex -1.37093699 1.34206295 0.357414007 - vertex -1.53544593 1.69159794 1.16037703 - vertex -1.31704998 1.29457402 0.420700014 - endloop - endfacet - - facet normal -0.882458806 -0.470319867 -0.00810799841 - outer loop - vertex -1.53544593 1.69159794 1.16037703 - vertex -1.52630997 1.67332196 1.22608101 - vertex -1.31704998 1.29457402 0.420700014 - endloop - endfacet - - facet normal -0.962198138 -0.243543014 -0.121908017 - outer loop - vertex -1.53544593 1.69159794 1.16037703 - vertex -1.55682492 1.78128397 1.14994001 - vertex -1.57726693 1.81927204 1.23539805 - endloop - endfacet - - facet normal -0.942623854 -0.331589013 0.0388449952 - outer loop - vertex -1.53544593 1.69159794 1.16037703 - vertex -1.57726693 1.81927204 1.23539805 - vertex -1.52630997 1.67332196 1.22608101 - endloop - endfacet - - facet normal -0.708266914 -0.692263961 0.138305992 - outer loop - vertex -1.55682492 1.78128397 1.14994001 - vertex -2.045259 2.13614988 0.424876004 - vertex -1.57726693 1.81927204 1.23539805 - endloop - endfacet - - facet normal -0.826425612 -0.483796358 0.28803125 - outer loop - vertex -2.045259 2.13614988 0.424876004 - vertex -2.07294297 2.21308303 0.474665999 - vertex -1.57726693 1.81927204 1.23539805 - endloop - endfacet - - facet normal -0.582625031 -0.810790002 0.0562819988 - outer loop - vertex -2.045259 2.13614988 0.424876004 - vertex -2.81529999 2.56972098 -1.30054402 - vertex -2.83839607 2.59127903 -1.22905695 - endloop - endfacet - - facet normal -0.836838305 -0.476067185 0.270299107 - outer loop - vertex -2.045259 2.13614988 0.424876004 - vertex -2.83839607 2.59127903 -1.22905695 - vertex -2.07294297 2.21308303 0.474665999 - endloop - endfacet - - facet normal -0.644049883 -0.764651895 0.0225229971 - outer loop - vertex -2.81529999 2.56972098 -1.30054402 - vertex -3.08185196 2.78627396 -1.57068002 - vertex -2.83839607 2.59127903 -1.22905695 - endloop - endfacet - - facet normal -0.70394212 -0.703137159 0.100317024 - outer loop - vertex -3.08185196 2.78627396 -1.57068002 - vertex -3.08611298 2.79408097 -1.54586196 - vertex -2.83839607 2.59127903 -1.22905695 - endloop - endfacet - - facet normal 0.0729879588 -0.424979746 0.902255476 - outer loop - vertex -0.790997982 -0.401975006 -0.0302719995 - vertex -0.704271972 -0.275191993 0.0224300008 - vertex -0.627824008 -0.379043013 -0.0326699987 - endloop - endfacet - - facet normal 0.288141072 0.809048176 0.512265146 - outer loop - vertex -1.08943605 -3.53432393 -1.60113692 - vertex -1.09849703 -3.51748109 -1.62264204 - vertex -1.07667601 -3.52539802 -1.62241101 - endloop - endfacet - - facet normal -0.722976804 -0.26038295 -0.639925897 - outer loop - vertex -0.785679996 -1.12330306 0.448507994 - vertex -0.733861029 -1.19883895 0.420700014 - vertex -0.627824008 -0.379043013 -0.0326699987 - endloop - endfacet - - facet normal -0.757175207 -0.233753055 -0.609955132 - outer loop - vertex -0.704271972 -0.275191993 0.0224300008 - vertex -0.785679996 -1.12330306 0.448507994 - vertex -0.627824008 -0.379043013 -0.0326699987 - endloop - endfacet - - facet normal -0.822168887 -0.424315959 -0.379465908 - outer loop - vertex -0.785679996 -1.12330306 0.448507994 - vertex -0.835425019 -1.72230005 1.22608101 - vertex -0.733861029 -1.19883895 0.420700014 - endloop - endfacet - - facet normal -0.716561258 -0.529666245 -0.453865141 - outer loop - vertex -0.785679996 -1.12330306 0.448507994 - vertex -0.881864011 -1.69738698 1.27032602 - vertex -0.835425019 -1.72230005 1.22608101 - endloop - endfacet - - facet normal -0.506153941 -0.148249999 -0.849605918 - outer loop - vertex -0.880029976 -1.90982795 1.28537703 - vertex -0.806580007 -1.87417507 1.23539805 - vertex -0.835425019 -1.72230005 1.22608101 - endloop - endfacet - - facet normal -0.704344094 -0.0562160127 -0.707629204 - outer loop - vertex -0.881864011 -1.69738698 1.27032602 - vertex -0.880029976 -1.90982795 1.28537703 - vertex -0.835425019 -1.72230005 1.22608101 - endloop - endfacet - - facet normal -0.632492721 0.574023724 -0.520047784 - outer loop - vertex -0.880029976 -1.90982795 1.28537703 - vertex -0.908545017 -2.86312103 0.267821014 - vertex -0.806580007 -1.87417507 1.23539805 - endloop - endfacet - - facet normal -0.727581859 0.510695934 -0.4580549 - outer loop - vertex -0.880029976 -1.90982795 1.28537703 - vertex -0.968795002 -2.93612289 0.282130986 - vertex -0.908545017 -2.86312103 0.267821014 - endloop - endfacet - - facet normal -0.823960662 0.55237174 -0.126388937 - outer loop - vertex -1.00637496 -3.34099007 -1.18288505 - vertex -0.972894013 -3.30494308 -1.24361598 - vertex -0.908545017 -2.86312103 0.267821014 - endloop - endfacet - - facet normal -0.776781023 0.611850977 -0.149162993 - outer loop - vertex -0.968795002 -2.93612289 0.282130986 - vertex -1.00637496 -3.34099007 -1.18288505 - vertex -0.908545017 -2.86312103 0.267821014 - endloop - endfacet - - facet normal -0.813524306 0.571128249 -0.10950204 - outer loop - vertex -1.00637496 -3.34099007 -1.18288505 - vertex -1.07667601 -3.52539802 -1.62241101 - vertex -0.972894013 -3.30494308 -1.24361598 - endloop - endfacet - - facet normal -0.731632829 0.66243583 -0.160909951 - outer loop - vertex -1.00637496 -3.34099007 -1.18288505 - vertex -1.08943605 -3.53432393 -1.60113692 - vertex -1.07667601 -3.52539802 -1.62241101 - endloop - endfacet - - facet normal 0.0729849711 -0.424977809 0.902256668 - outer loop - vertex -0.790997982 -0.401975006 -0.0302719995 - vertex -0.846026003 -0.26681 0.0378439985 - vertex -0.704271972 -0.275191993 0.0224300008 - endloop - endfacet - - facet normal 0.288122952 0.809049785 0.512272894 - outer loop - vertex -1.10902095 -3.53056598 -1.59605598 - vertex -1.09849703 -3.51748109 -1.62264204 - vertex -1.08943605 -3.53432393 -1.60113692 - endloop - endfacet - - facet normal -0.122729048 -0.436093181 -0.89149332 - outer loop - vertex -0.846026003 -0.26681 0.0378439985 - vertex -0.785679996 -1.12330306 0.448507994 - vertex -0.704271972 -0.275191993 0.0224300008 - endloop - endfacet - - facet normal 0.167711034 -0.416587085 -0.893492162 - outer loop - vertex -0.846026003 -0.26681 0.0378439985 - vertex -0.873220026 -1.09718001 0.41989699 - vertex -0.785679996 -1.12330306 0.448507994 - endloop - endfacet - - facet normal -0.0466770045 -0.816322029 -0.575708032 - outer loop - vertex -0.938548028 -1.68671608 1.25979102 - vertex -0.881864011 -1.69738698 1.27032602 - vertex -0.785679996 -1.12330306 0.448507994 - endloop - endfacet - - facet normal -0.0548560023 -0.815250099 -0.576505065 - outer loop - vertex -0.873220026 -1.09718001 0.41989699 - vertex -0.938548028 -1.68671608 1.25979102 - vertex -0.785679996 -1.12330306 0.448507994 - endloop - endfacet - - facet normal 0.169880018 -0.0681860074 -0.983103096 - outer loop - vertex -0.938548028 -1.68671608 1.25979102 - vertex -0.880029976 -1.90982795 1.28537703 - vertex -0.881864011 -1.69738698 1.27032602 - endloop - endfacet - - facet normal 0.242068902 -0.0476449765 -0.969088554 - outer loop - vertex -0.938548028 -1.68671608 1.25979102 - vertex -0.972913027 -1.91116905 1.26224196 - vertex -0.880029976 -1.90982795 1.28537703 - endloop - endfacet - - facet normal 0.173446015 0.680724084 -0.711710036 - outer loop - vertex -1.05841696 -2.94668603 0.25018701 - vertex -0.968795002 -2.93612289 0.282130986 - vertex -0.880029976 -1.90982795 1.28537703 - endloop - endfacet - - facet normal 0.167480066 0.681978166 -0.711938262 - outer loop - vertex -0.972913027 -1.91116905 1.26224196 - vertex -1.05841696 -2.94668603 0.25018701 - vertex -0.880029976 -1.90982795 1.28537703 - endloop - endfacet - - facet normal -0.0188320037 0.963823199 -0.265876055 - outer loop - vertex -1.05841696 -2.94668603 0.25018701 - vertex -1.00637496 -3.34099007 -1.18288505 - vertex -0.968795002 -2.93612289 0.282130986 - endloop - endfacet - - facet normal -0.0057739974 0.964099646 -0.265477866 - outer loop - vertex -1.05841696 -2.94668603 0.25018701 - vertex -1.04957104 -3.33721995 -1.16825497 - vertex -1.00637496 -3.34099007 -1.18288505 - endloop - endfacet - - facet normal 0.0616750047 0.901266992 -0.428852022 - outer loop - vertex -1.10902095 -3.53056598 -1.59605598 - vertex -1.08943605 -3.53432393 -1.60113692 - vertex -1.00637496 -3.34099007 -1.18288505 - endloop - endfacet - - facet normal -0.057355985 0.912721753 -0.404535919 - outer loop - vertex -1.04957104 -3.33721995 -1.16825497 - vertex -1.10902095 -3.53056598 -1.59605598 - vertex -1.00637496 -3.34099007 -1.18288505 - endloop - endfacet - - facet normal 0.0729859918 -0.424976945 0.902256966 - outer loop - vertex -0.790997982 -0.401975006 -0.0302719995 - vertex -0.946343005 -0.360211015 0.00196600007 - vertex -0.846026003 -0.26681 0.0378439985 - endloop - endfacet - - facet normal 0.288119972 0.809050918 0.512272954 - outer loop - vertex -1.12068105 -3.5169549 -1.61099398 - vertex -1.09849703 -3.51748109 -1.62264204 - vertex -1.10902095 -3.53056598 -1.59605598 - endloop - endfacet - - facet normal 0.798518658 -0.272996873 -0.536507785 - outer loop - vertex -0.930561006 -1.14014304 0.356413007 - vertex -0.873220026 -1.09718001 0.41989699 - vertex -0.846026003 -0.26681 0.0378439985 - endloop - endfacet - - facet normal 0.574401855 -0.329012901 -0.749541819 - outer loop - vertex -0.946343005 -0.360211015 0.00196600007 - vertex -0.930561006 -1.14014304 0.356413007 - vertex -0.846026003 -0.26681 0.0378439985 - endloop - endfacet - - facet normal 0.770561874 -0.548315942 -0.324936926 - outer loop - vertex -0.930561006 -1.14014304 0.356413007 - vertex -0.938548028 -1.68671608 1.25979102 - vertex -0.873220026 -1.09718001 0.41989699 - endloop - endfacet - - facet normal 0.849924028 -0.454108 -0.267235994 - outer loop - vertex -0.930561006 -1.14014304 0.356413007 - vertex -0.962791979 -1.69832397 1.20240998 - vertex -0.938548028 -1.68671608 1.25979102 - endloop - endfacet - - facet normal 0.847210824 -0.135322973 -0.51373291 - outer loop - vertex -1.01528394 -1.87718606 1.18341398 - vertex -0.972913027 -1.91116905 1.26224196 - vertex -0.938548028 -1.68671608 1.25979102 - endloop - endfacet - - facet normal 0.912006497 -0.231711864 -0.338457793 - outer loop - vertex -0.962791979 -1.69832397 1.20240998 - vertex -1.01528394 -1.87718606 1.18341398 - vertex -0.938548028 -1.68671608 1.25979102 - endloop - endfacet - - facet normal 0.891000748 0.277561903 -0.359272897 - outer loop - vertex -1.01528394 -1.87718606 1.18341398 - vertex -1.05841696 -2.94668603 0.25018701 - vertex -0.972913027 -1.91116905 1.26224196 - endloop - endfacet - - facet normal 0.839562833 0.337562919 -0.425658911 - outer loop - vertex -1.01528394 -1.87718606 1.18341398 - vertex -1.10992396 -2.88685608 0.196043 - vertex -1.05841696 -2.94668603 0.25018701 - endloop - endfacet - - facet normal 0.926300108 0.364708066 -0.0946370065 - outer loop - vertex -1.06995296 -3.29647708 -1.21073997 - vertex -1.04957104 -3.33721995 -1.16825497 - vertex -1.05841696 -2.94668603 0.25018701 - endloop - endfacet - - facet normal 0.810221374 0.568532228 -0.142522067 - outer loop - vertex -1.10992396 -2.88685608 0.196043 - vertex -1.06995296 -3.29647708 -1.21073997 - vertex -1.05841696 -2.94668603 0.25018701 - endloop - endfacet - - facet normal 0.94495213 0.228233024 -0.234468028 - outer loop - vertex -1.06995296 -3.29647708 -1.21073997 - vertex -1.10902095 -3.53056598 -1.59605598 - vertex -1.04957104 -3.33721995 -1.16825497 - endloop - endfacet - - facet normal 0.864126801 0.386452913 -0.322395921 - outer loop - vertex -1.06995296 -3.29647708 -1.21073997 - vertex -1.12068105 -3.5169549 -1.61099398 - vertex -1.10902095 -3.53056598 -1.59605598 - endloop - endfacet - - facet normal 0.0729829893 -0.424985975 0.902252913 - outer loop - vertex -0.790997982 -0.401975006 -0.0302719995 - vertex -0.929682016 -0.485058993 -0.0581879988 - vertex -0.946343005 -0.360211015 0.00196600007 - endloop - endfacet - - facet normal 0.288078994 0.809116066 0.512193084 - outer loop - vertex -1.11563802 -3.50374198 -1.63470399 - vertex -1.09849703 -3.51748109 -1.62264204 - vertex -1.12068105 -3.5169549 -1.61099398 - endloop - endfacet - - facet normal 0.990013659 0.0744389743 0.119714953 - outer loop - vertex -0.929682016 -0.485058993 -0.0581879988 - vertex -0.930561006 -1.14014304 0.356413007 - vertex -0.946343005 -0.360211015 0.00196600007 - endloop - endfacet - - facet normal 0.98270458 0.0980890542 0.157068089 - outer loop - vertex -0.929682016 -0.485058993 -0.0581879988 - vertex -0.914526999 -1.21983802 0.305858999 - vertex -0.930561006 -1.14014304 0.356413007 - endloop - endfacet - - facet normal 0.90709877 0.334623903 0.25533995 - outer loop - vertex -0.936339974 -1.72346997 1.14139199 - vertex -0.962791979 -1.69832397 1.20240998 - vertex -0.930561006 -1.14014304 0.356413007 - endloop - endfacet - - facet normal 0.985707998 0.131661996 0.105094999 - outer loop - vertex -0.914526999 -1.21983802 0.305858999 - vertex -0.936339974 -1.72346997 1.14139199 - vertex -0.930561006 -1.14014304 0.356413007 - endloop - endfacet - - facet normal 0.826960862 -0.293624967 0.479499906 - outer loop - vertex -0.936339974 -1.72346997 1.14139199 - vertex -1.01528394 -1.87718606 1.18341398 - vertex -0.962791979 -1.69832397 1.20240998 - endloop - endfacet - - facet normal 0.887753963 -0.388381988 0.247087985 - outer loop - vertex -0.936339974 -1.72346997 1.14139199 - vertex -0.975238979 -1.83346891 1.10825098 - vertex -1.01528394 -1.87718606 1.18341398 - endloop - endfacet - - facet normal 0.963315189 -0.228283063 0.141105026 - outer loop - vertex -1.08453 -2.80168796 0.160469994 - vertex -1.10992396 -2.88685608 0.196043 - vertex -1.01528394 -1.87718606 1.18341398 - endloop - endfacet - - facet normal 0.893704414 -0.361284137 0.266019106 - outer loop - vertex -0.975238979 -1.83346891 1.10825098 - vertex -1.08453 -2.80168796 0.160469994 - vertex -1.01528394 -1.87718606 1.18341398 - endloop - endfacet - - facet normal 0.964169204 -0.246102065 0.0990530252 - outer loop - vertex -1.08453 -2.80168796 0.160469994 - vertex -1.06995296 -3.29647708 -1.21073997 - vertex -1.10992396 -2.88685608 0.196043 - endloop - endfacet - - facet normal 0.968371511 -0.231236875 0.0937339589 - outer loop - vertex -1.08453 -2.80168796 0.160469994 - vertex -1.052176 -3.24943709 -1.27835 - vertex -1.06995296 -3.29647708 -1.21073997 - endloop - endfacet - - facet normal 0.955379665 -0.292643875 0.0401169844 - outer loop - vertex -1.11563802 -3.50374198 -1.63470399 - vertex -1.12068105 -3.5169549 -1.61099398 - vertex -1.06995296 -3.29647708 -1.21073997 - endloop - endfacet - - facet normal 0.953478754 -0.298360944 0.0431159884 - outer loop - vertex -1.052176 -3.24943709 -1.27835 - vertex -1.11563802 -3.50374198 -1.63470399 - vertex -1.06995296 -3.29647708 -1.21073997 - endloop - endfacet - - facet normal 0.0729859844 -0.424989969 0.902250886 - outer loop - vertex -0.790997982 -0.401975006 -0.0302719995 - vertex -0.808588982 -0.547342002 -0.0973210037 - vertex -0.929682016 -0.485058993 -0.0581879988 - endloop - endfacet - - facet normal 0.288051158 0.809107423 0.51222229 - outer loop - vertex -1.09768903 -3.50087404 -1.64932895 - vertex -1.09849703 -3.51748109 -1.62264204 - vertex -1.11563802 -3.50374198 -1.63470399 - endloop - endfacet - - facet normal 0.307018965 0.427585959 0.850240946 - outer loop - vertex -0.837188005 -1.27625394 0.306304008 - vertex -0.914526999 -1.21983802 0.305858999 - vertex -0.929682016 -0.485058993 -0.0581879988 - endloop - endfacet - - facet normal 0.465866804 0.414588839 0.781718731 - outer loop - vertex -0.808588982 -0.547342002 -0.0973210037 - vertex -0.837188005 -1.27625394 0.306304008 - vertex -0.929682016 -0.485058993 -0.0581879988 - endloop - endfacet - - facet normal 0.524762154 0.722922206 0.449453115 - outer loop - vertex -0.837188005 -1.27625394 0.306304008 - vertex -0.936339974 -1.72346997 1.14139199 - vertex -0.914526999 -1.21983802 0.305858999 - endloop - endfacet - - facet normal 0.420921862 0.777905762 0.466569841 - outer loop - vertex -0.837188005 -1.27625394 0.306304008 - vertex -0.879109979 -1.74321699 1.12268603 - vertex -0.936339974 -1.72346997 1.14139199 - endloop - endfacet - - facet normal 0.225110888 -0.353195846 0.908062637 - outer loop - vertex -0.882930994 -1.81293797 1.09335303 - vertex -0.975238979 -1.83346891 1.10825098 - vertex -0.936339974 -1.72346997 1.14139199 - endloop - endfacet - - facet normal 0.161638066 -0.390208155 0.906427443 - outer loop - vertex -0.879109979 -1.74321699 1.12268603 - vertex -0.882930994 -1.81293797 1.09335303 - vertex -0.936339974 -1.72346997 1.14139199 - endloop - endfacet - - facet normal 0.262340188 -0.689983487 0.67461139 - outer loop - vertex -0.882930994 -1.81293797 1.09335303 - vertex -1.08453 -2.80168796 0.160469994 - vertex -0.975238979 -1.83346891 1.10825098 - endloop - endfacet - - facet normal 0.304611921 -0.685768843 0.661008835 - outer loop - vertex -0.882930994 -1.81293797 1.09335303 - vertex -1.00135601 -2.75531101 0.170255005 - vertex -1.08453 -2.80168796 0.160469994 - endloop - endfacet - - facet normal 0.576873899 -0.77617085 0.254509926 - outer loop - vertex -1.009624 -3.23152399 -1.32017207 - vertex -1.052176 -3.24943709 -1.27835 - vertex -1.08453 -2.80168796 0.160469994 - endloop - endfacet - - facet normal 0.444392174 -0.854044378 0.270414114 - outer loop - vertex -1.00135601 -2.75531101 0.170255005 - vertex -1.009624 -3.23152399 -1.32017207 - vertex -1.08453 -2.80168796 0.160469994 - endloop - endfacet - - facet normal 0.642835021 -0.673016012 0.365804017 - outer loop - vertex -1.009624 -3.23152399 -1.32017207 - vertex -1.11563802 -3.50374198 -1.63470399 - vertex -1.052176 -3.24943709 -1.27835 - endloop - endfacet - - facet normal 0.496770978 -0.732070029 0.466145962 - outer loop - vertex -1.009624 -3.23152399 -1.32017207 - vertex -1.09768903 -3.50087404 -1.64932895 - vertex -1.11563802 -3.50374198 -1.63470399 - endloop - endfacet - - facet normal 0.0729879811 -0.424989879 0.902250707 - outer loop - vertex -0.790997982 -0.401975006 -0.0302719995 - vertex -0.674250007 -0.500160992 -0.0859650001 - vertex -0.808588982 -0.547342002 -0.0973210037 - endloop - endfacet - - facet normal 0.288186014 0.809071004 0.512204051 - outer loop - vertex -1.08034897 -3.51051307 -1.64385903 - vertex -1.09849703 -3.51748109 -1.62264204 - vertex -1.09768903 -3.50087404 -1.64932895 - endloop - endfacet - - facet normal -0.239184961 0.477530897 0.845431745 - outer loop - vertex -0.674250007 -0.500160992 -0.0859650001 - vertex -0.837188005 -1.27625394 0.306304008 - vertex -0.808588982 -0.547342002 -0.0973210037 - endloop - endfacet - - facet normal -0.511622965 0.470719934 0.718793988 - outer loop - vertex -0.674250007 -0.500160992 -0.0859650001 - vertex -0.756784022 -1.266909 0.357414007 - vertex -0.837188005 -1.27625394 0.306304008 - endloop - endfacet - - facet normal -0.382025898 0.810512841 0.443987906 - outer loop - vertex -0.834199011 -1.74269497 1.16037703 - vertex -0.879109979 -1.74321699 1.12268603 - vertex -0.837188005 -1.27625394 0.306304008 - endloop - endfacet - - facet normal -0.377227038 0.812248111 0.44491905 - outer loop - vertex -0.756784022 -1.266909 0.357414007 - vertex -0.834199011 -1.74269497 1.16037703 - vertex -0.837188005 -1.27625394 0.306304008 - endloop - endfacet - - facet normal -0.615875244 -0.27661112 0.737688243 - outer loop - vertex -0.834199011 -1.74269497 1.16037703 - vertex -0.882930994 -1.81293797 1.09335303 - vertex -0.879109979 -1.74321699 1.12268603 - endloop - endfacet - - facet normal -0.620292902 -0.27173996 0.735794902 - outer loop - vertex -0.834199011 -1.74269497 1.16037703 - vertex -0.807870984 -1.83105493 1.14994001 - vertex -0.882930994 -1.81293797 1.09335303 - endloop - endfacet - - facet normal -0.567791045 -0.538507044 0.622594059 - outer loop - vertex -0.923036993 -2.78265285 0.218032002 - vertex -1.00135601 -2.75531101 0.170255005 - vertex -0.882930994 -1.81293797 1.09335303 - endloop - endfacet - - facet normal -0.588796079 -0.528039038 0.61195904 - outer loop - vertex -0.807870984 -1.83105493 1.14994001 - vertex -0.923036993 -2.78265285 0.218032002 - vertex -0.882930994 -1.81293797 1.09335303 - endloop - endfacet - - facet normal -0.461007148 -0.844552219 0.272404075 - outer loop - vertex -0.923036993 -2.78265285 0.218032002 - vertex -1.009624 -3.23152399 -1.32017207 - vertex -1.00135601 -2.75531101 0.170255005 - endloop - endfacet - - facet normal -0.626499951 -0.73802501 0.250632972 - outer loop - vertex -0.923036993 -2.78265285 0.218032002 - vertex -0.974340975 -3.25622606 -1.30471396 - vertex -1.009624 -3.23152399 -1.32017207 - endloop - endfacet - - facet normal -0.521524727 -0.586632669 0.61957562 - outer loop - vertex -1.08034897 -3.51051307 -1.64385903 - vertex -1.09768903 -3.50087404 -1.64932895 - vertex -1.009624 -3.23152399 -1.32017207 - endloop - endfacet - - facet normal -0.621271908 -0.521286964 0.58504796 - outer loop - vertex -0.974340975 -3.25622606 -1.30471396 - vertex -1.08034897 -3.51051307 -1.64385903 - vertex -1.009624 -3.23152399 -1.32017207 - endloop - endfacet - - facet normal 0.0729890391 -0.42498818 0.902251422 - outer loop - vertex -0.790997982 -0.401975006 -0.0302719995 - vertex -0.627824008 -0.379043013 -0.0326699987 - vertex -0.674250007 -0.500160992 -0.0859650001 - endloop - endfacet - - facet normal 0.288157016 0.809092045 0.512187064 - outer loop - vertex -1.07667601 -3.52539802 -1.62241101 - vertex -1.09849703 -3.51748109 -1.62264204 - vertex -1.08034897 -3.51051307 -1.64385903 - endloop - endfacet - - facet normal -0.970688343 0.189770058 0.147484049 - outer loop - vertex -0.733861029 -1.19883895 0.420700014 - vertex -0.756784022 -1.266909 0.357414007 - vertex -0.674250007 -0.500160992 -0.0859650001 - endloop - endfacet - - facet normal -0.937109649 0.253748894 0.239659891 - outer loop - vertex -0.627824008 -0.379043013 -0.0326699987 - vertex -0.733861029 -1.19883895 0.420700014 - vertex -0.674250007 -0.500160992 -0.0859650001 - endloop - endfacet - - facet normal -0.962245107 0.26455602 0.0639880076 - outer loop - vertex -0.733861029 -1.19883895 0.420700014 - vertex -0.834199011 -1.74269497 1.16037703 - vertex -0.756784022 -1.266909 0.357414007 - endloop - endfacet - - facet normal -0.992402613 0.11104396 -0.0529739819 - outer loop - vertex -0.733861029 -1.19883895 0.420700014 - vertex -0.835425019 -1.72230005 1.22608101 - vertex -0.834199011 -1.74269497 1.16037703 - endloop - endfacet - - facet normal -0.955060899 -0.270181984 -0.121902987 - outer loop - vertex -0.806580007 -1.87417507 1.23539805 - vertex -0.807870984 -1.83105493 1.14994001 - vertex -0.834199011 -1.74269497 1.16037703 - endloop - endfacet - - facet normal -0.982130885 -0.184146971 0.0388449915 - outer loop - vertex -0.835425019 -1.72230005 1.22608101 - vertex -0.806580007 -1.87417507 1.23539805 - vertex -0.834199011 -1.74269497 1.16037703 - endloop - endfacet - - facet normal -0.99619478 0.0708289742 0.0507849865 - outer loop - vertex -0.806580007 -1.87417507 1.23539805 - vertex -0.923036993 -2.78265285 0.218032002 - vertex -0.807870984 -1.83105493 1.14994001 - endloop - endfacet - - facet normal -0.982285738 -0.0691299886 0.174171969 - outer loop - vertex -0.806580007 -1.87417507 1.23539805 - vertex -0.908545017 -2.86312103 0.267821014 - vertex -0.923036993 -2.78265285 0.218032002 - endloop - endfacet - - facet normal -0.999482691 0.00903299823 0.0308649931 - outer loop - vertex -0.972894013 -3.30494308 -1.24361598 - vertex -0.974340975 -3.25622606 -1.30471396 - vertex -0.923036993 -2.78265285 0.218032002 - endloop - endfacet - - facet normal -0.98847574 -0.128696963 0.0797049776 - outer loop - vertex -0.908545017 -2.86312103 0.267821014 - vertex -0.972894013 -3.30494308 -1.24361598 - vertex -0.923036993 -2.78265285 0.218032002 - endloop - endfacet - - facet normal -0.969163716 0.180971935 0.167244941 - outer loop - vertex -0.972894013 -3.30494308 -1.24361598 - vertex -1.08034897 -3.51051307 -1.64385903 - vertex -0.974340975 -3.25622606 -1.30471396 - endloop - endfacet - - facet normal -0.972173095 0.078271009 0.220801026 - outer loop - vertex -0.972894013 -3.30494308 -1.24361598 - vertex -1.07667601 -3.52539802 -1.62241101 - vertex -1.08034897 -3.51051307 -1.64385903 - endloop - endfacet - - facet normal -0.0414419994 0.415021986 0.908867061 - outer loop - vertex -0.614050984 0.300803006 -0.0098240003 - vertex -0.709182978 0.205512002 0.029352 - vertex -0.771920979 0.340665013 -0.0352250002 - endloop - endfacet - - facet normal 0.109958999 -0.990333974 0.084544003 - outer loop - vertex -0.45371899 3.54508805 -1.52100492 - vertex -0.47586599 3.54209304 -1.52728605 - vertex -0.468991995 3.54522705 -1.49951506 - endloop - endfacet - - facet normal -0.570165336 0.334529191 -0.750334442 - outer loop - vertex -0.614050984 0.300803006 -0.0098240003 - vertex -0.712240994 1.12719202 0.433227003 - vertex -0.77559799 1.05748606 0.450291991 - endloop - endfacet - - facet normal -0.611606061 0.311619043 -0.727208078 - outer loop - vertex -0.614050984 0.300803006 -0.0098240003 - vertex -0.77559799 1.05748606 0.450291991 - vertex -0.709182978 0.205512002 0.029352 - endloop - endfacet - - facet normal -0.695668578 0.507932723 -0.507985711 - outer loop - vertex -0.712240994 1.12719202 0.433227003 - vertex -0.897680998 1.65840197 1.21833503 - vertex -0.77559799 1.05748606 0.450291991 - endloop - endfacet - - facet normal -0.568472028 0.601665974 -0.561104 - outer loop - vertex -0.897680998 1.65840197 1.21833503 - vertex -0.953411996 1.63847899 1.25343502 - vertex -0.77559799 1.05748606 0.450291991 - endloop - endfacet - - facet normal -0.332945973 0.200343996 -0.921416938 - outer loop - vertex -0.897680998 1.65840197 1.21833503 - vertex -0.855413973 1.80642998 1.23524797 - vertex -0.932361007 1.84956503 1.27243102 - endloop - endfacet - - facet normal -0.561175942 0.129531994 -0.817498028 - outer loop - vertex -0.897680998 1.65840197 1.21833503 - vertex -0.932361007 1.84956503 1.27243102 - vertex -0.953411996 1.63847899 1.25343502 - endloop - endfacet - - facet normal -0.57141304 -0.403153062 -0.714811087 - outer loop - vertex -0.855413973 1.80642998 1.23524797 - vertex -0.710753024 2.34805298 0.814131975 - vertex -0.932361007 1.84956503 1.27243102 - endloop - endfacet - - facet normal -0.60945785 -0.37249589 -0.699862778 - outer loop - vertex -0.710753024 2.34805298 0.814131975 - vertex -0.764732003 2.42695308 0.819145024 - vertex -0.932361007 1.84956503 1.27243102 - endloop - endfacet - - facet normal -0.704304576 -0.627007663 -0.332890779 - outer loop - vertex -0.710753024 2.34805298 0.814131975 - vertex -0.440544993 3.16025901 -1.28735995 - vertex -0.480170995 3.16933012 -1.220608 - endloop - endfacet - - facet normal -0.79560107 -0.52511704 -0.302111 - outer loop - vertex -0.710753024 2.34805298 0.814131975 - vertex -0.480170995 3.16933012 -1.220608 - vertex -0.764732003 2.42695308 0.819145024 - endloop - endfacet - - facet normal -0.836549938 -0.30498296 -0.455159009 - outer loop - vertex -0.440544993 3.16025901 -1.28735995 - vertex -0.45371899 3.54508805 -1.52100492 - vertex -0.480170995 3.16933012 -1.220608 - endloop - endfacet - - facet normal -0.756833076 -0.374799043 -0.535471082 - outer loop - vertex -0.45371899 3.54508805 -1.52100492 - vertex -0.468991995 3.54522705 -1.49951506 - vertex -0.480170995 3.16933012 -1.220608 - endloop - endfacet - - facet normal -0.0414459892 0.4150199 0.908867776 - outer loop - vertex -0.709182978 0.205512002 0.029352 - vertex -0.851558983 0.211992994 0.0198999997 - vertex -0.771920979 0.340665013 -0.0352250002 - endloop - endfacet - - facet normal 0.10995701 -0.990334094 0.0845450088 - outer loop - vertex -0.468991995 3.54522705 -1.49951506 - vertex -0.47586599 3.54209304 -1.52728605 - vertex -0.489443004 3.54300499 -1.49893701 - endloop - endfacet - - facet normal 0.0794950202 0.44653213 -0.891229272 - outer loop - vertex -0.709182978 0.205512002 0.029352 - vertex -0.77559799 1.05748606 0.450291991 - vertex -0.851558983 0.211992994 0.0198999997 - endloop - endfacet - - facet normal 0.362293869 0.396774888 -0.843393683 - outer loop - vertex -0.77559799 1.05748606 0.450291991 - vertex -0.859055996 1.04065704 0.406524003 - vertex -0.851558983 0.211992994 0.0198999997 - endloop - endfacet - - facet normal 0.13829203 0.816729188 -0.560204148 - outer loop - vertex -0.77559799 1.05748606 0.450291991 - vertex -0.953411996 1.63847899 1.25343502 - vertex -1.00819802 1.63379192 1.23307705 - endloop - endfacet - - facet normal 0.130304053 0.816518307 -0.562422156 - outer loop - vertex -0.77559799 1.05748606 0.450291991 - vertex -1.00819802 1.63379192 1.23307705 - vertex -0.859055996 1.04065704 0.406524003 - endloop - endfacet - - facet normal 0.344124049 0.0500540063 -0.937589109 - outer loop - vertex -0.953411996 1.63847899 1.25343502 - vertex -0.932361007 1.84956503 1.27243102 - vertex -1.00819802 1.63379192 1.23307705 - endloop - endfacet - - facet normal 0.410272747 0.0220819861 -0.91169548 - outer loop - vertex -0.932361007 1.84956503 1.27243102 - vertex -1.01917601 1.86060798 1.23362994 - vertex -1.00819802 1.63379192 1.23307705 - endloop - endfacet - - facet normal 0.258674949 -0.641833842 -0.72189784 - outer loop - vertex -0.932361007 1.84956503 1.27243102 - vertex -0.764732003 2.42695308 0.819145024 - vertex -0.845875025 2.44682598 0.772400022 - endloop - endfacet - - facet normal 0.242763117 -0.643158317 -0.72623229 - outer loop - vertex -0.932361007 1.84956503 1.27243102 - vertex -0.845875025 2.44682598 0.772400022 - vertex -1.01917601 1.86060798 1.23362994 - endloop - endfacet - - facet normal -0.0306019913 -0.937877655 -0.345613897 - outer loop - vertex -0.764732003 2.42695308 0.819145024 - vertex -0.480170995 3.16933012 -1.220608 - vertex -0.845875025 2.44682598 0.772400022 - endloop - endfacet - - facet normal 0.0047039995 -0.940395951 -0.340048969 - outer loop - vertex -0.480170995 3.16933012 -1.220608 - vertex -0.525406003 3.1667769 -1.21417403 - vertex -0.845875025 2.44682598 0.772400022 - endloop - endfacet - - facet normal 0.0421100184 -0.596148252 -0.801769316 - outer loop - vertex -0.480170995 3.16933012 -1.220608 - vertex -0.468991995 3.54522705 -1.49951506 - vertex -0.489443004 3.54300499 -1.49893701 - endloop - endfacet - - facet normal -0.0798940063 -0.596715033 -0.798466146 - outer loop - vertex -0.480170995 3.16933012 -1.220608 - vertex -0.489443004 3.54300499 -1.49893701 - vertex -0.525406003 3.1667769 -1.21417403 - endloop - endfacet - - facet normal -0.0414439961 0.415019959 0.908867955 - outer loop - vertex -0.851558983 0.211992994 0.0198999997 - vertex -0.933965981 0.31536901 -0.0310629997 - vertex -0.771920979 0.340665013 -0.0352250002 - endloop - endfacet - - facet normal 0.109933957 -0.99033761 0.0845339671 - outer loop - vertex -0.489443004 3.54300499 -1.49893701 - vertex -0.47586599 3.54209304 -1.52728605 - vertex -0.499669999 3.540097 -1.51970506 - endloop - endfacet - - facet normal 0.903346419 0.188033089 -0.385498166 - outer loop - vertex -0.851558983 0.211992994 0.0198999997 - vertex -0.859055996 1.04065704 0.406524003 - vertex -0.899770975 1.089378 0.334881008 - endloop - endfacet - - facet normal 0.726601124 0.267169058 -0.632986128 - outer loop - vertex -0.851558983 0.211992994 0.0198999997 - vertex -0.899770975 1.089378 0.334881008 - vertex -0.933965981 0.31536901 -0.0310629997 - endloop - endfacet - - facet normal 0.867568493 0.464764267 -0.176972091 - outer loop - vertex -0.859055996 1.04065704 0.406524003 - vertex -1.00819802 1.63379192 1.23307705 - vertex -0.899770975 1.089378 0.334881008 - endloop - endfacet - - facet normal 0.925577819 0.362778932 -0.108153984 - outer loop - vertex -1.00819802 1.63379192 1.23307705 - vertex -1.02078402 1.64787102 1.17259204 - vertex -0.899770975 1.089378 0.334881008 - endloop - endfacet - - facet normal 0.932907045 0.0460240059 -0.357164025 - outer loop - vertex -1.00819802 1.63379192 1.23307705 - vertex -1.01917601 1.86060798 1.23362994 - vertex -1.05048597 1.83123994 1.14806604 - endloop - endfacet - - facet normal 0.975855947 0.135112986 -0.171608999 - outer loop - vertex -1.00819802 1.63379192 1.23307705 - vertex -1.05048597 1.83123994 1.14806604 - vertex -1.02078402 1.64787102 1.17259204 - endloop - endfacet - - facet normal 0.89283818 -0.410217047 -0.185908034 - outer loop - vertex -1.01917601 1.86060798 1.23362994 - vertex -0.845875025 2.44682598 0.772400022 - vertex -1.05048597 1.83123994 1.14806604 - endloop - endfacet - - facet normal 0.85756886 -0.443764925 -0.260092974 - outer loop - vertex -0.845875025 2.44682598 0.772400022 - vertex -0.893078029 2.39270806 0.709097981 - vertex -1.05048597 1.83123994 1.14806604 - endloop - endfacet - - facet normal 0.783792257 -0.613570154 -0.0959240273 - outer loop - vertex -0.845875025 2.44682598 0.772400022 - vertex -0.525406003 3.1667769 -1.21417403 - vertex -0.542185009 3.15452409 -1.27289903 - endloop - endfacet - - facet normal 0.799032509 -0.594929636 -0.0872109383 - outer loop - vertex -0.845875025 2.44682598 0.772400022 - vertex -0.542185009 3.15452409 -1.27289903 - vertex -0.893078029 2.39270806 0.709097981 - endloop - endfacet - - facet normal 0.942718029 -0.253892004 -0.21638301 - outer loop - vertex -0.525406003 3.1667769 -1.21417403 - vertex -0.489443004 3.54300499 -1.49893701 - vertex -0.542185009 3.15452409 -1.27289903 - endloop - endfacet - - facet normal 0.862533689 -0.336815894 -0.377611876 - outer loop - vertex -0.489443004 3.54300499 -1.49893701 - vertex -0.499669999 3.540097 -1.51970506 - vertex -0.542185009 3.15452409 -1.27289903 - endloop - endfacet - - facet normal -0.0414459854 0.41502884 0.908863723 - outer loop - vertex -0.933965981 0.31536901 -0.0310629997 - vertex -0.894349992 0.43779099 -0.0851600021 - vertex -0.771920979 0.340665013 -0.0352250002 - endloop - endfacet - - facet normal 0.109905973 -0.990348756 0.0844399855 - outer loop - vertex -0.499669999 3.540097 -1.51970506 - vertex -0.47586599 3.54209304 -1.52728605 - vertex -0.491973996 3.5386939 -1.54618096 - endloop - endfacet - - facet normal 0.941180944 -0.177515998 0.287517965 - outer loop - vertex -0.933965981 0.31536901 -0.0310629997 - vertex -0.899770975 1.089378 0.334881008 - vertex -0.894349992 0.43779099 -0.0851600021 - endloop - endfacet - - facet normal 0.925101757 -0.200271949 0.322610945 - outer loop - vertex -0.899770975 1.089378 0.334881008 - vertex -0.867084026 1.16696095 0.289312005 - vertex -0.894349992 0.43779099 -0.0851600021 - endloop - endfacet - - facet normal 0.80964005 -0.427607 0.402038991 - outer loop - vertex -0.899770975 1.089378 0.334881008 - vertex -1.02078402 1.64787102 1.17259204 - vertex -0.981692016 1.67011404 1.11752605 - endloop - endfacet - - facet normal 0.933612227 -0.233975038 0.271337062 - outer loop - vertex -0.899770975 1.089378 0.334881008 - vertex -0.981692016 1.67011404 1.11752605 - vertex -0.867084026 1.16696095 0.289312005 - endloop - endfacet - - facet normal 0.756896913 0.205576986 0.620359004 - outer loop - vertex -1.02078402 1.64787102 1.17259204 - vertex -1.05048597 1.83123994 1.14806604 - vertex -0.981692016 1.67011404 1.11752605 - endloop - endfacet - - facet normal 0.866550148 0.293460041 0.403698057 - outer loop - vertex -1.05048597 1.83123994 1.14806604 - vertex -1.00271201 1.78357601 1.08016706 - vertex -0.981692016 1.67011404 1.11752605 - endloop - endfacet - - facet normal 0.904024541 0.0755139664 0.4207578 - outer loop - vertex -1.05048597 1.83123994 1.14806604 - vertex -0.893078029 2.39270806 0.709097981 - vertex -0.870796978 2.30535197 0.676904976 - endloop - endfacet - - facet normal 0.856916606 0.161637917 0.48945576 - outer loop - vertex -1.05048597 1.83123994 1.14806604 - vertex -0.870796978 2.30535197 0.676904976 - vertex -1.00271201 1.78357601 1.08016706 - endloop - endfacet - - facet normal 0.959722757 0.15956296 0.231239945 - outer loop - vertex -0.893078029 2.39270806 0.709097981 - vertex -0.542185009 3.15452409 -1.27289903 - vertex -0.870796978 2.30535197 0.676904976 - endloop - endfacet - - facet normal 0.942926586 0.216352895 0.25314188 - outer loop - vertex -0.542185009 3.15452409 -1.27289903 - vertex -0.517876983 3.14179301 -1.35256696 - vertex -0.870796978 2.30535197 0.676904976 - endloop - endfacet - - facet normal 0.958869755 0.070299983 0.275002927 - outer loop - vertex -0.542185009 3.15452409 -1.27289903 - vertex -0.499669999 3.540097 -1.51970506 - vertex -0.491973996 3.5386939 -1.54618096 - endloop - endfacet - - facet normal 0.957076609 0.07421197 0.280172884 - outer loop - vertex -0.542185009 3.15452409 -1.27289903 - vertex -0.491973996 3.5386939 -1.54618096 - vertex -0.517876983 3.14179301 -1.35256696 - endloop - endfacet - - facet normal -0.0414430015 0.415033042 0.908861995 - outer loop - vertex -0.894349992 0.43779099 -0.0851600021 - vertex -0.762543023 0.487076014 -0.101654999 - vertex -0.771920979 0.340665013 -0.0352250002 - endloop - endfacet - - facet normal 0.109901994 -0.990348935 0.0844429955 - outer loop - vertex -0.491973996 3.5386939 -1.54618096 - vertex -0.47586599 3.54209304 -1.52728605 - vertex -0.472149014 3.53985 -1.55842602 - endloop - endfacet - - facet normal 0.109039977 -0.457335919 0.882583737 - outer loop - vertex -0.894349992 0.43779099 -0.0851600021 - vertex -0.867084026 1.16696095 0.289312005 - vertex -0.78560698 1.21498394 0.304129988 - endloop - endfacet - - facet normal 0.277854979 -0.461013973 0.842770934 - outer loop - vertex -0.894349992 0.43779099 -0.0851600021 - vertex -0.78560698 1.21498394 0.304129988 - vertex -0.762543023 0.487076014 -0.101654999 - endloop - endfacet - - facet normal 0.361492991 -0.773815036 0.520128012 - outer loop - vertex -0.867084026 1.16696095 0.289312005 - vertex -0.981692016 1.67011404 1.11752605 - vertex -0.78560698 1.21498394 0.304129988 - endloop - endfacet - - facet normal 0.251163065 -0.817641199 0.518054128 - outer loop - vertex -0.981692016 1.67011404 1.11752605 - vertex -0.920360029 1.68377101 1.10934496 - vertex -0.78560698 1.21498394 0.304129988 - endloop - endfacet - - facet normal 0.0991519839 0.327727944 0.93955487 - outer loop - vertex -0.981692016 1.67011404 1.11752605 - vertex -1.00271201 1.78357601 1.08016706 - vertex -0.911831021 1.75351 1.08106399 - endloop - endfacet - - facet normal 0.0410849974 0.371169001 0.927655935 - outer loop - vertex -0.981692016 1.67011404 1.11752605 - vertex -0.911831021 1.75351 1.08106399 - vertex -0.920360029 1.68377101 1.10934496 - endloop - endfacet - - facet normal 0.181426108 0.57224232 0.799764454 - outer loop - vertex -1.00271201 1.78357601 1.08016706 - vertex -0.870796978 2.30535197 0.676904976 - vertex -0.911831021 1.75351 1.08106399 - endloop - endfacet - - facet normal 0.172062024 0.573693037 0.800794065 - outer loop - vertex -0.870796978 2.30535197 0.676904976 - vertex -0.795808017 2.25053501 0.70006299 - vertex -0.911831021 1.75351 1.08106399 - endloop - endfacet - - facet normal 0.41394791 0.814008892 0.407475978 - outer loop - vertex -0.870796978 2.30535197 0.676904976 - vertex -0.517876983 3.14179301 -1.35256696 - vertex -0.470782012 3.13817596 -1.39318299 - endloop - endfacet - - facet normal 0.453948915 0.792807758 0.406676918 - outer loop - vertex -0.870796978 2.30535197 0.676904976 - vertex -0.470782012 3.13817596 -1.39318299 - vertex -0.795808017 2.25053501 0.70006299 - endloop - endfacet - - facet normal 0.635360062 0.304674029 0.709571123 - outer loop - vertex -0.517876983 3.14179301 -1.35256696 - vertex -0.491973996 3.5386939 -1.54618096 - vertex -0.470782012 3.13817596 -1.39318299 - endloop - endfacet - - facet normal 0.480959207 0.334958136 0.810235322 - outer loop - vertex -0.491973996 3.5386939 -1.54618096 - vertex -0.472149014 3.53985 -1.55842602 - vertex -0.470782012 3.13817596 -1.39318299 - endloop - endfacet - - facet normal -0.0414400063 0.415033072 0.908862054 - outer loop - vertex -0.762543023 0.487076014 -0.101654999 - vertex -0.637798011 0.426109999 -0.0681279972 - vertex -0.771920979 0.340665013 -0.0352250002 - endloop - endfacet - - facet normal 0.110049009 -0.990331113 0.0844600126 - outer loop - vertex -0.472149014 3.53985 -1.55842602 - vertex -0.47586599 3.54209304 -1.52728605 - vertex -0.455123991 3.54269695 -1.54722202 - endloop - endfacet - - facet normal -0.430224836 -0.44991383 0.782613695 - outer loop - vertex -0.762543023 0.487076014 -0.101654999 - vertex -0.78560698 1.21498394 0.304129988 - vertex -0.637798011 0.426109999 -0.0681279972 - endloop - endfacet - - facet normal -0.674363017 -0.414663017 0.610974014 - outer loop - vertex -0.78560698 1.21498394 0.304129988 - vertex -0.716696024 1.19728494 0.368178993 - vertex -0.637798011 0.426109999 -0.0681279972 - endloop - endfacet - - facet normal -0.534693182 -0.766140282 0.356556147 - outer loop - vertex -0.78560698 1.21498394 0.304129988 - vertex -0.920360029 1.68377101 1.10934496 - vertex -0.882972002 1.67855692 1.15420997 - endloop - endfacet - - facet normal -0.530332863 -0.768367827 0.358270913 - outer loop - vertex -0.78560698 1.21498394 0.304129988 - vertex -0.882972002 1.67855692 1.15420997 - vertex -0.716696024 1.19728494 0.368178993 - endloop - endfacet - - facet normal -0.702823222 0.339469105 0.62514019 - outer loop - vertex -0.920360029 1.68377101 1.10934496 - vertex -0.911831021 1.75351 1.08106399 - vertex -0.882972002 1.67855692 1.15420997 - endloop - endfacet - - facet normal -0.707317054 0.335090995 0.62242806 - outer loop - vertex -0.911831021 1.75351 1.08106399 - vertex -0.846276999 1.76368093 1.15008199 - vertex -0.882972002 1.67855692 1.15420997 - endloop - endfacet - - facet normal -0.619192243 0.563640237 0.54672724 - outer loop - vertex -0.911831021 1.75351 1.08106399 - vertex -0.795808017 2.25053501 0.70006299 - vertex -0.72458303 2.26954007 0.761135995 - endloop - endfacet - - facet normal -0.640845776 0.558843732 0.526317775 - outer loop - vertex -0.911831021 1.75351 1.08106399 - vertex -0.72458303 2.26954007 0.761135995 - vertex -0.846276999 1.76368093 1.15008199 - endloop - endfacet - - facet normal -0.466304839 0.838133693 0.28300491 - outer loop - vertex -0.795808017 2.25053501 0.70006299 - vertex -0.470782012 3.13817596 -1.39318299 - vertex -0.72458303 2.26954007 0.761135995 - endloop - endfacet - - facet normal -0.445953965 0.847108006 0.289020956 - outer loop - vertex -0.470782012 3.13817596 -1.39318299 - vertex -0.436367005 3.14639306 -1.36416399 - vertex -0.72458303 2.26954007 0.761135995 - endloop - endfacet - - facet normal -0.558003902 0.314087898 0.768101811 - outer loop - vertex -0.470782012 3.13817596 -1.39318299 - vertex -0.472149014 3.53985 -1.55842602 - vertex -0.455123991 3.54269695 -1.54722202 - endloop - endfacet - - facet normal -0.656410813 0.290523916 0.696218848 - outer loop - vertex -0.470782012 3.13817596 -1.39318299 - vertex -0.455123991 3.54269695 -1.54722202 - vertex -0.436367005 3.14639306 -1.36416399 - endloop - endfacet - - facet normal -0.041439008 0.415031046 0.908863068 - outer loop - vertex -0.637798011 0.426109999 -0.0681279972 - vertex -0.614050984 0.300803006 -0.0098240003 - vertex -0.771920979 0.340665013 -0.0352250002 - endloop - endfacet - - facet normal 0.109999016 -0.990341127 0.0844070092 - outer loop - vertex -0.455123991 3.54269695 -1.54722202 - vertex -0.47586599 3.54209304 -1.52728605 - vertex -0.45371899 3.54508805 -1.52100492 - endloop - endfacet - - facet normal -0.995849967 -0.0872669965 -0.0258349981 - outer loop - vertex -0.637798011 0.426109999 -0.0681279972 - vertex -0.716696024 1.19728494 0.368178993 - vertex -0.712240994 1.12719202 0.433227003 - endloop - endfacet - - facet normal -0.985554934 -0.154403999 0.0695760027 - outer loop - vertex -0.637798011 0.426109999 -0.0681279972 - vertex -0.712240994 1.12719202 0.433227003 - vertex -0.614050984 0.300803006 -0.0098240003 - endloop - endfacet - - facet normal -0.980780065 -0.162526011 -0.107962005 - outer loop - vertex -0.716696024 1.19728494 0.368178993 - vertex -0.882972002 1.67855692 1.15420997 - vertex -0.712240994 1.12719202 0.433227003 - endloop - endfacet - - facet normal -0.974204123 -0.00670300052 -0.22556901 - outer loop - vertex -0.882972002 1.67855692 1.15420997 - vertex -0.897680998 1.65840197 1.21833503 - vertex -0.712240994 1.12719202 0.433227003 - endloop - endfacet - - facet normal -0.886417329 0.368533105 -0.280085117 - outer loop - vertex -0.882972002 1.67855692 1.15420997 - vertex -0.846276999 1.76368093 1.15008199 - vertex -0.855413973 1.80642998 1.23524797 - endloop - endfacet - - facet normal -0.949701548 0.285797149 -0.128012076 - outer loop - vertex -0.882972002 1.67855692 1.15420997 - vertex -0.855413973 1.80642998 1.23524797 - vertex -0.897680998 1.65840197 1.21833503 - endloop - endfacet - - facet normal -0.980542064 0.111841008 -0.161335021 - outer loop - vertex -0.846276999 1.76368093 1.15008199 - vertex -0.72458303 2.26954007 0.761135995 - vertex -0.855413973 1.80642998 1.23524797 - endloop - endfacet - - facet normal -0.975146174 0.212986052 -0.0610480122 - outer loop - vertex -0.72458303 2.26954007 0.761135995 - vertex -0.710753024 2.34805298 0.814131975 - vertex -0.855413973 1.80642998 1.23524797 - endloop - endfacet - - facet normal -0.987725139 0.135249019 -0.0781470165 - outer loop - vertex -0.72458303 2.26954007 0.761135995 - vertex -0.436367005 3.14639306 -1.36416399 - vertex -0.440544993 3.16025901 -1.28735995 - endloop - endfacet - - facet normal -0.977861404 0.203926086 -0.0469170213 - outer loop - vertex -0.72458303 2.26954007 0.761135995 - vertex -0.440544993 3.16025901 -1.28735995 - vertex -0.710753024 2.34805298 0.814131975 - endloop - endfacet - - facet normal -0.996882498 -0.0666720271 -0.0421910174 - outer loop - vertex -0.436367005 3.14639306 -1.36416399 - vertex -0.455123991 3.54269695 -1.54722202 - vertex -0.440544993 3.16025901 -1.28735995 - endloop - endfacet - - facet normal -0.998558164 -0.00160700036 0.0536570102 - outer loop - vertex -0.455123991 3.54269695 -1.54722202 - vertex -0.45371899 3.54508805 -1.52100492 - vertex -0.440544993 3.16025901 -1.28735995 - endloop - endfacet - - facet normal 0.272473931 0.292851925 0.916512847 - outer loop - vertex -0.253796995 0.214561 -0.0458349995 - vertex -0.376740992 0.170128003 0.00491400016 - vertex -0.376302004 0.324781001 -0.0446330011 - endloop - endfacet - - facet normal -0.247445986 -0.867469966 0.431585968 - outer loop - vertex 0.933724999 3.97960711 -1.55623698 - vertex 0.911266029 3.98531508 -1.55764103 - vertex 0.926603973 3.99251413 -1.53437805 - endloop - endfacet - - facet normal -0.777862728 0.563657761 -0.277883887 - outer loop - vertex -0.253796995 0.214561 -0.0458349995 - vertex 0.103583999 0.929287016 0.403519988 - vertex 0.0455540009 0.876025975 0.457924992 - endloop - endfacet - - facet normal -0.477919996 0.658785999 -0.581027985 - outer loop - vertex -0.253796995 0.214561 -0.0458349995 - vertex 0.0455540009 0.876025975 0.457924992 - vertex -0.376740992 0.170128003 0.00491400016 - endloop - endfacet - - facet normal -0.779423177 0.557750106 -0.285332054 - outer loop - vertex 0.103583999 0.929287016 0.403519988 - vertex 0.204065993 1.50625706 1.256863 - vertex 0.0455540009 0.876025975 0.457924992 - endloop - endfacet - - facet normal -0.765340209 0.570419192 -0.298121095 - outer loop - vertex 0.204065993 1.50625706 1.256863 - vertex 0.174039006 1.49804199 1.31822896 - vertex 0.0455540009 0.876025975 0.457924992 - endloop - endfacet - - facet normal -0.725149751 0.339532942 -0.599061906 - outer loop - vertex 0.204065993 1.50625706 1.256863 - vertex 0.282788992 1.63802505 1.23625302 - vertex 0.252124012 1.695822 1.30613101 - endloop - endfacet - - facet normal -0.867439389 0.319119155 -0.381722182 - outer loop - vertex 0.204065993 1.50625706 1.256863 - vertex 0.252124012 1.695822 1.30613101 - vertex 0.174039006 1.49804199 1.31822896 - endloop - endfacet - - facet normal -0.88659811 0.0803210139 -0.45551309 - outer loop - vertex 0.282788992 1.63802505 1.23625302 - vertex 0.676886976 2.68409705 0.653648019 - vertex 0.252124012 1.695822 1.30613101 - endloop - endfacet - - facet normal -0.822990119 -0.0210630018 -0.56766504 - outer loop - vertex 0.676886976 2.68409705 0.653648019 - vertex 0.655480981 2.77318811 0.681376994 - vertex 0.252124012 1.695822 1.30613101 - endloop - endfacet - - facet normal -0.987821043 -0.06729801 -0.140287012 - outer loop - vertex 0.676886976 2.68409705 0.653648019 - vertex 0.870212972 3.71105099 -1.20028496 - vertex 0.858199 3.75496197 -1.13675594 - endloop - endfacet - - facet normal -0.965101719 -0.169848949 -0.199323922 - outer loop - vertex 0.676886976 2.68409705 0.653648019 - vertex 0.858199 3.75496197 -1.13675594 - vertex 0.655480981 2.77318811 0.681376994 - endloop - endfacet - - facet normal -0.983463764 -0.00726499874 -0.180958956 - outer loop - vertex 0.870212972 3.71105099 -1.20028496 - vertex 0.933724999 3.97960711 -1.55623698 - vertex 0.858199 3.75496197 -1.13675594 - endloop - endfacet - - facet normal -0.962732613 -0.124580957 -0.240052924 - outer loop - vertex 0.933724999 3.97960711 -1.55623698 - vertex 0.926603973 3.99251413 -1.53437805 - vertex 0.858199 3.75496197 -1.13675594 - endloop - endfacet - - facet normal 0.272466928 0.292851955 0.916514874 - outer loop - vertex -0.376740992 0.170128003 0.00491400016 - vertex -0.499356002 0.242149994 0.018352 - vertex -0.376302004 0.324781001 -0.0446330011 - endloop - endfacet - - facet normal -0.247477978 -0.86745286 0.431601971 - outer loop - vertex 0.926603973 3.99251413 -1.53437805 - vertex 0.911266029 3.98531508 -1.55764103 - vertex 0.907932997 4 -1.530038 - endloop - endfacet - - facet normal 0.172648042 0.456757128 -0.872677267 - outer loop - vertex -0.376740992 0.170128003 0.00491400016 - vertex 0.0455540009 0.876025975 0.457924992 - vertex -0.499356002 0.242149994 0.018352 - endloop - endfacet - - facet normal -0.0465159826 0.595931768 -0.801686645 - outer loop - vertex 0.0455540009 0.876025975 0.457924992 - vertex -0.0493079983 0.883087993 0.468677998 - vertex -0.499356002 0.242149994 0.018352 - endloop - endfacet - - facet normal 0.00578699959 0.809948981 -0.586471915 - outer loop - vertex 0.0455540009 0.876025975 0.457924992 - vertex 0.174039006 1.49804199 1.31822896 - vertex 0.117908001 1.50824094 1.331761 - endloop - endfacet - - facet normal -0.00608000066 0.810414016 -0.585826039 - outer loop - vertex 0.0455540009 0.876025975 0.457924992 - vertex 0.117908001 1.50824094 1.331761 - vertex -0.0493079983 0.883087993 0.468677998 - endloop - endfacet - - facet normal -0.228951991 0.0308769979 -0.972947955 - outer loop - vertex 0.174039006 1.49804199 1.31822896 - vertex 0.252124012 1.695822 1.30613101 - vertex 0.117908001 1.50824094 1.331761 - endloop - endfacet - - facet normal -0.16855301 -0.0140670007 -0.985592186 - outer loop - vertex 0.252124012 1.695822 1.30613101 - vertex 0.164021999 1.73031497 1.32070494 - vertex 0.117908001 1.50824094 1.331761 - endloop - endfacet - - facet normal -0.246820986 -0.415316969 -0.875551939 - outer loop - vertex 0.252124012 1.695822 1.30613101 - vertex 0.655480981 2.77318811 0.681376994 - vertex 0.569666028 2.81540704 0.685541987 - endloop - endfacet - - facet normal -0.298811018 -0.396427006 -0.86807698 - outer loop - vertex 0.252124012 1.695822 1.30613101 - vertex 0.569666028 2.81540704 0.685541987 - vertex 0.164021999 1.73031497 1.32070494 - endloop - endfacet - - facet normal -0.408190787 -0.78346467 -0.468575805 - outer loop - vertex 0.655480981 2.77318811 0.681376994 - vertex 0.858199 3.75496197 -1.13675594 - vertex 0.569666028 2.81540704 0.685541987 - endloop - endfacet - - facet normal -0.526139736 -0.719045639 -0.454037786 - outer loop - vertex 0.858199 3.75496197 -1.13675594 - vertex 0.819289029 3.77494597 -1.12331498 - vertex 0.569666028 2.81540704 0.685541987 - endloop - endfacet - - facet normal -0.419349998 -0.74575603 -0.517681003 - outer loop - vertex 0.858199 3.75496197 -1.13675594 - vertex 0.926603973 3.99251413 -1.53437805 - vertex 0.907932997 4 -1.530038 - endloop - endfacet - - facet normal -0.526108801 -0.690327764 -0.496645838 - outer loop - vertex 0.858199 3.75496197 -1.13675594 - vertex 0.907932997 4 -1.530038 - vertex 0.819289029 3.77494597 -1.12331498 - endloop - endfacet - - facet normal 0.272470057 0.292848051 0.916515231 - outer loop - vertex -0.499356002 0.242149994 0.018352 - vertex -0.529305995 0.376396 -0.0156389996 - vertex -0.376302004 0.324781001 -0.0446330011 - endloop - endfacet - - facet normal -0.24746196 -0.8674559 0.431604952 - outer loop - vertex 0.907932997 4 -1.530038 - vertex 0.911266029 3.98531508 -1.55764103 - vertex 0.891771019 3.99642801 -1.54648209 - endloop - endfacet - - facet normal 0.628429115 0.100671016 -0.771325052 - outer loop - vertex -0.499356002 0.242149994 0.018352 - vertex -0.0493079983 0.883087993 0.468677998 - vertex -0.109567001 0.945155025 0.427682996 - endloop - endfacet - - facet normal 0.73198396 -0.0091859987 -0.68125993 - outer loop - vertex -0.499356002 0.242149994 0.018352 - vertex -0.109567001 0.945155025 0.427682996 - vertex -0.529305995 0.376396 -0.0156389996 - endloop - endfacet - - facet normal 0.76850915 0.438165128 -0.466267079 - outer loop - vertex -0.0493079983 0.883087993 0.468677998 - vertex 0.117908001 1.50824094 1.331761 - vertex -0.109567001 0.945155025 0.427682996 - endloop - endfacet - - facet normal 0.759350061 0.449147046 -0.470802039 - outer loop - vertex 0.117908001 1.50824094 1.331761 - vertex 0.0779410005 1.52917695 1.28727102 - vertex -0.109567001 0.945155025 0.427682996 - endloop - endfacet - - facet normal 0.560264945 -0.156830981 -0.813330948 - outer loop - vertex 0.117908001 1.50824094 1.331761 - vertex 0.164021999 1.73031497 1.32070494 - vertex 0.0848250017 1.71552598 1.26900196 - endloop - endfacet - - facet normal 0.71818012 -0.0941240117 -0.689462125 - outer loop - vertex 0.117908001 1.50824094 1.331761 - vertex 0.0848250017 1.71552598 1.26900196 - vertex 0.0779410005 1.52917695 1.28727102 - endloop - endfacet - - facet normal 0.5217219 -0.567762852 -0.636750877 - outer loop - vertex 0.164021999 1.73031497 1.32070494 - vertex 0.569666028 2.81540704 0.685541987 - vertex 0.0848250017 1.71552598 1.26900196 - endloop - endfacet - - facet normal 0.426157176 -0.563345253 -0.70783627 - outer loop - vertex 0.569666028 2.81540704 0.685541987 - vertex 0.484059989 2.77896309 0.663007021 - vertex 0.0848250017 1.71552598 1.26900196 - endloop - endfacet - - facet normal 0.688727021 -0.67552197 -0.263296992 - outer loop - vertex 0.569666028 2.81540704 0.685541987 - vertex 0.819289029 3.77494597 -1.12331498 - vertex 0.782781005 3.75595093 -1.17007899 - endloop - endfacet - - facet normal 0.444303989 -0.81863606 -0.363907993 - outer loop - vertex 0.569666028 2.81540704 0.685541987 - vertex 0.782781005 3.75595093 -1.17007899 - vertex 0.484059989 2.77896309 0.663007021 - endloop - endfacet - - facet normal 0.672470868 -0.699886858 -0.24070996 - outer loop - vertex 0.819289029 3.77494597 -1.12331498 - vertex 0.907932997 4 -1.530038 - vertex 0.782781005 3.75595093 -1.17007899 - endloop - endfacet - - facet normal 0.523807168 -0.778528273 -0.345716119 - outer loop - vertex 0.907932997 4 -1.530038 - vertex 0.891771019 3.99642801 -1.54648209 - vertex 0.782781005 3.75595093 -1.17007899 - endloop - endfacet - - facet normal 0.272472084 0.292858124 0.916511357 - outer loop - vertex -0.529305995 0.376396 -0.0156389996 - vertex -0.44404301 0.471772999 -0.0714629963 - vertex -0.376302004 0.324781001 -0.0446330011 - endloop - endfacet - - facet normal -0.247544035 -0.867487133 0.43149507 - outer loop - vertex 0.891771019 3.99642801 -1.54648209 - vertex 0.911266029 3.98531508 -1.55764103 - vertex 0.890290976 3.98449087 -1.57133007 - endloop - endfacet - - facet normal 0.767617583 -0.634875655 0.087727949 - outer loop - vertex -0.529305995 0.376396 -0.0156389996 - vertex -0.109567001 0.945155025 0.427682996 - vertex -0.44404301 0.471772999 -0.0714629963 - endloop - endfacet - - facet normal 0.886159658 -0.420221835 -0.19528091 - outer loop - vertex -0.109567001 0.945155025 0.427682996 - vertex -0.0898490027 1.01548898 0.365808994 - vertex -0.44404301 0.471772999 -0.0714629963 - endloop - endfacet - - facet normal 0.946716785 -0.321837902 0.0121489968 - outer loop - vertex -0.109567001 0.945155025 0.427682996 - vertex 0.0779410005 1.52917695 1.28727102 - vertex 0.0842330009 1.54508102 1.21826005 - endloop - endfacet - - facet normal 0.958333194 -0.285045058 -0.0186190046 - outer loop - vertex -0.109567001 0.945155025 0.427682996 - vertex 0.0842330009 1.54508102 1.21826005 - vertex -0.0898490027 1.01548898 0.365808994 - endloop - endfacet - - facet normal 0.996036589 -0.028538987 0.0842419714 - outer loop - vertex 0.0779410005 1.52917695 1.28727102 - vertex 0.0848250017 1.71552598 1.26900196 - vertex 0.0842330009 1.54508102 1.21826005 - endloop - endfacet - - facet normal 0.985584736 0.0451189876 -0.163054958 - outer loop - vertex 0.0848250017 1.71552598 1.26900196 - vertex 0.0741709992 1.66259193 1.18995297 - vertex 0.0842330009 1.54508102 1.21826005 - endloop - endfacet - - facet normal 0.950562179 -0.277941048 0.138492033 - outer loop - vertex 0.0848250017 1.71552598 1.26900196 - vertex 0.484059989 2.77896309 0.663007021 - vertex 0.463129014 2.69129992 0.630741 - endloop - endfacet - - facet normal 0.946162283 -0.313099086 0.0821340233 - outer loop - vertex 0.0848250017 1.71552598 1.26900196 - vertex 0.463129014 2.69129992 0.630741 - vertex 0.0741709992 1.66259193 1.18995297 - endloop - endfacet - - facet normal 0.96979171 -0.242210925 0.0289449915 - outer loop - vertex 0.484059989 2.77896309 0.663007021 - vertex 0.782781005 3.75595093 -1.17007899 - vertex 0.463129014 2.69129992 0.630741 - endloop - endfacet - - facet normal 0.974733412 -0.219097078 0.0434880182 - outer loop - vertex 0.782781005 3.75595093 -1.17007899 - vertex 0.776166975 3.71228409 -1.24183702 - vertex 0.463129014 2.69129992 0.630741 - endloop - endfacet - - facet normal 0.951111138 -0.296674043 0.0858610049 - outer loop - vertex 0.782781005 3.75595093 -1.17007899 - vertex 0.891771019 3.99642801 -1.54648209 - vertex 0.890290976 3.98449087 -1.57133007 - endloop - endfacet - - facet normal 0.95249474 -0.291143954 0.0893819779 - outer loop - vertex 0.782781005 3.75595093 -1.17007899 - vertex 0.890290976 3.98449087 -1.57133007 - vertex 0.776166975 3.71228409 -1.24183702 - endloop - endfacet - - facet normal 0.272473961 0.292857975 0.91651094 - outer loop - vertex -0.44404301 0.471772999 -0.0714629963 - vertex -0.307767004 0.456461996 -0.107084997 - vertex -0.376302004 0.324781001 -0.0446330011 - endloop - endfacet - - facet normal -0.247539937 -0.867491841 0.431487888 - outer loop - vertex 0.890290976 3.98449087 -1.57133007 - vertex 0.911266029 3.98531508 -1.55764103 - vertex 0.904604971 3.97317505 -1.58586907 - endloop - endfacet - - facet normal 0.444817066 -0.719116151 0.533863127 - outer loop - vertex -0.44404301 0.471772999 -0.0714629963 - vertex -0.0898490027 1.01548898 0.365808994 - vertex -0.00500100013 1.04112697 0.329647988 - endloop - endfacet - - facet normal 0.127748966 -0.63515991 0.76174283 - outer loop - vertex -0.44404301 0.471772999 -0.0714629963 - vertex -0.00500100013 1.04112697 0.329647988 - vertex -0.307767004 0.456461996 -0.107084997 - endloop - endfacet - - facet normal 0.420685858 -0.806614757 0.415205866 - outer loop - vertex -0.0898490027 1.01548898 0.365808994 - vertex 0.0842330009 1.54508102 1.21826005 - vertex -0.00500100013 1.04112697 0.329647988 - endloop - endfacet - - facet normal 0.35758391 -0.827291846 0.433268875 - outer loop - vertex 0.0842330009 1.54508102 1.21826005 - vertex 0.132046998 1.54398 1.17669499 - vertex -0.00500100013 1.04112697 0.329647988 - endloop - endfacet - - facet normal 0.675312042 0.226875037 0.701770067 - outer loop - vertex 0.0842330009 1.54508102 1.21826005 - vertex 0.0741709992 1.66259193 1.18995297 - vertex 0.140081003 1.61137402 1.14308596 - endloop - endfacet - - facet normal 0.632811844 0.283820927 0.72041285 - outer loop - vertex 0.0842330009 1.54508102 1.21826005 - vertex 0.140081003 1.61137402 1.14308596 - vertex 0.132046998 1.54398 1.17669499 - endloop - endfacet - - facet normal 0.650720775 0.157726958 0.742754757 - outer loop - vertex 0.0741709992 1.66259193 1.18995297 - vertex 0.463129014 2.69129992 0.630741 - vertex 0.140081003 1.61137402 1.14308596 - endloop - endfacet - - facet normal 0.52620393 0.230870992 0.818417966 - outer loop - vertex 0.463129014 2.69129992 0.630741 - vertex 0.522632003 2.61842799 0.61303997 - vertex 0.140081003 1.61137402 1.14308596 - endloop - endfacet - - facet normal 0.890468955 0.320121974 0.323398978 - outer loop - vertex 0.463129014 2.69129992 0.630741 - vertex 0.776166975 3.71228409 -1.24183702 - vertex 0.804427981 3.67682505 -1.28455305 - endloop - endfacet - - facet normal 0.754780054 0.51879406 0.401447028 - outer loop - vertex 0.463129014 2.69129992 0.630741 - vertex 0.804427981 3.67682505 -1.28455305 - vertex 0.522632003 2.61842799 0.61303997 - endloop - endfacet - - facet normal 0.880504668 0.167588919 0.443424821 - outer loop - vertex 0.776166975 3.71228409 -1.24183702 - vertex 0.890290976 3.98449087 -1.57133007 - vertex 0.804427981 3.67682505 -1.28455305 - endloop - endfacet - - facet normal 0.785179079 0.290597022 0.546852052 - outer loop - vertex 0.890290976 3.98449087 -1.57133007 - vertex 0.904604971 3.97317505 -1.58586907 - vertex 0.804427981 3.67682505 -1.28455305 - endloop - endfacet - - facet normal 0.272477061 0.292857081 0.916510224 - outer loop - vertex -0.307767004 0.456461996 -0.107084997 - vertex -0.223100007 0.341991007 -0.095679 - vertex -0.376302004 0.324781001 -0.0446330011 - endloop - endfacet - - facet normal -0.247493982 -0.867506921 0.431483984 - outer loop - vertex 0.904604971 3.97317505 -1.58586907 - vertex 0.911266029 3.98531508 -1.55764103 - vertex 0.923933983 3.97100115 -1.57915306 - endloop - endfacet - - facet normal -0.530284762 -0.313739836 0.787632704 - outer loop - vertex -0.307767004 0.456461996 -0.107084997 - vertex -0.00500100013 1.04112697 0.329647988 - vertex -0.223100007 0.341991007 -0.095679 - endloop - endfacet - - facet normal -0.346002042 -0.406495035 0.845602989 - outer loop - vertex -0.00500100013 1.04112697 0.329647988 - vertex 0.0810849965 1.00276399 0.346430987 - vertex -0.223100007 0.341991007 -0.095679 - endloop - endfacet - - facet normal -0.409732103 -0.753765106 0.513768137 - outer loop - vertex -0.00500100013 1.04112697 0.329647988 - vertex 0.132046998 1.54398 1.17669499 - vertex 0.185377002 1.52670002 1.19387603 - endloop - endfacet - - facet normal -0.430921048 -0.742863119 0.512310088 - outer loop - vertex -0.00500100013 1.04112697 0.329647988 - vertex 0.185377002 1.52670002 1.19387603 - vertex 0.0810849965 1.00276399 0.346430987 - endloop - endfacet - - facet normal -0.136054054 0.455070168 0.880000293 - outer loop - vertex 0.132046998 1.54398 1.17669499 - vertex 0.140081003 1.61137402 1.14308596 - vertex 0.185377002 1.52670002 1.19387603 - endloop - endfacet - - facet normal -0.142184019 0.452121019 0.8805511 - outer loop - vertex 0.140081003 1.61137402 1.14308596 - vertex 0.232925996 1.60044098 1.16369104 - vertex 0.185377002 1.52670002 1.19387603 - endloop - endfacet - - facet normal -0.0768329725 0.487080812 0.869970679 - outer loop - vertex 0.140081003 1.61137402 1.14308596 - vertex 0.522632003 2.61842799 0.61303997 - vertex 0.617762983 2.61522293 0.623236001 - endloop - endfacet - - facet normal -0.130130991 0.504069924 0.853802919 - outer loop - vertex 0.140081003 1.61137402 1.14308596 - vertex 0.617762983 2.61522293 0.623236001 - vertex 0.232925996 1.60044098 1.16369104 - endloop - endfacet - - facet normal -0.0224629845 0.874534488 0.484442741 - outer loop - vertex 0.522632003 2.61842799 0.61303997 - vertex 0.804427981 3.67682505 -1.28455305 - vertex 0.617762983 2.61522293 0.623236001 - endloop - endfacet - - facet normal -0.19305791 0.865288615 0.462605834 - outer loop - vertex 0.804427981 3.67682505 -1.28455305 - vertex 0.846280992 3.67627597 -1.26605999 - vertex 0.617762983 2.61522293 0.623236001 - endloop - endfacet - - facet normal -0.149908945 0.729361773 0.667501807 - outer loop - vertex 0.804427981 3.67682505 -1.28455305 - vertex 0.904604971 3.97317505 -1.58586907 - vertex 0.923933983 3.97100115 -1.57915306 - endloop - endfacet - - facet normal -0.266439945 0.733926833 0.62478888 - outer loop - vertex 0.804427981 3.67682505 -1.28455305 - vertex 0.923933983 3.97100115 -1.57915306 - vertex 0.846280992 3.67627597 -1.26605999 - endloop - endfacet - - facet normal 0.272476882 0.292855889 0.916510582 - outer loop - vertex -0.223100007 0.341991007 -0.095679 - vertex -0.253796995 0.214561 -0.0458349995 - vertex -0.376302004 0.324781001 -0.0446330011 - endloop - endfacet - - facet normal -0.247450024 -0.8675071 0.431509018 - outer loop - vertex 0.923933983 3.97100115 -1.57915306 - vertex 0.911266029 3.98531508 -1.55764103 - vertex 0.933724999 3.97960711 -1.55623698 - endloop - endfacet - - facet normal -0.879711032 0.0929580033 0.466333985 - outer loop - vertex -0.223100007 0.341991007 -0.095679 - vertex 0.0810849965 1.00276399 0.346430987 - vertex 0.103583999 0.929287016 0.403519988 - endloop - endfacet - - facet normal -0.920438349 0.313202113 0.233875081 - outer loop - vertex -0.223100007 0.341991007 -0.095679 - vertex 0.103583999 0.929287016 0.403519988 - vertex -0.253796995 0.214561 -0.0458349995 - endloop - endfacet - - facet normal -0.969112396 -0.137853056 0.204494074 - outer loop - vertex 0.0810849965 1.00276399 0.346430987 - vertex 0.185377002 1.52670002 1.19387603 - vertex 0.103583999 0.929287016 0.403519988 - endloop - endfacet - - facet normal -0.958291411 -0.17137289 0.22871086 - outer loop - vertex 0.185377002 1.52670002 1.19387603 - vertex 0.204065993 1.50625706 1.256863 - vertex 0.103583999 0.929287016 0.403519988 - endloop - endfacet - - facet normal -0.774107695 0.591492772 0.225595891 - outer loop - vertex 0.185377002 1.52670002 1.19387603 - vertex 0.232925996 1.60044098 1.16369104 - vertex 0.282788992 1.63802505 1.23625302 - endloop - endfacet - - facet normal -0.76090014 0.516107082 0.393274039 - outer loop - vertex 0.185377002 1.52670002 1.19387603 - vertex 0.282788992 1.63802505 1.23625302 - vertex 0.204065993 1.50625706 1.256863 - endloop - endfacet - - facet normal -0.8194713 0.47864911 0.315217078 - outer loop - vertex 0.232925996 1.60044098 1.16369104 - vertex 0.617762983 2.61522293 0.623236001 - vertex 0.282788992 1.63802505 1.23625302 - endloop - endfacet - - facet normal -0.778669 0.502564967 0.375636965 - outer loop - vertex 0.617762983 2.61522293 0.623236001 - vertex 0.676886976 2.68409705 0.653648019 - vertex 0.282788992 1.63802505 1.23625302 - endloop - endfacet - - facet normal -0.907485962 0.403503984 0.11684899 - outer loop - vertex 0.617762983 2.61522293 0.623236001 - vertex 0.846280992 3.67627597 -1.26605999 - vertex 0.870212972 3.71105099 -1.20028496 - endloop - endfacet - - facet normal -0.786379635 0.571479738 0.234558895 - outer loop - vertex 0.617762983 2.61522293 0.623236001 - vertex 0.870212972 3.71105099 -1.20028496 - vertex 0.676886976 2.68409705 0.653648019 - endloop - endfacet - - facet normal -0.915194154 0.380924076 0.131592035 - outer loop - vertex 0.846280992 3.67627597 -1.26605999 - vertex 0.923933983 3.97100115 -1.57915306 - vertex 0.870212972 3.71105099 -1.20028496 - endloop - endfacet - - facet normal -0.864411891 0.463298917 0.195310965 - outer loop - vertex 0.923933983 3.97100115 -1.57915306 - vertex 0.933724999 3.97960711 -1.55623698 - vertex 0.870212972 3.71105099 -1.20028496 - endloop - endfacet - - facet normal 0.272473931 -0.292851925 0.916512847 - outer loop - vertex -0.376302004 -0.324781001 -0.0446330011 - vertex -0.376740992 -0.170128003 0.00491400016 - vertex -0.253796995 -0.214561 -0.0458349995 - endloop - endfacet - - facet normal -0.247445986 0.867469966 0.431585968 - outer loop - vertex 0.926603973 -3.99251413 -1.53437805 - vertex 0.911266029 -3.98531508 -1.55764103 - vertex 0.933724999 -3.97960711 -1.55623698 - endloop - endfacet - - facet normal -0.777862728 -0.563657761 -0.277883887 - outer loop - vertex 0.0455540009 -0.876025975 0.457924992 - vertex 0.103583999 -0.929287016 0.403519988 - vertex -0.253796995 -0.214561 -0.0458349995 - endloop - endfacet - - facet normal -0.477919996 -0.658785999 -0.581027985 - outer loop - vertex -0.376740992 -0.170128003 0.00491400016 - vertex 0.0455540009 -0.876025975 0.457924992 - vertex -0.253796995 -0.214561 -0.0458349995 - endloop - endfacet - - facet normal -0.779423177 -0.557750106 -0.285332054 - outer loop - vertex 0.0455540009 -0.876025975 0.457924992 - vertex 0.204065993 -1.50625706 1.256863 - vertex 0.103583999 -0.929287016 0.403519988 - endloop - endfacet - - facet normal -0.76534009 -0.570420086 -0.298120022 - outer loop - vertex 0.0455540009 -0.876025975 0.457924992 - vertex 0.174039006 -1.49804103 1.31822896 - vertex 0.204065993 -1.50625706 1.256863 - endloop - endfacet - - facet normal -0.725148976 -0.339533001 -0.599062979 - outer loop - vertex 0.252124012 -1.695822 1.30613101 - vertex 0.282788992 -1.63802397 1.23625302 - vertex 0.204065993 -1.50625706 1.256863 - endloop - endfacet - - facet normal -0.867439389 -0.319119155 -0.381722182 - outer loop - vertex 0.174039006 -1.49804103 1.31822896 - vertex 0.252124012 -1.695822 1.30613101 - vertex 0.204065993 -1.50625706 1.256863 - endloop - endfacet - - facet normal -0.88659811 -0.0803210139 -0.45551309 - outer loop - vertex 0.252124012 -1.695822 1.30613101 - vertex 0.676886976 -2.68409705 0.653648019 - vertex 0.282788992 -1.63802397 1.23625302 - endloop - endfacet - - facet normal -0.822990119 0.0210630018 -0.56766504 - outer loop - vertex 0.252124012 -1.695822 1.30613101 - vertex 0.655480981 -2.77318811 0.681376994 - vertex 0.676886976 -2.68409705 0.653648019 - endloop - endfacet - - facet normal -0.987821043 0.06729801 -0.140287012 - outer loop - vertex 0.858199 -3.75496197 -1.13675594 - vertex 0.870212972 -3.71105099 -1.20028496 - vertex 0.676886976 -2.68409705 0.653648019 - endloop - endfacet - - facet normal -0.965101719 0.169848949 -0.199323922 - outer loop - vertex 0.655480981 -2.77318811 0.681376994 - vertex 0.858199 -3.75496197 -1.13675594 - vertex 0.676886976 -2.68409705 0.653648019 - endloop - endfacet - - facet normal -0.983463764 0.00726499874 -0.180958956 - outer loop - vertex 0.858199 -3.75496197 -1.13675594 - vertex 0.933724999 -3.97960711 -1.55623698 - vertex 0.870212972 -3.71105099 -1.20028496 - endloop - endfacet - - facet normal -0.962732613 0.124580957 -0.240052924 - outer loop - vertex 0.858199 -3.75496197 -1.13675594 - vertex 0.926603973 -3.99251413 -1.53437805 - vertex 0.933724999 -3.97960711 -1.55623698 - endloop - endfacet - - facet normal 0.272466928 -0.292851955 0.916514874 - outer loop - vertex -0.376302004 -0.324781001 -0.0446330011 - vertex -0.499356002 -0.242149994 0.018352 - vertex -0.376740992 -0.170128003 0.00491400016 - endloop - endfacet - - facet normal -0.247477025 0.867453039 0.431602061 - outer loop - vertex 0.907932997 -4 -1.530038 - vertex 0.911266029 -3.98531508 -1.55764103 - vertex 0.926603973 -3.99251413 -1.53437805 - endloop - endfacet - - facet normal 0.172648042 -0.456757128 -0.872677267 - outer loop - vertex -0.499356002 -0.242149994 0.018352 - vertex 0.0455540009 -0.876025975 0.457924992 - vertex -0.376740992 -0.170128003 0.00491400016 - endloop - endfacet - - facet normal -0.0465160124 -0.595931113 -0.801687181 - outer loop - vertex -0.499356002 -0.242149994 0.018352 - vertex -0.0493079983 -0.883087993 0.468677998 - vertex 0.0455540009 -0.876025975 0.457924992 - endloop - endfacet - - facet normal 0.00578899914 -0.809948862 -0.586471856 - outer loop - vertex 0.117908001 -1.50824094 1.331761 - vertex 0.174039006 -1.49804103 1.31822896 - vertex 0.0455540009 -0.876025975 0.457924992 - endloop - endfacet - - facet normal -0.00607900089 -0.810414076 -0.585826099 - outer loop - vertex -0.0493079983 -0.883087993 0.468677998 - vertex 0.117908001 -1.50824094 1.331761 - vertex 0.0455540009 -0.876025975 0.457924992 - endloop - endfacet - - facet normal -0.228951037 -0.0308770034 -0.972948134 - outer loop - vertex 0.117908001 -1.50824094 1.331761 - vertex 0.252124012 -1.695822 1.30613101 - vertex 0.174039006 -1.49804103 1.31822896 - endloop - endfacet - - facet normal -0.16855301 0.0140670007 -0.985592186 - outer loop - vertex 0.117908001 -1.50824094 1.331761 - vertex 0.164021999 -1.73031497 1.32070494 - vertex 0.252124012 -1.695822 1.30613101 - endloop - endfacet - - facet normal -0.246820047 0.415317029 -0.875552118 - outer loop - vertex 0.569666028 -2.81540704 0.685541987 - vertex 0.655480981 -2.77318811 0.681376994 - vertex 0.252124012 -1.695822 1.30613101 - endloop - endfacet - - facet normal -0.298811018 0.396427006 -0.86807698 - outer loop - vertex 0.164021999 -1.73031497 1.32070494 - vertex 0.569666028 -2.81540704 0.685541987 - vertex 0.252124012 -1.695822 1.30613101 - endloop - endfacet - - facet normal -0.40818882 0.783465683 -0.468575835 - outer loop - vertex 0.569666028 -2.81540704 0.685541987 - vertex 0.858199 -3.75496197 -1.13675594 - vertex 0.655480981 -2.77318811 0.681376994 - endloop - endfacet - - facet normal -0.526139736 0.719045639 -0.454037786 - outer loop - vertex 0.569666028 -2.81540704 0.685541987 - vertex 0.819289029 -3.77494597 -1.12331498 - vertex 0.858199 -3.75496197 -1.13675594 - endloop - endfacet - - facet normal -0.419348896 0.745756686 -0.517680824 - outer loop - vertex 0.907932997 -4 -1.530038 - vertex 0.926603973 -3.99251413 -1.53437805 - vertex 0.858199 -3.75496197 -1.13675594 - endloop - endfacet - - facet normal -0.526108801 0.690327764 -0.496645838 - outer loop - vertex 0.819289029 -3.77494597 -1.12331498 - vertex 0.907932997 -4 -1.530038 - vertex 0.858199 -3.75496197 -1.13675594 - endloop - endfacet - - facet normal 0.272470057 -0.292848051 0.916515231 - outer loop - vertex -0.376302004 -0.324781001 -0.0446330011 - vertex -0.529305995 -0.376397014 -0.0156389996 - vertex -0.499356002 -0.242149994 0.018352 - endloop - endfacet - - facet normal -0.247462898 0.867455661 0.431604832 - outer loop - vertex 0.891771019 -3.99642801 -1.54648209 - vertex 0.911266029 -3.98531508 -1.55764103 - vertex 0.907932997 -4 -1.530038 - endloop - endfacet - - facet normal 0.628429115 -0.100671016 -0.771325052 - outer loop - vertex -0.109567001 -0.945155025 0.427682996 - vertex -0.0493079983 -0.883087993 0.468677998 - vertex -0.499356002 -0.242149994 0.018352 - endloop - endfacet - - facet normal 0.73198396 0.0091859987 -0.68125993 - outer loop - vertex -0.529305995 -0.376397014 -0.0156389996 - vertex -0.109567001 -0.945155025 0.427682996 - vertex -0.499356002 -0.242149994 0.018352 - endloop - endfacet - - facet normal 0.768508792 -0.438165873 -0.46626687 - outer loop - vertex -0.109567001 -0.945155025 0.427682996 - vertex 0.117908001 -1.50824094 1.331761 - vertex -0.0493079983 -0.883087993 0.468677998 - endloop - endfacet - - facet normal 0.759350061 -0.449147046 -0.470802039 - outer loop - vertex -0.109567001 -0.945155025 0.427682996 - vertex 0.0779410005 -1.52917695 1.28727102 - vertex 0.117908001 -1.50824094 1.331761 - endloop - endfacet - - facet normal 0.560264945 0.156830981 -0.813330948 - outer loop - vertex 0.0848250017 -1.71552598 1.26900196 - vertex 0.164021999 -1.73031497 1.32070494 - vertex 0.117908001 -1.50824094 1.331761 - endloop - endfacet - - facet normal 0.71818012 0.0941240117 -0.689462125 - outer loop - vertex 0.0779410005 -1.52917695 1.28727102 - vertex 0.0848250017 -1.71552598 1.26900196 - vertex 0.117908001 -1.50824094 1.331761 - endloop - endfacet - - facet normal 0.521720767 0.567762733 -0.636751771 - outer loop - vertex 0.0848250017 -1.71552598 1.26900196 - vertex 0.569666028 -2.81540704 0.685541987 - vertex 0.164021999 -1.73031497 1.32070494 - endloop - endfacet - - facet normal 0.426156938 0.563345969 -0.707835913 - outer loop - vertex 0.0848250017 -1.71552598 1.26900196 - vertex 0.484059989 -2.77896309 0.663007021 - vertex 0.569666028 -2.81540704 0.685541987 - endloop - endfacet - - facet normal 0.688727021 0.67552197 -0.263296992 - outer loop - vertex 0.782781005 -3.75595093 -1.17007899 - vertex 0.819289029 -3.77494597 -1.12331498 - vertex 0.569666028 -2.81540704 0.685541987 - endloop - endfacet - - facet normal 0.444303989 0.81863606 -0.363907993 - outer loop - vertex 0.484059989 -2.77896309 0.663007021 - vertex 0.782781005 -3.75595093 -1.17007899 - vertex 0.569666028 -2.81540704 0.685541987 - endloop - endfacet - - facet normal 0.672470868 0.699886858 -0.24070996 - outer loop - vertex 0.782781005 -3.75595093 -1.17007899 - vertex 0.907932997 -4 -1.530038 - vertex 0.819289029 -3.77494597 -1.12331498 - endloop - endfacet - - facet normal 0.523808122 0.778528154 -0.345715046 - outer loop - vertex 0.782781005 -3.75595093 -1.17007899 - vertex 0.891771019 -3.99642801 -1.54648209 - vertex 0.907932997 -4 -1.530038 - endloop - endfacet - - facet normal 0.272471815 -0.292857826 0.916511476 - outer loop - vertex -0.376302004 -0.324781001 -0.0446330011 - vertex -0.44404301 -0.471772999 -0.0714629963 - vertex -0.529305995 -0.376397014 -0.0156389996 - endloop - endfacet - - facet normal -0.247543931 0.867487788 0.431493849 - outer loop - vertex 0.890290976 -3.98449087 -1.57133007 - vertex 0.911266029 -3.98531508 -1.55764103 - vertex 0.891771019 -3.99642801 -1.54648209 - endloop - endfacet - - facet normal 0.76761812 0.634875059 0.0877270103 - outer loop - vertex -0.44404301 -0.471772999 -0.0714629963 - vertex -0.109567001 -0.945155025 0.427682996 - vertex -0.529305995 -0.376397014 -0.0156389996 - endloop - endfacet - - facet normal 0.886159658 0.420221835 -0.19528091 - outer loop - vertex -0.44404301 -0.471772999 -0.0714629963 - vertex -0.0898490027 -1.01548898 0.365808994 - vertex -0.109567001 -0.945155025 0.427682996 - endloop - endfacet - - facet normal 0.946716785 0.321837902 0.0121489968 - outer loop - vertex 0.0842330009 -1.54508102 1.21826005 - vertex 0.0779410005 -1.52917695 1.28727102 - vertex -0.109567001 -0.945155025 0.427682996 - endloop - endfacet - - facet normal 0.958333194 0.285045058 -0.0186190046 - outer loop - vertex -0.0898490027 -1.01548898 0.365808994 - vertex 0.0842330009 -1.54508102 1.21826005 - vertex -0.109567001 -0.945155025 0.427682996 - endloop - endfacet - - facet normal 0.996036589 0.028538987 0.0842419714 - outer loop - vertex 0.0842330009 -1.54508102 1.21826005 - vertex 0.0848250017 -1.71552598 1.26900196 - vertex 0.0779410005 -1.52917695 1.28727102 - endloop - endfacet - - facet normal 0.985584736 -0.0451189876 -0.163054958 - outer loop - vertex 0.0842330009 -1.54508102 1.21826005 - vertex 0.0741709992 -1.66259193 1.18995297 - vertex 0.0848250017 -1.71552598 1.26900196 - endloop - endfacet - - facet normal 0.950562179 0.277941048 0.138492033 - outer loop - vertex 0.463129014 -2.69129992 0.630741 - vertex 0.484059989 -2.77896309 0.663007021 - vertex 0.0848250017 -1.71552598 1.26900196 - endloop - endfacet - - facet normal 0.946162283 0.313099086 0.0821340233 - outer loop - vertex 0.0741709992 -1.66259193 1.18995297 - vertex 0.463129014 -2.69129992 0.630741 - vertex 0.0848250017 -1.71552598 1.26900196 - endloop - endfacet - - facet normal 0.96979171 0.242210925 0.0289449915 - outer loop - vertex 0.463129014 -2.69129992 0.630741 - vertex 0.782781005 -3.75595093 -1.17007899 - vertex 0.484059989 -2.77896309 0.663007021 - endloop - endfacet - - facet normal 0.974733412 0.219097078 0.0434880182 - outer loop - vertex 0.463129014 -2.69129992 0.630741 - vertex 0.776166975 -3.71228409 -1.24183702 - vertex 0.782781005 -3.75595093 -1.17007899 - endloop - endfacet - - facet normal 0.951111257 0.296673089 0.0858620256 - outer loop - vertex 0.890290976 -3.98449087 -1.57133007 - vertex 0.891771019 -3.99642801 -1.54648209 - vertex 0.782781005 -3.75595093 -1.17007899 - endloop - endfacet - - facet normal 0.95249474 0.291143954 0.0893819779 - outer loop - vertex 0.776166975 -3.71228409 -1.24183702 - vertex 0.890290976 -3.98449087 -1.57133007 - vertex 0.782781005 -3.75595093 -1.17007899 - endloop - endfacet - - facet normal 0.272473961 -0.292857975 0.91651094 - outer loop - vertex -0.376302004 -0.324781001 -0.0446330011 - vertex -0.307767004 -0.456461996 -0.107084997 - vertex -0.44404301 -0.471772999 -0.0714629963 - endloop - endfacet - - facet normal -0.247540042 0.867492199 0.431487083 - outer loop - vertex 0.904604971 -3.97317505 -1.58586907 - vertex 0.911266029 -3.98531508 -1.55764103 - vertex 0.890290976 -3.98449087 -1.57133007 - endloop - endfacet - - facet normal 0.444816053 0.719116032 0.533864081 - outer loop - vertex -0.00500100013 -1.04112697 0.329647988 - vertex -0.0898490027 -1.01548898 0.365808994 - vertex -0.44404301 -0.471772999 -0.0714629963 - endloop - endfacet - - facet normal 0.127748966 0.63515991 0.76174283 - outer loop - vertex -0.307767004 -0.456461996 -0.107084997 - vertex -0.00500100013 -1.04112697 0.329647988 - vertex -0.44404301 -0.471772999 -0.0714629963 - endloop - endfacet - - facet normal 0.420685023 0.806615114 0.415206045 - outer loop - vertex -0.00500100013 -1.04112697 0.329647988 - vertex 0.0842330009 -1.54508102 1.21826005 - vertex -0.0898490027 -1.01548898 0.365808994 - endloop - endfacet - - facet normal 0.357582062 0.827292144 0.433270067 - outer loop - vertex -0.00500100013 -1.04112697 0.329647988 - vertex 0.132046998 -1.54397893 1.17669499 - vertex 0.0842330009 -1.54508102 1.21826005 - endloop - endfacet - - facet normal 0.675312042 -0.226875037 0.701770067 - outer loop - vertex 0.140081003 -1.61137402 1.14308596 - vertex 0.0741709992 -1.66259193 1.18995297 - vertex 0.0842330009 -1.54508102 1.21826005 - endloop - endfacet - - facet normal 0.632813096 -0.283820063 0.720412195 - outer loop - vertex 0.132046998 -1.54397893 1.17669499 - vertex 0.140081003 -1.61137402 1.14308596 - vertex 0.0842330009 -1.54508102 1.21826005 - endloop - endfacet - - facet normal 0.650720775 -0.157726958 0.742754757 - outer loop - vertex 0.140081003 -1.61137402 1.14308596 - vertex 0.463129014 -2.69129992 0.630741 - vertex 0.0741709992 -1.66259193 1.18995297 - endloop - endfacet - - facet normal 0.52620393 -0.230870992 0.818417966 - outer loop - vertex 0.140081003 -1.61137402 1.14308596 - vertex 0.522632003 -2.61842704 0.61303997 - vertex 0.463129014 -2.69129992 0.630741 - endloop - endfacet - - facet normal 0.890468955 -0.320121974 0.323398978 - outer loop - vertex 0.804427981 -3.67682505 -1.28455305 - vertex 0.776166975 -3.71228409 -1.24183702 - vertex 0.463129014 -2.69129992 0.630741 - endloop - endfacet - - facet normal 0.754779816 -0.518793881 0.401447922 - outer loop - vertex 0.522632003 -2.61842704 0.61303997 - vertex 0.804427981 -3.67682505 -1.28455305 - vertex 0.463129014 -2.69129992 0.630741 - endloop - endfacet - - facet normal 0.880504668 -0.167588919 0.443424821 - outer loop - vertex 0.804427981 -3.67682505 -1.28455305 - vertex 0.890290976 -3.98449087 -1.57133007 - vertex 0.776166975 -3.71228409 -1.24183702 - endloop - endfacet - - facet normal 0.785177827 -0.290598929 0.546852827 - outer loop - vertex 0.804427981 -3.67682505 -1.28455305 - vertex 0.904604971 -3.97317505 -1.58586907 - vertex 0.890290976 -3.98449087 -1.57133007 - endloop - endfacet - - facet normal 0.272477061 -0.292857081 0.916510224 - outer loop - vertex -0.376302004 -0.324781001 -0.0446330011 - vertex -0.223100007 -0.341991007 -0.095679 - vertex -0.307767004 -0.456461996 -0.107084997 - endloop - endfacet - - facet normal -0.247495025 0.8675071 0.43148303 - outer loop - vertex 0.923933983 -3.97100115 -1.57915306 - vertex 0.911266029 -3.98531508 -1.55764103 - vertex 0.904604971 -3.97317505 -1.58586907 - endloop - endfacet - - facet normal -0.530284762 0.313739836 0.787632704 - outer loop - vertex -0.223100007 -0.341991007 -0.095679 - vertex -0.00500100013 -1.04112697 0.329647988 - vertex -0.307767004 -0.456461996 -0.107084997 - endloop - endfacet - - facet normal -0.346002042 0.406495035 0.845602989 - outer loop - vertex -0.223100007 -0.341991007 -0.095679 - vertex 0.0810849965 -1.00276399 0.346430987 - vertex -0.00500100013 -1.04112697 0.329647988 - endloop - endfacet - - facet normal -0.409730107 0.753766179 0.513768196 - outer loop - vertex 0.185377002 -1.52670002 1.19387603 - vertex 0.132046998 -1.54397893 1.17669499 - vertex -0.00500100013 -1.04112697 0.329647988 - endloop - endfacet - - facet normal -0.430921048 0.742863119 0.512310088 - outer loop - vertex 0.0810849965 -1.00276399 0.346430987 - vertex 0.185377002 -1.52670002 1.19387603 - vertex -0.00500100013 -1.04112697 0.329647988 - endloop - endfacet - - facet normal -0.136055022 -0.455070078 0.880000114 - outer loop - vertex 0.185377002 -1.52670002 1.19387603 - vertex 0.140081003 -1.61137402 1.14308596 - vertex 0.132046998 -1.54397893 1.17669499 - endloop - endfacet - - facet normal -0.142182976 -0.452121913 0.880550802 - outer loop - vertex 0.185377002 -1.52670002 1.19387603 - vertex 0.232925996 -1.60044098 1.16369104 - vertex 0.140081003 -1.61137402 1.14308596 - endloop - endfacet - - facet normal -0.0768340006 -0.487081975 0.869969964 - outer loop - vertex 0.617762983 -2.61522293 0.623236001 - vertex 0.522632003 -2.61842704 0.61303997 - vertex 0.140081003 -1.61137402 1.14308596 - endloop - endfacet - - facet normal -0.130130008 -0.504070044 0.853803098 - outer loop - vertex 0.232925996 -1.60044098 1.16369104 - vertex 0.617762983 -2.61522293 0.623236001 - vertex 0.140081003 -1.61137402 1.14308596 - endloop - endfacet - - facet normal -0.0224649999 -0.874534965 0.484441966 - outer loop - vertex 0.617762983 -2.61522293 0.623236001 - vertex 0.804427981 -3.67682505 -1.28455305 - vertex 0.522632003 -2.61842704 0.61303997 - endloop - endfacet - - facet normal -0.19305791 -0.865288615 0.462605834 - outer loop - vertex 0.617762983 -2.61522293 0.623236001 - vertex 0.846280992 -3.67627597 -1.26605999 - vertex 0.804427981 -3.67682505 -1.28455305 - endloop - endfacet - - facet normal -0.149909943 -0.729361713 0.667501748 - outer loop - vertex 0.923933983 -3.97100115 -1.57915306 - vertex 0.904604971 -3.97317505 -1.58586907 - vertex 0.804427981 -3.67682505 -1.28455305 - endloop - endfacet - - facet normal -0.266439945 -0.733926833 0.62478888 - outer loop - vertex 0.846280992 -3.67627597 -1.26605999 - vertex 0.923933983 -3.97100115 -1.57915306 - vertex 0.804427981 -3.67682505 -1.28455305 - endloop - endfacet - - facet normal 0.272477865 -0.292855829 0.916510463 - outer loop - vertex -0.376302004 -0.324781001 -0.0446330011 - vertex -0.253796995 -0.214561 -0.0458349995 - vertex -0.223100007 -0.341991007 -0.095679 - endloop - endfacet - - facet normal -0.247450024 0.8675071 0.431509018 - outer loop - vertex 0.933724999 -3.97960711 -1.55623698 - vertex 0.911266029 -3.98531508 -1.55764103 - vertex 0.923933983 -3.97100115 -1.57915306 - endloop - endfacet - - facet normal -0.879710913 -0.0929590017 0.466333956 - outer loop - vertex 0.103583999 -0.929287016 0.403519988 - vertex 0.0810849965 -1.00276399 0.346430987 - vertex -0.223100007 -0.341991007 -0.095679 - endloop - endfacet - - facet normal -0.920438349 -0.313202113 0.233875081 - outer loop - vertex -0.253796995 -0.214561 -0.0458349995 - vertex 0.103583999 -0.929287016 0.403519988 - vertex -0.223100007 -0.341991007 -0.095679 - endloop - endfacet - - facet normal -0.969112217 0.137854025 0.204494044 - outer loop - vertex 0.103583999 -0.929287016 0.403519988 - vertex 0.185377002 -1.52670002 1.19387603 - vertex 0.0810849965 -1.00276399 0.346430987 - endloop - endfacet - - facet normal -0.958291709 0.171372935 0.228709921 - outer loop - vertex 0.103583999 -0.929287016 0.403519988 - vertex 0.204065993 -1.50625706 1.256863 - vertex 0.185377002 -1.52670002 1.19387603 - endloop - endfacet - - facet normal -0.774107099 -0.591494083 0.225595027 - outer loop - vertex 0.282788992 -1.63802397 1.23625302 - vertex 0.232925996 -1.60044098 1.16369104 - vertex 0.185377002 -1.52670002 1.19387603 - endloop - endfacet - - facet normal -0.760899842 -0.516106904 0.393274903 - outer loop - vertex 0.204065993 -1.50625706 1.256863 - vertex 0.282788992 -1.63802397 1.23625302 - vertex 0.185377002 -1.52670002 1.19387603 - endloop - endfacet - - facet normal -0.819471061 -0.47864899 0.315218031 - outer loop - vertex 0.282788992 -1.63802397 1.23625302 - vertex 0.617762983 -2.61522293 0.623236001 - vertex 0.232925996 -1.60044098 1.16369104 - endloop - endfacet - - facet normal -0.778668165 -0.502565145 0.375638098 - outer loop - vertex 0.282788992 -1.63802397 1.23625302 - vertex 0.676886976 -2.68409705 0.653648019 - vertex 0.617762983 -2.61522293 0.623236001 - endloop - endfacet - - facet normal -0.907485962 -0.403503984 0.11684899 - outer loop - vertex 0.870212972 -3.71105099 -1.20028496 - vertex 0.846280992 -3.67627597 -1.26605999 - vertex 0.617762983 -2.61522293 0.623236001 - endloop - endfacet - - facet normal -0.78637892 -0.57148093 0.23455897 - outer loop - vertex 0.676886976 -2.68409705 0.653648019 - vertex 0.870212972 -3.71105099 -1.20028496 - vertex 0.617762983 -2.61522293 0.623236001 - endloop - endfacet - - facet normal -0.915194154 -0.380924076 0.131592035 - outer loop - vertex 0.870212972 -3.71105099 -1.20028496 - vertex 0.923933983 -3.97100115 -1.57915306 - vertex 0.846280992 -3.67627597 -1.26605999 - endloop - endfacet - - facet normal -0.864411891 -0.463298917 0.195310965 - outer loop - vertex 0.870212972 -3.71105099 -1.20028496 - vertex 0.933724999 -3.97960711 -1.55623698 - vertex 0.923933983 -3.97100115 -1.57915306 - endloop - endfacet - - facet normal 0.287100911 0.22565192 0.930942714 - outer loop - vertex 0.254595995 0.172016993 -0.203339994 - vertex 0.134731993 0.118957996 -0.153512999 - vertex 0.128368005 0.27710101 -0.189882994 - endloop - endfacet - - facet normal -0.184374034 -0.887741089 0.421808064 - outer loop - vertex 1.54126191 3.22838593 -1.48060107 - vertex 1.51856101 3.23323202 -1.48032296 - vertex 1.53517103 3.24041605 -1.45794499 - endloop - endfacet - - facet normal -0.475138009 0.667518914 -0.573290944 - outer loop - vertex 0.254595995 0.172016993 -0.203339994 - vertex 0.599097013 0.871927977 0.326090991 - vertex 0.515124023 0.832623005 0.349920988 - endloop - endfacet - - facet normal -0.518250883 0.659948826 -0.543951869 - outer loop - vertex 0.254595995 0.172016993 -0.203339994 - vertex 0.515124023 0.832623005 0.349920988 - vertex 0.134731993 0.118957996 -0.153512999 - endloop - endfacet - - facet normal -0.472386926 0.830829799 -0.294231981 - outer loop - vertex 0.599097013 0.871927977 0.326090991 - vertex 0.79035902 1.28275597 1.17908895 - vertex 0.515124023 0.832623005 0.349920988 - endloop - endfacet - - facet normal -0.327001125 0.87180227 -0.364734113 - outer loop - vertex 0.79035902 1.28275597 1.17908895 - vertex 0.737774014 1.28159404 1.22345495 - vertex 0.515124023 0.832623005 0.349920988 - endloop - endfacet - - facet normal -0.350684077 0.480596095 -0.803771198 - outer loop - vertex 0.79035902 1.28275597 1.17908895 - vertex 0.895301998 1.39467299 1.20021999 - vertex 0.851906002 1.45822704 1.25715399 - endloop - endfacet - - facet normal -0.56752795 0.492581934 -0.659753859 - outer loop - vertex 0.79035902 1.28275597 1.17908895 - vertex 0.851906002 1.45822704 1.25715399 - vertex 0.737774014 1.28159404 1.22345495 - endloop - endfacet - - facet normal -0.840571404 -0.0964380503 -0.533047318 - outer loop - vertex 0.895301998 1.39467299 1.20021999 - vertex 1.33289194 2.38362789 0.331256986 - vertex 0.851906002 1.45822704 1.25715399 - endloop - endfacet - - facet normal -0.887653232 0.000607000198 -0.460512102 - outer loop - vertex 1.33289194 2.38362789 0.331256986 - vertex 1.32048297 2.47545505 0.355296999 - vertex 0.851906002 1.45822704 1.25715399 - endloop - endfacet - - facet normal -0.991470754 -0.0184189957 -0.129020974 - outer loop - vertex 1.33289194 2.38362789 0.331256986 - vertex 1.51245093 2.94816399 -1.12917101 - vertex 1.50315905 2.98986292 -1.06372297 - endloop - endfacet - - facet normal -0.982979894 -0.0910649896 -0.159554973 - outer loop - vertex 1.33289194 2.38362789 0.331256986 - vertex 1.50315905 2.98986292 -1.06372297 - vertex 1.32048297 2.47545505 0.355296999 - endloop - endfacet - - facet normal -0.992559552 -0.0415029824 -0.114467941 - outer loop - vertex 1.51245093 2.94816399 -1.12917101 - vertex 1.54126191 3.22838593 -1.48060107 - vertex 1.50315905 2.98986292 -1.06372297 - endloop - endfacet - - facet normal -0.971499264 -0.156291023 -0.178220034 - outer loop - vertex 1.54126191 3.22838593 -1.48060107 - vertex 1.53517103 3.24041605 -1.45794499 - vertex 1.50315905 2.98986292 -1.06372297 - endloop - endfacet - - facet normal 0.28709814 0.225652128 0.930943549 - outer loop - vertex 0.134731993 0.118957996 -0.153512999 - vertex 0.0100760004 0.184983999 -0.131073996 - vertex 0.128368005 0.27710101 -0.189882994 - endloop - endfacet - - facet normal -0.184354961 -0.887750745 0.421795905 - outer loop - vertex 1.53517103 3.24041605 -1.45794499 - vertex 1.51856101 3.23323202 -1.48032296 - vertex 1.51657104 3.24703598 -1.45213997 - endloop - endfacet - - facet normal 0.127014041 0.525668204 -0.841154218 - outer loop - vertex 0.134731993 0.118957996 -0.153512999 - vertex 0.515124023 0.832623005 0.349920988 - vertex 0.0100760004 0.184983999 -0.131073996 - endloop - endfacet - - facet normal 0.362982959 0.356360972 -0.860958934 - outer loop - vertex 0.515124023 0.832623005 0.349920988 - vertex 0.427042991 0.859715998 0.324000001 - vertex 0.0100760004 0.184983999 -0.131073996 - endloop - endfacet - - facet normal 0.393000096 0.773265183 -0.497606099 - outer loop - vertex 0.515124023 0.832623005 0.349920988 - vertex 0.737774014 1.28159404 1.22345495 - vertex 0.68404901 1.30356801 1.21517205 - endloop - endfacet - - facet normal 0.385495007 0.776755929 -0.498039961 - outer loop - vertex 0.515124023 0.832623005 0.349920988 - vertex 0.68404901 1.30356801 1.21517205 - vertex 0.427042991 0.859715998 0.324000001 - endloop - endfacet - - facet normal 0.180199996 0.0707409903 -0.981082916 - outer loop - vertex 0.737774014 1.28159404 1.22345495 - vertex 0.851906002 1.45822704 1.25715399 - vertex 0.68404901 1.30356801 1.21517205 - endloop - endfacet - - facet normal 0.230535865 0.013906992 -0.972964406 - outer loop - vertex 0.851906002 1.45822704 1.25715399 - vertex 0.773847997 1.51072693 1.23941004 - vertex 0.68404901 1.30356801 1.21517205 - endloop - endfacet - - facet normal -0.213641912 -0.591137767 -0.777761698 - outer loop - vertex 0.851906002 1.45822704 1.25715399 - vertex 1.32048297 2.47545505 0.355296999 - vertex 1.25007606 2.53493595 0.329427987 - endloop - endfacet - - facet normal -0.219361112 -0.589029253 -0.777769387 - outer loop - vertex 0.851906002 1.45822704 1.25715399 - vertex 1.25007606 2.53493595 0.329427987 - vertex 0.773847997 1.51072693 1.23941004 - endloop - endfacet - - facet normal -0.526722312 -0.775172412 -0.348814219 - outer loop - vertex 1.32048297 2.47545505 0.355296999 - vertex 1.50315905 2.98986292 -1.06372297 - vertex 1.25007606 2.53493595 0.329427987 - endloop - endfacet - - facet normal -0.515794992 -0.782329023 -0.349166006 - outer loop - vertex 1.50315905 2.98986292 -1.06372297 - vertex 1.46452999 3.00791907 -1.04711497 - vertex 1.25007606 2.53493595 0.329427987 - endloop - endfacet - - facet normal -0.425265014 -0.747819066 -0.509819984 - outer loop - vertex 1.50315905 2.98986292 -1.06372297 - vertex 1.53517103 3.24041605 -1.45794499 - vertex 1.51657104 3.24703598 -1.45213997 - endloop - endfacet - - facet normal -0.532313049 -0.697276056 -0.48005107 - outer loop - vertex 1.50315905 2.98986292 -1.06372297 - vertex 1.51657104 3.24703598 -1.45213997 - vertex 1.46452999 3.00791907 -1.04711497 - endloop - endfacet - - facet normal 0.287100106 0.225650087 0.93094337 - outer loop - vertex 0.0100760004 0.184983999 -0.131073996 - vertex -0.0255030002 0.320378006 -0.152918994 - vertex 0.128368005 0.27710101 -0.189882994 - endloop - endfacet - - facet normal -0.184421048 -0.887741208 0.421787083 - outer loop - vertex 1.51657104 3.24703598 -1.45213997 - vertex 1.51856101 3.23323202 -1.48032296 - vertex 1.49947 3.24326396 -1.46755695 - endloop - endfacet - - facet normal 0.821849883 -0.134506986 -0.553597927 - outer loop - vertex 0.0100760004 0.184983999 -0.131073996 - vertex 0.427042991 0.859715998 0.324000001 - vertex 0.401181996 0.932807028 0.267848015 - endloop - endfacet - - facet normal 0.661470056 0.0531199984 -0.748088062 - outer loop - vertex 0.0100760004 0.184983999 -0.131073996 - vertex 0.401181996 0.932807028 0.267848015 - vertex -0.0255030002 0.320378006 -0.152918994 - endloop - endfacet - - facet normal 0.944037497 0.0903129503 -0.317232847 - outer loop - vertex 0.427042991 0.859715998 0.324000001 - vertex 0.68404901 1.30356801 1.21517205 - vertex 0.401181996 0.932807028 0.267848015 - endloop - endfacet - - facet normal 0.961416662 -0.0369919837 -0.272597879 - outer loop - vertex 0.68404901 1.30356801 1.21517205 - vertex 0.669640005 1.33213401 1.16047704 - vertex 0.401181996 0.932807028 0.267848015 - endloop - endfacet - - facet normal 0.79018414 -0.278662026 -0.545854032 - outer loop - vertex 0.68404901 1.30356801 1.21517205 - vertex 0.773847997 1.51072693 1.23941004 - vertex 0.719905972 1.51263702 1.16034794 - endloop - endfacet - - facet normal 0.896213293 -0.249837101 -0.366583139 - outer loop - vertex 0.68404901 1.30356801 1.21517205 - vertex 0.719905972 1.51263702 1.16034794 - vertex 0.669640005 1.33213401 1.16047704 - endloop - endfacet - - facet normal 0.608522832 -0.666114748 -0.431266844 - outer loop - vertex 0.773847997 1.51072693 1.23941004 - vertex 1.25007606 2.53493595 0.329427987 - vertex 0.719905972 1.51263702 1.16034794 - endloop - endfacet - - facet normal 0.533110023 -0.68267101 -0.499754041 - outer loop - vertex 1.25007606 2.53493595 0.329427987 - vertex 1.17469096 2.5172801 0.273131013 - vertex 0.719905972 1.51263702 1.16034794 - endloop - endfacet - - facet normal 0.592279136 -0.785880089 -0.177758023 - outer loop - vertex 1.25007606 2.53493595 0.329427987 - vertex 1.46452999 3.00791907 -1.04711497 - vertex 1.42565393 2.98873901 -1.09184897 - endloop - endfacet - - facet normal 0.385834247 -0.891602457 -0.237017125 - outer loop - vertex 1.25007606 2.53493595 0.329427987 - vertex 1.42565393 2.98873901 -1.09184897 - vertex 1.17469096 2.5172801 0.273131013 - endloop - endfacet - - facet normal 0.680419087 -0.665989041 -0.305759013 - outer loop - vertex 1.46452999 3.00791907 -1.04711497 - vertex 1.51657104 3.24703598 -1.45213997 - vertex 1.42565393 2.98873901 -1.09184897 - endloop - endfacet - - facet normal 0.528014839 -0.747651756 -0.402761877 - outer loop - vertex 1.51657104 3.24703598 -1.45213997 - vertex 1.49947 3.24326396 -1.46755695 - vertex 1.42565393 2.98873901 -1.09184897 - endloop - endfacet - - facet normal 0.287102133 0.225660101 0.93094033 - outer loop - vertex -0.0255030002 0.320378006 -0.152918994 - vertex 0.0547849983 0.423180997 -0.202600002 - vertex 0.128368005 0.27710101 -0.189882994 - endloop - endfacet - - facet normal -0.184494004 -0.887767971 0.421698987 - outer loop - vertex 1.49947 3.24326396 -1.46755695 - vertex 1.51856101 3.23323202 -1.48032296 - vertex 1.49674499 3.23194098 -1.49258697 - endloop - endfacet - - facet normal 0.800132632 -0.597048759 0.0576229766 - outer loop - vertex -0.0255030002 0.320378006 -0.152918994 - vertex 0.401181996 0.932807028 0.267848015 - vertex 0.0547849983 0.423180997 -0.202600002 - endloop - endfacet - - facet normal 0.781402111 -0.617047131 0.0930780172 - outer loop - vertex 0.401181996 0.932807028 0.267848015 - vertex 0.457011998 0.996855974 0.223748997 - vertex 0.0547849983 0.423180997 -0.202600002 - endloop - endfacet - - facet normal 0.591941774 -0.786967635 0.17403093 - outer loop - vertex 0.401181996 0.932807028 0.267848015 - vertex 0.669640005 1.33213401 1.16047704 - vertex 0.705398023 1.34577894 1.10055697 - endloop - endfacet - - facet normal 0.766158044 -0.64151305 0.0382480025 - outer loop - vertex 0.401181996 0.932807028 0.267848015 - vertex 0.705398023 1.34577894 1.10055697 - vertex 0.457011998 0.996855974 0.223748997 - endloop - endfacet - - facet normal 0.856900215 -0.238298073 0.457095116 - outer loop - vertex 0.669640005 1.33213401 1.16047704 - vertex 0.719905972 1.51263702 1.16034794 - vertex 0.705398023 1.34577894 1.10055697 - endloop - endfacet - - facet normal 0.958668172 -0.166126028 0.230992049 - outer loop - vertex 0.719905972 1.51263702 1.16034794 - vertex 0.730700016 1.46251702 1.07950306 - vertex 0.705398023 1.34577894 1.10055697 - endloop - endfacet - - facet normal 0.937909067 -0.32989803 0.107209012 - outer loop - vertex 0.719905972 1.51263702 1.16034794 - vertex 1.17469096 2.5172801 0.273131013 - vertex 1.15109396 2.43578506 0.228796005 - endloop - endfacet - - facet normal 0.949355602 -0.194093928 0.247085899 - outer loop - vertex 0.719905972 1.51263702 1.16034794 - vertex 1.15109396 2.43578506 0.228796005 - vertex 0.730700016 1.46251702 1.07950306 - endloop - endfacet - - facet normal 0.94807595 -0.310923964 0.0669199899 - outer loop - vertex 1.17469096 2.5172801 0.273131013 - vertex 1.42565393 2.98873901 -1.09184897 - vertex 1.15109396 2.43578506 0.228796005 - endloop - endfacet - - facet normal 0.943761826 -0.325121939 0.0600779913 - outer loop - vertex 1.42565393 2.98873901 -1.09184897 - vertex 1.415802 2.94676304 -1.16424298 - vertex 1.15109396 2.43578506 0.228796005 - endloop - endfacet - - facet normal 0.965287447 -0.260869861 0.012922992 - outer loop - vertex 1.42565393 2.98873901 -1.09184897 - vertex 1.49947 3.24326396 -1.46755695 - vertex 1.49674499 3.23194098 -1.49258697 - endloop - endfacet - - facet normal 0.966702342 -0.25537008 0.0165120047 - outer loop - vertex 1.42565393 2.98873901 -1.09184897 - vertex 1.49674499 3.23194098 -1.49258697 - vertex 1.415802 2.94676304 -1.16424298 - endloop - endfacet - - facet normal 0.287104994 0.225661978 0.930938959 - outer loop - vertex 0.0547849983 0.423180997 -0.202600002 - vertex 0.190484002 0.415984005 -0.242705002 - vertex 0.128368005 0.27710101 -0.189882994 - endloop - endfacet - - facet normal -0.184529021 -0.887732148 0.421759069 - outer loop - vertex 1.49674499 3.23194098 -1.49258697 - vertex 1.51856101 3.23323202 -1.48032296 - vertex 1.51044703 3.22158909 -1.50838101 - endloop - endfacet - - facet normal 0.0337819979 -0.611306012 0.790672958 - outer loop - vertex 0.0547849983 0.423180997 -0.202600002 - vertex 0.457011998 0.996855974 0.223748997 - vertex 0.552495003 1.00363195 0.224907994 - endloop - endfacet - - facet normal 0.175379902 -0.676896572 0.714879572 - outer loop - vertex 0.0547849983 0.423180997 -0.202600002 - vertex 0.552495003 1.00363195 0.224907994 - vertex 0.190484002 0.415984005 -0.242705002 - endloop - endfacet - - facet normal 0.0619340129 -0.933253109 0.353840023 - outer loop - vertex 0.457011998 0.996855974 0.223748997 - vertex 0.705398023 1.34577894 1.10055697 - vertex 0.552495003 1.00363195 0.224907994 - endloop - endfacet - - facet normal -0.055268988 -0.926689804 0.371740907 - outer loop - vertex 0.705398023 1.34577894 1.10055697 - vertex 0.764394999 1.33422804 1.08053398 - vertex 0.552495003 1.00363195 0.224907994 - endloop - endfacet - - facet normal 0.378070056 0.0843310058 0.921928108 - outer loop - vertex 0.705398023 1.34577894 1.10055697 - vertex 0.730700016 1.46251702 1.07950306 - vertex 0.798102021 1.39811099 1.05775404 - endloop - endfacet - - facet normal 0.343855888 0.149168938 0.927098632 - outer loop - vertex 0.705398023 1.34577894 1.10055697 - vertex 0.798102021 1.39811099 1.05775404 - vertex 0.764394999 1.33422804 1.08053398 - endloop - endfacet - - facet normal 0.588112831 0.373159915 0.717547894 - outer loop - vertex 0.730700016 1.46251702 1.07950306 - vertex 1.15109396 2.43578506 0.228796005 - vertex 0.798102021 1.39811099 1.05775404 - endloop - endfacet - - facet normal 0.621727347 0.348749191 0.701305389 - outer loop - vertex 1.15109396 2.43578506 0.228796005 - vertex 1.19705403 2.35181308 0.229809001 - vertex 0.798102021 1.39811099 1.05775404 - endloop - endfacet - - facet normal 0.900523543 0.324001819 0.289965838 - outer loop - vertex 1.15109396 2.43578506 0.228796005 - vertex 1.415802 2.94676304 -1.16424298 - vertex 1.44239593 2.91360092 -1.20978105 - endloop - endfacet - - facet normal 0.829440415 0.457835257 0.320024192 - outer loop - vertex 1.15109396 2.43578506 0.228796005 - vertex 1.44239593 2.91360092 -1.20978105 - vertex 1.19705403 2.35181308 0.229809001 - endloop - endfacet - - facet normal 0.90179795 0.19060199 0.387854964 - outer loop - vertex 1.415802 2.94676304 -1.16424298 - vertex 1.49674499 3.23194098 -1.49258697 - vertex 1.44239593 2.91360092 -1.20978105 - endloop - endfacet - - facet normal 0.809248447 0.306871176 0.500946224 - outer loop - vertex 1.49674499 3.23194098 -1.49258697 - vertex 1.51044703 3.22158909 -1.50838101 - vertex 1.44239593 2.91360092 -1.20978105 - endloop - endfacet - - facet normal 0.287105978 0.22566098 0.930938959 - outer loop - vertex 0.190484002 0.415984005 -0.242705002 - vertex 0.279406995 0.304206014 -0.243034005 - vertex 0.128368005 0.27710101 -0.189882994 - endloop - endfacet - - facet normal -0.184329927 -0.887789667 0.421724826 - outer loop - vertex 1.51044703 3.22158909 -1.50838101 - vertex 1.51856101 3.23323202 -1.48032296 - vertex 1.53025901 3.22000909 -1.50304699 - endloop - endfacet - - facet normal -0.455893964 -0.365067989 0.811717927 - outer loop - vertex 0.190484002 0.415984005 -0.242705002 - vertex 0.552495003 1.00363195 0.224907994 - vertex 0.279406995 0.304206014 -0.243034005 - endloop - endfacet - - facet normal -0.683324039 -0.202508017 0.701469064 - outer loop - vertex 0.552495003 1.00363195 0.224907994 - vertex 0.615728974 0.948033988 0.270455003 - vertex 0.279406995 0.304206014 -0.243034005 - endloop - endfacet - - facet normal -0.75348109 -0.528666079 0.390869021 - outer loop - vertex 0.552495003 1.00363195 0.224907994 - vertex 0.764394999 1.33422804 1.08053398 - vertex 0.802205026 1.30618 1.115484 - endloop - endfacet - - facet normal -0.750331104 -0.532756031 0.391375035 - outer loop - vertex 0.552495003 1.00363195 0.224907994 - vertex 0.802205026 1.30618 1.115484 - vertex 0.615728974 0.948033988 0.270455003 - endloop - endfacet - - facet normal -0.375712037 0.480761021 0.792281091 - outer loop - vertex 0.764394999 1.33422804 1.08053398 - vertex 0.798102021 1.39811099 1.05775404 - vertex 0.802205026 1.30618 1.115484 - endloop - endfacet - - facet normal -0.38203603 0.479180038 0.790212035 - outer loop - vertex 0.798102021 1.39811099 1.05775404 - vertex 0.871357024 1.36791801 1.11147904 - vertex 0.802205026 1.30618 1.115484 - endloop - endfacet - - facet normal -0.196147874 0.688334584 0.698370576 - outer loop - vertex 0.798102021 1.39811099 1.05775404 - vertex 1.19705403 2.35181308 0.229809001 - vertex 1.27796102 2.32860303 0.275409013 - endloop - endfacet - - facet normal -0.219507039 0.691687107 0.688030124 - outer loop - vertex 0.798102021 1.39811099 1.05775404 - vertex 1.27796102 2.32860303 0.275409013 - vertex 0.871357024 1.36791801 1.11147904 - endloop - endfacet - - facet normal 0.0565909743 0.926781595 0.371312857 - outer loop - vertex 1.19705403 2.35181308 0.229809001 - vertex 1.44239593 2.91360092 -1.20978105 - vertex 1.27796102 2.32860303 0.275409013 - endloop - endfacet - - facet normal -0.140208036 0.926429152 0.349386096 - outer loop - vertex 1.44239593 2.91360092 -1.20978105 - vertex 1.48540699 2.91422415 -1.19417298 - vertex 1.27796102 2.32860303 0.275409013 - endloop - endfacet - - facet normal -0.131446019 0.704857111 0.697064102 - outer loop - vertex 1.44239593 2.91360092 -1.20978105 - vertex 1.51044703 3.22158909 -1.50838101 - vertex 1.53025901 3.22000909 -1.50304699 - endloop - endfacet - - facet normal -0.250628948 0.705919802 0.662466824 - outer loop - vertex 1.44239593 2.91360092 -1.20978105 - vertex 1.53025901 3.22000909 -1.50304699 - vertex 1.48540699 2.91422415 -1.19417298 - endloop - endfacet - - facet normal 0.287107021 0.225659013 0.930939138 - outer loop - vertex 0.279406995 0.304206014 -0.243034005 - vertex 0.254595995 0.172016993 -0.203339994 - vertex 0.128368005 0.27710101 -0.189882994 - endloop - endfacet - - facet normal -0.184386045 -0.887791276 0.42169711 - outer loop - vertex 1.53025901 3.22000909 -1.50304699 - vertex 1.51856101 3.23323202 -1.48032296 - vertex 1.54126191 3.22838593 -1.48060107 - endloop - endfacet - - facet normal -0.923753202 0.336100101 0.183621049 - outer loop - vertex 0.279406995 0.304206014 -0.243034005 - vertex 0.615728974 0.948033988 0.270455003 - vertex 0.599097013 0.871927977 0.326090991 - endloop - endfacet - - facet normal -0.929180562 0.254806876 0.26776287 - outer loop - vertex 0.279406995 0.304206014 -0.243034005 - vertex 0.599097013 0.871927977 0.326090991 - vertex 0.254595995 0.172016993 -0.203339994 - endloop - endfacet - - facet normal -0.95622313 0.277304024 0.093486011 - outer loop - vertex 0.615728974 0.948033988 0.270455003 - vertex 0.802205026 1.30618 1.115484 - vertex 0.599097013 0.871927977 0.326090991 - endloop - endfacet - - facet normal -0.900449932 0.434893966 -0.00755599979 - outer loop - vertex 0.802205026 1.30618 1.115484 - vertex 0.79035902 1.28275597 1.17908895 - vertex 0.599097013 0.871927977 0.326090991 - endloop - endfacet - - facet normal -0.666760981 0.743948996 -0.0443829969 - outer loop - vertex 0.802205026 1.30618 1.115484 - vertex 0.871357024 1.36791801 1.11147904 - vertex 0.895301998 1.39467299 1.20021999 - endloop - endfacet - - facet normal -0.735310256 0.66884923 0.109359048 - outer loop - vertex 0.802205026 1.30618 1.115484 - vertex 0.895301998 1.39467299 1.20021999 - vertex 0.79035902 1.28275597 1.17908895 - endloop - endfacet - - facet normal -0.882354856 0.459964931 0.0994089916 - outer loop - vertex 0.871357024 1.36791801 1.11147904 - vertex 1.27796102 2.32860303 0.275409013 - vertex 0.895301998 1.39467299 1.20021999 - endloop - endfacet - - facet normal -0.796392024 0.557778955 0.233756989 - outer loop - vertex 1.27796102 2.32860303 0.275409013 - vertex 1.33289194 2.38362789 0.331256986 - vertex 0.895301998 1.39467299 1.20021999 - endloop - endfacet - - facet normal -0.852394104 0.515902042 0.0852600113 - outer loop - vertex 1.27796102 2.32860303 0.275409013 - vertex 1.48540699 2.91422415 -1.19417298 - vertex 1.51245093 2.94816399 -1.12917101 - endloop - endfacet - - facet normal -0.770075798 0.621154904 0.145429969 - outer loop - vertex 1.27796102 2.32860303 0.275409013 - vertex 1.51245093 2.94816399 -1.12917101 - vertex 1.33289194 2.38362789 0.331256986 - endloop - endfacet - - facet normal -0.917796791 0.340652943 0.203971952 - outer loop - vertex 1.48540699 2.91422415 -1.19417298 - vertex 1.53025901 3.22000909 -1.50304699 - vertex 1.51245093 2.94816399 -1.12917101 - endloop - endfacet - - facet normal -0.865918994 0.42326802 0.266512007 - outer loop - vertex 1.53025901 3.22000909 -1.50304699 - vertex 1.54126191 3.22838593 -1.48060107 - vertex 1.51245093 2.94816399 -1.12917101 - endloop - endfacet - - facet normal 0.287100911 -0.22565192 0.930942714 - outer loop - vertex 0.073210001 -0.249522001 -0.189882994 - vertex 0.0795739964 -0.0913780034 -0.153512999 - vertex 0.199438006 -0.144437 -0.203339994 - endloop - endfacet - - facet normal -0.184375003 0.887741923 0.421805978 - outer loop - vertex 1.48001194 -3.21283698 -1.45794499 - vertex 1.46340299 -3.20565391 -1.48032296 - vertex 1.48610401 -3.20080709 -1.48060107 - endloop - endfacet - - facet normal -0.475138009 -0.667518914 -0.573290944 - outer loop - vertex 0.459966004 -0.805042982 0.349920988 - vertex 0.543938994 -0.844349027 0.326090991 - vertex 0.199438006 -0.144437 -0.203339994 - endloop - endfacet - - facet normal -0.518250108 -0.659949124 -0.543952107 - outer loop - vertex 0.0795739964 -0.0913780034 -0.153512999 - vertex 0.459966004 -0.805042982 0.349920988 - vertex 0.199438006 -0.144437 -0.203339994 - endloop - endfacet - - facet normal -0.472386926 -0.830829799 -0.294231981 - outer loop - vertex 0.459966004 -0.805042982 0.349920988 - vertex 0.735199988 -1.25517702 1.17908895 - vertex 0.543938994 -0.844349027 0.326090991 - endloop - endfacet - - facet normal -0.327001125 -0.87180227 -0.364734113 - outer loop - vertex 0.459966004 -0.805042982 0.349920988 - vertex 0.682615995 -1.25401497 1.22345495 - vertex 0.735199988 -1.25517702 1.17908895 - endloop - endfacet - - facet normal -0.35068512 -0.480595142 -0.803771257 - outer loop - vertex 0.796747029 -1.43064797 1.25715399 - vertex 0.840143979 -1.36709404 1.20021999 - vertex 0.735199988 -1.25517702 1.17908895 - endloop - endfacet - - facet normal -0.56752795 -0.492581934 -0.659753859 - outer loop - vertex 0.682615995 -1.25401497 1.22345495 - vertex 0.796747029 -1.43064797 1.25715399 - vertex 0.735199988 -1.25517702 1.17908895 - endloop - endfacet - - facet normal -0.840571702 0.0964379609 -0.533046842 - outer loop - vertex 0.796747029 -1.43064797 1.25715399 - vertex 1.27773297 -2.35605001 0.331256986 - vertex 0.840143979 -1.36709404 1.20021999 - endloop - endfacet - - facet normal -0.887653232 -0.000607000198 -0.460512102 - outer loop - vertex 0.796747029 -1.43064797 1.25715399 - vertex 1.265324 -2.44787598 0.355296999 - vertex 1.27773297 -2.35605001 0.331256986 - endloop - endfacet - - facet normal -0.991470754 0.0184189957 -0.129020974 - outer loop - vertex 1.44800103 -2.96228409 -1.06372297 - vertex 1.45729196 -2.92058492 -1.12917101 - vertex 1.27773297 -2.35605001 0.331256986 - endloop - endfacet - - facet normal -0.982979894 0.0910649896 -0.159554973 - outer loop - vertex 1.265324 -2.44787598 0.355296999 - vertex 1.44800103 -2.96228409 -1.06372297 - vertex 1.27773297 -2.35605001 0.331256986 - endloop - endfacet - - facet normal -0.992559552 0.0415029824 -0.114467941 - outer loop - vertex 1.44800103 -2.96228409 -1.06372297 - vertex 1.48610401 -3.20080709 -1.48060107 - vertex 1.45729196 -2.92058492 -1.12917101 - endloop - endfacet - - facet normal -0.971497953 0.156295985 -0.178222984 - outer loop - vertex 1.44800103 -2.96228409 -1.06372297 - vertex 1.48001194 -3.21283698 -1.45794499 - vertex 1.48610401 -3.20080709 -1.48060107 - endloop - endfacet - - facet normal 0.28709814 -0.225652128 0.930943549 - outer loop - vertex 0.073210001 -0.249522001 -0.189882994 - vertex -0.0450830013 -0.157405004 -0.131073996 - vertex 0.0795739964 -0.0913780034 -0.153512999 - endloop - endfacet - - facet normal -0.184356034 0.887751043 0.42179507 - outer loop - vertex 1.46141195 -3.2194581 -1.45213997 - vertex 1.46340299 -3.20565391 -1.48032296 - vertex 1.48001194 -3.21283698 -1.45794499 - endloop - endfacet - - facet normal 0.127014041 -0.525668204 -0.841154218 - outer loop - vertex -0.0450830013 -0.157405004 -0.131073996 - vertex 0.459966004 -0.805042982 0.349920988 - vertex 0.0795739964 -0.0913780034 -0.153512999 - endloop - endfacet - - facet normal 0.362984002 -0.356359988 -0.860958934 - outer loop - vertex -0.0450830013 -0.157405004 -0.131073996 - vertex 0.371885002 -0.832136989 0.324000001 - vertex 0.459966004 -0.805042982 0.349920988 - endloop - endfacet - - facet normal 0.393002957 -0.773263991 -0.497605979 - outer loop - vertex 0.628889978 -1.27599001 1.21517205 - vertex 0.682615995 -1.25401497 1.22345495 - vertex 0.459966004 -0.805042982 0.349920988 - endloop - endfacet - - facet normal 0.38549611 -0.776755273 -0.49804014 - outer loop - vertex 0.371885002 -0.832136989 0.324000001 - vertex 0.628889978 -1.27599001 1.21517205 - vertex 0.459966004 -0.805042982 0.349920988 - endloop - endfacet - - facet normal 0.180200949 -0.0707409829 -0.981082737 - outer loop - vertex 0.628889978 -1.27599001 1.21517205 - vertex 0.796747029 -1.43064797 1.25715399 - vertex 0.682615995 -1.25401497 1.22345495 - endloop - endfacet - - facet normal 0.230534911 -0.0139069958 -0.972964704 - outer loop - vertex 0.628889978 -1.27599001 1.21517205 - vertex 0.718689024 -1.48314798 1.23941004 - vertex 0.796747029 -1.43064797 1.25715399 - endloop - endfacet - - facet normal -0.213641912 0.591137767 -0.777761698 - outer loop - vertex 1.19491804 -2.50735712 0.329427987 - vertex 1.265324 -2.44787598 0.355296999 - vertex 0.796747029 -1.43064797 1.25715399 - endloop - endfacet - - facet normal -0.219361112 0.589029253 -0.777769387 - outer loop - vertex 0.718689024 -1.48314798 1.23941004 - vertex 1.19491804 -2.50735712 0.329427987 - vertex 0.796747029 -1.43064797 1.25715399 - endloop - endfacet - - facet normal -0.526722312 0.775172412 -0.348814219 - outer loop - vertex 1.19491804 -2.50735712 0.329427987 - vertex 1.44800103 -2.96228409 -1.06372297 - vertex 1.265324 -2.44787598 0.355296999 - endloop - endfacet - - facet normal -0.515791953 0.78233093 -0.349165976 - outer loop - vertex 1.19491804 -2.50735712 0.329427987 - vertex 1.40937102 -2.98034 -1.04711497 - vertex 1.44800103 -2.96228409 -1.06372297 - endloop - endfacet - - facet normal -0.425266892 0.747818768 -0.509818792 - outer loop - vertex 1.46141195 -3.2194581 -1.45213997 - vertex 1.48001194 -3.21283698 -1.45794499 - vertex 1.44800103 -2.96228409 -1.06372297 - endloop - endfacet - - facet normal -0.53230989 0.697277844 -0.480051875 - outer loop - vertex 1.40937102 -2.98034 -1.04711497 - vertex 1.46141195 -3.2194581 -1.45213997 - vertex 1.44800103 -2.96228409 -1.06372297 - endloop - endfacet - - facet normal 0.287100106 -0.225650087 0.93094337 - outer loop - vertex 0.073210001 -0.249522001 -0.189882994 - vertex -0.0806619972 -0.292798996 -0.152918994 - vertex -0.0450830013 -0.157405004 -0.131073996 - endloop - endfacet - - facet normal -0.184420034 0.887742043 0.42178604 - outer loop - vertex 1.44431102 -3.21568489 -1.46755695 - vertex 1.46340299 -3.20565391 -1.48032296 - vertex 1.46141195 -3.2194581 -1.45213997 - endloop - endfacet - - facet normal 0.821849883 0.134506986 -0.553597927 - outer loop - vertex 0.346022993 -0.905228019 0.267848015 - vertex 0.371885002 -0.832136989 0.324000001 - vertex -0.0450830013 -0.157405004 -0.131073996 - endloop - endfacet - - facet normal 0.661470056 -0.0531199984 -0.748088062 - outer loop - vertex -0.0806619972 -0.292798996 -0.152918994 - vertex 0.346022993 -0.905228019 0.267848015 - vertex -0.0450830013 -0.157405004 -0.131073996 - endloop - endfacet - - facet normal 0.944037497 -0.0903129503 -0.317232847 - outer loop - vertex 0.346022993 -0.905228019 0.267848015 - vertex 0.628889978 -1.27599001 1.21517205 - vertex 0.371885002 -0.832136989 0.324000001 - endloop - endfacet - - facet normal 0.961416781 0.0369929932 -0.272596925 - outer loop - vertex 0.346022993 -0.905228019 0.267848015 - vertex 0.614481986 -1.30455494 1.16047704 - vertex 0.628889978 -1.27599001 1.21517205 - endloop - endfacet - - facet normal 0.790184855 0.278661966 -0.545852959 - outer loop - vertex 0.664747 -1.485057 1.16034794 - vertex 0.718689024 -1.48314798 1.23941004 - vertex 0.628889978 -1.27599001 1.21517205 - endloop - endfacet - - facet normal 0.89621377 0.249836937 -0.366581917 - outer loop - vertex 0.614481986 -1.30455494 1.16047704 - vertex 0.664747 -1.485057 1.16034794 - vertex 0.628889978 -1.27599001 1.21517205 - endloop - endfacet - - facet normal 0.608522832 0.666114748 -0.431266844 - outer loop - vertex 0.664747 -1.485057 1.16034794 - vertex 1.19491804 -2.50735712 0.329427987 - vertex 0.718689024 -1.48314798 1.23941004 - endloop - endfacet - - facet normal 0.53311193 0.682670951 -0.499751985 - outer loop - vertex 0.664747 -1.485057 1.16034794 - vertex 1.11953306 -2.48970103 0.273131013 - vertex 1.19491804 -2.50735712 0.329427987 - endloop - endfacet - - facet normal 0.592273772 0.785883725 -0.17775993 - outer loop - vertex 1.37049496 -2.96115994 -1.09184897 - vertex 1.40937102 -2.98034 -1.04711497 - vertex 1.19491804 -2.50735712 0.329427987 - endloop - endfacet - - facet normal 0.385835916 0.891601741 -0.237016946 - outer loop - vertex 1.11953306 -2.48970103 0.273131013 - vertex 1.37049496 -2.96115994 -1.09184897 - vertex 1.19491804 -2.50735712 0.329427987 - endloop - endfacet - - facet normal 0.680415094 0.665992081 -0.305761069 - outer loop - vertex 1.37049496 -2.96115994 -1.09184897 - vertex 1.46141195 -3.2194581 -1.45213997 - vertex 1.40937102 -2.98034 -1.04711497 - endloop - endfacet - - facet normal 0.528015077 0.747652054 -0.402761072 - outer loop - vertex 1.37049496 -2.96115994 -1.09184897 - vertex 1.44431102 -3.21568489 -1.46755695 - vertex 1.46141195 -3.2194581 -1.45213997 - endloop - endfacet - - facet normal 0.287102133 -0.225660101 0.93094033 - outer loop - vertex 0.073210001 -0.249522001 -0.189882994 - vertex -0.000372999988 -0.395601988 -0.202600002 - vertex -0.0806619972 -0.292798996 -0.152918994 - endloop - endfacet - - facet normal -0.184492007 0.887767911 0.421699971 - outer loop - vertex 1.44158602 -3.20436192 -1.49258697 - vertex 1.46340299 -3.20565391 -1.48032296 - vertex 1.44431102 -3.21568489 -1.46755695 - endloop - endfacet - - facet normal 0.800132632 0.597048759 0.0576229766 - outer loop - vertex -0.000372999988 -0.395601988 -0.202600002 - vertex 0.346022993 -0.905228019 0.267848015 - vertex -0.0806619972 -0.292798996 -0.152918994 - endloop - endfacet - - facet normal 0.781401157 0.617048144 0.0930790231 - outer loop - vertex -0.000372999988 -0.395601988 -0.202600002 - vertex 0.401854008 -0.969277024 0.223748997 - vertex 0.346022993 -0.905228019 0.267848015 - endloop - endfacet - - facet normal 0.591942251 0.786967278 0.174031064 - outer loop - vertex 0.650238991 -1.31819999 1.10055697 - vertex 0.614481986 -1.30455494 1.16047704 - vertex 0.346022993 -0.905228019 0.267848015 - endloop - endfacet - - facet normal 0.766158044 0.64151305 0.0382480025 - outer loop - vertex 0.401854008 -0.969277024 0.223748997 - vertex 0.650238991 -1.31819999 1.10055697 - vertex 0.346022993 -0.905228019 0.267848015 - endloop - endfacet - - facet normal 0.856900215 0.238298073 0.457095116 - outer loop - vertex 0.650238991 -1.31819999 1.10055697 - vertex 0.664747 -1.485057 1.16034794 - vertex 0.614481986 -1.30455494 1.16047704 - endloop - endfacet - - facet normal 0.958668351 0.166127056 0.230991095 - outer loop - vertex 0.650238991 -1.31819999 1.10055697 - vertex 0.675541997 -1.43493795 1.07950306 - vertex 0.664747 -1.485057 1.16034794 - endloop - endfacet - - facet normal 0.937909067 0.329899043 0.107207015 - outer loop - vertex 1.09593499 -2.40820599 0.228796005 - vertex 1.11953306 -2.48970103 0.273131013 - vertex 0.664747 -1.485057 1.16034794 - endloop - endfacet - - facet normal 0.949355602 0.194093928 0.247085899 - outer loop - vertex 0.675541997 -1.43493795 1.07950306 - vertex 1.09593499 -2.40820599 0.228796005 - vertex 0.664747 -1.485057 1.16034794 - endloop - endfacet - - facet normal 0.948075712 0.310924917 0.0669189766 - outer loop - vertex 1.09593499 -2.40820599 0.228796005 - vertex 1.37049496 -2.96115994 -1.09184897 - vertex 1.11953306 -2.48970103 0.273131013 - endloop - endfacet - - facet normal 0.943762779 0.325118899 0.0600789823 - outer loop - vertex 1.09593499 -2.40820599 0.228796005 - vertex 1.36064398 -2.91918492 -1.16424298 - vertex 1.37049496 -2.96115994 -1.09184897 - endloop - endfacet - - facet normal 0.965285242 0.260878086 0.0129180038 - outer loop - vertex 1.44158602 -3.20436192 -1.49258697 - vertex 1.44431102 -3.21568489 -1.46755695 - vertex 1.37049496 -2.96115994 -1.09184897 - endloop - endfacet - - facet normal 0.966703117 0.255367041 0.0165140014 - outer loop - vertex 1.36064398 -2.91918492 -1.16424298 - vertex 1.44158602 -3.20436192 -1.49258697 - vertex 1.37049496 -2.96115994 -1.09184897 - endloop - endfacet - - facet normal 0.287104994 -0.225661978 0.930938959 - outer loop - vertex 0.073210001 -0.249522001 -0.189882994 - vertex 0.135325 -0.388404995 -0.242705002 - vertex -0.000372999988 -0.395601988 -0.202600002 - endloop - endfacet - - facet normal -0.184526041 0.887733161 0.421758115 - outer loop - vertex 1.45528793 -3.19401002 -1.50838101 - vertex 1.46340299 -3.20565391 -1.48032296 - vertex 1.44158602 -3.20436192 -1.49258697 - endloop - endfacet - - facet normal 0.0337819979 0.611306012 0.790672958 - outer loop - vertex 0.497337013 -0.976052999 0.224907994 - vertex 0.401854008 -0.969277024 0.223748997 - vertex -0.000372999988 -0.395601988 -0.202600002 - endloop - endfacet - - facet normal 0.175379902 0.676896572 0.714879572 - outer loop - vertex 0.135325 -0.388404995 -0.242705002 - vertex 0.497337013 -0.976052999 0.224907994 - vertex -0.000372999988 -0.395601988 -0.202600002 - endloop - endfacet - - facet normal 0.0619340129 0.933253109 0.353840023 - outer loop - vertex 0.497337013 -0.976052999 0.224907994 - vertex 0.650238991 -1.31819999 1.10055697 - vertex 0.401854008 -0.969277024 0.223748997 - endloop - endfacet - - facet normal -0.0552679971 0.926689863 0.371740967 - outer loop - vertex 0.497337013 -0.976052999 0.224907994 - vertex 0.709236979 -1.30664897 1.08053398 - vertex 0.650238991 -1.31819999 1.10055697 - endloop - endfacet - - facet normal 0.378070056 -0.0843310058 0.921928108 - outer loop - vertex 0.742944002 -1.37053204 1.05775404 - vertex 0.675541997 -1.43493795 1.07950306 - vertex 0.650238991 -1.31819999 1.10055697 - endloop - endfacet - - facet normal 0.343855888 -0.149168938 0.927098632 - outer loop - vertex 0.709236979 -1.30664897 1.08053398 - vertex 0.742944002 -1.37053204 1.05775404 - vertex 0.650238991 -1.31819999 1.10055697 - endloop - endfacet - - facet normal 0.588112831 -0.373159915 0.717547894 - outer loop - vertex 0.742944002 -1.37053204 1.05775404 - vertex 1.09593499 -2.40820599 0.228796005 - vertex 0.675541997 -1.43493795 1.07950306 - endloop - endfacet - - facet normal 0.621727943 -0.348748952 0.701304913 - outer loop - vertex 0.742944002 -1.37053204 1.05775404 - vertex 1.14189506 -2.32423401 0.229809001 - vertex 1.09593499 -2.40820599 0.228796005 - endloop - endfacet - - facet normal 0.900525928 -0.323995978 0.289964974 - outer loop - vertex 1.38723803 -2.88602304 -1.20978105 - vertex 1.36064398 -2.91918492 -1.16424298 - vertex 1.09593499 -2.40820599 0.228796005 - endloop - endfacet - - facet normal 0.829440713 -0.45783484 0.320023894 - outer loop - vertex 1.14189506 -2.32423401 0.229809001 - vertex 1.38723803 -2.88602304 -1.20978105 - vertex 1.09593499 -2.40820599 0.228796005 - endloop - endfacet - - facet normal 0.901799619 -0.190599918 0.387851864 - outer loop - vertex 1.38723803 -2.88602304 -1.20978105 - vertex 1.44158602 -3.20436192 -1.49258697 - vertex 1.36064398 -2.91918492 -1.16424298 - endloop - endfacet - - facet normal 0.809241116 -0.306879073 0.500953138 - outer loop - vertex 1.38723803 -2.88602304 -1.20978105 - vertex 1.45528793 -3.19401002 -1.50838101 - vertex 1.44158602 -3.20436192 -1.49258697 - endloop - endfacet - - facet normal 0.287105978 -0.22566098 0.930938959 - outer loop - vertex 0.073210001 -0.249522001 -0.189882994 - vertex 0.224249005 -0.276625991 -0.243034005 - vertex 0.135325 -0.388404995 -0.242705002 - endloop - endfacet - - facet normal -0.184329927 0.887789667 0.421724826 - outer loop - vertex 1.47510099 -3.19243002 -1.50304699 - vertex 1.46340299 -3.20565391 -1.48032296 - vertex 1.45528793 -3.19401002 -1.50838101 - endloop - endfacet - - facet normal -0.455893964 0.365067989 0.811717927 - outer loop - vertex 0.224249005 -0.276625991 -0.243034005 - vertex 0.497337013 -0.976052999 0.224907994 - vertex 0.135325 -0.388404995 -0.242705002 - endloop - endfacet - - facet normal -0.683324039 0.202508017 0.701469064 - outer loop - vertex 0.224249005 -0.276625991 -0.243034005 - vertex 0.560570002 -0.920454979 0.270455003 - vertex 0.497337013 -0.976052999 0.224907994 - endloop - endfacet - - facet normal -0.753481925 0.528664887 0.390868932 - outer loop - vertex 0.747047007 -1.27860093 1.115484 - vertex 0.709236979 -1.30664897 1.08053398 - vertex 0.497337013 -0.976052999 0.224907994 - endloop - endfacet - - facet normal -0.750331104 0.532756031 0.391375035 - outer loop - vertex 0.560570002 -0.920454979 0.270455003 - vertex 0.747047007 -1.27860093 1.115484 - vertex 0.497337013 -0.976052999 0.224907994 - endloop - endfacet - - facet normal -0.375710905 -0.480760843 0.792281747 - outer loop - vertex 0.747047007 -1.27860093 1.115484 - vertex 0.742944002 -1.37053204 1.05775404 - vertex 0.709236979 -1.30664897 1.08053398 - endloop - endfacet - - facet normal -0.382036179 -0.479179204 0.790212393 - outer loop - vertex 0.747047007 -1.27860093 1.115484 - vertex 0.816199005 -1.34033895 1.11147904 - vertex 0.742944002 -1.37053204 1.05775404 - endloop - endfacet - - facet normal -0.196147874 -0.688334584 0.698370576 - outer loop - vertex 1.22280204 -2.30102396 0.275409013 - vertex 1.14189506 -2.32423401 0.229809001 - vertex 0.742944002 -1.37053204 1.05775404 - endloop - endfacet - - facet normal -0.219507039 -0.691687107 0.688030124 - outer loop - vertex 0.816199005 -1.34033895 1.11147904 - vertex 1.22280204 -2.30102396 0.275409013 - vertex 0.742944002 -1.37053204 1.05775404 - endloop - endfacet - - facet normal 0.0565900318 -0.926781535 0.371313214 - outer loop - vertex 1.22280204 -2.30102396 0.275409013 - vertex 1.38723803 -2.88602304 -1.20978105 - vertex 1.14189506 -2.32423401 0.229809001 - endloop - endfacet - - facet normal -0.140208036 -0.926429152 0.349386096 - outer loop - vertex 1.22280204 -2.30102396 0.275409013 - vertex 1.43024898 -2.88664603 -1.19417298 - vertex 1.38723803 -2.88602304 -1.20978105 - endloop - endfacet - - facet normal -0.131445929 -0.704857588 0.697063565 - outer loop - vertex 1.47510099 -3.19243002 -1.50304699 - vertex 1.45528793 -3.19401002 -1.50838101 - vertex 1.38723803 -2.88602304 -1.20978105 - endloop - endfacet - - facet normal -0.250627995 -0.705919981 0.662467003 - outer loop - vertex 1.43024898 -2.88664603 -1.19417298 - vertex 1.47510099 -3.19243002 -1.50304699 - vertex 1.38723803 -2.88602304 -1.20978105 - endloop - endfacet - - facet normal 0.287107021 -0.225659013 0.930939138 - outer loop - vertex 0.073210001 -0.249522001 -0.189882994 - vertex 0.199438006 -0.144437 -0.203339994 - vertex 0.224249005 -0.276625991 -0.243034005 - endloop - endfacet - - facet normal -0.184387118 0.887791514 0.421696275 - outer loop - vertex 1.48610401 -3.20080709 -1.48060107 - vertex 1.46340299 -3.20565391 -1.48032296 - vertex 1.47510099 -3.19243002 -1.50304699 - endloop - endfacet - - facet normal -0.923753202 -0.336100101 0.183621049 - outer loop - vertex 0.543938994 -0.844349027 0.326090991 - vertex 0.560570002 -0.920454979 0.270455003 - vertex 0.224249005 -0.276625991 -0.243034005 - endloop - endfacet - - facet normal -0.929180801 -0.254806966 0.267761976 - outer loop - vertex 0.199438006 -0.144437 -0.203339994 - vertex 0.543938994 -0.844349027 0.326090991 - vertex 0.224249005 -0.276625991 -0.243034005 - endloop - endfacet - - facet normal -0.95622313 -0.277304024 0.093486011 - outer loop - vertex 0.543938994 -0.844349027 0.326090991 - vertex 0.747047007 -1.27860093 1.115484 - vertex 0.560570002 -0.920454979 0.270455003 - endloop - endfacet - - facet normal -0.900449932 -0.434893966 -0.00755599979 - outer loop - vertex 0.543938994 -0.844349027 0.326090991 - vertex 0.735199988 -1.25517702 1.17908895 - vertex 0.747047007 -1.27860093 1.115484 - endloop - endfacet - - facet normal -0.666762054 -0.743947983 -0.0443830006 - outer loop - vertex 0.840143979 -1.36709404 1.20021999 - vertex 0.816199005 -1.34033895 1.11147904 - vertex 0.747047007 -1.27860093 1.115484 - endloop - endfacet - - facet normal -0.735310256 -0.66884923 0.109359048 - outer loop - vertex 0.735199988 -1.25517702 1.17908895 - vertex 0.840143979 -1.36709404 1.20021999 - vertex 0.747047007 -1.27860093 1.115484 - endloop - endfacet - - facet normal -0.882354856 -0.459964931 0.0994089916 - outer loop - vertex 0.840143979 -1.36709404 1.20021999 - vertex 1.22280204 -2.30102396 0.275409013 - vertex 0.816199005 -1.34033895 1.11147904 - endloop - endfacet - - facet normal -0.796393156 -0.55777812 0.233755067 - outer loop - vertex 0.840143979 -1.36709404 1.20021999 - vertex 1.27773297 -2.35605001 0.331256986 - vertex 1.22280204 -2.30102396 0.275409013 - endloop - endfacet - - facet normal -0.852396131 -0.515899062 0.0852590129 - outer loop - vertex 1.45729196 -2.92058492 -1.12917101 - vertex 1.43024898 -2.88664603 -1.19417298 - vertex 1.22280204 -2.30102396 0.275409013 - endloop - endfacet - - facet normal -0.770077705 -0.621152759 0.145428941 - outer loop - vertex 1.27773297 -2.35605001 0.331256986 - vertex 1.45729196 -2.92058492 -1.12917101 - vertex 1.22280204 -2.30102396 0.275409013 - endloop - endfacet - - facet normal -0.917797387 -0.34065178 0.203970864 - outer loop - vertex 1.45729196 -2.92058492 -1.12917101 - vertex 1.47510099 -3.19243002 -1.50304699 - vertex 1.43024898 -2.88664603 -1.19417298 - endloop - endfacet - - facet normal -0.865921021 -0.42326504 0.26651001 - outer loop - vertex 1.45729196 -2.92058492 -1.12917101 - vertex 1.48610401 -3.20080709 -1.48060107 - vertex 1.47510099 -3.19243002 -1.50304699 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04417706 0.405290008 -0.393858999 - vertex -2.04417706 0.405290008 -0.393858999 - vertex -2.04417706 0.405290008 -0.393858999 - endloop - endfacet - - facet normal -0.891946077 0.305186033 -0.333607048 - outer loop - vertex -1.82387102 0.262814999 0.436648995 - vertex -1.88754892 0.152713999 0.506179988 - vertex -1.82589507 0.173241004 0.360118985 - endloop - endfacet - - facet normal -0.885285795 -0.462163895 0.0517069884 - outer loop - vertex -1.96445894 0.327820987 0.278562993 - vertex -1.92826998 0.255225986 0.249314994 - vertex -2.04417706 0.405290008 -0.393858999 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04417706 0.405290008 -0.393858999 - vertex -1.96445894 0.327820987 0.278562993 - vertex -2.04417706 0.405290008 -0.393858999 - endloop - endfacet - - facet normal -0.782588899 -0.524955928 0.334627926 - outer loop - vertex -1.96445894 0.327820987 0.278562993 - vertex -1.82589507 0.173241004 0.360118985 - vertex -1.92826998 0.255225986 0.249314994 - endloop - endfacet - - facet normal -0.758137107 -0.413600057 0.504146039 - outer loop - vertex -1.96445894 0.327820987 0.278562993 - vertex -1.82387102 0.262814999 0.436648995 - vertex -1.82589507 0.173241004 0.360118985 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04417706 0.405290008 -0.393858999 - vertex -2.04417706 0.405290008 -0.393858999 - vertex -2.04417706 0.405290008 -0.393858999 - endloop - endfacet - - facet normal -0.891943216 0.305170089 -0.333629102 - outer loop - vertex -1.86979997 0.26947999 0.565533996 - vertex -1.88754892 0.152713999 0.506179988 - vertex -1.82387102 0.262814999 0.436648995 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04417706 0.405290008 -0.393858999 - vertex -1.96445894 0.327820987 0.278562993 - vertex -2.04417706 0.405290008 -0.393858999 - endloop - endfacet - - facet normal -0.239093035 -0.967433095 -0.0831130072 - outer loop - vertex -2.04417706 0.405290008 -0.393858999 - vertex -2.0592401 0.350344002 0.289045006 - vertex -1.96445894 0.327820987 0.278562993 - endloop - endfacet - - facet normal -0.347355902 -0.934693694 -0.0754429772 - outer loop - vertex -1.86979997 0.26947999 0.565533996 - vertex -1.82387102 0.262814999 0.436648995 - vertex -1.96445894 0.327820987 0.278562993 - endloop - endfacet - - facet normal -0.241774052 -0.963362157 -0.116098031 - outer loop - vertex -2.0592401 0.350344002 0.289045006 - vertex -1.86979997 0.26947999 0.565533996 - vertex -1.96445894 0.327820987 0.278562993 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04417706 0.405290008 -0.393858999 - vertex -2.04417706 0.405290008 -0.393858999 - vertex -2.04417706 0.405290008 -0.393858999 - endloop - endfacet - - facet normal -0.891948104 0.305166036 -0.333620042 - outer loop - vertex -1.929093 0.188217998 0.649725974 - vertex -1.88754892 0.152713999 0.506179988 - vertex -1.86979997 0.26947999 0.565533996 - endloop - endfacet - - facet normal 0.485254169 -0.872347355 -0.059485022 - outer loop - vertex -2.14123702 0.305835992 0.272870004 - vertex -2.0592401 0.350344002 0.289045006 - vertex -2.04417706 0.405290008 -0.393858999 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04417706 0.405290008 -0.393858999 - vertex -2.14123702 0.305835992 0.272870004 - vertex -2.04417706 0.405290008 -0.393858999 - endloop - endfacet - - facet normal 0.482345223 -0.694664299 -0.533652186 - outer loop - vertex -2.14123702 0.305835992 0.272870004 - vertex -1.86979997 0.26947999 0.565533996 - vertex -2.0592401 0.350344002 0.289045006 - endloop - endfacet - - facet normal 0.403894991 -0.783667028 -0.471947998 - outer loop - vertex -2.14123702 0.305835992 0.272870004 - vertex -1.929093 0.188217998 0.649725974 - vertex -1.86979997 0.26947999 0.565533996 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04417706 0.405290008 -0.393858999 - vertex -2.04417706 0.405290008 -0.393858999 - vertex -2.04417706 0.405290008 -0.393858999 - endloop - endfacet - - facet normal -0.891944408 0.305174798 -0.3336218 - outer loop - vertex -1.95710492 0.080219999 0.625826001 - vertex -1.88754892 0.152713999 0.506179988 - vertex -1.929093 0.188217998 0.649725974 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04417706 0.405290008 -0.393858999 - vertex -2.14123702 0.305835992 0.272870004 - vertex -2.04417706 0.405290008 -0.393858999 - endloop - endfacet - - facet normal 0.98234576 -0.14196597 0.121829979 - outer loop - vertex -2.04417706 0.405290008 -0.393858999 - vertex -2.14871097 0.227810994 0.242217004 - vertex -2.14123702 0.305835992 0.272870004 - endloop - endfacet - - facet normal 0.851677775 -0.107379965 -0.512946844 - outer loop - vertex -1.95710492 0.080219999 0.625826001 - vertex -1.929093 0.188217998 0.649725974 - vertex -2.14123702 0.305835992 0.272870004 - endloop - endfacet - - facet normal 0.903621852 0.0788339823 -0.421013921 - outer loop - vertex -2.14871097 0.227810994 0.242217004 - vertex -1.95710492 0.080219999 0.625826001 - vertex -2.14123702 0.305835992 0.272870004 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04417706 0.405290008 -0.393858999 - vertex -2.04417706 0.405290008 -0.393858999 - vertex -2.04417706 0.405290008 -0.393858999 - endloop - endfacet - - facet normal -0.891943157 0.30518806 -0.333613068 - outer loop - vertex -1.93274093 0.026811 0.511829019 - vertex -1.88754892 0.152713999 0.506179988 - vertex -1.95710492 0.080219999 0.625826001 - endloop - endfacet - - facet normal 0.618495345 0.72466743 0.303843141 - outer loop - vertex -2.07603097 0.175025001 0.220166996 - vertex -2.14871097 0.227810994 0.242217004 - vertex -2.04417706 0.405290008 -0.393858999 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04417706 0.405290008 -0.393858999 - vertex -2.07603097 0.175025001 0.220166996 - vertex -2.04417706 0.405290008 -0.393858999 - endloop - endfacet - - facet normal 0.590632141 0.806794107 0.0153980022 - outer loop - vertex -2.07603097 0.175025001 0.220166996 - vertex -1.95710492 0.080219999 0.625826001 - vertex -2.14871097 0.227810994 0.242217004 - endloop - endfacet - - facet normal 0.806724787 0.582333744 -0.100411966 - outer loop - vertex -2.07603097 0.175025001 0.220166996 - vertex -1.93274093 0.026811 0.511829019 - vertex -1.95710492 0.080219999 0.625826001 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04417706 0.405290008 -0.393858999 - vertex -2.04417706 0.405290008 -0.393858999 - vertex -2.04417706 0.405290008 -0.393858999 - endloop - endfacet - - facet normal -0.891946197 0.305190057 -0.333603054 - outer loop - vertex -1.874349 0.0682099983 0.393579006 - vertex -1.88754892 0.152713999 0.506179988 - vertex -1.93274093 0.026811 0.511829019 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04417706 0.405290008 -0.393858999 - vertex -2.07603097 0.175025001 0.220166996 - vertex -2.04417706 0.405290008 -0.393858999 - endloop - endfacet - - facet normal -0.126801014 0.93091315 0.342523098 - outer loop - vertex -2.04417706 0.405290008 -0.393858999 - vertex -1.97792399 0.187224999 0.223326996 - vertex -2.07603097 0.175025001 0.220166996 - endloop - endfacet - - facet normal 0.14587602 0.909062088 0.390289992 - outer loop - vertex -1.874349 0.0682099983 0.393579006 - vertex -1.93274093 0.026811 0.511829019 - vertex -2.07603097 0.175025001 0.220166996 - endloop - endfacet - - facet normal -0.116740949 0.779241621 0.615754724 - outer loop - vertex -1.97792399 0.187224999 0.223326996 - vertex -1.874349 0.0682099983 0.393579006 - vertex -2.07603097 0.175025001 0.220166996 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04417706 0.405290008 -0.393858999 - vertex -2.04417706 0.405290008 -0.393858999 - vertex -2.04417706 0.405290008 -0.393858999 - endloop - endfacet - - facet normal -0.89194411 0.30519402 -0.333605021 - outer loop - vertex -1.82589507 0.173241004 0.360118985 - vertex -1.88754892 0.152713999 0.506179988 - vertex -1.874349 0.0682099983 0.393579006 - endloop - endfacet - - facet normal -0.824144065 0.500415027 0.265276015 - outer loop - vertex -1.92826998 0.255225986 0.249314994 - vertex -1.97792399 0.187224999 0.223326996 - vertex -2.04417706 0.405290008 -0.393858999 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04417706 0.405290008 -0.393858999 - vertex -1.92826998 0.255225986 0.249314994 - vertex -2.04417706 0.405290008 -0.393858999 - endloop - endfacet - - facet normal -0.719097137 0.282446057 0.63492012 - outer loop - vertex -1.92826998 0.255225986 0.249314994 - vertex -1.874349 0.0682099983 0.393579006 - vertex -1.97792399 0.187224999 0.223326996 - endloop - endfacet - - facet normal -0.460380882 0.455178916 0.762142777 - outer loop - vertex -1.92826998 0.255225986 0.249314994 - vertex -1.82589507 0.173241004 0.360118985 - vertex -1.874349 0.0682099983 0.393579006 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 0.828594983 0.146647006 - vertex -2.04218507 0.828594983 0.146647006 - vertex -2.04218507 0.828594983 0.146647006 - endloop - endfacet - - facet normal -0.417208999 0.680008054 -0.602931023 - outer loop - vertex -1.57823491 0.290569007 0.523093998 - vertex -1.56890297 0.382055014 0.619817972 - vertex -1.69004607 0.288253009 0.597850978 - endloop - endfacet - - facet normal 0.390951902 0.768584847 0.506392896 - outer loop - vertex -1.67776895 0.494724005 0.372043014 - vertex -1.75114 0.490709007 0.434781998 - vertex -2.04218507 0.828594983 0.146647006 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 0.828594983 0.146647006 - vertex -1.67776895 0.494724005 0.372043014 - vertex -2.04218507 0.828594983 0.146647006 - endloop - endfacet - - facet normal 0.482774973 0.633068919 0.605104983 - outer loop - vertex -1.67776895 0.494724005 0.372043014 - vertex -1.69004607 0.288253009 0.597850978 - vertex -1.75114 0.490709007 0.434781998 - endloop - endfacet - - facet normal 0.40646106 0.663174093 0.628482044 - outer loop - vertex -1.67776895 0.494724005 0.372043014 - vertex -1.57823491 0.290569007 0.523093998 - vertex -1.69004607 0.288253009 0.597850978 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 0.828594983 0.146647006 - vertex -2.04218507 0.828594983 0.146647006 - vertex -2.04218507 0.828594983 0.146647006 - endloop - endfacet - - facet normal -0.417207867 0.680008829 -0.602930844 - outer loop - vertex -1.45939696 0.361775994 0.521173 - vertex -1.56890297 0.382055014 0.619817972 - vertex -1.57823491 0.290569007 0.523093998 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 0.828594983 0.146647006 - vertex -1.67776895 0.494724005 0.372043014 - vertex -2.04218507 0.828594983 0.146647006 - endloop - endfacet - - facet normal -0.141779974 0.442905903 0.885286808 - outer loop - vertex -2.04218507 0.828594983 0.146647006 - vertex -1.61079192 0.552542984 0.353843004 - vertex -1.67776895 0.494724005 0.372043014 - endloop - endfacet - - facet normal -0.275092959 0.481556952 0.832121909 - outer loop - vertex -1.45939696 0.361775994 0.521173 - vertex -1.57823491 0.290569007 0.523093998 - vertex -1.67776895 0.494724005 0.372043014 - endloop - endfacet - - facet normal -0.234951034 0.528878033 0.815528095 - outer loop - vertex -1.61079192 0.552542984 0.353843004 - vertex -1.45939696 0.361775994 0.521173 - vertex -1.67776895 0.494724005 0.372043014 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 0.828594983 0.146647006 - vertex -2.04218507 0.828594983 0.146647006 - vertex -2.04218507 0.828594983 0.146647006 - endloop - endfacet - - facet normal -0.417209029 0.680007041 -0.602932036 - outer loop - vertex -1.42301798 0.448253989 0.593532026 - vertex -1.56890297 0.382055014 0.619817972 - vertex -1.45939696 0.361775994 0.521173 - endloop - endfacet - - facet normal -0.577504694 -0.348285824 0.738366604 - outer loop - vertex -1.60065103 0.620625973 0.39388901 - vertex -1.61079192 0.552542984 0.353843004 - vertex -2.04218507 0.828594983 0.146647006 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 0.828594983 0.146647006 - vertex -1.60065103 0.620625973 0.39388901 - vertex -2.04218507 0.828594983 0.146647006 - endloop - endfacet - - facet normal -0.825805068 -0.189439997 0.531185985 - outer loop - vertex -1.60065103 0.620625973 0.39388901 - vertex -1.45939696 0.361775994 0.521173 - vertex -1.61079192 0.552542984 0.353843004 - endloop - endfacet - - facet normal -0.799487293 -0.150301054 0.581575215 - outer loop - vertex -1.60065103 0.620625973 0.39388901 - vertex -1.42301798 0.448253989 0.593532026 - vertex -1.45939696 0.361775994 0.521173 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 0.828594983 0.146647006 - vertex -2.04218507 0.828594983 0.146647006 - vertex -2.04218507 0.828594983 0.146647006 - endloop - endfacet - - facet normal -0.417208076 0.680003166 -0.602937102 - outer loop - vertex -1.49649501 0.484885007 0.685687006 - vertex -1.56890297 0.382055014 0.619817972 - vertex -1.42301798 0.448253989 0.593532026 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 0.828594983 0.146647006 - vertex -1.60065103 0.620625973 0.39388901 - vertex -2.04218507 0.828594983 0.146647006 - endloop - endfacet - - facet normal -0.432327896 -0.901612759 0.0136729963 - outer loop - vertex -2.04218507 0.828594983 0.146647006 - vertex -1.65497303 0.647706985 0.462024987 - vertex -1.60065103 0.620625973 0.39388901 - endloop - endfacet - - facet normal -0.591237962 -0.791076005 -0.156959981 - outer loop - vertex -1.49649501 0.484885007 0.685687006 - vertex -1.42301798 0.448253989 0.593532026 - vertex -1.60065103 0.620625973 0.39388901 - endloop - endfacet - - facet normal -0.591241717 -0.791073561 -0.156957924 - outer loop - vertex -1.65497303 0.647706985 0.462024987 - vertex -1.49649501 0.484885007 0.685687006 - vertex -1.60065103 0.620625973 0.39388901 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 0.828594983 0.146647006 - vertex -2.04218507 0.828594983 0.146647006 - vertex -2.04218507 0.828594983 0.146647006 - endloop - endfacet - - facet normal -0.417217761 0.680004597 -0.602928638 - outer loop - vertex -1.62449408 0.444081008 0.728241026 - vertex -1.56890297 0.382055014 0.619817972 - vertex -1.49649501 0.484885007 0.685687006 - endloop - endfacet - - facet normal 0.0537490025 -0.836287022 -0.545651078 - outer loop - vertex -1.73286009 0.613394976 0.50694102 - vertex -1.65497303 0.647706985 0.462024987 - vertex -2.04218507 0.828594983 0.146647006 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 0.828594983 0.146647006 - vertex -1.73286009 0.613394976 0.50694102 - vertex -2.04218507 0.828594983 0.146647006 - endloop - endfacet - - facet normal 0.0118340012 -0.804398 -0.593973041 - outer loop - vertex -1.73286009 0.613394976 0.50694102 - vertex -1.49649501 0.484885007 0.685687006 - vertex -1.65497303 0.647706985 0.462024987 - endloop - endfacet - - facet normal 0.043387007 -0.783097148 -0.620384037 - outer loop - vertex -1.73286009 0.613394976 0.50694102 - vertex -1.62449408 0.444081008 0.728241026 - vertex -1.49649501 0.484885007 0.685687006 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 0.828594983 0.146647006 - vertex -2.04218507 0.828594983 0.146647006 - vertex -2.04218507 0.828594983 0.146647006 - endloop - endfacet - - facet normal -0.417210042 0.680010021 -0.602928042 - outer loop - vertex -1.71063101 0.356572986 0.689149976 - vertex -1.56890297 0.382055014 0.619817972 - vertex -1.62449408 0.444081008 0.728241026 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 0.828594983 0.146647006 - vertex -1.73286009 0.613394976 0.50694102 - vertex -2.04218507 0.828594983 0.146647006 - endloop - endfacet - - facet normal 0.644525826 -0.270723939 -0.71504885 - outer loop - vertex -2.04218507 0.828594983 0.146647006 - vertex -1.77565801 0.543522 0.494818002 - vertex -1.73286009 0.613394976 0.50694102 - endloop - endfacet - - facet normal 0.676752985 -0.386139005 -0.626819015 - outer loop - vertex -1.71063101 0.356572986 0.689149976 - vertex -1.62449408 0.444081008 0.728241026 - vertex -1.73286009 0.613394976 0.50694102 - endloop - endfacet - - facet normal 0.734697938 -0.349096984 -0.581678927 - outer loop - vertex -1.77565801 0.543522 0.494818002 - vertex -1.71063101 0.356572986 0.689149976 - vertex -1.73286009 0.613394976 0.50694102 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 0.828594983 0.146647006 - vertex -2.04218507 0.828594983 0.146647006 - vertex -2.04218507 0.828594983 0.146647006 - endloop - endfacet - - facet normal -0.417209774 0.680009604 -0.602928638 - outer loop - vertex -1.69004607 0.288253009 0.597850978 - vertex -1.56890297 0.382055014 0.619817972 - vertex -1.71063101 0.356572986 0.689149976 - endloop - endfacet - - facet normal 0.811764777 0.561054826 -0.162034959 - outer loop - vertex -1.75114 0.490709007 0.434781998 - vertex -1.77565801 0.543522 0.494818002 - vertex -2.04218507 0.828594983 0.146647006 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 0.828594983 0.146647006 - vertex -1.75114 0.490709007 0.434781998 - vertex -2.04218507 0.828594983 0.146647006 - endloop - endfacet - - facet normal 0.926234186 0.373678088 0.0495470129 - outer loop - vertex -1.75114 0.490709007 0.434781998 - vertex -1.71063101 0.356572986 0.689149976 - vertex -1.77565801 0.543522 0.494818002 - endloop - endfacet - - facet normal 0.957405746 0.28874594 -0.000203999953 - outer loop - vertex -1.75114 0.490709007 0.434781998 - vertex -1.69004607 0.288253009 0.597850978 - vertex -1.71063101 0.356572986 0.689149976 - endloop - endfacet - - facet normal -0.188931033 -0.0582350083 -0.980262101 - outer loop - vertex -1.06898999 0.00229800004 1.10110295 - vertex -1.10224402 0.110184997 1.10110295 - vertex -1.51577306 0.00229800004 1.18721402 - endloop - endfacet - - facet normal -0.184715971 -0.0741749927 -0.979988813 - outer loop - vertex -1.10224402 0.110184997 1.10110295 - vertex -1.21148205 0.382218003 1.10110295 - vertex -1.51577306 0.00229800004 1.18721402 - endloop - endfacet - - facet normal -0.0691840202 -0.258199126 0.963611364 - outer loop - vertex -1.51577306 0.250510991 0.26087001 - vertex -1.39166701 0.217256993 0.26087001 - vertex -1.51577306 0.00229800004 0.194362 - endloop - endfacet - - facet normal 0.0497390032 -0.258499026 -0.964730144 - outer loop - vertex -1.21148205 0.382218003 1.10110295 - vertex -1.51577306 0.250510991 1.12070501 - vertex -1.51577306 0.00229800004 1.18721402 - endloop - endfacet - - facet normal 0.86519593 -0.129780978 0.484347939 - outer loop - vertex -1.59023798 0.217256993 0.384977013 - vertex -1.51577306 0.250510991 0.26087001 - vertex -1.51577306 0.00229800004 0.194362 - endloop - endfacet - - facet normal 0.0691840202 -0.258199126 -0.963611364 - outer loop - vertex -1.51577306 0.250510991 1.12070501 - vertex -1.63987899 0.217256993 1.12070501 - vertex -1.51577306 0.00229800004 1.18721402 - endloop - endfacet - - facet normal 0.202385992 -0.60955894 0.766471028 - outer loop - vertex -1.68109 0.126405001 0.336712986 - vertex -1.59023798 0.217256993 0.384977013 - vertex -1.51577306 0.00229800004 0.194362 - endloop - endfacet - - facet normal 0.189015046 -0.189015046 -0.963611245 - outer loop - vertex -1.63987899 0.217256993 1.12070501 - vertex -1.73073196 0.126405001 1.12070501 - vertex -1.51577306 0.00229800004 1.18721402 - endloop - endfacet - - facet normal 0.57565999 -0.154247016 0.803008974 - outer loop - vertex -1.71434402 0.00229800004 0.336712986 - vertex -1.68109 0.126405001 0.336712986 - vertex -1.51577306 0.00229800004 0.194362 - endloop - endfacet - - facet normal 0.258199096 -0.069185026 -0.963611245 - outer loop - vertex -1.73073196 0.126405001 1.12070501 - vertex -1.76398599 0.00229800004 1.12070501 - vertex -1.51577306 0.00229800004 1.18721402 - endloop - endfacet - - facet normal 0.202413991 -0.788102925 0.581310928 - outer loop - vertex -1.39166701 0.217256993 0.26087001 - vertex -1.30081499 0.37461701 0.442575008 - vertex -1.16183996 0.410311013 0.442575008 - endloop - endfacet - - facet normal -0.0857829452 -0.731517494 0.676404655 - outer loop - vertex -1.51577306 0.489080012 0.539102018 - vertex -1.30081499 0.37461701 0.442575008 - vertex -1.39166701 0.217256993 0.26087001 - endloop - endfacet - - facet normal -0.199330047 -0.743908167 0.637862206 - outer loop - vertex -1.51577306 0.250510991 0.26087001 - vertex -1.51577306 0.489080012 0.539102018 - vertex -1.39166701 0.217256993 0.26087001 - endloop - endfacet - - facet normal 0.818344295 -0.436300159 0.374105155 - outer loop - vertex -1.59023798 0.217256993 0.384977013 - vertex -1.51577306 0.489080012 0.539102018 - vertex -1.51577306 0.250510991 0.26087001 - endloop - endfacet - - facet normal -0.151083976 -0.455930918 0.877097785 - outer loop - vertex -1.59023798 0.217256993 0.384977013 - vertex -1.73073196 0.37461701 0.442575008 - vertex -1.51577306 0.489080012 0.539102018 - endloop - endfacet - - facet normal 0.186527073 -0.186527073 0.964580357 - outer loop - vertex -1.88809204 0.217256993 0.442575008 - vertex -1.73073196 0.37461701 0.442575008 - vertex -1.59023798 0.217256993 0.384977013 - endloop - endfacet - - facet normal 0.154704109 -0.579695344 0.800012469 - outer loop - vertex -1.68109 0.126405001 0.336712986 - vertex -1.88809204 0.217256993 0.442575008 - vertex -1.59023798 0.217256993 0.384977013 - endloop - endfacet - - facet normal 0.413534015 -0.110806011 0.903721035 - outer loop - vertex -1.71434402 0.00229800004 0.336712986 - vertex -1.88809204 0.217256993 0.442575008 - vertex -1.68109 0.126405001 0.336712986 - endloop - endfacet - - facet normal 0.30303511 -0.212556094 0.928972363 - outer loop - vertex -1.71434402 0.00229800004 0.336712986 - vertex -1.93741703 0.00229800004 0.409480006 - vertex -1.88809204 0.217256993 0.442575008 - endloop - endfacet - - facet normal -0.839146435 -0.156273901 0.520971715 - outer loop - vertex -1.16183996 0.410311013 0.442575008 - vertex -0.820776999 0.00229800004 0.869545996 - vertex -1.08585596 0.00229800004 0.442575008 - endloop - endfacet - - facet normal -0.844083071 -0.17527701 0.506756067 - outer loop - vertex -1.16183996 0.410311013 0.442575008 - vertex -0.905672014 0.411127001 0.869545996 - vertex -0.820776999 0.00229800004 0.869545996 - endloop - endfacet - - facet normal -0.610179067 -0.701910973 0.367426991 - outer loop - vertex -1.087376 0.569083989 0.869545996 - vertex -0.905672014 0.411127001 0.869545996 - vertex -1.16183996 0.410311013 0.442575008 - endloop - endfacet - - facet normal 0.237148076 -0.923342288 0.301993102 - outer loop - vertex -1.30081499 0.37461701 0.442575008 - vertex -1.087376 0.569083989 0.869545996 - vertex -1.16183996 0.410311013 0.442575008 - endloop - endfacet - - facet normal -0.22484003 -0.839457035 0.494731098 - outer loop - vertex -1.51577306 0.489080012 0.539102018 - vertex -1.087376 0.569083989 0.869545996 - vertex -1.30081499 0.37461701 0.442575008 - endloop - endfacet - - facet normal 0.136153921 -0.988691449 0.0628579706 - outer loop - vertex -1.51577306 0.489080012 0.539102018 - vertex -1.51577306 0.498724014 0.690787971 - vertex -1.087376 0.569083989 0.869545996 - endloop - endfacet - - facet normal 0.379295141 -0.923411429 0.0587080233 - outer loop - vertex -1.82231998 0.370554 0.655328989 - vertex -1.51577306 0.498724014 0.690787971 - vertex -1.51577306 0.489080012 0.539102018 - endloop - endfacet - - facet normal 0.40773806 -0.899263144 0.158352017 - outer loop - vertex -1.73073196 0.37461701 0.442575008 - vertex -1.82231998 0.370554 0.655328989 - vertex -1.51577306 0.489080012 0.539102018 - endloop - endfacet - - facet normal 0.678963184 -0.678963184 0.279317051 - outer loop - vertex -1.88809204 0.217256993 0.442575008 - vertex -1.82231998 0.370554 0.655328989 - vertex -1.73073196 0.37461701 0.442575008 - endloop - endfacet - - facet normal 0.852383077 -0.512161076 0.105518013 - outer loop - vertex -1.88809204 0.217256993 0.442575008 - vertex -1.95466805 0.150288999 0.655328989 - vertex -1.82231998 0.370554 0.655328989 - endloop - endfacet - - facet normal 0.871156156 -0.475356132 0.122977026 - outer loop - vertex -2.02874589 0.00229800004 0.608051002 - vertex -1.95466805 0.150288999 0.655328989 - vertex -1.88809204 0.217256993 0.442575008 - endloop - endfacet - - facet normal 0.876484156 -0.263182074 0.403126091 - outer loop - vertex -1.93741703 0.00229800004 0.409480006 - vertex -2.02874589 0.00229800004 0.608051002 - vertex -1.88809204 0.217256993 0.442575008 - endloop - endfacet - - facet normal -0.881306171 -0.176261023 -0.438442081 - outer loop - vertex -0.963268995 0.382218003 1.00323403 - vertex -0.887284994 0.00229800004 1.00323403 - vertex -0.820776999 0.00229800004 0.869545996 - endloop - endfacet - - facet normal -0.887549043 -0.184303015 -0.422242999 - outer loop - vertex -0.905672014 0.411127001 0.869545996 - vertex -0.963268995 0.382218003 1.00323403 - vertex -0.820776999 0.00229800004 0.869545996 - endloop - endfacet - - facet normal -0.599210024 -0.689293087 -0.407213032 - outer loop - vertex -1.087376 0.569083989 0.869545996 - vertex -0.963268995 0.382218003 1.00323403 - vertex -0.905672014 0.411127001 0.869545996 - endloop - endfacet - - facet normal -0.599209309 -0.689293444 -0.407213241 - outer loop - vertex -1.087376 0.569083989 0.869545996 - vertex -1.12063003 0.519014001 1.00323403 - vertex -0.963268995 0.382218003 1.00323403 - endloop - endfacet - - facet normal 0.249579027 -0.925593197 -0.284583032 - outer loop - vertex -1.51577306 0.432215005 0.939001024 - vertex -1.12063003 0.519014001 1.00323403 - vertex -1.087376 0.569083989 0.869545996 - endloop - endfacet - - facet normal 0.257640004 -0.933317065 -0.250081986 - outer loop - vertex -1.51577306 0.498724014 0.690787971 - vertex -1.51577306 0.432215005 0.939001024 - vertex -1.087376 0.569083989 0.869545996 - endloop - endfacet - - facet normal 0.397967875 -0.88613981 -0.23743996 - outer loop - vertex -1.82231998 0.370554 0.655328989 - vertex -1.51577306 0.432215005 0.939001024 - vertex -1.51577306 0.498724014 0.690787971 - endloop - endfacet - - facet normal 0.404057056 -0.881292224 -0.245075062 - outer loop - vertex -1.82231998 0.370554 0.655328989 - vertex -1.77611899 0.322712004 0.903541982 - vertex -1.51577306 0.432215005 0.939001024 - endloop - endfacet - - facet normal 0.894504726 -0.365525872 -0.25739491 - outer loop - vertex -2.02874589 0.00229800004 0.608051002 - vertex -1.89073491 0.131956995 0.903541982 - vertex -1.95466805 0.150288999 0.655328989 - endloop - endfacet - - facet normal 0.875202715 -0.431018859 -0.219642922 - outer loop - vertex -2.02874589 0.00229800004 0.608051002 - vertex -1.94569099 0.00229800004 0.939001024 - vertex -1.89073491 0.131956995 0.903541982 - endloop - endfacet - - facet normal -0.472085088 -0.0944170132 -0.876482189 - outer loop - vertex -0.963268995 0.382218003 1.00323403 - vertex -1.06898999 0.00229800004 1.10110295 - vertex -0.887284994 0.00229800004 1.00323403 - endloop - endfacet - - facet normal -0.399085015 -0.123011015 -0.908625066 - outer loop - vertex -0.963268995 0.382218003 1.00323403 - vertex -1.10224402 0.110184997 1.10110295 - vertex -1.06898999 0.00229800004 1.10110295 - endloop - endfacet - - facet normal -0.362892926 -0.145723984 -0.92036581 - outer loop - vertex -1.21148205 0.382218003 1.10110295 - vertex -1.10224402 0.110184997 1.10110295 - vertex -0.963268995 0.382218003 1.00323403 - endloop - endfacet - - facet normal -0.337955117 -0.388762146 -0.857117355 - outer loop - vertex -1.12063003 0.519014001 1.00323403 - vertex -1.21148205 0.382218003 1.10110295 - vertex -0.963268995 0.382218003 1.00323403 - endloop - endfacet - - facet normal 0.260202944 -0.670137882 -0.695132792 - outer loop - vertex -1.51577306 0.432215005 0.939001024 - vertex -1.21148205 0.382218003 1.10110295 - vertex -1.12063003 0.519014001 1.00323403 - endloop - endfacet - - facet normal 0.25209403 -0.68426919 -0.68426919 - outer loop - vertex -1.51577306 0.432215005 0.939001024 - vertex -1.51577306 0.250510991 1.12070501 - vertex -1.21148205 0.382218003 1.10110295 - endloop - endfacet - - facet normal 0.186157033 -0.694746077 -0.694747031 - outer loop - vertex -1.63987899 0.217256993 1.12070501 - vertex -1.51577306 0.250510991 1.12070501 - vertex -1.51577306 0.432215005 0.939001024 - endloop - endfacet - - facet normal 0.380661011 -0.715228021 -0.586127996 - outer loop - vertex -1.77611899 0.322712004 0.903541982 - vertex -1.63987899 0.217256993 1.12070501 - vertex -1.51577306 0.432215005 0.939001024 - endloop - endfacet - - facet normal 0.673306763 -0.404560834 -0.618860781 - outer loop - vertex -1.89073491 0.131956995 0.903541982 - vertex -1.63987899 0.217256993 1.12070501 - vertex -1.77611899 0.322712004 0.903541982 - endloop - endfacet - - facet normal 0.622429729 -0.622429729 -0.474512786 - outer loop - vertex -1.89073491 0.131956995 0.903541982 - vertex -1.73073196 0.126405001 1.12070501 - vertex -1.63987899 0.217256993 1.12070501 - endloop - endfacet - - facet normal 0.784513175 -0.210212052 -0.583395123 - outer loop - vertex -1.76398599 0.00229800004 1.12070501 - vertex -1.73073196 0.126405001 1.12070501 - vertex -1.89073491 0.131956995 0.903541982 - endloop - endfacet - - facet normal 0.634200931 -0.442243934 -0.634199917 - outer loop - vertex -1.94569099 0.00229800004 0.939001024 - vertex -1.76398599 0.00229800004 1.12070501 - vertex -1.89073491 0.131956995 0.903541982 - endloop - endfacet - - facet normal -0.521937072 0.820092142 0.23458603 - outer loop - vertex -1.86046696 0.186704993 0.72092402 - vertex -1.861866 0.181822002 0.734879017 - vertex -1.90136802 0.159713998 0.72427702 - endloop - endfacet - - facet normal 0.561335981 0.0371459983 -0.826753974 - outer loop - vertex -1.86046696 0.186704993 0.72092402 - vertex -1.88625693 0.190698996 0.703593016 - vertex -1.95466805 0.150288999 0.655328989 - endloop - endfacet - - facet normal 0.511620045 -0.810681105 -0.284677029 - outer loop - vertex -1.95466805 0.150288999 0.655328989 - vertex -1.90136802 0.159713998 0.72427702 - vertex -1.86046696 0.186704993 0.72092402 - endloop - endfacet - - facet normal 0.675249338 -0.414224237 -0.610292375 - outer loop - vertex -1.95466805 0.150288999 0.655328989 - vertex -1.88625693 0.190698996 0.703593016 - vertex -1.862077 0.240274996 0.69669801 - endloop - endfacet - - facet normal 0.672121286 -0.413267165 -0.6143803 - outer loop - vertex -1.84992504 0.223467007 0.721297979 - vertex -1.862077 0.240274996 0.69669801 - vertex -1.88625693 0.190698996 0.703593016 - endloop - endfacet - - facet normal 0.536241055 -0.145309016 -0.831463099 - outer loop - vertex -1.88625693 0.190698996 0.703593016 - vertex -1.86046696 0.186704993 0.72092402 - vertex -1.84992504 0.223467007 0.721297979 - endloop - endfacet - - facet normal 0.895058095 -0.00553400069 -0.445915073 - outer loop - vertex -1.862077 0.240274996 0.69669801 - vertex -1.84992504 0.223467007 0.721297979 - vertex -1.83589005 0.272417009 0.748862982 - endloop - endfacet - - facet normal 0.914936006 -0.290188998 -0.280503988 - outer loop - vertex -1.83589005 0.272417009 0.748862982 - vertex -1.83185291 0.302246004 0.731172025 - vertex -1.862077 0.240274996 0.69669801 - endloop - endfacet - - facet normal 0.968834221 -0.246922046 -0.0197400041 - outer loop - vertex -1.82278597 0.320836991 0.786329985 - vertex -1.83589005 0.272417009 0.748862982 - vertex -1.82824898 0.299062014 0.790558994 - endloop - endfacet - - facet normal 0.977088094 -0.189592034 -0.0967150182 - outer loop - vertex -1.83589005 0.272417009 0.748862982 - vertex -1.82278597 0.320836991 0.786329985 - vertex -1.83185291 0.302246004 0.731172025 - endloop - endfacet - - facet normal 0.997900486 -0.059398029 0.0258140136 - outer loop - vertex -1.82883 0.30844301 0.834594011 - vertex -1.82824898 0.299062014 0.790558994 - vertex -1.82945108 0.294871002 0.827350974 - endloop - endfacet - - facet normal 0.970842361 -0.231529072 0.0621240176 - outer loop - vertex -1.82883 0.30844301 0.834594011 - vertex -1.82278597 0.320836991 0.786329985 - vertex -1.82824898 0.299062014 0.790558994 - endloop - endfacet - - facet normal 0.980895996 -0.124577008 0.149410993 - outer loop - vertex -1.82945108 0.294871002 0.827350974 - vertex -1.83534694 0.274305999 0.848914027 - vertex -1.82883 0.30844301 0.834594011 - endloop - endfacet - - facet normal 0.919196248 -0.0103330025 0.393664122 - outer loop - vertex -1.83534694 0.274305999 0.848914027 - vertex -1.84091997 0.283654988 0.862173021 - vertex -1.82883 0.30844301 0.834594011 - endloop - endfacet - - facet normal 0.793512285 -0.287576079 0.536319196 - outer loop - vertex -1.83534694 0.274305999 0.848914027 - vertex -1.859056 0.246472001 0.869068027 - vertex -1.84091997 0.283654988 0.862173021 - endloop - endfacet - - facet normal 0.804793894 -0.326704979 0.495550931 - outer loop - vertex -1.859056 0.246472001 0.869068027 - vertex -1.83534694 0.274305999 0.848914027 - vertex -1.84234202 0.249915004 0.844193995 - endloop - endfacet - - facet normal 0.715217113 -0.572144091 0.401392102 - outer loop - vertex -1.84234202 0.249915004 0.844193995 - vertex -1.87719107 0.209290996 0.848384023 - vertex -1.859056 0.246472001 0.869068027 - endloop - endfacet - - facet normal 0.653585851 -0.502279878 0.566162825 - outer loop - vertex -1.87719107 0.209290996 0.848384023 - vertex -1.84234202 0.249915004 0.844193995 - vertex -1.84963393 0.224481001 0.830048978 - endloop - endfacet - - facet normal 0.553774893 -0.8182289 0.154384971 - outer loop - vertex -1.87719107 0.209290996 0.848384023 - vertex -1.84963393 0.224481001 0.830048978 - vertex -1.851632 0.217520997 0.800320983 - endloop - endfacet - - facet normal 0.600953341 -0.777222455 0.18649511 - outer loop - vertex -1.851632 0.217520997 0.800320983 - vertex -1.88625693 0.190698996 0.800119996 - vertex -1.87719107 0.209290996 0.848384023 - endloop - endfacet - - facet normal 0.597519338 -0.772989392 0.213209108 - outer loop - vertex -1.88625693 0.190698996 0.800119996 - vertex -1.851632 0.217520997 0.800320983 - vertex -1.85507607 0.205509007 0.766424 - endloop - endfacet - - facet normal 0.664247036 -0.676876068 0.317198038 - outer loop - vertex -1.85507607 0.205509007 0.766424 - vertex -1.89834702 0.165911004 0.772539973 - vertex -1.88625693 0.190698996 0.800119996 - endloop - endfacet - - facet normal 0.657633066 -0.663550079 0.356679052 - outer loop - vertex -1.89834702 0.165911004 0.772539973 - vertex -1.85507607 0.205509007 0.766424 - vertex -1.861866 0.181822002 0.734879017 - endloop - endfacet - - facet normal 0.469525933 -0.87896502 0.0834619924 - outer loop - vertex -1.861866 0.181822002 0.734879017 - vertex -1.90136802 0.159713998 0.72427702 - vertex -1.89834702 0.165911004 0.772539973 - endloop - endfacet - - facet normal 0.373062909 -0.913278759 -0.163541958 - outer loop - vertex -1.95466805 0.150288999 0.655328989 - vertex -1.89073491 0.131956995 0.903541982 - vertex -1.90136802 0.159713998 0.72427702 - endloop - endfacet - - facet normal 0.998070359 -0.0116050038 -0.0609980263 - outer loop - vertex -1.89073491 0.131956995 0.903541982 - vertex -1.89834702 0.165911004 0.772539973 - vertex -1.90136802 0.159713998 0.72427702 - endloop - endfacet - - facet normal 0.941413999 -0.30913201 -0.13482298 - outer loop - vertex -1.89073491 0.131956995 0.903541982 - vertex -1.88625693 0.190698996 0.800119996 - vertex -1.89834702 0.165911004 0.772539973 - endloop - endfacet - - facet normal 0.967765391 -0.234683871 -0.0913969502 - outer loop - vertex -1.89073491 0.131956995 0.903541982 - vertex -1.87719107 0.209290996 0.848384023 - vertex -1.88625693 0.190698996 0.800119996 - endloop - endfacet - - facet normal 0.919147134 -0.322406024 -0.22632502 - outer loop - vertex -1.89073491 0.131956995 0.903541982 - vertex -1.859056 0.246472001 0.869068027 - vertex -1.87719107 0.209290996 0.848384023 - endloop - endfacet - - facet normal 0.629824102 -0.378435016 -0.678313076 - outer loop - vertex -1.859056 0.246472001 0.869068027 - vertex -1.89073491 0.131956995 0.903541982 - vertex -1.77611899 0.322712004 0.903541982 - endloop - endfacet - - facet normal 0.655910254 -0.434390157 -0.617322266 - outer loop - vertex -1.77611899 0.322712004 0.903541982 - vertex -1.84091997 0.283654988 0.862173021 - vertex -1.859056 0.246472001 0.869068027 - endloop - endfacet - - facet normal 0.635727286 -0.691575289 -0.342891186 - outer loop - vertex -1.77611899 0.322712004 0.903541982 - vertex -1.82883 0.30844301 0.834594011 - vertex -1.84091997 0.283654988 0.862173021 - endloop - endfacet - - facet normal 0.669934034 -0.402535051 -0.623822033 - outer loop - vertex -1.82231998 0.370554 0.655328989 - vertex -1.95466805 0.150288999 0.655328989 - vertex -1.862077 0.240274996 0.69669801 - endloop - endfacet - - facet normal 0.919927955 -0.341761976 -0.192174986 - outer loop - vertex -1.82231998 0.370554 0.655328989 - vertex -1.862077 0.240274996 0.69669801 - vertex -1.83185291 0.302246004 0.731172025 - endloop - endfacet - - facet normal 0.970269501 -0.227404878 -0.082849957 - outer loop - vertex -1.82231998 0.370554 0.655328989 - vertex -1.83185291 0.302246004 0.731172025 - vertex -1.82278597 0.320836991 0.786329985 - endloop - endfacet - - facet normal 0.549644947 -0.795333922 -0.255605966 - outer loop - vertex -1.82883 0.30844301 0.834594011 - vertex -1.77611899 0.322712004 0.903541982 - vertex -1.82231998 0.370554 0.655328989 - endloop - endfacet - - facet normal -0.685612202 -0.679787159 -0.260433048 - outer loop - vertex -1.82231998 0.370554 0.655328989 - vertex -1.82883 0.30844301 0.834594011 - vertex -1.82278597 0.320836991 0.786329985 - endloop - endfacet - - facet normal 0.0977169722 0.666159749 0.739379823 - outer loop - vertex 0.0900690034 -0.167228997 -0.494951993 - vertex -0.233263001 -0.437810004 -0.208434999 - vertex -0.584930003 -0.284505993 -0.300080001 - endloop - endfacet - - facet normal -0.169718057 0.339002132 0.925350368 - outer loop - vertex 0.460438013 -0.389086008 -0.345746011 - vertex 0.0900690034 -0.167228997 -0.494951993 - vertex 0.457304001 0 -0.488862008 - endloop - endfacet - - facet normal 0.0998720229 0.664684117 0.740419149 - outer loop - vertex 0.460438013 -0.389086008 -0.345746011 - vertex -0.233263001 -0.437810004 -0.208434999 - vertex 0.0900690034 -0.167228997 -0.494951993 - endloop - endfacet - - facet normal 0.220944032 0.28046003 0.934091032 - outer loop - vertex -0.610705018 0 -0.379406005 - vertex 0.0900690034 -0.167228997 -0.494951993 - vertex -0.584930003 -0.284505993 -0.300080001 - endloop - endfacet - - facet normal 0.220944032 -0.28046003 0.934091032 - outer loop - vertex 0.0900690034 0.167228997 -0.494951993 - vertex -0.610705018 0 -0.379406005 - vertex -0.584930003 0.284505993 -0.300080001 - endloop - endfacet - - facet normal -0.0165820085 0 0.999862492 - outer loop - vertex 0.0900690034 -0.167228997 -0.494951993 - vertex 0.0900690034 0.167228997 -0.494951993 - vertex 0.457304001 0 -0.488862008 - endloop - endfacet - - facet normal 0.162686959 0 0.986677706 - outer loop - vertex 0.0900690034 0.167228997 -0.494951993 - vertex 0.0900690034 -0.167228997 -0.494951993 - vertex -0.610705018 0 -0.379406005 - endloop - endfacet - - facet normal -0.190478116 0.247609138 0.94995153 - outer loop - vertex -1.14137506 -0.292008013 -0.409700006 - vertex -0.610705018 0 -0.379406005 - vertex -0.584930003 -0.284505993 -0.300080001 - endloop - endfacet - - facet normal 0.699706972 0 0.714430034 - outer loop - vertex -1.14137506 0.292008013 -0.409700006 - vertex -1.14137506 -0.292008013 -0.409700006 - vertex -1.41845703 0 -0.138327003 - endloop - endfacet - - facet normal -0.190478116 -0.247609138 0.94995153 - outer loop - vertex -0.610705018 0 -0.379406005 - vertex -1.14137506 0.292008013 -0.409700006 - vertex -0.584930003 0.284505993 -0.300080001 - endloop - endfacet - - facet normal -0.0569919795 0 0.998374581 - outer loop - vertex -1.14137506 0.292008013 -0.409700006 - vertex -0.610705018 0 -0.379406005 - vertex -1.14137506 -0.292008013 -0.409700006 - endloop - endfacet - - facet normal 0.802316129 -0.263622016 0.535530031 - outer loop - vertex -1.33845997 0.472478002 -0.0255929995 - vertex -1.14137506 0.292008013 -0.409700006 - vertex -1.41845703 0 -0.138327003 - endloop - endfacet - - facet normal -0.193044886 -0.149941906 0.969665408 - outer loop - vertex -1.14137506 0.292008013 -0.409700006 - vertex -0.903297007 0.764486015 -0.289241999 - vertex -0.584930003 0.284505993 -0.300080001 - endloop - endfacet - - facet normal 0.667306721 -0.481181771 0.568476737 - outer loop - vertex -0.903297007 0.764486015 -0.289241999 - vertex -1.14137506 0.292008013 -0.409700006 - vertex -1.33845997 0.472478002 -0.0255929995 - endloop - endfacet - - facet normal 0.0977169722 -0.666159749 0.739379823 - outer loop - vertex -0.233263001 0.437810004 -0.208434999 - vertex 0.0900690034 0.167228997 -0.494951993 - vertex -0.584930003 0.284505993 -0.300080001 - endloop - endfacet - - facet normal -0.169718057 -0.339002132 0.925350368 - outer loop - vertex 0.0900690034 0.167228997 -0.494951993 - vertex 0.460438013 0.389086008 -0.345746011 - vertex 0.457304001 0 -0.488862008 - endloop - endfacet - - facet normal 0.0998720229 -0.664684117 0.740419149 - outer loop - vertex 0.460438013 0.389086008 -0.345746011 - vertex 0.0900690034 0.167228997 -0.494951993 - vertex -0.233263001 0.437810004 -0.208434999 - endloop - endfacet - - facet normal -0.193044886 0.149941906 0.969665408 - outer loop - vertex -0.903297007 -0.764486015 -0.289241999 - vertex -1.14137506 -0.292008013 -0.409700006 - vertex -0.584930003 -0.284505993 -0.300080001 - endloop - endfacet - - facet normal 0.802316129 0.263622016 0.535530031 - outer loop - vertex -1.14137506 -0.292008013 -0.409700006 - vertex -1.33845997 -0.472478002 -0.0255929995 - vertex -1.41845703 0 -0.138327003 - endloop - endfacet - - facet normal 0.667307138 0.481182069 0.568476081 - outer loop - vertex -1.33845997 -0.472478002 -0.0255929995 - vertex -1.14137506 -0.292008013 -0.409700006 - vertex -0.903297007 -0.764486015 -0.289241999 - endloop - endfacet - - facet normal -0.188931033 0.0582350083 -0.980262101 - outer loop - vertex -1.51577306 0.00046000001 1.18721402 - vertex -1.10224402 -0.107427001 1.10110295 - vertex -1.06898999 0.00046000001 1.10110295 - endloop - endfacet - - facet normal -0.184715971 0.0741749927 -0.979988813 - outer loop - vertex -1.51577306 0.00046000001 1.18721402 - vertex -1.21148205 -0.379460007 1.10110295 - vertex -1.10224402 -0.107427001 1.10110295 - endloop - endfacet - - facet normal -0.0691840202 0.258199126 0.963611364 - outer loop - vertex -1.51577306 0.00046000001 0.194362 - vertex -1.39166701 -0.214498997 0.26087001 - vertex -1.51577306 -0.247752994 0.26087001 - endloop - endfacet - - facet normal 0.0497390032 0.258499026 -0.964730144 - outer loop - vertex -1.51577306 0.00046000001 1.18721402 - vertex -1.51577306 -0.247752994 1.12070501 - vertex -1.21148205 -0.379460007 1.10110295 - endloop - endfacet - - facet normal 0.86519593 0.129780978 0.484347939 - outer loop - vertex -1.51577306 0.00046000001 0.194362 - vertex -1.51577306 -0.247752994 0.26087001 - vertex -1.59023798 -0.214498997 0.384977013 - endloop - endfacet - - facet normal 0.0691840202 0.258199126 -0.963611364 - outer loop - vertex -1.51577306 0.00046000001 1.18721402 - vertex -1.63987899 -0.214498997 1.12070501 - vertex -1.51577306 -0.247752994 1.12070501 - endloop - endfacet - - facet normal 0.202385992 0.60955894 0.766471028 - outer loop - vertex -1.51577306 0.00046000001 0.194362 - vertex -1.59023798 -0.214498997 0.384977013 - vertex -1.68109 -0.123646997 0.336712986 - endloop - endfacet - - facet normal 0.189015046 0.189015046 -0.963611245 - outer loop - vertex -1.51577306 0.00046000001 1.18721402 - vertex -1.73073196 -0.123646997 1.12070501 - vertex -1.63987899 -0.214498997 1.12070501 - endloop - endfacet - - facet normal 0.57565999 0.154247016 0.803008974 - outer loop - vertex -1.51577306 0.00046000001 0.194362 - vertex -1.68109 -0.123646997 0.336712986 - vertex -1.71434402 0.00046000001 0.336712986 - endloop - endfacet - - facet normal 0.258199096 0.069185026 -0.963611245 - outer loop - vertex -1.51577306 0.00046000001 1.18721402 - vertex -1.76398599 0.00046000001 1.12070501 - vertex -1.73073196 -0.123646997 1.12070501 - endloop - endfacet - - facet normal 0.202413991 0.788102925 0.581310928 - outer loop - vertex -1.16183996 -0.407552987 0.442575008 - vertex -1.30081499 -0.371859998 0.442575008 - vertex -1.39166701 -0.214498997 0.26087001 - endloop - endfacet - - facet normal -0.0857829452 0.731517494 0.676404655 - outer loop - vertex -1.39166701 -0.214498997 0.26087001 - vertex -1.30081499 -0.371859998 0.442575008 - vertex -1.51577306 -0.486321986 0.539102018 - endloop - endfacet - - facet normal -0.199329078 0.743908286 0.637862265 - outer loop - vertex -1.39166701 -0.214498997 0.26087001 - vertex -1.51577306 -0.486321986 0.539102018 - vertex -1.51577306 -0.247752994 0.26087001 - endloop - endfacet - - facet normal 0.818344295 0.436300159 0.374105155 - outer loop - vertex -1.51577306 -0.247752994 0.26087001 - vertex -1.51577306 -0.486321986 0.539102018 - vertex -1.59023798 -0.214498997 0.384977013 - endloop - endfacet - - facet normal -0.151083976 0.455930918 0.877097785 - outer loop - vertex -1.51577306 -0.486321986 0.539102018 - vertex -1.73073196 -0.371859998 0.442575008 - vertex -1.59023798 -0.214498997 0.384977013 - endloop - endfacet - - facet normal 0.186527073 0.186527073 0.964580357 - outer loop - vertex -1.59023798 -0.214498997 0.384977013 - vertex -1.73073196 -0.371859998 0.442575008 - vertex -1.88809204 -0.214498997 0.442575008 - endloop - endfacet - - facet normal 0.154704019 0.579696059 0.800012052 - outer loop - vertex -1.59023798 -0.214498997 0.384977013 - vertex -1.88809204 -0.214498997 0.442575008 - vertex -1.68109 -0.123646997 0.336712986 - endloop - endfacet - - facet normal 0.413534015 0.110806011 0.903721035 - outer loop - vertex -1.68109 -0.123646997 0.336712986 - vertex -1.88809204 -0.214498997 0.442575008 - vertex -1.71434402 0.00046000001 0.336712986 - endloop - endfacet - - facet normal 0.30303511 0.212556094 0.928972363 - outer loop - vertex -1.88809204 -0.214498997 0.442575008 - vertex -1.93741703 0.00046000001 0.409480006 - vertex -1.71434402 0.00046000001 0.336712986 - endloop - endfacet - - facet normal -0.839146435 0.156273901 0.520971715 - outer loop - vertex -1.08585596 0.00046000001 0.442575008 - vertex -0.820776999 0.00046000001 0.869545996 - vertex -1.16183996 -0.407552987 0.442575008 - endloop - endfacet - - facet normal -0.844083071 0.17527701 0.506756067 - outer loop - vertex -0.820776999 0.00046000001 0.869545996 - vertex -0.905672014 -0.408369005 0.869545996 - vertex -1.16183996 -0.407552987 0.442575008 - endloop - endfacet - - facet normal -0.610179067 0.701910973 0.367426991 - outer loop - vertex -1.16183996 -0.407552987 0.442575008 - vertex -0.905672014 -0.408369005 0.869545996 - vertex -1.087376 -0.566326022 0.869545996 - endloop - endfacet - - facet normal 0.237148017 0.92334199 0.301994026 - outer loop - vertex -1.16183996 -0.407552987 0.442575008 - vertex -1.087376 -0.566326022 0.869545996 - vertex -1.30081499 -0.371859998 0.442575008 - endloop - endfacet - - facet normal -0.22484003 0.839457035 0.494731098 - outer loop - vertex -1.30081499 -0.371859998 0.442575008 - vertex -1.087376 -0.566326022 0.869545996 - vertex -1.51577306 -0.486321986 0.539102018 - endloop - endfacet - - facet normal 0.136153921 0.988691449 0.0628579706 - outer loop - vertex -1.087376 -0.566326022 0.869545996 - vertex -1.51577306 -0.495965987 0.690787971 - vertex -1.51577306 -0.486321986 0.539102018 - endloop - endfacet - - facet normal 0.379295141 0.923411429 0.0587080233 - outer loop - vertex -1.51577306 -0.486321986 0.539102018 - vertex -1.51577306 -0.495965987 0.690787971 - vertex -1.82231998 -0.367796004 0.655328989 - endloop - endfacet - - facet normal 0.40773806 0.899263144 0.158352017 - outer loop - vertex -1.51577306 -0.486321986 0.539102018 - vertex -1.82231998 -0.367796004 0.655328989 - vertex -1.73073196 -0.371859998 0.442575008 - endloop - endfacet - - facet normal 0.678963006 0.678963006 0.279318005 - outer loop - vertex -1.73073196 -0.371859998 0.442575008 - vertex -1.82231998 -0.367796004 0.655328989 - vertex -1.88809204 -0.214498997 0.442575008 - endloop - endfacet - - facet normal 0.852383077 0.512161076 0.105518013 - outer loop - vertex -1.82231998 -0.367796004 0.655328989 - vertex -1.95466805 -0.147531003 0.655328989 - vertex -1.88809204 -0.214498997 0.442575008 - endloop - endfacet - - facet normal 0.871156156 0.475356132 0.122977026 - outer loop - vertex -1.88809204 -0.214498997 0.442575008 - vertex -1.95466805 -0.147531003 0.655328989 - vertex -2.02874589 0.00046000001 0.608051002 - endloop - endfacet - - facet normal 0.876483917 0.263182968 0.403125972 - outer loop - vertex -1.88809204 -0.214498997 0.442575008 - vertex -2.02874589 0.00046000001 0.608051002 - vertex -1.93741703 0.00046000001 0.409480006 - endloop - endfacet - - facet normal -0.881306171 0.176261023 -0.438442081 - outer loop - vertex -0.820776999 0.00046000001 0.869545996 - vertex -0.887284994 0.00046000001 1.00323403 - vertex -0.963268995 -0.379460007 1.00323403 - endloop - endfacet - - facet normal -0.887549043 0.184303015 -0.422242999 - outer loop - vertex -0.820776999 0.00046000001 0.869545996 - vertex -0.963268995 -0.379460007 1.00323403 - vertex -0.905672014 -0.408369005 0.869545996 - endloop - endfacet - - facet normal -0.599210024 0.689293087 -0.407213032 - outer loop - vertex -0.905672014 -0.408369005 0.869545996 - vertex -0.963268995 -0.379460007 1.00323403 - vertex -1.087376 -0.566326022 0.869545996 - endloop - endfacet - - facet normal -0.59920913 0.689293206 -0.407214105 - outer loop - vertex -0.963268995 -0.379460007 1.00323403 - vertex -1.12063003 -0.516255975 1.00323403 - vertex -1.087376 -0.566326022 0.869545996 - endloop - endfacet - - facet normal 0.249578983 0.925593019 -0.284583956 - outer loop - vertex -1.087376 -0.566326022 0.869545996 - vertex -1.12063003 -0.516255975 1.00323403 - vertex -1.51577306 -0.429457009 0.939001024 - endloop - endfacet - - facet normal 0.257640928 0.933316827 -0.250081927 - outer loop - vertex -1.087376 -0.566326022 0.869545996 - vertex -1.51577306 -0.429457009 0.939001024 - vertex -1.51577306 -0.495965987 0.690787971 - endloop - endfacet - - facet normal 0.397967786 0.886139572 -0.237440884 - outer loop - vertex -1.51577306 -0.495965987 0.690787971 - vertex -1.51577306 -0.429457009 0.939001024 - vertex -1.82231998 -0.367796004 0.655328989 - endloop - endfacet - - facet normal 0.404057056 0.881292224 -0.245075062 - outer loop - vertex -1.51577306 -0.429457009 0.939001024 - vertex -1.77611899 -0.319954008 0.903541982 - vertex -1.82231998 -0.367796004 0.655328989 - endloop - endfacet - - facet normal 0.894504726 0.365525872 -0.25739491 - outer loop - vertex -1.95466805 -0.147531003 0.655328989 - vertex -1.89073491 -0.129198998 0.903541982 - vertex -2.02874589 0.00046000001 0.608051002 - endloop - endfacet - - facet normal 0.875202715 0.431018859 -0.219642922 - outer loop - vertex -1.89073491 -0.129198998 0.903541982 - vertex -1.94569099 0.00046000001 0.939001024 - vertex -2.02874589 0.00046000001 0.608051002 - endloop - endfacet - - facet normal -0.472085088 0.0944170132 -0.876482189 - outer loop - vertex -0.887284994 0.00046000001 1.00323403 - vertex -1.06898999 0.00046000001 1.10110295 - vertex -0.963268995 -0.379460007 1.00323403 - endloop - endfacet - - facet normal -0.399085015 0.123011015 -0.908625066 - outer loop - vertex -1.06898999 0.00046000001 1.10110295 - vertex -1.10224402 -0.107427001 1.10110295 - vertex -0.963268995 -0.379460007 1.00323403 - endloop - endfacet - - facet normal -0.362892926 0.145723984 -0.92036581 - outer loop - vertex -0.963268995 -0.379460007 1.00323403 - vertex -1.10224402 -0.107427001 1.10110295 - vertex -1.21148205 -0.379460007 1.10110295 - endloop - endfacet - - facet normal -0.337954879 0.388761878 -0.857117712 - outer loop - vertex -0.963268995 -0.379460007 1.00323403 - vertex -1.21148205 -0.379460007 1.10110295 - vertex -1.12063003 -0.516255975 1.00323403 - endloop - endfacet - - facet normal 0.260202944 0.670137882 -0.695132792 - outer loop - vertex -1.12063003 -0.516255975 1.00323403 - vertex -1.21148205 -0.379460007 1.10110295 - vertex -1.51577306 -0.429457009 0.939001024 - endloop - endfacet - - facet normal 0.25209403 0.68426919 -0.68426919 - outer loop - vertex -1.21148205 -0.379460007 1.10110295 - vertex -1.51577306 -0.247752994 1.12070501 - vertex -1.51577306 -0.429457009 0.939001024 - endloop - endfacet - - facet normal 0.186156049 0.694746196 -0.69474715 - outer loop - vertex -1.51577306 -0.429457009 0.939001024 - vertex -1.51577306 -0.247752994 1.12070501 - vertex -1.63987899 -0.214498997 1.12070501 - endloop - endfacet - - facet normal 0.380661011 0.715228021 -0.586127996 - outer loop - vertex -1.51577306 -0.429457009 0.939001024 - vertex -1.63987899 -0.214498997 1.12070501 - vertex -1.77611899 -0.319954008 0.903541982 - endloop - endfacet - - facet normal 0.673306763 0.404560834 -0.618860781 - outer loop - vertex -1.77611899 -0.319954008 0.903541982 - vertex -1.63987899 -0.214498997 1.12070501 - vertex -1.89073491 -0.129198998 0.903541982 - endloop - endfacet - - facet normal 0.622429729 0.622429729 -0.474512786 - outer loop - vertex -1.63987899 -0.214498997 1.12070501 - vertex -1.73073196 -0.123646997 1.12070501 - vertex -1.89073491 -0.129198998 0.903541982 - endloop - endfacet - - facet normal 0.784513354 0.210211098 -0.583395302 - outer loop - vertex -1.89073491 -0.129198998 0.903541982 - vertex -1.73073196 -0.123646997 1.12070501 - vertex -1.76398599 0.00046000001 1.12070501 - endloop - endfacet - - facet normal 0.634200931 0.442243934 -0.634199917 - outer loop - vertex -1.89073491 -0.129198998 0.903541982 - vertex -1.76398599 0.00046000001 1.12070501 - vertex -1.94569099 0.00046000001 0.939001024 - endloop - endfacet - - facet normal -0.521937132 -0.820093155 0.234582052 - outer loop - vertex -1.90136802 -0.156956002 0.72427702 - vertex -1.861866 -0.179064006 0.734879017 - vertex -1.86046696 -0.183946997 0.72092402 - endloop - endfacet - - facet normal 0.561335981 -0.0371459983 -0.826753974 - outer loop - vertex -1.95466805 -0.147531003 0.655328989 - vertex -1.88625693 -0.187941998 0.703593016 - vertex -1.86046696 -0.183946997 0.72092402 - endloop - endfacet - - facet normal 0.511620224 0.810681403 -0.284676135 - outer loop - vertex -1.86046696 -0.183946997 0.72092402 - vertex -1.90136802 -0.156956002 0.72427702 - vertex -1.95466805 -0.147531003 0.655328989 - endloop - endfacet - - facet normal 0.67524904 0.414225072 -0.610292137 - outer loop - vertex -1.862077 -0.237517998 0.69669801 - vertex -1.88625693 -0.187941998 0.703593016 - vertex -1.95466805 -0.147531003 0.655328989 - endloop - endfacet - - facet normal 0.672120929 0.413266957 -0.614380956 - outer loop - vertex -1.88625693 -0.187941998 0.703593016 - vertex -1.862077 -0.237517998 0.69669801 - vertex -1.84992504 -0.220708996 0.721297979 - endloop - endfacet - - facet normal 0.536240935 0.14531 -0.831462979 - outer loop - vertex -1.84992504 -0.220708996 0.721297979 - vertex -1.86046696 -0.183946997 0.72092402 - vertex -1.88625693 -0.187941998 0.703593016 - endloop - endfacet - - facet normal 0.895058095 0.00553400069 -0.445915073 - outer loop - vertex -1.83589005 -0.269659013 0.748862982 - vertex -1.84992504 -0.220708996 0.721297979 - vertex -1.862077 -0.237517998 0.69669801 - endloop - endfacet - - facet normal 0.914935708 0.290189922 -0.280503899 - outer loop - vertex -1.862077 -0.237517998 0.69669801 - vertex -1.83185291 -0.299488008 0.731172025 - vertex -1.83589005 -0.269659013 0.748862982 - endloop - endfacet - - facet normal 0.968834102 0.246923044 -0.019739002 - outer loop - vertex -1.82824898 -0.296305001 0.790558994 - vertex -1.83589005 -0.269659013 0.748862982 - vertex -1.82278597 -0.318078995 0.786329985 - endloop - endfacet - - facet normal 0.977088094 0.189592034 -0.0967150182 - outer loop - vertex -1.83185291 -0.299488008 0.731172025 - vertex -1.82278597 -0.318078995 0.786329985 - vertex -1.83589005 -0.269659013 0.748862982 - endloop - endfacet - - facet normal 0.997900426 0.0593979657 0.0258139856 - outer loop - vertex -1.82945108 -0.292113006 0.827350974 - vertex -1.82824898 -0.296305001 0.790558994 - vertex -1.82883 -0.305685014 0.834594011 - endloop - endfacet - - facet normal 0.970842183 0.231530026 0.0621240065 - outer loop - vertex -1.82824898 -0.296305001 0.790558994 - vertex -1.82278597 -0.318078995 0.786329985 - vertex -1.82883 -0.305685014 0.834594011 - endloop - endfacet - - facet normal 0.980895877 0.124576986 0.149411991 - outer loop - vertex -1.82883 -0.305685014 0.834594011 - vertex -1.83534694 -0.271548003 0.848914027 - vertex -1.82945108 -0.292113006 0.827350974 - endloop - endfacet - - facet normal 0.919196248 0.0103330025 0.393664122 - outer loop - vertex -1.82883 -0.305685014 0.834594011 - vertex -1.84091997 -0.280896991 0.862173021 - vertex -1.83534694 -0.271548003 0.848914027 - endloop - endfacet - - facet normal 0.793512464 0.287575155 0.536319315 - outer loop - vertex -1.84091997 -0.280896991 0.862173021 - vertex -1.859056 -0.243714005 0.869068027 - vertex -1.83534694 -0.271548003 0.848914027 - endloop - endfacet - - facet normal 0.804794014 0.326705992 0.495550007 - outer loop - vertex -1.84234202 -0.247156993 0.844193995 - vertex -1.83534694 -0.271548003 0.848914027 - vertex -1.859056 -0.243714005 0.869068027 - endloop - endfacet - - facet normal 0.715218186 0.572144151 0.401390105 - outer loop - vertex -1.859056 -0.243714005 0.869068027 - vertex -1.87719107 -0.206533 0.848384023 - vertex -1.84234202 -0.247156993 0.844193995 - endloop - endfacet - - facet normal 0.653584957 0.502278864 0.566164911 - outer loop - vertex -1.84963393 -0.221723005 0.830048978 - vertex -1.84234202 -0.247156993 0.844193995 - vertex -1.87719107 -0.206533 0.848384023 - endloop - endfacet - - facet normal 0.55377394 0.818229973 0.154382989 - outer loop - vertex -1.851632 -0.214763001 0.800320983 - vertex -1.84963393 -0.221723005 0.830048978 - vertex -1.87719107 -0.206533 0.848384023 - endloop - endfacet - - facet normal 0.600952983 0.777222991 0.186493993 - outer loop - vertex -1.87719107 -0.206533 0.848384023 - vertex -1.88625693 -0.187941998 0.800119996 - vertex -1.851632 -0.214763001 0.800320983 - endloop - endfacet - - facet normal 0.597518981 0.772989929 0.21320799 - outer loop - vertex -1.85507607 -0.202750996 0.766424 - vertex -1.851632 -0.214763001 0.800320983 - vertex -1.88625693 -0.187941998 0.800119996 - endloop - endfacet - - facet normal 0.664247036 0.676876068 0.317198038 - outer loop - vertex -1.88625693 -0.187941998 0.800119996 - vertex -1.89834702 -0.163152993 0.772539973 - vertex -1.85507607 -0.202750996 0.766424 - endloop - endfacet - - facet normal 0.657633364 0.663550377 0.356678218 - outer loop - vertex -1.861866 -0.179064006 0.734879017 - vertex -1.85507607 -0.202750996 0.766424 - vertex -1.89834702 -0.163152993 0.772539973 - endloop - endfacet - - facet normal 0.469525933 0.87896502 0.0834619924 - outer loop - vertex -1.89834702 -0.163152993 0.772539973 - vertex -1.90136802 -0.156956002 0.72427702 - vertex -1.861866 -0.179064006 0.734879017 - endloop - endfacet - - facet normal 0.373062909 0.913278759 -0.163541958 - outer loop - vertex -1.90136802 -0.156956002 0.72427702 - vertex -1.89073491 -0.129198998 0.903541982 - vertex -1.95466805 -0.147531003 0.655328989 - endloop - endfacet - - facet normal 0.998070359 0.0116050038 -0.0609980263 - outer loop - vertex -1.90136802 -0.156956002 0.72427702 - vertex -1.89834702 -0.163152993 0.772539973 - vertex -1.89073491 -0.129198998 0.903541982 - endloop - endfacet - - facet normal 0.941414237 0.309131056 -0.134823024 - outer loop - vertex -1.89834702 -0.163152993 0.772539973 - vertex -1.88625693 -0.187941998 0.800119996 - vertex -1.89073491 -0.129198998 0.903541982 - endloop - endfacet - - facet normal 0.967765391 0.234684095 -0.0913970396 - outer loop - vertex -1.88625693 -0.187941998 0.800119996 - vertex -1.87719107 -0.206533 0.848384023 - vertex -1.89073491 -0.129198998 0.903541982 - endloop - endfacet - - facet normal 0.919146776 0.322406918 -0.226324946 - outer loop - vertex -1.87719107 -0.206533 0.848384023 - vertex -1.859056 -0.243714005 0.869068027 - vertex -1.89073491 -0.129198998 0.903541982 - endloop - endfacet - - facet normal 0.629824638 0.378434747 -0.6783126 - outer loop - vertex -1.77611899 -0.319954008 0.903541982 - vertex -1.89073491 -0.129198998 0.903541982 - vertex -1.859056 -0.243714005 0.869068027 - endloop - endfacet - - facet normal 0.655910134 0.434389085 -0.617323101 - outer loop - vertex -1.859056 -0.243714005 0.869068027 - vertex -1.84091997 -0.280896991 0.862173021 - vertex -1.77611899 -0.319954008 0.903541982 - endloop - endfacet - - facet normal 0.635726869 0.691575825 -0.342890948 - outer loop - vertex -1.84091997 -0.280896991 0.862173021 - vertex -1.82883 -0.305685014 0.834594011 - vertex -1.77611899 -0.319954008 0.903541982 - endloop - endfacet - - facet normal 0.669934034 0.402535051 -0.623822033 - outer loop - vertex -1.862077 -0.237517998 0.69669801 - vertex -1.95466805 -0.147531003 0.655328989 - vertex -1.82231998 -0.367796004 0.655328989 - endloop - endfacet - - facet normal 0.919927955 0.341761976 -0.192174986 - outer loop - vertex -1.83185291 -0.299488008 0.731172025 - vertex -1.862077 -0.237517998 0.69669801 - vertex -1.82231998 -0.367796004 0.655328989 - endloop - endfacet - - facet normal 0.970269859 0.227403969 -0.0828489959 - outer loop - vertex -1.82278597 -0.318078995 0.786329985 - vertex -1.83185291 -0.299488008 0.731172025 - vertex -1.82231998 -0.367796004 0.655328989 - endloop - endfacet - - facet normal 0.549644947 0.795333922 -0.255605966 - outer loop - vertex -1.82231998 -0.367796004 0.655328989 - vertex -1.77611899 -0.319954008 0.903541982 - vertex -1.82883 -0.305685014 0.834594011 - endloop - endfacet - - facet normal -0.685613155 0.679786205 -0.260433048 - outer loop - vertex -1.82278597 -0.318078995 0.786329985 - vertex -1.82883 -0.305685014 0.834594011 - vertex -1.82231998 -0.367796004 0.655328989 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.03665495 -0.418327987 -0.397619992 - vertex -2.03665495 -0.418327987 -0.397619992 - vertex -2.03665495 -0.418327987 -0.397619992 - endloop - endfacet - - facet normal -0.891945779 -0.305186927 -0.333606929 - outer loop - vertex -1.81837392 -0.186278999 0.356357992 - vertex -1.88002801 -0.165750995 0.502418995 - vertex -1.81634998 -0.275851995 0.432888001 - endloop - endfacet - - facet normal -0.885285795 0.462163895 0.0517069884 - outer loop - vertex -2.03665495 -0.418327987 -0.397619992 - vertex -1.92074704 -0.268263996 0.245554 - vertex -1.95693707 -0.340858012 0.274801999 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.03665495 -0.418327987 -0.397619992 - vertex -1.95693707 -0.340858012 0.274801999 - vertex -2.03665495 -0.418327987 -0.397619992 - endloop - endfacet - - facet normal -0.782590985 0.524955928 0.334623009 - outer loop - vertex -1.92074704 -0.268263996 0.245554 - vertex -1.81837392 -0.186278999 0.356357992 - vertex -1.95693707 -0.340858012 0.274801999 - endloop - endfacet - - facet normal -0.758138955 0.413597971 0.504144967 - outer loop - vertex -1.81837392 -0.186278999 0.356357992 - vertex -1.81634998 -0.275851995 0.432888001 - vertex -1.95693707 -0.340858012 0.274801999 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.03665495 -0.418327987 -0.397619992 - vertex -2.03665495 -0.418327987 -0.397619992 - vertex -2.03665495 -0.418327987 -0.397619992 - endloop - endfacet - - facet normal -0.89194268 -0.305170923 -0.333629906 - outer loop - vertex -1.81634998 -0.275851995 0.432888001 - vertex -1.88002801 -0.165750995 0.502418995 - vertex -1.86227798 -0.282516986 0.561774015 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.03665495 -0.418327987 -0.397619992 - vertex -1.95693707 -0.340858012 0.274801999 - vertex -2.03665495 -0.418327987 -0.397619992 - endloop - endfacet - - facet normal -0.239093035 0.967433095 -0.0831130072 - outer loop - vertex -1.95693707 -0.340858012 0.274801999 - vertex -2.051718 -0.363382012 0.285284013 - vertex -2.03665495 -0.418327987 -0.397619992 - endloop - endfacet - - facet normal -0.347357035 0.934693158 -0.0754440054 - outer loop - vertex -1.95693707 -0.340858012 0.274801999 - vertex -1.81634998 -0.275851995 0.432888001 - vertex -1.86227798 -0.282516986 0.561774015 - endloop - endfacet - - facet normal -0.241774037 0.963362098 -0.116099015 - outer loop - vertex -1.95693707 -0.340858012 0.274801999 - vertex -1.86227798 -0.282516986 0.561774015 - vertex -2.051718 -0.363382012 0.285284013 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.03665495 -0.418327987 -0.397619992 - vertex -2.03665495 -0.418327987 -0.397619992 - vertex -2.03665495 -0.418327987 -0.397619992 - endloop - endfacet - - facet normal -0.89195025 -0.305165082 -0.333615094 - outer loop - vertex -1.86227798 -0.282516986 0.561774015 - vertex -1.88002801 -0.165750995 0.502418995 - vertex -1.92157102 -0.201254994 0.645965993 - endloop - endfacet - - facet normal 0.485254169 0.872347355 -0.059485022 - outer loop - vertex -2.03665495 -0.418327987 -0.397619992 - vertex -2.051718 -0.363382012 0.285284013 - vertex -2.13371491 -0.318872988 0.269109011 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.03665495 -0.418327987 -0.397619992 - vertex -2.13371491 -0.318872988 0.269109011 - vertex -2.03665495 -0.418327987 -0.397619992 - endloop - endfacet - - facet normal 0.482345134 0.694665194 -0.533651114 - outer loop - vertex -2.051718 -0.363382012 0.285284013 - vertex -1.86227798 -0.282516986 0.561774015 - vertex -2.13371491 -0.318872988 0.269109011 - endloop - endfacet - - facet normal 0.403897047 0.78366518 -0.47194913 - outer loop - vertex -1.86227798 -0.282516986 0.561774015 - vertex -1.92157102 -0.201254994 0.645965993 - vertex -2.13371491 -0.318872988 0.269109011 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.03665495 -0.418327987 -0.397619992 - vertex -2.03665495 -0.418327987 -0.397619992 - vertex -2.03665495 -0.418327987 -0.397619992 - endloop - endfacet - - facet normal -0.891946554 -0.305174887 -0.333615839 - outer loop - vertex -1.92157102 -0.201254994 0.645965993 - vertex -1.88002801 -0.165750995 0.502418995 - vertex -1.94958198 -0.0932570025 0.622065008 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.03665495 -0.418327987 -0.397619992 - vertex -2.13371491 -0.318872988 0.269109011 - vertex -2.03665495 -0.418327987 -0.397619992 - endloop - endfacet - - facet normal 0.98234576 0.14196597 0.121829979 - outer loop - vertex -2.13371491 -0.318872988 0.269109011 - vertex -2.1411891 -0.240848005 0.238456994 - vertex -2.03665495 -0.418327987 -0.397619992 - endloop - endfacet - - facet normal 0.851677418 0.107381061 -0.512947261 - outer loop - vertex -2.13371491 -0.318872988 0.269109011 - vertex -1.92157102 -0.201254994 0.645965993 - vertex -1.94958198 -0.0932570025 0.622065008 - endloop - endfacet - - facet normal 0.903621852 -0.0788339823 -0.421013921 - outer loop - vertex -2.13371491 -0.318872988 0.269109011 - vertex -1.94958198 -0.0932570025 0.622065008 - vertex -2.1411891 -0.240848005 0.238456994 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.03665495 -0.418327987 -0.397619992 - vertex -2.03665495 -0.418327987 -0.397619992 - vertex -2.03665495 -0.418327987 -0.397619992 - endloop - endfacet - - facet normal -0.89194572 -0.305181921 -0.333611906 - outer loop - vertex -1.94958198 -0.0932570025 0.622065008 - vertex -1.88002801 -0.165750995 0.502418995 - vertex -1.92521799 -0.0398489982 0.508068025 - endloop - endfacet - - facet normal 0.618494272 -0.724668384 0.303843141 - outer loop - vertex -2.03665495 -0.418327987 -0.397619992 - vertex -2.1411891 -0.240848005 0.238456994 - vertex -2.0685091 -0.188061997 0.216406003 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.03665495 -0.418327987 -0.397619992 - vertex -2.0685091 -0.188061997 0.216406003 - vertex -2.03665495 -0.418327987 -0.397619992 - endloop - endfacet - - facet normal 0.590630949 -0.806794941 0.0153989978 - outer loop - vertex -2.1411891 -0.240848005 0.238456994 - vertex -1.94958198 -0.0932570025 0.622065008 - vertex -2.0685091 -0.188061997 0.216406003 - endloop - endfacet - - facet normal 0.806724012 -0.582334995 -0.100410998 - outer loop - vertex -1.94958198 -0.0932570025 0.622065008 - vertex -1.92521799 -0.0398489982 0.508068025 - vertex -2.0685091 -0.188061997 0.216406003 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.03665495 -0.418327987 -0.397619992 - vertex -2.03665495 -0.418327987 -0.397619992 - vertex -2.03665495 -0.418327987 -0.397619992 - endloop - endfacet - - facet normal -0.891949534 -0.305183858 -0.333599836 - outer loop - vertex -1.92521799 -0.0398489982 0.508068025 - vertex -1.88002801 -0.165750995 0.502418995 - vertex -1.86682701 -0.0812470019 0.389818996 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.03665495 -0.418327987 -0.397619992 - vertex -2.0685091 -0.188061997 0.216406003 - vertex -2.03665495 -0.418327987 -0.397619992 - endloop - endfacet - - facet normal -0.126801014 -0.93091315 0.342523098 - outer loop - vertex -2.0685091 -0.188061997 0.216406003 - vertex -1.970402 -0.200262994 0.219567001 - vertex -2.03665495 -0.418327987 -0.397619992 - endloop - endfacet - - facet normal 0.145876989 -0.909062028 0.390289962 - outer loop - vertex -2.0685091 -0.188061997 0.216406003 - vertex -1.92521799 -0.0398489982 0.508068025 - vertex -1.86682701 -0.0812470019 0.389818996 - endloop - endfacet - - facet normal -0.116740949 -0.779241621 0.615754724 - outer loop - vertex -2.0685091 -0.188061997 0.216406003 - vertex -1.86682701 -0.0812470019 0.389818996 - vertex -1.970402 -0.200262994 0.219567001 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.03665495 -0.418327987 -0.397619992 - vertex -2.03665495 -0.418327987 -0.397619992 - vertex -2.03665495 -0.418327987 -0.397619992 - endloop - endfacet - - facet normal -0.891944349 -0.305192113 -0.333606124 - outer loop - vertex -1.86682701 -0.0812470019 0.389818996 - vertex -1.88002801 -0.165750995 0.502418995 - vertex -1.81837392 -0.186278999 0.356357992 - endloop - endfacet - - facet normal -0.824144065 -0.500415027 0.265276015 - outer loop - vertex -2.03665495 -0.418327987 -0.397619992 - vertex -1.970402 -0.200262994 0.219567001 - vertex -1.92074704 -0.268263996 0.245554 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.03665495 -0.418327987 -0.397619992 - vertex -1.92074704 -0.268263996 0.245554 - vertex -2.03665495 -0.418327987 -0.397619992 - endloop - endfacet - - facet normal -0.71909833 -0.282447189 0.634918332 - outer loop - vertex -1.970402 -0.200262994 0.219567001 - vertex -1.86682701 -0.0812470019 0.389818996 - vertex -1.92074704 -0.268263996 0.245554 - endloop - endfacet - - facet normal -0.460383147 -0.455179155 0.762141228 - outer loop - vertex -1.86682701 -0.0812470019 0.389818996 - vertex -1.81837392 -0.186278999 0.356357992 - vertex -1.92074704 -0.268263996 0.245554 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 -0.77343601 0.148902997 - vertex -2.04218507 -0.77343601 0.148902997 - vertex -2.04218507 -0.77343601 0.148902997 - endloop - endfacet - - facet normal -0.417207867 -0.680008829 -0.602930844 - outer loop - vertex -1.69004607 -0.233094007 0.600108027 - vertex -1.56890297 -0.326896995 0.622075021 - vertex -1.57823491 -0.235411003 0.525350988 - endloop - endfacet - - facet normal 0.39095211 -0.768585265 0.506392181 - outer loop - vertex -2.04218507 -0.77343601 0.148902997 - vertex -1.75114 -0.435550988 0.437038004 - vertex -1.67776895 -0.439565986 0.37429899 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 -0.77343601 0.148902997 - vertex -1.67776895 -0.439565986 0.37429899 - vertex -2.04218507 -0.77343601 0.148902997 - endloop - endfacet - - facet normal 0.482775718 -0.633069634 0.605103672 - outer loop - vertex -1.75114 -0.435550988 0.437038004 - vertex -1.69004607 -0.233094007 0.600108027 - vertex -1.67776895 -0.439565986 0.37429899 - endloop - endfacet - - facet normal 0.40646106 -0.663174093 0.628482044 - outer loop - vertex -1.69004607 -0.233094007 0.600108027 - vertex -1.57823491 -0.235411003 0.525350988 - vertex -1.67776895 -0.439565986 0.37429899 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 -0.77343601 0.148902997 - vertex -2.04218507 -0.77343601 0.148902997 - vertex -2.04218507 -0.77343601 0.148902997 - endloop - endfacet - - facet normal -0.417207867 -0.680008829 -0.602930844 - outer loop - vertex -1.57823491 -0.235411003 0.525350988 - vertex -1.56890297 -0.326896995 0.622075021 - vertex -1.45939696 -0.306618005 0.523428977 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 -0.77343601 0.148902997 - vertex -1.67776895 -0.439565986 0.37429899 - vertex -2.04218507 -0.77343601 0.148902997 - endloop - endfacet - - facet normal -0.141780034 -0.442905128 0.885287166 - outer loop - vertex -1.67776895 -0.439565986 0.37429899 - vertex -1.61079192 -0.497384012 0.35609901 - vertex -2.04218507 -0.77343601 0.148902997 - endloop - endfacet - - facet normal -0.275092959 -0.481556952 0.832121909 - outer loop - vertex -1.67776895 -0.439565986 0.37429899 - vertex -1.57823491 -0.235411003 0.525350988 - vertex -1.45939696 -0.306618005 0.523428977 - endloop - endfacet - - facet normal -0.234952107 -0.528877199 0.815528333 - outer loop - vertex -1.67776895 -0.439565986 0.37429899 - vertex -1.45939696 -0.306618005 0.523428977 - vertex -1.61079192 -0.497384012 0.35609901 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 -0.77343601 0.148902997 - vertex -2.04218507 -0.77343601 0.148902997 - vertex -2.04218507 -0.77343601 0.148902997 - endloop - endfacet - - facet normal -0.417209029 -0.680007041 -0.602932036 - outer loop - vertex -1.45939696 -0.306618005 0.523428977 - vertex -1.56890297 -0.326896995 0.622075021 - vertex -1.42301798 -0.393096 0.595789015 - endloop - endfacet - - facet normal -0.577504694 0.348285824 0.738366604 - outer loop - vertex -2.04218507 -0.77343601 0.148902997 - vertex -1.61079192 -0.497384012 0.35609901 - vertex -1.60065103 -0.565468013 0.396145999 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 -0.77343601 0.148902997 - vertex -1.60065103 -0.565468013 0.396145999 - vertex -2.04218507 -0.77343601 0.148902997 - endloop - endfacet - - facet normal -0.825805068 0.189439997 0.531185985 - outer loop - vertex -1.61079192 -0.497384012 0.35609901 - vertex -1.45939696 -0.306618005 0.523428977 - vertex -1.60065103 -0.565468013 0.396145999 - endloop - endfacet - - facet normal -0.799487293 0.150301054 0.581575215 - outer loop - vertex -1.45939696 -0.306618005 0.523428977 - vertex -1.42301798 -0.393096 0.595789015 - vertex -1.60065103 -0.565468013 0.396145999 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 -0.77343601 0.148902997 - vertex -2.04218507 -0.77343601 0.148902997 - vertex -2.04218507 -0.77343601 0.148902997 - endloop - endfacet - - facet normal -0.417208076 -0.680003166 -0.602937102 - outer loop - vertex -1.42301798 -0.393096 0.595789015 - vertex -1.56890297 -0.326896995 0.622075021 - vertex -1.49649501 -0.429726005 0.687943995 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 -0.77343601 0.148902997 - vertex -1.60065103 -0.565468013 0.396145999 - vertex -2.04218507 -0.77343601 0.148902997 - endloop - endfacet - - facet normal -0.432327896 0.901612759 0.0136719961 - outer loop - vertex -1.60065103 -0.565468013 0.396145999 - vertex -1.65497303 -0.592549026 0.464280993 - vertex -2.04218507 -0.77343601 0.148902997 - endloop - endfacet - - facet normal -0.591237903 0.791075826 -0.156960949 - outer loop - vertex -1.60065103 -0.565468013 0.396145999 - vertex -1.42301798 -0.393096 0.595789015 - vertex -1.49649501 -0.429726005 0.687943995 - endloop - endfacet - - facet normal -0.591241062 0.791074038 -0.156958029 - outer loop - vertex -1.60065103 -0.565468013 0.396145999 - vertex -1.49649501 -0.429726005 0.687943995 - vertex -1.65497303 -0.592549026 0.464280993 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 -0.77343601 0.148902997 - vertex -2.04218507 -0.77343601 0.148902997 - vertex -2.04218507 -0.77343601 0.148902997 - endloop - endfacet - - facet normal -0.417216957 -0.680004954 -0.602928936 - outer loop - vertex -1.49649501 -0.429726005 0.687943995 - vertex -1.56890297 -0.326896995 0.622075021 - vertex -1.62449408 -0.388922006 0.730498016 - endloop - endfacet - - facet normal 0.0537480377 0.836287558 -0.545650363 - outer loop - vertex -2.04218507 -0.77343601 0.148902997 - vertex -1.65497303 -0.592549026 0.464280993 - vertex -1.73286009 -0.558236003 0.50919801 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 -0.77343601 0.148902997 - vertex -1.73286009 -0.558236003 0.50919801 - vertex -2.04218507 -0.77343601 0.148902997 - endloop - endfacet - - facet normal 0.011833 0.804398 -0.593973041 - outer loop - vertex -1.65497303 -0.592549026 0.464280993 - vertex -1.49649501 -0.429726005 0.687943995 - vertex -1.73286009 -0.558236003 0.50919801 - endloop - endfacet - - facet normal 0.043387007 0.783097148 -0.620384037 - outer loop - vertex -1.49649501 -0.429726005 0.687943995 - vertex -1.62449408 -0.388922006 0.730498016 - vertex -1.73286009 -0.558236003 0.50919801 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 -0.77343601 0.148902997 - vertex -2.04218507 -0.77343601 0.148902997 - vertex -2.04218507 -0.77343601 0.148902997 - endloop - endfacet - - facet normal -0.417210042 -0.680010021 -0.602928042 - outer loop - vertex -1.62449408 -0.388922006 0.730498016 - vertex -1.56890297 -0.326896995 0.622075021 - vertex -1.71063101 -0.301414013 0.691406012 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 -0.77343601 0.148902997 - vertex -1.73286009 -0.558236003 0.50919801 - vertex -2.04218507 -0.77343601 0.148902997 - endloop - endfacet - - facet normal 0.644526064 0.270723015 -0.715049088 - outer loop - vertex -1.73286009 -0.558236003 0.50919801 - vertex -1.77565801 -0.488364011 0.497074008 - vertex -2.04218507 -0.77343601 0.148902997 - endloop - endfacet - - facet normal 0.676753223 0.386138141 -0.626819253 - outer loop - vertex -1.73286009 -0.558236003 0.50919801 - vertex -1.62449408 -0.388922006 0.730498016 - vertex -1.71063101 -0.301414013 0.691406012 - endloop - endfacet - - facet normal 0.734698236 0.349097162 -0.581678212 - outer loop - vertex -1.73286009 -0.558236003 0.50919801 - vertex -1.71063101 -0.301414013 0.691406012 - vertex -1.77565801 -0.488364011 0.497074008 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 -0.77343601 0.148902997 - vertex -2.04218507 -0.77343601 0.148902997 - vertex -2.04218507 -0.77343601 0.148902997 - endloop - endfacet - - facet normal -0.417209774 -0.680009604 -0.602928638 - outer loop - vertex -1.71063101 -0.301414013 0.691406012 - vertex -1.56890297 -0.326896995 0.622075021 - vertex -1.69004607 -0.233094007 0.600108027 - endloop - endfacet - - facet normal 0.811764777 -0.561054826 -0.162034959 - outer loop - vertex -2.04218507 -0.77343601 0.148902997 - vertex -1.77565801 -0.488364011 0.497074008 - vertex -1.75114 -0.435550988 0.437038004 - endloop - endfacet - - facet normal 0 0 0 - outer loop - vertex -2.04218507 -0.77343601 0.148902997 - vertex -1.75114 -0.435550988 0.437038004 - vertex -2.04218507 -0.77343601 0.148902997 - endloop - endfacet - - facet normal 0.926234186 -0.373678088 0.0495470129 - outer loop - vertex -1.77565801 -0.488364011 0.497074008 - vertex -1.71063101 -0.301414013 0.691406012 - vertex -1.75114 -0.435550988 0.437038004 - endloop - endfacet - - facet normal 0.957405746 -0.28874594 -0.000203999953 - outer loop - vertex -1.71063101 -0.301414013 0.691406012 - vertex -1.69004607 -0.233094007 0.600108027 - vertex -1.75114 -0.435550988 0.437038004 - endloop - endfacet - - facet normal -0.416388899 -0.719635844 -0.55564785 - outer loop - vertex -1.74193907 0.30865401 0.859300017 - vertex -1.71201098 0.319709986 0.822552979 - vertex -1.75035691 0.337029994 0.828857005 - endloop - endfacet - - facet normal 0.0543019809 -0.722881734 -0.688834786 - outer loop - vertex -1.79036307 0.316971004 0.846754014 - vertex -1.74193907 0.30865401 0.859300017 - vertex -1.75035691 0.337029994 0.828857005 - endloop - endfacet - - facet normal 0.418186873 -0.411096871 -0.810011804 - outer loop - vertex -1.78778303 0.271717012 0.87105298 - vertex -1.79036307 0.316971004 0.846754014 - vertex -1.82835197 0.274188995 0.848854005 - endloop - endfacet - - facet normal 0.147420973 -0.461364925 -0.874876797 - outer loop - vertex -1.78778303 0.271717012 0.87105298 - vertex -1.74193907 0.30865401 0.859300017 - vertex -1.79036307 0.316971004 0.846754014 - endloop - endfacet - - facet normal 0.471917987 -0.110359997 -0.874708056 - outer loop - vertex -1.83203197 0.223009005 0.853326023 - vertex -1.78778303 0.271717012 0.87105298 - vertex -1.82835197 0.274188995 0.848854005 - endloop - endfacet - - facet normal 0.488865733 0.668644667 -0.560289621 - outer loop - vertex -1.85778403 0.181133002 0.812886 - vertex -1.82527602 0.146935999 0.800439 - vertex -1.82124805 0.177171007 0.840036988 - endloop - endfacet - - facet normal 0.901277065 0.41694206 -0.117724016 - outer loop - vertex -1.85520399 0.162085995 0.765182972 - vertex -1.85778403 0.181133002 0.812886 - vertex -1.87216401 0.202947006 0.780054986 - endloop - endfacet - - facet normal 0.416387886 0.908436775 0.0369279943 - outer loop - vertex -1.82527602 0.146935999 0.800439 - vertex -1.85520399 0.162085995 0.765182972 - vertex -1.81685805 0.144767001 0.758879006 - endloop - endfacet - - facet normal 0.654929042 0.713351011 -0.249396011 - outer loop - vertex -1.85520399 0.162085995 0.765182972 - vertex -1.82527602 0.146935999 0.800439 - vertex -1.85778403 0.181133002 0.812886 - endloop - endfacet - - facet normal 0.901276946 0.243723989 0.358187973 - outer loop - vertex -1.85778403 0.207340002 0.740882993 - vertex -1.85520399 0.162085995 0.765182972 - vertex -1.87216401 0.202947006 0.780054986 - endloop - endfacet - - facet normal 0.488877177 0.152067065 0.858996332 - outer loop - vertex -1.82527602 0.173142999 0.72843498 - vertex -1.85778403 0.207340002 0.740882993 - vertex -1.82124805 0.221757993 0.717536986 - endloop - endfacet - - facet normal 0.416390866 0.719640791 0.555639863 - outer loop - vertex -1.85520399 0.162085995 0.765182972 - vertex -1.82527602 0.173142999 0.72843498 - vertex -1.81685805 0.144767001 0.758879006 - endloop - endfacet - - facet normal 0.654934824 0.386147916 0.649576843 - outer loop - vertex -1.82527602 0.173142999 0.72843498 - vertex -1.85520399 0.162085995 0.765182972 - vertex -1.85778403 0.207340002 0.740882993 - endloop - endfacet - - facet normal 0.289381891 -0.326072842 0.899963617 - outer loop - vertex -1.83203197 0.265412986 0.736820996 - vertex -1.78360796 0.267105997 0.721863985 - vertex -1.82124805 0.221757993 0.717536986 - endloop - endfacet - - facet normal 0.471929044 -0.646786034 0.599125087 - outer loop - vertex -1.78778303 0.314121008 0.75454998 - vertex -1.83203197 0.265412986 0.736820996 - vertex -1.82835197 0.301744998 0.77314502 - endloop - endfacet - - facet normal 0.00501300022 -0.570511997 0.821273983 - outer loop - vertex -1.78360796 0.267105997 0.721863985 - vertex -1.78778303 0.314121008 0.75454998 - vertex -1.74596691 0.304625988 0.747698009 - endloop - endfacet - - facet normal 0.266493112 -0.534111202 0.802313328 - outer loop - vertex -1.78778303 0.314121008 0.75454998 - vertex -1.78360796 0.267105997 0.721863985 - vertex -1.83203197 0.265412986 0.736820996 - endloop - endfacet - - facet normal 0.418194056 -0.835585117 0.356246054 - outer loop - vertex -1.79036307 0.333168 0.802253008 - vertex -1.78778303 0.314121008 0.75454998 - vertex -1.82835197 0.301744998 0.77314502 - endloop - endfacet - - facet normal 0.0543040037 -0.996534169 0.0630150065 - outer loop - vertex -1.74193907 0.33486101 0.787295997 - vertex -1.79036307 0.333168 0.802253008 - vertex -1.75035691 0.337029994 0.828857005 - endloop - endfacet - - facet normal -0.0790780187 -0.788413107 0.610042095 - outer loop - vertex -1.78778303 0.314121008 0.75454998 - vertex -1.74193907 0.33486101 0.787295997 - vertex -1.74596691 0.304625988 0.747698009 - endloop - endfacet - - facet normal 0.147421986 -0.915789962 0.37362498 - outer loop - vertex -1.74193907 0.33486101 0.787295997 - vertex -1.78778303 0.314121008 0.75454998 - vertex -1.79036307 0.333168 0.802253008 - endloop - endfacet - - facet normal -0.416385919 -0.908437848 -0.036924988 - outer loop - vertex -1.71201098 0.319709986 0.822552979 - vertex -1.74193907 0.33486101 0.787295997 - vertex -1.75035691 0.337029994 0.828857005 - endloop - endfacet - - facet normal 0.629848003 -0.584398985 -0.511633992 - outer loop - vertex -1.79036307 0.316971004 0.846754014 - vertex -1.83620691 0.296231002 0.814006984 - vertex -1.82835197 0.274188995 0.848854005 - endloop - endfacet - - facet normal 0.303186983 -0.895461917 -0.325922996 - outer loop - vertex -1.79036307 0.333168 0.802253008 - vertex -1.79036307 0.316971004 0.846754014 - vertex -1.75035691 0.337029994 0.828857005 - endloop - endfacet - - facet normal 0.629847765 -0.77654773 0.0162899923 - outer loop - vertex -1.83620691 0.296231002 0.814006984 - vertex -1.79036307 0.333168 0.802253008 - vertex -1.82835197 0.301744998 0.77314502 - endloop - endfacet - - facet normal 0.556288183 -0.780874252 -0.284216076 - outer loop - vertex -1.79036307 0.333168 0.802253008 - vertex -1.83620691 0.296231002 0.814006984 - vertex -1.79036307 0.316971004 0.846754014 - endloop - endfacet - - facet normal 0.813501 -0.390870959 -0.430622935 - outer loop - vertex -1.83620691 0.296231002 0.814006984 - vertex -1.86195993 0.238159001 0.818068981 - vertex -1.82835197 0.274188995 0.848854005 - endloop - endfacet - - facet normal 0.813501239 -0.576223135 0.0786300153 - outer loop - vertex -1.86195993 0.254355997 0.773567975 - vertex -1.83620691 0.296231002 0.814006984 - vertex -1.82835197 0.301744998 0.77314502 - endloop - endfacet - - facet normal 0.976355553 -0.203133896 -0.0739349648 - outer loop - vertex -1.86195993 0.238159001 0.818068981 - vertex -1.86195993 0.254355997 0.773567975 - vertex -1.87216401 0.202947006 0.780054986 - endloop - endfacet - - facet normal 0.900026679 -0.409550846 -0.14906396 - outer loop - vertex -1.86195993 0.254355997 0.773567975 - vertex -1.86195993 0.238159001 0.818068981 - vertex -1.83620691 0.296231002 0.814006984 - endloop - endfacet - - facet normal 0.733524799 -0.111328974 -0.670482814 - outer loop - vertex -1.86195993 0.238159001 0.818068981 - vertex -1.83203197 0.223009005 0.853326023 - vertex -1.82835197 0.274188995 0.848854005 - endloop - endfacet - - facet normal 0.934058905 0.0995580032 -0.342960984 - outer loop - vertex -1.85778403 0.181133002 0.812886 - vertex -1.86195993 0.238159001 0.818068981 - vertex -1.87216401 0.202947006 0.780054986 - endloop - endfacet - - facet normal 0.582925737 0.34976086 -0.733392715 - outer loop - vertex -1.83203197 0.223009005 0.853326023 - vertex -1.85778403 0.181133002 0.812886 - vertex -1.82124805 0.177171007 0.840036988 - endloop - endfacet - - facet normal 0.78092432 0.113006048 -0.614318252 - outer loop - vertex -1.85778403 0.181133002 0.812886 - vertex -1.83203197 0.223009005 0.853326023 - vertex -1.86195993 0.238159001 0.818068981 - endloop - endfacet - - facet normal 0.582937837 -0.203489929 0.786622763 - outer loop - vertex -1.85778403 0.207340002 0.740882993 - vertex -1.83203197 0.265412986 0.736820996 - vertex -1.82124805 0.221757993 0.717536986 - endloop - endfacet - - facet normal 0.934058607 -0.144184947 0.326718837 - outer loop - vertex -1.86195993 0.254355997 0.773567975 - vertex -1.85778403 0.207340002 0.740882993 - vertex -1.87216401 0.202947006 0.780054986 - endloop - endfacet - - facet normal 0.733523011 -0.516260922 0.442061961 - outer loop - vertex -1.83203197 0.265412986 0.736820996 - vertex -1.86195993 0.254355997 0.773567975 - vertex -1.82835197 0.301744998 0.77314502 - endloop - endfacet - - facet normal 0.780922234 -0.308308095 0.54323715 - outer loop - vertex -1.86195993 0.254355997 0.773567975 - vertex -1.83203197 0.265412986 0.736820996 - vertex -1.85778403 0.207340002 0.740882993 - endloop - endfacet - - facet normal -0.416389078 0.719636202 -0.555647135 - outer loop - vertex -1.75035691 -0.325998992 0.817825019 - vertex -1.71201098 -0.308679014 0.811520994 - vertex -1.74193907 -0.297621995 0.848267972 - endloop - endfacet - - facet normal 0.0543030165 0.722881198 -0.688835204 - outer loop - vertex -1.75035691 -0.325998992 0.817825019 - vertex -1.74193907 -0.297621995 0.848267972 - vertex -1.79036307 -0.305938989 0.835722029 - endloop - endfacet - - facet normal 0.418186873 0.411096871 -0.810011804 - outer loop - vertex -1.82835197 -0.263157994 0.83782202 - vertex -1.79036307 -0.305938989 0.835722029 - vertex -1.78778303 -0.260684997 0.860022008 - endloop - endfacet - - facet normal 0.147422016 0.461364061 -0.874877095 - outer loop - vertex -1.79036307 -0.305938989 0.835722029 - vertex -1.74193907 -0.297621995 0.848267972 - vertex -1.78778303 -0.260684997 0.860022008 - endloop - endfacet - - facet normal 0.471917987 0.110359997 -0.874708056 - outer loop - vertex -1.82835197 -0.263157994 0.83782202 - vertex -1.78778303 -0.260684997 0.860022008 - vertex -1.83203197 -0.211977005 0.842293978 - endloop - endfacet - - facet normal 0.488864958 -0.668645024 -0.560289919 - outer loop - vertex -1.82124805 -0.166140005 0.829005003 - vertex -1.82527602 -0.135903999 0.789408028 - vertex -1.85778403 -0.170101002 0.801854014 - endloop - endfacet - - facet normal 0.901277483 -0.416941255 -0.117724068 - outer loop - vertex -1.87216401 -0.191915005 0.769023001 - vertex -1.85778403 -0.170101002 0.801854014 - vertex -1.85520399 -0.151054993 0.754150987 - endloop - endfacet - - facet normal 0.416387081 -0.908437192 0.0369280092 - outer loop - vertex -1.81685805 -0.133735001 0.747847021 - vertex -1.85520399 -0.151054993 0.754150987 - vertex -1.82527602 -0.135903999 0.789408028 - endloop - endfacet - - facet normal 0.654929042 -0.713351011 -0.249396011 - outer loop - vertex -1.85778403 -0.170101002 0.801854014 - vertex -1.82527602 -0.135903999 0.789408028 - vertex -1.85520399 -0.151054993 0.754150987 - endloop - endfacet - - facet normal 0.901276588 -0.243724898 0.358187854 - outer loop - vertex -1.87216401 -0.191915005 0.769023001 - vertex -1.85520399 -0.151054993 0.754150987 - vertex -1.85778403 -0.196308002 0.729851007 - endloop - endfacet - - facet normal 0.488875985 -0.152067006 0.858997047 - outer loop - vertex -1.82124805 -0.210725993 0.706505001 - vertex -1.85778403 -0.196308002 0.729851007 - vertex -1.82527602 -0.162110999 0.717404008 - endloop - endfacet - - facet normal 0.416390151 -0.719640255 0.555641174 - outer loop - vertex -1.81685805 -0.133735001 0.747847021 - vertex -1.82527602 -0.162110999 0.717404008 - vertex -1.85520399 -0.151054993 0.754150987 - endloop - endfacet - - facet normal 0.65493387 -0.386147916 0.649577796 - outer loop - vertex -1.85778403 -0.196308002 0.729851007 - vertex -1.85520399 -0.151054993 0.754150987 - vertex -1.82527602 -0.162110999 0.717404008 - endloop - endfacet - - facet normal 0.289381891 0.326072842 0.899963617 - outer loop - vertex -1.82124805 -0.210725993 0.706505001 - vertex -1.78360796 -0.256074011 0.710832 - vertex -1.83203197 -0.254381001 0.725790024 - endloop - endfacet - - facet normal 0.471929044 0.646786034 0.599125087 - outer loop - vertex -1.82835197 -0.290713996 0.762113988 - vertex -1.83203197 -0.254381001 0.725790024 - vertex -1.78778303 -0.303088993 0.743517995 - endloop - endfacet - - facet normal 0.00501300115 0.570513129 0.821273208 - outer loop - vertex -1.74596691 -0.293594003 0.736666024 - vertex -1.78778303 -0.303088993 0.743517995 - vertex -1.78360796 -0.256074011 0.710832 - endloop - endfacet - - facet normal 0.266492993 0.534111917 0.80231297 - outer loop - vertex -1.83203197 -0.254381001 0.725790024 - vertex -1.78360796 -0.256074011 0.710832 - vertex -1.78778303 -0.303088993 0.743517995 - endloop - endfacet - - facet normal 0.418193072 0.835586071 0.356245041 - outer loop - vertex -1.82835197 -0.290713996 0.762113988 - vertex -1.78778303 -0.303088993 0.743517995 - vertex -1.79036307 -0.322136015 0.791221976 - endloop - endfacet - - facet normal 0.0543069914 0.996533871 0.06301599 - outer loop - vertex -1.75035691 -0.325998992 0.817825019 - vertex -1.79036307 -0.322136015 0.791221976 - vertex -1.74193907 -0.323828995 0.776265025 - endloop - endfacet - - facet normal -0.0790780187 0.788413107 0.610042095 - outer loop - vertex -1.74596691 -0.293594003 0.736666024 - vertex -1.74193907 -0.323828995 0.776265025 - vertex -1.78778303 -0.303088993 0.743517995 - endloop - endfacet - - facet normal 0.14742294 0.915790558 0.373622864 - outer loop - vertex -1.79036307 -0.322136015 0.791221976 - vertex -1.78778303 -0.303088993 0.743517995 - vertex -1.74193907 -0.323828995 0.776265025 - endloop - endfacet - - facet normal -0.416385919 0.908437848 -0.036924988 - outer loop - vertex -1.75035691 -0.325998992 0.817825019 - vertex -1.74193907 -0.323828995 0.776265025 - vertex -1.71201098 -0.308679014 0.811520994 - endloop - endfacet - - facet normal 0.629849017 0.584397912 -0.511633933 - outer loop - vertex -1.82835197 -0.263157994 0.83782202 - vertex -1.83620691 -0.285198987 0.802974999 - vertex -1.79036307 -0.305938989 0.835722029 - endloop - endfacet - - facet normal 0.30318898 0.895461917 -0.325920969 - outer loop - vertex -1.75035691 -0.325998992 0.817825019 - vertex -1.79036307 -0.305938989 0.835722029 - vertex -1.79036307 -0.322136015 0.791221976 - endloop - endfacet - - facet normal 0.629846632 0.776548624 0.016288992 - outer loop - vertex -1.82835197 -0.290713996 0.762113988 - vertex -1.79036307 -0.322136015 0.791221976 - vertex -1.83620691 -0.285198987 0.802974999 - endloop - endfacet - - facet normal 0.556288064 0.780875146 -0.28421402 - outer loop - vertex -1.79036307 -0.305938989 0.835722029 - vertex -1.83620691 -0.285198987 0.802974999 - vertex -1.79036307 -0.322136015 0.791221976 - endloop - endfacet - - facet normal 0.813500583 0.390871763 -0.430622727 - outer loop - vertex -1.82835197 -0.263157994 0.83782202 - vertex -1.86195993 -0.227127001 0.807036996 - vertex -1.83620691 -0.285198987 0.802974999 - endloop - endfacet - - facet normal 0.813501239 0.576223135 0.0786290169 - outer loop - vertex -1.82835197 -0.290713996 0.762113988 - vertex -1.83620691 -0.285198987 0.802974999 - vertex -1.86195993 -0.243323997 0.762535989 - endloop - endfacet - - facet normal 0.976355791 0.203132957 -0.0739349872 - outer loop - vertex -1.87216401 -0.191915005 0.769023001 - vertex -1.86195993 -0.243323997 0.762535989 - vertex -1.86195993 -0.227127001 0.807036996 - endloop - endfacet - - facet normal 0.900026679 0.409550846 -0.14906396 - outer loop - vertex -1.83620691 -0.285198987 0.802974999 - vertex -1.86195993 -0.227127001 0.807036996 - vertex -1.86195993 -0.243323997 0.762535989 - endloop - endfacet - - facet normal 0.733524799 0.111328974 -0.670482814 - outer loop - vertex -1.82835197 -0.263157994 0.83782202 - vertex -1.83203197 -0.211977005 0.842293978 - vertex -1.86195993 -0.227127001 0.807036996 - endloop - endfacet - - facet normal 0.934059024 -0.0995570049 -0.342961013 - outer loop - vertex -1.87216401 -0.191915005 0.769023001 - vertex -1.86195993 -0.227127001 0.807036996 - vertex -1.85778403 -0.170101002 0.801854014 - endloop - endfacet - - facet normal 0.582925141 -0.349761069 -0.733393192 - outer loop - vertex -1.82124805 -0.166140005 0.829005003 - vertex -1.85778403 -0.170101002 0.801854014 - vertex -1.83203197 -0.211977005 0.842293978 - endloop - endfacet - - facet normal 0.78092432 -0.113006048 -0.614318252 - outer loop - vertex -1.86195993 -0.227127001 0.807036996 - vertex -1.83203197 -0.211977005 0.842293978 - vertex -1.85778403 -0.170101002 0.801854014 - endloop - endfacet - - facet normal 0.582937121 0.203490049 0.78662318 - outer loop - vertex -1.82124805 -0.210725993 0.706505001 - vertex -1.83203197 -0.254381001 0.725790024 - vertex -1.85778403 -0.196308002 0.729851007 - endloop - endfacet - - facet normal 0.934058905 0.144184992 0.326718003 - outer loop - vertex -1.87216401 -0.191915005 0.769023001 - vertex -1.85778403 -0.196308002 0.729851007 - vertex -1.86195993 -0.243323997 0.762535989 - endloop - endfacet - - facet normal 0.733523011 0.516260922 0.442061961 - outer loop - vertex -1.82835197 -0.290713996 0.762113988 - vertex -1.86195993 -0.243323997 0.762535989 - vertex -1.83203197 -0.254381001 0.725790024 - endloop - endfacet - - facet normal 0.780922592 0.308307827 0.543236673 - outer loop - vertex -1.85778403 -0.196308002 0.729851007 - vertex -1.83203197 -0.254381001 0.725790024 - vertex -1.86195993 -0.243323997 0.762535989 - endloop - endfacet - -endsolid AssimpScene From 067993d60772da35c97facab6a9c138225d2c645 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 5 May 2021 15:11:20 +0200 Subject: [PATCH 071/335] Delete dae.dae --- test/dae.dae | 80 ---------------------------------------------------- 1 file changed, 80 deletions(-) delete mode 100644 test/dae.dae diff --git a/test/dae.dae b/test/dae.dae deleted file mode 100644 index f8db45d6d..000000000 --- a/test/dae.dae +++ /dev/null @@ -1,80 +0,0 @@ - - - - - Assimp - Assimp Exporter - - 2021-05-05T14:41:31 - 2021-05-05T14:41:31 - - Y_UP - - - - - - - - - - 1 0 0 1 - - - 0.0373546556 - - - - - - - - - - - - - - - - 1 0 0 0 1 0 0 0 1 - - - - - - - - - - - - - - 3 -

0 1 2

-
-
-
-
- - - - - - 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 - - - - - - - - - - - - - - -
From 3f5c3eb38c6181134f690a6d19f4208b94bdf888 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 5 May 2021 15:11:36 +0200 Subject: [PATCH 072/335] Delete dna.txt --- test/dna.txt | 8008 -------------------------------------------------- 1 file changed, 8008 deletions(-) delete mode 100644 test/dna.txt diff --git a/test/dna.txt b/test/dna.txt deleted file mode 100644 index b89d0db4b..000000000 --- a/test/dna.txt +++ /dev/null @@ -1,8008 +0,0 @@ -Field format: type name offset size -Structure format: name size -Link 16 - - Link *next 0 8 - Link *prev 8 8 - -LinkData 24 - - LinkData *next 0 8 - LinkData *prev 8 8 - void *data 16 8 - -ListBase 16 - - void *first 0 8 - void *last 8 8 - -vec2s 4 - - short x 0 2 - short y 2 2 - -vec2f 8 - - float x 0 4 - float y 4 4 - -vec3f 12 - - float x 0 4 - float y 4 4 - float z 8 4 - -rcti 16 - - int xmin 0 4 - int xmax 4 4 - int ymin 8 4 - int ymax 12 4 - -rctf 16 - - float xmin 0 4 - float xmax 4 4 - float ymin 8 4 - float ymax 12 4 - -IDPropertyData 32 - - void *pointer 0 8 - ListBase group 8 16 - int val 24 4 - int val2 28 4 - -IDProperty 128 - - IDProperty *next 0 8 - IDProperty *prev 8 8 - char type 16 1 - char subtype 17 1 - short flag 18 2 - char name 20 64 - int saved 84 4 - IDPropertyData data 88 32 - int len 120 4 - int totallen 124 4 - -ID 120 - - void *next 0 8 - void *prev 8 8 - ID *newid 16 8 - Library *lib 24 8 - char name 32 66 - short pad 98 2 - short us 100 2 - short flag 102 2 - int icon_id 104 4 - int pad2 108 4 - IDProperty *properties 112 8 - -Library 2200 - - ID id 0 120 - ID *idblock 120 8 - FileData *filedata 128 8 - char name 136 1024 - char filepath 1160 1024 - Library *parent 2184 8 - PackedFile *packedfile 2192 8 - -PreviewImage 56 - - int w 0 8 - int h 8 8 - short changed 16 4 - short changed_timestamp 20 4 - int *rect 24 16 - GPUTexture *gputexture 40 16 - -IpoDriver 144 - - Object *ob 0 8 - short blocktype 8 2 - short adrcode 10 2 - short type 12 2 - short flag 14 2 - char name 16 128 - -IpoCurve 112 - - IpoCurve *next 0 8 - IpoCurve *prev 8 8 - BPoint *bp 16 8 - BezTriple *bezt 24 8 - rctf maxrct 32 16 - rctf totrct 48 16 - short blocktype 64 2 - short adrcode 66 2 - short vartype 68 2 - short totvert 70 2 - short ipo 72 2 - short extrap 74 2 - short flag 76 2 - short rt 78 2 - float ymin 80 4 - float ymax 84 4 - int bitmask 88 4 - float slide_min 92 4 - float slide_max 96 4 - float curval 100 4 - IpoDriver *driver 104 8 - -Ipo 160 - - ID id 0 120 - ListBase curve 120 16 - rctf cur 136 16 - short blocktype 152 2 - short showkey 154 2 - short muteipo 156 2 - short pad 158 2 - -KeyBlock 184 - - KeyBlock *next 0 8 - KeyBlock *prev 8 8 - float pos 16 4 - float curval 20 4 - short type 24 2 - short pad1 26 2 - short relative 28 2 - short flag 30 2 - int totelem 32 4 - int uid 36 4 - void *data 40 8 - char name 48 64 - char vgroup 112 64 - float slidermin 176 4 - float slidermax 180 4 - -Key 224 - - ID id 0 120 - AnimData *adt 120 8 - KeyBlock *refkey 128 8 - char elemstr 136 32 - int elemsize 168 4 - int pad 172 4 - ListBase block 176 16 - Ipo *ipo 192 8 - ID *from 200 8 - short type 208 2 - short totkey 210 2 - short slurph 212 2 - short flag 214 2 - float ctime 216 4 - int uidgen 220 4 - -TextLine 40 - - TextLine *next 0 8 - TextLine *prev 8 8 - char *line 16 8 - char *format 24 8 - int len 32 4 - int blen 36 4 - -Text 208 - - ID id 0 120 - char *name 120 8 - int flags 128 4 - int nlines 132 4 - ListBase lines 136 16 - TextLine *curl 152 8 - TextLine *sell 160 8 - int curc 168 4 - int selc 172 4 - char *undo_buf 176 8 - int undo_pos 184 4 - int undo_len 188 4 - void *compiled 192 8 - double mtime 200 8 - -PackedFile 16 - - int size 0 4 - int seek 4 4 - void *data 8 8 - -Camera 200 - - ID id 0 120 - AnimData *adt 120 8 - char type 128 1 - char dtx 129 1 - short flag 130 2 - float passepartalpha 132 4 - float clipsta 136 4 - float clipend 140 4 - float lens 144 4 - float ortho_scale 148 4 - float drawsize 152 4 - float sensor_x 156 4 - float sensor_y 160 4 - float shiftx 164 4 - float shifty 168 4 - float YF_dofdist 172 4 - Ipo *ipo 176 8 - Object *dof_ob 184 8 - char sensor_fit 192 1 - char pad 193 7 - -ImageUser 40 - - Scene *scene 0 8 - int framenr 8 4 - int frames 12 4 - int offset 16 4 - int sfra 20 4 - char fie_ima 24 1 - char cycl 25 1 - char ok 26 1 - char pad 27 1 - short multi_index 28 2 - short layer 30 2 - short pass 32 2 - short flag 34 2 - int pad2 36 4 - -Image 1408 - - ID id 0 120 - char name 120 1024 - ListBase ibufs 1144 16 - GPUTexture *gputexture 1160 8 - anim *anim 1168 8 - RenderResult *rr 1176 8 - RenderResult *renders 1184 64 - short render_slot 1248 2 - short last_render_slot 1250 2 - short ok 1252 2 - short flag 1254 2 - short source 1256 2 - short type 1258 2 - int lastframe 1260 4 - short tpageflag 1264 2 - short totbind 1266 2 - short xrep 1268 2 - short yrep 1270 2 - short twsta 1272 2 - short twend 1274 2 - int bindcode 1276 4 - int *repbind 1280 8 - PackedFile *packedfile 1288 8 - PreviewImage *preview 1296 8 - float lastupdate 1304 4 - int lastused 1308 4 - short animspeed 1312 2 - short pad2 1314 2 - int gen_x 1316 4 - int gen_y 1320 4 - char gen_type 1324 1 - char gen_flag 1325 1 - short gen_depth 1326 2 - float aspx 1328 4 - float aspy 1332 4 - ColorManagedColorspaceSettings colorspace_settings 1336 64 - char alpha_mode 1400 1 - char pad 1401 7 - -MTex 312 - - short texco 0 2 - short mapto 2 2 - short maptoneg 4 2 - short blendtype 6 2 - Object *object 8 8 - Tex *tex 16 8 - char uvname 24 64 - char projx 88 1 - char projy 89 1 - char projz 90 1 - char mapping 91 1 - float ofs 92 12 - float size 104 12 - float rot 116 4 - short texflag 120 2 - short colormodel 122 2 - short pmapto 124 2 - short pmaptoneg 126 2 - short normapspace 128 2 - short which_output 130 2 - char brush_map_mode 132 1 - char pad 133 7 - float r 140 4 - float g 144 4 - float b 148 4 - float k 152 4 - float def_var 156 4 - float rt 160 4 - float colfac 164 4 - float varfac 168 4 - float norfac 172 4 - float dispfac 176 4 - float warpfac 180 4 - float colspecfac 184 4 - float mirrfac 188 4 - float alphafac 192 4 - float difffac 196 4 - float specfac 200 4 - float emitfac 204 4 - float hardfac 208 4 - float raymirrfac 212 4 - float translfac 216 4 - float ambfac 220 4 - float colemitfac 224 4 - float colreflfac 228 4 - float coltransfac 232 4 - float densfac 236 4 - float scatterfac 240 4 - float reflfac 244 4 - float timefac 248 4 - float lengthfac 252 4 - float clumpfac 256 4 - float dampfac 260 4 - float kinkfac 264 4 - float roughfac 268 4 - float padensfac 272 4 - float gravityfac 276 4 - float lifefac 280 4 - float sizefac 284 4 - float ivelfac 288 4 - float fieldfac 292 4 - float shadowfac 296 4 - float zenupfac 300 4 - float zendownfac 304 4 - float blendfac 308 4 - -CBData 24 - - float r 0 4 - float g 4 4 - float b 8 4 - float a 12 4 - float pos 16 4 - int cur 20 4 - -ColorBand 776 - - short flag 0 2 - short tot 2 2 - short cur 4 2 - short ipotype 6 2 - CBData data 8 768 - -EnvMap 200 - - Object *object 0 8 - Image *ima 8 8 - ImBuf *cube 16 48 - float imat 64 64 - float obimat 128 36 - short type 164 2 - short stype 166 2 - float clipsta 168 4 - float clipend 172 4 - float viewscale 176 4 - int notlay 180 4 - short cuberes 184 2 - short depth 186 2 - int ok 188 4 - int lastframe 192 4 - short recalc 196 2 - short lastsize 198 2 - -PointDensity 104 - - short flag 0 2 - short falloff_type 2 2 - float falloff_softness 4 4 - float radius 8 4 - short source 12 2 - short color_source 14 2 - int totpoints 16 4 - int pdpad 20 4 - Object *object 24 8 - int psys 32 4 - short psys_cache_space 36 2 - short ob_cache_space 38 2 - void *point_tree 40 8 - float *point_data 48 8 - float noise_size 56 4 - short noise_depth 60 2 - short noise_influence 62 2 - short noise_basis 64 2 - short pdpad3 66 6 - float noise_fac 72 4 - float speed_scale 76 4 - float falloff_speed_scale 80 4 - float pdpad2 84 4 - ColorBand *coba 88 8 - CurveMapping *falloff_curve 96 8 - -VoxelData 1088 - - int resol 0 12 - int interp_type 12 4 - short file_format 16 2 - short flag 18 2 - short extend 20 2 - short smoked_type 22 2 - short data_type 24 2 - short pad 26 2 - int _pad 28 4 - Object *object 32 8 - float int_multiplier 40 4 - int still_frame 44 4 - char source_path 48 1024 - float *dataset 1072 8 - int cachedframe 1080 4 - int ok 1084 4 - -OceanTex 80 - - Object *object 0 8 - char oceanmod 8 64 - int output 72 4 - int pad 76 4 - -Tex 416 - - ID id 0 120 - AnimData *adt 120 8 - float noisesize 128 4 - float turbul 132 4 - float bright 136 4 - float contrast 140 4 - float saturation 144 4 - float rfac 148 4 - float gfac 152 4 - float bfac 156 4 - float filtersize 160 4 - float pad2 164 4 - float mg_H 168 4 - float mg_lacunarity 172 4 - float mg_octaves 176 4 - float mg_offset 180 4 - float mg_gain 184 4 - float dist_amount 188 4 - float ns_outscale 192 4 - float vn_w1 196 4 - float vn_w2 200 4 - float vn_w3 204 4 - float vn_w4 208 4 - float vn_mexp 212 4 - short vn_distm 216 2 - short vn_coltype 218 2 - short noisedepth 220 2 - short noisetype 222 2 - short noisebasis 224 2 - short noisebasis2 226 2 - short imaflag 228 2 - short flag 230 2 - short type 232 2 - short stype 234 2 - float cropxmin 236 4 - float cropymin 240 4 - float cropxmax 244 4 - float cropymax 248 4 - int texfilter 252 4 - int afmax 256 4 - short xrepeat 260 2 - short yrepeat 262 2 - short extend 264 2 - short fie_ima 266 2 - int len 268 4 - int frames 272 4 - int offset 276 4 - int sfra 280 4 - float checkerdist 284 4 - float nabla 288 4 - float pad1 292 4 - ImageUser iuser 296 40 - bNodeTree *nodetree 336 8 - Ipo *ipo 344 8 - Image *ima 352 8 - ColorBand *coba 360 8 - EnvMap *env 368 8 - PreviewImage *preview 376 8 - PointDensity *pd 384 8 - VoxelData *vd 392 8 - OceanTex *ot 400 8 - char use_nodes 408 1 - char pad 409 7 - -TexMapping 144 - - float loc 0 12 - float rot 12 12 - float size 24 12 - int flag 36 4 - char projx 40 1 - char projy 41 1 - char projz 42 1 - char mapping 43 1 - int type 44 4 - float mat 48 64 - float min 112 12 - float max 124 12 - Object *ob 136 8 - -ColorMapping 824 - - ColorBand coba 0 776 - float bright 776 4 - float contrast 780 4 - float saturation 784 4 - int flag 788 4 - float blend_color 792 12 - float blend_factor 804 4 - int blend_type 808 4 - int pad 812 12 - -Lamp 528 - - ID id 0 120 - AnimData *adt 120 8 - short type 128 2 - short flag 130 2 - int mode 132 4 - short colormodel 136 2 - short totex 138 2 - float r 140 4 - float g 144 4 - float b 148 4 - float k 152 4 - float shdwr 156 4 - float shdwg 160 4 - float shdwb 164 4 - float shdwpad 168 4 - float energy 172 4 - float dist 176 4 - float spotsize 180 4 - float spotblend 184 4 - float haint 188 4 - float att1 192 4 - float att2 196 4 - CurveMapping *curfalloff 200 8 - short falloff_type 208 2 - short pad2 210 2 - float clipsta 212 4 - float clipend 216 4 - float shadspotsize 220 4 - float bias 224 4 - float soft 228 4 - float compressthresh 232 4 - float bleedbias 236 4 - float pad5 240 8 - short bufsize 248 2 - short samp 250 2 - short buffers 252 2 - short filtertype 254 2 - char bufflag 256 1 - char buftype 257 1 - short ray_samp 258 2 - short ray_sampy 260 2 - short ray_sampz 262 2 - short ray_samp_type 264 2 - short area_shape 266 2 - float area_size 268 4 - float area_sizey 272 4 - float area_sizez 276 4 - float adapt_thresh 280 4 - short ray_samp_method 284 2 - short shadowmap_type 286 2 - short texact 288 2 - short shadhalostep 290 2 - short sun_effect_type 292 2 - short skyblendtype 294 2 - float horizon_brightness 296 4 - float spread 300 4 - float sun_brightness 304 4 - float sun_size 308 4 - float backscattered_light 312 4 - float sun_intensity 316 4 - float atm_turbidity 320 4 - float atm_inscattering_factor 324 4 - float atm_extinction_factor 328 4 - float atm_distance_factor 332 4 - float skyblendfac 336 4 - float sky_exposure 340 4 - float shadow_frustum_size 344 4 - short sky_colorspace 348 2 - char pad4 350 2 - Ipo *ipo 352 8 - MTex *mtex 360 144 - short pr_texture 504 2 - short use_nodes 506 2 - char pad6 508 4 - PreviewImage *preview 512 8 - bNodeTree *nodetree 520 8 - -VolumeSettings 88 - - float density 0 4 - float emission 4 4 - float scattering 8 4 - float reflection 12 4 - float emission_col 16 12 - float transmission_col 28 12 - float reflection_col 40 12 - float density_scale 52 4 - float depth_cutoff 56 4 - float asymmetry 60 4 - short stepsize_type 64 2 - short shadeflag 66 2 - short shade_type 68 2 - short precache_resolution 70 2 - float stepsize 72 4 - float ms_diff 76 4 - float ms_intensity 80 4 - float ms_spread 84 4 - -GameSettings 16 - - int flag 0 4 - int alpha_blend 4 4 - int face_orientation 8 4 - int pad1 12 4 - -Material 904 - - ID id 0 120 - AnimData *adt 120 8 - short material_type 128 2 - short flag 130 2 - float r 132 4 - float g 136 4 - float b 140 4 - float specr 144 4 - float specg 148 4 - float specb 152 4 - float mirr 156 4 - float mirg 160 4 - float mirb 164 4 - float ambr 168 4 - float ambb 172 4 - float ambg 176 4 - float amb 180 4 - float emit 184 4 - float ang 188 4 - float spectra 192 4 - float ray_mirror 196 4 - float alpha 200 4 - float ref 204 4 - float spec 208 4 - float zoffs 212 4 - float add 216 4 - float translucency 220 4 - VolumeSettings vol 224 88 - GameSettings game 312 16 - float fresnel_mir 328 4 - float fresnel_mir_i 332 4 - float fresnel_tra 336 4 - float fresnel_tra_i 340 4 - float filter 344 4 - float tx_limit 348 4 - float tx_falloff 352 4 - short ray_depth 356 2 - short ray_depth_tra 358 2 - short har 360 2 - char seed1 362 1 - char seed2 363 1 - float gloss_mir 364 4 - float gloss_tra 368 4 - short samp_gloss_mir 372 2 - short samp_gloss_tra 374 2 - float adapt_thresh_mir 376 4 - float adapt_thresh_tra 380 4 - float aniso_gloss_mir 384 4 - float dist_mir 388 4 - short fadeto_mir 392 2 - short shade_flag 394 2 - int mode 396 4 - int mode_l 400 4 - short flarec 404 2 - short starc 406 2 - short linec 408 2 - short ringc 410 2 - float hasize 412 4 - float flaresize 416 4 - float subsize 420 4 - float flareboost 424 4 - float strand_sta 428 4 - float strand_end 432 4 - float strand_ease 436 4 - float strand_surfnor 440 4 - float strand_min 444 4 - float strand_widthfade 448 4 - char strand_uvname 452 64 - float sbias 516 4 - float lbias 520 4 - float shad_alpha 524 4 - int septex 528 4 - char rgbsel 532 1 - char texact 533 1 - char pr_type 534 1 - char use_nodes 535 1 - short pr_lamp 536 2 - short pr_texture 538 2 - short ml_flag 540 2 - char mapflag 542 1 - char pad 543 1 - short diff_shader 544 2 - short spec_shader 546 2 - float roughness 548 4 - float refrac 552 4 - float param 556 16 - float rms 572 4 - float darkness 576 4 - short texco 580 2 - short mapto 582 2 - ColorBand *ramp_col 584 8 - ColorBand *ramp_spec 592 8 - char rampin_col 600 1 - char rampin_spec 601 1 - char rampblend_col 602 1 - char rampblend_spec 603 1 - short ramp_show 604 2 - short pad3 606 2 - float rampfac_col 608 4 - float rampfac_spec 612 4 - MTex *mtex 616 144 - bNodeTree *nodetree 760 8 - Ipo *ipo 768 8 - Group *group 776 8 - PreviewImage *preview 784 8 - float friction 792 4 - float fh 796 4 - float reflect 800 4 - float fhdist 804 4 - float xyfrict 808 4 - short dynamode 812 2 - short pad2 814 2 - float sss_radius 816 12 - float sss_col 828 12 - float sss_error 840 4 - float sss_scale 844 4 - float sss_ior 848 4 - float sss_colfac 852 4 - float sss_texfac 856 4 - float sss_front 860 4 - float sss_back 864 4 - short sss_flag 868 2 - short sss_preset 870 2 - int mapto_textured 872 4 - short shadowonly_flag 876 2 - short index 878 2 - short vcol_alpha 880 2 - short pad4 882 6 - ListBase gpumaterial 888 16 - -VFont 1168 - - ID id 0 120 - char name 120 1024 - VFontData *data 1144 8 - PackedFile *packedfile 1152 8 - PackedFile *temp_pf 1160 8 - -MetaElem 104 - - MetaElem *next 0 8 - MetaElem *prev 8 8 - BoundBox *bb 16 8 - short type 24 2 - short flag 26 2 - short selcol1 28 2 - short selcol2 30 2 - float x 32 4 - float y 36 4 - float z 40 4 - float quat 44 16 - float expx 60 4 - float expy 64 4 - float expz 68 4 - float rad 72 4 - float rad2 76 4 - float s 80 4 - float len 84 4 - float *mat 88 8 - float *imat 96 8 - -MetaBall 248 - - ID id 0 120 - AnimData *adt 120 8 - ListBase elems 128 16 - ListBase disp 144 16 - ListBase *editelems 160 8 - Ipo *ipo 168 8 - Material **mat 176 8 - char flag 184 1 - char flag2 185 1 - short totcol 186 2 - short texflag 188 2 - short pad 190 2 - float loc 192 12 - float size 204 12 - float rot 216 12 - float wiresize 228 4 - float rendersize 232 4 - float thresh 236 4 - MetaElem *lastelem 240 8 - -BezTriple 56 - - float vec 0 36 - float alfa 36 4 - float weight 40 4 - float radius 44 4 - short ipo 48 2 - char h1 50 1 - char h2 51 1 - char f1 52 1 - char f2 53 1 - char f3 54 1 - char hide 55 1 - -BPoint 36 - - float vec 0 16 - float alfa 16 4 - float weight 20 4 - short f1 24 2 - short hide 26 2 - float radius 28 4 - float pad 32 4 - -Nurb 80 - - Nurb *next 0 8 - Nurb *prev 8 8 - short type 16 2 - short mat_nr 18 2 - short hide 20 2 - short flag 22 2 - short pntsu 24 2 - short pntsv 26 2 - short resolu 28 2 - short resolv 30 2 - short orderu 32 2 - short orderv 34 2 - short flagu 36 2 - short flagv 38 2 - float *knotsu 40 8 - float *knotsv 48 8 - BPoint *bp 56 8 - BezTriple *bezt 64 8 - short tilt_interp 72 2 - short radius_interp 74 2 - int charidx 76 4 - -CharInfo 8 - - short kern 0 2 - short mat_nr 2 2 - char flag 4 1 - char pad 5 1 - short pad2 6 2 - -TextBox 16 - - float x 0 4 - float y 4 4 - float w 8 4 - float h 12 4 - -EditNurb 32 - - ListBase nurbs 0 16 - GHash *keyindex 16 8 - int shapenr 24 4 - char pad 28 4 - -Curve 504 - - ID id 0 120 - AnimData *adt 120 8 - BoundBox *bb 128 8 - ListBase nurb 136 16 - ListBase disp 152 16 - EditNurb *editnurb 168 8 - Object *bevobj 176 8 - Object *taperobj 184 8 - Object *textoncurve 192 8 - Ipo *ipo 200 8 - Key *key 208 8 - Material **mat 216 8 - float loc 224 12 - float size 236 12 - float rot 248 12 - short type 260 2 - short texflag 262 2 - short drawflag 264 2 - short twist_mode 266 2 - float twist_smooth 268 4 - float smallcaps_scale 272 4 - int pathlen 276 4 - short bevresol 280 2 - short totcol 282 2 - int flag 284 4 - float width 288 4 - float ext1 292 4 - float ext2 296 4 - short resolu 300 2 - short resolv 302 2 - short resolu_ren 304 2 - short resolv_ren 306 2 - int actnu 308 4 - void *lastsel 312 8 - short len 320 2 - short lines 322 2 - short pos 324 2 - short spacemode 326 2 - float spacing 328 4 - float linedist 332 4 - float shear 336 4 - float fsize 340 4 - float wordspace 344 4 - float ulpos 348 4 - float ulheight 352 4 - float xof 356 4 - float yof 360 4 - float linewidth 364 4 - char *str 368 8 - SelBox *selboxes 376 8 - EditFont *editfont 384 8 - char family 392 24 - VFont *vfont 416 8 - VFont *vfontb 424 8 - VFont *vfonti 432 8 - VFont *vfontbi 440 8 - int sepchar 448 4 - float ctime 452 4 - int totbox 456 4 - int actbox 460 4 - TextBox *tb 464 8 - int selstart 472 4 - int selend 476 4 - CharInfo *strinfo 480 8 - CharInfo curinfo 488 8 - float bevfac1 496 4 - float bevfac2 500 4 - -Mesh 1336 - - ID id 0 120 - AnimData *adt 120 8 - BoundBox *bb 128 8 - Ipo *ipo 136 8 - Key *key 144 8 - Material **mat 152 8 - MSelect *mselect 160 8 - MPoly *mpoly 168 8 - MTexPoly *mtpoly 176 8 - MLoop *mloop 184 8 - MLoopUV *mloopuv 192 8 - MLoopCol *mloopcol 200 8 - MFace *mface 208 8 - MTFace *mtface 216 8 - TFace *tface 224 8 - MVert *mvert 232 8 - MEdge *medge 240 8 - MDeformVert *dvert 248 8 - MCol *mcol 256 8 - Mesh *texcomesh 264 8 - BMEditMesh *edit_btmesh 272 8 - CustomData vdata 280 192 - CustomData edata 472 192 - CustomData fdata 664 192 - CustomData pdata 856 192 - CustomData ldata 1048 192 - int totvert 1240 4 - int totedge 1244 4 - int totface 1248 4 - int totselect 1252 4 - int totpoly 1256 4 - int totloop 1260 4 - int act_face 1264 4 - float loc 1268 12 - float size 1280 12 - float rot 1292 12 - int drawflag 1304 4 - short texflag 1308 2 - short pad2 1310 6 - short smoothresh 1316 2 - short flag 1318 2 - char cd_flag 1320 1 - char pad 1321 1 - char subdiv 1322 1 - char subdivr 1323 1 - char subsurftype 1324 1 - char editflag 1325 1 - short totcol 1326 2 - Multires *mr 1328 8 - -TFace 64 - - void *tpage 0 8 - float uv 8 32 - int col 40 16 - char flag 56 1 - char transp 57 1 - short mode 58 2 - short tile 60 2 - short unwrap 62 2 - -MFace 20 - - int v1 0 4 - int v2 4 4 - int v3 8 4 - int v4 12 4 - short mat_nr 16 2 - char edcode 18 1 - char flag 19 1 - -MEdge 12 - - int v1 0 4 - int v2 4 4 - char crease 8 1 - char bweight 9 1 - short flag 10 2 - -MDeformWeight 8 - - int def_nr 0 4 - float weight 4 4 - -MDeformVert 16 - - MDeformWeight *dw 0 8 - int totweight 8 4 - int flag 12 4 - -MVert 20 - - float co 0 12 - short no 12 6 - char flag 18 1 - char bweight 19 1 - -MCol 4 - - char a 0 1 - char r 1 1 - char g 2 1 - char b 3 1 - -MPoly 12 - - int loopstart 0 4 - int totloop 4 4 - short mat_nr 8 2 - char flag 10 1 - char pad 11 1 - -MLoop 8 - - int v 0 4 - int e 4 4 - -MTexPoly 16 - - Image *tpage 0 8 - char flag 8 1 - char transp 9 1 - short mode 10 2 - short tile 12 2 - short pad 14 2 - -MLoopUV 12 - - float uv 0 8 - int flag 8 4 - -MLoopCol 4 - - char r 0 1 - char g 1 1 - char b 2 1 - char a 3 1 - -MSelect 8 - - int index 0 4 - int type 4 4 - -MTFace 48 - - float uv 0 32 - Image *tpage 32 8 - char flag 40 1 - char transp 41 1 - short mode 42 2 - short tile 44 2 - short unwrap 46 2 - -MFloatProperty 4 - - float f 0 4 - -MIntProperty 4 - - int i 0 4 - -MStringProperty 256 - - char s 0 255 - char s_len 255 1 - -OrigSpaceFace 32 - - float uv 0 32 - -OrigSpaceLoop 8 - - float uv 0 8 - -MDisps 20 - - int totdisp 0 4 - int level 4 4 - float (*disps)() 8 4 - int *hidden 12 8 - -MultiresCol 16 - - float a 0 4 - float r 4 4 - float g 8 4 - float b 12 4 - -MultiresColFace 64 - - MultiresCol col 0 64 - -MultiresFace 24 - - int v 0 16 - int mid 16 4 - char flag 20 1 - char mat_nr 21 1 - char pad 22 2 - -MultiresEdge 12 - - int v 0 8 - int mid 8 4 - -MultiresLevel 64 - - MultiresLevel *next 0 8 - MultiresLevel *prev 8 8 - MultiresFace *faces 16 8 - MultiresColFace *colfaces 24 8 - MultiresEdge *edges 32 8 - int totvert 40 4 - int totface 44 4 - int totedge 48 4 - int pad 52 4 - MVert *verts 56 8 - -Multires 432 - - ListBase levels 0 16 - MVert *verts 16 8 - char level_count 24 1 - char current 25 1 - char newlvl 26 1 - char edgelvl 27 1 - char pinlvl 28 1 - char renderlvl 29 1 - char use_col 30 1 - char flag 31 1 - CustomData vdata 32 192 - CustomData fdata 224 192 - short *edge_flags 416 8 - char *edge_creases 424 8 - -MRecast 4 - - int i 0 4 - -GridPaintMask 16 - - float *data 0 8 - int level 8 4 - int pad 12 4 - -MVertSkin 16 - - float radius 0 12 - int flag 12 4 - -FreestyleEdge 4 - - char flag 0 1 - char pad 1 3 - -FreestyleFace 4 - - char flag 0 1 - char pad 1 3 - -ModifierData 112 - - ModifierData *next 0 8 - ModifierData *prev 8 8 - int type 16 4 - int mode 20 4 - int stackindex 24 4 - int pad 28 4 - char name 32 64 - Scene *scene 96 8 - char *error 104 8 - -MappingInfoModifierData 200 - - ModifierData modifier 0 112 - Tex *texture 112 8 - Object *map_object 120 8 - char uvlayer_name 128 64 - int uvlayer_tmp 192 4 - int texmapping 196 4 - -SubsurfModifierData 136 - - ModifierData modifier 0 112 - short subdivType 112 2 - short levels 114 2 - short renderLevels 116 2 - short flags 118 2 - void *emCache 120 8 - void *mCache 128 8 - -LatticeModifierData 192 - - ModifierData modifier 0 112 - Object *object 112 8 - char name 120 64 - float strength 184 4 - char pad 188 4 - -CurveModifierData 192 - - ModifierData modifier 0 112 - Object *object 112 8 - char name 120 64 - short defaxis 184 2 - char pad 186 6 - -BuildModifierData 128 - - ModifierData modifier 0 112 - float start 112 4 - float length 116 4 - int randomize 120 4 - int seed 124 4 - -MaskModifierData 192 - - ModifierData modifier 0 112 - Object *ob_arm 112 8 - char vgroup 120 64 - int mode 184 4 - int flag 188 4 - -ArrayModifierData 192 - - ModifierData modifier 0 112 - Object *start_cap 112 8 - Object *end_cap 120 8 - Object *curve_ob 128 8 - Object *offset_ob 136 8 - float offset 144 12 - float scale 156 12 - float length 168 4 - float merge_dist 172 4 - int fit_type 176 4 - int offset_type 180 4 - int flags 184 4 - int count 188 4 - -MirrorModifierData 128 - - ModifierData modifier 0 112 - short axis 112 2 - short flag 114 2 - float tolerance 116 4 - Object *mirror_ob 120 8 - -EdgeSplitModifierData 120 - - ModifierData modifier 0 112 - float split_angle 112 4 - int flags 116 4 - -BevelModifierData 200 - - ModifierData modifier 0 112 - float value 112 4 - int res 116 4 - int pad 120 4 - short flags 124 2 - short val_flags 126 2 - short lim_flags 128 2 - short e_flags 130 2 - float bevel_angle 132 4 - char defgrp_name 136 64 - -SmokeModifierData 144 - - ModifierData modifier 0 112 - SmokeDomainSettings *domain 112 8 - SmokeFlowSettings *flow 120 8 - SmokeCollSettings *coll 128 8 - float time 136 4 - int type 140 4 - -DisplaceModifierData 280 - - ModifierData modifier 0 112 - Tex *texture 112 8 - Object *map_object 120 8 - char uvlayer_name 128 64 - int uvlayer_tmp 192 4 - int texmapping 196 4 - float strength 200 4 - int direction 204 4 - char defgrp_name 208 64 - float midlevel 272 4 - int pad 276 4 - -UVProjectModifierData 296 - - ModifierData modifier 0 112 - Object *projectors 112 80 - Image *image 192 8 - int flags 200 4 - int num_projectors 204 4 - float aspectx 208 4 - float aspecty 212 4 - float scalex 216 4 - float scaley 220 4 - char uvlayer_name 224 64 - int uvlayer_tmp 288 4 - int pad 292 4 - -DecimateModifierData 200 - - ModifierData modifier 0 112 - float percent 112 4 - short iter 116 2 - char delimit 118 1 - char pad 119 1 - float angle 120 4 - char defgrp_name 124 64 - short flag 188 2 - short mode 190 2 - int face_count 192 4 - int pad2 196 4 - -SmoothModifierData 184 - - ModifierData modifier 0 112 - float fac 112 4 - char defgrp_name 116 64 - short flag 180 2 - short repeat 182 2 - -CastModifierData 200 - - ModifierData modifier 0 112 - Object *object 112 8 - float fac 120 4 - float radius 124 4 - float size 128 4 - char defgrp_name 132 64 - short flag 196 2 - short type 198 2 - -WaveModifierData 320 - - ModifierData modifier 0 112 - Tex *texture 112 8 - Object *map_object 120 8 - char uvlayer_name 128 64 - int uvlayer_tmp 192 4 - int texmapping 196 4 - Object *objectcenter 200 8 - char defgrp_name 208 64 - short flag 272 2 - short pad 274 2 - float startx 276 4 - float starty 280 4 - float height 284 4 - float width 288 4 - float narrow 292 4 - float speed 296 4 - float damp 300 4 - float falloff 304 4 - float timeoffs 308 4 - float lifetime 312 4 - float pad1 316 4 - -ArmatureModifierData 200 - - ModifierData modifier 0 112 - short deformflag 112 2 - short multi 114 2 - int pad2 116 4 - Object *object 120 8 - float *prevCos 128 8 - char defgrp_name 136 64 - -HookModifierData 344 - - ModifierData modifier 0 112 - Object *object 112 8 - char subtarget 120 64 - float parentinv 184 64 - float cent 248 12 - float falloff 260 4 - int *indexar 264 8 - int totindex 272 4 - float force 276 4 - char name 280 64 - -SoftbodyModifierData 112 - - ModifierData modifier 0 112 - -ClothModifierData 168 - - ModifierData modifier 0 112 - Scene *scene 112 8 - Cloth *clothObject 120 8 - ClothSimSettings *sim_parms 128 8 - ClothCollSettings *coll_parms 136 8 - PointCache *point_cache 144 8 - ListBase ptcaches 152 16 - -CollisionModifierData 192 - - ModifierData modifier 0 112 - MVert *x 112 8 - MVert *xnew 120 8 - MVert *xold 128 8 - MVert *current_xnew 136 8 - MVert *current_x 144 8 - MVert *current_v 152 8 - MFace *mfaces 160 8 - int numverts 168 4 - int numfaces 172 4 - float time_x 176 4 - float time_xnew 180 4 - BVHTree *bvhtree 184 8 - -SurfaceModifierData 152 - - ModifierData modifier 0 112 - MVert *x 112 8 - MVert *v 120 8 - DerivedMesh *dm 128 8 - BVHTreeFromMesh *bvhtree 136 8 - int cfra 144 4 - int numverts 148 4 - -BooleanModifierData 128 - - ModifierData modifier 0 112 - Object *object 112 8 - int operation 120 4 - int pad 124 4 - -MDefInfluence 8 - - int vertex 0 4 - float weight 4 4 - -MDefCell 8 - - int offset 0 4 - int totinfluence 4 4 - -MeshDeformModifierData 360 - - ModifierData modifier 0 112 - Object *object 112 8 - char defgrp_name 120 64 - short gridsize 184 2 - short flag 186 2 - short mode 188 2 - short pad 190 2 - MDefInfluence *bindinfluences 192 8 - int *bindoffsets 200 8 - float *bindcagecos 208 8 - int totvert 216 4 - int totcagevert 220 4 - MDefCell *dyngrid 224 8 - MDefInfluence *dyninfluences 232 8 - int *dynverts 240 8 - int *pad2 248 8 - int dyngridsize 256 4 - int totinfluence 260 4 - float dyncellmin 264 12 - float dyncellwidth 276 4 - float bindmat 280 64 - float *bindweights 344 8 - float *bindcos 352 8 - void (*bindfunc)() 360 0 - -ParticleSystemModifierData 144 - - ModifierData modifier 0 112 - ParticleSystem *psys 112 8 - DerivedMesh *dm 120 8 - int totdmvert 128 4 - int totdmedge 132 4 - int totdmface 136 4 - short flag 140 2 - short rt 142 2 - -ParticleInstanceModifierData 136 - - ModifierData modifier 0 112 - Object *ob 112 8 - short psys 120 2 - short flag 122 2 - short axis 124 2 - short rt 126 2 - float position 128 4 - float random_position 132 4 - -ExplodeModifierData 192 - - ModifierData modifier 0 112 - int *facepa 112 8 - short flag 120 2 - short vgroup 122 2 - float protect 124 4 - char uvname 128 64 - -MultiresModifierData 120 - - ModifierData modifier 0 112 - char lvl 112 1 - char sculptlvl 113 1 - char renderlvl 114 1 - char totlvl 115 1 - char simple 116 1 - char flags 117 1 - char pad 118 2 - -FluidsimModifierData 128 - - ModifierData modifier 0 112 - FluidsimSettings *fss 112 8 - PointCache *point_cache 120 8 - -ShrinkwrapModifierData 208 - - ModifierData modifier 0 112 - Object *target 112 8 - Object *auxTarget 120 8 - char vgroup_name 128 64 - float keepDist 192 4 - short shrinkType 196 2 - short shrinkOpts 198 2 - float projLimit 200 4 - char projAxis 204 1 - char subsurfLevels 205 1 - char pad 206 2 - -SimpleDeformModifierData 200 - - ModifierData modifier 0 112 - Object *origin 112 8 - char vgroup_name 120 64 - float factor 184 4 - float limit 188 8 - char mode 196 1 - char axis 197 1 - char pad 198 2 - -ShapeKeyModifierData 112 - - ModifierData modifier 0 112 - -SolidifyModifierData 216 - - ModifierData modifier 0 112 - char defgrp_name 112 64 - float offset 176 4 - float offset_fac 180 4 - float offset_fac_vg 184 4 - float offset_clamp 188 4 - float pad 192 4 - float crease_inner 196 4 - float crease_outer 200 4 - float crease_rim 204 4 - int flag 208 4 - short mat_ofs 212 2 - short mat_ofs_rim 214 2 - -ScrewModifierData 144 - - ModifierData modifier 0 112 - Object *ob_axis 112 8 - int steps 120 4 - int render_steps 124 4 - int iter 128 4 - float screw_ofs 132 4 - float angle 136 4 - short axis 140 2 - short flag 142 2 - -OceanModifierData 1296 - - ModifierData modifier 0 112 - Ocean *ocean 112 8 - OceanCache *oceancache 120 8 - int resolution 128 4 - int spatial_size 132 4 - float wind_velocity 136 4 - float damp 140 4 - float smallest_wave 144 4 - float depth 148 4 - float wave_alignment 152 4 - float wave_direction 156 4 - float wave_scale 160 4 - float chop_amount 164 4 - float foam_coverage 168 4 - float time 172 4 - int bakestart 176 4 - int bakeend 180 4 - char cachepath 184 1024 - char foamlayername 1208 64 - char cached 1272 1 - char geometry_mode 1273 1 - char flag 1274 1 - char refresh 1275 1 - short repeat_x 1276 2 - short repeat_y 1278 2 - int seed 1280 4 - float size 1284 4 - float foam_fade 1288 4 - int pad 1292 4 - -WarpModifierData 304 - - ModifierData modifier 0 112 - Tex *texture 112 8 - Object *map_object 120 8 - char uvlayer_name 128 64 - int uvlayer_tmp 192 4 - int texmapping 196 4 - Object *object_from 200 8 - Object *object_to 208 8 - CurveMapping *curfalloff 216 8 - char defgrp_name 224 64 - float strength 288 4 - float falloff_radius 292 4 - char flag 296 1 - char falloff_type 297 1 - char pad 298 6 - -WeightVGEditModifierData 360 - - ModifierData modifier 0 112 - char defgrp_name 112 64 - short edit_flags 176 2 - short falloff_type 178 2 - float default_weight 180 4 - CurveMapping *cmap_curve 184 8 - float add_threshold 192 4 - float rem_threshold 196 4 - float mask_constant 200 4 - char mask_defgrp_name 204 64 - int mask_tex_use_channel 268 4 - Tex *mask_texture 272 8 - Object *mask_tex_map_obj 280 8 - int mask_tex_mapping 288 4 - char mask_tex_uvlayer_name 292 64 - int pad_i1 356 4 - -WeightVGMixModifierData 416 - - ModifierData modifier 0 112 - char defgrp_name_a 112 64 - char defgrp_name_b 176 64 - float default_weight_a 240 4 - float default_weight_b 244 4 - char mix_mode 248 1 - char mix_set 249 1 - char pad_c1 250 6 - float mask_constant 256 4 - char mask_defgrp_name 260 64 - int mask_tex_use_channel 324 4 - Tex *mask_texture 328 8 - Object *mask_tex_map_obj 336 8 - int mask_tex_mapping 344 4 - char mask_tex_uvlayer_name 348 64 - int pad_i1 412 4 - -WeightVGProximityModifierData 360 - - ModifierData modifier 0 112 - char defgrp_name 112 64 - int proximity_mode 176 4 - int proximity_flags 180 4 - Object *proximity_ob_target 184 8 - float mask_constant 192 4 - char mask_defgrp_name 196 64 - int mask_tex_use_channel 260 4 - Tex *mask_texture 264 8 - Object *mask_tex_map_obj 272 8 - int mask_tex_mapping 280 4 - char mask_tex_uvlayer_name 284 64 - float min_dist 348 4 - float max_dist 352 4 - short falloff_type 356 2 - short pad_s1 358 2 - -DynamicPaintModifierData 136 - - ModifierData modifier 0 112 - DynamicPaintCanvasSettings *canvas 112 8 - DynamicPaintBrushSettings *brush 120 8 - int type 128 4 - int pad 132 4 - -RemeshModifierData 128 - - ModifierData modifier 0 112 - float threshold 112 4 - float scale 116 4 - float hermite_num 120 4 - char depth 124 1 - char flag 125 1 - char mode 126 1 - char pad 127 1 - -SkinModifierData 120 - - ModifierData modifier 0 112 - float branch_smoothing 112 4 - char flag 116 1 - char symmetry_axes 117 1 - char pad 118 2 - -TriangulateModifierData 120 - - ModifierData modifier 0 112 - int flag 112 4 - int pad 116 4 - -LaplacianSmoothModifierData 192 - - ModifierData modifier 0 112 - float lambda 112 4 - float lambda_border 116 4 - float pad1 120 4 - char defgrp_name 124 64 - short flag 188 2 - short repeat 190 2 - -UVWarpModifierData 400 - - ModifierData modifier 0 112 - char axis_u 112 1 - char axis_v 113 1 - char pad 114 6 - float center 120 8 - Object *object_src 128 8 - char bone_src 136 64 - Object *object_dst 200 8 - char bone_dst 208 64 - char vgroup_name 272 64 - char uvlayer_name 336 64 - -MeshCacheModifierData 1176 - - ModifierData modifier 0 112 - char flag 112 1 - char type 113 1 - char time_mode 114 1 - char play_mode 115 1 - char forward_axis 116 1 - char up_axis 117 1 - char flip_axis 118 1 - char interp 119 1 - float factor 120 4 - char deform_mode 124 1 - char pad 125 7 - float frame_start 132 4 - float frame_scale 136 4 - float eval_frame 140 4 - float eval_time 144 4 - float eval_factor 148 4 - char filepath 152 1024 - -EditLatt 16 - - Lattice *latt 0 8 - int shapenr 8 4 - char pad 12 4 - -Lattice 280 - - ID id 0 120 - AnimData *adt 120 8 - short pntsu 128 2 - short pntsv 130 2 - short pntsw 132 2 - short flag 134 2 - short opntsu 136 2 - short opntsv 138 2 - short opntsw 140 2 - short pad2 142 2 - char typeu 144 1 - char typev 145 1 - char typew 146 1 - char pad3 147 1 - int actbp 148 4 - float fu 152 4 - float fv 156 4 - float fw 160 4 - float du 164 4 - float dv 168 4 - float dw 172 4 - BPoint *def 176 8 - Ipo *ipo 184 8 - Key *key 192 8 - MDeformVert *dvert 200 8 - char vgroup 208 64 - EditLatt *editlatt 272 8 - -bDeformGroup 88 - - bDeformGroup *next 0 8 - bDeformGroup *prev 8 8 - char name 16 64 - char flag 80 1 - char pad 81 7 - -BoundBox 104 - - float vec 0 96 - int flag 96 4 - int pad 100 4 - -Object 1416 - - ID id 0 120 - AnimData *adt 120 8 - SculptSession *sculpt 128 8 - short type 136 2 - short partype 138 2 - int par1 140 4 - int par2 144 4 - int par3 148 4 - char parsubstr 152 64 - Object *parent 216 8 - Object *track 224 8 - Object *proxy 232 8 - Object *proxy_group 240 8 - Object *proxy_from 248 8 - Ipo *ipo 256 8 - BoundBox *bb 264 8 - bAction *action 272 8 - bAction *poselib 280 8 - bPose *pose 288 8 - void *data 296 8 - bGPdata *gpd 304 8 - bAnimVizSettings avs 312 48 - bMotionPath *mpath 360 8 - ListBase constraintChannels 368 16 - ListBase effect 384 16 - ListBase defbase 400 16 - ListBase modifiers 416 16 - int mode 432 4 - int restore_mode 436 4 - Material **mat 440 8 - char *matbits 448 8 - int totcol 456 4 - int actcol 460 4 - float loc 464 12 - float dloc 476 12 - float orig 488 12 - float size 500 12 - float dsize 512 12 - float dscale 524 12 - float rot 536 12 - float drot 548 12 - float quat 560 16 - float dquat 576 16 - float rotAxis 592 12 - float drotAxis 604 12 - float rotAngle 616 4 - float drotAngle 620 4 - float obmat 624 64 - float parentinv 688 64 - float constinv 752 64 - float imat 816 64 - float imat_ren 880 64 - int lay 944 4 - float sf 948 4 - short flag 952 2 - short colbits 954 2 - short transflag 956 2 - short protectflag 958 2 - short trackflag 960 2 - short upflag 962 2 - short nlaflag 964 2 - short ipoflag 966 2 - short scaflag 968 2 - char scavisflag 970 1 - char depsflag 971 1 - int dupon 972 4 - int dupoff 976 4 - int dupsta 980 4 - int dupend 984 4 - float mass 988 4 - float damping 992 4 - float inertia 996 4 - float formfactor 1000 4 - float rdamping 1004 4 - float sizefac 1008 4 - float margin 1012 4 - float max_vel 1016 4 - float min_vel 1020 4 - float m_contactProcessingThreshold 1024 4 - float obstacleRad 1028 4 - float step_height 1032 4 - float jump_speed 1036 4 - float fall_speed 1040 4 - short col_group 1044 2 - short col_mask 1046 2 - short rotmode 1048 2 - char boundtype 1050 1 - char collision_boundtype 1051 1 - short dtx 1052 2 - char dt 1054 1 - char empty_drawtype 1055 1 - float empty_drawsize 1056 4 - float dupfacesca 1060 4 - ListBase prop 1064 16 - ListBase sensors 1080 16 - ListBase controllers 1096 16 - ListBase actuators 1112 16 - float bbsize 1128 12 - short index 1140 2 - short actdef 1142 2 - float col 1144 16 - int gameflag 1160 4 - int gameflag2 1164 4 - BulletSoftBody *bsoft 1168 8 - char restrictflag 1176 1 - char recalc 1177 1 - short softflag 1178 2 - float anisotropicFriction 1180 12 - ListBase constraints 1192 16 - ListBase nlastrips 1208 16 - ListBase hooks 1224 16 - ListBase particlesystem 1240 16 - PartDeflect *pd 1256 8 - SoftBody *soft 1264 8 - Group *dup_group 1272 8 - char body_type 1280 1 - char shapeflag 1281 1 - short shapenr 1282 2 - float smoothresh 1284 4 - FluidsimSettings *fluidsimSettings 1288 8 - DerivedMesh *derivedDeform 1296 8 - DerivedMesh *derivedFinal 1304 8 - int *pad 1312 8 - uint64_t lastDataMask 1320 8 - uint64_t customdata_mask 1328 8 - int state 1336 4 - int init_state 1340 4 - ListBase gpulamp 1344 16 - ListBase pc_ids 1360 16 - ListBase *duplilist 1376 8 - RigidBodyOb *rigidbody_object 1384 8 - RigidBodyCon *rigidbody_constraint 1392 8 - float ima_ofs 1400 8 - CurveCache *curve_cache 1408 8 - -ObHook 256 - - ObHook *next 0 8 - ObHook *prev 8 8 - Object *parent 16 8 - float parentinv 24 64 - float mat 88 64 - float cent 152 12 - float falloff 164 4 - char name 168 64 - int *indexar 232 8 - int totindex 240 4 - int curindex 244 4 - short type 248 2 - short active 250 2 - float force 252 4 - -DupliObject 224 - - DupliObject *next 0 8 - DupliObject *prev 8 8 - Object *ob 16 8 - int origlay 24 4 - int pad 28 4 - float mat 32 64 - float omat 96 64 - float orco 160 12 - float uv 172 8 - short type 180 2 - char no_draw 182 1 - char animated 183 1 - int persistent_id 184 32 - ParticleSystem *particle_system 216 8 - -PartDeflect 160 - - int flag 0 4 - short deflect 4 2 - short forcefield 6 2 - short falloff 8 2 - short shape 10 2 - short tex_mode 12 2 - short kink 14 2 - short kink_axis 16 2 - short zdir 18 2 - float f_strength 20 4 - float f_damp 24 4 - float f_flow 28 4 - float f_size 32 4 - float f_power 36 4 - float maxdist 40 4 - float mindist 44 4 - float f_power_r 48 4 - float maxrad 52 4 - float minrad 56 4 - float pdef_damp 60 4 - float pdef_rdamp 64 4 - float pdef_perm 68 4 - float pdef_frict 72 4 - float pdef_rfrict 76 4 - float pdef_stickness 80 4 - float absorption 84 4 - float pdef_sbdamp 88 4 - float pdef_sbift 92 4 - float pdef_sboft 96 4 - float clump_fac 100 4 - float clump_pow 104 4 - float kink_freq 108 4 - float kink_shape 112 4 - float kink_amp 116 4 - float free_end 120 4 - float tex_nabla 124 4 - Tex *tex 128 8 - RNG *rng 136 8 - float f_noise 144 4 - int seed 148 4 - Object *f_source 152 8 - -EffectorWeights 80 - - Group *group 0 8 - float weight 8 56 - float global_gravity 64 4 - short flag 68 2 - short rt 70 6 - int pad 76 4 - -PTCacheExtra 32 - - PTCacheExtra *next 0 8 - PTCacheExtra *prev 8 8 - int type 16 4 - int totdata 20 4 - void *data 24 8 - -PTCacheMem 176 - - PTCacheMem *next 0 8 - PTCacheMem *prev 8 8 - int frame 16 4 - int totpoint 20 4 - int data_types 24 4 - int flag 28 4 - void *data 32 64 - void *cur 96 64 - ListBase extradata 160 16 - -PointCache 1312 - - PointCache *next 0 8 - PointCache *prev 8 8 - int flag 16 4 - int step 20 4 - int simframe 24 4 - int startframe 28 4 - int endframe 32 4 - int editframe 36 4 - int last_exact 40 4 - int last_valid 44 4 - int pad 48 4 - int totpoint 52 4 - int index 56 4 - short compression 60 2 - short rt 62 2 - char name 64 64 - char prev_name 128 64 - char info 192 64 - char path 256 1024 - char *cached_frames 1280 8 - ListBase mem_cache 1288 16 - PTCacheEdit *edit 1304 8 - void (*free_edit)() 1312 0 - -SBVertex 16 - - float vec 0 16 - -BulletSoftBody 120 - - int flag 0 4 - float linStiff 4 4 - float angStiff 8 4 - float volume 12 4 - int viterations 16 4 - int piterations 20 4 - int diterations 24 4 - int citerations 28 4 - float kSRHR_CL 32 4 - float kSKHR_CL 36 4 - float kSSHR_CL 40 4 - float kSR_SPLT_CL 44 4 - float kSK_SPLT_CL 48 4 - float kSS_SPLT_CL 52 4 - float kVCF 56 4 - float kDP 60 4 - float kDG 64 4 - float kLF 68 4 - float kPR 72 4 - float kVC 76 4 - float kDF 80 4 - float kMT 84 4 - float kCHR 88 4 - float kKHR 92 4 - float kSHR 96 4 - float kAHR 100 4 - int collisionflags 104 4 - int numclusteriterations 108 4 - float welding 112 4 - float margin 116 4 - -SoftBody 472 - - int totpoint 0 4 - int totspring 4 4 - BodyPoint *bpoint 8 8 - BodySpring *bspring 16 8 - char pad 24 1 - char msg_lock 25 1 - short msg_value 26 2 - float nodemass 28 4 - char namedVG_Mass 32 64 - float grav 96 4 - float mediafrict 100 4 - float rklimit 104 4 - float physics_speed 108 4 - float goalspring 112 4 - float goalfrict 116 4 - float mingoal 120 4 - float maxgoal 124 4 - float defgoal 128 4 - short vertgroup 132 2 - char namedVG_Softgoal 134 64 - short fuzzyness 198 2 - float inspring 200 4 - float infrict 204 4 - char namedVG_Spring_K 208 64 - int sfra 272 4 - int efra 276 4 - int interval 280 4 - short local 284 2 - short solverflags 286 2 - SBVertex **keys 288 8 - int totpointkey 296 4 - int totkey 300 4 - float secondspring 304 4 - float colball 308 4 - float balldamp 312 4 - float ballstiff 316 4 - short sbc_mode 320 2 - short aeroedge 322 2 - short minloops 324 2 - short maxloops 326 2 - short choke 328 2 - short solver_ID 330 2 - short plastic 332 2 - short springpreload 334 2 - SBScratch *scratch 336 8 - float shearstiff 344 4 - float inpush 348 4 - PointCache *pointcache 352 8 - ListBase ptcaches 360 16 - EffectorWeights *effector_weights 376 8 - float lcom 384 12 - float lrot 396 36 - float lscale 432 36 - int last_frame 468 4 - -FluidVertexVelocity 12 - - float vel 0 12 - -FluidsimSettings 1256 - - FluidsimModifierData *fmd 0 8 - int threads 8 4 - int pad1 12 4 - short type 16 2 - short show_advancedoptions 18 2 - short resolutionxyz 20 2 - short previewresxyz 22 2 - float realsize 24 4 - short guiDisplayMode 28 2 - short renderDisplayMode 30 2 - float viscosityValue 32 4 - short viscosityMode 36 2 - short viscosityExponent 38 2 - float grav 40 12 - float animStart 52 4 - float animEnd 56 4 - int bakeStart 60 4 - int bakeEnd 64 4 - int frameOffset 68 4 - int pad2 72 4 - float gstar 76 4 - int maxRefine 80 4 - float iniVelx 84 4 - float iniVely 88 4 - float iniVelz 92 4 - Mesh *orgMesh 96 8 - Mesh *meshBB 104 8 - char surfdataPath 112 1024 - float bbStart 1136 12 - float bbSize 1148 12 - Ipo *ipo 1160 8 - short typeFlags 1168 2 - char domainNovecgen 1170 1 - char volumeInitType 1171 1 - float partSlipValue 1172 4 - int generateTracers 1176 4 - float generateParticles 1180 4 - float surfaceSmoothing 1184 4 - int surfaceSubdivs 1188 4 - int flag 1192 4 - float particleInfSize 1196 4 - float particleInfAlpha 1200 4 - float farFieldSize 1204 4 - FluidVertexVelocity *meshVelocities 1208 8 - int totvert 1216 4 - float cpsTimeStart 1220 4 - float cpsTimeEnd 1224 4 - float cpsQuality 1228 4 - float attractforceStrength 1232 4 - float attractforceRadius 1236 4 - float velocityforceStrength 1240 4 - float velocityforceRadius 1244 4 - int lastgoodframe 1248 4 - float animRate 1252 4 - -World 528 - - ID id 0 120 - AnimData *adt 120 8 - short colormodel 128 2 - short totex 130 2 - short texact 132 2 - short mistype 134 2 - float horr 136 4 - float horg 140 4 - float horb 144 4 - float zenr 148 4 - float zeng 152 4 - float zenb 156 4 - float ambr 160 4 - float ambg 164 4 - float ambb 168 4 - float exposure 172 4 - float exp 176 4 - float range 180 4 - float linfac 184 4 - float logfac 188 4 - float gravity 192 4 - float activityBoxRadius 196 4 - short skytype 200 2 - short mode 202 2 - short occlusionRes 204 2 - short physicsEngine 206 2 - short ticrate 208 2 - short maxlogicstep 210 2 - short physubstep 212 2 - short maxphystep 214 2 - float misi 216 4 - float miststa 220 4 - float mistdist 224 4 - float misthi 228 4 - float starr 232 4 - float starg 236 4 - float starb 240 4 - float stark 244 4 - float starsize 248 4 - float starmindist 252 4 - float stardist 256 4 - float starcolnoise 260 4 - short dofsta 264 2 - short dofend 266 2 - short dofmin 268 2 - short dofmax 270 2 - float aodist 272 4 - float aodistfac 276 4 - float aoenergy 280 4 - float aobias 284 4 - short aomode 288 2 - short aosamp 290 2 - short aomix 292 2 - short aocolor 294 2 - float ao_adapt_thresh 296 4 - float ao_adapt_speed_fac 300 4 - float ao_approx_error 304 4 - float ao_approx_correction 308 4 - float ao_indirect_energy 312 4 - float ao_env_energy 316 4 - float ao_pad2 320 4 - short ao_indirect_bounces 324 2 - short ao_pad 326 2 - short ao_samp_method 328 2 - short ao_gather_method 330 2 - short ao_approx_passes 332 2 - short flag 334 2 - float *aosphere 336 8 - float *aotables 344 8 - Ipo *ipo 352 8 - MTex *mtex 360 144 - short pr_texture 504 2 - short use_nodes 506 2 - short pad 508 4 - PreviewImage *preview 512 8 - bNodeTree *nodetree 520 8 - -Base 40 - - Base *next 0 8 - Base *prev 8 8 - int lay 16 4 - int selcol 20 4 - int flag 24 4 - short sx 28 2 - short sy 30 2 - Object *object 32 8 - -AviCodecData 184 - - void *lpFormat 0 8 - void *lpParms 8 8 - int cbFormat 16 4 - int cbParms 20 4 - int fccType 24 4 - int fccHandler 28 4 - int dwKeyFrameEvery 32 4 - int dwQuality 36 4 - int dwBytesPerSecond 40 4 - int dwFlags 44 4 - int dwInterleaveEvery 48 4 - int pad 52 4 - char avicodecname 56 128 - -QuicktimeCodecData 152 - - void *cdParms 0 8 - void *pad 8 8 - int cdSize 16 4 - int pad2 20 4 - char qtcodecname 24 128 - -QuicktimeCodecSettings 64 - - int codecType 0 4 - int codecSpatialQuality 4 4 - int codec 8 4 - int codecFlags 12 4 - int colorDepth 16 4 - int codecTemporalQuality 20 4 - int minSpatialQuality 24 4 - int minTemporalQuality 28 4 - int keyFrameRate 32 4 - int bitRate 36 4 - int audiocodecType 40 4 - int audioSampleRate 44 4 - short audioBitDepth 48 2 - short audioChannels 50 2 - int audioCodecFlags 52 4 - int audioBitRate 56 4 - int pad1 60 4 - -FFMpegCodecData 72 - - int type 0 4 - int codec 4 4 - int audio_codec 8 4 - int video_bitrate 12 4 - int audio_bitrate 16 4 - int audio_mixrate 20 4 - int audio_channels 24 4 - int audio_pad 28 4 - float audio_volume 32 4 - int gop_size 36 4 - int flags 40 4 - int rc_min_rate 44 4 - int rc_max_rate 48 4 - int rc_buffer_size 52 4 - int mux_packet_size 56 4 - int mux_rate 60 4 - IDProperty *properties 64 8 - -AudioData 32 - - int mixrate 0 4 - float main 4 4 - float speed_of_sound 8 4 - float doppler_factor 12 4 - int distance_model 16 4 - short flag 20 2 - short pad 22 2 - float volume 24 4 - float pad2 28 4 - -SceneRenderLayer 184 - - SceneRenderLayer *next 0 8 - SceneRenderLayer *prev 8 8 - char name 16 64 - Material *mat_override 80 8 - Group *light_override 88 8 - int lay 96 4 - int lay_zmask 100 4 - int lay_exclude 104 4 - int layflag 108 4 - int passflag 112 4 - int pass_xor 116 4 - int samples 120 4 - int pad 124 4 - FreestyleConfig freestyleConfig 128 56 - -ImageFormatData 248 - - char imtype 0 1 - char depth 1 1 - char planes 2 1 - char flag 3 1 - char quality 4 1 - char compress 5 1 - char exr_codec 6 1 - char cineon_flag 7 1 - short cineon_white 8 2 - short cineon_black 10 2 - float cineon_gamma 12 4 - char jp2_flag 16 1 - char jp2_codec 17 1 - char pad 18 6 - ColorManagedViewSettings view_settings 24 160 - ColorManagedDisplaySettings display_settings 184 64 - -RenderData 2608 - - ImageFormatData im_format 0 248 - AviCodecData *avicodecdata 248 8 - QuicktimeCodecData *qtcodecdata 256 8 - QuicktimeCodecSettings qtcodecsettings 264 64 - FFMpegCodecData ffcodecdata 328 72 - int cfra 400 4 - int sfra 404 4 - int efra 408 4 - float subframe 412 4 - int psfra 416 4 - int pefra 420 4 - int images 424 4 - int framapto 428 4 - short flag 432 2 - short threads 434 2 - float framelen 436 4 - float blurfac 440 4 - float edgeR 444 4 - float edgeG 448 4 - float edgeB 452 4 - short fullscreen 456 2 - short xplay 458 2 - short yplay 460 2 - short freqplay 462 2 - short depth 464 2 - short attrib 466 2 - int frame_step 468 4 - short stereomode 472 2 - short dimensionspreset 474 2 - short filtertype 476 2 - short size 478 2 - short maximsize 480 2 - short pad6 482 2 - int xsch 484 4 - int ysch 488 4 - short xparts 492 2 - short yparts 494 2 - int tilex 496 4 - int tiley 500 4 - short planes 504 2 - short imtype 506 2 - short subimtype 508 2 - short quality 510 2 - short displaymode 512 2 - short pad7 514 2 - int scemode 516 4 - int mode 520 4 - int raytrace_options 524 4 - short raytrace_structure 528 2 - short pad1 530 2 - short ocres 532 2 - short pad4 534 2 - short alphamode 536 2 - short osa 538 2 - short frs_sec 540 2 - short edgeint 542 2 - rctf safety 544 16 - rctf border 560 16 - rcti disprect 576 16 - ListBase layers 592 16 - short actlay 608 2 - short mblur_samples 610 2 - float xasp 612 4 - float yasp 616 4 - float frs_sec_base 620 4 - float gauss 624 4 - int color_mgt_flag 628 4 - float postgamma 632 4 - float posthue 636 4 - float postsat 640 4 - float dither_intensity 644 4 - short bake_osa 648 2 - short bake_filter 650 2 - short bake_mode 652 2 - short bake_flag 654 2 - short bake_normal_space 656 2 - short bake_quad_split 658 2 - float bake_maxdist 660 4 - float bake_biasdist 664 4 - short bake_samples 668 2 - short bake_pad 670 2 - char pic 672 1024 - int stamp 1696 4 - short stamp_font_id 1700 2 - short pad3 1702 2 - char stamp_udata 1704 768 - float fg_stamp 2472 16 - float bg_stamp 2488 16 - char seq_prev_type 2504 1 - char seq_rend_type 2505 1 - char seq_flag 2506 1 - char pad5 2507 5 - int simplify_flag 2512 4 - short simplify_subsurf 2516 2 - short simplify_shadowsamples 2518 2 - float simplify_particles 2520 4 - float simplify_aosss 2524 4 - short cineonwhite 2528 2 - short cineonblack 2530 2 - float cineongamma 2532 4 - short jp2_preset 2536 2 - short jp2_depth 2538 2 - int rpad3 2540 4 - short domeres 2544 2 - short domemode 2546 2 - short domeangle 2548 2 - short dometilt 2550 2 - float domeresbuf 2552 4 - float pad2 2556 4 - Text *dometext 2560 8 - int line_thickness_mode 2568 4 - float unit_line_thickness 2572 4 - char engine 2576 32 - -RenderProfile 64 - - RenderProfile *next 0 8 - RenderProfile *prev 8 8 - char name 16 32 - short particle_perc 48 2 - short subsurf_max 50 2 - short shadbufsample_max 52 2 - short pad1 54 2 - float ao_error 56 4 - float pad2 60 4 - -GameDome 24 - - short res 0 2 - short mode 2 2 - short angle 4 2 - short tilt 6 2 - float resbuf 8 4 - float pad2 12 4 - Text *warptext 16 8 - -GameFraming 16 - - float col 0 12 - char type 12 1 - char pad1 13 1 - char pad2 14 1 - char pad3 15 1 - -RecastData 56 - - float cellsize 0 4 - float cellheight 4 4 - float agentmaxslope 8 4 - float agentmaxclimb 12 4 - float agentheight 16 4 - float agentradius 20 4 - float edgemaxlen 24 4 - float edgemaxerror 28 4 - float regionminsize 32 4 - float regionmergesize 36 4 - int vertsperpoly 40 4 - float detailsampledist 44 4 - float detailsamplemaxerror 48 4 - short pad1 52 2 - short pad2 54 2 - -GameData 184 - - GameFraming framing 0 16 - short playerflag 16 2 - short xplay 18 2 - short yplay 20 2 - short freqplay 22 2 - short depth 24 2 - short attrib 26 2 - short rt1 28 2 - short rt2 30 2 - short aasamples 32 2 - short pad4 34 6 - GameDome dome 40 24 - short stereoflag 64 2 - short stereomode 66 2 - float eyeseparation 68 4 - RecastData recastData 72 56 - float gravity 128 4 - float activityBoxRadius 132 4 - int flag 136 4 - short mode 140 2 - short matmode 142 2 - short occlusionRes 144 2 - short physicsEngine 146 2 - short exitkey 148 2 - short vsync 150 2 - short ticrate 152 2 - short maxlogicstep 154 2 - short physubstep 156 2 - short maxphystep 158 2 - short obstacleSimulation 160 2 - short raster_storage 162 2 - float levelHeight 164 4 - float deactivationtime 168 4 - float lineardeactthreshold 172 4 - float angulardeactthreshold 176 4 - float pad2 180 4 - -TimeMarker 96 - - TimeMarker *next 0 8 - TimeMarker *prev 8 8 - int frame 16 4 - char name 20 64 - int flag 84 4 - Object *camera 88 8 - -Paint 32 - - Brush *brush 0 8 - void *paint_cursor 8 8 - char paint_cursor_col 16 4 - int flags 20 4 - int num_input_samples 24 4 - int pad 28 4 - -ImagePaintSettings 56 - - Paint paint 0 32 - short flag 32 2 - short pad 34 2 - short seam_bleed 36 2 - short normal_angle 38 2 - short screen_grab_size 40 4 - int pad1 44 4 - void *paintcursor 48 8 - -ParticleBrushData 16 - - short size 0 2 - short step 2 2 - short invert 4 2 - short count 6 2 - int flag 8 4 - float strength 12 4 - -ParticleEditSettings 168 - - short flag 0 2 - short totrekey 2 2 - short totaddkey 4 2 - short brushtype 6 2 - ParticleBrushData brush 8 112 - void *paintcursor 120 8 - float emitterdist 128 4 - float rt 132 4 - int selectmode 136 4 - int edittype 140 4 - int draw_step 144 4 - int fade_frames 148 4 - Scene *scene 152 8 - Object *object 160 8 - -Sculpt 56 - - Paint paint 0 32 - int flags 32 4 - int radial_symm 36 12 - int detail_size 48 4 - int symmetrize_direction 52 4 - -UvSculpt 32 - - Paint paint 0 32 - -VPaint 64 - - Paint paint 0 32 - short flag 32 2 - short pad 34 2 - int tot 36 4 - int *vpaint_prev 40 8 - MDeformVert *wpaint_prev 48 8 - void *paintcursor 56 8 - -TransformOrientation 120 - - TransformOrientation *next 0 8 - TransformOrientation *prev 8 8 - char name 16 64 - float mat 80 36 - int pad 116 4 - -UnifiedPaintSettings 80 - - int size 0 4 - float unprojected_radius 4 4 - float alpha 8 4 - float weight 12 4 - int flag 16 4 - float last_rake 20 8 - int pad 28 4 - float brush_rotation 32 4 - int draw_anchored 36 4 - int anchored_size 40 4 - float anchored_initial_mouse 44 8 - int draw_pressure 52 4 - float pressure_value 56 4 - float tex_mouse 60 8 - float mask_tex_mouse 68 8 - float pixel_radius 76 4 - -MeshStatVis 40 - - char type 0 1 - char _pad1 1 2 - char overhang_axis 3 1 - float overhang_min 4 4 - float overhang_max 8 4 - float thickness_min 12 4 - float thickness_max 16 4 - char thickness_samples 20 1 - char _pad2 21 3 - float distort_min 24 4 - float distort_max 28 4 - float sharp_min 32 4 - float sharp_max 36 4 - -ToolSettings 600 - - VPaint *vpaint 0 8 - VPaint *wpaint 8 8 - Sculpt *sculpt 16 8 - UvSculpt *uvsculpt 24 8 - float vgroup_weight 32 4 - short cornertype 36 2 - short pad1 38 2 - float jointrilimit 40 4 - float degr 44 4 - short step 48 2 - short turn 50 2 - float extr_offs 52 4 - float doublimit 56 4 - float normalsize 60 4 - short automerge 64 2 - short selectmode 66 2 - short segments 68 2 - short rings 70 2 - short vertices 72 2 - short unwrapper 74 2 - float uvcalc_radius 76 4 - float uvcalc_cubesize 80 4 - float uvcalc_margin 84 4 - short uvcalc_mapdir 88 2 - short uvcalc_mapalign 90 2 - short uvcalc_flag 92 2 - short uv_flag 94 2 - short uv_selectmode 96 2 - short pad2 98 2 - short gpencil_flags 100 2 - short autoik_chainlen 102 2 - ImagePaintSettings imapaint 104 56 - ParticleEditSettings particle 160 168 - float proportional_size 328 4 - float select_thresh 332 4 - float clean_thresh 336 4 - short autokey_mode 340 2 - short autokey_flag 342 2 - char multires_subdiv_type 344 1 - char pad3 345 5 - short skgen_resolution 350 2 - float skgen_threshold_internal 352 4 - float skgen_threshold_external 356 4 - float skgen_length_ratio 360 4 - float skgen_length_limit 364 4 - float skgen_angle_limit 368 4 - float skgen_correlation_limit 372 4 - float skgen_symmetry_limit 376 4 - float skgen_retarget_angle_weight 380 4 - float skgen_retarget_length_weight 384 4 - float skgen_retarget_distance_weight 388 4 - short skgen_options 392 2 - char skgen_postpro 394 1 - char skgen_postpro_passes 395 1 - char skgen_subdivisions 396 3 - char skgen_multi_level 399 1 - Object *skgen_template 400 8 - char bone_sketching 408 1 - char bone_sketching_convert 409 1 - char skgen_subdivision_number 410 1 - char skgen_retarget_options 411 1 - char skgen_retarget_roll 412 1 - char skgen_side_string 413 8 - char skgen_num_string 421 8 - char edge_mode 429 1 - char edge_mode_live_unwrap 430 1 - char snap_mode 431 1 - char snap_node_mode 432 1 - char snap_uv_mode 433 1 - short snap_flag 434 2 - short snap_target 436 2 - short proportional 438 2 - short prop_mode 440 2 - char proportional_objects 442 1 - char proportional_mask 443 1 - char auto_normalize 444 1 - char multipaint 445 1 - char weightuser 446 1 - char vgroupsubset 447 1 - int use_uv_sculpt 448 4 - int uv_sculpt_settings 452 4 - int uv_sculpt_tool 456 4 - int uv_relax_method 460 4 - short sculpt_paint_settings 464 2 - short pad5 466 2 - int sculpt_paint_unified_size 468 4 - float sculpt_paint_unified_unprojected_radius 472 4 - float sculpt_paint_unified_alpha 476 4 - UnifiedPaintSettings unified_paint_settings 480 80 - MeshStatVis statvis 560 40 - -bStats 32 - - int totobj 0 4 - int totlamp 4 4 - int totobjsel 8 4 - int totcurve 12 4 - int totmesh 16 4 - int totarmature 20 4 - int totvert 24 4 - int totface 28 4 - -UnitSettings 8 - - float scale_length 0 4 - char system 4 1 - char system_rotation 5 1 - short flag 6 2 - -PhysicsSettings 24 - - float gravity 0 12 - int flag 12 4 - int quick_cache_step 16 4 - int rt 20 4 - -Scene 3584 - - ID id 0 120 - AnimData *adt 120 8 - Object *camera 128 8 - World *world 136 8 - Scene *set 144 8 - ListBase base 152 16 - Base *basact 168 8 - Object *obedit 176 8 - float cursor 184 12 - float twcent 196 12 - float twmin 208 12 - float twmax 220 12 - int lay 232 4 - int layact 236 4 - int lay_updated 240 4 - short flag 244 2 - short use_nodes 246 2 - bNodeTree *nodetree 248 8 - Editing *ed 256 8 - ToolSettings *toolsettings 264 8 - SceneStats *stats 272 8 - RenderData r 280 2608 - AudioData audio 2888 32 - ListBase markers 2920 16 - ListBase transform_spaces 2936 16 - void *sound_scene 2952 8 - void *sound_scene_handle 2960 8 - void *sound_scrub_handle 2968 8 - void *speaker_handles 2976 8 - void *fps_info 2984 8 - DagForest *theDag 2992 8 - short dagflags 3000 2 - short recalc 3002 2 - int active_keyingset 3004 4 - ListBase keyingsets 3008 16 - GameFraming framing 3024 16 - GameData gm 3040 184 - UnitSettings unit 3224 8 - bGPdata *gpd 3232 8 - PhysicsSettings physics_settings 3240 24 - MovieClip *clip 3264 8 - uint64_t customdata_mask 3272 8 - uint64_t customdata_mask_modal 3280 8 - ColorManagedViewSettings view_settings 3288 160 - ColorManagedDisplaySettings display_settings 3448 64 - ColorManagedColorspaceSettings sequencer_colorspace_settings 3512 64 - RigidBodyWorld *rigidbody_world 3576 8 - -BGpic 104 - - BGpic *next 0 8 - BGpic *prev 8 8 - Image *ima 16 8 - ImageUser iuser 24 40 - MovieClip *clip 64 8 - MovieClipUser cuser 72 8 - float xof 80 4 - float yof 84 4 - float size 88 4 - float blend 92 4 - short view 96 2 - short flag 98 2 - short source 100 2 - short pad 102 2 - -RegionView3D 896 - - float winmat 0 64 - float viewmat 64 64 - float viewinv 128 64 - float persmat 192 64 - float persinv 256 64 - float viewmatob 320 64 - float persmatob 384 64 - float clip 448 96 - float clip_local 544 96 - BoundBox *clipbb 640 8 - bGPdata *gpd 648 8 - RegionView3D *localvd 656 8 - RenderInfo *ri 664 8 - RenderEngine *render_engine 672 8 - ViewDepths *depths 680 8 - void *gpuoffscreen 688 8 - SmoothView3DStore *sms 696 8 - wmTimer *smooth_timer 704 8 - float twmat 712 64 - float viewquat 776 16 - float dist 792 4 - float camdx 796 4 - float camdy 800 4 - float pixsize 804 4 - float ofs 808 12 - float camzoom 820 4 - char is_persp 824 1 - char persp 825 1 - char view 826 1 - char viewlock 827 1 - char viewlock_quad 828 1 - char pad 829 3 - float ofs_lock 832 8 - short twdrawflag 840 2 - short rflag 842 2 - float lviewquat 844 16 - short lpersp 860 2 - short lview 862 2 - float gridview 864 4 - float twangle 868 12 - float rot_angle 880 4 - float rot_axis 884 12 - -View3D 376 - - SpaceLink *next 0 8 - SpaceLink *prev 8 8 - ListBase regionbase 16 16 - int spacetype 32 4 - float blockscale 36 4 - short blockhandler 40 16 - float viewquat 56 16 - float dist 72 4 - float bundle_size 76 4 - char bundle_drawtype 80 1 - char pad 81 3 - int lay_prev 84 4 - int lay_used 88 4 - short persp 92 2 - short view 94 2 - Object *camera 96 8 - Object *ob_centre 104 8 - rctf render_border 112 16 - ListBase bgpicbase 128 16 - BGpic *bgpic 144 8 - View3D *localvd 152 8 - char ob_centre_bone 160 64 - int lay 224 4 - int layact 228 4 - short drawtype 232 2 - short ob_centre_cursor 234 2 - short scenelock 236 2 - short around 238 2 - short flag 240 2 - short flag2 242 2 - float lens 244 4 - float grid 248 4 - float near 252 4 - float far 256 4 - float ofs 260 12 - float cursor 272 12 - short matcap_icon 284 2 - short gridlines 286 2 - short gridsubdiv 288 2 - char gridflag 290 1 - char twtype 291 1 - char twmode 292 1 - char twflag 293 1 - char pad2 294 2 - ListBase afterdraw_transp 296 16 - ListBase afterdraw_xray 312 16 - ListBase afterdraw_xraytransp 328 16 - char zbuf 344 1 - char transp 345 1 - char xray 346 1 - char pad3 347 5 - void *properties_storage 352 8 - Material *defmaterial 360 8 - bGPdata *gpd 368 8 - -View2D 160 - - rctf tot 0 16 - rctf cur 16 16 - rcti vert 32 16 - rcti hor 48 16 - rcti mask 64 16 - float min 80 8 - float max 88 8 - float minzoom 96 4 - float maxzoom 100 4 - short scroll 104 2 - short scroll_ui 106 2 - short keeptot 108 2 - short keepzoom 110 2 - short keepofs 112 2 - short flag 114 2 - short align 116 2 - short winx 118 2 - short winy 120 2 - short oldwinx 122 2 - short oldwiny 124 2 - short around 126 2 - float *tab_offset 128 8 - int tab_num 136 4 - int tab_cur 140 4 - SmoothView2DStore *sms 144 8 - wmTimer *smooth_timer 152 8 - -SpaceLink 56 - - SpaceLink *next 0 8 - SpaceLink *prev 8 8 - ListBase regionbase 16 16 - int spacetype 32 4 - float blockscale 36 4 - short blockhandler 40 16 - -SpaceInfo 64 - - SpaceLink *next 0 8 - SpaceLink *prev 8 8 - ListBase regionbase 16 16 - int spacetype 32 4 - float blockscale 36 4 - short blockhandler 40 16 - char rpt_mask 56 1 - char pad 57 7 - -SpaceButs 272 - - SpaceLink *next 0 8 - SpaceLink *prev 8 8 - ListBase regionbase 16 16 - int spacetype 32 4 - float blockscale 36 4 - short blockhandler 40 16 - View2D v2d 56 160 - short mainb 216 2 - short mainbo 218 2 - short mainbuser 220 2 - short re_align 222 2 - short align 224 2 - short preview 226 2 - short texture_context 228 2 - short texture_context_prev 230 2 - char flag 232 1 - char pad 233 7 - void *path 240 8 - int pathflag 248 4 - int dataicon 252 4 - ID *pinid 256 8 - void *texuser 264 8 - -SpaceOops 304 - - SpaceLink *next 0 8 - SpaceLink *prev 8 8 - ListBase regionbase 16 16 - int spacetype 32 4 - float blockscale 36 4 - short blockhandler 40 16 - View2D v2d 56 160 - ListBase tree 216 16 - BLI_mempool *treestore 232 8 - char search_string 240 32 - TreeStoreElem search_tse 272 16 - short flag 288 2 - short outlinevis 290 2 - short storeflag 292 2 - short search_flags 294 2 - void *treehash 296 8 - -SpaceIpo 256 - - SpaceLink *next 0 8 - SpaceLink *prev 8 8 - ListBase regionbase 16 16 - int spacetype 32 4 - float blockscale 36 4 - short blockhandler 40 16 - View2D v2d 56 160 - bDopeSheet *ads 216 8 - ListBase ghostCurves 224 16 - short mode 240 2 - short autosnap 242 2 - int flag 244 4 - float cursorVal 248 4 - int around 252 4 - -SpaceNla 232 - - SpaceLink *next 0 8 - SpaceLink *prev 8 8 - ListBase regionbase 16 16 - int spacetype 32 4 - float blockscale 36 4 - short blockhandler 40 16 - short autosnap 56 2 - short flag 58 2 - int pad 60 4 - bDopeSheet *ads 64 8 - View2D v2d 72 160 - -SpaceTimeCache 24 - - SpaceTimeCache *next 0 8 - SpaceTimeCache *prev 8 8 - float *array 16 8 - -SpaceTime 224 - - SpaceLink *next 0 8 - SpaceLink *prev 8 8 - ListBase regionbase 16 16 - int spacetype 32 4 - float blockscale 36 4 - View2D v2d 40 160 - ListBase caches 200 16 - int cache_display 216 4 - int flag 220 4 - -SpaceSeq 304 - - SpaceLink *next 0 8 - SpaceLink *prev 8 8 - ListBase regionbase 16 16 - int spacetype 32 4 - float blockscale 36 4 - short blockhandler 40 16 - View2D v2d 56 160 - float xof 216 4 - float yof 220 4 - short mainb 224 2 - short render_size 226 2 - short chanshown 228 2 - short zebra 230 2 - int flag 232 4 - float zoom 236 4 - int view 240 4 - int overlay_type 244 4 - bGPdata *gpd 248 8 - SequencerScopes scopes 256 48 - -MaskSpaceInfo 16 - - Mask *mask 0 8 - char draw_flag 8 1 - char draw_type 9 1 - char pad3 10 6 - -FileSelectParams 2016 - - char title 0 96 - char dir 96 1056 - char file 1152 256 - char renamefile 1408 256 - char renameedit 1664 256 - char filter_glob 1920 64 - int active_file 1984 4 - int sel_first 1988 4 - int sel_last 1992 4 - short type 1996 2 - short flag 1998 2 - short sort 2000 2 - short display 2002 2 - short filter 2004 2 - short f_fp 2006 2 - char fp_str 2008 8 - -SpaceFile 104 - - SpaceLink *next 0 8 - SpaceLink *prev 8 8 - ListBase regionbase 16 16 - int spacetype 32 4 - int scroll_offset 36 4 - FileSelectParams *params 40 8 - FileList *files 48 8 - ListBase *folders_prev 56 8 - ListBase *folders_next 64 8 - wmOperator *op 72 8 - wmTimer *smoothscroll_timer 80 8 - FileLayout *layout 88 8 - short recentnr 96 2 - short bookmarknr 98 2 - short systemnr 100 2 - short pad2 102 2 - -SpaceImage 10584 - - SpaceLink *next 0 8 - SpaceLink *prev 8 8 - ListBase regionbase 16 16 - int spacetype 32 4 - int flag 36 4 - Image *image 40 8 - ImageUser iuser 48 40 - CurveMapping *cumap 88 8 - Scopes scopes 96 5264 - Histogram sample_line_hist 5360 5160 - bGPdata *gpd 10520 8 - float cursor 10528 8 - float xof 10536 4 - float yof 10540 4 - float zoom 10544 4 - float centx 10548 4 - float centy 10552 4 - char mode 10556 1 - char pin 10557 1 - short pad 10558 2 - short curtile 10560 2 - short lock 10562 2 - char dt_uv 10564 1 - char sticky 10565 1 - char dt_uvstretch 10566 1 - char around 10567 1 - MaskSpaceInfo mask_info 10568 16 - -SpaceText 672 - - SpaceLink *next 0 8 - SpaceLink *prev 8 8 - ListBase regionbase 16 16 - int spacetype 32 4 - float blockscale 36 4 - short blockhandler 40 16 - Text *text 56 8 - int top 64 4 - int viewlines 68 4 - short flags 72 2 - short menunr 74 2 - short lheight 76 2 - char cwidth 78 1 - char linenrs_tot 79 1 - int left 80 4 - int showlinenrs 84 4 - int tabnumber 88 4 - short showsyntax 92 2 - short line_hlight 94 2 - short overwrite 96 2 - short live_edit 98 2 - float pix_per_line 100 4 - rcti txtscroll 104 16 - rcti txtbar 120 16 - int wordwrap 136 4 - int doplugins 140 4 - char findstr 144 256 - char replacestr 400 256 - short margin_column 656 2 - short lheight_dpi 658 2 - char pad 660 4 - void *drawcache 664 8 - -Script 1448 - - ID id 0 120 - void *py_draw 120 8 - void *py_event 128 8 - void *py_button 136 8 - void *py_browsercallback 144 8 - void *py_globaldict 152 8 - int flags 160 4 - int lastspace 164 4 - char scriptname 168 1024 - char scriptarg 1192 256 - -SpaceScript 64 - - SpaceLink *next 0 8 - SpaceLink *prev 8 8 - ListBase regionbase 16 16 - int spacetype 32 4 - float blockscale 36 4 - Script *script 40 8 - short flags 48 2 - short menunr 50 2 - int pad1 52 4 - void *but_refs 56 8 - -bNodeTreePath 104 - - bNodeTreePath *next 0 8 - bNodeTreePath *prev 8 8 - bNodeTree *nodetree 16 8 - bNodeInstanceKey parent_key 24 4 - int pad 28 4 - float view_center 32 8 - char node_name 40 64 - -SpaceNode 400 - - SpaceLink *next 0 8 - SpaceLink *prev 8 8 - ListBase regionbase 16 16 - int spacetype 32 4 - float blockscale 36 4 - short blockhandler 40 16 - View2D v2d 56 160 - ID *id 216 8 - ID *from 224 8 - short flag 232 2 - short pad1 234 2 - float aspect 236 4 - float pad2 240 4 - float xof 244 4 - float yof 248 4 - float zoom 252 4 - float cursor 256 8 - ListBase treepath 264 16 - bNodeTree *nodetree 280 8 - bNodeTree *edittree 288 8 - char tree_idname 296 64 - int treetype 360 4 - int pad3 364 4 - short texfrom 368 2 - short shaderfrom 370 2 - short recalc 372 2 - short pad4 374 2 - ListBase linkdrag 376 16 - bGPdata *gpd 392 8 - -SpaceLogic 72 - - SpaceLink *next 0 8 - SpaceLink *prev 8 8 - ListBase regionbase 16 16 - int spacetype 32 4 - float blockscale 36 4 - short blockhandler 40 16 - short flag 56 2 - short scaflag 58 2 - int pad 60 4 - bGPdata *gpd 64 8 - -ConsoleLine 40 - - ConsoleLine *next 0 8 - ConsoleLine *prev 8 8 - int len_alloc 16 4 - int len 20 4 - char *line 24 8 - int cursor 32 4 - int type 36 4 - -SpaceConsole 392 - - SpaceLink *next 0 8 - SpaceLink *prev 8 8 - ListBase regionbase 16 16 - int spacetype 32 4 - float blockscale 36 4 - short blockhandler 40 16 - int lheight 56 4 - int pad 60 4 - ListBase scrollback 64 16 - ListBase history 80 16 - char prompt 96 256 - char language 352 32 - int sel_start 384 4 - int sel_end 388 4 - -SpaceUserPref 104 - - SpaceLink *next 0 8 - SpaceLink *prev 8 8 - ListBase regionbase 16 16 - int spacetype 32 4 - char pad 36 3 - char filter_type 39 1 - char filter 40 64 - -SpaceClip 408 - - SpaceLink *next 0 8 - SpaceLink *prev 8 8 - ListBase regionbase 16 16 - int spacetype 32 4 - float xof 36 4 - float yof 40 4 - float xlockof 44 4 - float ylockof 48 4 - float zoom 52 4 - MovieClipUser user 56 8 - MovieClip *clip 64 8 - MovieClipScopes scopes 72 136 - int flag 208 4 - short mode 212 2 - short view 214 2 - int path_length 216 4 - float loc 220 8 - float scale 228 4 - float angle 232 4 - int pad 236 4 - float stabmat 240 64 - float unistabmat 304 64 - int postproc_flag 368 4 - short gpencil_src 372 2 - short pad2 374 2 - int around 376 4 - int pad4 380 4 - float cursor 384 8 - MaskSpaceInfo mask_info 392 16 - -uiFont 1048 - - uiFont *next 0 8 - uiFont *prev 8 8 - char filename 16 1024 - short blf_id 1040 2 - short uifont_id 1042 2 - short r_to_l 1044 2 - short hinting 1046 2 - -uiFontStyle 32 - - short uifont_id 0 2 - short points 2 2 - short kerning 4 2 - char pad 6 6 - short italic 12 2 - short bold 14 2 - short shadow 16 2 - short shadx 18 2 - short shady 20 2 - short align 22 2 - float shadowalpha 24 4 - float shadowcolor 28 4 - -uiStyle 232 - - uiStyle *next 0 8 - uiStyle *prev 8 8 - char name 16 64 - uiFontStyle paneltitle 80 32 - uiFontStyle grouplabel 112 32 - uiFontStyle widgetlabel 144 32 - uiFontStyle widget 176 32 - float panelzoom 208 4 - short minlabelchars 212 2 - short minwidgetchars 214 2 - short columnspace 216 2 - short templatespace 218 2 - short boxspace 220 2 - short buttonspacex 222 2 - short buttonspacey 224 2 - short panelspace 226 2 - short panelouter 228 2 - short pad 230 2 - -uiWidgetColors 32 - - char outline 0 4 - char inner 4 4 - char inner_sel 8 4 - char item 12 4 - char text 16 4 - char text_sel 20 4 - short shaded 24 2 - short shadetop 26 2 - short shadedown 28 2 - short alpha_check 30 2 - -uiWidgetStateColors 32 - - char inner_anim 0 4 - char inner_anim_sel 4 4 - char inner_key 8 4 - char inner_key_sel 12 4 - char inner_driven 16 4 - char inner_driven_sel 20 4 - float blend 24 4 - float pad 28 4 - -uiPanelColors 16 - - char header 0 4 - char back 4 4 - short show_header 8 2 - short show_back 10 2 - int pad 12 4 - -uiGradientColors 16 - - char gradient 0 4 - char high_gradient 4 4 - int show_grad 8 4 - int pad2 12 4 - -ThemeUI 872 - - uiWidgetColors wcol_regular 0 32 - uiWidgetColors wcol_tool 32 32 - uiWidgetColors wcol_text 64 32 - uiWidgetColors wcol_radio 96 32 - uiWidgetColors wcol_option 128 32 - uiWidgetColors wcol_toggle 160 32 - uiWidgetColors wcol_num 192 32 - uiWidgetColors wcol_numslider 224 32 - uiWidgetColors wcol_menu 256 32 - uiWidgetColors wcol_pulldown 288 32 - uiWidgetColors wcol_menu_back 320 32 - uiWidgetColors wcol_menu_item 352 32 - uiWidgetColors wcol_tooltip 384 32 - uiWidgetColors wcol_box 416 32 - uiWidgetColors wcol_scroll 448 32 - uiWidgetColors wcol_progress 480 32 - uiWidgetColors wcol_list_item 512 32 - uiWidgetStateColors wcol_state 544 32 - uiPanelColors panel 576 16 - float menu_shadow_fac 592 4 - short menu_shadow_width 596 2 - short pad 598 2 - char iconfile 600 256 - float icon_alpha 856 4 - char xaxis 860 4 - char yaxis 864 4 - char zaxis 868 4 - -ThemeSpace 584 - - char back 0 4 - char title 4 4 - char text 8 4 - char text_hi 12 4 - char header 16 4 - char header_title 20 4 - char header_text 24 4 - char header_text_hi 28 4 - char button 32 4 - char button_title 36 4 - char button_text 40 4 - char button_text_hi 44 4 - char list 48 4 - char list_title 52 4 - char list_text 56 4 - char list_text_hi 60 4 - uiPanelColors panelcolors 64 16 - uiGradientColors gradients 80 16 - char shade1 96 4 - char shade2 100 4 - char hilite 104 4 - char grid 108 4 - char wire 112 4 - char wire_edit 116 4 - char select 120 4 - char lamp 124 4 - char speaker 128 4 - char empty 132 4 - char camera 136 4 - char pad 140 4 - char active 144 4 - char group 148 4 - char group_active 152 4 - char transform 156 4 - char vertex 160 4 - char vertex_select 164 4 - char vertex_unreferenced 168 4 - char edge 172 4 - char edge_select 176 4 - char edge_seam 180 4 - char edge_sharp 184 4 - char edge_facesel 188 4 - char edge_crease 192 4 - char face 196 4 - char face_select 200 4 - char face_dot 204 4 - char extra_edge_len 208 4 - char extra_edge_angle 212 4 - char extra_face_angle 216 4 - char extra_face_area 220 4 - char normal 224 4 - char vertex_normal 228 4 - char bone_solid 232 4 - char bone_pose 236 4 - char bone_pose_active 240 4 - char strip 244 4 - char strip_select 248 4 - char cframe 252 4 - char freestyle_edge_mark 256 4 - char freestyle_face_mark 260 4 - char nurb_uline 264 4 - char nurb_vline 268 4 - char act_spline 272 4 - char nurb_sel_uline 276 4 - char nurb_sel_vline 280 4 - char lastsel_point 284 4 - char handle_free 288 4 - char handle_auto 292 4 - char handle_vect 296 4 - char handle_align 300 4 - char handle_auto_clamped 304 4 - char handle_sel_free 308 4 - char handle_sel_auto 312 4 - char handle_sel_vect 316 4 - char handle_sel_align 320 4 - char handle_sel_auto_clamped 324 4 - char ds_channel 328 4 - char ds_subchannel 332 4 - char console_output 336 4 - char console_input 340 4 - char console_info 344 4 - char console_error 348 4 - char console_cursor 352 4 - char console_select 356 4 - char pad1 360 4 - char vertex_size 364 1 - char outline_width 365 1 - char facedot_size 366 1 - char noodle_curving 367 1 - char syntaxl 368 4 - char syntaxs 372 4 - char syntaxb 376 4 - char syntaxn 380 4 - char syntaxv 384 4 - char syntaxc 388 4 - char syntaxd 392 4 - char syntaxr 396 4 - char movie 400 4 - char movieclip 404 4 - char mask 408 4 - char image 412 4 - char scene 416 4 - char audio 420 4 - char effect 424 4 - char transition 428 4 - char meta 432 4 - char editmesh_active 436 4 - char handle_vertex 440 4 - char handle_vertex_select 444 4 - char pad2 448 4 - char handle_vertex_size 452 1 - char marker_outline 453 4 - char marker 457 4 - char act_marker 461 4 - char sel_marker 465 4 - char dis_marker 469 4 - char lock_marker 473 4 - char bundle_solid 477 4 - char path_before 481 4 - char path_after 485 4 - char camera_path 489 4 - char hpad 493 3 - char preview_back 496 4 - char preview_stitch_face 500 4 - char preview_stitch_edge 504 4 - char preview_stitch_vert 508 4 - char preview_stitch_stitchable 512 4 - char preview_stitch_unstitchable 516 4 - char preview_stitch_active 520 4 - char uv_shadow 524 4 - char uv_others 528 4 - char match 532 4 - char selected_highlight 536 4 - char skin_root 540 4 - char anim_active 544 4 - char anim_non_active 548 4 - char nla_tweaking 552 4 - char nla_tweakdupli 556 4 - char nla_transition 560 4 - char nla_transition_sel 564 4 - char nla_meta 568 4 - char nla_meta_sel 572 4 - char nla_sound 576 4 - char nla_sound_sel 580 4 - -ThemeWireColor 16 - - char solid 0 4 - char select 4 4 - char active 8 4 - short flag 12 2 - short pad 14 2 - -bTheme 11176 - - bTheme *next 0 8 - bTheme *prev 8 8 - char name 16 32 - ThemeUI tui 48 872 - ThemeSpace tbuts 920 584 - ThemeSpace tv3d 1504 584 - ThemeSpace tfile 2088 584 - ThemeSpace tipo 2672 584 - ThemeSpace tinfo 3256 584 - ThemeSpace tact 3840 584 - ThemeSpace tnla 4424 584 - ThemeSpace tseq 5008 584 - ThemeSpace tima 5592 584 - ThemeSpace text 6176 584 - ThemeSpace toops 6760 584 - ThemeSpace ttime 7344 584 - ThemeSpace tnode 7928 584 - ThemeSpace tlogic 8512 584 - ThemeSpace tuserpref 9096 584 - ThemeSpace tconsole 9680 584 - ThemeSpace tclip 10264 584 - ThemeWireColor tarm 10848 320 - int active_theme_area 11168 4 - int pad 11172 4 - -bAddon 88 - - bAddon *next 0 8 - bAddon *prev 8 8 - char module 16 64 - IDProperty *prop 80 8 - -bPathCompare 792 - - bPathCompare *next 0 8 - bPathCompare *prev 8 8 - char path 16 768 - char flag 784 1 - char pad 785 7 - -SolidLight 56 - - int flag 0 4 - int pad 4 4 - float col 8 16 - float spec 24 16 - float vec 40 16 - -UserDef 9104 - - int versionfile 0 4 - int subversionfile 4 4 - int flag 8 4 - int dupflag 12 4 - int savetime 16 4 - char tempdir 20 768 - char fontdir 788 768 - char renderdir 1556 1024 - char textudir 2580 768 - char pythondir 3348 768 - char sounddir 4116 768 - char i18ndir 4884 768 - char image_editor 5652 1024 - char anim_player 6676 1024 - int anim_player_preset 7700 4 - short v2d_min_gridsize 7704 2 - short timecode_style 7706 2 - short versions 7708 2 - short dbl_click_time 7710 2 - short gameflags 7712 2 - short wheellinescroll 7714 2 - int uiflag 7716 4 - int uiflag2 7720 4 - int language 7724 4 - short userpref 7728 2 - short viewzoom 7730 2 - int mixbufsize 7732 4 - int audiodevice 7736 4 - int audiorate 7740 4 - int audioformat 7744 4 - int audiochannels 7748 4 - int scrollback 7752 4 - int dpi 7756 4 - short encoding 7760 2 - short transopts 7762 2 - short menuthreshold1 7764 2 - short menuthreshold2 7766 2 - ListBase themes 7768 16 - ListBase uifonts 7784 16 - ListBase uistyles 7800 16 - ListBase keymaps 7816 16 - ListBase user_keymaps 7832 16 - ListBase addons 7848 16 - ListBase autoexec_paths 7864 16 - char keyconfigstr 7880 64 - short undosteps 7944 2 - short undomemory 7946 2 - short gp_manhattendist 7948 2 - short gp_euclideandist 7950 2 - short gp_eraser 7952 2 - short gp_settings 7954 2 - short tb_leftmouse 7956 2 - short tb_rightmouse 7958 2 - SolidLight light 7960 168 - short tw_hotspot 8128 2 - short tw_flag 8130 2 - short tw_handlesize 8132 2 - short tw_size 8134 2 - short textimeout 8136 2 - short texcollectrate 8138 2 - short wmdrawmethod 8140 2 - short dragthreshold 8142 2 - int memcachelimit 8144 4 - int prefetchframes 8148 4 - short frameserverport 8152 2 - short pad_rot_angle 8154 2 - short obcenter_dia 8156 2 - short rvisize 8158 2 - short rvibright 8160 2 - short recent_files 8162 2 - short smooth_viewtx 8164 2 - short glreslimit 8166 2 - short curssize 8168 2 - short color_picker_type 8170 2 - short ipo_new 8172 2 - short keyhandles_new 8174 2 - short scrcastfps 8176 2 - short scrcastwait 8178 2 - short widget_unit 8180 2 - short anisotropic_filter 8182 2 - short use_16bit_textures 8184 2 - short use_gpu_mipmap 8186 2 - float ndof_sensitivity 8188 4 - float ndof_orbit_sensitivity 8192 4 - int ndof_flag 8196 4 - short ogl_multisamples 8200 2 - short image_draw_method 8202 2 - float glalphaclip 8204 4 - short autokey_mode 8208 2 - short autokey_flag 8210 2 - short text_render 8212 2 - short pad9 8214 2 - ColorBand coba_weight 8216 776 - float sculpt_paint_overlay_col 8992 12 - short tweak_threshold 9004 2 - short pad3 9006 2 - char author 9008 80 - int compute_device_type 9088 4 - int compute_device_id 9092 4 - float fcu_inactive_alpha 9096 4 - float pixelsize 9100 4 - -bScreen 248 - - ID id 0 120 - ListBase vertbase 120 16 - ListBase edgebase 136 16 - ListBase areabase 152 16 - ListBase regionbase 168 16 - Scene *scene 184 8 - Scene *newscene 192 8 - int redraws_flag 200 4 - int pad1 204 4 - short full 208 2 - short temp 210 2 - short winid 212 2 - short do_draw 214 2 - short do_refresh 216 2 - short do_draw_gesture 218 2 - short do_draw_paintcursor 220 2 - short do_draw_drag 222 2 - short swap 224 2 - short mainwin 226 2 - short subwinactive 228 2 - short pad 230 2 - wmTimer *animtimer 232 8 - void *context 240 8 - -ScrVert 32 - - ScrVert *next 0 8 - ScrVert *prev 8 8 - ScrVert *newv 16 8 - vec2s vec 24 4 - short flag 28 2 - short editflag 30 2 - -ScrEdge 40 - - ScrEdge *next 0 8 - ScrEdge *prev 8 8 - ScrVert *v1 16 8 - ScrVert *v2 24 8 - short border 32 2 - short flag 34 2 - int pad 36 4 - -Panel 272 - - Panel *next 0 8 - Panel *prev 8 8 - PanelType *type 16 8 - uiLayout *layout 24 8 - char panelname 32 64 - char tabname 96 64 - char drawname 160 64 - int ofsx 224 4 - int ofsy 228 4 - int sizex 232 4 - int sizey 236 4 - short labelofs 240 2 - short pad 242 2 - short flag 244 2 - short runtime_flag 246 2 - short control 248 2 - short snap 250 2 - int sortorder 252 4 - Panel *paneltab 256 8 - void *activedata 264 8 - -uiList 200 - - uiList *next 0 8 - uiList *prev 8 8 - uiListType *type 16 8 - char list_id 24 64 - int layout_type 88 4 - int flag 92 4 - int list_scroll 96 4 - int list_grip 100 4 - int list_last_len 104 4 - int padi1 108 4 - char filter_byname 112 64 - int filter_flag 176 4 - int filter_sort_flag 180 4 - IDProperty *properties 184 8 - uiListDyn *dyn_data 192 8 - -ScrArea 160 - - ScrArea *next 0 8 - ScrArea *prev 8 8 - ScrVert *v1 16 8 - ScrVert *v2 24 8 - ScrVert *v3 32 8 - ScrVert *v4 40 8 - bScreen *full 48 8 - rcti totrct 56 16 - char spacetype 72 1 - char butspacetype 73 1 - short winx 74 2 - short winy 76 2 - short headertype 78 2 - short do_refresh 80 2 - short flag 82 2 - short region_active_win 84 2 - short pad 86 2 - SpaceType *type 88 8 - ListBase spacedata 96 16 - ListBase regionbase 112 16 - ListBase handlers 128 16 - ListBase actionzones 144 16 - -ARegion 336 - - ARegion *next 0 8 - ARegion *prev 8 8 - View2D v2d 16 160 - rcti winrct 176 16 - rcti drawrct 192 16 - short winx 208 2 - short winy 210 2 - short swinid 212 2 - short regiontype 214 2 - short alignment 216 2 - short flag 218 2 - float fsize 220 4 - short sizex 224 2 - short sizey 226 2 - short do_draw 228 2 - short do_draw_overlay 230 2 - short swap 232 2 - short overlap 234 2 - short pad 236 4 - ARegionType *type 240 8 - ListBase uiblocks 248 16 - ListBase panels 264 16 - ListBase ui_lists 280 16 - ListBase handlers 296 16 - wmTimer *regiontimer 312 8 - char *headerstr 320 8 - void *regiondata 328 8 - -FileGlobal 1072 - - char subvstr 0 4 - short subversion 4 2 - short pads 6 2 - short minversion 8 2 - short minsubversion 10 2 - short displaymode 12 2 - short winpos 14 2 - bScreen *curscreen 16 8 - Scene *curscene 24 8 - int fileflags 32 4 - int globalf 36 4 - int revision 40 4 - int pad 44 4 - char filename 48 1024 - -StripElem 264 - - char name 0 256 - int orig_width 256 4 - int orig_height 260 4 - -StripCrop 16 - - int top 0 4 - int bottom 4 4 - int left 8 4 - int right 12 4 - -StripTransform 8 - - int xofs 0 4 - int yofs 4 4 - -StripColorBalance 44 - - float lift 0 12 - float gamma 12 12 - float gain 24 12 - int flag 36 4 - int pad 40 4 - -StripProxy 1040 - - char dir 0 768 - char file 768 256 - anim *anim 1024 8 - short tc 1032 2 - short quality 1034 2 - short build_size_flags 1036 2 - short build_tc_flags 1038 2 - -Strip 904 - - Strip *next 0 8 - Strip *prev 8 8 - int us 16 4 - int done 20 4 - int startstill 24 4 - int endstill 28 4 - StripElem *stripdata 32 8 - char dir 40 768 - StripProxy *proxy 808 8 - StripCrop *crop 816 8 - StripTransform *transform 824 8 - StripColorBalance *color_balance 832 8 - ColorManagedColorspaceSettings colorspace_settings 840 64 - -Sequence 352 - - Sequence *next 0 8 - Sequence *prev 8 8 - void *tmp 16 8 - void *lib 24 8 - char name 32 64 - int flag 96 4 - int type 100 4 - int len 104 4 - int start 108 4 - int startofs 112 4 - int endofs 116 4 - int startstill 120 4 - int endstill 124 4 - int machine 128 4 - int depth 132 4 - int startdisp 136 4 - int enddisp 140 4 - float sat 144 4 - float mul 148 4 - float handsize 152 4 - short anim_preseek 156 2 - short streamindex 158 2 - int multicam_source 160 4 - int clip_flag 164 4 - Strip *strip 168 8 - Ipo *ipo 176 8 - Scene *scene 184 8 - Object *scene_camera 192 8 - MovieClip *clip 200 8 - Mask *mask 208 8 - anim *anim 216 8 - float effect_fader 224 4 - float speed_fader 228 4 - Sequence *seq1 232 8 - Sequence *seq2 240 8 - Sequence *seq3 248 8 - ListBase seqbase 256 16 - bSound *sound 272 8 - void *scene_sound 280 8 - float volume 288 4 - float pitch 292 4 - float pan 296 4 - float strobe 300 4 - void *effectdata 304 8 - int anim_startofs 312 4 - int anim_endofs 316 4 - int blend_mode 320 4 - float blend_opacity 324 4 - int sfra 328 4 - char alpha_mode 332 1 - char pad 333 3 - ListBase modifiers 336 16 - -MetaStack 32 - - MetaStack *next 0 8 - MetaStack *prev 8 8 - ListBase *oldbasep 16 8 - Sequence *parseq 24 8 - -Editing 2128 - - ListBase *seqbasep 0 8 - ListBase seqbase 8 16 - ListBase metastack 24 16 - Sequence *act_seq 40 8 - char act_imagedir 48 1024 - char act_sounddir 1072 1024 - int over_ofs 2096 4 - int over_cfra 2100 4 - int over_flag 2104 4 - int pad 2108 4 - rctf over_border 2112 16 - -WipeVars 12 - - float edgeWidth 0 4 - float angle 4 4 - short forward 8 2 - short wipetype 10 2 - -GlowVars 24 - - float fMini 0 4 - float fClamp 4 4 - float fBoost 8 4 - float dDist 12 4 - int dQuality 16 4 - int bNoComp 20 4 - -TransformVars 32 - - float ScalexIni 0 4 - float ScaleyIni 4 4 - float xIni 8 4 - float yIni 12 4 - float rotIni 16 4 - int percent 20 4 - int interpolation 24 4 - int uniform_scale 28 4 - -SolidColorVars 16 - - float col 0 12 - float pad 12 4 - -SpeedControlVars 24 - - float *frameMap 0 8 - float globalSpeed 8 4 - int flags 12 4 - int length 16 4 - int lastValidFrame 20 4 - -SequenceModifierData 112 - - SequenceModifierData *next 0 8 - SequenceModifierData *prev 8 8 - int type 16 4 - int flag 20 4 - char name 24 64 - int mask_input_type 88 4 - int pad 92 4 - Sequence *mask_sequence 96 8 - Mask *mask_id 104 8 - -ColorBalanceModifierData 160 - - SequenceModifierData modifier 0 112 - StripColorBalance color_balance 112 44 - float color_multiply 156 4 - -CurvesModifierData 432 - - SequenceModifierData modifier 0 112 - CurveMapping curve_mapping 112 320 - -HueCorrectModifierData 432 - - SequenceModifierData modifier 0 112 - CurveMapping curve_mapping 112 320 - -BrightContrastModifierData 120 - - SequenceModifierData modifier 0 112 - float bright 112 4 - float contrast 116 4 - -SequencerMaskModifierData 112 - - SequenceModifierData modifier 0 112 - -SequencerScopes 48 - - ImBuf *reference_ibuf 0 8 - ImBuf *zebra_ibuf 8 8 - ImBuf *waveform_ibuf 16 8 - ImBuf *sep_waveform_ibuf 24 8 - ImBuf *vector_ibuf 32 8 - ImBuf *histogram_ibuf 40 8 - -Effect 24 - - Effect *next 0 8 - Effect *prev 8 8 - short type 16 2 - short flag 18 2 - short buttype 20 2 - short rt 22 2 - -BuildEff 32 - - BuildEff *next 0 8 - BuildEff *prev 8 8 - short type 16 2 - short flag 18 2 - short buttype 20 2 - short rt 22 2 - float len 24 4 - float sfra 28 4 - -PartEff 392 - - PartEff *next 0 8 - PartEff *prev 8 8 - short type 16 2 - short flag 18 2 - short buttype 20 2 - short stype 22 2 - short vertgroup 24 2 - short userjit 26 2 - float sta 28 4 - float end 32 4 - float lifetime 36 4 - int totpart 40 4 - int totkey 44 4 - int seed 48 4 - float normfac 52 4 - float obfac 56 4 - float randfac 60 4 - float texfac 64 4 - float randlife 68 4 - float force 72 12 - float damp 84 4 - float nabla 88 4 - float vectsize 92 4 - float maxlen 96 4 - float pad 100 4 - float defvec 104 12 - float mult 116 16 - float life 132 16 - short child 148 8 - short mat 156 8 - short texmap 164 2 - short curmult 166 2 - short staticstep 168 2 - short omat 170 2 - short timetex 172 2 - short speedtex 174 2 - short flag2 176 2 - short flag2neg 178 2 - short disp 180 2 - short vertgroup_v 182 2 - char vgroupname 184 64 - char vgroupname_v 248 64 - float imat 312 64 - Particle *keys 376 8 - Group *group 384 8 - -WaveEff 64 - - WaveEff *next 0 8 - WaveEff *prev 8 8 - short type 16 2 - short flag 18 2 - short buttype 20 2 - short stype 22 2 - float startx 24 4 - float starty 28 4 - float height 32 4 - float width 36 4 - float narrow 40 4 - float speed 44 4 - float minfac 48 4 - float damp 52 4 - float timeoffs 56 4 - float lifetime 60 4 - -TreeStoreElem 16 - - short type 0 2 - short nr 2 2 - short flag 4 2 - short used 6 2 - ID *id 8 8 - -TreeStore 16 - - int totelem 0 4 - int usedelem 4 4 - TreeStoreElem *data 8 8 - -bProperty 96 - - bProperty *next 0 8 - bProperty *prev 8 8 - char name 16 64 - short type 80 2 - short flag 82 2 - int data 84 4 - void *poin 88 8 - -bNearSensor 80 - - char name 0 64 - float dist 64 4 - float resetdist 68 4 - int lastval 72 4 - int pad 76 4 - -bMouseSensor 8 - - short type 0 2 - short flag 2 2 - short pad1 4 2 - short pad2 6 2 - -bTouchSensor 80 - - char name 0 64 - Material *ma 64 8 - float dist 72 4 - float pad 76 4 - -bKeyboardSensor 136 - - short key 0 2 - short qual 2 2 - short type 4 2 - short qual2 6 2 - char targetName 8 64 - char toggleName 72 64 - -bPropertySensor 200 - - int type 0 4 - int pad 4 4 - char name 8 64 - char value 72 64 - char maxvalue 136 64 - -bActuatorSensor 72 - - int type 0 4 - int pad 4 4 - char name 8 64 - -bDelaySensor 8 - - short delay 0 2 - short duration 2 2 - short flag 4 2 - short pad 6 2 - -bCollisionSensor 136 - - char name 0 64 - char materialName 64 64 - short damptimer 128 2 - short damp 130 2 - short mode 132 2 - short pad2 134 2 - -bRadarSensor 76 - - char name 0 64 - float angle 64 4 - float range 68 4 - short flag 72 2 - short axis 74 2 - -bRandomSensor 72 - - char name 0 64 - int seed 64 4 - int delay 68 4 - -bRaySensor 204 - - char name 0 64 - float range 64 4 - char propname 68 64 - char matname 132 64 - short mode 196 2 - short pad1 198 2 - int axisflag 200 4 - -bArmatureSensor 136 - - char posechannel 0 64 - char constraint 64 64 - int type 128 4 - float value 132 4 - -bMessageSensor 136 - - Object *fromObject 0 8 - char subject 8 64 - char body 72 64 - -bSensor 128 - - bSensor *next 0 8 - bSensor *prev 8 8 - short type 16 2 - short otype 18 2 - short flag 20 2 - short pulse 22 2 - short freq 24 2 - short totlinks 26 2 - short pad1 28 2 - short pad2 30 2 - char name 32 64 - void *data 96 8 - bController **links 104 8 - Object *ob 112 8 - short invert 120 2 - short level 122 2 - short tap 124 2 - short pad 126 2 - -bJoystickSensor 92 - - char name 0 64 - char type 64 1 - char joyindex 65 1 - short flag 66 2 - short axis 68 2 - short axis_single 70 2 - int axisf 72 4 - int button 76 4 - int hat 80 4 - int hatf 84 4 - int precision 88 4 - -bExpressionCont 128 - - char str 0 128 - -bPythonCont 80 - - Text *text 0 8 - char module 8 64 - int mode 72 4 - int flag 76 4 - -bController 136 - - bController *next 0 8 - bController *prev 8 8 - bController *mynew 16 8 - short type 24 2 - short flag 26 2 - short inputs 28 2 - short totlinks 30 2 - short otype 32 2 - short totslinks 34 2 - short pad2 36 2 - short pad3 38 2 - char name 40 64 - void *data 104 8 - bActuator **links 112 8 - bSensor **slinks 120 8 - short val 128 2 - short valo 130 2 - int state_mask 132 4 - -bAddObjectActuator 16 - - int time 0 4 - int pad 4 4 - Object *ob 8 8 - -bActionActuator 168 - - bAction *act 0 8 - short type 8 2 - short flag 10 2 - float sta 12 4 - float end 16 4 - char name 20 64 - char frameProp 84 64 - short blendin 148 2 - short priority 150 2 - short layer 152 2 - short end_reset 154 2 - short strideaxis 156 2 - short blend_mode 158 2 - float stridelength 160 4 - float layer_weight 164 4 - -Sound3D 32 - - float min_gain 0 4 - float max_gain 4 4 - float reference_distance 8 4 - float max_distance 12 4 - float rolloff_factor 16 4 - float cone_inner_angle 20 4 - float cone_outer_angle 24 4 - float cone_outer_gain 28 4 - -bSoundActuator 72 - - short flag 0 2 - short sndnr 2 2 - int pad1 4 4 - int pad2 8 4 - short pad3 12 4 - float volume 16 4 - float pitch 20 4 - bSound *sound 24 8 - Sound3D sound3D 32 32 - short type 64 2 - short pad4 66 2 - short pad5 68 2 - short pad6 70 2 - -bEditObjectActuator 120 - - int time 0 4 - short type 4 2 - short flag 6 2 - Object *ob 8 8 - Mesh *me 16 8 - char name 24 64 - float linVelocity 88 12 - float angVelocity 100 12 - float mass 112 4 - short localflag 116 2 - short dyn_operation 118 2 - -bSceneActuator 24 - - short type 0 2 - short pad1 2 2 - int pad 4 4 - Scene *scene 8 8 - Object *camera 16 8 - -bPropertyActuator 144 - - int pad 0 4 - int type 4 4 - char name 8 64 - char value 72 64 - Object *ob 136 8 - -bObjectActuator 112 - - short flag 0 2 - short type 2 2 - short otype 4 2 - short damping 6 2 - float forceloc 8 12 - float forcerot 20 12 - float pad 32 12 - float pad1 44 12 - float dloc 56 12 - float drot 68 12 - float linearvelocity 80 12 - float angularvelocity 92 12 - Object *reference 104 8 - -bIpoActuator 148 - - short flag 0 2 - short type 2 2 - float sta 4 4 - float end 8 4 - char name 12 64 - char frameProp 76 64 - short pad1 140 2 - short pad2 142 2 - short pad3 144 2 - short pad4 146 2 - -bCameraActuator 32 - - Object *ob 0 8 - float height 8 4 - float min 12 4 - float max 16 4 - float damping 20 4 - short pad1 24 2 - short axis 26 2 - float pad2 28 4 - -bConstraintActuator 128 - - short type 0 2 - short mode 2 2 - short flag 4 2 - short damp 6 2 - short time 8 2 - short rotdamp 10 2 - int pad 12 4 - float minloc 16 12 - float maxloc 28 12 - float minrot 40 12 - float maxrot 52 12 - char matprop 64 64 - -bGroupActuator 88 - - short flag 0 2 - short type 2 2 - int sta 4 4 - int end 8 4 - char name 12 64 - short pad 76 6 - short cur 82 2 - short butsta 84 2 - short butend 86 2 - -bRandomActuator 88 - - int seed 0 4 - int distribution 4 4 - int int_arg_1 8 4 - int int_arg_2 12 4 - float float_arg_1 16 4 - float float_arg_2 20 4 - char propname 24 64 - -bMessageActuator 208 - - char toPropName 0 64 - Object *toObject 64 8 - char subject 72 64 - short bodyType 136 2 - short pad1 138 2 - int pad2 140 4 - char body 144 64 - -bGameActuator 140 - - short flag 0 2 - short type 2 2 - int sta 4 4 - int end 8 4 - char filename 12 64 - char loadaniname 76 64 - -bVisibilityActuator 4 - - int flag 0 4 - -bTwoDFilterActuator 24 - - char pad 0 4 - short type 4 2 - short flag 6 2 - int int_arg 8 4 - float float_arg 12 4 - Text *text 16 8 - -bParentActuator 16 - - char pad 0 2 - short flag 2 2 - int type 4 4 - Object *ob 8 8 - -bStateActuator 8 - - int type 0 4 - int mask 4 4 - -bArmatureActuator 160 - - char posechannel 0 64 - char constraint 64 64 - int type 128 4 - float weight 132 4 - float influence 136 4 - float pad 140 4 - Object *target 144 8 - Object *subtarget 152 8 - -bSteeringActuator 48 - - char pad 0 5 - char flag 5 1 - short facingaxis 6 2 - int type 8 4 - float dist 12 4 - float velocity 16 4 - float acceleration 20 4 - float turnspeed 24 4 - int updateTime 28 4 - Object *target 32 8 - Object *navmesh 40 8 - -bActuator 112 - - bActuator *next 0 8 - bActuator *prev 8 8 - bActuator *mynew 16 8 - short type 24 2 - short flag 26 2 - short otype 28 2 - short go 30 2 - char name 32 64 - void *data 96 8 - Object *ob 104 8 - -bSound 1232 - - ID id 0 120 - char name 120 1024 - PackedFile *packedfile 1144 8 - void *handle 1152 8 - PackedFile *newpackedfile 1160 8 - Ipo *ipo 1168 8 - float volume 1176 4 - float attenuation 1180 4 - float pitch 1184 4 - float min_gain 1188 4 - float max_gain 1192 4 - float distance 1196 4 - int flags 1200 4 - int pad 1204 4 - void *cache 1208 8 - void *waveform 1216 8 - void *playback_handle 1224 8 - -GroupObject 40 - - GroupObject *next 0 8 - GroupObject *prev 8 8 - Object *ob 16 8 - void *lampren 24 8 - short recalc 32 2 - char pad 34 6 - -Group 152 - - ID id 0 120 - ListBase gobject 120 16 - int layer 136 4 - float dupli_ofs 140 12 - -Bone 328 - - Bone *next 0 8 - Bone *prev 8 8 - IDProperty *prop 16 8 - Bone *parent 24 8 - ListBase childbase 32 16 - char name 48 64 - float roll 112 4 - float head 116 12 - float tail 128 12 - float bone_mat 140 36 - int flag 176 4 - float arm_head 180 12 - float arm_tail 192 12 - float arm_mat 204 64 - float arm_roll 268 4 - float dist 272 4 - float weight 276 4 - float xwidth 280 4 - float length 284 4 - float zwidth 288 4 - float ease1 292 4 - float ease2 296 4 - float rad_head 300 4 - float rad_tail 304 4 - float size 308 12 - int layer 320 4 - short segments 324 2 - short pad 326 2 - -bArmature 256 - - ID id 0 120 - AnimData *adt 120 8 - ListBase bonebase 128 16 - ListBase chainbase 144 16 - ListBase *edbo 160 8 - Bone *act_bone 168 8 - EditBone *act_edbone 176 8 - void *sketch 184 8 - int flag 192 4 - int drawtype 196 4 - int gevertdeformer 200 4 - int pad 204 4 - short deformflag 208 2 - short pathflag 210 2 - int layer_used 212 4 - int layer 216 4 - int layer_protected 220 4 - short ghostep 224 2 - short ghostsize 226 2 - short ghosttype 228 2 - short pathsize 230 2 - int ghostsf 232 4 - int ghostef 236 4 - int pathsf 240 4 - int pathef 244 4 - int pathbc 248 4 - int pathac 252 4 - -bMotionPathVert 16 - - float co 0 12 - int flag 12 4 - -bMotionPath 24 - - bMotionPathVert *points 0 8 - int length 8 4 - int start_frame 12 4 - int end_frame 16 4 - int flag 20 4 - -bAnimVizSettings 48 - - int ghost_sf 0 4 - int ghost_ef 4 4 - int ghost_bc 8 4 - int ghost_ac 12 4 - short ghost_type 16 2 - short ghost_step 18 2 - short ghost_flag 20 2 - short recalc 22 2 - short path_type 24 2 - short path_step 26 2 - short path_viewflag 28 2 - short path_bakeflag 30 2 - int path_sf 32 4 - int path_ef 36 4 - int path_bc 40 4 - int path_ac 44 4 - -bPoseChannel 544 - - bPoseChannel *next 0 8 - bPoseChannel *prev 8 8 - IDProperty *prop 16 8 - ListBase constraints 24 16 - char name 40 64 - short flag 104 2 - short ikflag 106 2 - short protectflag 108 2 - short agrp_index 110 2 - char constflag 112 1 - char selectflag 113 1 - char pad0 114 6 - Bone *bone 120 8 - bPoseChannel *parent 128 8 - bPoseChannel *child 136 8 - ListBase iktree 144 16 - ListBase siktree 160 16 - bMotionPath *mpath 176 8 - Object *custom 184 8 - bPoseChannel *custom_tx 192 8 - float loc 200 12 - float size 212 12 - float eul 224 12 - float quat 236 16 - float rotAxis 252 12 - float rotAngle 264 4 - short rotmode 268 2 - short pad 270 2 - float chan_mat 272 64 - float pose_mat 336 64 - float constinv 400 64 - float pose_head 464 12 - float pose_tail 476 12 - float limitmin 488 12 - float limitmax 500 12 - float stiffness 512 12 - float ikstretch 524 4 - float ikrotweight 528 4 - float iklinweight 532 4 - void *temp 536 8 - -bPose 216 - - ListBase chanbase 0 16 - GHash *chanhash 16 8 - short flag 24 2 - short pad 26 2 - int proxy_layer 28 4 - int pad1 32 4 - float ctime 36 4 - float stride_offset 40 12 - float cyclic_offset 52 12 - ListBase agroups 64 16 - int active_group 80 4 - int iksolver 84 4 - void *ikdata 88 8 - void *ikparam 96 8 - bAnimVizSettings avs 104 48 - char proxy_act_bone 152 64 - -bIKParam 4 - - int iksolver 0 4 - -bItasc 40 - - int iksolver 0 4 - float precision 4 4 - short numiter 8 2 - short numstep 10 2 - float minstep 12 4 - float maxstep 16 4 - short solver 20 2 - short flag 22 2 - float feedback 24 4 - float maxvel 28 4 - float dampmax 32 4 - float dampeps 36 4 - -bActionGroup 120 - - bActionGroup *next 0 8 - bActionGroup *prev 8 8 - ListBase channels 16 16 - int flag 32 4 - int customCol 36 4 - char name 40 64 - ThemeWireColor cs 104 16 - -bAction 200 - - ID id 0 120 - ListBase curves 120 16 - ListBase chanbase 136 16 - ListBase groups 152 16 - ListBase markers 168 16 - int flag 184 4 - int active_marker 188 4 - int idroot 192 4 - int pad 196 4 - -bDopeSheet 112 - - ID *source 0 8 - ListBase chanbase 8 16 - Group *filter_grp 24 8 - char searchstr 32 64 - int filterflag 96 4 - int flag 100 4 - int renameIndex 104 4 - int pad 108 4 - -SpaceAction 344 - - SpaceLink *next 0 8 - SpaceLink *prev 8 8 - ListBase regionbase 16 16 - int spacetype 32 4 - float blockscale 36 4 - short blockhandler 40 16 - View2D v2d 56 160 - bAction *action 216 8 - bDopeSheet ads 224 112 - char mode 336 1 - char autosnap 337 1 - short flag 338 2 - float timeslide 340 4 - -bActionChannel 120 - - bActionChannel *next 0 8 - bActionChannel *prev 8 8 - bActionGroup *grp 16 8 - Ipo *ipo 24 8 - ListBase constraintChannels 32 16 - int flag 48 4 - char name 52 64 - int temp 116 4 - -bConstraintChannel 56 - - bConstraintChannel *next 0 8 - bConstraintChannel *prev 8 8 - Ipo *ipo 16 8 - short flag 24 2 - char name 26 30 - -bConstraint 120 - - bConstraint *next 0 8 - bConstraint *prev 8 8 - void *data 16 8 - short type 24 2 - short flag 26 2 - char ownspace 28 1 - char tarspace 29 1 - char name 30 64 - short pad 94 2 - float enforce 96 4 - float headtail 100 4 - Ipo *ipo 104 8 - float lin_error 112 4 - float rot_error 116 4 - -bConstraintTarget 160 - - bConstraintTarget *next 0 8 - bConstraintTarget *prev 8 8 - Object *tar 16 8 - char subtarget 24 64 - float matrix 88 64 - short space 152 2 - short flag 154 2 - short type 156 2 - short rotOrder 158 2 - -bPythonConstraint 112 - - Text *text 0 8 - IDProperty *prop 8 8 - int flag 16 4 - int tarnum 20 4 - ListBase targets 24 16 - Object *tar 40 8 - char subtarget 48 64 - -bKinematicConstraint 184 - - Object *tar 0 8 - short iterations 8 2 - short flag 10 2 - short rootbone 12 2 - short max_rootbone 14 2 - char subtarget 16 64 - Object *poletar 80 8 - char polesubtarget 88 64 - float poleangle 152 4 - float weight 156 4 - float orientweight 160 4 - float grabtarget 164 12 - short type 176 2 - short mode 178 2 - float dist 180 4 - -bSplineIKConstraint 24 - - Object *tar 0 8 - float *points 8 8 - short numpoints 16 2 - short chainlen 18 2 - short flag 20 2 - short xzScaleMode 22 2 - -bTrackToConstraint 88 - - Object *tar 0 8 - int reserved1 8 4 - int reserved2 12 4 - int flags 16 4 - int pad 20 4 - char subtarget 24 64 - -bRotateLikeConstraint 80 - - Object *tar 0 8 - int flag 8 4 - int reserved1 12 4 - char subtarget 16 64 - -bLocateLikeConstraint 80 - - Object *tar 0 8 - int flag 8 4 - int reserved1 12 4 - char subtarget 16 64 - -bSizeLikeConstraint 80 - - Object *tar 0 8 - int flag 8 4 - int reserved1 12 4 - char subtarget 16 64 - -bSameVolumeConstraint 8 - - int flag 0 4 - float volume 4 4 - -bTransLikeConstraint 72 - - Object *tar 0 8 - char subtarget 8 64 - -bMinMaxConstraint 104 - - Object *tar 0 8 - int minmaxflag 8 4 - float offset 12 4 - int flag 16 4 - short sticky 20 2 - short stuck 22 2 - short pad1 24 2 - short pad2 26 2 - float cache 28 12 - char subtarget 40 64 - -bActionConstraint 104 - - Object *tar 0 8 - short type 8 2 - short local 10 2 - int start 12 4 - int end 16 4 - float min 20 4 - float max 24 4 - int flag 28 4 - bAction *act 32 8 - char subtarget 40 64 - -bLockTrackConstraint 80 - - Object *tar 0 8 - int trackflag 8 4 - int lockflag 12 4 - char subtarget 16 64 - -bDampTrackConstraint 80 - - Object *tar 0 8 - int trackflag 8 4 - int pad 12 4 - char subtarget 16 64 - -bFollowPathConstraint 24 - - Object *tar 0 8 - float offset 8 4 - float offset_fac 12 4 - int followflag 16 4 - short trackflag 20 2 - short upflag 22 2 - -bStretchToConstraint 88 - - Object *tar 0 8 - int volmode 8 4 - int plane 12 4 - float orglength 16 4 - float bulge 20 4 - char subtarget 24 64 - -bRigidBodyJointConstraint 104 - - Object *tar 0 8 - Object *child 8 8 - int type 16 4 - float pivX 20 4 - float pivY 24 4 - float pivZ 28 4 - float axX 32 4 - float axY 36 4 - float axZ 40 4 - float minLimit 44 24 - float maxLimit 68 24 - float extraFz 92 4 - short flag 96 2 - short pad 98 2 - short pad1 100 2 - short pad2 102 2 - -bClampToConstraint 16 - - Object *tar 0 8 - int flag 8 4 - int flag2 12 4 - -bChildOfConstraint 144 - - Object *tar 0 8 - int flag 8 4 - int pad 12 4 - float invmat 16 64 - char subtarget 80 64 - -bTransformConstraint 128 - - Object *tar 0 8 - char subtarget 8 64 - short from 72 2 - short to 74 2 - char map 76 3 - char expo 79 1 - float from_min 80 12 - float from_max 92 12 - float to_min 104 12 - float to_max 116 12 - -bPivotConstraint 88 - - Object *tar 0 8 - char subtarget 8 64 - float offset 72 12 - short rotAxis 84 2 - short flag 86 2 - -bLocLimitConstraint 28 - - float xmin 0 4 - float xmax 4 4 - float ymin 8 4 - float ymax 12 4 - float zmin 16 4 - float zmax 20 4 - short flag 24 2 - short flag2 26 2 - -bRotLimitConstraint 28 - - float xmin 0 4 - float xmax 4 4 - float ymin 8 4 - float ymax 12 4 - float zmin 16 4 - float zmax 20 4 - short flag 24 2 - short flag2 26 2 - -bSizeLimitConstraint 28 - - float xmin 0 4 - float xmax 4 4 - float ymin 8 4 - float ymax 12 4 - float zmin 16 4 - float zmax 20 4 - short flag 24 2 - short flag2 26 2 - -bDistLimitConstraint 88 - - Object *tar 0 8 - char subtarget 8 64 - float dist 72 4 - float soft 76 4 - short flag 80 2 - short mode 82 2 - int pad 84 4 - -bShrinkwrapConstraint 24 - - Object *target 0 8 - float dist 8 4 - short shrinkType 12 2 - char projAxis 14 1 - char projAxisSpace 15 1 - float projLimit 16 4 - char pad 20 4 - -bFollowTrackConstraint 160 - - MovieClip *clip 0 8 - char track 8 64 - int flag 72 4 - int frame_method 76 4 - char object 80 64 - Object *camera 144 8 - Object *depth_ob 152 8 - -bCameraSolverConstraint 16 - - MovieClip *clip 0 8 - int flag 8 4 - int pad 12 4 - -bObjectSolverConstraint 152 - - MovieClip *clip 0 8 - int flag 8 4 - int pad 12 4 - char object 16 64 - float invmat 80 64 - Object *camera 144 8 - -bActionModifier 72 - - bActionModifier *next 0 8 - bActionModifier *prev 8 8 - short type 16 2 - short flag 18 2 - char channel 20 32 - float noisesize 52 4 - float turbul 56 4 - short channels 60 2 - short no_rot_axis 62 2 - Object *ob 64 8 - -bActionStrip 168 - - bActionStrip *next 0 8 - bActionStrip *prev 8 8 - short flag 16 2 - short mode 18 2 - short stride_axis 20 2 - short curmod 22 2 - Ipo *ipo 24 8 - bAction *act 32 8 - Object *object 40 8 - float start 48 4 - float end 52 4 - float actstart 56 4 - float actend 60 4 - float actoffs 64 4 - float stridelen 68 4 - float repeat 72 4 - float scale 76 4 - float blendin 80 4 - float blendout 84 4 - char stridechannel 88 32 - char offs_bone 120 32 - ListBase modifiers 152 16 - -bNodeStack 48 - - float vec 0 16 - float min 16 4 - float max 20 4 - void *data 24 8 - short hasinput 32 2 - short hasoutput 34 2 - short datatype 36 2 - short sockettype 38 2 - short is_copy 40 2 - short external 42 2 - short pad 44 4 - -bNodeSocket 352 - - bNodeSocket *next 0 8 - bNodeSocket *prev 8 8 - bNodeSocket *new_sock 16 8 - IDProperty *prop 24 8 - char identifier 32 64 - char name 96 64 - void *storage 160 8 - short type 168 2 - short flag 170 2 - short limit 172 2 - short in_out 174 2 - bNodeSocketType *typeinfo 176 8 - char idname 184 64 - float locx 248 4 - float locy 252 4 - void *default_value 256 8 - short stack_index 264 2 - short stack_type 266 2 - int resizemode 268 4 - void *cache 272 8 - int own_index 280 4 - int to_index 284 4 - bNodeSocket *groupsock 288 8 - bNodeLink *link 296 8 - bNodeStack ns 304 48 - -bNode 464 - - bNode *next 0 8 - bNode *prev 8 8 - bNode *new_node 16 8 - IDProperty *prop 24 8 - bNodeType *typeinfo 32 8 - char idname 40 64 - char name 104 64 - int flag 168 4 - short type 172 2 - short pad 174 2 - short done 176 2 - short level 178 2 - short lasty 180 2 - short menunr 182 2 - short stack_index 184 2 - short nr 186 2 - float color 188 12 - ListBase inputs 200 16 - ListBase outputs 216 16 - bNode *parent 232 8 - ID *id 240 8 - void *storage 248 8 - bNode *original 256 8 - ListBase internal_links 264 16 - float locx 280 4 - float locy 284 4 - float width 288 4 - float height 292 4 - float miniwidth 296 4 - float offsetx 300 4 - float offsety 304 4 - int update 308 4 - char label 312 64 - short custom1 376 2 - short custom2 378 2 - float custom3 380 4 - float custom4 384 4 - short need_exec 388 2 - short exec 390 2 - void *threaddata 392 8 - rctf totr 400 16 - rctf butr 416 16 - rctf prvr 432 16 - short preview_xsize 448 2 - short preview_ysize 450 2 - int pad2 452 4 - uiBlock *block 456 8 - -bNodeInstanceKey 4 - - int value 0 4 - -bNodeInstanceHashEntry 8 - - bNodeInstanceKey key 0 4 - short tag 4 2 - short pad 6 2 - -bNodePreview 24 - - bNodeInstanceHashEntry hash_entry 0 8 - char *rect 8 8 - short xsize 16 2 - short ysize 18 2 - int pad 20 4 - -bNodeLink 56 - - bNodeLink *next 0 8 - bNodeLink *prev 8 8 - bNode *fromnode 16 8 - bNode *tonode 24 8 - bNodeSocket *fromsock 32 8 - bNodeSocket *tosock 40 8 - int flag 48 4 - int pad 52 4 - -bNodeTree 404 - - ID id 0 120 - AnimData *adt 120 8 - bNodeTreeType *typeinfo 128 8 - char idname 136 64 - StructRNA *interface_type 200 8 - bGPdata *gpd 208 8 - float view_center 216 8 - ListBase nodes 224 16 - ListBase links 240 16 - int type 256 4 - int init 260 4 - int cur_index 264 4 - int flag 268 4 - int update 272 4 - short is_updating 276 2 - short done 278 2 - int pad2 280 4 - int nodetype 284 4 - short edit_quality 288 2 - short render_quality 290 2 - int chunksize 292 4 - rctf viewer_border 296 16 - ListBase inputs 312 16 - ListBase outputs 328 16 - bNodeInstanceHash *previews 344 8 - bNodeInstanceKey active_viewer_key 352 4 - int pad 356 4 - bNodeTreeExec *execdata 360 8 - void (*progress)() 368 0 - void (*stats_draw)() 368 0 - int (*test_break)() 368 4 - void (*update_draw)() 372 0 - void *tbh 372 8 - void *prh 380 8 - void *sdh 388 8 - void *udh 396 8 - -bNodeSocketValueInt 16 - - int subtype 0 4 - int value 4 4 - int min 8 4 - int max 12 4 - -bNodeSocketValueFloat 16 - - int subtype 0 4 - float value 4 4 - float min 8 4 - float max 12 4 - -bNodeSocketValueBoolean 4 - - char value 0 1 - char pad 1 3 - -bNodeSocketValueVector 24 - - int subtype 0 4 - float value 4 12 - float min 16 4 - float max 20 4 - -bNodeSocketValueRGBA 16 - - float value 0 16 - -bNodeSocketValueString 1032 - - int subtype 0 4 - int pad 4 4 - char value 8 1024 - -NodeFrame 4 - - short flag 0 2 - short label_size 2 2 - -NodeImageAnim 16 - - int frames 0 4 - int sfra 4 4 - int nr 8 4 - char cyclic 12 1 - char movie 13 1 - short pad 14 2 - -ColorCorrectionData 24 - - float saturation 0 4 - float contrast 4 4 - float gamma 8 4 - float gain 12 4 - float lift 16 4 - int pad 20 4 - -NodeColorCorrection 104 - - ColorCorrectionData master 0 24 - ColorCorrectionData shadows 24 24 - ColorCorrectionData midtones 48 24 - ColorCorrectionData highlights 72 24 - float startmidtones 96 4 - float endmidtones 100 4 - -NodeBokehImage 20 - - float angle 0 4 - int flaps 4 4 - float rounding 8 4 - float catadioptric 12 4 - float lensshift 16 4 - -NodeBoxMask 24 - - float x 0 4 - float y 4 4 - float rotation 8 4 - float height 12 4 - float width 16 4 - int pad 20 4 - -NodeEllipseMask 24 - - float x 0 4 - float y 4 4 - float rotation 8 4 - float height 12 4 - float width 16 4 - int pad 20 4 - -NodeImageLayer 8 - - int pass_index 0 4 - int pass_flag 4 4 - -NodeBlurData 40 - - short sizex 0 2 - short sizey 2 2 - short samples 4 2 - short maxspeed 6 2 - short minspeed 8 2 - short relative 10 2 - short aspect 12 2 - short curved 14 2 - float fac 16 4 - float percentx 20 4 - float percenty 24 4 - short filtertype 28 2 - char bokeh 30 1 - char gamma 31 1 - int image_in_width 32 4 - int image_in_height 36 4 - -NodeDBlurData 28 - - float center_x 0 4 - float center_y 4 4 - float distance 8 4 - float angle 12 4 - float spin 16 4 - float zoom 20 4 - short iter 24 2 - char wrap 26 1 - char pad 27 1 - -NodeBilateralBlurData 12 - - float sigma_color 0 4 - float sigma_space 4 4 - short iter 8 2 - short pad 10 2 - -NodeHueSat 12 - - float hue 0 4 - float sat 4 4 - float val 8 4 - -NodeImageFile 1280 - - char name 0 1024 - ImageFormatData im_format 1024 248 - int sfra 1272 4 - int efra 1276 4 - -NodeImageMultiFile 1288 - - char base_path 0 1024 - ImageFormatData format 1024 248 - int sfra 1272 4 - int efra 1276 4 - int active_input 1280 4 - int pad 1284 4 - -NodeImageMultiFileSocket 1312 - - short use_render_format 0 2 - short use_node_format 2 2 - int pad1 4 4 - char path 8 1024 - ImageFormatData format 1032 248 - char layer 1280 30 - char pad2 1310 2 - -NodeChroma 44 - - float t1 0 4 - float t2 4 4 - float t3 8 4 - float fsize 12 4 - float fstrength 16 4 - float falpha 20 4 - float key 24 16 - short algorithm 40 2 - short channel 42 2 - -NodeTwoXYs 24 - - short x1 0 2 - short x2 2 2 - short y1 4 2 - short y2 6 2 - float fac_x1 8 4 - float fac_x2 12 4 - float fac_y1 16 4 - float fac_y2 20 4 - -NodeTwoFloats 8 - - float x 0 4 - float y 4 4 - -NodeGeometry 128 - - char uvname 0 64 - char colname 64 64 - -NodeVertexCol 64 - - char name 0 64 - -NodeDefocus 32 - - char bktype 0 1 - char pad_c1 1 1 - char preview 2 1 - char gamco 3 1 - short samples 4 2 - short no_zbuf 6 2 - float fstop 8 4 - float maxblur 12 4 - float bthresh 16 4 - float scale 20 4 - float rotation 24 4 - float pad_f1 28 4 - -NodeScriptDict 16 - - void *dict 0 8 - void *node 8 8 - -NodeGlare 32 - - char quality 0 1 - char type 1 1 - char iter 2 1 - char angle 3 1 - char pad_c1 4 1 - char size 5 1 - char pad 6 2 - float colmod 8 4 - float mix 12 4 - float threshold 16 4 - float fade 20 4 - float angle_ofs 24 4 - float pad_f1 28 4 - -NodeTonemap 32 - - float key 0 4 - float offset 4 4 - float gamma 8 4 - float f 12 4 - float m 16 4 - float a 20 4 - float c 24 4 - int type 28 4 - -NodeLensDist 8 - - short jit 0 2 - short proj 2 2 - short fit 4 2 - short pad 6 2 - -NodeColorBalance 96 - - float slope 0 12 - float offset 12 12 - float power 24 12 - float lift 36 12 - float gamma 48 12 - float gain 60 12 - float lift_lgg 72 12 - float gamma_inv 84 12 - -NodeColorspill 20 - - short limchan 0 2 - short unspill 2 2 - float limscale 4 4 - float uspillr 8 4 - float uspillg 12 4 - float uspillb 16 4 - -NodeDilateErode 8 - - char falloff 0 1 - char pad 1 7 - -NodeMask 8 - - int size_x 0 4 - int size_y 4 4 - -NodeTexBase 968 - - TexMapping tex_mapping 0 144 - ColorMapping color_mapping 144 824 - -NodeTexSky 992 - - NodeTexBase base 0 968 - int sky_model 968 4 - float sun_direction 972 12 - float turbidity 984 4 - float ground_albedo 988 4 - -NodeTexImage 1024 - - NodeTexBase base 0 968 - ImageUser iuser 968 40 - int color_space 1008 4 - int projection 1012 4 - float projection_blend 1016 4 - int pad 1020 4 - -NodeTexChecker 968 - - NodeTexBase base 0 968 - -NodeTexBrick 984 - - NodeTexBase base 0 968 - int offset_freq 968 4 - int squash_freq 972 4 - float offset 976 4 - float squash 980 4 - -NodeTexEnvironment 1016 - - NodeTexBase base 0 968 - ImageUser iuser 968 40 - int color_space 1008 4 - int projection 1012 4 - -NodeTexGradient 976 - - NodeTexBase base 0 968 - int gradient_type 968 4 - int pad 972 4 - -NodeTexNoise 968 - - NodeTexBase base 0 968 - -NodeTexVoronoi 976 - - NodeTexBase base 0 968 - int coloring 968 4 - int pad 972 4 - -NodeTexMusgrave 976 - - NodeTexBase base 0 968 - int musgrave_type 968 4 - int pad 972 4 - -NodeTexWave 976 - - NodeTexBase base 0 968 - int wave_type 968 4 - int pad 972 4 - -NodeTexMagic 976 - - NodeTexBase base 0 968 - int depth 968 4 - int pad 972 4 - -NodeShaderAttribute 64 - - char name 0 64 - -NodeShaderVectTransform 16 - - int type 0 4 - int convert_from 4 4 - int convert_to 8 4 - int pad 12 4 - -TexNodeOutput 64 - - char name 0 64 - -NodeKeyingScreenData 64 - - char tracking_object 0 64 - -NodeKeyingData 48 - - float screen_balance 0 4 - float despill_factor 4 4 - float despill_balance 8 4 - int edge_kernel_radius 12 4 - float edge_kernel_tolerance 16 4 - float clip_black 20 4 - float clip_white 24 4 - int dilate_distance 28 4 - int feather_distance 32 4 - int feather_falloff 36 4 - int blur_pre 40 4 - int blur_post 44 4 - -NodeTrackPosData 128 - - char tracking_object 0 64 - char track_name 64 64 - -NodeTranslateData 8 - - char wrap_axis 0 1 - char relative 1 1 - char pad 2 6 - -NodePlaneTrackDeformData 128 - - char tracking_object 0 64 - char plane_track_name 64 64 - -NodeShaderScript 1104 - - int mode 0 4 - int flag 4 4 - char filepath 8 1024 - char bytecode_hash 1032 64 - char *bytecode 1096 8 - -NodeShaderTangent 72 - - int direction_type 0 4 - int axis 4 4 - char uv_map 8 64 - -NodeShaderNormalMap 68 - - int space 0 4 - char uv_map 4 64 - -CurveMapPoint 12 - - float x 0 4 - float y 4 4 - short flag 8 2 - short shorty 10 2 - -CurveMap 56 - - short totpoint 0 2 - short flag 2 2 - float range 4 4 - float mintable 8 4 - float maxtable 12 4 - float ext_in 16 8 - float ext_out 24 8 - CurveMapPoint *curve 32 8 - CurveMapPoint *table 40 8 - CurveMapPoint *premultable 48 8 - -CurveMapping 320 - - int flag 0 4 - int cur 4 4 - int preset 8 4 - int changed_timestamp 12 4 - rctf curr 16 16 - rctf clipr 32 16 - CurveMap cm 48 224 - float black 272 12 - float white 284 12 - float bwmul 296 12 - float sample 308 12 - -Histogram 5160 - - int channels 0 4 - int x_resolution 4 4 - float data_luma 8 1024 - float data_r 1032 1024 - float data_g 2056 1024 - float data_b 3080 1024 - float data_a 4104 1024 - float xmax 5128 4 - float ymax 5132 4 - short mode 5136 2 - short flag 5138 2 - int height 5140 4 - float co 5144 16 - -Scopes 5264 - - int ok 0 4 - int sample_full 4 4 - int sample_lines 8 4 - float accuracy 12 4 - int wavefrm_mode 16 4 - float wavefrm_alpha 20 4 - float wavefrm_yfac 24 4 - int wavefrm_height 28 4 - float vecscope_alpha 32 4 - int vecscope_height 36 4 - float minmax 40 24 - Histogram hist 64 5160 - float *waveform_1 5224 8 - float *waveform_2 5232 8 - float *waveform_3 5240 8 - float *vecscope 5248 8 - int waveform_tot 5256 4 - int pad 5260 4 - -ColorManagedViewSettings 160 - - int flag 0 4 - int pad 4 4 - char look 8 64 - char view_transform 72 64 - float exposure 136 4 - float gamma 140 4 - CurveMapping *curve_mapping 144 8 - void *pad2 152 8 - -ColorManagedDisplaySettings 64 - - char display_device 0 64 - -ColorManagedColorspaceSettings 64 - - char name 0 64 - -BrushClone 24 - - Image *image 0 8 - float offset 8 8 - float alpha 16 4 - float pad 20 4 - -Brush 1992 - - ID id 0 120 - BrushClone clone 120 24 - CurveMapping *curve 144 8 - MTex mtex 152 312 - MTex mask_mtex 464 312 - Brush *toggle_brush 776 8 - ImBuf *icon_imbuf 784 8 - PreviewImage *preview 792 8 - char icon_filepath 800 1024 - float normal_weight 1824 4 - short blend 1828 2 - short ob_mode 1830 2 - float weight 1832 4 - int size 1836 4 - int flag 1840 4 - float jitter 1844 4 - int jitter_absolute 1848 4 - int overlay_flags 1852 4 - int spacing 1856 4 - int smooth_stroke_radius 1860 4 - float smooth_stroke_factor 1864 4 - float rate 1868 4 - float rgb 1872 12 - float alpha 1884 4 - int sculpt_plane 1888 4 - float plane_offset 1892 4 - char sculpt_tool 1896 1 - char vertexpaint_tool 1897 1 - char imagepaint_tool 1898 1 - char mask_tool 1899 1 - float autosmooth_factor 1900 4 - float crease_pinch_factor 1904 4 - float plane_trim 1908 4 - float height 1912 4 - float texture_sample_bias 1916 4 - int texture_overlay_alpha 1920 4 - int mask_overlay_alpha 1924 4 - int cursor_overlay_alpha 1928 4 - float unprojected_radius 1932 4 - float add_col 1936 12 - float sub_col 1948 12 - float stencil_pos 1960 8 - float stencil_dimension 1968 8 - float mask_stencil_pos 1976 8 - float mask_stencil_dimension 1984 8 - -CustomDataLayer 104 - - int type 0 4 - int offset 4 4 - int flag 8 4 - int active 12 4 - int active_rnd 16 4 - int active_clone 20 4 - int active_mask 24 4 - int uid 28 4 - char name 32 64 - void *data 96 8 - -CustomDataExternal 1024 - - char filename 0 1024 - -CustomData 192 - - CustomDataLayer *layers 0 8 - int typemap 8 156 - int totlayer 164 4 - int maxlayer 168 4 - int totsize 172 4 - void *pool 176 8 - CustomDataExternal *external 184 8 - -HairKey 24 - - float co 0 12 - float time 12 4 - float weight 16 4 - short editflag 20 2 - short pad 22 2 - -ParticleKey 56 - - float co 0 12 - float vel 12 12 - float rot 24 16 - float ave 40 12 - float time 52 4 - -BoidParticle 56 - - Object *ground 0 8 - BoidData data 8 20 - float gravity 28 12 - float wander 40 12 - float rt 52 4 - -ParticleSpring 16 - - float rest_length 0 4 - int particle_index 4 8 - int delete_flag 12 4 - -ChildParticle 64 - - int num 0 4 - int parent 4 4 - int pa 8 16 - float w 24 16 - float fuv 40 16 - float foffset 56 4 - float rt 60 4 - -ParticleTarget 40 - - ParticleTarget *next 0 8 - ParticleTarget *prev 8 8 - Object *ob 16 8 - int psys 24 4 - short flag 28 2 - short mode 30 2 - float time 32 4 - float duration 36 4 - -ParticleDupliWeight 32 - - ParticleDupliWeight *next 0 8 - ParticleDupliWeight *prev 8 8 - Object *ob 16 8 - short count 24 2 - short flag 26 2 - short index 28 2 - short rt 30 2 - -ParticleData 200 - - ParticleKey state 0 56 - ParticleKey prev_state 56 56 - HairKey *hair 112 8 - ParticleKey *keys 120 8 - BoidParticle *boid 128 8 - int totkey 136 4 - float time 140 4 - float lifetime 144 4 - float dietime 148 4 - int num 152 4 - int num_dmcache 156 4 - float fuv 160 16 - float foffset 176 4 - float size 180 4 - float sphdensity 184 4 - int pad 188 4 - int hair_index 192 4 - short flag 196 2 - short alive 198 2 - -SPHFluidSettings 68 - - float radius 0 4 - float spring_k 4 4 - float rest_length 8 4 - float plasticity_constant 12 4 - float yield_ratio 16 4 - float plasticity_balance 20 4 - float yield_balance 24 4 - float viscosity_omega 28 4 - float viscosity_beta 32 4 - float stiffness_k 36 4 - float stiffness_knear 40 4 - float rest_density 44 4 - float buoyancy 48 4 - int flag 52 4 - int spring_frames 56 4 - short solver 60 2 - short pad 62 6 - -ParticleSettings 800 - - ID id 0 120 - AnimData *adt 120 8 - BoidSettings *boids 128 8 - SPHFluidSettings *fluid 136 8 - EffectorWeights *effector_weights 144 8 - int flag 152 4 - int rt 156 4 - short type 160 2 - short from 162 2 - short distr 164 2 - short texact 166 2 - short phystype 168 2 - short rotmode 170 2 - short avemode 172 2 - short reactevent 174 2 - int draw 176 4 - int pad1 180 4 - short draw_as 184 2 - short draw_size 186 2 - short childtype 188 2 - short pad2 190 2 - short ren_as 192 2 - short subframes 194 2 - short draw_col 196 2 - short draw_step 198 2 - short ren_step 200 2 - short hair_step 202 2 - short keys_step 204 2 - short adapt_angle 206 2 - short adapt_pix 208 2 - short disp 210 2 - short omat 212 2 - short interpolation 214 2 - short integrator 216 2 - short rotfrom 218 2 - short kink 220 2 - short kink_axis 222 2 - short bb_align 224 2 - short bb_uv_split 226 2 - short bb_anim 228 2 - short bb_split_offset 230 2 - float bb_tilt 232 4 - float bb_rand_tilt 236 4 - float bb_offset 240 8 - float bb_size 248 8 - float bb_vel_head 256 4 - float bb_vel_tail 260 4 - float color_vec_max 264 4 - short simplify_flag 268 2 - short simplify_refsize 270 2 - float simplify_rate 272 4 - float simplify_transition 276 4 - float simplify_viewport 280 4 - float sta 284 4 - float end 288 4 - float lifetime 292 4 - float randlife 296 4 - float timetweak 300 4 - float courant_target 304 4 - float jitfac 308 4 - float eff_hair 312 4 - float grid_rand 316 4 - float ps_offset 320 4 - int totpart 324 4 - int userjit 328 4 - int grid_res 332 4 - int effector_amount 336 4 - short time_flag 340 2 - short time_pad 342 6 - float normfac 348 4 - float obfac 352 4 - float randfac 356 4 - float partfac 360 4 - float tanfac 364 4 - float tanphase 368 4 - float reactfac 372 4 - float ob_vel 376 12 - float avefac 388 4 - float phasefac 392 4 - float randrotfac 396 4 - float randphasefac 400 4 - float mass 404 4 - float size 408 4 - float randsize 412 4 - float acc 416 12 - float dragfac 428 4 - float brownfac 432 4 - float dampfac 436 4 - float randlength 440 4 - int child_nbr 444 4 - int ren_child_nbr 448 4 - float parents 452 4 - float childsize 456 4 - float childrandsize 460 4 - float childrad 464 4 - float childflat 468 4 - float clumpfac 472 4 - float clumppow 476 4 - float kink_amp 480 4 - float kink_freq 484 4 - float kink_shape 488 4 - float kink_flat 492 4 - float kink_amp_clump 496 4 - float rough1 500 4 - float rough1_size 504 4 - float rough2 508 4 - float rough2_size 512 4 - float rough2_thres 516 4 - float rough_end 520 4 - float rough_end_shape 524 4 - float clength 528 4 - float clength_thres 532 4 - float parting_fac 536 4 - float parting_min 540 4 - float parting_max 544 4 - float branch_thres 548 4 - float draw_line 552 8 - float path_start 560 4 - float path_end 564 4 - int trail_count 568 4 - int keyed_loops 572 4 - MTex *mtex 576 144 - Group *dup_group 720 8 - ListBase dupliweights 728 16 - Group *eff_group 744 8 - Object *dup_ob 752 8 - Object *bb_ob 760 8 - Ipo *ipo 768 8 - PartDeflect *pd 776 8 - PartDeflect *pd2 784 8 - short use_modifier_stack 792 2 - short pad 794 6 - -ParticleSystem 656 - - ParticleSystem *next 0 8 - ParticleSystem *prev 8 8 - ParticleSettings *part 16 8 - ParticleData *particles 24 8 - ChildParticle *child 32 8 - PTCacheEdit *edit 40 8 - void (*free_edit)() 48 0 - ParticleCacheKey **pathcache 48 8 - ParticleCacheKey **childcache 56 8 - ListBase pathcachebufs 64 16 - ListBase childcachebufs 80 16 - ClothModifierData *clmd 96 8 - DerivedMesh *hair_in_dm 104 8 - DerivedMesh *hair_out_dm 112 8 - Object *target_ob 120 8 - LatticeDeformData *lattice_deform_data 128 8 - Object *parent 136 8 - ListBase targets 144 16 - char name 160 64 - float imat 224 64 - float cfra 288 4 - float tree_frame 292 4 - float bvhtree_frame 296 4 - int seed 300 4 - int child_seed 304 4 - int flag 308 4 - int totpart 312 4 - int totunexist 316 4 - int totchild 320 4 - int totcached 324 4 - int totchildcache 328 4 - short recalc 332 2 - short target_psys 334 2 - short totkeyed 336 2 - short bakespace 338 2 - char bb_uvname 340 192 - short vgroup 532 24 - short vg_neg 556 2 - short rt3 558 2 - void *renderdata 560 8 - PointCache *pointcache 568 8 - ListBase ptcaches 576 16 - ListBase *effectors 592 8 - ParticleSpring *fluid_springs 600 8 - int tot_fluidsprings 608 4 - int alloc_fluidsprings 612 4 - KDTree *tree 616 8 - BVHTree *bvhtree 624 8 - ParticleDrawData *pdd 632 8 - float *frand 640 8 - float dt_frac 648 4 - float _pad 652 4 - -ClothSimSettings 152 - - LinkNode *cache 0 8 - float mingoal 8 4 - float Cdis 12 4 - float Cvi 16 4 - float gravity 20 12 - float dt 32 4 - float mass 36 4 - float structural 40 4 - float shear 44 4 - float bending 48 4 - float max_bend 52 4 - float max_struct 56 4 - float max_shear 60 4 - float avg_spring_len 64 4 - float timescale 68 4 - float maxgoal 72 4 - float eff_force_scale 76 4 - float eff_wind_scale 80 4 - float sim_time_old 84 4 - float defgoal 88 4 - float goalspring 92 4 - float goalfrict 96 4 - float velocity_smooth 100 4 - float collider_friction 104 4 - float vel_damping 108 4 - int stepsPerFrame 112 4 - int flags 116 4 - int preroll 120 4 - int maxspringlen 124 4 - short solver_type 128 2 - short vgroup_bend 130 2 - short vgroup_mass 132 2 - short vgroup_struct 134 2 - short shapekey_rest 136 2 - short presets 138 2 - short reset 140 2 - short pad 142 2 - EffectorWeights *effector_weights 144 8 - -ClothCollSettings 56 - - LinkNode *collision_list 0 8 - float epsilon 8 4 - float self_friction 12 4 - float friction 16 4 - float selfepsilon 20 4 - float repel_force 24 4 - float distance_repel 28 4 - int flags 32 4 - short self_loop_count 36 2 - short loop_count 38 2 - Group *group 40 8 - short vgroup_selfcol 48 2 - short pad 50 2 - int pad2 52 4 - -bGPDspoint 20 - - float x 0 4 - float y 4 4 - float z 8 4 - float pressure 12 4 - float time 16 4 - -bGPDstroke 48 - - bGPDstroke *next 0 8 - bGPDstroke *prev 8 8 - bGPDspoint *points 16 8 - void *pad 24 8 - int totpoints 32 4 - short thickness 36 2 - short flag 38 2 - double inittime 40 8 - -bGPDframe 40 - - bGPDframe *next 0 8 - bGPDframe *prev 8 8 - ListBase strokes 16 16 - int framenum 32 4 - int flag 36 4 - -bGPDlayer 192 - - bGPDlayer *next 0 8 - bGPDlayer *prev 8 8 - ListBase frames 16 16 - bGPDframe *actframe 32 8 - int flag 40 4 - short thickness 44 2 - short gstep 46 2 - float color 48 16 - char info 64 128 - -bGPdata 152 - - ID id 0 120 - ListBase layers 120 16 - int flag 136 4 - short sbuffer_size 140 2 - short sbuffer_sflag 142 2 - void *sbuffer 144 8 - -ReportList 40 - - ListBase list 0 16 - int printlevel 16 4 - int storelevel 20 4 - int flag 24 4 - int pad 28 4 - wmTimer *reporttimer 32 8 - -wmWindowManager 344 - - ID id 0 120 - wmWindow *windrawable 120 8 - wmWindow *winactive 128 8 - ListBase windows 136 16 - int initialized 152 4 - short file_saved 156 2 - short op_undo_depth 158 2 - ListBase operators 160 16 - ListBase queue 176 16 - ReportList reports 192 40 - ListBase jobs 232 16 - ListBase paintcursors 248 16 - ListBase drags 264 16 - ListBase keyconfigs 280 16 - wmKeyConfig *defaultconf 296 8 - wmKeyConfig *addonconf 304 8 - wmKeyConfig *userconf 312 8 - ListBase timers 320 16 - wmTimer *autosavetimer 336 8 - -wmWindow 256 - - wmWindow *next 0 8 - wmWindow *prev 8 8 - void *ghostwin 16 8 - int winid 24 4 - short grabcursor 28 2 - short pad 30 2 - bScreen *screen 32 8 - bScreen *newscreen 40 8 - char screenname 48 64 - short posx 112 2 - short posy 114 2 - short sizex 116 2 - short sizey 118 2 - short windowstate 120 2 - short monitor 122 2 - short active 124 2 - short cursor 126 2 - short lastcursor 128 2 - short modalcursor 130 2 - short addmousemove 132 2 - short pad2 134 2 - wmEvent *eventstate 136 8 - wmSubWindow *curswin 144 8 - wmGesture *tweak 152 8 - int drawmethod 160 4 - int drawfail 164 4 - void *drawdata 168 8 - ListBase queue 176 16 - ListBase handlers 192 16 - ListBase modalhandlers 208 16 - ListBase subwindows 224 16 - ListBase gesture 240 16 - -wmKeyMapItem 184 - - wmKeyMapItem *next 0 8 - wmKeyMapItem *prev 8 8 - char idname 16 64 - IDProperty *properties 80 8 - char propvalue_str 88 64 - short propvalue 152 2 - short type 154 2 - short val 156 2 - short shift 158 2 - short ctrl 160 2 - short alt 162 2 - short oskey 164 2 - short keymodifier 166 2 - short flag 168 2 - short maptype 170 2 - short id 172 2 - short pad 174 2 - PointerRNA *ptr 176 8 - -wmKeyMapDiffItem 32 - - wmKeyMapDiffItem *next 0 8 - wmKeyMapDiffItem *prev 8 8 - wmKeyMapItem *remove_item 16 8 - wmKeyMapItem *add_item 24 8 - -wmKeyMap 132 - - wmKeyMap *next 0 8 - wmKeyMap *prev 8 8 - ListBase items 16 16 - ListBase diff_items 32 16 - char idname 48 64 - short spaceid 112 2 - short regionid 114 2 - short flag 116 2 - short kmi_id 118 2 - int (*poll)() 120 4 - void *modal_items 124 8 - -wmKeyConfig 168 - - wmKeyConfig *next 0 8 - wmKeyConfig *prev 8 8 - char idname 16 64 - char basename 80 64 - ListBase keymaps 144 16 - int actkeymap 160 4 - int flag 164 4 - -wmOperator 168 - - wmOperator *next 0 8 - wmOperator *prev 8 8 - char idname 16 64 - IDProperty *properties 80 8 - wmOperatorType *type 88 8 - void *customdata 96 8 - void *py_instance 104 8 - PointerRNA *ptr 112 8 - ReportList *reports 120 8 - ListBase macro 128 16 - wmOperator *opm 144 8 - uiLayout *layout 152 8 - short flag 160 2 - short pad 162 6 - -FModifier 120 - - FModifier *next 0 8 - FModifier *prev 8 8 - void *data 16 8 - void *edata 24 8 - char name 32 64 - short type 96 2 - short flag 98 2 - float influence 100 4 - float sfra 104 4 - float efra 108 4 - float blendin 112 4 - float blendout 116 4 - -FMod_Generator 24 - - float *coefficients 0 8 - int arraysize 8 4 - int poly_order 12 4 - int mode 16 4 - int flag 20 4 - -FMod_FunctionGenerator 24 - - float amplitude 0 4 - float phase_multiplier 4 4 - float phase_offset 8 4 - float value_offset 12 4 - int type 16 4 - int flag 20 4 - -FCM_EnvelopeData 16 - - float min 0 4 - float max 4 4 - float time 8 4 - short f1 12 2 - short f2 14 2 - -FMod_Envelope 24 - - FCM_EnvelopeData *data 0 8 - int totvert 8 4 - float midval 12 4 - float min 16 4 - float max 20 4 - -FMod_Cycles 8 - - short before_mode 0 2 - short after_mode 2 2 - short before_cycles 4 2 - short after_cycles 6 2 - -FMod_Python 16 - - Text *script 0 8 - IDProperty *prop 8 8 - -FMod_Limits 24 - - rctf rect 0 16 - int flag 16 4 - int pad 20 4 - -FMod_Noise 20 - - float size 0 4 - float strength 4 4 - float phase 8 4 - float pad 12 4 - short depth 16 2 - short modification 18 2 - -FMod_Stepped 20 - - float step_size 0 4 - float offset 4 4 - float start_frame 8 4 - float end_frame 12 4 - int flag 16 4 - -DriverTarget 56 - - ID *id 0 8 - char *rna_path 8 8 - char pchan_name 16 32 - short transChan 48 2 - short flag 50 2 - int idtype 52 4 - -DriverVar 536 - - DriverVar *next 0 8 - DriverVar *prev 8 8 - char name 16 64 - DriverTarget targets 80 448 - short num_targets 528 2 - short type 530 2 - float curval 532 4 - -ChannelDriver 296 - - ListBase variables 0 16 - char expression 16 256 - void *expr_comp 272 8 - float curval 280 4 - float influence 284 4 - int type 288 4 - int flag 292 4 - -FPoint 16 - - float vec 0 8 - int flag 8 4 - int pad 12 4 - -FCurve 104 - - FCurve *next 0 8 - FCurve *prev 8 8 - bActionGroup *grp 16 8 - ChannelDriver *driver 24 8 - ListBase modifiers 32 16 - BezTriple *bezt 48 8 - FPoint *fpt 56 8 - int totvert 64 4 - float curval 68 4 - short flag 72 2 - short extend 74 2 - int array_index 76 4 - char *rna_path 80 8 - int color_mode 88 4 - float color 92 12 - -AnimMapPair 256 - - char from 0 128 - char to 128 128 - -AnimMapper 40 - - AnimMapper *next 0 8 - AnimMapper *prev 8 8 - bAction *target 16 8 - ListBase mappings 24 16 - -NlaStrip 208 - - NlaStrip *next 0 8 - NlaStrip *prev 8 8 - ListBase strips 16 16 - bAction *act 32 8 - AnimMapper *remap 40 8 - ListBase fcurves 48 16 - ListBase modifiers 64 16 - char name 80 64 - float influence 144 4 - float strip_time 148 4 - float start 152 4 - float end 156 4 - float actstart 160 4 - float actend 164 4 - float repeat 168 4 - float scale 172 4 - float blendin 176 4 - float blendout 180 4 - short blendmode 184 2 - short extendmode 186 2 - short pad1 188 2 - short type 190 2 - void *speaker_handle 192 8 - int flag 200 4 - int pad2 204 4 - -NlaTrack 104 - - NlaTrack *next 0 8 - NlaTrack *prev 8 8 - ListBase strips 16 16 - int flag 32 4 - int index 36 4 - char name 40 64 - -KS_Path 112 - - KS_Path *next 0 8 - KS_Path *prev 8 8 - ID *id 16 8 - char group 24 64 - int idtype 88 4 - short groupmode 92 2 - short pad 94 2 - char *rna_path 96 8 - int array_index 104 4 - short flag 108 2 - short keyingflag 110 2 - -KeyingSet 472 - - KeyingSet *next 0 8 - KeyingSet *prev 8 8 - ListBase paths 16 16 - char idname 32 64 - char name 96 64 - char description 160 240 - char typeinfo 400 64 - short flag 464 2 - short keyingflag 466 2 - int active_path 468 4 - -AnimOverride 32 - - AnimOverride *next 0 8 - AnimOverride *prev 8 8 - char *rna_path 16 8 - int array_index 24 4 - float value 28 4 - -AnimData 96 - - bAction *action 0 8 - bAction *tmpact 8 8 - AnimMapper *remap 16 8 - ListBase nla_tracks 24 16 - NlaStrip *actstrip 40 8 - ListBase drivers 48 16 - ListBase overrides 64 16 - int flag 80 4 - int recalc 84 4 - short act_blendmode 88 2 - short act_extendmode 90 2 - float act_influence 92 4 - -IdAdtTemplate 128 - - ID id 0 120 - AnimData *adt 120 8 - -BoidRule 56 - - BoidRule *next 0 8 - BoidRule *prev 8 8 - int type 16 4 - int flag 20 4 - char name 24 32 - -BoidRuleGoalAvoid 80 - - BoidRule rule 0 56 - Object *ob 56 8 - int options 64 4 - float fear_factor 68 4 - int signal_id 72 4 - int channels 76 4 - -BoidRuleAvoidCollision 64 - - BoidRule rule 0 56 - int options 56 4 - float look_ahead 60 4 - -BoidRuleFollowLeader 104 - - BoidRule rule 0 56 - Object *ob 56 8 - float loc 64 12 - float oloc 76 12 - float cfra 88 4 - float distance 92 4 - int options 96 4 - int queue_size 100 4 - -BoidRuleAverageSpeed 72 - - BoidRule rule 0 56 - float wander 56 4 - float level 60 4 - float speed 64 4 - float rt 68 4 - -BoidRuleFight 64 - - BoidRule rule 0 56 - float distance 56 4 - float flee_distance 60 4 - -BoidData 20 - - float health 0 4 - float acc 4 12 - short state_id 16 2 - short mode 18 2 - -BoidState 128 - - BoidState *next 0 8 - BoidState *prev 8 8 - ListBase rules 16 16 - ListBase conditions 32 16 - ListBase actions 48 16 - char name 64 32 - int id 96 4 - int flag 100 4 - int ruleset_type 104 4 - float rule_fuzziness 108 4 - int signal_id 112 4 - int channels 116 4 - float volume 120 4 - float falloff 124 4 - -BoidSettings 104 - - int options 0 4 - int last_state_id 4 4 - float landing_smoothness 8 4 - float height 12 4 - float banking 16 4 - float pitch 20 4 - float health 24 4 - float aggression 28 4 - float strength 32 4 - float accuracy 36 4 - float range 40 4 - float air_min_speed 44 4 - float air_max_speed 48 4 - float air_max_acc 52 4 - float air_max_ave 56 4 - float air_personal_space 60 4 - float land_jump_speed 64 4 - float land_max_speed 68 4 - float land_max_acc 72 4 - float land_max_ave 76 4 - float land_personal_space 80 4 - float land_stick_force 84 4 - ListBase states 88 16 - -SmokeDomainSettings 584 - - SmokeModifierData *smd 0 8 - FLUID_3D *fluid 8 8 - void *fluid_mutex 16 8 - Group *fluid_group 24 8 - Group *eff_group 32 8 - Group *coll_group 40 8 - WTURBULENCE *wt 48 8 - GPUTexture *tex 56 8 - GPUTexture *tex_wt 64 8 - GPUTexture *tex_shadow 72 8 - GPUTexture *tex_flame 80 8 - float *shadow 88 8 - float p0 96 12 - float p1 108 12 - float dp0 120 12 - float cell_size 132 12 - float global_size 144 12 - float prev_loc 156 12 - int shift 168 12 - float shift_f 180 12 - float obj_shift_f 192 12 - float imat 204 64 - float obmat 268 64 - int base_res 332 12 - int res_min 344 12 - int res_max 356 12 - int res 368 12 - int total_cells 380 4 - float dx 384 4 - float scale 388 4 - int adapt_margin 392 4 - int adapt_res 396 4 - float adapt_threshold 400 4 - float alpha 404 4 - float beta 408 4 - int amplify 412 4 - int maxres 416 4 - int flags 420 4 - int viewsettings 424 4 - short noise 428 2 - short diss_percent 430 2 - int diss_speed 432 4 - float strength 436 4 - int res_wt 440 12 - float dx_wt 452 4 - int cache_comp 456 4 - int cache_high_comp 460 4 - PointCache *point_cache 464 16 - ListBase ptcaches 480 32 - EffectorWeights *effector_weights 512 8 - int border_collisions 520 4 - float time_scale 524 4 - float vorticity 528 4 - int active_fields 532 4 - float active_color 536 12 - int highres_sampling 548 4 - float burning_rate 552 4 - float flame_smoke 556 4 - float flame_vorticity 560 4 - float flame_ignition 564 4 - float flame_max_temp 568 4 - float flame_smoke_color 572 12 - -SmokeFlowSettings 184 - - SmokeModifierData *smd 0 8 - DerivedMesh *dm 8 8 - ParticleSystem *psys 16 8 - Tex *noise_texture 24 8 - float *verts_old 32 8 - int numverts 40 4 - float vel_multi 44 4 - float vel_normal 48 4 - float vel_random 52 4 - float density 56 4 - float color 60 12 - float fuel_amount 72 4 - float temp 76 4 - float volume_density 80 4 - float surface_distance 84 4 - float particle_size 88 4 - int subframes 92 4 - float texture_size 96 4 - float texture_offset 100 4 - int pad 104 4 - char uvlayer_name 108 64 - short vgroup_density 172 2 - short type 174 2 - short source 176 2 - short texture_type 178 2 - int flags 180 4 - -SmokeCollSettings 32 - - SmokeModifierData *smd 0 8 - DerivedMesh *dm 8 8 - float *verts_old 16 8 - int numverts 24 4 - short type 28 2 - short pad 30 2 - -Speaker 184 - - ID id 0 120 - AnimData *adt 120 8 - bSound *sound 128 8 - float volume_max 136 4 - float volume_min 140 4 - float distance_max 144 4 - float distance_reference 148 4 - float attenuation 152 4 - float cone_angle_outer 156 4 - float cone_angle_inner 160 4 - float cone_volume_outer 164 4 - float volume 168 4 - float pitch 172 4 - short flag 176 2 - short pad1 178 6 - -MovieClipUser 8 - - int framenr 0 4 - short render_size 4 2 - short render_flag 6 2 - -MovieClipProxy 776 - - char dir 0 768 - short tc 768 2 - short quality 770 2 - short build_size_flag 772 2 - short build_tc_flag 774 2 - -MovieClip 2384 - - ID id 0 120 - AnimData *adt 120 8 - char name 128 1024 - int source 1152 4 - int lastframe 1156 4 - int lastsize 1160 8 - float aspx 1168 4 - float aspy 1172 4 - anim *anim 1176 8 - MovieClipCache *cache 1184 8 - bGPdata *gpd 1192 8 - MovieTracking tracking 1200 320 - void *tracking_context 1520 8 - MovieClipProxy proxy 1528 776 - int flag 2304 4 - int len 2308 4 - int start_frame 2312 4 - int frame_offset 2316 4 - ColorManagedColorspaceSettings colorspace_settings 2320 64 - -MovieClipScopes 136 - - short ok 0 2 - short use_track_mask 2 2 - int track_preview_height 4 4 - int frame_width 8 4 - int frame_height 12 4 - MovieTrackingMarker undist_marker 16 64 - ImBuf *track_search 80 8 - ImBuf *track_preview 88 8 - float track_pos 96 8 - short track_disabled 104 2 - short track_locked 106 2 - int framenr 108 4 - MovieTrackingTrack *track 112 8 - MovieTrackingMarker *marker 120 8 - float slide_scale 128 8 - -MovieReconstructedCamera 72 - - int framenr 0 4 - float error 4 4 - float mat 8 64 - -MovieTrackingCamera 48 - - void *intrinsics 0 8 - float sensor_width 8 4 - float pixel_aspect 12 4 - float pad 16 4 - float focal 20 4 - short units 24 2 - short pad1 26 2 - float principal 28 8 - float k1 36 4 - float k2 40 4 - float k3 44 4 - -MovieTrackingMarker 64 - - float pos 0 8 - float pattern_corners 8 32 - float search_min 40 8 - float search_max 48 8 - int framenr 56 4 - int flag 60 4 - -MovieTrackingTrack 200 - - MovieTrackingTrack *next 0 8 - MovieTrackingTrack *prev 8 8 - char name 16 64 - float pat_min 80 8 - float pat_max 88 8 - float search_min 96 8 - float search_max 104 8 - float offset 112 8 - int markersnr 120 4 - int last_marker 124 4 - MovieTrackingMarker *markers 128 8 - float bundle_pos 136 12 - float error 148 4 - int flag 152 4 - int pat_flag 156 4 - int search_flag 160 4 - float color 164 12 - short frames_limit 176 2 - short margin 178 2 - short pattern_match 180 2 - short motion_model 182 2 - int algorithm_flag 184 4 - float minimum_correlation 188 4 - bGPdata *gpd 192 8 - -MovieTrackingPlaneMarker 40 - - float corners 0 32 - int framenr 32 4 - int flag 36 4 - -MovieTrackingPlaneTrack 120 - - MovieTrackingPlaneTrack *next 0 8 - MovieTrackingPlaneTrack *prev 8 8 - char name 16 64 - MovieTrackingTrack **point_tracks 80 8 - int point_tracksnr 88 4 - int pad 92 4 - MovieTrackingPlaneMarker *markers 96 8 - int markersnr 104 4 - int flag 108 4 - int last_marker 112 4 - int pad2 116 4 - -MovieTrackingSettings 72 - - int flag 0 4 - short default_motion_model 4 2 - short default_algorithm_flag 6 2 - float default_minimum_correlation 8 4 - short default_pattern_size 12 2 - short default_search_size 14 2 - short default_frames_limit 16 2 - short default_margin 18 2 - short default_pattern_match 20 2 - short default_flag 22 2 - short motion_flag 24 2 - short speed 26 2 - int keyframe1 28 4 - int keyframe2 32 4 - float reconstruction_success_threshold 36 4 - int reconstruction_flag 40 4 - short refine_camera_intrinsics 44 2 - short pad2 46 2 - float dist 48 4 - int clean_frames 52 4 - int clean_action 56 4 - float clean_error 60 4 - float object_distance 64 4 - int pad3 68 4 - -MovieTrackingStabilization 48 - - int flag 0 4 - int tot_track 4 4 - int act_track 8 4 - float maxscale 12 4 - MovieTrackingTrack *rot_track 16 8 - float locinf 24 4 - float scaleinf 28 4 - float rotinf 32 4 - int filter 36 4 - int ok 40 4 - float scale 44 4 - -MovieTrackingReconstruction 24 - - int flag 0 4 - float error 4 4 - int last_camera 8 4 - int camnr 12 4 - MovieReconstructedCamera *cameras 16 8 - -MovieTrackingObject 152 - - MovieTrackingObject *next 0 8 - MovieTrackingObject *prev 8 8 - char name 16 64 - int flag 80 4 - float scale 84 4 - ListBase tracks 88 16 - ListBase plane_tracks 104 16 - MovieTrackingReconstruction reconstruction 120 24 - int keyframe1 144 4 - int keyframe2 148 4 - -MovieTrackingStats 256 - - char message 0 256 - -MovieTrackingDopesheetChannel 112 - - MovieTrackingDopesheetChannel *next 0 8 - MovieTrackingDopesheetChannel *prev 8 8 - MovieTrackingTrack *track 16 8 - int pad 24 4 - char name 28 64 - int tot_segment 92 4 - int *segments 96 8 - int max_segment 104 4 - int total_frames 108 4 - -MovieTrackingDopesheetCoverageSegment 32 - - MovieTrackingDopesheetCoverageSegment *next 0 8 - MovieTrackingDopesheetCoverageSegment *prev 8 8 - int coverage 16 4 - int start_frame 20 4 - int end_frame 24 4 - int pad 28 4 - -MovieTrackingDopesheet 48 - - int ok 0 4 - short sort_method 4 2 - short flag 6 2 - ListBase coverage_segments 8 16 - ListBase channels 24 16 - int tot_channel 40 4 - int pad 44 4 - -MovieTracking 320 - - MovieTrackingSettings settings 0 72 - MovieTrackingCamera camera 72 48 - ListBase tracks 120 16 - ListBase plane_tracks 136 16 - MovieTrackingReconstruction reconstruction 152 24 - MovieTrackingStabilization stabilization 176 48 - MovieTrackingTrack *act_track 224 8 - MovieTrackingPlaneTrack *act_plane_track 232 8 - ListBase objects 240 16 - int objectnr 256 4 - int tot_object 260 4 - MovieTrackingStats *stats 264 8 - MovieTrackingDopesheet dopesheet 272 48 - -DynamicPaintSurface 1560 - - DynamicPaintSurface *next 0 8 - DynamicPaintSurface *prev 8 8 - DynamicPaintCanvasSettings *canvas 16 8 - PaintSurfaceData *data 24 8 - Group *brush_group 32 8 - EffectorWeights *effector_weights 40 8 - PointCache *pointcache 48 8 - ListBase ptcaches 56 16 - int current_frame 72 4 - char name 76 64 - short format 140 2 - short type 142 2 - short disp_type 144 2 - short image_fileformat 146 2 - short effect_ui 148 2 - short preview_id 150 2 - short init_color_type 152 2 - short pad_s 154 2 - int flags 156 4 - int effect 160 4 - int image_resolution 164 4 - int substeps 168 4 - int start_frame 172 4 - int end_frame 176 4 - int pad 180 4 - float init_color 184 16 - Tex *init_texture 200 8 - char init_layername 208 64 - int dry_speed 272 4 - int diss_speed 276 4 - float color_dry_threshold 280 4 - float depth_clamp 284 4 - float disp_factor 288 4 - float spread_speed 292 4 - float color_spread_speed 296 4 - float shrink_speed 300 4 - float drip_vel 304 4 - float drip_acc 308 4 - float influence_scale 312 4 - float radius_scale 316 4 - float wave_damping 320 4 - float wave_speed 324 4 - float wave_timescale 328 4 - float wave_spring 332 4 - float wave_smoothness 336 4 - int pad2 340 4 - char uvlayer_name 344 64 - char image_output_path 408 1024 - char output_name 1432 64 - char output_name2 1496 64 - -DynamicPaintCanvasSettings 104 - - DynamicPaintModifierData *pmd 0 8 - DerivedMesh *dm 8 8 - ListBase surfaces 16 16 - short active_sur 32 2 - short flags 34 2 - int pad 36 4 - char error 40 64 - -DynamicPaintBrushSettings 112 - - DynamicPaintModifierData *pmd 0 8 - DerivedMesh *dm 8 8 - ParticleSystem *psys 16 8 - Material *mat 24 8 - int flags 32 4 - int collision 36 4 - float r 40 4 - float g 44 4 - float b 48 4 - float alpha 52 4 - float wetness 56 4 - float particle_radius 60 4 - float particle_smooth 64 4 - float paint_distance 68 4 - ColorBand *paint_ramp 72 8 - ColorBand *vel_ramp 80 8 - short proximity_falloff 88 2 - short wave_type 90 2 - short ray_dir 92 2 - short pad 94 2 - float wave_factor 96 4 - float wave_clamp 100 4 - float max_velocity 104 4 - float smudge_strength 108 4 - -Mask 168 - - ID id 0 120 - AnimData *adt 120 8 - ListBase masklayers 128 16 - int masklay_act 144 4 - int masklay_tot 148 4 - int sfra 152 4 - int efra 156 4 - int flag 160 4 - int pad 164 4 - -MaskParent 184 - - int id_type 0 4 - int type 4 4 - ID *id 8 8 - char parent 16 64 - char sub_parent 80 64 - float parent_orig 144 8 - float parent_corners_orig 152 32 - -MaskSplinePointUW 12 - - float u 0 4 - float w 4 4 - int flag 8 4 - -MaskSplinePoint 256 - - BezTriple bezt 0 56 - int pad 56 4 - int tot_uw 60 4 - MaskSplinePointUW *uw 64 8 - MaskParent parent 72 184 - -MaskSpline 224 - - MaskSpline *next 0 8 - MaskSpline *prev 8 8 - short flag 16 2 - char offset_mode 18 1 - char weight_interp 19 1 - int tot_point 20 4 - MaskSplinePoint *points 24 8 - MaskParent parent 32 184 - MaskSplinePoint *points_deform 216 8 - -MaskLayerShape 40 - - MaskLayerShape *next 0 8 - MaskLayerShape *prev 8 8 - float *data 16 8 - int tot_vert 24 4 - int frame 28 4 - char flag 32 1 - char pad 33 7 - -MaskLayer 144 - - MaskLayer *next 0 8 - MaskLayer *prev 8 8 - char name 16 64 - ListBase splines 80 16 - ListBase splines_shapes 96 16 - MaskSpline *act_spline 112 8 - MaskSplinePoint *act_point 120 8 - float alpha 128 4 - char blend 132 1 - char blend_flag 133 1 - char falloff 134 1 - char pad 135 7 - char flag 142 1 - char restrictflag 143 1 - -RigidBodyWorld 88 - - EffectorWeights *effector_weights 0 8 - Group *group 8 8 - Object **objects 16 8 - Group *constraints 24 8 - int pad 32 4 - float ltime 36 4 - PointCache *pointcache 40 8 - ListBase ptcaches 48 16 - int numbodies 64 4 - short steps_per_second 68 2 - short num_solver_iterations 70 2 - int flag 72 4 - float time_scale 76 4 - void *physics_world 80 8 - -RigidBodyOb 96 - - void *physics_object 0 8 - void *physics_shape 8 8 - short type 16 2 - short shape 18 2 - int flag 20 4 - int col_groups 24 4 - int pad 28 4 - float mass 32 4 - float friction 36 4 - float restitution 40 4 - float margin 44 4 - float lin_damping 48 4 - float ang_damping 52 4 - float lin_sleep_thresh 56 4 - float ang_sleep_thresh 60 4 - float orn 64 16 - float pos 80 12 - float pad1 92 4 - -RigidBodyCon 128 - - Object *ob1 0 8 - Object *ob2 8 8 - short type 16 2 - short num_solver_iterations 18 2 - int flag 20 4 - float breaking_threshold 24 4 - float pad 28 4 - float limit_lin_x_lower 32 4 - float limit_lin_x_upper 36 4 - float limit_lin_y_lower 40 4 - float limit_lin_y_upper 44 4 - float limit_lin_z_lower 48 4 - float limit_lin_z_upper 52 4 - float limit_ang_x_lower 56 4 - float limit_ang_x_upper 60 4 - float limit_ang_y_lower 64 4 - float limit_ang_y_upper 68 4 - float limit_ang_z_lower 72 4 - float limit_ang_z_upper 76 4 - float spring_stiffness_x 80 4 - float spring_stiffness_y 84 4 - float spring_stiffness_z 88 4 - float spring_damping_x 92 4 - float spring_damping_y 96 4 - float spring_damping_z 100 4 - float motor_lin_target_velocity 104 4 - float motor_ang_target_velocity 108 4 - float motor_lin_max_impulse 112 4 - float motor_ang_max_impulse 116 4 - void *physics_constraint 120 8 - -FreestyleLineSet 128 - - FreestyleLineSet *next 0 8 - FreestyleLineSet *prev 8 8 - char name 16 64 - int flags 80 4 - int selection 84 4 - short qi 88 2 - short pad1 90 2 - int qi_start 92 4 - int qi_end 96 4 - int edge_types 100 4 - int exclude_edge_types 104 4 - int pad2 108 4 - Group *group 112 8 - FreestyleLineStyle *linestyle 120 8 - -FreestyleModuleConfig 32 - - FreestyleModuleConfig *next 0 8 - FreestyleModuleConfig *prev 8 8 - Text *script 16 8 - short is_displayed 24 2 - short pad 26 6 - -FreestyleConfig 56 - - ListBase modules 0 16 - int mode 16 4 - int raycasting_algorithm 20 4 - int flags 24 4 - float sphere_radius 28 4 - float dkr_epsilon 32 4 - float crease_angle 36 4 - ListBase linesets 40 16 - -LineStyleModifier 96 - - LineStyleModifier *next 0 8 - LineStyleModifier *prev 8 8 - char name 16 64 - int type 80 4 - float influence 84 4 - int flags 88 4 - int blend 92 4 - -LineStyleColorModifier_AlongStroke 104 - - LineStyleModifier modifier 0 96 - ColorBand *color_ramp 96 8 - -LineStyleAlphaModifier_AlongStroke 112 - - LineStyleModifier modifier 0 96 - CurveMapping *curve 96 8 - int flags 104 4 - int pad 108 4 - -LineStyleThicknessModifier_AlongStroke 120 - - LineStyleModifier modifier 0 96 - CurveMapping *curve 96 8 - int flags 104 4 - float value_min 108 4 - float value_max 112 4 - int pad 116 4 - -LineStyleColorModifier_DistanceFromCamera 112 - - LineStyleModifier modifier 0 96 - ColorBand *color_ramp 96 8 - float range_min 104 4 - float range_max 108 4 - -LineStyleAlphaModifier_DistanceFromCamera 120 - - LineStyleModifier modifier 0 96 - CurveMapping *curve 96 8 - int flags 104 4 - float range_min 108 4 - float range_max 112 4 - int pad 116 4 - -LineStyleThicknessModifier_DistanceFromCamera 128 - - LineStyleModifier modifier 0 96 - CurveMapping *curve 96 8 - int flags 104 4 - float range_min 108 4 - float range_max 112 4 - float value_min 116 4 - float value_max 120 4 - int pad 124 4 - -LineStyleColorModifier_DistanceFromObject 120 - - LineStyleModifier modifier 0 96 - Object *target 96 8 - ColorBand *color_ramp 104 8 - float range_min 112 4 - float range_max 116 4 - -LineStyleAlphaModifier_DistanceFromObject 128 - - LineStyleModifier modifier 0 96 - Object *target 96 8 - CurveMapping *curve 104 8 - int flags 112 4 - float range_min 116 4 - float range_max 120 4 - int pad 124 4 - -LineStyleThicknessModifier_DistanceFromObject 136 - - LineStyleModifier modifier 0 96 - Object *target 96 8 - CurveMapping *curve 104 8 - int flags 112 4 - float range_min 116 4 - float range_max 120 4 - float value_min 124 4 - float value_max 128 4 - int pad 132 4 - -LineStyleColorModifier_Material 112 - - LineStyleModifier modifier 0 96 - ColorBand *color_ramp 96 8 - int flags 104 4 - int mat_attr 108 4 - -LineStyleAlphaModifier_Material 112 - - LineStyleModifier modifier 0 96 - CurveMapping *curve 96 8 - int flags 104 4 - int mat_attr 108 4 - -LineStyleThicknessModifier_Material 120 - - LineStyleModifier modifier 0 96 - CurveMapping *curve 96 8 - int flags 104 4 - float value_min 108 4 - float value_max 112 4 - int mat_attr 116 4 - -LineStyleGeometryModifier_Sampling 104 - - LineStyleModifier modifier 0 96 - float sampling 96 4 - int pad 100 4 - -LineStyleGeometryModifier_BezierCurve 104 - - LineStyleModifier modifier 0 96 - float error 96 4 - int pad 100 4 - -LineStyleGeometryModifier_SinusDisplacement 112 - - LineStyleModifier modifier 0 96 - float wavelength 96 4 - float amplitude 100 4 - float phase 104 4 - int pad 108 4 - -LineStyleGeometryModifier_SpatialNoise 112 - - LineStyleModifier modifier 0 96 - float amplitude 96 4 - float scale 100 4 - int octaves 104 4 - int flags 108 4 - -LineStyleGeometryModifier_PerlinNoise1D 120 - - LineStyleModifier modifier 0 96 - float frequency 96 4 - float amplitude 100 4 - float angle 104 4 - int octaves 108 4 - int seed 112 4 - int pad1 116 4 - -LineStyleGeometryModifier_PerlinNoise2D 120 - - LineStyleModifier modifier 0 96 - float frequency 96 4 - float amplitude 100 4 - float angle 104 4 - int octaves 108 4 - int seed 112 4 - int pad1 116 4 - -LineStyleGeometryModifier_BackboneStretcher 104 - - LineStyleModifier modifier 0 96 - float backbone_length 96 4 - int pad 100 4 - -LineStyleGeometryModifier_TipRemover 104 - - LineStyleModifier modifier 0 96 - float tip_length 96 4 - int pad 100 4 - -LineStyleGeometryModifier_Polygonalization 104 - - LineStyleModifier modifier 0 96 - float error 96 4 - int pad 100 4 - -LineStyleGeometryModifier_GuidingLines 104 - - LineStyleModifier modifier 0 96 - float offset 96 4 - int pad 100 4 - -LineStyleGeometryModifier_Blueprint 120 - - LineStyleModifier modifier 0 96 - int flags 96 4 - int rounds 100 4 - float backbone_length 104 4 - int random_radius 108 4 - int random_center 112 4 - int random_backbone 116 4 - -LineStyleGeometryModifier_2DOffset 112 - - LineStyleModifier modifier 0 96 - float start 96 4 - float end 100 4 - float x 104 4 - float y 108 4 - -LineStyleGeometryModifier_2DTransform 128 - - LineStyleModifier modifier 0 96 - int pivot 96 4 - float scale_x 100 4 - float scale_y 104 4 - float angle 108 4 - float pivot_u 112 4 - float pivot_x 116 4 - float pivot_y 120 4 - int pad 124 4 - -LineStyleThicknessModifier_Calligraphy 112 - - LineStyleModifier modifier 0 96 - float min_thickness 96 4 - float max_thickness 100 4 - float orientation 104 4 - int pad 108 4 - -FreestyleLineStyle 288 - - ID id 0 120 - AnimData *adt 120 8 - float r 128 4 - float g 132 4 - float b 136 4 - float alpha 140 4 - float thickness 144 4 - int thickness_position 148 4 - float thickness_ratio 152 4 - int flag 156 4 - int caps 160 4 - int chaining 164 4 - int rounds 168 4 - float split_length 172 4 - float min_angle 176 4 - float max_angle 180 4 - float min_length 184 4 - float max_length 188 4 - short split_dash1 192 2 - short split_gap1 194 2 - short split_dash2 196 2 - short split_gap2 198 2 - short split_dash3 200 2 - short split_gap3 202 2 - int pad 204 4 - short dash1 208 2 - short gap1 210 2 - short dash2 212 2 - short gap2 214 2 - short dash3 216 2 - short gap3 218 2 - int panel 220 4 - ListBase color_modifiers 224 16 - ListBase alpha_modifiers 240 16 - ListBase thickness_modifiers 256 16 - ListBase geometry_modifiers 272 16 - From 24df52647f4432d0bb97be468799e31082dd4dde Mon Sep 17 00:00:00 2001 From: Krishty Date: Wed, 5 May 2021 23:05:44 +0200 Subject: [PATCH 073/335] merge and update all copies of stb_image.h --- code/AssetLib/M3D/M3DWrapper.h | 4 + code/Common/Assimp.cpp | 33 + code/Pbrt/PbrtExporter.cpp | 3 +- code/Pbrt/stb_image.h | 7756 -------------------------------- contrib/stb_image/stb_image.h | 652 ++- 5 files changed, 514 insertions(+), 7934 deletions(-) delete mode 100644 code/Pbrt/stb_image.h diff --git a/code/AssetLib/M3D/M3DWrapper.h b/code/AssetLib/M3D/M3DWrapper.h index 5c370e607..34bec49b2 100644 --- a/code/AssetLib/M3D/M3DWrapper.h +++ b/code/AssetLib/M3D/M3DWrapper.h @@ -56,6 +56,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //#define ASSIMP_USE_M3D_READFILECB //#define M3D_ASCII +// Share stb_image's PNG loader with other importers/exporters instead of bringing our own copy. +#define STBI_ONLY_PNG +#include + #include "m3d.h" namespace Assimp { diff --git a/code/Common/Assimp.cpp b/code/Common/Assimp.cpp index 4e2c7117c..6074b84bc 100644 --- a/code/Common/Assimp.cpp +++ b/code/Common/Assimp.cpp @@ -1251,3 +1251,36 @@ ASSIMP_API void aiQuaternionInterpolate( ai_assert(nullptr != end); aiQuaternion::Interpolate(*dst, *start, *end, factor); } + + +// stb_image is a lightweight image loader. It is shared by: +// - M3D import +// - PBRT export +// Since it's a header-only library, its implementation must be instantiated in some cpp file. +// Don't scatter this task over multiple importers/exporters. Maintain it in a central place (here!). + +#define ASSIMP_HAS_PBRT_EXPORT (!ASSIMP_BUILD_NO_EXPORT && !ASSIMP_BUILD_NO_PBRT_EXPORTER) +#define ASSIMP_HAS_M3D ((!ASSIMP_BUILD_NO_EXPORT && !ASSIMP_BUILD_NO_M3D_EXPORTER) || !ASSIMP_BUILD_NO_M3D_IMPORTER) + +#if ASSIMP_HAS_PBRT_EXPORT +# define ASSIMP_NEEDS_STB_IMAGE 1 +#elif ASSIMP_HAS_M3D +# define ASSIMP_NEEDS_STB_IMAGE 1 +# define STBI_ONLY_PNG +#endif + +#if ASSIMP_NEEDS_STB_IMAGE + +# if _MSC_VER // "unreferenced function has been removed" (SSE2 detection routine in x64 builds) +# pragma warning(push) +# pragma warning(disable: 4505) +# endif + +# define STB_IMAGE_IMPLEMENTATION +# include "../contrib/stb_image/stb_image.h" + +# if _MSC_VER +# pragma warning(pop) +# endif + +#endif diff --git a/code/Pbrt/PbrtExporter.cpp b/code/Pbrt/PbrtExporter.cpp index b793c37f9..35b108702 100644 --- a/code/Pbrt/PbrtExporter.cpp +++ b/code/Pbrt/PbrtExporter.cpp @@ -83,8 +83,7 @@ Other: #include #include -#define STB_IMAGE_IMPLEMENTATION -#include "stb_image.h" +#include "../contrib/stb_image/stb_image.h" using namespace Assimp; diff --git a/code/Pbrt/stb_image.h b/code/Pbrt/stb_image.h deleted file mode 100644 index 65a205f6e..000000000 --- a/code/Pbrt/stb_image.h +++ /dev/null @@ -1,7756 +0,0 @@ -/* stb_image - v2.26 - public domain image loader - http://nothings.org/stb - no warranty implied; use at your own risk - - Do this: - #define STB_IMAGE_IMPLEMENTATION - before you include this file in *one* C or C++ file to create the implementation. - - // i.e. it should look like this: - #include ... - #include ... - #include ... - #define STB_IMAGE_IMPLEMENTATION - #include "stb_image.h" - - You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. - And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free - - - QUICK NOTES: - Primarily of interest to game developers and other people who can - avoid problematic images and only need the trivial interface - - JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) - PNG 1/2/4/8/16-bit-per-channel - - TGA (not sure what subset, if a subset) - BMP non-1bpp, non-RLE - PSD (composited view only, no extra channels, 8/16 bit-per-channel) - - GIF (*comp always reports as 4-channel) - HDR (radiance rgbE format) - PIC (Softimage PIC) - PNM (PPM and PGM binary only) - - Animated GIF still needs a proper API, but here's one way to do it: - http://gist.github.com/urraka/685d9a6340b26b830d49 - - - decode from memory or through FILE (define STBI_NO_STDIO to remove code) - - decode from arbitrary I/O callbacks - - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) - - Full documentation under "DOCUMENTATION" below. - - -LICENSE - - See end of file for license information. - -RECENT REVISION HISTORY: - - 2.26 (2020-07-13) many minor fixes - 2.25 (2020-02-02) fix warnings - 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically - 2.23 (2019-08-11) fix clang static analysis warning - 2.22 (2019-03-04) gif fixes, fix warnings - 2.21 (2019-02-25) fix typo in comment - 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings - 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes - 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 - RGB-format JPEG; remove white matting in PSD; - allocate large structures on the stack; - correct channel count for PNG & BMP - 2.10 (2016-01-22) avoid warning introduced in 2.09 - 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED - - See end of file for full revision history. - - - ============================ Contributors ========================= - - Image formats Extensions, features - Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) - Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) - Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) - Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) - Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) - Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) - Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) - github:urraka (animated gif) Junggon Kim (PNM comments) - Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) - socks-the-fox (16-bit PNG) - Jeremy Sawicki (handle all ImageNet JPGs) - Optimizations & bugfixes Mikhail Morozov (1-bit BMP) - Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) - Arseny Kapoulkine - John-Mark Allen - Carmelo J Fdez-Aguera - - Bug & warning fixes - Marc LeBlanc David Woo Guillaume George Martins Mozeiko - Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski - Phil Jordan Dave Moore Roy Eltham - Hayaki Saito Nathan Reed Won Chun - Luke Graham Johan Duparc Nick Verigakis the Horde3D community - Thomas Ruf Ronny Chevalier github:rlyeh - Janez Zemva John Bartholomew Michal Cichon github:romigrou - Jonathan Blow Ken Hamada Tero Hanninen github:svdijk - Laurent Gomila Cort Stratton github:snagar - Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex - Cass Everitt Ryamond Barbiero github:grim210 - Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw - Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus - Josh Tobin Matthew Gregan github:poppolopoppo - Julian Raschke Gregory Mullen Christian Floisand github:darealshinji - Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 - Brad Weinberger Matvey Cherevko [reserved] - Luca Sas Alexander Veselov Zack Middleton [reserved] - Ryan C. Gordon [reserved] [reserved] - DO NOT ADD YOUR NAME HERE - - To add your name to the credits, pick a random blank space in the middle and fill it. - 80% of merge conflicts on stb PRs are due to people adding their name at the end - of the credits. -*/ - -#ifndef STBI_INCLUDE_STB_IMAGE_H -#define STBI_INCLUDE_STB_IMAGE_H - -// DOCUMENTATION -// -// Limitations: -// - no 12-bit-per-channel JPEG -// - no JPEGs with arithmetic coding -// - GIF always returns *comp=4 -// -// Basic usage (see HDR discussion below for HDR usage): -// int x,y,n; -// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); -// // ... process data if not NULL ... -// // ... x = width, y = height, n = # 8-bit components per pixel ... -// // ... replace '0' with '1'..'4' to force that many components per pixel -// // ... but 'n' will always be the number that it would have been if you said 0 -// stbi_image_free(data) -// -// Standard parameters: -// int *x -- outputs image width in pixels -// int *y -- outputs image height in pixels -// int *channels_in_file -- outputs # of image components in image file -// int desired_channels -- if non-zero, # of image components requested in result -// -// The return value from an image loader is an 'unsigned char *' which points -// to the pixel data, or NULL on an allocation failure or if the image is -// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, -// with each pixel consisting of N interleaved 8-bit components; the first -// pixel pointed to is top-left-most in the image. There is no padding between -// image scanlines or between pixels, regardless of format. The number of -// components N is 'desired_channels' if desired_channels is non-zero, or -// *channels_in_file otherwise. If desired_channels is non-zero, -// *channels_in_file has the number of components that _would_ have been -// output otherwise. E.g. if you set desired_channels to 4, you will always -// get RGBA output, but you can check *channels_in_file to see if it's trivially -// opaque because e.g. there were only 3 channels in the source image. -// -// An output image with N components has the following components interleaved -// in this order in each pixel: -// -// N=#comp components -// 1 grey -// 2 grey, alpha -// 3 red, green, blue -// 4 red, green, blue, alpha -// -// If image loading fails for any reason, the return value will be NULL, -// and *x, *y, *channels_in_file will be unchanged. The function -// stbi_failure_reason() can be queried for an extremely brief, end-user -// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS -// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly -// more user-friendly ones. -// -// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. -// -// =========================================================================== -// -// UNICODE: -// -// If compiling for Windows and you wish to use Unicode filenames, compile -// with -// #define STBI_WINDOWS_UTF8 -// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert -// Windows wchar_t filenames to utf8. -// -// =========================================================================== -// -// Philosophy -// -// stb libraries are designed with the following priorities: -// -// 1. easy to use -// 2. easy to maintain -// 3. good performance -// -// Sometimes I let "good performance" creep up in priority over "easy to maintain", -// and for best performance I may provide less-easy-to-use APIs that give higher -// performance, in addition to the easy-to-use ones. Nevertheless, it's important -// to keep in mind that from the standpoint of you, a client of this library, -// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. -// -// Some secondary priorities arise directly from the first two, some of which -// provide more explicit reasons why performance can't be emphasized. -// -// - Portable ("ease of use") -// - Small source code footprint ("easy to maintain") -// - No dependencies ("ease of use") -// -// =========================================================================== -// -// I/O callbacks -// -// I/O callbacks allow you to read from arbitrary sources, like packaged -// files or some other source. Data read from callbacks are processed -// through a small internal buffer (currently 128 bytes) to try to reduce -// overhead. -// -// The three functions you must define are "read" (reads some bytes of data), -// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). -// -// =========================================================================== -// -// SIMD support -// -// The JPEG decoder will try to automatically use SIMD kernels on x86 when -// supported by the compiler. For ARM Neon support, you must explicitly -// request it. -// -// (The old do-it-yourself SIMD API is no longer supported in the current -// code.) -// -// On x86, SSE2 will automatically be used when available based on a run-time -// test; if not, the generic C versions are used as a fall-back. On ARM targets, -// the typical path is to have separate builds for NEON and non-NEON devices -// (at least this is true for iOS and Android). Therefore, the NEON support is -// toggled by a build flag: define STBI_NEON to get NEON loops. -// -// If for some reason you do not want to use any of SIMD code, or if -// you have issues compiling it, you can disable it entirely by -// defining STBI_NO_SIMD. -// -// =========================================================================== -// -// HDR image support (disable by defining STBI_NO_HDR) -// -// stb_image supports loading HDR images in general, and currently the Radiance -// .HDR file format specifically. You can still load any file through the existing -// interface; if you attempt to load an HDR file, it will be automatically remapped -// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; -// both of these constants can be reconfigured through this interface: -// -// stbi_hdr_to_ldr_gamma(2.2f); -// stbi_hdr_to_ldr_scale(1.0f); -// -// (note, do not use _inverse_ constants; stbi_image will invert them -// appropriately). -// -// Additionally, there is a new, parallel interface for loading files as -// (linear) floats to preserve the full dynamic range: -// -// float *data = stbi_loadf(filename, &x, &y, &n, 0); -// -// If you load LDR images through this interface, those images will -// be promoted to floating point values, run through the inverse of -// constants corresponding to the above: -// -// stbi_ldr_to_hdr_scale(1.0f); -// stbi_ldr_to_hdr_gamma(2.2f); -// -// Finally, given a filename (or an open file or memory block--see header -// file for details) containing image data, you can query for the "most -// appropriate" interface to use (that is, whether the image is HDR or -// not), using: -// -// stbi_is_hdr(char *filename); -// -// =========================================================================== -// -// iPhone PNG support: -// -// By default we convert iphone-formatted PNGs back to RGB, even though -// they are internally encoded differently. You can disable this conversion -// by calling stbi_convert_iphone_png_to_rgb(0), in which case -// you will always just get the native iphone "format" through (which -// is BGR stored in RGB). -// -// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per -// pixel to remove any premultiplied alpha *only* if the image file explicitly -// says there's premultiplied data (currently only happens in iPhone images, -// and only if iPhone convert-to-rgb processing is on). -// -// =========================================================================== -// -// ADDITIONAL CONFIGURATION -// -// - You can suppress implementation of any of the decoders to reduce -// your code footprint by #defining one or more of the following -// symbols before creating the implementation. -// -// STBI_NO_JPEG -// STBI_NO_PNG -// STBI_NO_BMP -// STBI_NO_PSD -// STBI_NO_TGA -// STBI_NO_GIF -// STBI_NO_HDR -// STBI_NO_PIC -// STBI_NO_PNM (.ppm and .pgm) -// -// - You can request *only* certain decoders and suppress all other ones -// (this will be more forward-compatible, as addition of new decoders -// doesn't require you to disable them explicitly): -// -// STBI_ONLY_JPEG -// STBI_ONLY_PNG -// STBI_ONLY_BMP -// STBI_ONLY_PSD -// STBI_ONLY_TGA -// STBI_ONLY_GIF -// STBI_ONLY_HDR -// STBI_ONLY_PIC -// STBI_ONLY_PNM (.ppm and .pgm) -// -// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still -// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB -// -// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater -// than that size (in either width or height) without further processing. -// This is to let programs in the wild set an upper bound to prevent -// denial-of-service attacks on untrusted data, as one could generate a -// valid image of gigantic dimensions and force stb_image to allocate a -// huge block of memory and spend disproportionate time decoding it. By -// default this is set to (1 << 24), which is 16777216, but that's still -// very big. - -#ifndef STBI_NO_STDIO -#include -#endif // STBI_NO_STDIO - -#define STBI_VERSION 1 - -enum -{ - STBI_default = 0, // only used for desired_channels - - STBI_grey = 1, - STBI_grey_alpha = 2, - STBI_rgb = 3, - STBI_rgb_alpha = 4 -}; - -#include -typedef unsigned char stbi_uc; -typedef unsigned short stbi_us; - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef STBIDEF -#ifdef STB_IMAGE_STATIC -#define STBIDEF static -#else -#define STBIDEF extern -#endif -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// PRIMARY API - works on images of any type -// - -// -// load image by filename, open file, or memory buffer -// - -typedef struct -{ - int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read - void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative - int (*eof) (void *user); // returns nonzero if we are at end of file/data -} stbi_io_callbacks; - -//////////////////////////////////// -// -// 8-bits-per-channel interface -// - -STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); - -#ifndef STBI_NO_STDIO -STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); -// for stbi_load_from_file, file pointer is left pointing immediately after image -#endif - -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -#endif - -#ifdef STBI_WINDOWS_UTF8 -STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); -#endif - -//////////////////////////////////// -// -// 16-bits-per-channel interface -// - -STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); - -#ifndef STBI_NO_STDIO -STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); -#endif - -//////////////////////////////////// -// -// float-per-channel interface -// -#ifndef STBI_NO_LINEAR - STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); - - #ifndef STBI_NO_STDIO - STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); - #endif -#endif - -#ifndef STBI_NO_HDR - STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); - STBIDEF void stbi_hdr_to_ldr_scale(float scale); -#endif // STBI_NO_HDR - -#ifndef STBI_NO_LINEAR - STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); - STBIDEF void stbi_ldr_to_hdr_scale(float scale); -#endif // STBI_NO_LINEAR - -// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename); -STBIDEF int stbi_is_hdr_from_file(FILE *f); -#endif // STBI_NO_STDIO - - -// get a VERY brief reason for failure -// on most compilers (and ALL modern mainstream compilers) this is threadsafe -STBIDEF const char *stbi_failure_reason (void); - -// free the loaded image -- this is just free() -STBIDEF void stbi_image_free (void *retval_from_stbi_load); - -// get image dimensions & components without fully decoding -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); -STBIDEF int stbi_is_16_bit (char const *filename); -STBIDEF int stbi_is_16_bit_from_file(FILE *f); -#endif - - - -// for image formats that explicitly notate that they have premultiplied alpha, -// we just return the colors as stored in the file. set this flag to force -// unpremultiplication. results are undefined if the unpremultiply overflow. -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); - -// indicate whether we should process iphone images back to canonical format, -// or just pass them through "as-is" -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); - -// flip the image vertically, so the first pixel in the output array is the bottom left -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); - -// as above, but only applies to images loaded on the thread that calls the function -// this function is only available if your compiler supports thread-local variables; -// calling it will fail to link if your compiler doesn't -STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); - -// ZLIB client - used by PNG, available for other purposes - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); -STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - -STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - - -#ifdef __cplusplus -} -#endif - -// -// -//// end header file ///////////////////////////////////////////////////// -#endif // STBI_INCLUDE_STB_IMAGE_H - -#ifdef STB_IMAGE_IMPLEMENTATION - -#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ - || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ - || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ - || defined(STBI_ONLY_ZLIB) - #ifndef STBI_ONLY_JPEG - #define STBI_NO_JPEG - #endif - #ifndef STBI_ONLY_PNG - #define STBI_NO_PNG - #endif - #ifndef STBI_ONLY_BMP - #define STBI_NO_BMP - #endif - #ifndef STBI_ONLY_PSD - #define STBI_NO_PSD - #endif - #ifndef STBI_ONLY_TGA - #define STBI_NO_TGA - #endif - #ifndef STBI_ONLY_GIF - #define STBI_NO_GIF - #endif - #ifndef STBI_ONLY_HDR - #define STBI_NO_HDR - #endif - #ifndef STBI_ONLY_PIC - #define STBI_NO_PIC - #endif - #ifndef STBI_ONLY_PNM - #define STBI_NO_PNM - #endif -#endif - -#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) -#define STBI_NO_ZLIB -#endif - - -#include -#include // ptrdiff_t on osx -#include -#include -#include - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -#include // ldexp, pow -#endif - -#ifndef STBI_NO_STDIO -#include -#endif - -#ifndef STBI_ASSERT -#include -#define STBI_ASSERT(x) assert(x) -#endif - -#ifdef __cplusplus -#define STBI_EXTERN extern "C" -#else -#define STBI_EXTERN extern -#endif - - -#ifndef _MSC_VER - #ifdef __cplusplus - #define stbi_inline inline - #else - #define stbi_inline - #endif -#else - #define stbi_inline __forceinline -#endif - -#ifndef STBI_NO_THREAD_LOCALS - #if defined(__cplusplus) && __cplusplus >= 201103L - #define STBI_THREAD_LOCAL thread_local - #elif defined(__GNUC__) && __GNUC__ < 5 - #define STBI_THREAD_LOCAL __thread - #elif defined(_MSC_VER) - #define STBI_THREAD_LOCAL __declspec(thread) - #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) - #define STBI_THREAD_LOCAL _Thread_local - #endif - - #ifndef STBI_THREAD_LOCAL - #if defined(__GNUC__) - #define STBI_THREAD_LOCAL __thread - #endif - #endif -#endif - -#ifdef _MSC_VER -typedef unsigned short stbi__uint16; -typedef signed short stbi__int16; -typedef unsigned int stbi__uint32; -typedef signed int stbi__int32; -#else -#include -typedef uint16_t stbi__uint16; -typedef int16_t stbi__int16; -typedef uint32_t stbi__uint32; -typedef int32_t stbi__int32; -#endif - -// should produce compiler error if size is wrong -typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; - -#ifdef _MSC_VER -#define STBI_NOTUSED(v) (void)(v) -#else -#define STBI_NOTUSED(v) (void)sizeof(v) -#endif - -#ifdef _MSC_VER -#define STBI_HAS_LROTL -#endif - -#ifdef STBI_HAS_LROTL - #define stbi_lrot(x,y) _lrotl(x,y) -#else - #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) -#endif - -#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) -// ok -#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) -// ok -#else -#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." -#endif - -#ifndef STBI_MALLOC -#define STBI_MALLOC(sz) malloc(sz) -#define STBI_REALLOC(p,newsz) realloc(p,newsz) -#define STBI_FREE(p) free(p) -#endif - -#ifndef STBI_REALLOC_SIZED -#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) -#endif - -// x86/x64 detection -#if defined(__x86_64__) || defined(_M_X64) -#define STBI__X64_TARGET -#elif defined(__i386) || defined(_M_IX86) -#define STBI__X86_TARGET -#endif - -#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) -// gcc doesn't support sse2 intrinsics unless you compile with -msse2, -// which in turn means it gets to use SSE2 everywhere. This is unfortunate, -// but previous attempts to provide the SSE2 functions with runtime -// detection caused numerous issues. The way architecture extensions are -// exposed in GCC/Clang is, sadly, not really suited for one-file libs. -// New behavior: if compiled with -msse2, we use SSE2 without any -// detection; if not, we don't use it at all. -#define STBI_NO_SIMD -#endif - -#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) -// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET -// -// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the -// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. -// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not -// simultaneously enabling "-mstackrealign". -// -// See https://github.com/nothings/stb/issues/81 for more information. -// -// So default to no SSE2 on 32-bit MinGW. If you've read this far and added -// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. -#define STBI_NO_SIMD -#endif - -#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) -#define STBI_SSE2 -#include - -#ifdef _MSC_VER - -#if _MSC_VER >= 1400 // not VC6 -#include // __cpuid -static int stbi__cpuid3(void) -{ - int info[4]; - __cpuid(info,1); - return info[3]; -} -#else -static int stbi__cpuid3(void) -{ - int res; - __asm { - mov eax,1 - cpuid - mov res,edx - } - return res; -} -#endif - -#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name - -#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) -static int stbi__sse2_available(void) -{ - int info3 = stbi__cpuid3(); - return ((info3 >> 26) & 1) != 0; -} -#endif - -#else // assume GCC-style if not VC++ -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) - -#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) -static int stbi__sse2_available(void) -{ - // If we're even attempting to compile this on GCC/Clang, that means - // -msse2 is on, which means the compiler is allowed to use SSE2 - // instructions at will, and so are we. - return 1; -} -#endif - -#endif -#endif - -// ARM NEON -#if defined(STBI_NO_SIMD) && defined(STBI_NEON) -#undef STBI_NEON -#endif - -#ifdef STBI_NEON -#include -// assume GCC or Clang on ARM targets -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) -#endif - -#ifndef STBI_SIMD_ALIGN -#define STBI_SIMD_ALIGN(type, name) type name -#endif - -#ifndef STBI_MAX_DIMENSIONS -#define STBI_MAX_DIMENSIONS (1 << 24) -#endif - -/////////////////////////////////////////////// -// -// stbi__context struct and start_xxx functions - -// stbi__context structure is our basic context used by all images, so it -// contains all the IO context, plus some basic image information -typedef struct -{ - stbi__uint32 img_x, img_y; - int img_n, img_out_n; - - stbi_io_callbacks io; - void *io_user_data; - - int read_from_callbacks; - int buflen; - stbi_uc buffer_start[128]; - int callback_already_read; - - stbi_uc *img_buffer, *img_buffer_end; - stbi_uc *img_buffer_original, *img_buffer_original_end; -} stbi__context; - - -static void stbi__refill_buffer(stbi__context *s); - -// initialize a memory-decode context -static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) -{ - s->io.read = NULL; - s->read_from_callbacks = 0; - s->callback_already_read = 0; - s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; - s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; -} - -// initialize a callback-based context -static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) -{ - s->io = *c; - s->io_user_data = user; - s->buflen = sizeof(s->buffer_start); - s->read_from_callbacks = 1; - s->callback_already_read = 0; - s->img_buffer = s->img_buffer_original = s->buffer_start; - stbi__refill_buffer(s); - s->img_buffer_original_end = s->img_buffer_end; -} - -#ifndef STBI_NO_STDIO - -static int stbi__stdio_read(void *user, char *data, int size) -{ - return (int) fread(data,1,size,(FILE*) user); -} - -static void stbi__stdio_skip(void *user, int n) -{ - int ch; - fseek((FILE*) user, n, SEEK_CUR); - ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ - if (ch != EOF) { - ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ - } -} - -static int stbi__stdio_eof(void *user) -{ - return feof((FILE*) user) || ferror((FILE *) user); -} - -static stbi_io_callbacks stbi__stdio_callbacks = -{ - stbi__stdio_read, - stbi__stdio_skip, - stbi__stdio_eof, -}; - -static void stbi__start_file(stbi__context *s, FILE *f) -{ - stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); -} - -//static void stop_file(stbi__context *s) { } - -#endif // !STBI_NO_STDIO - -static void stbi__rewind(stbi__context *s) -{ - // conceptually rewind SHOULD rewind to the beginning of the stream, - // but we just rewind to the beginning of the initial buffer, because - // we only use it after doing 'test', which only ever looks at at most 92 bytes - s->img_buffer = s->img_buffer_original; - s->img_buffer_end = s->img_buffer_original_end; -} - -enum -{ - STBI_ORDER_RGB, - STBI_ORDER_BGR -}; - -typedef struct -{ - int bits_per_channel; - int num_channels; - int channel_order; -} stbi__result_info; - -#ifndef STBI_NO_JPEG -static int stbi__jpeg_test(stbi__context *s); -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNG -static int stbi__png_test(stbi__context *s); -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__png_is16(stbi__context *s); -#endif - -#ifndef STBI_NO_BMP -static int stbi__bmp_test(stbi__context *s); -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_TGA -static int stbi__tga_test(stbi__context *s); -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s); -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__psd_is16(stbi__context *s); -#endif - -#ifndef STBI_NO_HDR -static int stbi__hdr_test(stbi__context *s); -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_test(stbi__context *s); -static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_GIF -static int stbi__gif_test(stbi__context *s); -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNM -static int stbi__pnm_test(stbi__context *s); -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -static -#ifdef STBI_THREAD_LOCAL -STBI_THREAD_LOCAL -#endif -const char *stbi__g_failure_reason; - -STBIDEF const char *stbi_failure_reason(void) -{ - return stbi__g_failure_reason; -} - -#ifndef STBI_NO_FAILURE_STRINGS -static int stbi__err(const char *str) -{ - stbi__g_failure_reason = str; - return 0; -} -#endif - -static void *stbi__malloc(size_t size) -{ - return STBI_MALLOC(size); -} - -// stb_image uses ints pervasively, including for offset calculations. -// therefore the largest decoded image size we can support with the -// current code, even on 64-bit targets, is INT_MAX. this is not a -// significant limitation for the intended use case. -// -// we do, however, need to make sure our size calculations don't -// overflow. hence a few helper functions for size calculations that -// multiply integers together, making sure that they're non-negative -// and no overflow occurs. - -// return 1 if the sum is valid, 0 on overflow. -// negative terms are considered invalid. -static int stbi__addsizes_valid(int a, int b) -{ - if (b < 0) return 0; - // now 0 <= b <= INT_MAX, hence also - // 0 <= INT_MAX - b <= INTMAX. - // And "a + b <= INT_MAX" (which might overflow) is the - // same as a <= INT_MAX - b (no overflow) - return a <= INT_MAX - b; -} - -// returns 1 if the product is valid, 0 on overflow. -// negative factors are considered invalid. -static int stbi__mul2sizes_valid(int a, int b) -{ - if (a < 0 || b < 0) return 0; - if (b == 0) return 1; // mul-by-0 is always safe - // portable way to check for no overflows in a*b - return a <= INT_MAX/b; -} - -#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) -// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow -static int stbi__mad2sizes_valid(int a, int b, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); -} -#endif - -// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow -static int stbi__mad3sizes_valid(int a, int b, int c, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__addsizes_valid(a*b*c, add); -} - -// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); -} -#endif - -#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) -// mallocs with size overflow checking -static void *stbi__malloc_mad2(int a, int b, int add) -{ - if (!stbi__mad2sizes_valid(a, b, add)) return NULL; - return stbi__malloc(a*b + add); -} -#endif - -static void *stbi__malloc_mad3(int a, int b, int c, int add) -{ - if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; - return stbi__malloc(a*b*c + add); -} - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) -{ - if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; - return stbi__malloc(a*b*c*d + add); -} -#endif - -// stbi__err - error -// stbi__errpf - error returning pointer to float -// stbi__errpuc - error returning pointer to unsigned char - -#ifdef STBI_NO_FAILURE_STRINGS - #define stbi__err(x,y) 0 -#elif defined(STBI_FAILURE_USERMSG) - #define stbi__err(x,y) stbi__err(y) -#else - #define stbi__err(x,y) stbi__err(x) -#endif - -#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) -#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) - -STBIDEF void stbi_image_free(void *retval_from_stbi_load) -{ - STBI_FREE(retval_from_stbi_load); -} - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); -#endif - -#ifndef STBI_NO_HDR -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); -#endif - -static int stbi__vertically_flip_on_load_global = 0; - -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load_global = flag_true_if_should_flip; -} - -#ifndef STBI_THREAD_LOCAL -#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global -#else -static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; - -STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load_local = flag_true_if_should_flip; - stbi__vertically_flip_on_load_set = 1; -} - -#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ - ? stbi__vertically_flip_on_load_local \ - : stbi__vertically_flip_on_load_global) -#endif // STBI_THREAD_LOCAL - -static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields - ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed - ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order - ri->num_channels = 0; - - #ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PNG - if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_BMP - if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_GIF - if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PSD - if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); - #else - STBI_NOTUSED(bpc); - #endif - #ifndef STBI_NO_PIC - if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PNM - if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); - return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); - } - #endif - - #ifndef STBI_NO_TGA - // test tga last because it's a crappy test! - if (stbi__tga_test(s)) - return stbi__tga_load(s,x,y,comp,req_comp, ri); - #endif - - return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); -} - -static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi_uc *reduced; - - reduced = (stbi_uc *) stbi__malloc(img_len); - if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) - reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling - - STBI_FREE(orig); - return reduced; -} - -static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi__uint16 *enlarged; - - enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); - if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) - enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff - - STBI_FREE(orig); - return enlarged; -} - -static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) -{ - int row; - size_t bytes_per_row = (size_t)w * bytes_per_pixel; - stbi_uc temp[2048]; - stbi_uc *bytes = (stbi_uc *)image; - - for (row = 0; row < (h>>1); row++) { - stbi_uc *row0 = bytes + row*bytes_per_row; - stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; - // swap row0 with row1 - size_t bytes_left = bytes_per_row; - while (bytes_left) { - size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); - memcpy(temp, row0, bytes_copy); - memcpy(row0, row1, bytes_copy); - memcpy(row1, temp, bytes_copy); - row0 += bytes_copy; - row1 += bytes_copy; - bytes_left -= bytes_copy; - } - } -} - -#ifndef STBI_NO_GIF -static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) -{ - int slice; - int slice_size = w * h * bytes_per_pixel; - - stbi_uc *bytes = (stbi_uc *)image; - for (slice = 0; slice < z; ++slice) { - stbi__vertical_flip(bytes, w, h, bytes_per_pixel); - bytes += slice_size; - } -} -#endif - -static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); - - if (result == NULL) - return NULL; - - // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. - STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); - - if (ri.bits_per_channel != 8) { - result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 8; - } - - // @TODO: move stbi__convert_format to here - - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); - } - - return (unsigned char *) result; -} - -static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); - - if (result == NULL) - return NULL; - - // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. - STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); - - if (ri.bits_per_channel != 16) { - result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 16; - } - - // @TODO: move stbi__convert_format16 to here - // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision - - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); - } - - return (stbi__uint16 *) result; -} - -#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) -static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) -{ - if (stbi__vertically_flip_on_load && result != NULL) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); - } -} -#endif - -#ifndef STBI_NO_STDIO - -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) -STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); -STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); -#endif - -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) -STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) -{ - return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); -} -#endif - -static FILE *stbi__fopen(char const *filename, char const *mode) -{ - FILE *f; -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) - wchar_t wMode[64]; - wchar_t wFilename[1024]; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) - return 0; - - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) - return 0; - -#if _MSC_VER >= 1400 - if (0 != _wfopen_s(&f, wFilename, wMode)) - f = 0; -#else - f = _wfopen(wFilename, wMode); -#endif - -#elif defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != fopen_s(&f, filename, mode)) - f=0; -#else - f = fopen(filename, mode); -#endif - return f; -} - - -STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - unsigned char *result; - if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} - -STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__uint16 *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} - -STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - stbi__uint16 *result; - if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file_16(f,x,y,comp,req_comp); - fclose(f); - return result; -} - - -#endif //!STBI_NO_STDIO - -STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); -} - -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); -} - -STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); -} - -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_mem(&s,buffer,len); - - result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); - if (stbi__vertically_flip_on_load) { - stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); - } - - return result; -} -#endif - -#ifndef STBI_NO_LINEAR -static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *data; - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - stbi__result_info ri; - float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); - if (hdr_data) - stbi__float_postprocess(hdr_data,x,y,comp,req_comp); - return hdr_data; - } - #endif - data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); - if (data) - return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); - return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); -} - -STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_STDIO -STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - float *result; - FILE *f = stbi__fopen(filename, "rb"); - if (!f) return stbi__errpf("can't fopen", "Unable to open file"); - result = stbi_loadf_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_file(&s,f); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} -#endif // !STBI_NO_STDIO - -#endif // !STBI_NO_LINEAR - -// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is -// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always -// reports false! - -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(buffer); - STBI_NOTUSED(len); - return 0; - #endif -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result=0; - if (f) { - result = stbi_is_hdr_from_file(f); - fclose(f); - } - return result; -} - -STBIDEF int stbi_is_hdr_from_file(FILE *f) -{ - #ifndef STBI_NO_HDR - long pos = ftell(f); - int res; - stbi__context s; - stbi__start_file(&s,f); - res = stbi__hdr_test(&s); - fseek(f, pos, SEEK_SET); - return res; - #else - STBI_NOTUSED(f); - return 0; - #endif -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(clbk); - STBI_NOTUSED(user); - return 0; - #endif -} - -#ifndef STBI_NO_LINEAR -static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; - -STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } -STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } -#endif - -static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; - -STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } -STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } - - -////////////////////////////////////////////////////////////////////////////// -// -// Common code used by all image loaders -// - -enum -{ - STBI__SCAN_load=0, - STBI__SCAN_type, - STBI__SCAN_header -}; - -static void stbi__refill_buffer(stbi__context *s) -{ - int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); - s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); - if (n == 0) { - // at end of file, treat same as if from memory, but need to handle case - // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file - s->read_from_callbacks = 0; - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start+1; - *s->img_buffer = 0; - } else { - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start + n; - } -} - -stbi_inline static stbi_uc stbi__get8(stbi__context *s) -{ - if (s->img_buffer < s->img_buffer_end) - return *s->img_buffer++; - if (s->read_from_callbacks) { - stbi__refill_buffer(s); - return *s->img_buffer++; - } - return 0; -} - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else -stbi_inline static int stbi__at_eof(stbi__context *s) -{ - if (s->io.read) { - if (!(s->io.eof)(s->io_user_data)) return 0; - // if feof() is true, check if buffer = end - // special case: we've only got the special 0 character at the end - if (s->read_from_callbacks == 0) return 1; - } - - return s->img_buffer >= s->img_buffer_end; -} -#endif - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) -// nothing -#else -static void stbi__skip(stbi__context *s, int n) -{ - if (n == 0) return; // already there! - if (n < 0) { - s->img_buffer = s->img_buffer_end; - return; - } - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - s->img_buffer = s->img_buffer_end; - (s->io.skip)(s->io_user_data, n - blen); - return; - } - } - s->img_buffer += n; -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) -// nothing -#else -static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) -{ - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - int res, count; - - memcpy(buffer, s->img_buffer, blen); - - count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); - res = (count == (n-blen)); - s->img_buffer = s->img_buffer_end; - return res; - } - } - - if (s->img_buffer+n <= s->img_buffer_end) { - memcpy(buffer, s->img_buffer, n); - s->img_buffer += n; - return 1; - } else - return 0; -} -#endif - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) -// nothing -#else -static int stbi__get16be(stbi__context *s) -{ - int z = stbi__get8(s); - return (z << 8) + stbi__get8(s); -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) -// nothing -#else -static stbi__uint32 stbi__get32be(stbi__context *s) -{ - stbi__uint32 z = stbi__get16be(s); - return (z << 16) + stbi__get16be(s); -} -#endif - -#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) -// nothing -#else -static int stbi__get16le(stbi__context *s) -{ - int z = stbi__get8(s); - return z + (stbi__get8(s) << 8); -} -#endif - -#ifndef STBI_NO_BMP -static stbi__uint32 stbi__get32le(stbi__context *s) -{ - stbi__uint32 z = stbi__get16le(s); - return z + (stbi__get16le(s) << 16); -} -#endif - -#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else -////////////////////////////////////////////////////////////////////////////// -// -// generic converter from built-in img_n to req_comp -// individual types do this automatically as much as possible (e.g. jpeg -// does all cases internally since it needs to colorspace convert anyway, -// and it never has alpha, so very few cases ). png can automatically -// interleave an alpha=255 channel, but falls back to this for other cases -// -// assume data buffer is malloced, so malloc a new one and free that one -// only failure mode is malloc failing - -static stbi_uc stbi__compute_y(int r, int g, int b) -{ - return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else -static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - unsigned char *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); - if (good == NULL) { - STBI_FREE(data); - return stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - unsigned char *src = data + j * x * img_n ; - unsigned char *dest = good + j * x * req_comp; - - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; - default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); - } - #undef STBI__CASE - } - - STBI_FREE(data); - return good; -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) -// nothing -#else -static stbi__uint16 stbi__compute_y_16(int r, int g, int b) -{ - return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) -// nothing -#else -static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - stbi__uint16 *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); - if (good == NULL) { - STBI_FREE(data); - return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - stbi__uint16 *src = data + j * x * img_n ; - stbi__uint16 *dest = good + j * x * req_comp; - - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; - default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); - } - #undef STBI__CASE - } - - STBI_FREE(data); - return good; -} -#endif - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) -{ - int i,k,n; - float *output; - if (!data) return NULL; - output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); - if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); - } - } - if (n < comp) { - for (i=0; i < x*y; ++i) { - output[i*comp + n] = data[i*comp + n]/255.0f; - } - } - STBI_FREE(data); - return output; -} -#endif - -#ifndef STBI_NO_HDR -#define stbi__float2int(x) ((int) (x)) -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) -{ - int i,k,n; - stbi_uc *output; - if (!data) return NULL; - output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); - if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - if (k < comp) { - float z = data[i*comp+k] * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - } - STBI_FREE(data); - return output; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// "baseline" JPEG/JFIF decoder -// -// simple implementation -// - doesn't support delayed output of y-dimension -// - simple interface (only one output format: 8-bit interleaved RGB) -// - doesn't try to recover corrupt jpegs -// - doesn't allow partial loading, loading multiple at once -// - still fast on x86 (copying globals into locals doesn't help x86) -// - allocates lots of intermediate memory (full size of all components) -// - non-interleaved case requires this anyway -// - allows good upsampling (see next) -// high-quality -// - upsampled channels are bilinearly interpolated, even across blocks -// - quality integer IDCT derived from IJG's 'slow' -// performance -// - fast huffman; reasonable integer IDCT -// - some SIMD kernels for common paths on targets with SSE2/NEON -// - uses a lot of intermediate memory, could cache poorly - -#ifndef STBI_NO_JPEG - -// huffman decoding acceleration -#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache - -typedef struct -{ - stbi_uc fast[1 << FAST_BITS]; - // weirdly, repacking this into AoS is a 10% speed loss, instead of a win - stbi__uint16 code[256]; - stbi_uc values[256]; - stbi_uc size[257]; - unsigned int maxcode[18]; - int delta[17]; // old 'firstsymbol' - old 'firstcode' -} stbi__huffman; - -typedef struct -{ - stbi__context *s; - stbi__huffman huff_dc[4]; - stbi__huffman huff_ac[4]; - stbi__uint16 dequant[4][64]; - stbi__int16 fast_ac[4][1 << FAST_BITS]; - -// sizes for components, interleaved MCUs - int img_h_max, img_v_max; - int img_mcu_x, img_mcu_y; - int img_mcu_w, img_mcu_h; - -// definition of jpeg image component - struct - { - int id; - int h,v; - int tq; - int hd,ha; - int dc_pred; - - int x,y,w2,h2; - stbi_uc *data; - void *raw_data, *raw_coeff; - stbi_uc *linebuf; - short *coeff; // progressive only - int coeff_w, coeff_h; // number of 8x8 coefficient blocks - } img_comp[4]; - - stbi__uint32 code_buffer; // jpeg entropy-coded buffer - int code_bits; // number of valid bits - unsigned char marker; // marker seen while filling entropy buffer - int nomore; // flag if we saw a marker so must stop - - int progressive; - int spec_start; - int spec_end; - int succ_high; - int succ_low; - int eob_run; - int jfif; - int app14_color_transform; // Adobe APP14 tag - int rgb; - - int scan_n, order[4]; - int restart_interval, todo; - -// kernels - void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); - void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); - stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); -} stbi__jpeg; - -static int stbi__build_huffman(stbi__huffman *h, int *count) -{ - int i,j,k=0; - unsigned int code; - // build size list for each symbol (from JPEG spec) - for (i=0; i < 16; ++i) - for (j=0; j < count[i]; ++j) - h->size[k++] = (stbi_uc) (i+1); - h->size[k] = 0; - - // compute actual symbols (from jpeg spec) - code = 0; - k = 0; - for(j=1; j <= 16; ++j) { - // compute delta to add to code to compute symbol id - h->delta[j] = k - code; - if (h->size[k] == j) { - while (h->size[k] == j) - h->code[k++] = (stbi__uint16) (code++); - if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); - } - // compute largest code + 1 for this size, preshifted as needed later - h->maxcode[j] = code << (16-j); - code <<= 1; - } - h->maxcode[j] = 0xffffffff; - - // build non-spec acceleration table; 255 is flag for not-accelerated - memset(h->fast, 255, 1 << FAST_BITS); - for (i=0; i < k; ++i) { - int s = h->size[i]; - if (s <= FAST_BITS) { - int c = h->code[i] << (FAST_BITS-s); - int m = 1 << (FAST_BITS-s); - for (j=0; j < m; ++j) { - h->fast[c+j] = (stbi_uc) i; - } - } - } - return 1; -} - -// build a table that decodes both magnitude and value of small ACs in -// one go. -static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) -{ - int i; - for (i=0; i < (1 << FAST_BITS); ++i) { - stbi_uc fast = h->fast[i]; - fast_ac[i] = 0; - if (fast < 255) { - int rs = h->values[fast]; - int run = (rs >> 4) & 15; - int magbits = rs & 15; - int len = h->size[fast]; - - if (magbits && len + magbits <= FAST_BITS) { - // magnitude code followed by receive_extend code - int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); - int m = 1 << (magbits - 1); - if (k < m) k += (~0U << magbits) + 1; - // if the result is small enough, we can fit it in fast_ac table - if (k >= -128 && k <= 127) - fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); - } - } - } -} - -static void stbi__grow_buffer_unsafe(stbi__jpeg *j) -{ - do { - unsigned int b = j->nomore ? 0 : stbi__get8(j->s); - if (b == 0xff) { - int c = stbi__get8(j->s); - while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes - if (c != 0) { - j->marker = (unsigned char) c; - j->nomore = 1; - return; - } - } - j->code_buffer |= b << (24 - j->code_bits); - j->code_bits += 8; - } while (j->code_bits <= 24); -} - -// (1 << n) - 1 -static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; - -// decode a jpeg huffman value from the bitstream -stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) -{ - unsigned int temp; - int c,k; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - // look at the top FAST_BITS and determine what symbol ID it is, - // if the code is <= FAST_BITS - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - k = h->fast[c]; - if (k < 255) { - int s = h->size[k]; - if (s > j->code_bits) - return -1; - j->code_buffer <<= s; - j->code_bits -= s; - return h->values[k]; - } - - // naive test is to shift the code_buffer down so k bits are - // valid, then test against maxcode. To speed this up, we've - // preshifted maxcode left so that it has (16-k) 0s at the - // end; in other words, regardless of the number of bits, it - // wants to be compared against something shifted to have 16; - // that way we don't need to shift inside the loop. - temp = j->code_buffer >> 16; - for (k=FAST_BITS+1 ; ; ++k) - if (temp < h->maxcode[k]) - break; - if (k == 17) { - // error! code not found - j->code_bits -= 16; - return -1; - } - - if (k > j->code_bits) - return -1; - - // convert the huffman code to the symbol id - c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; - STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); - - // convert the id to a symbol - j->code_bits -= k; - j->code_buffer <<= k; - return h->values[c]; -} - -// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); - - sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB - k = stbi_lrot(j->code_buffer, n); - if (n < 0 || n >= (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))) return 0; - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k + (stbi__jbias[n] & ~sgn); -} - -// get some unsigned bits -stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) -{ - unsigned int k; - if (j->code_bits < n) stbi__grow_buffer_unsafe(j); - k = stbi_lrot(j->code_buffer, n); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k; -} - -stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) -{ - unsigned int k; - if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); - k = j->code_buffer; - j->code_buffer <<= 1; - --j->code_bits; - return k & 0x80000000; -} - -// given a value that's at position X in the zigzag stream, -// where does it appear in the 8x8 matrix coded as row-major? -static const stbi_uc stbi__jpeg_dezigzag[64+15] = -{ - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63, - // let corrupt input sample past end - 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63 -}; - -// decode one 64-entry block-- -static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) -{ - int diff,dc,k; - int t; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - - // 0 all the ac values now so we can do it 32-bits at a time - memset(data,0,64*sizeof(data[0])); - - diff = t ? stbi__extend_receive(j, t) : 0; - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc * dequant[0]); - - // decode AC components, see JPEG spec - k = 1; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) * dequant[zig]); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (rs != 0xf0) break; // end block - k += 16; - } else { - k += r; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); - } - } - } while (k < 64); - return 1; -} - -static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) -{ - int diff,dc; - int t; - if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - if (j->succ_high == 0) { - // first scan for DC coefficient, must be first - memset(data,0,64*sizeof(data[0])); // 0 all the ac values now - t = stbi__jpeg_huff_decode(j, hdc); - if (t == -1) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - diff = t ? stbi__extend_receive(j, t) : 0; - - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc << j->succ_low); - } else { - // refinement scan for DC coefficient - if (stbi__jpeg_get_bit(j)) - data[0] += (short) (1 << j->succ_low); - } - return 1; -} - -// @OPTIMIZE: store non-zigzagged during the decode passes, -// and only de-zigzag when dequantizing -static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) -{ - int k; - if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->succ_high == 0) { - int shift = j->succ_low; - - if (j->eob_run) { - --j->eob_run; - return 1; - } - - k = j->spec_start; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) << shift); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r); - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - --j->eob_run; - break; - } - k += 16; - } else { - k += r; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) << shift); - } - } - } while (k <= j->spec_end); - } else { - // refinement scan for these AC coefficients - - short bit = (short) (1 << j->succ_low); - - if (j->eob_run) { - --j->eob_run; - for (k = j->spec_start; k <= j->spec_end; ++k) { - short *p = &data[stbi__jpeg_dezigzag[k]]; - if (*p != 0) - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } - } else { - k = j->spec_start; - do { - int r,s; - int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r) - 1; - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - r = 64; // force end of block - } else { - // r=15 s=0 should write 16 0s, so we just do - // a run of 15 0s and then write s (which is 0), - // so we don't have to do anything special here - } - } else { - if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); - // sign bit - if (stbi__jpeg_get_bit(j)) - s = bit; - else - s = -bit; - } - - // advance by r - while (k <= j->spec_end) { - short *p = &data[stbi__jpeg_dezigzag[k++]]; - if (*p != 0) { - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } else { - if (r == 0) { - *p = (short) s; - break; - } - --r; - } - } - } while (k <= j->spec_end); - } - } - return 1; -} - -// take a -128..127 value and stbi__clamp it and convert to 0..255 -stbi_inline static stbi_uc stbi__clamp(int x) -{ - // trick to use a single test to catch both cases - if ((unsigned int) x > 255) { - if (x < 0) return 0; - if (x > 255) return 255; - } - return (stbi_uc) x; -} - -#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) -#define stbi__fsh(x) ((x) * 4096) - -// derived from jidctint -- DCT_ISLOW -#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ - int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ - p2 = s2; \ - p3 = s6; \ - p1 = (p2+p3) * stbi__f2f(0.5411961f); \ - t2 = p1 + p3*stbi__f2f(-1.847759065f); \ - t3 = p1 + p2*stbi__f2f( 0.765366865f); \ - p2 = s0; \ - p3 = s4; \ - t0 = stbi__fsh(p2+p3); \ - t1 = stbi__fsh(p2-p3); \ - x0 = t0+t3; \ - x3 = t0-t3; \ - x1 = t1+t2; \ - x2 = t1-t2; \ - t0 = s7; \ - t1 = s5; \ - t2 = s3; \ - t3 = s1; \ - p3 = t0+t2; \ - p4 = t1+t3; \ - p1 = t0+t3; \ - p2 = t1+t2; \ - p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ - t0 = t0*stbi__f2f( 0.298631336f); \ - t1 = t1*stbi__f2f( 2.053119869f); \ - t2 = t2*stbi__f2f( 3.072711026f); \ - t3 = t3*stbi__f2f( 1.501321110f); \ - p1 = p5 + p1*stbi__f2f(-0.899976223f); \ - p2 = p5 + p2*stbi__f2f(-2.562915447f); \ - p3 = p3*stbi__f2f(-1.961570560f); \ - p4 = p4*stbi__f2f(-0.390180644f); \ - t3 += p1+p4; \ - t2 += p2+p3; \ - t1 += p2+p4; \ - t0 += p1+p3; - -static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) -{ - int i,val[64],*v=val; - stbi_uc *o; - short *d = data; - - // columns - for (i=0; i < 8; ++i,++d, ++v) { - // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing - if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 - && d[40]==0 && d[48]==0 && d[56]==0) { - // no shortcut 0 seconds - // (1|2|3|4|5|6|7)==0 0 seconds - // all separate -0.047 seconds - // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds - int dcterm = d[0]*4; - v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; - } else { - STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) - // constants scaled things up by 1<<12; let's bring them back - // down, but keep 2 extra bits of precision - x0 += 512; x1 += 512; x2 += 512; x3 += 512; - v[ 0] = (x0+t3) >> 10; - v[56] = (x0-t3) >> 10; - v[ 8] = (x1+t2) >> 10; - v[48] = (x1-t2) >> 10; - v[16] = (x2+t1) >> 10; - v[40] = (x2-t1) >> 10; - v[24] = (x3+t0) >> 10; - v[32] = (x3-t0) >> 10; - } - } - - for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { - // no fast case since the first 1D IDCT spread components out - STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) - // constants scaled things up by 1<<12, plus we had 1<<2 from first - // loop, plus horizontal and vertical each scale by sqrt(8) so together - // we've got an extra 1<<3, so 1<<17 total we need to remove. - // so we want to round that, which means adding 0.5 * 1<<17, - // aka 65536. Also, we'll end up with -128 to 127 that we want - // to encode as 0..255 by adding 128, so we'll add that before the shift - x0 += 65536 + (128<<17); - x1 += 65536 + (128<<17); - x2 += 65536 + (128<<17); - x3 += 65536 + (128<<17); - // tried computing the shifts into temps, or'ing the temps to see - // if any were out of range, but that was slower - o[0] = stbi__clamp((x0+t3) >> 17); - o[7] = stbi__clamp((x0-t3) >> 17); - o[1] = stbi__clamp((x1+t2) >> 17); - o[6] = stbi__clamp((x1-t2) >> 17); - o[2] = stbi__clamp((x2+t1) >> 17); - o[5] = stbi__clamp((x2-t1) >> 17); - o[3] = stbi__clamp((x3+t0) >> 17); - o[4] = stbi__clamp((x3-t0) >> 17); - } -} - -#ifdef STBI_SSE2 -// sse2 integer IDCT. not the fastest possible implementation but it -// produces bit-identical results to the generic C version so it's -// fully "transparent". -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - // This is constructed to match our regular (generic) integer IDCT exactly. - __m128i row0, row1, row2, row3, row4, row5, row6, row7; - __m128i tmp; - - // dot product constant: even elems=x, odd elems=y - #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) - - // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) - // out(1) = c1[even]*x + c1[odd]*y - #define dct_rot(out0,out1, x,y,c0,c1) \ - __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ - __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ - __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ - __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ - __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ - __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) - - // out = in << 12 (in 16-bit, out 32-bit) - #define dct_widen(out, in) \ - __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ - __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) - - // wide add - #define dct_wadd(out, a, b) \ - __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_add_epi32(a##_h, b##_h) - - // wide sub - #define dct_wsub(out, a, b) \ - __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) - - // butterfly a/b, add bias, then shift by "s" and pack - #define dct_bfly32o(out0, out1, a,b,bias,s) \ - { \ - __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ - __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ - dct_wadd(sum, abiased, b); \ - dct_wsub(dif, abiased, b); \ - out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ - out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ - } - - // 8-bit interleave step (for transposes) - #define dct_interleave8(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi8(a, b); \ - b = _mm_unpackhi_epi8(tmp, b) - - // 16-bit interleave step (for transposes) - #define dct_interleave16(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi16(a, b); \ - b = _mm_unpackhi_epi16(tmp, b) - - #define dct_pass(bias,shift) \ - { \ - /* even part */ \ - dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ - __m128i sum04 = _mm_add_epi16(row0, row4); \ - __m128i dif04 = _mm_sub_epi16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ - dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ - __m128i sum17 = _mm_add_epi16(row1, row7); \ - __m128i sum35 = _mm_add_epi16(row3, row5); \ - dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ - dct_wadd(x4, y0o, y4o); \ - dct_wadd(x5, y1o, y5o); \ - dct_wadd(x6, y2o, y5o); \ - dct_wadd(x7, y3o, y4o); \ - dct_bfly32o(row0,row7, x0,x7,bias,shift); \ - dct_bfly32o(row1,row6, x1,x6,bias,shift); \ - dct_bfly32o(row2,row5, x2,x5,bias,shift); \ - dct_bfly32o(row3,row4, x3,x4,bias,shift); \ - } - - __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); - __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); - __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); - __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); - __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); - __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); - __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); - __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); - - // rounding biases in column/row passes, see stbi__idct_block for explanation. - __m128i bias_0 = _mm_set1_epi32(512); - __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); - - // load - row0 = _mm_load_si128((const __m128i *) (data + 0*8)); - row1 = _mm_load_si128((const __m128i *) (data + 1*8)); - row2 = _mm_load_si128((const __m128i *) (data + 2*8)); - row3 = _mm_load_si128((const __m128i *) (data + 3*8)); - row4 = _mm_load_si128((const __m128i *) (data + 4*8)); - row5 = _mm_load_si128((const __m128i *) (data + 5*8)); - row6 = _mm_load_si128((const __m128i *) (data + 6*8)); - row7 = _mm_load_si128((const __m128i *) (data + 7*8)); - - // column pass - dct_pass(bias_0, 10); - - { - // 16bit 8x8 transpose pass 1 - dct_interleave16(row0, row4); - dct_interleave16(row1, row5); - dct_interleave16(row2, row6); - dct_interleave16(row3, row7); - - // transpose pass 2 - dct_interleave16(row0, row2); - dct_interleave16(row1, row3); - dct_interleave16(row4, row6); - dct_interleave16(row5, row7); - - // transpose pass 3 - dct_interleave16(row0, row1); - dct_interleave16(row2, row3); - dct_interleave16(row4, row5); - dct_interleave16(row6, row7); - } - - // row pass - dct_pass(bias_1, 17); - - { - // pack - __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 - __m128i p1 = _mm_packus_epi16(row2, row3); - __m128i p2 = _mm_packus_epi16(row4, row5); - __m128i p3 = _mm_packus_epi16(row6, row7); - - // 8bit 8x8 transpose pass 1 - dct_interleave8(p0, p2); // a0e0a1e1... - dct_interleave8(p1, p3); // c0g0c1g1... - - // transpose pass 2 - dct_interleave8(p0, p1); // a0c0e0g0... - dct_interleave8(p2, p3); // b0d0f0h0... - - // transpose pass 3 - dct_interleave8(p0, p2); // a0b0c0d0... - dct_interleave8(p1, p3); // a4b4c4d4... - - // store - _mm_storel_epi64((__m128i *) out, p0); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p2); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p1); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p3); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); - } - -#undef dct_const -#undef dct_rot -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_interleave8 -#undef dct_interleave16 -#undef dct_pass -} - -#endif // STBI_SSE2 - -#ifdef STBI_NEON - -// NEON integer IDCT. should produce bit-identical -// results to the generic C version. -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; - - int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); - int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); - int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); - int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); - int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); - int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); - int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); - int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); - int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); - int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); - int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); - int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); - -#define dct_long_mul(out, inq, coeff) \ - int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) - -#define dct_long_mac(out, acc, inq, coeff) \ - int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) - -#define dct_widen(out, inq) \ - int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ - int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) - -// wide add -#define dct_wadd(out, a, b) \ - int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vaddq_s32(a##_h, b##_h) - -// wide sub -#define dct_wsub(out, a, b) \ - int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vsubq_s32(a##_h, b##_h) - -// butterfly a/b, then shift using "shiftop" by "s" and pack -#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ - { \ - dct_wadd(sum, a, b); \ - dct_wsub(dif, a, b); \ - out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ - out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ - } - -#define dct_pass(shiftop, shift) \ - { \ - /* even part */ \ - int16x8_t sum26 = vaddq_s16(row2, row6); \ - dct_long_mul(p1e, sum26, rot0_0); \ - dct_long_mac(t2e, p1e, row6, rot0_1); \ - dct_long_mac(t3e, p1e, row2, rot0_2); \ - int16x8_t sum04 = vaddq_s16(row0, row4); \ - int16x8_t dif04 = vsubq_s16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - int16x8_t sum15 = vaddq_s16(row1, row5); \ - int16x8_t sum17 = vaddq_s16(row1, row7); \ - int16x8_t sum35 = vaddq_s16(row3, row5); \ - int16x8_t sum37 = vaddq_s16(row3, row7); \ - int16x8_t sumodd = vaddq_s16(sum17, sum35); \ - dct_long_mul(p5o, sumodd, rot1_0); \ - dct_long_mac(p1o, p5o, sum17, rot1_1); \ - dct_long_mac(p2o, p5o, sum35, rot1_2); \ - dct_long_mul(p3o, sum37, rot2_0); \ - dct_long_mul(p4o, sum15, rot2_1); \ - dct_wadd(sump13o, p1o, p3o); \ - dct_wadd(sump24o, p2o, p4o); \ - dct_wadd(sump23o, p2o, p3o); \ - dct_wadd(sump14o, p1o, p4o); \ - dct_long_mac(x4, sump13o, row7, rot3_0); \ - dct_long_mac(x5, sump24o, row5, rot3_1); \ - dct_long_mac(x6, sump23o, row3, rot3_2); \ - dct_long_mac(x7, sump14o, row1, rot3_3); \ - dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ - dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ - dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ - dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ - } - - // load - row0 = vld1q_s16(data + 0*8); - row1 = vld1q_s16(data + 1*8); - row2 = vld1q_s16(data + 2*8); - row3 = vld1q_s16(data + 3*8); - row4 = vld1q_s16(data + 4*8); - row5 = vld1q_s16(data + 5*8); - row6 = vld1q_s16(data + 6*8); - row7 = vld1q_s16(data + 7*8); - - // add DC bias - row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); - - // column pass - dct_pass(vrshrn_n_s32, 10); - - // 16bit 8x8 transpose - { -// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. -// whether compilers actually get this is another story, sadly. -#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } -#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } - - // pass 1 - dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 - dct_trn16(row2, row3); - dct_trn16(row4, row5); - dct_trn16(row6, row7); - - // pass 2 - dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 - dct_trn32(row1, row3); - dct_trn32(row4, row6); - dct_trn32(row5, row7); - - // pass 3 - dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 - dct_trn64(row1, row5); - dct_trn64(row2, row6); - dct_trn64(row3, row7); - -#undef dct_trn16 -#undef dct_trn32 -#undef dct_trn64 - } - - // row pass - // vrshrn_n_s32 only supports shifts up to 16, we need - // 17. so do a non-rounding shift of 16 first then follow - // up with a rounding shift by 1. - dct_pass(vshrn_n_s32, 16); - - { - // pack and round - uint8x8_t p0 = vqrshrun_n_s16(row0, 1); - uint8x8_t p1 = vqrshrun_n_s16(row1, 1); - uint8x8_t p2 = vqrshrun_n_s16(row2, 1); - uint8x8_t p3 = vqrshrun_n_s16(row3, 1); - uint8x8_t p4 = vqrshrun_n_s16(row4, 1); - uint8x8_t p5 = vqrshrun_n_s16(row5, 1); - uint8x8_t p6 = vqrshrun_n_s16(row6, 1); - uint8x8_t p7 = vqrshrun_n_s16(row7, 1); - - // again, these can translate into one instruction, but often don't. -#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } -#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } - - // sadly can't use interleaved stores here since we only write - // 8 bytes to each scan line! - - // 8x8 8-bit transpose pass 1 - dct_trn8_8(p0, p1); - dct_trn8_8(p2, p3); - dct_trn8_8(p4, p5); - dct_trn8_8(p6, p7); - - // pass 2 - dct_trn8_16(p0, p2); - dct_trn8_16(p1, p3); - dct_trn8_16(p4, p6); - dct_trn8_16(p5, p7); - - // pass 3 - dct_trn8_32(p0, p4); - dct_trn8_32(p1, p5); - dct_trn8_32(p2, p6); - dct_trn8_32(p3, p7); - - // store - vst1_u8(out, p0); out += out_stride; - vst1_u8(out, p1); out += out_stride; - vst1_u8(out, p2); out += out_stride; - vst1_u8(out, p3); out += out_stride; - vst1_u8(out, p4); out += out_stride; - vst1_u8(out, p5); out += out_stride; - vst1_u8(out, p6); out += out_stride; - vst1_u8(out, p7); - -#undef dct_trn8_8 -#undef dct_trn8_16 -#undef dct_trn8_32 - } - -#undef dct_long_mul -#undef dct_long_mac -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_pass -} - -#endif // STBI_NEON - -#define STBI__MARKER_none 0xff -// if there's a pending marker from the entropy stream, return that -// otherwise, fetch from the stream and get a marker. if there's no -// marker, return 0xff, which is never a valid marker value -static stbi_uc stbi__get_marker(stbi__jpeg *j) -{ - stbi_uc x; - if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } - x = stbi__get8(j->s); - if (x != 0xff) return STBI__MARKER_none; - while (x == 0xff) - x = stbi__get8(j->s); // consume repeated 0xff fill bytes - return x; -} - -// in each scan, we'll have scan_n components, and the order -// of the components is specified by order[] -#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) - -// after a restart interval, stbi__jpeg_reset the entropy decoder and -// the dc prediction -static void stbi__jpeg_reset(stbi__jpeg *j) -{ - j->code_bits = 0; - j->code_buffer = 0; - j->nomore = 0; - j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; - j->marker = STBI__MARKER_none; - j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; - j->eob_run = 0; - // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, - // since we don't even allow 1<<30 pixels -} - -static int stbi__parse_entropy_coded_data(stbi__jpeg *z) -{ - stbi__jpeg_reset(z); - if (!z->progressive) { - if (z->scan_n == 1) { - int i,j; - STBI_SIMD_ALIGN(short, data[64]); - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - // if it's NOT a restart, then just bail, so we get corrupt data - // rather than no data - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - STBI_SIMD_ALIGN(short, data[64]); - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x)*8; - int y2 = (j*z->img_comp[n].v + y)*8; - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } else { - if (z->scan_n == 1) { - int i,j; - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - if (z->spec_start == 0) { - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } else { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) - return 0; - } - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x); - int y2 = (j*z->img_comp[n].v + y); - short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } -} - -static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) -{ - int i; - for (i=0; i < 64; ++i) - data[i] *= dequant[i]; -} - -static void stbi__jpeg_finish(stbi__jpeg *z) -{ - if (z->progressive) { - // dequantize and idct the data - int i,j,n; - for (n=0; n < z->s->img_n; ++n) { - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - } - } - } - } -} - -static int stbi__process_marker(stbi__jpeg *z, int m) -{ - int L; - switch (m) { - case STBI__MARKER_none: // no marker found - return stbi__err("expected marker","Corrupt JPEG"); - - case 0xDD: // DRI - specify restart interval - if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); - z->restart_interval = stbi__get16be(z->s); - return 1; - - case 0xDB: // DQT - define quantization table - L = stbi__get16be(z->s)-2; - while (L > 0) { - int q = stbi__get8(z->s); - int p = q >> 4, sixteen = (p != 0); - int t = q & 15,i; - if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); - if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); - - for (i=0; i < 64; ++i) - z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); - L -= (sixteen ? 129 : 65); - } - return L==0; - - case 0xC4: // DHT - define huffman table - L = stbi__get16be(z->s)-2; - while (L > 0) { - stbi_uc *v; - int sizes[16],i,n=0; - int q = stbi__get8(z->s); - int tc = q >> 4; - int th = q & 15; - if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); - for (i=0; i < 16; ++i) { - sizes[i] = stbi__get8(z->s); - n += sizes[i]; - } - L -= 17; - if (tc == 0) { - if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; - v = z->huff_dc[th].values; - } else { - if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; - v = z->huff_ac[th].values; - } - for (i=0; i < n; ++i) - v[i] = stbi__get8(z->s); - if (tc != 0) - stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); - L -= n; - } - return L==0; - } - - // check for comment block or APP blocks - if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { - L = stbi__get16be(z->s); - if (L < 2) { - if (m == 0xFE) - return stbi__err("bad COM len","Corrupt JPEG"); - else - return stbi__err("bad APP len","Corrupt JPEG"); - } - L -= 2; - - if (m == 0xE0 && L >= 5) { // JFIF APP0 segment - static const unsigned char tag[5] = {'J','F','I','F','\0'}; - int ok = 1; - int i; - for (i=0; i < 5; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 5; - if (ok) - z->jfif = 1; - } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment - static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; - int ok = 1; - int i; - for (i=0; i < 6; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 6; - if (ok) { - stbi__get8(z->s); // version - stbi__get16be(z->s); // flags0 - stbi__get16be(z->s); // flags1 - z->app14_color_transform = stbi__get8(z->s); // color transform - L -= 6; - } - } - - stbi__skip(z->s, L); - return 1; - } - - return stbi__err("unknown marker","Corrupt JPEG"); -} - -// after we see SOS -static int stbi__process_scan_header(stbi__jpeg *z) -{ - int i; - int Ls = stbi__get16be(z->s); - z->scan_n = stbi__get8(z->s); - if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); - if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); - for (i=0; i < z->scan_n; ++i) { - int id = stbi__get8(z->s), which; - int q = stbi__get8(z->s); - for (which = 0; which < z->s->img_n; ++which) - if (z->img_comp[which].id == id) - break; - if (which == z->s->img_n) return 0; // no match - z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); - z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); - z->order[i] = which; - } - - { - int aa; - z->spec_start = stbi__get8(z->s); - z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 - aa = stbi__get8(z->s); - z->succ_high = (aa >> 4); - z->succ_low = (aa & 15); - if (z->progressive) { - if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) - return stbi__err("bad SOS", "Corrupt JPEG"); - } else { - if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); - if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); - z->spec_end = 63; - } - } - - return 1; -} - -static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) -{ - int i; - for (i=0; i < ncomp; ++i) { - if (z->img_comp[i].raw_data) { - STBI_FREE(z->img_comp[i].raw_data); - z->img_comp[i].raw_data = NULL; - z->img_comp[i].data = NULL; - } - if (z->img_comp[i].raw_coeff) { - STBI_FREE(z->img_comp[i].raw_coeff); - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].coeff = 0; - } - if (z->img_comp[i].linebuf) { - STBI_FREE(z->img_comp[i].linebuf); - z->img_comp[i].linebuf = NULL; - } - } - return why; -} - -static int stbi__process_frame_header(stbi__jpeg *z, int scan) -{ - stbi__context *s = z->s; - int Lf,p,i,q, h_max=1,v_max=1,c; - Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG - p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline - s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG - s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - c = stbi__get8(s); - if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); - s->img_n = c; - for (i=0; i < c; ++i) { - z->img_comp[i].data = NULL; - z->img_comp[i].linebuf = NULL; - } - - if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); - - z->rgb = 0; - for (i=0; i < s->img_n; ++i) { - static const unsigned char rgb[3] = { 'R', 'G', 'B' }; - z->img_comp[i].id = stbi__get8(s); - if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) - ++z->rgb; - q = stbi__get8(s); - z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); - z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); - z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); - } - - if (scan != STBI__SCAN_load) return 1; - - if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); - - for (i=0; i < s->img_n; ++i) { - if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; - if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; - } - - // compute interleaved mcu info - z->img_h_max = h_max; - z->img_v_max = v_max; - z->img_mcu_w = h_max * 8; - z->img_mcu_h = v_max * 8; - // these sizes can't be more than 17 bits - z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; - z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; - - for (i=0; i < s->img_n; ++i) { - // number of effective pixels (e.g. for non-interleaved MCU) - z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; - z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; - // to simplify generation, we'll allocate enough memory to decode - // the bogus oversized data from using interleaved MCUs and their - // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't - // discard the extra data until colorspace conversion - // - // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) - // so these muls can't overflow with 32-bit ints (which we require) - z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; - z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; - z->img_comp[i].coeff = 0; - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].linebuf = NULL; - z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); - if (z->img_comp[i].raw_data == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - // align blocks for idct using mmx/sse - z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); - if (z->progressive) { - // w2, h2 are multiples of 8 (see above) - z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; - z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; - z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); - if (z->img_comp[i].raw_coeff == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); - } - } - - return 1; -} - -// use comparisons since in some cases we handle more than one case (e.g. SOF) -#define stbi__DNL(x) ((x) == 0xdc) -#define stbi__SOI(x) ((x) == 0xd8) -#define stbi__EOI(x) ((x) == 0xd9) -#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) -#define stbi__SOS(x) ((x) == 0xda) - -#define stbi__SOF_progressive(x) ((x) == 0xc2) - -static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) -{ - int m; - z->jfif = 0; - z->app14_color_transform = -1; // valid values are 0,1,2 - z->marker = STBI__MARKER_none; // initialize cached marker to empty - m = stbi__get_marker(z); - if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); - if (scan == STBI__SCAN_type) return 1; - m = stbi__get_marker(z); - while (!stbi__SOF(m)) { - if (!stbi__process_marker(z,m)) return 0; - m = stbi__get_marker(z); - while (m == STBI__MARKER_none) { - // some files have extra padding after their blocks, so ok, we'll scan - if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); - m = stbi__get_marker(z); - } - } - z->progressive = stbi__SOF_progressive(m); - if (!stbi__process_frame_header(z, scan)) return 0; - return 1; -} - -// decode image to YCbCr format -static int stbi__decode_jpeg_image(stbi__jpeg *j) -{ - int m; - for (m = 0; m < 4; m++) { - j->img_comp[m].raw_data = NULL; - j->img_comp[m].raw_coeff = NULL; - } - j->restart_interval = 0; - if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; - m = stbi__get_marker(j); - while (!stbi__EOI(m)) { - if (stbi__SOS(m)) { - if (!stbi__process_scan_header(j)) return 0; - if (!stbi__parse_entropy_coded_data(j)) return 0; - if (j->marker == STBI__MARKER_none ) { - // handle 0s at the end of image data from IP Kamera 9060 - while (!stbi__at_eof(j->s)) { - int x = stbi__get8(j->s); - if (x == 255) { - j->marker = stbi__get8(j->s); - break; - } - } - // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 - } - } else if (stbi__DNL(m)) { - int Ld = stbi__get16be(j->s); - stbi__uint32 NL = stbi__get16be(j->s); - if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); - if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); - } else { - if (!stbi__process_marker(j, m)) return 0; - } - m = stbi__get_marker(j); - } - if (j->progressive) - stbi__jpeg_finish(j); - return 1; -} - -// static jfif-centered resampling (across block boundaries) - -typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, - int w, int hs); - -#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) - -static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - STBI_NOTUSED(out); - STBI_NOTUSED(in_far); - STBI_NOTUSED(w); - STBI_NOTUSED(hs); - return in_near; -} - -static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples vertically for every one in input - int i; - STBI_NOTUSED(hs); - for (i=0; i < w; ++i) - out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); - return out; -} - -static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples horizontally for every one in input - int i; - stbi_uc *input = in_near; - - if (w == 1) { - // if only one sample, can't do any interpolation - out[0] = out[1] = input[0]; - return out; - } - - out[0] = input[0]; - out[1] = stbi__div4(input[0]*3 + input[1] + 2); - for (i=1; i < w-1; ++i) { - int n = 3*input[i]+2; - out[i*2+0] = stbi__div4(n+input[i-1]); - out[i*2+1] = stbi__div4(n+input[i+1]); - } - out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); - out[i*2+1] = input[w-1]; - - STBI_NOTUSED(in_far); - STBI_NOTUSED(hs); - - return out; -} - -#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) - -static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i,t0,t1; - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - out[0] = stbi__div4(t1+2); - for (i=1; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i=0,t0,t1; - - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - // process groups of 8 pixels for as long as we can. - // note we can't handle the last pixel in a row in this loop - // because we need to handle the filter boundary conditions. - for (; i < ((w-1) & ~7); i += 8) { -#if defined(STBI_SSE2) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - __m128i zero = _mm_setzero_si128(); - __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); - __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); - __m128i farw = _mm_unpacklo_epi8(farb, zero); - __m128i nearw = _mm_unpacklo_epi8(nearb, zero); - __m128i diff = _mm_sub_epi16(farw, nearw); - __m128i nears = _mm_slli_epi16(nearw, 2); - __m128i curr = _mm_add_epi16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - __m128i prv0 = _mm_slli_si128(curr, 2); - __m128i nxt0 = _mm_srli_si128(curr, 2); - __m128i prev = _mm_insert_epi16(prv0, t1, 0); - __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - __m128i bias = _mm_set1_epi16(8); - __m128i curs = _mm_slli_epi16(curr, 2); - __m128i prvd = _mm_sub_epi16(prev, curr); - __m128i nxtd = _mm_sub_epi16(next, curr); - __m128i curb = _mm_add_epi16(curs, bias); - __m128i even = _mm_add_epi16(prvd, curb); - __m128i odd = _mm_add_epi16(nxtd, curb); - - // interleave even and odd pixels, then undo scaling. - __m128i int0 = _mm_unpacklo_epi16(even, odd); - __m128i int1 = _mm_unpackhi_epi16(even, odd); - __m128i de0 = _mm_srli_epi16(int0, 4); - __m128i de1 = _mm_srli_epi16(int1, 4); - - // pack and write output - __m128i outv = _mm_packus_epi16(de0, de1); - _mm_storeu_si128((__m128i *) (out + i*2), outv); -#elif defined(STBI_NEON) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - uint8x8_t farb = vld1_u8(in_far + i); - uint8x8_t nearb = vld1_u8(in_near + i); - int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); - int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); - int16x8_t curr = vaddq_s16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - int16x8_t prv0 = vextq_s16(curr, curr, 7); - int16x8_t nxt0 = vextq_s16(curr, curr, 1); - int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); - int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - int16x8_t curs = vshlq_n_s16(curr, 2); - int16x8_t prvd = vsubq_s16(prev, curr); - int16x8_t nxtd = vsubq_s16(next, curr); - int16x8_t even = vaddq_s16(curs, prvd); - int16x8_t odd = vaddq_s16(curs, nxtd); - - // undo scaling and round, then store with even/odd phases interleaved - uint8x8x2_t o; - o.val[0] = vqrshrun_n_s16(even, 4); - o.val[1] = vqrshrun_n_s16(odd, 4); - vst2_u8(out + i*2, o); -#endif - - // "previous" value for next iter - t1 = 3*in_near[i+7] + in_far[i+7]; - } - - t0 = t1; - t1 = 3*in_near[i] + in_far[i]; - out[i*2] = stbi__div16(3*t1 + t0 + 8); - - for (++i; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} -#endif - -static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // resample with nearest-neighbor - int i,j; - STBI_NOTUSED(in_far); - for (i=0; i < w; ++i) - for (j=0; j < hs; ++j) - out[i*hs+j] = in_near[i]; - return out; -} - -// this is a reduced-precision calculation of YCbCr-to-RGB introduced -// to make sure the code produces the same results in both SIMD and scalar -#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) -static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) -{ - int i; - for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) -{ - int i = 0; - -#ifdef STBI_SSE2 - // step == 3 is pretty ugly on the final interleave, and i'm not convinced - // it's useful in practice (you wouldn't use it for textures, for example). - // so just accelerate step == 4 case. - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - __m128i signflip = _mm_set1_epi8(-0x80); - __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); - __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); - __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); - __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); - __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); - __m128i xw = _mm_set1_epi16(255); // alpha channel - - for (; i+7 < count; i += 8) { - // load - __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); - __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); - __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); - __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 - __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 - - // unpack to short (and left-shift cr, cb by 8) - __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); - __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); - __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); - - // color transform - __m128i yws = _mm_srli_epi16(yw, 4); - __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); - __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); - __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); - __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); - __m128i rws = _mm_add_epi16(cr0, yws); - __m128i gwt = _mm_add_epi16(cb0, yws); - __m128i bws = _mm_add_epi16(yws, cb1); - __m128i gws = _mm_add_epi16(gwt, cr1); - - // descale - __m128i rw = _mm_srai_epi16(rws, 4); - __m128i bw = _mm_srai_epi16(bws, 4); - __m128i gw = _mm_srai_epi16(gws, 4); - - // back to byte, set up for transpose - __m128i brb = _mm_packus_epi16(rw, bw); - __m128i gxb = _mm_packus_epi16(gw, xw); - - // transpose to interleave channels - __m128i t0 = _mm_unpacklo_epi8(brb, gxb); - __m128i t1 = _mm_unpackhi_epi8(brb, gxb); - __m128i o0 = _mm_unpacklo_epi16(t0, t1); - __m128i o1 = _mm_unpackhi_epi16(t0, t1); - - // store - _mm_storeu_si128((__m128i *) (out + 0), o0); - _mm_storeu_si128((__m128i *) (out + 16), o1); - out += 32; - } - } -#endif - -#ifdef STBI_NEON - // in this version, step=3 support would be easy to add. but is there demand? - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - uint8x8_t signflip = vdup_n_u8(0x80); - int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); - int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); - int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); - int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); - - for (; i+7 < count; i += 8) { - // load - uint8x8_t y_bytes = vld1_u8(y + i); - uint8x8_t cr_bytes = vld1_u8(pcr + i); - uint8x8_t cb_bytes = vld1_u8(pcb + i); - int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); - int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); - - // expand to s16 - int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); - int16x8_t crw = vshll_n_s8(cr_biased, 7); - int16x8_t cbw = vshll_n_s8(cb_biased, 7); - - // color transform - int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); - int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); - int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); - int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); - int16x8_t rws = vaddq_s16(yws, cr0); - int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); - int16x8_t bws = vaddq_s16(yws, cb1); - - // undo scaling, round, convert to byte - uint8x8x4_t o; - o.val[0] = vqrshrun_n_s16(rws, 4); - o.val[1] = vqrshrun_n_s16(gws, 4); - o.val[2] = vqrshrun_n_s16(bws, 4); - o.val[3] = vdup_n_u8(255); - - // store, interleaving r/g/b/a - vst4_u8(out, o); - out += 8*4; - } - } -#endif - - for (; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} -#endif - -// set up the kernels -static void stbi__setup_jpeg(stbi__jpeg *j) -{ - j->idct_block_kernel = stbi__idct_block; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; - -#ifdef STBI_SSE2 - if (stbi__sse2_available()) { - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; - } -#endif - -#ifdef STBI_NEON - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; -#endif -} - -// clean up the temporary component buffers -static void stbi__cleanup_jpeg(stbi__jpeg *j) -{ - stbi__free_jpeg_components(j, j->s->img_n, 0); -} - -typedef struct -{ - resample_row_func resample; - stbi_uc *line0,*line1; - int hs,vs; // expansion factor in each axis - int w_lores; // horizontal pixels pre-expansion - int ystep; // how far through vertical expansion we are - int ypos; // which pre-expansion row we're on -} stbi__resample; - -// fast 0..255 * 0..255 => 0..255 rounded multiplication -static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) -{ - unsigned int t = x*y + 128; - return (stbi_uc) ((t + (t >>8)) >> 8); -} - -static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) -{ - int n, decode_n, is_rgb; - z->s->img_n = 0; // make stbi__cleanup_jpeg safe - - // validate req_comp - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - - // load a jpeg image from whichever source, but leave in YCbCr format - if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } - - // determine actual number of components to generate - n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; - - is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); - - if (z->s->img_n == 3 && n < 3 && !is_rgb) - decode_n = 1; - else - decode_n = z->s->img_n; - - // resample and color-convert - { - int k; - unsigned int i,j; - stbi_uc *output; - stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; - - stbi__resample res_comp[4]; - - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - - // allocate line buffer big enough for upsampling off the edges - // with upsample factor of 4 - z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); - if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - r->hs = z->img_h_max / z->img_comp[k].h; - r->vs = z->img_v_max / z->img_comp[k].v; - r->ystep = r->vs >> 1; - r->w_lores = (z->s->img_x + r->hs-1) / r->hs; - r->ypos = 0; - r->line0 = r->line1 = z->img_comp[k].data; - - if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; - else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; - else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; - else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; - else r->resample = stbi__resample_row_generic; - } - - // can't error after this so, this is safe - output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); - if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - // now go ahead and resample - for (j=0; j < z->s->img_y; ++j) { - stbi_uc *out = output + n * z->s->img_x * j; - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - int y_bot = r->ystep >= (r->vs >> 1); - coutput[k] = r->resample(z->img_comp[k].linebuf, - y_bot ? r->line1 : r->line0, - y_bot ? r->line0 : r->line1, - r->w_lores, r->hs); - if (++r->ystep >= r->vs) { - r->ystep = 0; - r->line0 = r->line1; - if (++r->ypos < z->img_comp[k].y) - r->line1 += z->img_comp[k].w2; - } - } - if (n >= 3) { - stbi_uc *y = coutput[0]; - if (z->s->img_n == 3) { - if (is_rgb) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = y[i]; - out[1] = coutput[1][i]; - out[2] = coutput[2][i]; - out[3] = 255; - out += n; - } - } else { - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else if (z->s->img_n == 4) { - if (z->app14_color_transform == 0) { // CMYK - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(coutput[0][i], m); - out[1] = stbi__blinn_8x8(coutput[1][i], m); - out[2] = stbi__blinn_8x8(coutput[2][i], m); - out[3] = 255; - out += n; - } - } else if (z->app14_color_transform == 2) { // YCCK - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(255 - out[0], m); - out[1] = stbi__blinn_8x8(255 - out[1], m); - out[2] = stbi__blinn_8x8(255 - out[2], m); - out += n; - } - } else { // YCbCr + alpha? Ignore the fourth channel for now - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else - for (i=0; i < z->s->img_x; ++i) { - out[0] = out[1] = out[2] = y[i]; - out[3] = 255; // not used if n==3 - out += n; - } - } else { - if (is_rgb) { - if (n == 1) - for (i=0; i < z->s->img_x; ++i) - *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - else { - for (i=0; i < z->s->img_x; ++i, out += 2) { - out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - out[1] = 255; - } - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); - stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); - stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); - out[0] = stbi__compute_y(r, g, b); - out[1] = 255; - out += n; - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); - out[1] = 255; - out += n; - } - } else { - stbi_uc *y = coutput[0]; - if (n == 1) - for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; - else - for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } - } - } - } - stbi__cleanup_jpeg(z); - *out_x = z->s->img_x; - *out_y = z->s->img_y; - if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output - return output; - } -} - -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - unsigned char* result; - stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); - STBI_NOTUSED(ri); - j->s = s; - stbi__setup_jpeg(j); - result = load_jpeg_image(j, x,y,comp,req_comp); - STBI_FREE(j); - return result; -} - -static int stbi__jpeg_test(stbi__context *s) -{ - int r; - stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); - j->s = s; - stbi__setup_jpeg(j); - r = stbi__decode_jpeg_header(j, STBI__SCAN_type); - stbi__rewind(s); - STBI_FREE(j); - return r; -} - -static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) -{ - if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { - stbi__rewind( j->s ); - return 0; - } - if (x) *x = j->s->img_x; - if (y) *y = j->s->img_y; - if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; - return 1; -} - -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) -{ - int result; - stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); - j->s = s; - result = stbi__jpeg_info_raw(j, x, y, comp); - STBI_FREE(j); - return result; -} -#endif - -// public domain zlib decode v0.2 Sean Barrett 2006-11-18 -// simple implementation -// - all input must be provided in an upfront buffer -// - all output is written to a single output buffer (can malloc/realloc) -// performance -// - fast huffman - -#ifndef STBI_NO_ZLIB - -// fast-way is faster to check than jpeg huffman, but slow way is slower -#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables -#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) - -// zlib-style huffman encoding -// (jpegs packs from left, zlib from right, so can't share code) -typedef struct -{ - stbi__uint16 fast[1 << STBI__ZFAST_BITS]; - stbi__uint16 firstcode[16]; - int maxcode[17]; - stbi__uint16 firstsymbol[16]; - stbi_uc size[288]; - stbi__uint16 value[288]; -} stbi__zhuffman; - -stbi_inline static int stbi__bitreverse16(int n) -{ - n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); - n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); - n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); - n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); - return n; -} - -stbi_inline static int stbi__bit_reverse(int v, int bits) -{ - STBI_ASSERT(bits <= 16); - // to bit reverse n bits, reverse 16 and shift - // e.g. 11 bits, bit reverse and shift away 5 - return stbi__bitreverse16(v) >> (16-bits); -} - -static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) -{ - int i,k=0; - int code, next_code[16], sizes[17]; - - // DEFLATE spec for generating codes - memset(sizes, 0, sizeof(sizes)); - memset(z->fast, 0, sizeof(z->fast)); - for (i=0; i < num; ++i) - ++sizes[sizelist[i]]; - sizes[0] = 0; - for (i=1; i < 16; ++i) - if (sizes[i] > (1 << i)) - return stbi__err("bad sizes", "Corrupt PNG"); - code = 0; - for (i=1; i < 16; ++i) { - next_code[i] = code; - z->firstcode[i] = (stbi__uint16) code; - z->firstsymbol[i] = (stbi__uint16) k; - code = (code + sizes[i]); - if (sizes[i]) - if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); - z->maxcode[i] = code << (16-i); // preshift for inner loop - code <<= 1; - k += sizes[i]; - } - z->maxcode[16] = 0x10000; // sentinel - for (i=0; i < num; ++i) { - int s = sizelist[i]; - if (s) { - int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; - stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); - z->size [c] = (stbi_uc ) s; - z->value[c] = (stbi__uint16) i; - if (s <= STBI__ZFAST_BITS) { - int j = stbi__bit_reverse(next_code[s],s); - while (j < (1 << STBI__ZFAST_BITS)) { - z->fast[j] = fastv; - j += (1 << s); - } - } - ++next_code[s]; - } - } - return 1; -} - -// zlib-from-memory implementation for PNG reading -// because PNG allows splitting the zlib stream arbitrarily, -// and it's annoying structurally to have PNG call ZLIB call PNG, -// we require PNG read all the IDATs and combine them into a single -// memory buffer - -typedef struct -{ - stbi_uc *zbuffer, *zbuffer_end; - int num_bits; - stbi__uint32 code_buffer; - - char *zout; - char *zout_start; - char *zout_end; - int z_expandable; - - stbi__zhuffman z_length, z_distance; -} stbi__zbuf; - -stbi_inline static int stbi__zeof(stbi__zbuf *z) -{ - return (z->zbuffer >= z->zbuffer_end); -} - -stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) -{ - return stbi__zeof(z) ? 0 : *z->zbuffer++; -} - -static void stbi__fill_bits(stbi__zbuf *z) -{ - do { - if (z->code_buffer >= (1U << z->num_bits)) { - z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ - return; - } - z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; - z->num_bits += 8; - } while (z->num_bits <= 24); -} - -stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) -{ - unsigned int k; - if (z->num_bits < n) stbi__fill_bits(z); - k = z->code_buffer & ((1 << n) - 1); - z->code_buffer >>= n; - z->num_bits -= n; - return k; -} - -static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s,k; - // not resolved by fast table, so compute it the slow way - // use jpeg approach, which requires MSbits at top - k = stbi__bit_reverse(a->code_buffer, 16); - for (s=STBI__ZFAST_BITS+1; ; ++s) - if (k < z->maxcode[s]) - break; - if (s >= 16) return -1; // invalid code! - // code size is s, so: - b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - if ((unsigned int)b >= sizeof (z->size)) return -1; // some data was corrupt somewhere! - if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; -} - -stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s; - if (a->num_bits < 16) { - if (stbi__zeof(a)) { - return -1; /* report error for unexpected end of data. */ - } - stbi__fill_bits(a); - } - b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; - if (b) { - s = b >> 9; - a->code_buffer >>= s; - a->num_bits -= s; - return b & 511; - } - return stbi__zhuffman_decode_slowpath(a, z); -} - -static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes -{ - char *q; - unsigned int cur, limit, old_limit; - z->zout = zout; - if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); - cur = (unsigned int) (z->zout - z->zout_start); - limit = old_limit = (unsigned) (z->zout_end - z->zout_start); - if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); - while (cur + n > limit) { - if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); - limit *= 2; - } - q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); - STBI_NOTUSED(old_limit); - if (q == NULL) return stbi__err("outofmem", "Out of memory"); - z->zout_start = q; - z->zout = q + cur; - z->zout_end = q + limit; - return 1; -} - -static const int stbi__zlength_base[31] = { - 3,4,5,6,7,8,9,10,11,13, - 15,17,19,23,27,31,35,43,51,59, - 67,83,99,115,131,163,195,227,258,0,0 }; - -static const int stbi__zlength_extra[31]= -{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; - -static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, -257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; - -static const int stbi__zdist_extra[32] = -{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; - -static int stbi__parse_huffman_block(stbi__zbuf *a) -{ - char *zout = a->zout; - for(;;) { - int z = stbi__zhuffman_decode(a, &a->z_length); - if (z < 256) { - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes - if (zout >= a->zout_end) { - if (!stbi__zexpand(a, zout, 1)) return 0; - zout = a->zout; - } - *zout++ = (char) z; - } else { - stbi_uc *p; - int len,dist; - if (z == 256) { - a->zout = zout; - return 1; - } - z -= 257; - len = stbi__zlength_base[z]; - if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); - z = stbi__zhuffman_decode(a, &a->z_distance); - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); - dist = stbi__zdist_base[z]; - if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); - if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); - if (zout + len > a->zout_end) { - if (!stbi__zexpand(a, zout, len)) return 0; - zout = a->zout; - } - p = (stbi_uc *) (zout - dist); - if (dist == 1) { // run of one byte; common in images. - stbi_uc v = *p; - if (len) { do *zout++ = v; while (--len); } - } else { - if (len) { do *zout++ = *p++; while (--len); } - } - } - } -} - -static int stbi__compute_huffman_codes(stbi__zbuf *a) -{ - static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; - stbi__zhuffman z_codelength; - stbi_uc lencodes[286+32+137];//padding for maximum single op - stbi_uc codelength_sizes[19]; - int i,n; - - int hlit = stbi__zreceive(a,5) + 257; - int hdist = stbi__zreceive(a,5) + 1; - int hclen = stbi__zreceive(a,4) + 4; - int ntot = hlit + hdist; - - memset(codelength_sizes, 0, sizeof(codelength_sizes)); - for (i=0; i < hclen; ++i) { - int s = stbi__zreceive(a,3); - codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; - } - if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; - - n = 0; - while (n < ntot) { - int c = stbi__zhuffman_decode(a, &z_codelength); - if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); - if (c < 16) - lencodes[n++] = (stbi_uc) c; - else { - stbi_uc fill = 0; - if (c == 16) { - c = stbi__zreceive(a,2)+3; - if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); - fill = lencodes[n-1]; - } else if (c == 17) { - c = stbi__zreceive(a,3)+3; - } else if (c == 18) { - c = stbi__zreceive(a,7)+11; - } else { - return stbi__err("bad codelengths", "Corrupt PNG"); - } - if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); - memset(lencodes+n, fill, c); - n += c; - } - } - if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); - if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; - return 1; -} - -static int stbi__parse_uncompressed_block(stbi__zbuf *a) -{ - stbi_uc header[4]; - int len,nlen,k; - if (a->num_bits & 7) - stbi__zreceive(a, a->num_bits & 7); // discard - // drain the bit-packed data into header - k = 0; - while (a->num_bits > 0) { - header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check - a->code_buffer >>= 8; - a->num_bits -= 8; - } - if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); - // now fill header the normal way - while (k < 4) - header[k++] = stbi__zget8(a); - len = header[1] * 256 + header[0]; - nlen = header[3] * 256 + header[2]; - if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); - if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); - if (a->zout + len > a->zout_end) - if (!stbi__zexpand(a, a->zout, len)) return 0; - memcpy(a->zout, a->zbuffer, len); - a->zbuffer += len; - a->zout += len; - return 1; -} - -static int stbi__parse_zlib_header(stbi__zbuf *a) -{ - int cmf = stbi__zget8(a); - int cm = cmf & 15; - /* int cinfo = cmf >> 4; */ - int flg = stbi__zget8(a); - if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec - if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec - if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png - if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png - // window = 1 << (8 + cinfo)... but who cares, we fully buffer output - return 1; -} - -static const stbi_uc stbi__zdefault_length[288] = -{ - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 -}; -static const stbi_uc stbi__zdefault_distance[32] = -{ - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 -}; -/* -Init algorithm: -{ - int i; // use <= to match clearly with spec - for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; - for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; - for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; - for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; - - for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; -} -*/ - -static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) -{ - int final, type; - if (parse_header) - if (!stbi__parse_zlib_header(a)) return 0; - a->num_bits = 0; - a->code_buffer = 0; - do { - final = stbi__zreceive(a,1); - type = stbi__zreceive(a,2); - if (type == 0) { - if (!stbi__parse_uncompressed_block(a)) return 0; - } else if (type == 3) { - return 0; - } else { - if (type == 1) { - // use fixed code lengths - if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; - } else { - if (!stbi__compute_huffman_codes(a)) return 0; - } - if (!stbi__parse_huffman_block(a)) return 0; - } - } while (!final); - return 1; -} - -static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) -{ - a->zout_start = obuf; - a->zout = obuf; - a->zout_end = obuf + olen; - a->z_expandable = exp; - - return stbi__parse_zlib(a, parse_header); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) -{ - return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) - return (int) (a.zout - a.zout_start); - else - return -1; -} - -STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(16384); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer+len; - if (stbi__do_zlib(&a, p, 16384, 1, 0)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) - return (int) (a.zout - a.zout_start); - else - return -1; -} -#endif - -// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 -// simple implementation -// - only 8-bit samples -// - no CRC checking -// - allocates lots of intermediate memory -// - avoids problem of streaming data between subsystems -// - avoids explicit window management -// performance -// - uses stb_zlib, a PD zlib implementation with fast huffman decoding - -#ifndef STBI_NO_PNG -typedef struct -{ - stbi__uint32 length; - stbi__uint32 type; -} stbi__pngchunk; - -static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) -{ - stbi__pngchunk c; - c.length = stbi__get32be(s); - c.type = stbi__get32be(s); - return c; -} - -static int stbi__check_png_header(stbi__context *s) -{ - static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; - int i; - for (i=0; i < 8; ++i) - if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); - return 1; -} - -typedef struct -{ - stbi__context *s; - stbi_uc *idata, *expanded, *out; - int depth; -} stbi__png; - - -enum { - STBI__F_none=0, - STBI__F_sub=1, - STBI__F_up=2, - STBI__F_avg=3, - STBI__F_paeth=4, - // synthetic filters used for first scanline to avoid needing a dummy row of 0s - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static stbi_uc first_row_filter[5] = -{ - STBI__F_none, - STBI__F_sub, - STBI__F_none, - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static int stbi__paeth(int a, int b, int c) -{ - int p = a + b - c; - int pa = abs(p-a); - int pb = abs(p-b); - int pc = abs(p-c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; -} - -static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; - -// create the png data from post-deflated data -static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) -{ - int bytes = (depth == 16? 2 : 1); - stbi__context *s = a->s; - stbi__uint32 i,j,stride = x*out_n*bytes; - stbi__uint32 img_len, img_width_bytes; - int k; - int img_n = s->img_n; // copy it into a local for later - - int output_bytes = out_n*bytes; - int filter_bytes = img_n*bytes; - int width = x; - - STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); - a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into - if (!a->out) return stbi__err("outofmem", "Out of memory"); - - if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); - img_width_bytes = (((img_n * x * depth) + 7) >> 3); - img_len = (img_width_bytes + 1) * y; - - // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, - // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), - // so just check for raw_len < img_len always. - if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); - - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *prior; - int filter = *raw++; - - if (filter > 4) - return stbi__err("invalid filter","Corrupt PNG"); - - if (depth < 8) { - if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); - cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place - filter_bytes = 1; - width = img_width_bytes; - } - prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above - - // if first row, use special filter that doesn't sample previous row - if (j == 0) filter = first_row_filter[filter]; - - // handle first byte explicitly - for (k=0; k < filter_bytes; ++k) { - switch (filter) { - case STBI__F_none : cur[k] = raw[k]; break; - case STBI__F_sub : cur[k] = raw[k]; break; - case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; - case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; - case STBI__F_avg_first : cur[k] = raw[k]; break; - case STBI__F_paeth_first: cur[k] = raw[k]; break; - } - } - - if (depth == 8) { - if (img_n != out_n) - cur[img_n] = 255; // first pixel - raw += img_n; - cur += out_n; - prior += out_n; - } else if (depth == 16) { - if (img_n != out_n) { - cur[filter_bytes] = 255; // first pixel top byte - cur[filter_bytes+1] = 255; // first pixel bottom byte - } - raw += filter_bytes; - cur += output_bytes; - prior += output_bytes; - } else { - raw += 1; - cur += 1; - prior += 1; - } - - // this is a little gross, so that we don't switch per-pixel or per-component - if (depth < 8 || img_n == out_n) { - int nk = (width - 1)*filter_bytes; - #define STBI__CASE(f) \ - case f: \ - for (k=0; k < nk; ++k) - switch (filter) { - // "none" filter turns into a memcpy here; make that explicit. - case STBI__F_none: memcpy(cur, raw, nk); break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; - } - #undef STBI__CASE - raw += nk; - } else { - STBI_ASSERT(img_n+1 == out_n); - #define STBI__CASE(f) \ - case f: \ - for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ - for (k=0; k < filter_bytes; ++k) - switch (filter) { - STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; - } - #undef STBI__CASE - - // the loop above sets the high byte of the pixels' alpha, but for - // 16 bit png files we also need the low byte set. we'll do that here. - if (depth == 16) { - cur = a->out + stride*j; // start at the beginning of the row again - for (i=0; i < x; ++i,cur+=output_bytes) { - cur[filter_bytes+1] = 255; - } - } - } - } - - // we make a separate pass to expand bits to pixels; for performance, - // this could run two scanlines behind the above code, so it won't - // intefere with filtering but will still be in the cache. - if (depth < 8) { - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; - // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit - // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop - stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range - - // note that the final byte might overshoot and write more data than desired. - // we can allocate enough data that this never writes out of memory, but it - // could also overwrite the next scanline. can it overwrite non-empty data - // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. - // so we need to explicitly clamp the final ones - - if (depth == 4) { - for (k=x*img_n; k >= 2; k-=2, ++in) { - *cur++ = scale * ((*in >> 4) ); - *cur++ = scale * ((*in ) & 0x0f); - } - if (k > 0) *cur++ = scale * ((*in >> 4) ); - } else if (depth == 2) { - for (k=x*img_n; k >= 4; k-=4, ++in) { - *cur++ = scale * ((*in >> 6) ); - *cur++ = scale * ((*in >> 4) & 0x03); - *cur++ = scale * ((*in >> 2) & 0x03); - *cur++ = scale * ((*in ) & 0x03); - } - if (k > 0) *cur++ = scale * ((*in >> 6) ); - if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); - if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); - } else if (depth == 1) { - for (k=x*img_n; k >= 8; k-=8, ++in) { - *cur++ = scale * ((*in >> 7) ); - *cur++ = scale * ((*in >> 6) & 0x01); - *cur++ = scale * ((*in >> 5) & 0x01); - *cur++ = scale * ((*in >> 4) & 0x01); - *cur++ = scale * ((*in >> 3) & 0x01); - *cur++ = scale * ((*in >> 2) & 0x01); - *cur++ = scale * ((*in >> 1) & 0x01); - *cur++ = scale * ((*in ) & 0x01); - } - if (k > 0) *cur++ = scale * ((*in >> 7) ); - if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); - if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); - if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); - if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); - if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); - if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); - } - if (img_n != out_n) { - int q; - // insert alpha = 255 - cur = a->out + stride*j; - if (img_n == 1) { - for (q=x-1; q >= 0; --q) { - cur[q*2+1] = 255; - cur[q*2+0] = cur[q]; - } - } else { - STBI_ASSERT(img_n == 3); - for (q=x-1; q >= 0; --q) { - cur[q*4+3] = 255; - cur[q*4+2] = cur[q*3+2]; - cur[q*4+1] = cur[q*3+1]; - cur[q*4+0] = cur[q*3+0]; - } - } - } - } - } else if (depth == 16) { - // force the image data from big-endian to platform-native. - // this is done in a separate pass due to the decoding relying - // on the data being untouched, but could probably be done - // per-line during decode if care is taken. - stbi_uc *cur = a->out; - stbi__uint16 *cur16 = (stbi__uint16*)cur; - - for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { - *cur16 = (cur[0] << 8) | cur[1]; - } - } - - return 1; -} - -static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) -{ - int bytes = (depth == 16 ? 2 : 1); - int out_bytes = out_n * bytes; - stbi_uc *final; - int p; - if (!interlaced) - return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); - - // de-interlacing - final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); - for (p=0; p < 7; ++p) { - int xorig[] = { 0,4,0,2,0,1,0 }; - int yorig[] = { 0,0,4,0,2,0,1 }; - int xspc[] = { 8,8,4,4,2,2,1 }; - int yspc[] = { 8,8,8,4,4,2,2 }; - int i,j,x,y; - // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 - x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; - y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; - if (x && y) { - stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; - if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { - STBI_FREE(final); - return 0; - } - for (j=0; j < y; ++j) { - for (i=0; i < x; ++i) { - int out_y = j*yspc[p]+yorig[p]; - int out_x = i*xspc[p]+xorig[p]; - memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, - a->out + (j*x+i)*out_bytes, out_bytes); - } - } - STBI_FREE(a->out); - image_data += img_len; - image_data_len -= img_len; - } - } - a->out = final; - - return 1; -} - -static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - // compute color-based transparency, assuming we've - // already got 255 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i=0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 255); - p += 2; - } - } else { - for (i=0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi__uint16 *p = (stbi__uint16*) z->out; - - // compute color-based transparency, assuming we've - // already got 65535 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i = 0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 65535); - p += 2; - } - } else { - for (i = 0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) -{ - stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; - stbi_uc *p, *temp_out, *orig = a->out; - - p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); - if (p == NULL) return stbi__err("outofmem", "Out of memory"); - - // between here and free(out) below, exitting would leak - temp_out = p; - - if (pal_img_n == 3) { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p += 3; - } - } else { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p[3] = palette[n+3]; - p += 4; - } - } - STBI_FREE(a->out); - a->out = temp_out; - - STBI_NOTUSED(len); - - return 1; -} - -static int stbi__unpremultiply_on_load = 0; -static int stbi__de_iphone_flag = 0; - -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) -{ - stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; -} - -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) -{ - stbi__de_iphone_flag = flag_true_if_should_convert; -} - -static void stbi__de_iphone(stbi__png *z) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - if (s->img_out_n == 3) { // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 3; - } - } else { - STBI_ASSERT(s->img_out_n == 4); - if (stbi__unpremultiply_on_load) { - // convert bgr to rgb and unpremultiply - for (i=0; i < pixel_count; ++i) { - stbi_uc a = p[3]; - stbi_uc t = p[0]; - if (a) { - stbi_uc half = a / 2; - p[0] = (p[2] * 255 + half) / a; - p[1] = (p[1] * 255 + half) / a; - p[2] = ( t * 255 + half) / a; - } else { - p[0] = p[2]; - p[2] = t; - } - p += 4; - } - } else { - // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 4; - } - } - } -} - -#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) - -static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) -{ - stbi_uc palette[1024], pal_img_n=0; - stbi_uc has_trans=0, tc[3]={0}; - stbi__uint16 tc16[3]; - stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; - int first=1,k,interlace=0, color=0, is_iphone=0; - stbi__context *s = z->s; - - z->expanded = NULL; - z->idata = NULL; - z->out = NULL; - - if (!stbi__check_png_header(s)) return 0; - - if (scan == STBI__SCAN_type) return 1; - - for (;;) { - stbi__pngchunk c = stbi__get_chunk_header(s); - switch (c.type) { - case STBI__PNG_TYPE('C','g','B','I'): - is_iphone = 1; - stbi__skip(s, c.length); - break; - case STBI__PNG_TYPE('I','H','D','R'): { - int comp,filter; - if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); - first = 0; - if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); - s->img_x = stbi__get32be(s); - s->img_y = stbi__get32be(s); - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); - color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); - comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); - filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); - interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); - if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); - if (!pal_img_n) { - s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - if (scan == STBI__SCAN_header) return 1; - } else { - // if paletted, then pal_n is our final components, and - // img_n is # components to decompress/filter. - s->img_n = 1; - if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); - // if SCAN_header, have to scan to see if we have a tRNS - } - break; - } - - case STBI__PNG_TYPE('P','L','T','E'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); - pal_len = c.length / 3; - if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); - for (i=0; i < pal_len; ++i) { - palette[i*4+0] = stbi__get8(s); - palette[i*4+1] = stbi__get8(s); - palette[i*4+2] = stbi__get8(s); - palette[i*4+3] = 255; - } - break; - } - - case STBI__PNG_TYPE('t','R','N','S'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); - if (pal_img_n) { - if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } - if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); - if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); - pal_img_n = 4; - for (i=0; i < c.length; ++i) - palette[i*4+3] = stbi__get8(s); - } else { - if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); - if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); - has_trans = 1; - if (z->depth == 16) { - for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is - } else { - for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger - } - } - break; - } - - case STBI__PNG_TYPE('I','D','A','T'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); - if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } - if ((int)(ioff + c.length) < (int)ioff) return 0; - if (ioff + c.length > idata_limit) { - stbi__uint32 idata_limit_old = idata_limit; - stbi_uc *p; - if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; - while (ioff + c.length > idata_limit) - idata_limit *= 2; - STBI_NOTUSED(idata_limit_old); - p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); - z->idata = p; - } - if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); - ioff += c.length; - break; - } - - case STBI__PNG_TYPE('I','E','N','D'): { - stbi__uint32 raw_len, bpl; - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (scan != STBI__SCAN_load) return 1; - if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); - // initial guess for decoded data size to avoid unnecessary reallocs - bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component - raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; - z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); - if (z->expanded == NULL) return 0; // zlib should set error - STBI_FREE(z->idata); z->idata = NULL; - if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) - s->img_out_n = s->img_n+1; - else - s->img_out_n = s->img_n; - if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; - if (has_trans) { - if (z->depth == 16) { - if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; - } else { - if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; - } - } - if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) - stbi__de_iphone(z); - if (pal_img_n) { - // pal_img_n == 3 or 4 - s->img_n = pal_img_n; // record the actual colors we had - s->img_out_n = pal_img_n; - if (req_comp >= 3) s->img_out_n = req_comp; - if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) - return 0; - } else if (has_trans) { - // non-paletted image with tRNS -> source image has (constant) alpha - ++s->img_n; - } - STBI_FREE(z->expanded); z->expanded = NULL; - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - return 1; - } - - default: - // if critical, fail - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if ((c.type & (1 << 29)) == 0) { - #ifndef STBI_NO_FAILURE_STRINGS - // not threadsafe - static char invalid_chunk[] = "XXXX PNG chunk not known"; - invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); - invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); - invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); - invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); - #endif - return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); - } - stbi__skip(s, c.length); - break; - } - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - } -} - -static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) -{ - void *result=NULL; - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth <= 8) - ri->bits_per_channel = 8; - else if (p->depth == 16) - ri->bits_per_channel = 16; - else - return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); - result = p->out; - p->out = NULL; - if (req_comp && req_comp != p->s->img_out_n) { - if (ri->bits_per_channel == 8) - result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - else - result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - p->s->img_out_n = req_comp; - if (result == NULL) return result; - } - *x = p->s->img_x; - *y = p->s->img_y; - if (n) *n = p->s->img_n; - } - STBI_FREE(p->out); p->out = NULL; - STBI_FREE(p->expanded); p->expanded = NULL; - STBI_FREE(p->idata); p->idata = NULL; - - return result; -} - -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi__png p; - p.s = s; - return stbi__do_png(&p, x,y,comp,req_comp, ri); -} - -static int stbi__png_test(stbi__context *s) -{ - int r; - r = stbi__check_png_header(s); - stbi__rewind(s); - return r; -} - -static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) -{ - if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { - stbi__rewind( p->s ); - return 0; - } - if (x) *x = p->s->img_x; - if (y) *y = p->s->img_y; - if (comp) *comp = p->s->img_n; - return 1; -} - -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__png p; - p.s = s; - return stbi__png_info_raw(&p, x, y, comp); -} - -static int stbi__png_is16(stbi__context *s) -{ - stbi__png p; - p.s = s; - if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) - return 0; - if (p.depth != 16) { - stbi__rewind(p.s); - return 0; - } - return 1; -} -#endif - -// Microsoft/Windows BMP image - -#ifndef STBI_NO_BMP -static int stbi__bmp_test_raw(stbi__context *s) -{ - int r; - int sz; - if (stbi__get8(s) != 'B') return 0; - if (stbi__get8(s) != 'M') return 0; - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - stbi__get32le(s); // discard data offset - sz = stbi__get32le(s); - r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); - return r; -} - -static int stbi__bmp_test(stbi__context *s) -{ - int r = stbi__bmp_test_raw(s); - stbi__rewind(s); - return r; -} - - -// returns 0..31 for the highest set bit -static int stbi__high_bit(unsigned int z) -{ - int n=0; - if (z == 0) return -1; - if (z >= 0x10000) { n += 16; z >>= 16; } - if (z >= 0x00100) { n += 8; z >>= 8; } - if (z >= 0x00010) { n += 4; z >>= 4; } - if (z >= 0x00004) { n += 2; z >>= 2; } - if (z >= 0x00002) { n += 1;/* >>= 1;*/ } - return n; -} - -static int stbi__bitcount(unsigned int a) -{ - a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 - a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 - a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits - a = (a + (a >> 8)); // max 16 per 8 bits - a = (a + (a >> 16)); // max 32 per 8 bits - return a & 0xff; -} - -// extract an arbitrarily-aligned N-bit value (N=bits) -// from v, and then make it 8-bits long and fractionally -// extend it to full full range. -static int stbi__shiftsigned(unsigned int v, int shift, int bits) -{ - static unsigned int mul_table[9] = { - 0, - 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, - 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, - }; - static unsigned int shift_table[9] = { - 0, 0,0,1,0,2,4,6,0, - }; - if (shift < 0) - v <<= -shift; - else - v >>= shift; - STBI_ASSERT(v < 256); - v >>= (8-bits); - STBI_ASSERT(bits >= 0 && bits <= 8); - return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; -} - -typedef struct -{ - int bpp, offset, hsz; - unsigned int mr,mg,mb,ma, all_a; - int extra_read; -} stbi__bmp_data; - -static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) -{ - int hsz; - if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - info->offset = stbi__get32le(s); - info->hsz = hsz = stbi__get32le(s); - info->mr = info->mg = info->mb = info->ma = 0; - info->extra_read = 14; - - if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); - - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); - if (hsz == 12) { - s->img_x = stbi__get16le(s); - s->img_y = stbi__get16le(s); - } else { - s->img_x = stbi__get32le(s); - s->img_y = stbi__get32le(s); - } - if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); - info->bpp = stbi__get16le(s); - if (hsz != 12) { - int compress = stbi__get32le(s); - if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); - stbi__get32le(s); // discard sizeof - stbi__get32le(s); // discard hres - stbi__get32le(s); // discard vres - stbi__get32le(s); // discard colorsused - stbi__get32le(s); // discard max important - if (hsz == 40 || hsz == 56) { - if (hsz == 56) { - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - } - if (info->bpp == 16 || info->bpp == 32) { - if (compress == 0) { - if (info->bpp == 32) { - info->mr = 0xffu << 16; - info->mg = 0xffu << 8; - info->mb = 0xffu << 0; - info->ma = 0xffu << 24; - info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 - } else { - info->mr = 31u << 10; - info->mg = 31u << 5; - info->mb = 31u << 0; - } - } else if (compress == 3) { - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->extra_read += 12; - // not documented, but generated by photoshop and handled by mspaint - if (info->mr == info->mg && info->mg == info->mb) { - // ?!?!? - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else { - int i; - if (hsz != 108 && hsz != 124) - return stbi__errpuc("bad BMP", "bad BMP"); - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->ma = stbi__get32le(s); - stbi__get32le(s); // discard color space - for (i=0; i < 12; ++i) - stbi__get32le(s); // discard color space parameters - if (hsz == 124) { - stbi__get32le(s); // discard rendering intent - stbi__get32le(s); // discard offset of profile data - stbi__get32le(s); // discard size of profile data - stbi__get32le(s); // discard reserved - } - } - } - return (void *) 1; -} - - -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - unsigned int mr=0,mg=0,mb=0,ma=0, all_a; - stbi_uc pal[256][4]; - int psize=0,i,j,width; - int flip_vertically, pad, target; - stbi__bmp_data info; - STBI_NOTUSED(ri); - - info.all_a = 255; - if (stbi__bmp_parse_header(s, &info) == NULL) - return NULL; // error code already set - - flip_vertically = ((int) s->img_y) > 0; - s->img_y = abs((int) s->img_y); - - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - mr = info.mr; - mg = info.mg; - mb = info.mb; - ma = info.ma; - all_a = info.all_a; - - if (info.hsz == 12) { - if (info.bpp < 24) - psize = (info.offset - info.extra_read - 24) / 3; - } else { - if (info.bpp < 16) - psize = (info.offset - info.extra_read - info.hsz) >> 2; - } - if (psize == 0) { - STBI_ASSERT(info.offset == s->callback_already_read + (int) (s->img_buffer - s->img_buffer_original)); - if (info.offset != s->callback_already_read + (s->img_buffer - s->buffer_start)) { - return stbi__errpuc("bad offset", "Corrupt BMP"); - } - } - - if (info.bpp == 24 && ma == 0xff000000) - s->img_n = 3; - else - s->img_n = ma ? 4 : 3; - if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 - target = req_comp; - else - target = s->img_n; // if they want monochrome, we'll post-convert - - // sanity-check size - if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) - return stbi__errpuc("too large", "Corrupt BMP"); - - out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - if (info.bpp < 16) { - int z=0; - if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } - for (i=0; i < psize; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - if (info.hsz != 12) stbi__get8(s); - pal[i][3] = 255; - } - stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); - if (info.bpp == 1) width = (s->img_x + 7) >> 3; - else if (info.bpp == 4) width = (s->img_x + 1) >> 1; - else if (info.bpp == 8) width = s->img_x; - else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } - pad = (-width)&3; - if (info.bpp == 1) { - for (j=0; j < (int) s->img_y; ++j) { - int bit_offset = 7, v = stbi__get8(s); - for (i=0; i < (int) s->img_x; ++i) { - int color = (v>>bit_offset)&0x1; - out[z++] = pal[color][0]; - out[z++] = pal[color][1]; - out[z++] = pal[color][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - if((--bit_offset) < 0) { - bit_offset = 7; - v = stbi__get8(s); - } - } - stbi__skip(s, pad); - } - } else { - for (j=0; j < (int) s->img_y; ++j) { - for (i=0; i < (int) s->img_x; i += 2) { - int v=stbi__get8(s),v2=0; - if (info.bpp == 4) { - v2 = v & 15; - v >>= 4; - } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - v = (info.bpp == 8) ? stbi__get8(s) : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - } - stbi__skip(s, pad); - } - } - } else { - int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; - int z = 0; - int easy=0; - stbi__skip(s, info.offset - info.extra_read - info.hsz); - if (info.bpp == 24) width = 3 * s->img_x; - else if (info.bpp == 16) width = 2*s->img_x; - else /* bpp = 32 and pad = 0 */ width=0; - pad = (-width) & 3; - if (info.bpp == 24) { - easy = 1; - } else if (info.bpp == 32) { - if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) - easy = 2; - } - if (!easy) { - if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - // right shift amt to put high bit in position #7 - rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); - gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); - bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); - ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); - if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - } - for (j=0; j < (int) s->img_y; ++j) { - if (easy) { - for (i=0; i < (int) s->img_x; ++i) { - unsigned char a; - out[z+2] = stbi__get8(s); - out[z+1] = stbi__get8(s); - out[z+0] = stbi__get8(s); - z += 3; - a = (easy == 2 ? stbi__get8(s) : 255); - all_a |= a; - if (target == 4) out[z++] = a; - } - } else { - int bpp = info.bpp; - for (i=0; i < (int) s->img_x; ++i) { - stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); - unsigned int a; - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); - a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); - all_a |= a; - if (target == 4) out[z++] = STBI__BYTECAST(a); - } - } - stbi__skip(s, pad); - } - } - - // if alpha channel is all 0s, replace with all 255s - if (target == 4 && all_a == 0) - for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) - out[i] = 255; - - if (flip_vertically) { - stbi_uc t; - for (j=0; j < (int) s->img_y>>1; ++j) { - stbi_uc *p1 = out + j *s->img_x*target; - stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; - for (i=0; i < (int) s->img_x*target; ++i) { - t = p1[i]; p1[i] = p2[i]; p2[i] = t; - } - } - } - - if (req_comp && req_comp != target) { - out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - return out; -} -#endif - -// Targa Truevision - TGA -// by Jonathan Dummer -#ifndef STBI_NO_TGA -// returns STBI_rgb or whatever, 0 on error -static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) -{ - // only RGB or RGBA (incl. 16bit) or grey allowed - if (is_rgb16) *is_rgb16 = 0; - switch(bits_per_pixel) { - case 8: return STBI_grey; - case 16: if(is_grey) return STBI_grey_alpha; - // fallthrough - case 15: if(is_rgb16) *is_rgb16 = 1; - return STBI_rgb; - case 24: // fallthrough - case 32: return bits_per_pixel/8; - default: return 0; - } -} - -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) -{ - int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; - int sz, tga_colormap_type; - stbi__get8(s); // discard Offset - tga_colormap_type = stbi__get8(s); // colormap type - if( tga_colormap_type > 1 ) { - stbi__rewind(s); - return 0; // only RGB or indexed allowed - } - tga_image_type = stbi__get8(s); // image type - if ( tga_colormap_type == 1 ) { // colormapped (paletted) image - if (tga_image_type != 1 && tga_image_type != 9) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip image x and y origin - tga_colormap_bpp = sz; - } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE - if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { - stbi__rewind(s); - return 0; // only RGB or grey allowed, +/- RLE - } - stbi__skip(s,9); // skip colormap specification and image x/y origin - tga_colormap_bpp = 0; - } - tga_w = stbi__get16le(s); - if( tga_w < 1 ) { - stbi__rewind(s); - return 0; // test width - } - tga_h = stbi__get16le(s); - if( tga_h < 1 ) { - stbi__rewind(s); - return 0; // test height - } - tga_bits_per_pixel = stbi__get8(s); // bits per pixel - stbi__get8(s); // ignore alpha bits - if (tga_colormap_bpp != 0) { - if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { - // when using a colormap, tga_bits_per_pixel is the size of the indexes - // I don't think anything but 8 or 16bit indexes makes sense - stbi__rewind(s); - return 0; - } - tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); - } else { - tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); - } - if(!tga_comp) { - stbi__rewind(s); - return 0; - } - if (x) *x = tga_w; - if (y) *y = tga_h; - if (comp) *comp = tga_comp; - return 1; // seems to have passed everything -} - -static int stbi__tga_test(stbi__context *s) -{ - int res = 0; - int sz, tga_color_type; - stbi__get8(s); // discard Offset - tga_color_type = stbi__get8(s); // color type - if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed - sz = stbi__get8(s); // image type - if ( tga_color_type == 1 ) { // colormapped (paletted) image - if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - stbi__skip(s,4); // skip image x and y origin - } else { // "normal" image w/o colormap - if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE - stbi__skip(s,9); // skip colormap specification and image x/y origin - } - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height - sz = stbi__get8(s); // bits per pixel - if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - - res = 1; // if we got this far, everything's good and we can return 1 instead of 0 - -errorEnd: - stbi__rewind(s); - return res; -} - -// read 16bit value and convert to 24bit RGB -static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) -{ - stbi__uint16 px = (stbi__uint16)stbi__get16le(s); - stbi__uint16 fiveBitMask = 31; - // we have 3 channels with 5bits each - int r = (px >> 10) & fiveBitMask; - int g = (px >> 5) & fiveBitMask; - int b = px & fiveBitMask; - // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later - out[0] = (stbi_uc)((r * 255)/31); - out[1] = (stbi_uc)((g * 255)/31); - out[2] = (stbi_uc)((b * 255)/31); - - // some people claim that the most significant bit might be used for alpha - // (possibly if an alpha-bit is set in the "image descriptor byte") - // but that only made 16bit test images completely translucent.. - // so let's treat all 15 and 16bit TGAs as RGB with no alpha. -} - -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - // read in the TGA header stuff - int tga_offset = stbi__get8(s); - int tga_indexed = stbi__get8(s); - int tga_image_type = stbi__get8(s); - int tga_is_RLE = 0; - int tga_palette_start = stbi__get16le(s); - int tga_palette_len = stbi__get16le(s); - int tga_palette_bits = stbi__get8(s); - int tga_x_origin = stbi__get16le(s); - int tga_y_origin = stbi__get16le(s); - int tga_width = stbi__get16le(s); - int tga_height = stbi__get16le(s); - int tga_bits_per_pixel = stbi__get8(s); - int tga_comp, tga_rgb16=0; - int tga_inverted = stbi__get8(s); - // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) - // image data - unsigned char *tga_data; - unsigned char *tga_palette = NULL; - int i, j; - unsigned char raw_data[4] = {0}; - int RLE_count = 0; - int RLE_repeating = 0; - int read_next_pixel = 1; - STBI_NOTUSED(ri); - STBI_NOTUSED(tga_x_origin); // @TODO - STBI_NOTUSED(tga_y_origin); // @TODO - - if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - // do a tiny bit of precessing - if ( tga_image_type >= 8 ) - { - tga_image_type -= 8; - tga_is_RLE = 1; - } - tga_inverted = 1 - ((tga_inverted >> 5) & 1); - - // If I'm paletted, then I'll use the number of bits from the palette - if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); - else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); - - if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency - return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); - - // tga info - *x = tga_width; - *y = tga_height; - if (comp) *comp = tga_comp; - - if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) - return stbi__errpuc("too large", "Corrupt TGA"); - - tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); - if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); - - // skip to the data's starting position (offset usually = 0) - stbi__skip(s, tga_offset ); - - if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { - for (i=0; i < tga_height; ++i) { - int row = tga_inverted ? tga_height -i - 1 : i; - stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; - stbi__getn(s, tga_row, tga_width * tga_comp); - } - } else { - // do I need to load a palette? - if ( tga_indexed) - { - if (tga_palette_len == 0) { /* you have to have at least one entry! */ - STBI_FREE(tga_data); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - - // any data to skip? (offset usually = 0) - stbi__skip(s, tga_palette_start ); - // load the palette - tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); - if (!tga_palette) { - STBI_FREE(tga_data); - return stbi__errpuc("outofmem", "Out of memory"); - } - if (tga_rgb16) { - stbi_uc *pal_entry = tga_palette; - STBI_ASSERT(tga_comp == STBI_rgb); - for (i=0; i < tga_palette_len; ++i) { - stbi__tga_read_rgb16(s, pal_entry); - pal_entry += tga_comp; - } - } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { - STBI_FREE(tga_data); - STBI_FREE(tga_palette); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - } - // load the data - for (i=0; i < tga_width * tga_height; ++i) - { - // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? - if ( tga_is_RLE ) - { - if ( RLE_count == 0 ) - { - // yep, get the next byte as a RLE command - int RLE_cmd = stbi__get8(s); - RLE_count = 1 + (RLE_cmd & 127); - RLE_repeating = RLE_cmd >> 7; - read_next_pixel = 1; - } else if ( !RLE_repeating ) - { - read_next_pixel = 1; - } - } else - { - read_next_pixel = 1; - } - // OK, if I need to read a pixel, do it now - if ( read_next_pixel ) - { - // load however much data we did have - if ( tga_indexed ) - { - // read in index, then perform the lookup - int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); - if ( pal_idx >= tga_palette_len ) { - // invalid index - pal_idx = 0; - } - pal_idx *= tga_comp; - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = tga_palette[pal_idx+j]; - } - } else if(tga_rgb16) { - STBI_ASSERT(tga_comp == STBI_rgb); - stbi__tga_read_rgb16(s, raw_data); - } else { - // read in the data raw - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = stbi__get8(s); - } - } - // clear the reading flag for the next pixel - read_next_pixel = 0; - } // end of reading a pixel - - // copy data - for (j = 0; j < tga_comp; ++j) - tga_data[i*tga_comp+j] = raw_data[j]; - - // in case we're in RLE mode, keep counting down - --RLE_count; - } - // do I need to invert the image? - if ( tga_inverted ) - { - for (j = 0; j*2 < tga_height; ++j) - { - int index1 = j * tga_width * tga_comp; - int index2 = (tga_height - 1 - j) * tga_width * tga_comp; - for (i = tga_width * tga_comp; i > 0; --i) - { - unsigned char temp = tga_data[index1]; - tga_data[index1] = tga_data[index2]; - tga_data[index2] = temp; - ++index1; - ++index2; - } - } - } - // clear my palette, if I had one - if ( tga_palette != NULL ) - { - STBI_FREE( tga_palette ); - } - } - - // swap RGB - if the source data was RGB16, it already is in the right order - if (tga_comp >= 3 && !tga_rgb16) - { - unsigned char* tga_pixel = tga_data; - for (i=0; i < tga_width * tga_height; ++i) - { - unsigned char temp = tga_pixel[0]; - tga_pixel[0] = tga_pixel[2]; - tga_pixel[2] = temp; - tga_pixel += tga_comp; - } - } - - // convert to target component count - if (req_comp && req_comp != tga_comp) - tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); - - // the things I do to get rid of an error message, and yet keep - // Microsoft's C compilers happy... [8^( - tga_palette_start = tga_palette_len = tga_palette_bits = - tga_x_origin = tga_y_origin = 0; - STBI_NOTUSED(tga_palette_start); - // OK, done - return tga_data; -} -#endif - -// ************************************************************************************************* -// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s) -{ - int r = (stbi__get32be(s) == 0x38425053); - stbi__rewind(s); - return r; -} - -static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) -{ - int count, nleft, len; - - count = 0; - while ((nleft = pixelCount - count) > 0) { - len = stbi__get8(s); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - if (len > nleft) return 0; // corrupt data - count += len; - while (len) { - *p = stbi__get8(s); - p += 4; - len--; - } - } else if (len > 128) { - stbi_uc val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len = 257 - len; - if (len > nleft) return 0; // corrupt data - val = stbi__get8(s); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } - } - - return 1; -} - -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - int pixelCount; - int channelCount, compression; - int channel, i; - int bitdepth; - int w,h; - stbi_uc *out; - STBI_NOTUSED(ri); - - // Check identifier - if (stbi__get32be(s) != 0x38425053) // "8BPS" - return stbi__errpuc("not PSD", "Corrupt PSD image"); - - // Check file type version. - if (stbi__get16be(s) != 1) - return stbi__errpuc("wrong version", "Unsupported version of PSD image"); - - // Skip 6 reserved bytes. - stbi__skip(s, 6 ); - - // Read the number of channels (R, G, B, A, etc). - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) - return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); - - // Read the rows and columns of the image. - h = stbi__get32be(s); - w = stbi__get32be(s); - - if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - // Make sure the depth is 8 bits. - bitdepth = stbi__get16be(s); - if (bitdepth != 8 && bitdepth != 16) - return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); - - // Make sure the color mode is RGB. - // Valid options are: - // 0: Bitmap - // 1: Grayscale - // 2: Indexed color - // 3: RGB color - // 4: CMYK color - // 7: Multichannel - // 8: Duotone - // 9: Lab color - if (stbi__get16be(s) != 3) - return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); - - // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) - stbi__skip(s,stbi__get32be(s) ); - - // Skip the image resources. (resolution, pen tool paths, etc) - stbi__skip(s, stbi__get32be(s) ); - - // Skip the reserved data. - stbi__skip(s, stbi__get32be(s) ); - - // Find out if the data is compressed. - // Known values: - // 0: no compression - // 1: RLE compressed - compression = stbi__get16be(s); - if (compression > 1) - return stbi__errpuc("bad compression", "PSD has an unknown compression format"); - - // Check size - if (!stbi__mad3sizes_valid(4, w, h, 0)) - return stbi__errpuc("too large", "Corrupt PSD"); - - // Create the destination image. - - if (!compression && bitdepth == 16 && bpc == 16) { - out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); - ri->bits_per_channel = 16; - } else - out = (stbi_uc *) stbi__malloc(4 * w*h); - - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - pixelCount = w*h; - - // Initialize the data to zero. - //memset( out, 0, pixelCount * 4 ); - - // Finally, the image data. - if (compression) { - // RLE as used by .PSD and .TIFF - // Loop until you get the number of unpacked bytes you are expecting: - // Read the next source byte into n. - // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. - // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. - // Else if n is 128, noop. - // Endloop - - // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, - // which we're going to just skip. - stbi__skip(s, h * channelCount * 2 ); - - // Read the RLE data by channel. - for (channel = 0; channel < 4; channel++) { - stbi_uc *p; - - p = out+channel; - if (channel >= channelCount) { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++, p += 4) - *p = (channel == 3 ? 255 : 0); - } else { - // Read the RLE data. - if (!stbi__psd_decode_rle(s, p, pixelCount)) { - STBI_FREE(out); - return stbi__errpuc("corrupt", "bad RLE data"); - } - } - } - - } else { - // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. - - // Read the data by channel. - for (channel = 0; channel < 4; channel++) { - if (channel >= channelCount) { - // Fill this channel with default data. - if (bitdepth == 16 && bpc == 16) { - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - stbi__uint16 val = channel == 3 ? 65535 : 0; - for (i = 0; i < pixelCount; i++, q += 4) - *q = val; - } else { - stbi_uc *p = out+channel; - stbi_uc val = channel == 3 ? 255 : 0; - for (i = 0; i < pixelCount; i++, p += 4) - *p = val; - } - } else { - if (ri->bits_per_channel == 16) { // output bpc - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - for (i = 0; i < pixelCount; i++, q += 4) - *q = (stbi__uint16) stbi__get16be(s); - } else { - stbi_uc *p = out+channel; - if (bitdepth == 16) { // input bpc - for (i = 0; i < pixelCount; i++, p += 4) - *p = (stbi_uc) (stbi__get16be(s) >> 8); - } else { - for (i = 0; i < pixelCount; i++, p += 4) - *p = stbi__get8(s); - } - } - } - } - } - - // remove weird white matte from PSD - if (channelCount >= 4) { - if (ri->bits_per_channel == 16) { - for (i=0; i < w*h; ++i) { - stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; - if (pixel[3] != 0 && pixel[3] != 65535) { - float a = pixel[3] / 65535.0f; - float ra = 1.0f / a; - float inv_a = 65535.0f * (1 - ra); - pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); - pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); - pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); - } - } - } else { - for (i=0; i < w*h; ++i) { - unsigned char *pixel = out + 4*i; - if (pixel[3] != 0 && pixel[3] != 255) { - float a = pixel[3] / 255.0f; - float ra = 1.0f / a; - float inv_a = 255.0f * (1 - ra); - pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); - pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); - pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); - } - } - } - } - - // convert to desired output format - if (req_comp && req_comp != 4) { - if (ri->bits_per_channel == 16) - out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); - else - out = stbi__convert_format(out, 4, req_comp, w, h); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - if (comp) *comp = 4; - *y = h; - *x = w; - - return out; -} -#endif - -// ************************************************************************************************* -// Softimage PIC loader -// by Tom Seddon -// -// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format -// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ - -#ifndef STBI_NO_PIC -static int stbi__pic_is4(stbi__context *s,const char *str) -{ - int i; - for (i=0; i<4; ++i) - if (stbi__get8(s) != (stbi_uc)str[i]) - return 0; - - return 1; -} - -static int stbi__pic_test_core(stbi__context *s) -{ - int i; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) - return 0; - - for(i=0;i<84;++i) - stbi__get8(s); - - if (!stbi__pic_is4(s,"PICT")) - return 0; - - return 1; -} - -typedef struct -{ - stbi_uc size,type,channel; -} stbi__pic_packet; - -static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) -{ - int mask=0x80, i; - - for (i=0; i<4; ++i, mask>>=1) { - if (channel & mask) { - if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); - dest[i]=stbi__get8(s); - } - } - - return dest; -} - -static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) -{ - int mask=0x80,i; - - for (i=0;i<4; ++i, mask>>=1) - if (channel&mask) - dest[i]=src[i]; -} - -static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) -{ - int act_comp=0,num_packets=0,y,chained; - stbi__pic_packet packets[10]; - - // this will (should...) cater for even some bizarre stuff like having data - // for the same channel in multiple packets. - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return stbi__errpuc("bad format","too many packets"); - - packet = &packets[num_packets++]; - - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - - act_comp |= packet->channel; - - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); - if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? - - for(y=0; ytype) { - default: - return stbi__errpuc("bad format","packet has bad compression type"); - - case 0: {//uncompressed - int x; - - for(x=0;xchannel,dest)) - return 0; - break; - } - - case 1://Pure RLE - { - int left=width, i; - - while (left>0) { - stbi_uc count,value[4]; - - count=stbi__get8(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); - - if (count > left) - count = (stbi_uc) left; - - if (!stbi__readval(s,packet->channel,value)) return 0; - - for(i=0; ichannel,dest,value); - left -= count; - } - } - break; - - case 2: {//Mixed RLE - int left=width; - while (left>0) { - int count = stbi__get8(s), i; - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); - - if (count >= 128) { // Repeated - stbi_uc value[4]; - - if (count==128) - count = stbi__get16be(s); - else - count -= 127; - if (count > left) - return stbi__errpuc("bad file","scanline overrun"); - - if (!stbi__readval(s,packet->channel,value)) - return 0; - - for(i=0;ichannel,dest,value); - } else { // Raw - ++count; - if (count>left) return stbi__errpuc("bad file","scanline overrun"); - - for(i=0;ichannel,dest)) - return 0; - } - left-=count; - } - break; - } - } - } - } - - return result; -} - -static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) -{ - stbi_uc *result; - int i, x,y, internal_comp; - STBI_NOTUSED(ri); - - if (!comp) comp = &internal_comp; - - for (i=0; i<92; ++i) - stbi__get8(s); - - x = stbi__get16be(s); - y = stbi__get16be(s); - - if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); - if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); - - stbi__get32be(s); //skip `ratio' - stbi__get16be(s); //skip `fields' - stbi__get16be(s); //skip `pad' - - // intermediate buffer is RGBA - result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); - memset(result, 0xff, x*y*4); - - if (!stbi__pic_load_core(s,x,y,comp, result)) { - STBI_FREE(result); - result=0; - } - *px = x; - *py = y; - if (req_comp == 0) req_comp = *comp; - result=stbi__convert_format(result,4,req_comp,x,y); - - return result; -} - -static int stbi__pic_test(stbi__context *s) -{ - int r = stbi__pic_test_core(s); - stbi__rewind(s); - return r; -} -#endif - -// ************************************************************************************************* -// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb - -#ifndef STBI_NO_GIF -typedef struct -{ - stbi__int16 prefix; - stbi_uc first; - stbi_uc suffix; -} stbi__gif_lzw; - -typedef struct -{ - int w,h; - stbi_uc *out; // output buffer (always 4 components) - stbi_uc *background; // The current "background" as far as a gif is concerned - stbi_uc *history; - int flags, bgindex, ratio, transparent, eflags; - stbi_uc pal[256][4]; - stbi_uc lpal[256][4]; - stbi__gif_lzw codes[8192]; - stbi_uc *color_table; - int parse, step; - int lflags; - int start_x, start_y; - int max_x, max_y; - int cur_x, cur_y; - int line_size; - int delay; -} stbi__gif; - -static int stbi__gif_test_raw(stbi__context *s) -{ - int sz; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; - sz = stbi__get8(s); - if (sz != '9' && sz != '7') return 0; - if (stbi__get8(s) != 'a') return 0; - return 1; -} - -static int stbi__gif_test(stbi__context *s) -{ - int r = stbi__gif_test_raw(s); - stbi__rewind(s); - return r; -} - -static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) -{ - int i; - for (i=0; i < num_entries; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - pal[i][3] = transp == i ? 0 : 255; - } -} - -static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) -{ - stbi_uc version; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') - return stbi__err("not GIF", "Corrupt GIF"); - - version = stbi__get8(s); - if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); - if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); - - stbi__g_failure_reason = ""; - g->w = stbi__get16le(s); - g->h = stbi__get16le(s); - g->flags = stbi__get8(s); - g->bgindex = stbi__get8(s); - g->ratio = stbi__get8(s); - g->transparent = -1; - - if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - - if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments - - if (is_info) return 1; - - if (g->flags & 0x80) - stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); - - return 1; -} - -static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); - if (!stbi__gif_header(s, g, comp, 1)) { - STBI_FREE(g); - stbi__rewind( s ); - return 0; - } - if (x) *x = g->w; - if (y) *y = g->h; - STBI_FREE(g); - return 1; -} - -static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) -{ - stbi_uc *p, *c; - int idx; - - // recurse to decode the prefixes, since the linked-list is backwards, - // and working backwards through an interleaved image would be nasty - if (g->codes[code].prefix >= 0) - stbi__out_gif_code(g, g->codes[code].prefix); - - if (g->cur_y >= g->max_y) return; - - idx = g->cur_x + g->cur_y; - p = &g->out[idx]; - g->history[idx / 4] = 1; - - c = &g->color_table[g->codes[code].suffix * 4]; - if (c[3] > 128) { // don't render transparent pixels; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; - } - g->cur_x += 4; - - if (g->cur_x >= g->max_x) { - g->cur_x = g->start_x; - g->cur_y += g->step; - - while (g->cur_y >= g->max_y && g->parse > 0) { - g->step = (1 << g->parse) * g->line_size; - g->cur_y = g->start_y + (g->step >> 1); - --g->parse; - } - } -} - -static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) -{ - stbi_uc lzw_cs; - stbi__int32 len, init_code; - stbi__uint32 first; - stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; - stbi__gif_lzw *p; - - lzw_cs = stbi__get8(s); - if (lzw_cs > 12) return NULL; - clear = 1 << lzw_cs; - first = 1; - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - bits = 0; - valid_bits = 0; - for (init_code = 0; init_code < clear; init_code++) { - g->codes[init_code].prefix = -1; - g->codes[init_code].first = (stbi_uc) init_code; - g->codes[init_code].suffix = (stbi_uc) init_code; - } - - // support no starting clear code - avail = clear+2; - oldcode = -1; - - len = 0; - for(;;) { - if (valid_bits < codesize) { - if (len == 0) { - len = stbi__get8(s); // start new block - if (len == 0) - return g->out; - } - --len; - bits |= (stbi__int32) stbi__get8(s) << valid_bits; - valid_bits += 8; - } else { - stbi__int32 code = bits & codemask; - bits >>= codesize; - valid_bits -= codesize; - // @OPTIMIZE: is there some way we can accelerate the non-clear path? - if (code == clear) { // clear code - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - avail = clear + 2; - oldcode = -1; - first = 0; - } else if (code == clear + 1) { // end of stream code - stbi__skip(s, len); - while ((len = stbi__get8(s)) > 0) - stbi__skip(s,len); - return g->out; - } else if (code <= avail) { - if (first) { - return stbi__errpuc("no clear code", "Corrupt GIF"); - } - - if (oldcode >= 0) { - p = &g->codes[avail++]; - if (avail > 8192) { - return stbi__errpuc("too many codes", "Corrupt GIF"); - } - - p->prefix = (stbi__int16) oldcode; - p->first = g->codes[oldcode].first; - p->suffix = (code == avail) ? p->first : g->codes[code].first; - } else if (code == avail) - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - - stbi__out_gif_code(g, (stbi__uint16) code); - - if ((avail & codemask) == 0 && avail <= 0x0FFF) { - codesize++; - codemask = (1 << codesize) - 1; - } - - oldcode = code; - } else { - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - } - } - } -} - -// this function is designed to support animated gifs, although stb_image doesn't support it -// two back is the image from two frames ago, used for a very specific disposal format -static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) -{ - int dispose; - int first_frame; - int pi; - int pcount; - STBI_NOTUSED(req_comp); - - // on first frame, any non-written pixels get the background colour (non-transparent) - first_frame = 0; - if (g->out == 0) { - if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header - if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) - return stbi__errpuc("too large", "GIF image is too large"); - pcount = g->w * g->h; - g->out = (stbi_uc *) stbi__malloc(4 * pcount); - g->background = (stbi_uc *) stbi__malloc(4 * pcount); - g->history = (stbi_uc *) stbi__malloc(pcount); - if (!g->out || !g->background || !g->history) - return stbi__errpuc("outofmem", "Out of memory"); - - // image is treated as "transparent" at the start - ie, nothing overwrites the current background; - // background colour is only used for pixels that are not rendered first frame, after that "background" - // color refers to the color that was there the previous frame. - memset(g->out, 0x00, 4 * pcount); - memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) - memset(g->history, 0x00, pcount); // pixels that were affected previous frame - first_frame = 1; - } else { - // second frame - how do we dispose of the previous one? - dispose = (g->eflags & 0x1C) >> 2; - pcount = g->w * g->h; - - if ((dispose == 3) && (two_back == 0)) { - dispose = 2; // if I don't have an image to revert back to, default to the old background - } - - if (dispose == 3) { // use previous graphic - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); - } - } - } else if (dispose == 2) { - // restore what was changed last frame to background before that frame; - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); - } - } - } else { - // This is a non-disposal case eithe way, so just - // leave the pixels as is, and they will become the new background - // 1: do not dispose - // 0: not specified. - } - - // background is what out is after the undoing of the previou frame; - memcpy( g->background, g->out, 4 * g->w * g->h ); - } - - // clear my history; - memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame - - for (;;) { - int tag = stbi__get8(s); - switch (tag) { - case 0x2C: /* Image Descriptor */ - { - stbi__int32 x, y, w, h; - stbi_uc *o; - - x = stbi__get16le(s); - y = stbi__get16le(s); - w = stbi__get16le(s); - h = stbi__get16le(s); - if (((x + w) > (g->w)) || ((y + h) > (g->h))) - return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); - - g->line_size = g->w * 4; - g->start_x = x * 4; - g->start_y = y * g->line_size; - g->max_x = g->start_x + w * 4; - g->max_y = g->start_y + h * g->line_size; - g->cur_x = g->start_x; - g->cur_y = g->start_y; - - // if the width of the specified rectangle is 0, that means - // we may not see *any* pixels or the image is malformed; - // to make sure this is caught, move the current y down to - // max_y (which is what out_gif_code checks). - if (w == 0) - g->cur_y = g->max_y; - - g->lflags = stbi__get8(s); - - if (g->lflags & 0x40) { - g->step = 8 * g->line_size; // first interlaced spacing - g->parse = 3; - } else { - g->step = g->line_size; - g->parse = 0; - } - - if (g->lflags & 0x80) { - stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); - g->color_table = (stbi_uc *) g->lpal; - } else if (g->flags & 0x80) { - g->color_table = (stbi_uc *) g->pal; - } else - return stbi__errpuc("missing color table", "Corrupt GIF"); - - o = stbi__process_gif_raster(s, g); - if (!o) return NULL; - - // if this was the first frame, - pcount = g->w * g->h; - if (first_frame && (g->bgindex > 0)) { - // if first frame, any pixel not drawn to gets the background color - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi] == 0) { - g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; - memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); - } - } - } - - return o; - } - - case 0x21: // Comment Extension. - { - int len; - int ext = stbi__get8(s); - if (ext == 0xF9) { // Graphic Control Extension. - len = stbi__get8(s); - if (len == 4) { - g->eflags = stbi__get8(s); - g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. - - // unset old transparent - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 255; - } - if (g->eflags & 0x01) { - g->transparent = stbi__get8(s); - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 0; - } - } else { - // don't need transparent - stbi__skip(s, 1); - g->transparent = -1; - } - } else { - stbi__skip(s, len); - break; - } - } - while ((len = stbi__get8(s)) != 0) { - stbi__skip(s, len); - } - break; - } - - case 0x3B: // gif stream termination code - return (stbi_uc *) s; // using '1' causes warning on some compilers - - default: - return stbi__errpuc("unknown code", "Corrupt GIF"); - } - } -} - -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - if (stbi__gif_test(s)) { - int layers = 0; - stbi_uc *u = 0; - stbi_uc *out = 0; - stbi_uc *two_back = 0; - stbi__gif g; - int stride; - memset(&g, 0, sizeof(g)); - if (delays) { - *delays = 0; - } - - do { - u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - - if (u) { - *x = g.w; - *y = g.h; - ++layers; - stride = g.w * g.h * 4; - - if (out) { - void *tmp = (stbi_uc*) STBI_REALLOC( out, layers * stride ); - if (NULL == tmp) { - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); - return stbi__errpuc("outofmem", "Out of memory"); - } - else { - out = (stbi_uc*) tmp; - } - - if (delays) { - *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); - } - } else { - out = (stbi_uc*)stbi__malloc( layers * stride ); - if (delays) { - *delays = (int*) stbi__malloc( layers * sizeof(int) ); - } - } - memcpy( out + ((layers - 1) * stride), u, stride ); - if (layers >= 2) { - two_back = out - 2 * stride; - } - - if (delays) { - (*delays)[layers - 1U] = g.delay; - } - } - } while (u != 0); - - // free temp buffer; - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); - - // do the final conversion after loading everything; - if (req_comp && req_comp != 4) - out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); - - *z = layers; - return out; - } else { - return stbi__errpuc("not GIF", "Image was not as a gif type."); - } -} - -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *u = 0; - stbi__gif g; - memset(&g, 0, sizeof(g)); - STBI_NOTUSED(ri); - - u = stbi__gif_load_next(s, &g, comp, req_comp, 0); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - if (u) { - *x = g.w; - *y = g.h; - - // moved conversion to after successful load so that the same - // can be done for multiple frames. - if (req_comp && req_comp != 4) - u = stbi__convert_format(u, 4, req_comp, g.w, g.h); - } else if (g.out) { - // if there was an error and we allocated an image buffer, free it! - STBI_FREE(g.out); - } - - // free buffers needed for multiple frame loading; - STBI_FREE(g.history); - STBI_FREE(g.background); - - return u; -} - -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) -{ - return stbi__gif_info_raw(s,x,y,comp); -} -#endif - -// ************************************************************************************************* -// Radiance RGBE HDR loader -// originally by Nicolas Schulz -#ifndef STBI_NO_HDR -static int stbi__hdr_test_core(stbi__context *s, const char *signature) -{ - int i; - for (i=0; signature[i]; ++i) - if (stbi__get8(s) != signature[i]) - return 0; - stbi__rewind(s); - return 1; -} - -static int stbi__hdr_test(stbi__context* s) -{ - int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); - stbi__rewind(s); - if(!r) { - r = stbi__hdr_test_core(s, "#?RGBE\n"); - stbi__rewind(s); - } - return r; -} - -#define STBI__HDR_BUFLEN 1024 -static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) -{ - int len=0; - char c = '\0'; - - c = (char) stbi__get8(z); - - while (!stbi__at_eof(z) && c != '\n') { - buffer[len++] = c; - if (len == STBI__HDR_BUFLEN-1) { - // flush to end of line - while (!stbi__at_eof(z) && stbi__get8(z) != '\n') - ; - break; - } - c = (char) stbi__get8(z); - } - - buffer[len] = 0; - return buffer; -} - -static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) -{ - if ( input[3] != 0 ) { - float f1; - // Exponent - f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); - if (req_comp <= 2) - output[0] = (input[0] + input[1] + input[2]) * f1 / 3; - else { - output[0] = input[0] * f1; - output[1] = input[1] * f1; - output[2] = input[2] * f1; - } - if (req_comp == 2) output[1] = 1; - if (req_comp == 4) output[3] = 1; - } else { - switch (req_comp) { - case 4: output[3] = 1; /* fallthrough */ - case 3: output[0] = output[1] = output[2] = 0; - break; - case 2: output[1] = 1; /* fallthrough */ - case 1: output[0] = 0; - break; - } - } -} - -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int width, height; - stbi_uc *scanline; - float *hdr_data; - int len; - unsigned char count, value; - int i, j, k, c1,c2, z; - const char *headerToken; - STBI_NOTUSED(ri); - - // Check identifier - headerToken = stbi__hdr_gettoken(s,buffer); - if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) - return stbi__errpf("not HDR", "Corrupt HDR image"); - - // Parse header - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); - - // Parse width and height - // can't use sscanf() if we're not using stdio! - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - height = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - width = (int) strtol(token, NULL, 10); - - if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); - if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); - - *x = width; - *y = height; - - if (comp) *comp = 3; - if (req_comp == 0) req_comp = 3; - - if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) - return stbi__errpf("too large", "HDR image is too large"); - - // Read data - hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); - if (!hdr_data) - return stbi__errpf("outofmem", "Out of memory"); - - // Load image data - // image data is stored as some number of sca - if ( width < 8 || width >= 32768) { - // Read flat data - for (j=0; j < height; ++j) { - for (i=0; i < width; ++i) { - stbi_uc rgbe[4]; - main_decode_loop: - stbi__getn(s, rgbe, 4); - stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); - } - } - } else { - // Read RLE-encoded data - scanline = NULL; - - for (j = 0; j < height; ++j) { - c1 = stbi__get8(s); - c2 = stbi__get8(s); - len = stbi__get8(s); - if (c1 != 2 || c2 != 2 || (len & 0x80)) { - // not run-length encoded, so we have to actually use THIS data as a decoded - // pixel (note this can't be a valid pixel--one of RGB must be >= 128) - stbi_uc rgbe[4]; - rgbe[0] = (stbi_uc) c1; - rgbe[1] = (stbi_uc) c2; - rgbe[2] = (stbi_uc) len; - rgbe[3] = (stbi_uc) stbi__get8(s); - stbi__hdr_convert(hdr_data, rgbe, req_comp); - i = 1; - j = 0; - STBI_FREE(scanline); - goto main_decode_loop; // yes, this makes no sense - } - len <<= 8; - len |= stbi__get8(s); - if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } - if (scanline == NULL) { - scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); - if (!scanline) { - STBI_FREE(hdr_data); - return stbi__errpf("outofmem", "Out of memory"); - } - } - - for (k = 0; k < 4; ++k) { - int nleft; - i = 0; - while ((nleft = width - i) > 0) { - count = stbi__get8(s); - if (count > 128) { - // Run - value = stbi__get8(s); - count -= 128; - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = value; - } else { - // Dump - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = stbi__get8(s); - } - } - } - for (i=0; i < width; ++i) - stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); - } - if (scanline) - STBI_FREE(scanline); - } - - return hdr_data; -} - -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int dummy; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - if (stbi__hdr_test(s) == 0) { - stbi__rewind( s ); - return 0; - } - - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) { - stbi__rewind( s ); - return 0; - } - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *y = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *x = (int) strtol(token, NULL, 10); - *comp = 3; - return 1; -} -#endif // STBI_NO_HDR - -#ifndef STBI_NO_BMP -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) -{ - void *p; - stbi__bmp_data info; - - info.all_a = 255; - p = stbi__bmp_parse_header(s, &info); - stbi__rewind( s ); - if (p == NULL) - return 0; - if (x) *x = s->img_x; - if (y) *y = s->img_y; - if (comp) { - if (info.bpp == 24 && info.ma == 0xff000000) - *comp = 3; - else - *comp = info.ma ? 4 : 3; - } - return 1; -} -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) -{ - int channelCount, dummy, depth; - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - *y = stbi__get32be(s); - *x = stbi__get32be(s); - depth = stbi__get16be(s); - if (depth != 8 && depth != 16) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 3) { - stbi__rewind( s ); - return 0; - } - *comp = 4; - return 1; -} - -static int stbi__psd_is16(stbi__context *s) -{ - int channelCount, depth; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - (void) stbi__get32be(s); - (void) stbi__get32be(s); - depth = stbi__get16be(s); - if (depth != 16) { - stbi__rewind( s ); - return 0; - } - return 1; -} -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) -{ - int act_comp=0,num_packets=0,chained,dummy; - stbi__pic_packet packets[10]; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { - stbi__rewind(s); - return 0; - } - - stbi__skip(s, 88); - - *x = stbi__get16be(s); - *y = stbi__get16be(s); - if (stbi__at_eof(s)) { - stbi__rewind( s); - return 0; - } - if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { - stbi__rewind( s ); - return 0; - } - - stbi__skip(s, 8); - - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return 0; - - packet = &packets[num_packets++]; - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - act_comp |= packet->channel; - - if (stbi__at_eof(s)) { - stbi__rewind( s ); - return 0; - } - if (packet->size != 8) { - stbi__rewind( s ); - return 0; - } - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); - - return 1; -} -#endif - -// ************************************************************************************************* -// Portable Gray Map and Portable Pixel Map loader -// by Ken Miller -// -// PGM: http://netpbm.sourceforge.net/doc/pgm.html -// PPM: http://netpbm.sourceforge.net/doc/ppm.html -// -// Known limitations: -// Does not support comments in the header section -// Does not support ASCII image data (formats P2 and P3) -// Does not support 16-bit-per-channel - -#ifndef STBI_NO_PNM - -static int stbi__pnm_test(stbi__context *s) -{ - char p, t; - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind( s ); - return 0; - } - return 1; -} - -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - STBI_NOTUSED(ri); - - if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) - return 0; - - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - - if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) - return stbi__errpuc("too large", "PNM too large"); - - out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - stbi__getn(s, out, s->img_n * s->img_x * s->img_y); - - if (req_comp && req_comp != s->img_n) { - out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - return out; -} - -static int stbi__pnm_isspace(char c) -{ - return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; -} - -static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) -{ - for (;;) { - while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) - *c = (char) stbi__get8(s); - - if (stbi__at_eof(s) || *c != '#') - break; - - while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) - *c = (char) stbi__get8(s); - } -} - -static int stbi__pnm_isdigit(char c) -{ - return c >= '0' && c <= '9'; -} - -static int stbi__pnm_getinteger(stbi__context *s, char *c) -{ - int value = 0; - - while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { - value = value*10 + (*c - '0'); - *c = (char) stbi__get8(s); - } - - return value; -} - -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) -{ - int maxv, dummy; - char c, p, t; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - stbi__rewind(s); - - // Get identifier - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind(s); - return 0; - } - - *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm - - c = (char) stbi__get8(s); - stbi__pnm_skip_whitespace(s, &c); - - *x = stbi__pnm_getinteger(s, &c); // read width - stbi__pnm_skip_whitespace(s, &c); - - *y = stbi__pnm_getinteger(s, &c); // read height - stbi__pnm_skip_whitespace(s, &c); - - maxv = stbi__pnm_getinteger(s, &c); // read max value - - if (maxv > 255) - return stbi__err("max value > 255", "PPM image not 8-bit"); - else - return 1; -} -#endif - -static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) -{ - #ifndef STBI_NO_JPEG - if (stbi__jpeg_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNG - if (stbi__png_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_GIF - if (stbi__gif_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_BMP - if (stbi__bmp_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PSD - if (stbi__psd_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PIC - if (stbi__pic_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNM - if (stbi__pnm_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_info(s, x, y, comp)) return 1; - #endif - - // test tga last because it's a crappy test! - #ifndef STBI_NO_TGA - if (stbi__tga_info(s, x, y, comp)) - return 1; - #endif - return stbi__err("unknown image type", "Image not of any known type, or corrupt"); -} - -static int stbi__is_16_main(stbi__context *s) -{ - #ifndef STBI_NO_PNG - if (stbi__png_is16(s)) return 1; - #endif - - #ifndef STBI_NO_PSD - if (stbi__psd_is16(s)) return 1; - #endif - - return 0; -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_info_from_file(f, x, y, comp); - fclose(f); - return result; -} - -STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__info_main(&s,x,y,comp); - fseek(f,pos,SEEK_SET); - return r; -} - -STBIDEF int stbi_is_16_bit(char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_is_16_bit_from_file(f); - fclose(f); - return result; -} - -STBIDEF int stbi_is_16_bit_from_file(FILE *f) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__is_16_main(&s); - fseek(f,pos,SEEK_SET); - return r; -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__is_16_main(&s); -} - -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__is_16_main(&s); -} - -#endif // STB_IMAGE_IMPLEMENTATION - -/* - revision history: - 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug - 1-bit BMP - *_is_16_bit api - avoid warnings - 2.16 (2017-07-23) all functions have 16-bit variants; - STBI_NO_STDIO works again; - compilation fixes; - fix rounding in unpremultiply; - optimize vertical flip; - disable raw_len validation; - documentation fixes - 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; - warning fixes; disable run-time SSE detection on gcc; - uniform handling of optional "return" values; - thread-safe initialization of zlib tables - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) allocate large structures on the stack - remove white matting for transparent PSD - fix reported channel count for PNG & BMP - re-enable SSE2 in non-gcc 64-bit - support RGB-formatted JPEG - read 16-bit PNGs (only as 8-bit) - 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED - 2.09 (2016-01-16) allow comments in PNM files - 16-bit-per-pixel TGA (not bit-per-component) - info() for TGA could break due to .hdr handling - info() for BMP to shares code instead of sloppy parse - can use STBI_REALLOC_SIZED if allocator doesn't support realloc - code cleanup - 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA - 2.07 (2015-09-13) fix compiler warnings - partial animated GIF support - limited 16-bpc PSD support - #ifdef unused functions - bug with < 92 byte PIC,PNM,HDR,TGA - 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value - 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning - 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit - 2.03 (2015-04-12) extra corruption checking (mmozeiko) - stbi_set_flip_vertically_on_load (nguillemot) - fix NEON support; fix mingw support - 2.02 (2015-01-19) fix incorrect assert, fix warning - 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 - 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG - 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) - progressive JPEG (stb) - PGM/PPM support (Ken Miller) - STBI_MALLOC,STBI_REALLOC,STBI_FREE - GIF bugfix -- seemingly never worked - STBI_NO_*, STBI_ONLY_* - 1.48 (2014-12-14) fix incorrectly-named assert() - 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) - optimize PNG (ryg) - fix bug in interlaced PNG with user-specified channel count (stb) - 1.46 (2014-08-26) - fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG - 1.45 (2014-08-16) - fix MSVC-ARM internal compiler error by wrapping malloc - 1.44 (2014-08-07) - various warning fixes from Ronny Chevalier - 1.43 (2014-07-15) - fix MSVC-only compiler problem in code changed in 1.42 - 1.42 (2014-07-09) - don't define _CRT_SECURE_NO_WARNINGS (affects user code) - fixes to stbi__cleanup_jpeg path - added STBI_ASSERT to avoid requiring assert.h - 1.41 (2014-06-25) - fix search&replace from 1.36 that messed up comments/error messages - 1.40 (2014-06-22) - fix gcc struct-initialization warning - 1.39 (2014-06-15) - fix to TGA optimization when req_comp != number of components in TGA; - fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) - add support for BMP version 5 (more ignored fields) - 1.38 (2014-06-06) - suppress MSVC warnings on integer casts truncating values - fix accidental rename of 'skip' field of I/O - 1.37 (2014-06-04) - remove duplicate typedef - 1.36 (2014-06-03) - convert to header file single-file library - if de-iphone isn't set, load iphone images color-swapped instead of returning NULL - 1.35 (2014-05-27) - various warnings - fix broken STBI_SIMD path - fix bug where stbi_load_from_file no longer left file pointer in correct place - fix broken non-easy path for 32-bit BMP (possibly never used) - TGA optimization by Arseny Kapoulkine - 1.34 (unknown) - use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case - 1.33 (2011-07-14) - make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements - 1.32 (2011-07-13) - support for "info" function for all supported filetypes (SpartanJ) - 1.31 (2011-06-20) - a few more leak fixes, bug in PNG handling (SpartanJ) - 1.30 (2011-06-11) - added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) - removed deprecated format-specific test/load functions - removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway - error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) - fix inefficiency in decoding 32-bit BMP (David Woo) - 1.29 (2010-08-16) - various warning fixes from Aurelien Pocheville - 1.28 (2010-08-01) - fix bug in GIF palette transparency (SpartanJ) - 1.27 (2010-08-01) - cast-to-stbi_uc to fix warnings - 1.26 (2010-07-24) - fix bug in file buffering for PNG reported by SpartanJ - 1.25 (2010-07-17) - refix trans_data warning (Won Chun) - 1.24 (2010-07-12) - perf improvements reading from files on platforms with lock-heavy fgetc() - minor perf improvements for jpeg - deprecated type-specific functions so we'll get feedback if they're needed - attempt to fix trans_data warning (Won Chun) - 1.23 fixed bug in iPhone support - 1.22 (2010-07-10) - removed image *writing* support - stbi_info support from Jetro Lauha - GIF support from Jean-Marc Lienher - iPhone PNG-extensions from James Brown - warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) - 1.21 fix use of 'stbi_uc' in header (reported by jon blow) - 1.20 added support for Softimage PIC, by Tom Seddon - 1.19 bug in interlaced PNG corruption check (found by ryg) - 1.18 (2008-08-02) - fix a threading bug (local mutable static) - 1.17 support interlaced PNG - 1.16 major bugfix - stbi__convert_format converted one too many pixels - 1.15 initialize some fields for thread safety - 1.14 fix threadsafe conversion bug - header-file-only version (#define STBI_HEADER_FILE_ONLY before including) - 1.13 threadsafe - 1.12 const qualifiers in the API - 1.11 Support installable IDCT, colorspace conversion routines - 1.10 Fixes for 64-bit (don't use "unsigned long") - optimized upsampling by Fabian "ryg" Giesen - 1.09 Fix format-conversion for PSD code (bad global variables!) - 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz - 1.07 attempt to fix C++ warning/errors again - 1.06 attempt to fix C++ warning/errors again - 1.05 fix TGA loading to return correct *comp and use good luminance calc - 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free - 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR - 1.02 support for (subset of) HDR files, float interface for preferred access to them - 1.01 fix bug: possible bug in handling right-side up bmps... not sure - fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all - 1.00 interface to zlib that skips zlib header - 0.99 correct handling of alpha in palette - 0.98 TGA loader by lonesock; dynamically add loaders (untested) - 0.97 jpeg errors on too large a file; also catch another malloc failure - 0.96 fix detection of invalid v value - particleman@mollyrocket forum - 0.95 during header scan, seek to markers in case of padding - 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same - 0.93 handle jpegtran output; verbose errors - 0.92 read 4,8,16,24,32-bit BMP files of several formats - 0.91 output 24-bit Windows 3.0 BMP files - 0.90 fix a few more warnings; bump version number to approach 1.0 - 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd - 0.60 fix compiling as c++ - 0.59 fix warnings: merge Dave Moore's -Wall fixes - 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian - 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available - 0.56 fix bug: zlib uncompressed mode len vs. nlen - 0.55 fix bug: restart_interval not initialized to 0 - 0.54 allow NULL for 'int *comp' - 0.53 fix bug in png 3->4; speedup png decoding - 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments - 0.51 obey req_comp requests, 1-component jpegs return as 1-component, - on 'test' only check type, not whether we support this variant - 0.50 (2006-11-19) - first released version -*/ - - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- -*/ diff --git a/contrib/stb_image/stb_image.h b/contrib/stb_image/stb_image.h index d9c21bc81..accef4839 100644 --- a/contrib/stb_image/stb_image.h +++ b/contrib/stb_image/stb_image.h @@ -1,4 +1,4 @@ -/* stb_image - v2.19 - public domain image loader - http://nothings.org/stb +/* stb_image - v2.26 - public domain image loader - http://nothings.org/stb no warranty implied; use at your own risk Do this: @@ -48,6 +48,13 @@ LICENSE RECENT REVISION HISTORY: + 2.26 (2020-07-13) many minor fixes + 2.25 (2020-02-02) fix warnings + 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically + 2.23 (2019-08-11) fix clang static analysis warning + 2.22 (2019-03-04) gif fixes, fix warnings + 2.21 (2019-02-25) fix typo in comment + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs 2.19 (2018-02-11) fix warning 2.18 (2018-01-30) fix warnings 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings @@ -84,23 +91,33 @@ RECENT REVISION HISTORY: Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) Arseny Kapoulkine John-Mark Allen + Carmelo J Fdez-Aguera Bug & warning fixes - Marc LeBlanc David Woo Guillaume George Martins Mozeiko - Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan - Dave Moore Roy Eltham Hayaki Saito Nathan Reed - Won Chun Luke Graham Johan Duparc Nick Verigakis - the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh - Janez Zemva John Bartholomew Michal Cichon github:romigrou - Jonathan Blow Ken Hamada Tero Hanninen github:svdijk - Laurent Gomila Cort Stratton Sergio Gonzalez github:snagar - Aruelien Pocheville Thibault Reuille Cass Everitt github:Zelex - Ryamond Barbiero Paul Du Bois Engin Manap github:grim210 - Aldo Culquicondor Philipp Wiesemann Dale Weiler github:sammyhw - Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus - Julian Raschke Gregory Mullen Baldur Karlsson github:poppolopoppo - Christian Floisand Kevin Schmidt github:darealshinji - Blazej Dariusz Roszkowski github:Michaelangel007 + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski + Phil Jordan Dave Moore Roy Eltham + Hayaki Saito Nathan Reed Won Chun + Luke Graham Johan Duparc Nick Verigakis the Horde3D community + Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Laurent Gomila Cort Stratton github:snagar + Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex + Cass Everitt Ryamond Barbiero github:grim210 + Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw + Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus + Josh Tobin Matthew Gregan github:poppolopoppo + Julian Raschke Gregory Mullen Christian Floisand github:darealshinji + Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 + Brad Weinberger Matvey Cherevko [reserved] + Luca Sas Alexander Veselov Zack Middleton [reserved] + Ryan C. Gordon [reserved] [reserved] + DO NOT ADD YOUR NAME HERE + + To add your name to the credits, pick a random blank space in the middle and fill it. + 80% of merge conflicts on stb PRs are due to people adding their name at the end + of the credits. */ #ifndef STBI_INCLUDE_STB_IMAGE_H @@ -161,6 +178,16 @@ RECENT REVISION HISTORY: // // =========================================================================== // +// UNICODE: +// +// If compiling for Windows and you wish to use Unicode filenames, compile +// with +// #define STBI_WINDOWS_UTF8 +// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert +// Windows wchar_t filenames to utf8. +// +// =========================================================================== +// // Philosophy // // stb libraries are designed with the following priorities: @@ -171,12 +198,12 @@ RECENT REVISION HISTORY: // // Sometimes I let "good performance" creep up in priority over "easy to maintain", // and for best performance I may provide less-easy-to-use APIs that give higher -// performance, in addition to the easy to use ones. Nevertheless, it's important +// performance, in addition to the easy-to-use ones. Nevertheless, it's important // to keep in mind that from the standpoint of you, a client of this library, // all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. // // Some secondary priorities arise directly from the first two, some of which -// make more explicit reasons why performance can't be emphasized. +// provide more explicit reasons why performance can't be emphasized. // // - Portable ("ease of use") // - Small source code footprint ("easy to maintain") @@ -219,11 +246,10 @@ RECENT REVISION HISTORY: // // HDR image support (disable by defining STBI_NO_HDR) // -// stb_image now supports loading HDR images in general, and currently -// the Radiance .HDR file format, although the support is provided -// generically. You can still load any file through the existing interface; -// if you attempt to load an HDR file, it will be automatically remapped to -// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// stb_image supports loading HDR images in general, and currently the Radiance +// .HDR file format specifically. You can still load any file through the existing +// interface; if you attempt to load an HDR file, it will be automatically remapped +// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; // both of these constants can be reconfigured through this interface: // // stbi_hdr_to_ldr_gamma(2.2f); @@ -257,7 +283,7 @@ RECENT REVISION HISTORY: // // By default we convert iphone-formatted PNGs back to RGB, even though // they are internally encoded differently. You can disable this conversion -// by by calling stbi_convert_iphone_png_to_rgb(0), in which case +// by calling stbi_convert_iphone_png_to_rgb(0), in which case // you will always just get the native iphone "format" through (which // is BGR stored in RGB). // @@ -301,7 +327,14 @@ RECENT REVISION HISTORY: // - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still // want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB // - +// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater +// than that size (in either width or height) without further processing. +// This is to let programs in the wild set an upper bound to prevent +// denial-of-service attacks on untrusted data, as one could generate a +// valid image of gigantic dimensions and force stb_image to allocate a +// huge block of memory and spend disproportionate time decoding it. By +// default this is set to (1 << 24), which is 16777216, but that's still +// very big. #ifndef STBI_NO_STDIO #include @@ -319,6 +352,7 @@ enum STBI_rgb_alpha = 4 }; +#include typedef unsigned char stbi_uc; typedef unsigned short stbi_us; @@ -326,11 +360,13 @@ typedef unsigned short stbi_us; extern "C" { #endif +#ifndef STBIDEF #ifdef STB_IMAGE_STATIC #define STBIDEF static #else #define STBIDEF extern #endif +#endif ////////////////////////////////////////////////////////////////////////////// // @@ -355,10 +391,6 @@ typedef struct STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -#endif - #ifndef STBI_NO_STDIO STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); @@ -366,6 +398,14 @@ STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in // for stbi_load_from_file, file pointer is left pointing immediately after image #endif +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + +#ifdef STBI_WINDOWS_UTF8 +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif + //////////////////////////////////// // // 16-bits-per-channel interface @@ -413,7 +453,7 @@ STBIDEF int stbi_is_hdr_from_file(FILE *f); // get a VERY brief reason for failure -// NOT THREADSAFE +// on most compilers (and ALL modern mainstream compilers) this is threadsafe STBIDEF const char *stbi_failure_reason (void); // free the loaded image -- this is just free() @@ -446,6 +486,11 @@ STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); // flip the image vertically, so the first pixel in the output array is the bottom left STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); +// as above, but only applies to images loaded on the thread that calls the function +// this function is only available if your compiler supports thread-local variables; +// calling it will fail to link if your compiler doesn't +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); + // ZLIB client - used by PNG, available for other purposes STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); @@ -525,6 +570,12 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #define STBI_ASSERT(x) assert(x) #endif +#ifdef __cplusplus +#define STBI_EXTERN extern "C" +#else +#define STBI_EXTERN extern +#endif + #ifndef _MSC_VER #ifdef __cplusplus @@ -536,6 +587,23 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #define stbi_inline __forceinline #endif +#ifndef STBI_NO_THREAD_LOCALS + #if defined(__cplusplus) && __cplusplus >= 201103L + #define STBI_THREAD_LOCAL thread_local + #elif defined(__GNUC__) && __GNUC__ < 5 + #define STBI_THREAD_LOCAL __thread + #elif defined(_MSC_VER) + #define STBI_THREAD_LOCAL __declspec(thread) + #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) + #define STBI_THREAD_LOCAL _Thread_local + #endif + + #ifndef STBI_THREAD_LOCAL + #if defined(__GNUC__) + #define STBI_THREAD_LOCAL __thread + #endif + #endif +#endif #ifdef _MSC_VER typedef unsigned short stbi__uint16; @@ -649,14 +717,18 @@ static int stbi__cpuid3(void) #define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) static int stbi__sse2_available(void) { int info3 = stbi__cpuid3(); return ((info3 >> 26) & 1) != 0; } +#endif + #else // assume GCC-style if not VC++ #define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) static int stbi__sse2_available(void) { // If we're even attempting to compile this on GCC/Clang, that means @@ -664,6 +736,8 @@ static int stbi__sse2_available(void) // instructions at will, and so are we. return 1; } +#endif + #endif #endif @@ -682,6 +756,10 @@ static int stbi__sse2_available(void) #define STBI_SIMD_ALIGN(type, name) type name #endif +#ifndef STBI_MAX_DIMENSIONS +#define STBI_MAX_DIMENSIONS (1 << 24) +#endif + /////////////////////////////////////////////// // // stbi__context struct and start_xxx functions @@ -699,6 +777,7 @@ typedef struct int read_from_callbacks; int buflen; stbi_uc buffer_start[128]; + int callback_already_read; stbi_uc *img_buffer, *img_buffer_end; stbi_uc *img_buffer_original, *img_buffer_original_end; @@ -712,6 +791,7 @@ static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) { s->io.read = NULL; s->read_from_callbacks = 0; + s->callback_already_read = 0; s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; } @@ -723,7 +803,8 @@ static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void * s->io_user_data = user; s->buflen = sizeof(s->buffer_start); s->read_from_callbacks = 1; - s->img_buffer_original = s->buffer_start; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = s->buffer_start; stbi__refill_buffer(s); s->img_buffer_original_end = s->img_buffer_end; } @@ -737,12 +818,17 @@ static int stbi__stdio_read(void *user, char *data, int size) static void stbi__stdio_skip(void *user, int n) { + int ch; fseek((FILE*) user, n, SEEK_CUR); + ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ + if (ch != EOF) { + ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ + } } static int stbi__stdio_eof(void *user) { - return feof((FILE*) user); + return feof((FILE*) user) || ferror((FILE *) user); } static stbi_io_callbacks stbi__stdio_callbacks = @@ -840,19 +926,24 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); #endif -// this is not threadsafe -static const char *stbi__g_failure_reason; +static +#ifdef STBI_THREAD_LOCAL +STBI_THREAD_LOCAL +#endif +const char *stbi__g_failure_reason; STBIDEF const char *stbi_failure_reason(void) { return stbi__g_failure_reason; } +#ifndef STBI_NO_FAILURE_STRINGS static int stbi__err(const char *str) { stbi__g_failure_reason = str; return 0; } +#endif static void *stbi__malloc(size_t size) { @@ -891,11 +982,13 @@ static int stbi__mul2sizes_valid(int a, int b) return a <= INT_MAX/b; } +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) // returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow static int stbi__mad2sizes_valid(int a, int b, int add) { return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); } +#endif // returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow static int stbi__mad3sizes_valid(int a, int b, int c, int add) @@ -913,12 +1006,14 @@ static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) } #endif +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) // mallocs with size overflow checking static void *stbi__malloc_mad2(int a, int b, int add) { if (!stbi__mad2sizes_valid(a, b, add)) return NULL; return stbi__malloc(a*b + add); } +#endif static void *stbi__malloc_mad3(int a, int b, int c, int add) { @@ -962,13 +1057,29 @@ static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); #endif -static int stbi__vertically_flip_on_load = 0; +static int stbi__vertically_flip_on_load_global = 0; STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) { - stbi__vertically_flip_on_load = flag_true_if_should_flip; + stbi__vertically_flip_on_load_global = flag_true_if_should_flip; } +#ifndef STBI_THREAD_LOCAL +#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global +#else +static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; + +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_local = flag_true_if_should_flip; + stbi__vertically_flip_on_load_set = 1; +} + +#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ + ? stbi__vertically_flip_on_load_local \ + : stbi__vertically_flip_on_load_global) +#endif // STBI_THREAD_LOCAL + static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) { memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields @@ -990,6 +1101,8 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re #endif #ifndef STBI_NO_PSD if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #else + STBI_NOTUSED(bpc); #endif #ifndef STBI_NO_PIC if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); @@ -1070,6 +1183,7 @@ static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) } } +#ifndef STBI_NO_GIF static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) { int slice; @@ -1077,10 +1191,11 @@ static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int byt stbi_uc *bytes = (stbi_uc *)image; for (slice = 0; slice < z; ++slice) { - stbi__vertical_flip(bytes, w, h, bytes_per_pixel); - bytes += slice_size; + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; } } +#endif static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) { @@ -1090,8 +1205,10 @@ static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, if (result == NULL) return NULL; + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + if (ri.bits_per_channel != 8) { - STBI_ASSERT(ri.bits_per_channel == 16); result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); ri.bits_per_channel = 8; } @@ -1114,8 +1231,10 @@ static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, if (result == NULL) return NULL; + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + if (ri.bits_per_channel != 16) { - STBI_ASSERT(ri.bits_per_channel == 8); result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); ri.bits_per_channel = 16; } @@ -1131,7 +1250,7 @@ static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, return (stbi__uint16 *) result; } -#if !defined(STBI_NO_HDR) || !defined(STBI_NO_LINEAR) +#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) { if (stbi__vertically_flip_on_load && result != NULL) { @@ -1143,10 +1262,38 @@ static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, in #ifndef STBI_NO_STDIO +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); +#endif + +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + static FILE *stbi__fopen(char const *filename, char const *mode) { FILE *f; -#if defined(_MSC_VER) && _MSC_VER >= 1400 +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) + return 0; + +#if _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 if (0 != fopen_s(&f, filename, mode)) f=0; #else @@ -1237,15 +1384,15 @@ STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *u STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { unsigned char *result; - stbi__context s; - stbi__start_mem(&s,buffer,len); - + stbi__context s; + stbi__start_mem(&s,buffer,len); + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); if (stbi__vertically_flip_on_load) { - stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); } - return result; + return result; } #endif @@ -1390,6 +1537,7 @@ enum static void stbi__refill_buffer(stbi__context *s) { int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); if (n == 0) { // at end of file, treat same as if from memory, but need to handle case // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file @@ -1414,6 +1562,9 @@ stbi_inline static stbi_uc stbi__get8(stbi__context *s) return 0; } +#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else stbi_inline static int stbi__at_eof(stbi__context *s) { if (s->io.read) { @@ -1425,9 +1576,14 @@ stbi_inline static int stbi__at_eof(stbi__context *s) return s->img_buffer >= s->img_buffer_end; } +#endif +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) +// nothing +#else static void stbi__skip(stbi__context *s, int n) { + if (n == 0) return; // already there! if (n < 0) { s->img_buffer = s->img_buffer_end; return; @@ -1442,7 +1598,11 @@ static void stbi__skip(stbi__context *s, int n) } s->img_buffer += n; } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) +// nothing +#else static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) { if (s->io.read) { @@ -1466,18 +1626,27 @@ static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) } else return 0; } +#endif +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else static int stbi__get16be(stbi__context *s) { int z = stbi__get8(s); return (z << 8) + stbi__get8(s); } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else static stbi__uint32 stbi__get32be(stbi__context *s) { stbi__uint32 z = stbi__get16be(s); return (z << 16) + stbi__get16be(s); } +#endif #if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) // nothing @@ -1499,7 +1668,9 @@ static stbi__uint32 stbi__get32le(stbi__context *s) #define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings - +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else ////////////////////////////////////////////////////////////////////////////// // // generic converter from built-in img_n to req_comp @@ -1515,7 +1686,11 @@ static stbi_uc stbi__compute_y(int r, int g, int b) { return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) { int i,j; @@ -1539,19 +1714,19 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r // convert source image with img_n components to one with req_comp components; // avoid switch per pixel, so use switch per scanline and massive macros switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0], dest[1]=255; } break; + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; STBI__CASE(2,1) { dest[0]=src[0]; } break; STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; - default: STBI_ASSERT(0); + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); } #undef STBI__CASE } @@ -1559,12 +1734,20 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r STBI_FREE(data); return good; } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else static stbi__uint16 stbi__compute_y_16(int r, int g, int b) { return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) { int i,j; @@ -1588,19 +1771,19 @@ static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int r // convert source image with img_n components to one with req_comp components; // avoid switch per pixel, so use switch per scanline and massive macros switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0], dest[1]=0xffff; } break; + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=0xffff; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; STBI__CASE(2,1) { dest[0]=src[0]; } break; STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=0xffff; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = 0xffff; } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; - default: STBI_ASSERT(0); + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); } #undef STBI__CASE } @@ -1608,6 +1791,7 @@ static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int r STBI_FREE(data); return good; } +#endif #ifndef STBI_NO_LINEAR static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) @@ -1623,7 +1807,11 @@ static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) for (k=0; k < n; ++k) { output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); } - if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + if (n < comp) { + for (i=0; i < x*y; ++i) { + output[i*comp + n] = data[i*comp + n]/255.0f; + } } STBI_FREE(data); return output; @@ -1904,7 +2092,7 @@ stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n) sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB k = stbi_lrot(j->code_buffer, n); - STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); + if (n < 0 || n >= (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))) return 0; j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; j->code_bits -= n; @@ -2015,6 +2203,7 @@ static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__ // first scan for DC coefficient, must be first memset(data,0,64*sizeof(data[0])); // 0 all the ac values now t = stbi__jpeg_huff_decode(j, hdc); + if (t == -1) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); diff = t ? stbi__extend_receive(j, t) : 0; dc = j->img_comp[b].dc_pred + diff; @@ -3005,6 +3194,8 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); c = stbi__get8(s); if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); s->img_n = c; @@ -3596,7 +3787,7 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp int k; unsigned int i,j; stbi_uc *output; - stbi_uc *coutput[4]; + stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; stbi__resample res_comp[4]; @@ -3717,7 +3908,7 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp if (n == 1) for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; else - for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } } } } @@ -3885,16 +4076,23 @@ typedef struct stbi__zhuffman z_length, z_distance; } stbi__zbuf; +stbi_inline static int stbi__zeof(stbi__zbuf *z) +{ + return (z->zbuffer >= z->zbuffer_end); +} + stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) { - if (z->zbuffer >= z->zbuffer_end) return 0; - return *z->zbuffer++; + return stbi__zeof(z) ? 0 : *z->zbuffer++; } static void stbi__fill_bits(stbi__zbuf *z) { do { - STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); + if (z->code_buffer >= (1U << z->num_bits)) { + z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ + return; + } z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; z->num_bits += 8; } while (z->num_bits <= 24); @@ -3919,10 +4117,11 @@ static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) for (s=STBI__ZFAST_BITS+1; ; ++s) if (k < z->maxcode[s]) break; - if (s == 16) return -1; // invalid code! + if (s >= 16) return -1; // invalid code! // code size is s, so: b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - STBI_ASSERT(z->size[b] == s); + if (b >= sizeof (z->size)) return -1; // some data was corrupt somewhere! + if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. a->code_buffer >>= s; a->num_bits -= s; return z->value[b]; @@ -3931,7 +4130,12 @@ static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) { int b,s; - if (a->num_bits < 16) stbi__fill_bits(a); + if (a->num_bits < 16) { + if (stbi__zeof(a)) { + return -1; /* report error for unexpected end of data. */ + } + stbi__fill_bits(a); + } b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; if (b) { s = b >> 9; @@ -3945,13 +4149,16 @@ stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes { char *q; - int cur, limit, old_limit; + unsigned int cur, limit, old_limit; z->zout = zout; if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); - cur = (int) (z->zout - z->zout_start); - limit = old_limit = (int) (z->zout_end - z->zout_start); - while (cur + n > limit) + cur = (unsigned int) (z->zout - z->zout_start); + limit = old_limit = (unsigned) (z->zout_end - z->zout_start); + if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); + while (cur + n > limit) { + if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); limit *= 2; + } q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); STBI_NOTUSED(old_limit); if (q == NULL) return stbi__err("outofmem", "Out of memory"); @@ -4049,11 +4256,12 @@ static int stbi__compute_huffman_codes(stbi__zbuf *a) c = stbi__zreceive(a,2)+3; if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); fill = lencodes[n-1]; - } else if (c == 17) + } else if (c == 17) { c = stbi__zreceive(a,3)+3; - else { - STBI_ASSERT(c == 18); + } else if (c == 18) { c = stbi__zreceive(a,7)+11; + } else { + return stbi__err("bad codelengths", "Corrupt PNG"); } if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); memset(lencodes+n, fill, c); @@ -4079,7 +4287,7 @@ static int stbi__parse_uncompressed_block(stbi__zbuf *a) a->code_buffer >>= 8; a->num_bits -= 8; } - STBI_ASSERT(a->num_bits == 0); + if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); // now fill header the normal way while (k < 4) header[k++] = stbi__zget8(a); @@ -4101,6 +4309,7 @@ static int stbi__parse_zlib_header(stbi__zbuf *a) int cm = cmf & 15; /* int cinfo = cmf >> 4; */ int flg = stbi__zget8(a); + if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png @@ -4362,7 +4571,7 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r return stbi__err("invalid filter","Corrupt PNG"); if (depth < 8) { - STBI_ASSERT(img_width_bytes <= x); + if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place filter_bytes = 1; width = img_width_bytes; @@ -4731,7 +4940,7 @@ static void stbi__de_iphone(stbi__png *z) static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { stbi_uc palette[1024], pal_img_n=0; - stbi_uc has_trans=0, tc[3]; + stbi_uc has_trans=0, tc[3]={0}; stbi__uint16 tc16[3]; stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; int first=1,k,interlace=0, color=0, is_iphone=0; @@ -4757,8 +4966,10 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); first = 0; if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); - s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); - s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + s->img_x = stbi__get32be(s); + s->img_y = stbi__get32be(s); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); @@ -4875,6 +5086,8 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) ++s->img_n; } STBI_FREE(z->expanded); z->expanded = NULL; + // end of PNG chunk, read and skip CRC + stbi__get32be(s); return 1; } @@ -4905,10 +5118,12 @@ static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, st void *result=NULL; if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth < 8) + if (p->depth <= 8) ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; else - ri->bits_per_channel = p->depth; + return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); result = p->out; p->out = NULL; if (req_comp && req_comp != p->s->img_out_n) { @@ -5009,11 +5224,11 @@ static int stbi__high_bit(unsigned int z) { int n=0; if (z == 0) return -1; - if (z >= 0x10000) n += 16, z >>= 16; - if (z >= 0x00100) n += 8, z >>= 8; - if (z >= 0x00010) n += 4, z >>= 4; - if (z >= 0x00004) n += 2, z >>= 2; - if (z >= 0x00002) n += 1, z >>= 1; + if (z >= 0x10000) { n += 16; z >>= 16; } + if (z >= 0x00100) { n += 8; z >>= 8; } + if (z >= 0x00010) { n += 4; z >>= 4; } + if (z >= 0x00004) { n += 2; z >>= 2; } + if (z >= 0x00002) { n += 1;/* >>= 1;*/ } return n; } @@ -5030,7 +5245,7 @@ static int stbi__bitcount(unsigned int a) // extract an arbitrarily-aligned N-bit value (N=bits) // from v, and then make it 8-bits long and fractionally // extend it to full full range. -static int stbi__shiftsigned(int v, int shift, int bits) +static int stbi__shiftsigned(unsigned int v, int shift, int bits) { static unsigned int mul_table[9] = { 0, @@ -5044,7 +5259,7 @@ static int stbi__shiftsigned(int v, int shift, int bits) v <<= -shift; else v >>= shift; - STBI_ASSERT(v >= 0 && v < 256); + STBI_ASSERT(v < 256); v >>= (8-bits); STBI_ASSERT(bits >= 0 && bits <= 8); return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; @@ -5054,6 +5269,7 @@ typedef struct { int bpp, offset, hsz; unsigned int mr,mg,mb,ma, all_a; + int extra_read; } stbi__bmp_data; static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) @@ -5066,6 +5282,9 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) info->offset = stbi__get32le(s); info->hsz = hsz = stbi__get32le(s); info->mr = info->mg = info->mb = info->ma = 0; + info->extra_read = 14; + + if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); if (hsz == 12) { @@ -5109,6 +5328,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) info->mr = stbi__get32le(s); info->mg = stbi__get32le(s); info->mb = stbi__get32le(s); + info->extra_read += 12; // not documented, but generated by photoshop and handled by mspaint if (info->mr == info->mg && info->mg == info->mb) { // ?!?!? @@ -5157,6 +5377,9 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req flip_vertically = ((int) s->img_y) > 0; s->img_y = abs((int) s->img_y); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + mr = info.mr; mg = info.mg; mb = info.mb; @@ -5165,13 +5388,22 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req if (info.hsz == 12) { if (info.bpp < 24) - psize = (info.offset - 14 - 24) / 3; + psize = (info.offset - info.extra_read - 24) / 3; } else { if (info.bpp < 16) - psize = (info.offset - 14 - info.hsz) >> 2; + psize = (info.offset - info.extra_read - info.hsz) >> 2; + } + if (psize == 0) { + STBI_ASSERT(info.offset == s->callback_already_read + (int) (s->img_buffer - s->img_buffer_original)); + if (info.offset != s->callback_already_read + (s->img_buffer - s->buffer_start)) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } } - s->img_n = ma ? 4 : 3; + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 target = req_comp; else @@ -5193,7 +5425,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req if (info.hsz != 12) stbi__get8(s); pal[i][3] = 255; } - stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); if (info.bpp == 1) width = (s->img_x + 7) >> 3; else if (info.bpp == 4) width = (s->img_x + 1) >> 1; else if (info.bpp == 8) width = s->img_x; @@ -5207,6 +5439,8 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req out[z++] = pal[color][0]; out[z++] = pal[color][1]; out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; if((--bit_offset) < 0) { bit_offset = 7; v = stbi__get8(s); @@ -5240,7 +5474,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; int z = 0; int easy=0; - stbi__skip(s, info.offset - 14 - info.hsz); + stbi__skip(s, info.offset - info.extra_read - info.hsz); if (info.bpp == 24) width = 3 * s->img_x; else if (info.bpp == 16) width = 2*s->img_x; else /* bpp = 32 and pad = 0 */ width=0; @@ -5258,6 +5492,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } } for (j=0; j < (int) s->img_y; ++j) { if (easy) { @@ -5299,7 +5534,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req stbi_uc *p1 = out + j *s->img_x*target; stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; for (i=0; i < (int) s->img_x*target; ++i) { - t = p1[i], p1[i] = p2[i], p2[i] = t; + t = p1[i]; p1[i] = p2[i]; p2[i] = t; } } } @@ -5479,6 +5714,11 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req int RLE_repeating = 0; int read_next_pixel = 1; STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO + + if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); // do a tiny bit of precessing if ( tga_image_type >= 8 ) @@ -5519,6 +5759,11 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req // do I need to load a palette? if ( tga_indexed) { + if (tga_palette_len == 0) { /* you have to have at least one entry! */ + STBI_FREE(tga_data); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + // any data to skip? (offset usually = 0) stbi__skip(s, tga_palette_start ); // load the palette @@ -5642,6 +5887,7 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req // Microsoft's C compilers happy... [8^( tga_palette_start = tga_palette_len = tga_palette_bits = tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); // OK, done return tga_data; } @@ -5726,6 +5972,9 @@ static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req h = stbi__get32be(s); w = stbi__get32be(s); + if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + // Make sure the depth is 8 bits. bitdepth = stbi__get16be(s); if (bitdepth != 8 && bitdepth != 16) @@ -5789,7 +6038,7 @@ static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req // Else if n is 128, noop. // Endloop - // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, // which we're going to just skip. stbi__skip(s, h * channelCount * 2 ); @@ -6080,6 +6329,10 @@ static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_c x = stbi__get16be(s); y = stbi__get16be(s); + + if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); @@ -6127,7 +6380,7 @@ typedef struct int w,h; stbi_uc *out; // output buffer (always 4 components) stbi_uc *background; // The current "background" as far as a gif is concerned - stbi_uc *history; + stbi_uc *history; int flags, bgindex, ratio, transparent, eflags; stbi_uc pal[256][4]; stbi_uc lpal[256][4]; @@ -6188,6 +6441,9 @@ static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_in g->ratio = stbi__get8(s); g->transparent = -1; + if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments if (is_info) return 1; @@ -6215,7 +6471,7 @@ static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) { stbi_uc *p, *c; - int idx; + int idx; // recurse to decode the prefixes, since the linked-list is backwards, // and working backwards through an interleaved image would be nasty @@ -6224,12 +6480,12 @@ static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) if (g->cur_y >= g->max_y) return; - idx = g->cur_x + g->cur_y; + idx = g->cur_x + g->cur_y; p = &g->out[idx]; - g->history[idx / 4] = 1; + g->history[idx / 4] = 1; c = &g->color_table[g->codes[code].suffix * 4]; - if (c[3] > 128) { // don't render transparent pixels; + if (c[3] > 128) { // don't render transparent pixels; p[0] = c[2]; p[1] = c[1]; p[2] = c[0]; @@ -6338,31 +6594,36 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) // two back is the image from two frames ago, used for a very specific disposal format static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) { - int dispose; - int first_frame; - int pi; - int pcount; + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); // on first frame, any non-written pixels get the background colour (non-transparent) - first_frame = 0; + first_frame = 0; if (g->out == 0) { - if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header - g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - g->background = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - g->history = (stbi_uc *) stbi__malloc(g->w * g->h); - if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); - // image is treated as "tranparent" at the start - ie, nothing overwrites the current background; + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; // background colour is only used for pixels that are not rendered first frame, after that "background" - // color refers to teh color that was there the previous frame. - memset( g->out, 0x00, 4 * g->w * g->h ); - memset( g->background, 0x00, 4 * g->w * g->h ); // state of the background (starts transparent) - memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame - first_frame = 1; + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; } else { - // second frame - how do we dispoase of the previous one? - dispose = (g->eflags & 0x1C) >> 2; - pcount = g->w * g->h; + // second frame - how do we dispose of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; if ((dispose == 3) && (two_back == 0)) { dispose = 2; // if I don't have an image to revert back to, default to the old background @@ -6371,32 +6632,32 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i if (dispose == 3) { // use previous graphic for (pi = 0; pi < pcount; ++pi) { if (g->history[pi]) { - memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); } } - } else if (dispose == 2) { - // restore what was changed last frame to background before that frame; + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; for (pi = 0; pi < pcount; ++pi) { if (g->history[pi]) { - memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); } } } else { - // This is a non-disposal case eithe way, so just + // This is a non-disposal case eithe way, so just // leave the pixels as is, and they will become the new background // 1: do not dispose // 0: not specified. } - // background is what out is after the undoing of the previou frame; - memcpy( g->background, g->out, 4 * g->w * g->h ); + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); } - // clear my history; + // clear my history; memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame for (;;) { - int tag = stbi__get8(s); + int tag = stbi__get8(s); switch (tag) { case 0x2C: /* Image Descriptor */ { @@ -6418,6 +6679,13 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i g->cur_x = g->start_x; g->cur_y = g->start_y; + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; + g->lflags = stbi__get8(s); if (g->lflags & 0x40) { @@ -6434,19 +6702,19 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i } else if (g->flags & 0x80) { g->color_table = (stbi_uc *) g->pal; } else - return stbi__errpuc("missing color table", "Corrupt GIF"); - - o = stbi__process_gif_raster(s, g); - if (o == NULL) return NULL; + return stbi__errpuc("missing color table", "Corrupt GIF"); - // if this was the first frame, - pcount = g->w * g->h; + o = stbi__process_gif_raster(s, g); + if (!o) return NULL; + + // if this was the first frame, + pcount = g->w * g->h; if (first_frame && (g->bgindex > 0)) { // if first frame, any pixel not drawn to gets the background color for (pi = 0; pi < pcount; ++pi) { if (g->history[pi] == 0) { - g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; - memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); } } } @@ -6457,7 +6725,7 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i case 0x21: // Comment Extension. { int len; - int ext = stbi__get8(s); + int ext = stbi__get8(s); if (ext == 0xF9) { // Graphic Control Extension. len = stbi__get8(s); if (len == 4) { @@ -6466,23 +6734,23 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i // unset old transparent if (g->transparent >= 0) { - g->pal[g->transparent][3] = 255; - } + g->pal[g->transparent][3] = 255; + } if (g->eflags & 0x01) { g->transparent = stbi__get8(s); if (g->transparent >= 0) { - g->pal[g->transparent][3] = 0; + g->pal[g->transparent][3] = 0; } } else { // don't need transparent - stbi__skip(s, 1); - g->transparent = -1; + stbi__skip(s, 1); + g->transparent = -1; } } else { stbi__skip(s, len); break; } - } + } while ((len = stbi__get8(s)) != 0) { stbi__skip(s, len); } @@ -6501,15 +6769,17 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { if (stbi__gif_test(s)) { - int layers = 0; + int layers = 0; stbi_uc *u = 0; stbi_uc *out = 0; - stbi_uc *two_back = 0; + stbi_uc *two_back = 0; stbi__gif g; - int stride; + int stride; + int out_size = 0; + int delays_size = 0; memset(&g, 0, sizeof(g)); if (delays) { - *delays = 0; + *delays = 0; } do { @@ -6519,44 +6789,58 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, if (u) { *x = g.w; *y = g.h; - ++layers; - stride = g.w * g.h * 4; - + ++layers; + stride = g.w * g.h * 4; + if (out) { - out = (stbi_uc*) STBI_REALLOC( out, layers * stride ); + void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); + if (NULL == tmp) { + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + return stbi__errpuc("outofmem", "Out of memory"); + } + else { + out = (stbi_uc*) tmp; + out_size = layers * stride; + } + if (delays) { - *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); + *delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + delays_size = layers * sizeof(int); } } else { - out = (stbi_uc*)stbi__malloc( layers * stride ); + out = (stbi_uc*)stbi__malloc( layers * stride ); + out_size = layers * stride; if (delays) { - *delays = (int*) stbi__malloc( layers * sizeof(int) ); + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + delays_size = layers * sizeof(int); } } - memcpy( out + ((layers - 1) * stride), u, stride ); + memcpy( out + ((layers - 1) * stride), u, stride ); if (layers >= 2) { - two_back = out - 2 * stride; + two_back = out - 2 * stride; } if (delays) { - (*delays)[layers - 1U] = g.delay; + (*delays)[layers - 1U] = g.delay; } } - } while (u != 0); + } while (u != 0); - // free temp buffer; - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); - // do the final conversion after loading everything; + // do the final conversion after loading everything; if (req_comp && req_comp != 4) out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); - *z = layers; + *z = layers; return out; } else { - return stbi__errpuc("not GIF", "Image was not as a gif type."); + return stbi__errpuc("not GIF", "Image was not as a gif type."); } } @@ -6565,6 +6849,7 @@ static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req stbi_uc *u = 0; stbi__gif g; memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); u = stbi__gif_load_next(s, &g, comp, req_comp, 0); if (u == (stbi_uc *) s) u = 0; // end of animated gif marker @@ -6573,14 +6858,17 @@ static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req *y = g.h; // moved conversion to after successful load so that the same - // can be done for multiple frames. + // can be done for multiple frames. if (req_comp && req_comp != 4) u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); } - // free buffers needed for multiple frame loading; + // free buffers needed for multiple frame loading; STBI_FREE(g.history); - STBI_FREE(g.background); + STBI_FREE(g.background); return u; } @@ -6705,6 +6993,9 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re token += 3; width = (int) strtol(token, NULL, 10); + if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + *x = width; *y = height; @@ -6852,7 +7143,12 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) return 0; if (x) *x = s->img_x; if (y) *y = s->img_y; - if (comp) *comp = info.ma ? 4 : 3; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } return 1; } #endif @@ -7014,6 +7310,9 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) return 0; + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + *x = s->img_x; *y = s->img_y; if (comp) *comp = s->img_n; @@ -7238,6 +7537,7 @@ STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user /* revision history: + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs 2.19 (2018-02-11) fix warning 2.18 (2018-01-30) fix warnings 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug From 785cca1bb43f9e6047cb566a910fcd0e3d49951f Mon Sep 17 00:00:00 2001 From: Jason C Date: Wed, 5 May 2021 17:13:10 -0400 Subject: [PATCH 074/335] [amf] Fix crash when file could not be parsed. Fix double free of mXmlParser (deleted but not reset in ParseFile, then deleted again in ~AMFImporter). Should probably use a smart pointer instead, though. Partially addresses https://github.com/assimp/assimp/issues/3888. --- code/AssetLib/AMF/AMFImporter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/AssetLib/AMF/AMFImporter.cpp b/code/AssetLib/AMF/AMFImporter.cpp index 1a3efba9a..add1cdb57 100644 --- a/code/AssetLib/AMF/AMFImporter.cpp +++ b/code/AssetLib/AMF/AMFImporter.cpp @@ -268,6 +268,7 @@ void AMFImporter::ParseFile(const std::string &pFile, IOSystem *pIOHandler) { mXmlParser = new XmlParser(); if (!mXmlParser->parse(file.get())) { delete mXmlParser; + mXmlParser = nullptr; throw DeadlyImportError("Failed to create XML reader for file" + pFile + "."); } From 816da9b6776fdca4dd5af418c552c955be586f9e Mon Sep 17 00:00:00 2001 From: Krishty Date: Wed, 5 May 2021 23:05:44 +0200 Subject: [PATCH 075/335] merge and update all copies of stb_image.h --- code/AssetLib/M3D/M3DWrapper.h | 4 + code/AssetLib/M3D/m3d.h | 1224 ----- code/Common/Assimp.cpp | 33 + code/Pbrt/PbrtExporter.cpp | 3 +- code/Pbrt/stb_image.h | 7756 -------------------------------- contrib/stb_image/stb_image.h | 652 ++- 6 files changed, 514 insertions(+), 9158 deletions(-) delete mode 100644 code/Pbrt/stb_image.h diff --git a/code/AssetLib/M3D/M3DWrapper.h b/code/AssetLib/M3D/M3DWrapper.h index 5c370e607..34bec49b2 100644 --- a/code/AssetLib/M3D/M3DWrapper.h +++ b/code/AssetLib/M3D/M3DWrapper.h @@ -56,6 +56,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //#define ASSIMP_USE_M3D_READFILECB //#define M3D_ASCII +// Share stb_image's PNG loader with other importers/exporters instead of bringing our own copy. +#define STBI_ONLY_PNG +#include + #include "m3d.h" namespace Assimp { diff --git a/code/AssetLib/M3D/m3d.h b/code/AssetLib/M3D/m3d.h index dfc30aec3..379d243a0 100644 --- a/code/AssetLib/M3D/m3d.h +++ b/code/AssetLib/M3D/m3d.h @@ -634,1230 +634,6 @@ static m3dcd_t m3d_commandtypes[] = { #include #include -#if !defined(M3D_NOIMPORTER) && !defined(STBI_INCLUDE_STB_IMAGE_H) -/* PNG decompressor from - - stb_image - v2.23 - public domain image loader - http://nothings.org/stb_image.h -*/ -static const char *_m3dstbi__g_failure_reason; - -enum { - STBI_default = 0, - - STBI_grey = 1, - STBI_grey_alpha = 2, - STBI_rgb = 3, - STBI_rgb_alpha = 4 -}; - -enum { - STBI__SCAN_load = 0, - STBI__SCAN_type, - STBI__SCAN_header -}; - -typedef unsigned short _m3dstbi_us; - -typedef uint16_t _m3dstbi__uint16; -typedef int16_t _m3dstbi__int16; -typedef uint32_t _m3dstbi__uint32; -typedef int32_t _m3dstbi__int32; - -typedef struct -{ - _m3dstbi__uint32 img_x, img_y; - int img_n, img_out_n; - - void *io_user_data; - - int read_from_callbacks; - int buflen; - unsigned char buffer_start[128]; - - unsigned char *img_buffer, *img_buffer_end; - unsigned char *img_buffer_original, *img_buffer_original_end; -} _m3dstbi__context; - -typedef struct -{ - int bits_per_channel; - int num_channels; - int channel_order; -} _m3dstbi__result_info; - -#define STBI_ASSERT(v) -#ifdef _MSC_VER -#define STBI_NOTUSED(v) (void)(v) -#else -#define STBI_NOTUSED(v) (void)sizeof(v) -#endif -#define STBI__BYTECAST(x) ((unsigned char)((x)&255)) -#define STBI_MALLOC(sz) M3D_MALLOC(sz) -#define STBI_REALLOC(p, newsz) M3D_REALLOC(p, newsz) -#define STBI_FREE(p) M3D_FREE(p) -#define STBI_REALLOC_SIZED(p, oldsz, newsz) STBI_REALLOC(p, newsz) - -_inline static unsigned char _m3dstbi__get8(_m3dstbi__context *s) { - if (s->img_buffer < s->img_buffer_end) - return *s->img_buffer++; - return 0; -} - -static void _m3dstbi__skip(_m3dstbi__context *s, int n) { - if (n < 0) { - s->img_buffer = s->img_buffer_end; - return; - } - s->img_buffer += n; -} - -static int _m3dstbi__getn(_m3dstbi__context *s, unsigned char *buffer, int n) { - if (s->img_buffer + n <= s->img_buffer_end) { - memcpy(buffer, s->img_buffer, n); - s->img_buffer += n; - return 1; - } else - return 0; -} - -static int _m3dstbi__get16be(_m3dstbi__context *s) { - int z = _m3dstbi__get8(s); - return (z << 8) + _m3dstbi__get8(s); -} - -static _m3dstbi__uint32 _m3dstbi__get32be(_m3dstbi__context *s) { - _m3dstbi__uint32 z = _m3dstbi__get16be(s); - return (z << 16) + _m3dstbi__get16be(s); -} - -#define _m3dstbi__err(x, y) _m3dstbi__errstr(y) -static int _m3dstbi__errstr(const char *str) { - _m3dstbi__g_failure_reason = str; - return 0; -} - -_inline static void *_m3dstbi__malloc(size_t size) { - return STBI_MALLOC(size); -} - -static int _m3dstbi__addsizes_valid(int a, int b) { - if (b < 0) return 0; - return a <= 2147483647 - b; -} - -static int _m3dstbi__mul2sizes_valid(int a, int b) { - if (a < 0 || b < 0) return 0; - if (b == 0) return 1; - return a <= 2147483647 / b; -} - -static int _m3dstbi__mad2sizes_valid(int a, int b, int add) { - return _m3dstbi__mul2sizes_valid(a, b) && _m3dstbi__addsizes_valid(a * b, add); -} - -static int _m3dstbi__mad3sizes_valid(int a, int b, int c, int add) { - return _m3dstbi__mul2sizes_valid(a, b) && _m3dstbi__mul2sizes_valid(a * b, c) && - _m3dstbi__addsizes_valid(a * b * c, add); -} - -static void *_m3dstbi__malloc_mad2(int a, int b, int add) { - if (!_m3dstbi__mad2sizes_valid(a, b, add)) return NULL; - return _m3dstbi__malloc(a * b + add); -} - -static void *_m3dstbi__malloc_mad3(int a, int b, int c, int add) { - if (!_m3dstbi__mad3sizes_valid(a, b, c, add)) return NULL; - return _m3dstbi__malloc(a * b * c + add); -} - -static unsigned char _m3dstbi__compute_y(int r, int g, int b) { - return (unsigned char)(((r * 77) + (g * 150) + (29 * b)) >> 8); -} - -static unsigned char *_m3dstbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) { - int i, j; - unsigned char *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (unsigned char *)_m3dstbi__malloc_mad3(req_comp, x, y, 0); - if (good == NULL) { - STBI_FREE(data); - _m3dstbi__err("outofmem", "Out of memory"); - return NULL; - } - - for (j = 0; j < (int)y; ++j) { - unsigned char *src = data + j * x * img_n; - unsigned char *dest = good + j * x * req_comp; - -#define STBI__COMBO(a, b) ((a)*8 + (b)) -#define STBI__CASE(a, b) \ - case STBI__COMBO(a, b): \ - for (i = x - 1; i >= 0; --i, src += a, dest += b) - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1, 2) { dest[0] = src[0], dest[1] = 255; } - break; - STBI__CASE(1, 3) { dest[0] = dest[1] = dest[2] = src[0]; } - break; - STBI__CASE(1, 4) { dest[0] = dest[1] = dest[2] = src[0], dest[3] = 255; } - break; - STBI__CASE(2, 1) { dest[0] = src[0]; } - break; - STBI__CASE(2, 3) { dest[0] = dest[1] = dest[2] = src[0]; } - break; - STBI__CASE(2, 4) { dest[0] = dest[1] = dest[2] = src[0], dest[3] = src[1]; } - break; - STBI__CASE(3, 4) { dest[0] = src[0], dest[1] = src[1], dest[2] = src[2], dest[3] = 255; } - break; - STBI__CASE(3, 1) { dest[0] = _m3dstbi__compute_y(src[0], src[1], src[2]); } - break; - STBI__CASE(3, 2) { dest[0] = _m3dstbi__compute_y(src[0], src[1], src[2]), dest[1] = 255; } - break; - STBI__CASE(4, 1) { dest[0] = _m3dstbi__compute_y(src[0], src[1], src[2]); } - break; - STBI__CASE(4, 2) { dest[0] = _m3dstbi__compute_y(src[0], src[1], src[2]), dest[1] = src[3]; } - break; - STBI__CASE(4, 3) { dest[0] = src[0], dest[1] = src[1], dest[2] = src[2]; } - break; - default: STBI_ASSERT(0); - } -#undef STBI__CASE - } - - STBI_FREE(data); - return good; -} - -static _m3dstbi__uint16 _m3dstbi__compute_y_16(int r, int g, int b) { - return (_m3dstbi__uint16)(((r * 77) + (g * 150) + (29 * b)) >> 8); -} - -static _m3dstbi__uint16 *_m3dstbi__convert_format16(_m3dstbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) { - int i, j; - _m3dstbi__uint16 *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (_m3dstbi__uint16 *)_m3dstbi__malloc(req_comp * x * y * 2); - if (good == NULL) { - STBI_FREE(data); - _m3dstbi__err("outofmem", "Out of memory"); - return NULL; - } - - for (j = 0; j < (int)y; ++j) { - _m3dstbi__uint16 *src = data + j * x * img_n; - _m3dstbi__uint16 *dest = good + j * x * req_comp; - -#define STBI__COMBO(a, b) ((a)*8 + (b)) -#define STBI__CASE(a, b) \ - case STBI__COMBO(a, b): \ - for (i = x - 1; i >= 0; --i, src += a, dest += b) - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1, 2) { dest[0] = src[0], dest[1] = 0xffff; } - break; - STBI__CASE(1, 3) { dest[0] = dest[1] = dest[2] = src[0]; } - break; - STBI__CASE(1, 4) { dest[0] = dest[1] = dest[2] = src[0], dest[3] = 0xffff; } - break; - STBI__CASE(2, 1) { dest[0] = src[0]; } - break; - STBI__CASE(2, 3) { dest[0] = dest[1] = dest[2] = src[0]; } - break; - STBI__CASE(2, 4) { dest[0] = dest[1] = dest[2] = src[0], dest[3] = src[1]; } - break; - STBI__CASE(3, 4) { dest[0] = src[0], dest[1] = src[1], dest[2] = src[2], dest[3] = 0xffff; } - break; - STBI__CASE(3, 1) { dest[0] = _m3dstbi__compute_y_16(src[0], src[1], src[2]); } - break; - STBI__CASE(3, 2) { dest[0] = _m3dstbi__compute_y_16(src[0], src[1], src[2]), dest[1] = 0xffff; } - break; - STBI__CASE(4, 1) { dest[0] = _m3dstbi__compute_y_16(src[0], src[1], src[2]); } - break; - STBI__CASE(4, 2) { dest[0] = _m3dstbi__compute_y_16(src[0], src[1], src[2]), dest[1] = src[3]; } - break; - STBI__CASE(4, 3) { dest[0] = src[0], dest[1] = src[1], dest[2] = src[2]; } - break; - default: STBI_ASSERT(0); - } -#undef STBI__CASE - } - - STBI_FREE(data); - return good; -} - -#define STBI__ZFAST_BITS 9 -#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) - -typedef struct -{ - _m3dstbi__uint16 fast[1 << STBI__ZFAST_BITS]; - _m3dstbi__uint16 firstcode[16]; - int maxcode[17]; - _m3dstbi__uint16 firstsymbol[16]; - unsigned char size[288]; - _m3dstbi__uint16 value[288]; -} _m3dstbi__zhuffman; - -_inline static int _m3dstbi__bitreverse16(int n) { - n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); - n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); - n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); - n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); - return n; -} - -_inline static int _m3dstbi__bit_reverse(int v, int bits) { - STBI_ASSERT(bits <= 16); - return _m3dstbi__bitreverse16(v) >> (16 - bits); -} - -static int _m3dstbi__zbuild_huffman(_m3dstbi__zhuffman *z, unsigned char *sizelist, int num) { - int i, k = 0; - int code, next_code[16], sizes[17]; - - memset(sizes, 0, sizeof(sizes)); - memset(z->fast, 0, sizeof(z->fast)); - for (i = 0; i < num; ++i) - ++sizes[sizelist[i]]; - sizes[0] = 0; - for (i = 1; i < 16; ++i) - if (sizes[i] > (1 << i)) - return _m3dstbi__err("bad sizes", "Corrupt PNG"); - code = 0; - for (i = 1; i < 16; ++i) { - next_code[i] = code; - z->firstcode[i] = (_m3dstbi__uint16)code; - z->firstsymbol[i] = (_m3dstbi__uint16)k; - code = (code + sizes[i]); - if (sizes[i]) - if (code - 1 >= (1 << i)) return _m3dstbi__err("bad codelengths", "Corrupt PNG"); - z->maxcode[i] = code << (16 - i); - code <<= 1; - k += sizes[i]; - } - z->maxcode[16] = 0x10000; - for (i = 0; i < num; ++i) { - int s = sizelist[i]; - if (s) { - int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; - _m3dstbi__uint16 fastv = (_m3dstbi__uint16)((s << 9) | i); - z->size[c] = (unsigned char)s; - z->value[c] = (_m3dstbi__uint16)i; - if (s <= STBI__ZFAST_BITS) { - int j = _m3dstbi__bit_reverse(next_code[s], s); - while (j < (1 << STBI__ZFAST_BITS)) { - z->fast[j] = fastv; - j += (1 << s); - } - } - ++next_code[s]; - } - } - return 1; -} - -typedef struct -{ - unsigned char *zbuffer, *zbuffer_end; - int num_bits; - _m3dstbi__uint32 code_buffer; - - char *zout; - char *zout_start; - char *zout_end; - int z_expandable; - - _m3dstbi__zhuffman z_length, z_distance; -} _m3dstbi__zbuf; - -_inline static unsigned char _m3dstbi__zget8(_m3dstbi__zbuf *z) { - if (z->zbuffer >= z->zbuffer_end) return 0; - return *z->zbuffer++; -} - -static void _m3dstbi__fill_bits(_m3dstbi__zbuf *z) { - do { - STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); - z->code_buffer |= (unsigned int)_m3dstbi__zget8(z) << z->num_bits; - z->num_bits += 8; - } while (z->num_bits <= 24); -} - -_inline static unsigned int _m3dstbi__zreceive(_m3dstbi__zbuf *z, int n) { - unsigned int k; - if (z->num_bits < n) _m3dstbi__fill_bits(z); - k = z->code_buffer & ((1 << n) - 1); - z->code_buffer >>= n; - z->num_bits -= n; - return k; -} - -static int _m3dstbi__zhuffman_decode_slowpath(_m3dstbi__zbuf *a, _m3dstbi__zhuffman *z) { - int b, s, k; - k = _m3dstbi__bit_reverse(a->code_buffer, 16); - for (s = STBI__ZFAST_BITS + 1;; ++s) - if (k < z->maxcode[s]) - break; - if (s == 16) return -1; - b = (k >> (16 - s)) - z->firstcode[s] + z->firstsymbol[s]; - STBI_ASSERT(z->size[b] == s); - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; -} - -_inline static int _m3dstbi__zhuffman_decode(_m3dstbi__zbuf *a, _m3dstbi__zhuffman *z) { - int b, s; - if (a->num_bits < 16) _m3dstbi__fill_bits(a); - b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; - if (b) { - s = b >> 9; - a->code_buffer >>= s; - a->num_bits -= s; - return b & 511; - } - return _m3dstbi__zhuffman_decode_slowpath(a, z); -} - -static int _m3dstbi__zexpand(_m3dstbi__zbuf *z, char *zout, int n) { - char *q; - int cur, limit, old_limit; - z->zout = zout; - if (!z->z_expandable) return _m3dstbi__err("output buffer limit", "Corrupt PNG"); - cur = (int)(z->zout - z->zout_start); - limit = old_limit = (int)(z->zout_end - z->zout_start); - while (cur + n > limit) - limit *= 2; - q = (char *)STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); - STBI_NOTUSED(old_limit); - if (q == NULL) return _m3dstbi__err("outofmem", "Out of memory"); - z->zout_start = q; - z->zout = q + cur; - z->zout_end = q + limit; - return 1; -} - -static int _m3dstbi__zlength_base[31] = { - 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, - 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, - 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 -}; - -static int _m3dstbi__zlength_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; - -static int _m3dstbi__zdist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, - 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 }; - -static int _m3dstbi__zdist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; - -static int _m3dstbi__parse_huffman_block(_m3dstbi__zbuf *a) { - char *zout = a->zout; - for (;;) { - int z = _m3dstbi__zhuffman_decode(a, &a->z_length); - if (z < 256) { - if (z < 0) return _m3dstbi__err("bad huffman code", "Corrupt PNG"); - if (zout >= a->zout_end) { - if (!_m3dstbi__zexpand(a, zout, 1)) return 0; - zout = a->zout; - } - *zout++ = (char)z; - } else { - unsigned char *p; - int len, dist; - if (z == 256) { - a->zout = zout; - return 1; - } - z -= 257; - len = _m3dstbi__zlength_base[z]; - if (_m3dstbi__zlength_extra[z]) len += _m3dstbi__zreceive(a, _m3dstbi__zlength_extra[z]); - z = _m3dstbi__zhuffman_decode(a, &a->z_distance); - if (z < 0) return _m3dstbi__err("bad huffman code", "Corrupt PNG"); - dist = _m3dstbi__zdist_base[z]; - if (_m3dstbi__zdist_extra[z]) dist += _m3dstbi__zreceive(a, _m3dstbi__zdist_extra[z]); - if (zout - a->zout_start < dist) return _m3dstbi__err("bad dist", "Corrupt PNG"); - if (zout + len > a->zout_end) { - if (!_m3dstbi__zexpand(a, zout, len)) return 0; - zout = a->zout; - } - p = (unsigned char *)(zout - dist); - if (dist == 1) { - unsigned char v = *p; - if (len) { - do - *zout++ = v; - while (--len); - } - } else { - if (len) { - do - *zout++ = *p++; - while (--len); - } - } - } - } -} - -static int _m3dstbi__compute_huffman_codes(_m3dstbi__zbuf *a) { - static unsigned char length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; - _m3dstbi__zhuffman z_codelength; - unsigned char lencodes[286 + 32 + 137]; - unsigned char codelength_sizes[19]; - int i, n; - - int hlit = _m3dstbi__zreceive(a, 5) + 257; - int hdist = _m3dstbi__zreceive(a, 5) + 1; - int hclen = _m3dstbi__zreceive(a, 4) + 4; - int ntot = hlit + hdist; - - memset(codelength_sizes, 0, sizeof(codelength_sizes)); - for (i = 0; i < hclen; ++i) { - int s = _m3dstbi__zreceive(a, 3); - codelength_sizes[length_dezigzag[i]] = (unsigned char)s; - } - if (!_m3dstbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; - - n = 0; - while (n < ntot) { - int c = _m3dstbi__zhuffman_decode(a, &z_codelength); - if (c < 0 || c >= 19) return _m3dstbi__err("bad codelengths", "Corrupt PNG"); - if (c < 16) - lencodes[n++] = (unsigned char)c; - else { - unsigned char fill = 0; - if (c == 16) { - c = _m3dstbi__zreceive(a, 2) + 3; - if (n == 0) return _m3dstbi__err("bad codelengths", "Corrupt PNG"); - fill = lencodes[n - 1]; - } else if (c == 17) - c = _m3dstbi__zreceive(a, 3) + 3; - else { - STBI_ASSERT(c == 18); - c = _m3dstbi__zreceive(a, 7) + 11; - } - if (ntot - n < c) return _m3dstbi__err("bad codelengths", "Corrupt PNG"); - memset(lencodes + n, fill, c); - n += c; - } - } - if (n != ntot) return _m3dstbi__err("bad codelengths", "Corrupt PNG"); - if (!_m3dstbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; - if (!_m3dstbi__zbuild_huffman(&a->z_distance, lencodes + hlit, hdist)) return 0; - return 1; -} - -_inline static int _m3dstbi__parse_uncompressed_block(_m3dstbi__zbuf *a) { - unsigned char header[4]; - int len, nlen, k; - if (a->num_bits & 7) - _m3dstbi__zreceive(a, a->num_bits & 7); - k = 0; - while (a->num_bits > 0) { - header[k++] = (unsigned char)(a->code_buffer & 255); - a->code_buffer >>= 8; - a->num_bits -= 8; - } - STBI_ASSERT(a->num_bits == 0); - while (k < 4) - header[k++] = _m3dstbi__zget8(a); - len = header[1] * 256 + header[0]; - nlen = header[3] * 256 + header[2]; - if (nlen != (len ^ 0xffff)) return _m3dstbi__err("zlib corrupt", "Corrupt PNG"); - if (a->zbuffer + len > a->zbuffer_end) return _m3dstbi__err("read past buffer", "Corrupt PNG"); - if (a->zout + len > a->zout_end) - if (!_m3dstbi__zexpand(a, a->zout, len)) return 0; - memcpy(a->zout, a->zbuffer, len); - a->zbuffer += len; - a->zout += len; - return 1; -} - -static int _m3dstbi__parse_zlib_header(_m3dstbi__zbuf *a) { - int cmf = _m3dstbi__zget8(a); - int cm = cmf & 15; - /* int cinfo = cmf >> 4; */ - int flg = _m3dstbi__zget8(a); - if ((cmf * 256 + flg) % 31 != 0) return _m3dstbi__err("bad zlib header", "Corrupt PNG"); - if (flg & 32) return _m3dstbi__err("no preset dict", "Corrupt PNG"); - if (cm != 8) return _m3dstbi__err("bad compression", "Corrupt PNG"); - return 1; -} - -static unsigned char _m3dstbi__zdefault_length[288], _m3dstbi__zdefault_distance[32]; -static void _m3dstbi__init_zdefaults(void) { - int i; - for (i = 0; i <= 143; ++i) - _m3dstbi__zdefault_length[i] = 8; - for (; i <= 255; ++i) - _m3dstbi__zdefault_length[i] = 9; - for (; i <= 279; ++i) - _m3dstbi__zdefault_length[i] = 7; - for (; i <= 287; ++i) - _m3dstbi__zdefault_length[i] = 8; - - for (i = 0; i <= 31; ++i) - _m3dstbi__zdefault_distance[i] = 5; -} - -static int _m3dstbi__parse_zlib(_m3dstbi__zbuf *a, int parse_header) { - int final, type; - if (parse_header) - if (!_m3dstbi__parse_zlib_header(a)) return 0; - a->num_bits = 0; - a->code_buffer = 0; - do { - final = _m3dstbi__zreceive(a, 1); - type = _m3dstbi__zreceive(a, 2); - if (type == 0) { - if (!_m3dstbi__parse_uncompressed_block(a)) return 0; - } else if (type == 3) { - return 0; - } else { - if (type == 1) { - if (!_m3dstbi__zbuild_huffman(&a->z_length, _m3dstbi__zdefault_length, 288)) return 0; - if (!_m3dstbi__zbuild_huffman(&a->z_distance, _m3dstbi__zdefault_distance, 32)) return 0; - } else { - if (!_m3dstbi__compute_huffman_codes(a)) return 0; - } - if (!_m3dstbi__parse_huffman_block(a)) return 0; - } - } while (!final); - return 1; -} - -static int _m3dstbi__do_zlib(_m3dstbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) { - a->zout_start = obuf; - a->zout = obuf; - a->zout_end = obuf + olen; - a->z_expandable = exp; - _m3dstbi__init_zdefaults(); - return _m3dstbi__parse_zlib(a, parse_header); -} - -char *_m3dstbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) { - _m3dstbi__zbuf a; - char *p = (char *)_m3dstbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (unsigned char *)buffer; - a.zbuffer_end = (unsigned char *)buffer + len; - if (_m3dstbi__do_zlib(&a, p, initial_size, 1, parse_header)) { - if (outlen) *outlen = (int)(a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -typedef struct -{ - _m3dstbi__uint32 length; - _m3dstbi__uint32 type; -} _m3dstbi__pngchunk; - -static _m3dstbi__pngchunk _m3dstbi__get_chunk_header(_m3dstbi__context *s) { - _m3dstbi__pngchunk c; - c.length = _m3dstbi__get32be(s); - c.type = _m3dstbi__get32be(s); - return c; -} - -_inline static int _m3dstbi__check_png_header(_m3dstbi__context *s) { - static unsigned char png_sig[8] = { 137, 80, 78, 71, 13, 10, 26, 10 }; - int i; - for (i = 0; i < 8; ++i) - if (_m3dstbi__get8(s) != png_sig[i]) return _m3dstbi__err("bad png sig", "Not a PNG"); - return 1; -} - -typedef struct -{ - _m3dstbi__context *s; - unsigned char *idata, *expanded, *out; - int depth; -} _m3dstbi__png; - -enum { - STBI__F_none = 0, - STBI__F_sub = 1, - STBI__F_up = 2, - STBI__F_avg = 3, - STBI__F_paeth = 4, - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static unsigned char first_row_filter[5] = { - STBI__F_none, - STBI__F_sub, - STBI__F_none, - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static int _m3dstbi__paeth(int a, int b, int c) { - int p = a + b - c; - int pa = abs(p - a); - int pb = abs(p - b); - int pc = abs(p - c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; -} - -static unsigned char _m3dstbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0, 0, 0, 0x01 }; - -static int _m3dstbi__create_png_image_raw(_m3dstbi__png *a, unsigned char *raw, _m3dstbi__uint32 raw_len, int out_n, _m3dstbi__uint32 x, _m3dstbi__uint32 y, int depth, int color) { - int bytes = (depth == 16 ? 2 : 1); - _m3dstbi__context *s = a->s; - _m3dstbi__uint32 i, j, stride = x * out_n * bytes; - _m3dstbi__uint32 img_len, img_width_bytes; - int k; - int img_n = s->img_n; - - int output_bytes = out_n * bytes; - int filter_bytes = img_n * bytes; - int width = x; - - STBI_ASSERT(out_n == s->img_n || out_n == s->img_n + 1); - a->out = (unsigned char *)_m3dstbi__malloc_mad3(x, y, output_bytes, 0); - if (!a->out) return _m3dstbi__err("outofmem", "Out of memory"); - - if (!_m3dstbi__mad3sizes_valid(img_n, x, depth, 7)) return _m3dstbi__err("too large", "Corrupt PNG"); - img_width_bytes = (((img_n * x * depth) + 7) >> 3); - img_len = (img_width_bytes + 1) * y; - if (s->img_x == x && s->img_y == y) { - if (raw_len != img_len) return _m3dstbi__err("not enough pixels", "Corrupt PNG"); - } else { - if (raw_len < img_len) return _m3dstbi__err("not enough pixels", "Corrupt PNG"); - } - - for (j = 0; j < y; ++j) { - unsigned char *cur = a->out + stride * j; - unsigned char *prior = cur - stride; - int filter = *raw++; - - if (filter > 4) - return _m3dstbi__err("invalid filter", "Corrupt PNG"); - - if (depth < 8) { - STBI_ASSERT(img_width_bytes <= x); - cur += x * out_n - img_width_bytes; - filter_bytes = 1; - width = img_width_bytes; - } - prior = cur - stride; - - if (j == 0) filter = first_row_filter[filter]; - - for (k = 0; k < filter_bytes; ++k) { - switch (filter) { - case STBI__F_none: cur[k] = raw[k]; break; - case STBI__F_sub: cur[k] = raw[k]; break; - case STBI__F_up: cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - case STBI__F_avg: cur[k] = STBI__BYTECAST(raw[k] + (prior[k] >> 1)); break; - case STBI__F_paeth: cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(0, prior[k], 0)); break; - case STBI__F_avg_first: cur[k] = raw[k]; break; - case STBI__F_paeth_first: cur[k] = raw[k]; break; - } - } - - if (depth == 8) { - if (img_n != out_n) - cur[img_n] = 255; - raw += img_n; - cur += out_n; - prior += out_n; - } else if (depth == 16) { - if (img_n != out_n) { - cur[filter_bytes] = 255; - cur[filter_bytes + 1] = 255; - } - raw += filter_bytes; - cur += output_bytes; - prior += output_bytes; - } else { - raw += 1; - cur += 1; - prior += 1; - } - - if (depth < 8 || img_n == out_n) { - int nk = (width - 1) * filter_bytes; -#define STBI__CASE(f) \ - case f: \ - for (k = 0; k < nk; ++k) - switch (filter) { - case STBI__F_none: - memcpy(cur, raw, nk); - break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k - filter_bytes]); } - break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } - break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k - filter_bytes]) >> 1)); } - break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(cur[k - filter_bytes], prior[k], prior[k - filter_bytes])); } - break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k - filter_bytes] >> 1)); } - break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(cur[k - filter_bytes], 0, 0)); } - break; - } -#undef STBI__CASE - raw += nk; - } else { - STBI_ASSERT(img_n + 1 == out_n); -#define STBI__CASE(f) \ - case f: \ - for (i = x - 1; i >= 1; --i, cur[filter_bytes] = 255, raw += filter_bytes, cur += output_bytes, prior += output_bytes) \ - for (k = 0; k < filter_bytes; ++k) - switch (filter) { - STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } - break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k - output_bytes]); } - break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } - break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k - output_bytes]) >> 1)); } - break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(cur[k - output_bytes], prior[k], prior[k - output_bytes])); } - break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k - output_bytes] >> 1)); } - break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(cur[k - output_bytes], 0, 0)); } - break; - } -#undef STBI__CASE - - if (depth == 16) { - cur = a->out + stride * j; - for (i = 0; i < x; ++i, cur += output_bytes) { - cur[filter_bytes + 1] = 255; - } - } - } - } - - if (depth < 8) { - for (j = 0; j < y; ++j) { - unsigned char *cur = a->out + stride * j; - unsigned char *in = a->out + stride * j + x * out_n - img_width_bytes; - unsigned char scale = (color == 0) ? _m3dstbi__depth_scale_table[depth] : 1; - - if (depth == 4) { - for (k = x * img_n; k >= 2; k -= 2, ++in) { - *cur++ = scale * ((*in >> 4)); - *cur++ = scale * ((*in) & 0x0f); - } - if (k > 0) *cur++ = scale * ((*in >> 4)); - } else if (depth == 2) { - for (k = x * img_n; k >= 4; k -= 4, ++in) { - *cur++ = scale * ((*in >> 6)); - *cur++ = scale * ((*in >> 4) & 0x03); - *cur++ = scale * ((*in >> 2) & 0x03); - *cur++ = scale * ((*in) & 0x03); - } - if (k > 0) *cur++ = scale * ((*in >> 6)); - if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); - if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); - } else if (depth == 1) { - for (k = x * img_n; k >= 8; k -= 8, ++in) { - *cur++ = scale * ((*in >> 7)); - *cur++ = scale * ((*in >> 6) & 0x01); - *cur++ = scale * ((*in >> 5) & 0x01); - *cur++ = scale * ((*in >> 4) & 0x01); - *cur++ = scale * ((*in >> 3) & 0x01); - *cur++ = scale * ((*in >> 2) & 0x01); - *cur++ = scale * ((*in >> 1) & 0x01); - *cur++ = scale * ((*in) & 0x01); - } - if (k > 0) *cur++ = scale * ((*in >> 7)); - if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); - if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); - if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); - if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); - if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); - if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); - } - if (img_n != out_n) { - int q; - cur = a->out + stride * j; - if (img_n == 1) { - for (q = x - 1; q >= 0; --q) { - cur[q * 2 + 1] = 255; - cur[q * 2 + 0] = cur[q]; - } - } else { - STBI_ASSERT(img_n == 3); - for (q = x - 1; q >= 0; --q) { - cur[q * 4 + 3] = 255; - cur[q * 4 + 2] = cur[q * 3 + 2]; - cur[q * 4 + 1] = cur[q * 3 + 1]; - cur[q * 4 + 0] = cur[q * 3 + 0]; - } - } - } - } - } else if (depth == 16) { - unsigned char *cur = a->out; - _m3dstbi__uint16 *cur16 = (_m3dstbi__uint16 *)cur; - - for (i = 0; i < x * y * out_n; ++i, cur16++, cur += 2) { - *cur16 = (cur[0] << 8) | cur[1]; - } - } - - return 1; -} - -static int _m3dstbi__create_png_image(_m3dstbi__png *a, unsigned char *image_data, _m3dstbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) { - int bytes = (depth == 16 ? 2 : 1); - int out_bytes = out_n * bytes; - unsigned char *final; - int p; - if (!interlaced) - return _m3dstbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); - - final = (unsigned char *)_m3dstbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); - for (p = 0; p < 7; ++p) { - int xorig[] = { 0, 4, 0, 2, 0, 1, 0 }; - int yorig[] = { 0, 0, 4, 0, 2, 0, 1 }; - int xspc[] = { 8, 8, 4, 4, 2, 2, 1 }; - int yspc[] = { 8, 8, 8, 4, 4, 2, 2 }; - int i, j, x, y; - x = (a->s->img_x - xorig[p] + xspc[p] - 1) / xspc[p]; - y = (a->s->img_y - yorig[p] + yspc[p] - 1) / yspc[p]; - if (x && y) { - _m3dstbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; - if (!_m3dstbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { - STBI_FREE(final); - return 0; - } - for (j = 0; j < y; ++j) { - for (i = 0; i < x; ++i) { - int out_y = j * yspc[p] + yorig[p]; - int out_x = i * xspc[p] + xorig[p]; - memcpy(final + out_y * a->s->img_x * out_bytes + out_x * out_bytes, - a->out + (j * x + i) * out_bytes, out_bytes); - } - } - STBI_FREE(a->out); - image_data += img_len; - image_data_len -= img_len; - } - } - a->out = final; - - return 1; -} - -static int _m3dstbi__compute_transparency(_m3dstbi__png *z, unsigned char* tc, int out_n) { - _m3dstbi__context *s = z->s; - _m3dstbi__uint32 i, pixel_count = s->img_x * s->img_y; - unsigned char *p = z->out; - - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i = 0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 255); - p += 2; - } - } else { - for (i = 0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int _m3dstbi__compute_transparency16(_m3dstbi__png *z, _m3dstbi__uint16 tc[3], int out_n) { - _m3dstbi__context *s = z->s; - _m3dstbi__uint32 i, pixel_count = s->img_x * s->img_y; - _m3dstbi__uint16 *p = (_m3dstbi__uint16 *)z->out; - - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i = 0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 65535); - p += 2; - } - } else { - for (i = 0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int _m3dstbi__expand_png_palette(_m3dstbi__png *a, unsigned char *palette, int len, int pal_img_n) { - _m3dstbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; - unsigned char *p, *temp_out, *orig = a->out; - - p = (unsigned char *)_m3dstbi__malloc_mad2(pixel_count, pal_img_n, 0); - if (p == NULL) return _m3dstbi__err("outofmem", "Out of memory"); - - temp_out = p; - - if (pal_img_n == 3) { - for (i = 0; i < pixel_count; ++i) { - int n = orig[i] * 4; - p[0] = palette[n]; - p[1] = palette[n + 1]; - p[2] = palette[n + 2]; - p += 3; - } - } else { - for (i = 0; i < pixel_count; ++i) { - int n = orig[i] * 4; - p[0] = palette[n]; - p[1] = palette[n + 1]; - p[2] = palette[n + 2]; - p[3] = palette[n + 3]; - p += 4; - } - } - STBI_FREE(a->out); - a->out = temp_out; - - STBI_NOTUSED(len); - - return 1; -} - -#define STBI__PNG_TYPE(a, b, c, d) (((unsigned)(a) << 24) + ((unsigned)(b) << 16) + ((unsigned)(c) << 8) + (unsigned)(d)) - -static int _m3dstbi__parse_png_file(_m3dstbi__png *z, int scan, int req_comp) { - unsigned char palette[1024], pal_img_n = 0; - unsigned char has_trans = 0, tc[3] = {}; - _m3dstbi__uint16 tc16[3] = {}; - _m3dstbi__uint32 ioff = 0, idata_limit = 0, i, pal_len = 0; - int first = 1, k, interlace = 0, color = 0; - _m3dstbi__context *s = z->s; - - z->expanded = NULL; - z->idata = NULL; - z->out = NULL; - - if (!_m3dstbi__check_png_header(s)) return 0; - - if (scan == STBI__SCAN_type) return 1; - - for (;;) { - _m3dstbi__pngchunk c = _m3dstbi__get_chunk_header(s); - switch (c.type) { - case STBI__PNG_TYPE('C', 'g', 'B', 'I'): - _m3dstbi__skip(s, c.length); - break; - case STBI__PNG_TYPE('I', 'H', 'D', 'R'): { - int comp, filter; - if (!first) return _m3dstbi__err("multiple IHDR", "Corrupt PNG"); - first = 0; - if (c.length != 13) return _m3dstbi__err("bad IHDR len", "Corrupt PNG"); - s->img_x = _m3dstbi__get32be(s); - if (s->img_x > (1 << 24)) return _m3dstbi__err("too large", "Very large image (corrupt?)"); - s->img_y = _m3dstbi__get32be(s); - if (s->img_y > (1 << 24)) return _m3dstbi__err("too large", "Very large image (corrupt?)"); - z->depth = _m3dstbi__get8(s); - if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return _m3dstbi__err("1/2/4/8/16-bit only", "PNG not supported: 1/2/4/8/16-bit only"); - color = _m3dstbi__get8(s); - if (color > 6) return _m3dstbi__err("bad ctype", "Corrupt PNG"); - if (color == 3 && z->depth == 16) return _m3dstbi__err("bad ctype", "Corrupt PNG"); - if (color == 3) - pal_img_n = 3; - else if (color & 1) - return _m3dstbi__err("bad ctype", "Corrupt PNG"); - comp = _m3dstbi__get8(s); - if (comp) return _m3dstbi__err("bad comp method", "Corrupt PNG"); - filter = _m3dstbi__get8(s); - if (filter) return _m3dstbi__err("bad filter method", "Corrupt PNG"); - interlace = _m3dstbi__get8(s); - if (interlace > 1) return _m3dstbi__err("bad interlace method", "Corrupt PNG"); - if (!s->img_x || !s->img_y) return _m3dstbi__err("0-pixel image", "Corrupt PNG"); - if (!pal_img_n) { - s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return _m3dstbi__err("too large", "Image too large to decode"); - if (scan == STBI__SCAN_header) return 1; - } else { - s->img_n = 1; - if ((1 << 30) / s->img_x / 4 < s->img_y) return _m3dstbi__err("too large", "Corrupt PNG"); - } - break; - } - - case STBI__PNG_TYPE('P', 'L', 'T', 'E'): { - if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); - if (c.length > 256 * 3) return _m3dstbi__err("invalid PLTE", "Corrupt PNG"); - pal_len = c.length / 3; - if (pal_len * 3 != c.length) return _m3dstbi__err("invalid PLTE", "Corrupt PNG"); - for (i = 0; i < pal_len; ++i) { - palette[i * 4 + 0] = _m3dstbi__get8(s); - palette[i * 4 + 1] = _m3dstbi__get8(s); - palette[i * 4 + 2] = _m3dstbi__get8(s); - palette[i * 4 + 3] = 255; - } - break; - } - - case STBI__PNG_TYPE('t', 'R', 'N', 'S'): { - if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); - if (z->idata) return _m3dstbi__err("tRNS after IDAT", "Corrupt PNG"); - if (pal_img_n) { - if (scan == STBI__SCAN_header) { - s->img_n = 4; - return 1; - } - if (pal_len == 0) return _m3dstbi__err("tRNS before PLTE", "Corrupt PNG"); - if (c.length > pal_len) return _m3dstbi__err("bad tRNS len", "Corrupt PNG"); - pal_img_n = 4; - for (i = 0; i < c.length; ++i) - palette[i * 4 + 3] = _m3dstbi__get8(s); - } else { - if (!(s->img_n & 1)) return _m3dstbi__err("tRNS with alpha", "Corrupt PNG"); - if (c.length != (_m3dstbi__uint32)s->img_n * 2) return _m3dstbi__err("bad tRNS len", "Corrupt PNG"); - has_trans = 1; - if (z->depth == 16) { - for (k = 0; k < s->img_n; ++k) - tc16[k] = (_m3dstbi__uint16)_m3dstbi__get16be(s); - } else { - for (k = 0; k < s->img_n; ++k) - tc[k] = (unsigned char)(_m3dstbi__get16be(s) & 255) * _m3dstbi__depth_scale_table[z->depth]; - } - } - break; - } - - case STBI__PNG_TYPE('I', 'D', 'A', 'T'): { - if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); - if (pal_img_n && !pal_len) return _m3dstbi__err("no PLTE", "Corrupt PNG"); - if (scan == STBI__SCAN_header) { - s->img_n = pal_img_n; - return 1; - } - if ((int)(ioff + c.length) < (int)ioff) return 0; - if (ioff + c.length > idata_limit) { - _m3dstbi__uint32 idata_limit_old = idata_limit; - unsigned char *p; - if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; - while (ioff + c.length > idata_limit) - idata_limit *= 2; - STBI_NOTUSED(idata_limit_old); - p = (unsigned char *)STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); - if (p == NULL) return _m3dstbi__err("outofmem", "Out of memory"); - z->idata = p; - } - if (!_m3dstbi__getn(s, z->idata + ioff, c.length)) return _m3dstbi__err("outofdata", "Corrupt PNG"); - ioff += c.length; - break; - } - - case STBI__PNG_TYPE('I', 'E', 'N', 'D'): { - _m3dstbi__uint32 raw_len, bpl; - if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); - if (scan != STBI__SCAN_load) return 1; - if (z->idata == NULL) return _m3dstbi__err("no IDAT", "Corrupt PNG"); - bpl = (s->img_x * z->depth + 7) / 8; - raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; - z->expanded = (unsigned char *)_m3dstbi_zlib_decode_malloc_guesssize_headerflag((char *)z->idata, ioff, raw_len, (int *)&raw_len, 1); - if (z->expanded == NULL) return 0; - STBI_FREE(z->idata); - z->idata = NULL; - if ((req_comp == s->img_n + 1 && req_comp != 3 && !pal_img_n) || has_trans) - s->img_out_n = s->img_n + 1; - else - s->img_out_n = s->img_n; - if (!_m3dstbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; - if (has_trans) { - if (z->depth == 16) { - if (!_m3dstbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; - } else { - if (!_m3dstbi__compute_transparency(z, tc, s->img_out_n)) return 0; - } - } - if (pal_img_n) { - s->img_n = pal_img_n; - s->img_out_n = pal_img_n; - if (req_comp >= 3) s->img_out_n = req_comp; - if (!_m3dstbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) - return 0; - } else if (has_trans) { - ++s->img_n; - } - STBI_FREE(z->expanded); - z->expanded = NULL; - return 1; - } - - default: - if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); - if ((c.type & (1 << 29)) == 0) { - return _m3dstbi__err("invalid_chunk", "PNG not supported: unknown PNG chunk type"); - } - _m3dstbi__skip(s, c.length); - break; - } - _m3dstbi__get32be(s); - } -} - -static void *_m3dstbi__do_png(_m3dstbi__png *p, int *x, int *y, int *n, int req_comp, _m3dstbi__result_info *ri) { - void *result = NULL; - if (req_comp < 0 || req_comp > 4) { - _m3dstbi__err("bad req_comp", "Internal error"); - return NULL; - } - if (_m3dstbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth < 8) - ri->bits_per_channel = 8; - else - ri->bits_per_channel = p->depth; - result = p->out; - p->out = NULL; - if (req_comp && req_comp != p->s->img_out_n) { - if (ri->bits_per_channel == 8) - result = _m3dstbi__convert_format((unsigned char *)result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - else - result = _m3dstbi__convert_format16((_m3dstbi__uint16 *)result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - p->s->img_out_n = req_comp; - if (result == NULL) return result; - } - *x = p->s->img_x; - *y = p->s->img_y; - if (n) *n = p->s->img_n; - } - STBI_FREE(p->out); - p->out = NULL; - STBI_FREE(p->expanded); - p->expanded = NULL; - STBI_FREE(p->idata); - p->idata = NULL; - - return result; -} - -static void *_m3dstbi__png_load(_m3dstbi__context *s, int *x, int *y, int *comp, int req_comp, _m3dstbi__result_info *ri) { - _m3dstbi__png p; - p.s = s; - return _m3dstbi__do_png(&p, x, y, comp, req_comp, ri); -} -#define stbi__context _m3dstbi__context -#define stbi__result_info _m3dstbi__result_info -#define stbi__png_load _m3dstbi__png_load -#define stbi_zlib_decode_malloc_guesssize_headerflag _m3dstbi_zlib_decode_malloc_guesssize_headerflag -#endif - #if defined(M3D_EXPORTER) && !defined(INCLUDE_STB_IMAGE_WRITE_H) /* zlib_compressor from diff --git a/code/Common/Assimp.cpp b/code/Common/Assimp.cpp index 4e2c7117c..6074b84bc 100644 --- a/code/Common/Assimp.cpp +++ b/code/Common/Assimp.cpp @@ -1251,3 +1251,36 @@ ASSIMP_API void aiQuaternionInterpolate( ai_assert(nullptr != end); aiQuaternion::Interpolate(*dst, *start, *end, factor); } + + +// stb_image is a lightweight image loader. It is shared by: +// - M3D import +// - PBRT export +// Since it's a header-only library, its implementation must be instantiated in some cpp file. +// Don't scatter this task over multiple importers/exporters. Maintain it in a central place (here!). + +#define ASSIMP_HAS_PBRT_EXPORT (!ASSIMP_BUILD_NO_EXPORT && !ASSIMP_BUILD_NO_PBRT_EXPORTER) +#define ASSIMP_HAS_M3D ((!ASSIMP_BUILD_NO_EXPORT && !ASSIMP_BUILD_NO_M3D_EXPORTER) || !ASSIMP_BUILD_NO_M3D_IMPORTER) + +#if ASSIMP_HAS_PBRT_EXPORT +# define ASSIMP_NEEDS_STB_IMAGE 1 +#elif ASSIMP_HAS_M3D +# define ASSIMP_NEEDS_STB_IMAGE 1 +# define STBI_ONLY_PNG +#endif + +#if ASSIMP_NEEDS_STB_IMAGE + +# if _MSC_VER // "unreferenced function has been removed" (SSE2 detection routine in x64 builds) +# pragma warning(push) +# pragma warning(disable: 4505) +# endif + +# define STB_IMAGE_IMPLEMENTATION +# include "../contrib/stb_image/stb_image.h" + +# if _MSC_VER +# pragma warning(pop) +# endif + +#endif diff --git a/code/Pbrt/PbrtExporter.cpp b/code/Pbrt/PbrtExporter.cpp index b793c37f9..35b108702 100644 --- a/code/Pbrt/PbrtExporter.cpp +++ b/code/Pbrt/PbrtExporter.cpp @@ -83,8 +83,7 @@ Other: #include #include -#define STB_IMAGE_IMPLEMENTATION -#include "stb_image.h" +#include "../contrib/stb_image/stb_image.h" using namespace Assimp; diff --git a/code/Pbrt/stb_image.h b/code/Pbrt/stb_image.h deleted file mode 100644 index 65a205f6e..000000000 --- a/code/Pbrt/stb_image.h +++ /dev/null @@ -1,7756 +0,0 @@ -/* stb_image - v2.26 - public domain image loader - http://nothings.org/stb - no warranty implied; use at your own risk - - Do this: - #define STB_IMAGE_IMPLEMENTATION - before you include this file in *one* C or C++ file to create the implementation. - - // i.e. it should look like this: - #include ... - #include ... - #include ... - #define STB_IMAGE_IMPLEMENTATION - #include "stb_image.h" - - You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. - And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free - - - QUICK NOTES: - Primarily of interest to game developers and other people who can - avoid problematic images and only need the trivial interface - - JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) - PNG 1/2/4/8/16-bit-per-channel - - TGA (not sure what subset, if a subset) - BMP non-1bpp, non-RLE - PSD (composited view only, no extra channels, 8/16 bit-per-channel) - - GIF (*comp always reports as 4-channel) - HDR (radiance rgbE format) - PIC (Softimage PIC) - PNM (PPM and PGM binary only) - - Animated GIF still needs a proper API, but here's one way to do it: - http://gist.github.com/urraka/685d9a6340b26b830d49 - - - decode from memory or through FILE (define STBI_NO_STDIO to remove code) - - decode from arbitrary I/O callbacks - - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) - - Full documentation under "DOCUMENTATION" below. - - -LICENSE - - See end of file for license information. - -RECENT REVISION HISTORY: - - 2.26 (2020-07-13) many minor fixes - 2.25 (2020-02-02) fix warnings - 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically - 2.23 (2019-08-11) fix clang static analysis warning - 2.22 (2019-03-04) gif fixes, fix warnings - 2.21 (2019-02-25) fix typo in comment - 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings - 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes - 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 - RGB-format JPEG; remove white matting in PSD; - allocate large structures on the stack; - correct channel count for PNG & BMP - 2.10 (2016-01-22) avoid warning introduced in 2.09 - 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED - - See end of file for full revision history. - - - ============================ Contributors ========================= - - Image formats Extensions, features - Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) - Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) - Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) - Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) - Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) - Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) - Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) - github:urraka (animated gif) Junggon Kim (PNM comments) - Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) - socks-the-fox (16-bit PNG) - Jeremy Sawicki (handle all ImageNet JPGs) - Optimizations & bugfixes Mikhail Morozov (1-bit BMP) - Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) - Arseny Kapoulkine - John-Mark Allen - Carmelo J Fdez-Aguera - - Bug & warning fixes - Marc LeBlanc David Woo Guillaume George Martins Mozeiko - Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski - Phil Jordan Dave Moore Roy Eltham - Hayaki Saito Nathan Reed Won Chun - Luke Graham Johan Duparc Nick Verigakis the Horde3D community - Thomas Ruf Ronny Chevalier github:rlyeh - Janez Zemva John Bartholomew Michal Cichon github:romigrou - Jonathan Blow Ken Hamada Tero Hanninen github:svdijk - Laurent Gomila Cort Stratton github:snagar - Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex - Cass Everitt Ryamond Barbiero github:grim210 - Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw - Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus - Josh Tobin Matthew Gregan github:poppolopoppo - Julian Raschke Gregory Mullen Christian Floisand github:darealshinji - Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 - Brad Weinberger Matvey Cherevko [reserved] - Luca Sas Alexander Veselov Zack Middleton [reserved] - Ryan C. Gordon [reserved] [reserved] - DO NOT ADD YOUR NAME HERE - - To add your name to the credits, pick a random blank space in the middle and fill it. - 80% of merge conflicts on stb PRs are due to people adding their name at the end - of the credits. -*/ - -#ifndef STBI_INCLUDE_STB_IMAGE_H -#define STBI_INCLUDE_STB_IMAGE_H - -// DOCUMENTATION -// -// Limitations: -// - no 12-bit-per-channel JPEG -// - no JPEGs with arithmetic coding -// - GIF always returns *comp=4 -// -// Basic usage (see HDR discussion below for HDR usage): -// int x,y,n; -// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); -// // ... process data if not NULL ... -// // ... x = width, y = height, n = # 8-bit components per pixel ... -// // ... replace '0' with '1'..'4' to force that many components per pixel -// // ... but 'n' will always be the number that it would have been if you said 0 -// stbi_image_free(data) -// -// Standard parameters: -// int *x -- outputs image width in pixels -// int *y -- outputs image height in pixels -// int *channels_in_file -- outputs # of image components in image file -// int desired_channels -- if non-zero, # of image components requested in result -// -// The return value from an image loader is an 'unsigned char *' which points -// to the pixel data, or NULL on an allocation failure or if the image is -// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, -// with each pixel consisting of N interleaved 8-bit components; the first -// pixel pointed to is top-left-most in the image. There is no padding between -// image scanlines or between pixels, regardless of format. The number of -// components N is 'desired_channels' if desired_channels is non-zero, or -// *channels_in_file otherwise. If desired_channels is non-zero, -// *channels_in_file has the number of components that _would_ have been -// output otherwise. E.g. if you set desired_channels to 4, you will always -// get RGBA output, but you can check *channels_in_file to see if it's trivially -// opaque because e.g. there were only 3 channels in the source image. -// -// An output image with N components has the following components interleaved -// in this order in each pixel: -// -// N=#comp components -// 1 grey -// 2 grey, alpha -// 3 red, green, blue -// 4 red, green, blue, alpha -// -// If image loading fails for any reason, the return value will be NULL, -// and *x, *y, *channels_in_file will be unchanged. The function -// stbi_failure_reason() can be queried for an extremely brief, end-user -// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS -// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly -// more user-friendly ones. -// -// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. -// -// =========================================================================== -// -// UNICODE: -// -// If compiling for Windows and you wish to use Unicode filenames, compile -// with -// #define STBI_WINDOWS_UTF8 -// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert -// Windows wchar_t filenames to utf8. -// -// =========================================================================== -// -// Philosophy -// -// stb libraries are designed with the following priorities: -// -// 1. easy to use -// 2. easy to maintain -// 3. good performance -// -// Sometimes I let "good performance" creep up in priority over "easy to maintain", -// and for best performance I may provide less-easy-to-use APIs that give higher -// performance, in addition to the easy-to-use ones. Nevertheless, it's important -// to keep in mind that from the standpoint of you, a client of this library, -// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. -// -// Some secondary priorities arise directly from the first two, some of which -// provide more explicit reasons why performance can't be emphasized. -// -// - Portable ("ease of use") -// - Small source code footprint ("easy to maintain") -// - No dependencies ("ease of use") -// -// =========================================================================== -// -// I/O callbacks -// -// I/O callbacks allow you to read from arbitrary sources, like packaged -// files or some other source. Data read from callbacks are processed -// through a small internal buffer (currently 128 bytes) to try to reduce -// overhead. -// -// The three functions you must define are "read" (reads some bytes of data), -// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). -// -// =========================================================================== -// -// SIMD support -// -// The JPEG decoder will try to automatically use SIMD kernels on x86 when -// supported by the compiler. For ARM Neon support, you must explicitly -// request it. -// -// (The old do-it-yourself SIMD API is no longer supported in the current -// code.) -// -// On x86, SSE2 will automatically be used when available based on a run-time -// test; if not, the generic C versions are used as a fall-back. On ARM targets, -// the typical path is to have separate builds for NEON and non-NEON devices -// (at least this is true for iOS and Android). Therefore, the NEON support is -// toggled by a build flag: define STBI_NEON to get NEON loops. -// -// If for some reason you do not want to use any of SIMD code, or if -// you have issues compiling it, you can disable it entirely by -// defining STBI_NO_SIMD. -// -// =========================================================================== -// -// HDR image support (disable by defining STBI_NO_HDR) -// -// stb_image supports loading HDR images in general, and currently the Radiance -// .HDR file format specifically. You can still load any file through the existing -// interface; if you attempt to load an HDR file, it will be automatically remapped -// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; -// both of these constants can be reconfigured through this interface: -// -// stbi_hdr_to_ldr_gamma(2.2f); -// stbi_hdr_to_ldr_scale(1.0f); -// -// (note, do not use _inverse_ constants; stbi_image will invert them -// appropriately). -// -// Additionally, there is a new, parallel interface for loading files as -// (linear) floats to preserve the full dynamic range: -// -// float *data = stbi_loadf(filename, &x, &y, &n, 0); -// -// If you load LDR images through this interface, those images will -// be promoted to floating point values, run through the inverse of -// constants corresponding to the above: -// -// stbi_ldr_to_hdr_scale(1.0f); -// stbi_ldr_to_hdr_gamma(2.2f); -// -// Finally, given a filename (or an open file or memory block--see header -// file for details) containing image data, you can query for the "most -// appropriate" interface to use (that is, whether the image is HDR or -// not), using: -// -// stbi_is_hdr(char *filename); -// -// =========================================================================== -// -// iPhone PNG support: -// -// By default we convert iphone-formatted PNGs back to RGB, even though -// they are internally encoded differently. You can disable this conversion -// by calling stbi_convert_iphone_png_to_rgb(0), in which case -// you will always just get the native iphone "format" through (which -// is BGR stored in RGB). -// -// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per -// pixel to remove any premultiplied alpha *only* if the image file explicitly -// says there's premultiplied data (currently only happens in iPhone images, -// and only if iPhone convert-to-rgb processing is on). -// -// =========================================================================== -// -// ADDITIONAL CONFIGURATION -// -// - You can suppress implementation of any of the decoders to reduce -// your code footprint by #defining one or more of the following -// symbols before creating the implementation. -// -// STBI_NO_JPEG -// STBI_NO_PNG -// STBI_NO_BMP -// STBI_NO_PSD -// STBI_NO_TGA -// STBI_NO_GIF -// STBI_NO_HDR -// STBI_NO_PIC -// STBI_NO_PNM (.ppm and .pgm) -// -// - You can request *only* certain decoders and suppress all other ones -// (this will be more forward-compatible, as addition of new decoders -// doesn't require you to disable them explicitly): -// -// STBI_ONLY_JPEG -// STBI_ONLY_PNG -// STBI_ONLY_BMP -// STBI_ONLY_PSD -// STBI_ONLY_TGA -// STBI_ONLY_GIF -// STBI_ONLY_HDR -// STBI_ONLY_PIC -// STBI_ONLY_PNM (.ppm and .pgm) -// -// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still -// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB -// -// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater -// than that size (in either width or height) without further processing. -// This is to let programs in the wild set an upper bound to prevent -// denial-of-service attacks on untrusted data, as one could generate a -// valid image of gigantic dimensions and force stb_image to allocate a -// huge block of memory and spend disproportionate time decoding it. By -// default this is set to (1 << 24), which is 16777216, but that's still -// very big. - -#ifndef STBI_NO_STDIO -#include -#endif // STBI_NO_STDIO - -#define STBI_VERSION 1 - -enum -{ - STBI_default = 0, // only used for desired_channels - - STBI_grey = 1, - STBI_grey_alpha = 2, - STBI_rgb = 3, - STBI_rgb_alpha = 4 -}; - -#include -typedef unsigned char stbi_uc; -typedef unsigned short stbi_us; - -#ifdef __cplusplus -extern "C" { -#endif - -#ifndef STBIDEF -#ifdef STB_IMAGE_STATIC -#define STBIDEF static -#else -#define STBIDEF extern -#endif -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// PRIMARY API - works on images of any type -// - -// -// load image by filename, open file, or memory buffer -// - -typedef struct -{ - int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read - void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative - int (*eof) (void *user); // returns nonzero if we are at end of file/data -} stbi_io_callbacks; - -//////////////////////////////////// -// -// 8-bits-per-channel interface -// - -STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); - -#ifndef STBI_NO_STDIO -STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); -// for stbi_load_from_file, file pointer is left pointing immediately after image -#endif - -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -#endif - -#ifdef STBI_WINDOWS_UTF8 -STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); -#endif - -//////////////////////////////////// -// -// 16-bits-per-channel interface -// - -STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); - -#ifndef STBI_NO_STDIO -STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); -#endif - -//////////////////////////////////// -// -// float-per-channel interface -// -#ifndef STBI_NO_LINEAR - STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); - - #ifndef STBI_NO_STDIO - STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); - #endif -#endif - -#ifndef STBI_NO_HDR - STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); - STBIDEF void stbi_hdr_to_ldr_scale(float scale); -#endif // STBI_NO_HDR - -#ifndef STBI_NO_LINEAR - STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); - STBIDEF void stbi_ldr_to_hdr_scale(float scale); -#endif // STBI_NO_LINEAR - -// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename); -STBIDEF int stbi_is_hdr_from_file(FILE *f); -#endif // STBI_NO_STDIO - - -// get a VERY brief reason for failure -// on most compilers (and ALL modern mainstream compilers) this is threadsafe -STBIDEF const char *stbi_failure_reason (void); - -// free the loaded image -- this is just free() -STBIDEF void stbi_image_free (void *retval_from_stbi_load); - -// get image dimensions & components without fully decoding -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); -STBIDEF int stbi_is_16_bit (char const *filename); -STBIDEF int stbi_is_16_bit_from_file(FILE *f); -#endif - - - -// for image formats that explicitly notate that they have premultiplied alpha, -// we just return the colors as stored in the file. set this flag to force -// unpremultiplication. results are undefined if the unpremultiply overflow. -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); - -// indicate whether we should process iphone images back to canonical format, -// or just pass them through "as-is" -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); - -// flip the image vertically, so the first pixel in the output array is the bottom left -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); - -// as above, but only applies to images loaded on the thread that calls the function -// this function is only available if your compiler supports thread-local variables; -// calling it will fail to link if your compiler doesn't -STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); - -// ZLIB client - used by PNG, available for other purposes - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); -STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - -STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - - -#ifdef __cplusplus -} -#endif - -// -// -//// end header file ///////////////////////////////////////////////////// -#endif // STBI_INCLUDE_STB_IMAGE_H - -#ifdef STB_IMAGE_IMPLEMENTATION - -#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ - || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ - || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ - || defined(STBI_ONLY_ZLIB) - #ifndef STBI_ONLY_JPEG - #define STBI_NO_JPEG - #endif - #ifndef STBI_ONLY_PNG - #define STBI_NO_PNG - #endif - #ifndef STBI_ONLY_BMP - #define STBI_NO_BMP - #endif - #ifndef STBI_ONLY_PSD - #define STBI_NO_PSD - #endif - #ifndef STBI_ONLY_TGA - #define STBI_NO_TGA - #endif - #ifndef STBI_ONLY_GIF - #define STBI_NO_GIF - #endif - #ifndef STBI_ONLY_HDR - #define STBI_NO_HDR - #endif - #ifndef STBI_ONLY_PIC - #define STBI_NO_PIC - #endif - #ifndef STBI_ONLY_PNM - #define STBI_NO_PNM - #endif -#endif - -#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) -#define STBI_NO_ZLIB -#endif - - -#include -#include // ptrdiff_t on osx -#include -#include -#include - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -#include // ldexp, pow -#endif - -#ifndef STBI_NO_STDIO -#include -#endif - -#ifndef STBI_ASSERT -#include -#define STBI_ASSERT(x) assert(x) -#endif - -#ifdef __cplusplus -#define STBI_EXTERN extern "C" -#else -#define STBI_EXTERN extern -#endif - - -#ifndef _MSC_VER - #ifdef __cplusplus - #define stbi_inline inline - #else - #define stbi_inline - #endif -#else - #define stbi_inline __forceinline -#endif - -#ifndef STBI_NO_THREAD_LOCALS - #if defined(__cplusplus) && __cplusplus >= 201103L - #define STBI_THREAD_LOCAL thread_local - #elif defined(__GNUC__) && __GNUC__ < 5 - #define STBI_THREAD_LOCAL __thread - #elif defined(_MSC_VER) - #define STBI_THREAD_LOCAL __declspec(thread) - #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) - #define STBI_THREAD_LOCAL _Thread_local - #endif - - #ifndef STBI_THREAD_LOCAL - #if defined(__GNUC__) - #define STBI_THREAD_LOCAL __thread - #endif - #endif -#endif - -#ifdef _MSC_VER -typedef unsigned short stbi__uint16; -typedef signed short stbi__int16; -typedef unsigned int stbi__uint32; -typedef signed int stbi__int32; -#else -#include -typedef uint16_t stbi__uint16; -typedef int16_t stbi__int16; -typedef uint32_t stbi__uint32; -typedef int32_t stbi__int32; -#endif - -// should produce compiler error if size is wrong -typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; - -#ifdef _MSC_VER -#define STBI_NOTUSED(v) (void)(v) -#else -#define STBI_NOTUSED(v) (void)sizeof(v) -#endif - -#ifdef _MSC_VER -#define STBI_HAS_LROTL -#endif - -#ifdef STBI_HAS_LROTL - #define stbi_lrot(x,y) _lrotl(x,y) -#else - #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) -#endif - -#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) -// ok -#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) -// ok -#else -#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." -#endif - -#ifndef STBI_MALLOC -#define STBI_MALLOC(sz) malloc(sz) -#define STBI_REALLOC(p,newsz) realloc(p,newsz) -#define STBI_FREE(p) free(p) -#endif - -#ifndef STBI_REALLOC_SIZED -#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) -#endif - -// x86/x64 detection -#if defined(__x86_64__) || defined(_M_X64) -#define STBI__X64_TARGET -#elif defined(__i386) || defined(_M_IX86) -#define STBI__X86_TARGET -#endif - -#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) -// gcc doesn't support sse2 intrinsics unless you compile with -msse2, -// which in turn means it gets to use SSE2 everywhere. This is unfortunate, -// but previous attempts to provide the SSE2 functions with runtime -// detection caused numerous issues. The way architecture extensions are -// exposed in GCC/Clang is, sadly, not really suited for one-file libs. -// New behavior: if compiled with -msse2, we use SSE2 without any -// detection; if not, we don't use it at all. -#define STBI_NO_SIMD -#endif - -#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) -// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET -// -// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the -// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. -// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not -// simultaneously enabling "-mstackrealign". -// -// See https://github.com/nothings/stb/issues/81 for more information. -// -// So default to no SSE2 on 32-bit MinGW. If you've read this far and added -// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. -#define STBI_NO_SIMD -#endif - -#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) -#define STBI_SSE2 -#include - -#ifdef _MSC_VER - -#if _MSC_VER >= 1400 // not VC6 -#include // __cpuid -static int stbi__cpuid3(void) -{ - int info[4]; - __cpuid(info,1); - return info[3]; -} -#else -static int stbi__cpuid3(void) -{ - int res; - __asm { - mov eax,1 - cpuid - mov res,edx - } - return res; -} -#endif - -#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name - -#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) -static int stbi__sse2_available(void) -{ - int info3 = stbi__cpuid3(); - return ((info3 >> 26) & 1) != 0; -} -#endif - -#else // assume GCC-style if not VC++ -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) - -#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) -static int stbi__sse2_available(void) -{ - // If we're even attempting to compile this on GCC/Clang, that means - // -msse2 is on, which means the compiler is allowed to use SSE2 - // instructions at will, and so are we. - return 1; -} -#endif - -#endif -#endif - -// ARM NEON -#if defined(STBI_NO_SIMD) && defined(STBI_NEON) -#undef STBI_NEON -#endif - -#ifdef STBI_NEON -#include -// assume GCC or Clang on ARM targets -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) -#endif - -#ifndef STBI_SIMD_ALIGN -#define STBI_SIMD_ALIGN(type, name) type name -#endif - -#ifndef STBI_MAX_DIMENSIONS -#define STBI_MAX_DIMENSIONS (1 << 24) -#endif - -/////////////////////////////////////////////// -// -// stbi__context struct and start_xxx functions - -// stbi__context structure is our basic context used by all images, so it -// contains all the IO context, plus some basic image information -typedef struct -{ - stbi__uint32 img_x, img_y; - int img_n, img_out_n; - - stbi_io_callbacks io; - void *io_user_data; - - int read_from_callbacks; - int buflen; - stbi_uc buffer_start[128]; - int callback_already_read; - - stbi_uc *img_buffer, *img_buffer_end; - stbi_uc *img_buffer_original, *img_buffer_original_end; -} stbi__context; - - -static void stbi__refill_buffer(stbi__context *s); - -// initialize a memory-decode context -static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) -{ - s->io.read = NULL; - s->read_from_callbacks = 0; - s->callback_already_read = 0; - s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; - s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; -} - -// initialize a callback-based context -static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) -{ - s->io = *c; - s->io_user_data = user; - s->buflen = sizeof(s->buffer_start); - s->read_from_callbacks = 1; - s->callback_already_read = 0; - s->img_buffer = s->img_buffer_original = s->buffer_start; - stbi__refill_buffer(s); - s->img_buffer_original_end = s->img_buffer_end; -} - -#ifndef STBI_NO_STDIO - -static int stbi__stdio_read(void *user, char *data, int size) -{ - return (int) fread(data,1,size,(FILE*) user); -} - -static void stbi__stdio_skip(void *user, int n) -{ - int ch; - fseek((FILE*) user, n, SEEK_CUR); - ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ - if (ch != EOF) { - ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ - } -} - -static int stbi__stdio_eof(void *user) -{ - return feof((FILE*) user) || ferror((FILE *) user); -} - -static stbi_io_callbacks stbi__stdio_callbacks = -{ - stbi__stdio_read, - stbi__stdio_skip, - stbi__stdio_eof, -}; - -static void stbi__start_file(stbi__context *s, FILE *f) -{ - stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); -} - -//static void stop_file(stbi__context *s) { } - -#endif // !STBI_NO_STDIO - -static void stbi__rewind(stbi__context *s) -{ - // conceptually rewind SHOULD rewind to the beginning of the stream, - // but we just rewind to the beginning of the initial buffer, because - // we only use it after doing 'test', which only ever looks at at most 92 bytes - s->img_buffer = s->img_buffer_original; - s->img_buffer_end = s->img_buffer_original_end; -} - -enum -{ - STBI_ORDER_RGB, - STBI_ORDER_BGR -}; - -typedef struct -{ - int bits_per_channel; - int num_channels; - int channel_order; -} stbi__result_info; - -#ifndef STBI_NO_JPEG -static int stbi__jpeg_test(stbi__context *s); -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNG -static int stbi__png_test(stbi__context *s); -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__png_is16(stbi__context *s); -#endif - -#ifndef STBI_NO_BMP -static int stbi__bmp_test(stbi__context *s); -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_TGA -static int stbi__tga_test(stbi__context *s); -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s); -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__psd_is16(stbi__context *s); -#endif - -#ifndef STBI_NO_HDR -static int stbi__hdr_test(stbi__context *s); -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_test(stbi__context *s); -static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_GIF -static int stbi__gif_test(stbi__context *s); -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNM -static int stbi__pnm_test(stbi__context *s); -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -static -#ifdef STBI_THREAD_LOCAL -STBI_THREAD_LOCAL -#endif -const char *stbi__g_failure_reason; - -STBIDEF const char *stbi_failure_reason(void) -{ - return stbi__g_failure_reason; -} - -#ifndef STBI_NO_FAILURE_STRINGS -static int stbi__err(const char *str) -{ - stbi__g_failure_reason = str; - return 0; -} -#endif - -static void *stbi__malloc(size_t size) -{ - return STBI_MALLOC(size); -} - -// stb_image uses ints pervasively, including for offset calculations. -// therefore the largest decoded image size we can support with the -// current code, even on 64-bit targets, is INT_MAX. this is not a -// significant limitation for the intended use case. -// -// we do, however, need to make sure our size calculations don't -// overflow. hence a few helper functions for size calculations that -// multiply integers together, making sure that they're non-negative -// and no overflow occurs. - -// return 1 if the sum is valid, 0 on overflow. -// negative terms are considered invalid. -static int stbi__addsizes_valid(int a, int b) -{ - if (b < 0) return 0; - // now 0 <= b <= INT_MAX, hence also - // 0 <= INT_MAX - b <= INTMAX. - // And "a + b <= INT_MAX" (which might overflow) is the - // same as a <= INT_MAX - b (no overflow) - return a <= INT_MAX - b; -} - -// returns 1 if the product is valid, 0 on overflow. -// negative factors are considered invalid. -static int stbi__mul2sizes_valid(int a, int b) -{ - if (a < 0 || b < 0) return 0; - if (b == 0) return 1; // mul-by-0 is always safe - // portable way to check for no overflows in a*b - return a <= INT_MAX/b; -} - -#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) -// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow -static int stbi__mad2sizes_valid(int a, int b, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); -} -#endif - -// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow -static int stbi__mad3sizes_valid(int a, int b, int c, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__addsizes_valid(a*b*c, add); -} - -// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); -} -#endif - -#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) -// mallocs with size overflow checking -static void *stbi__malloc_mad2(int a, int b, int add) -{ - if (!stbi__mad2sizes_valid(a, b, add)) return NULL; - return stbi__malloc(a*b + add); -} -#endif - -static void *stbi__malloc_mad3(int a, int b, int c, int add) -{ - if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; - return stbi__malloc(a*b*c + add); -} - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) -{ - if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; - return stbi__malloc(a*b*c*d + add); -} -#endif - -// stbi__err - error -// stbi__errpf - error returning pointer to float -// stbi__errpuc - error returning pointer to unsigned char - -#ifdef STBI_NO_FAILURE_STRINGS - #define stbi__err(x,y) 0 -#elif defined(STBI_FAILURE_USERMSG) - #define stbi__err(x,y) stbi__err(y) -#else - #define stbi__err(x,y) stbi__err(x) -#endif - -#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) -#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) - -STBIDEF void stbi_image_free(void *retval_from_stbi_load) -{ - STBI_FREE(retval_from_stbi_load); -} - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); -#endif - -#ifndef STBI_NO_HDR -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); -#endif - -static int stbi__vertically_flip_on_load_global = 0; - -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load_global = flag_true_if_should_flip; -} - -#ifndef STBI_THREAD_LOCAL -#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global -#else -static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; - -STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load_local = flag_true_if_should_flip; - stbi__vertically_flip_on_load_set = 1; -} - -#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ - ? stbi__vertically_flip_on_load_local \ - : stbi__vertically_flip_on_load_global) -#endif // STBI_THREAD_LOCAL - -static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields - ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed - ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order - ri->num_channels = 0; - - #ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PNG - if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_BMP - if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_GIF - if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PSD - if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); - #else - STBI_NOTUSED(bpc); - #endif - #ifndef STBI_NO_PIC - if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PNM - if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); - return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); - } - #endif - - #ifndef STBI_NO_TGA - // test tga last because it's a crappy test! - if (stbi__tga_test(s)) - return stbi__tga_load(s,x,y,comp,req_comp, ri); - #endif - - return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); -} - -static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi_uc *reduced; - - reduced = (stbi_uc *) stbi__malloc(img_len); - if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) - reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling - - STBI_FREE(orig); - return reduced; -} - -static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi__uint16 *enlarged; - - enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); - if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) - enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff - - STBI_FREE(orig); - return enlarged; -} - -static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) -{ - int row; - size_t bytes_per_row = (size_t)w * bytes_per_pixel; - stbi_uc temp[2048]; - stbi_uc *bytes = (stbi_uc *)image; - - for (row = 0; row < (h>>1); row++) { - stbi_uc *row0 = bytes + row*bytes_per_row; - stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; - // swap row0 with row1 - size_t bytes_left = bytes_per_row; - while (bytes_left) { - size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); - memcpy(temp, row0, bytes_copy); - memcpy(row0, row1, bytes_copy); - memcpy(row1, temp, bytes_copy); - row0 += bytes_copy; - row1 += bytes_copy; - bytes_left -= bytes_copy; - } - } -} - -#ifndef STBI_NO_GIF -static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) -{ - int slice; - int slice_size = w * h * bytes_per_pixel; - - stbi_uc *bytes = (stbi_uc *)image; - for (slice = 0; slice < z; ++slice) { - stbi__vertical_flip(bytes, w, h, bytes_per_pixel); - bytes += slice_size; - } -} -#endif - -static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); - - if (result == NULL) - return NULL; - - // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. - STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); - - if (ri.bits_per_channel != 8) { - result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 8; - } - - // @TODO: move stbi__convert_format to here - - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); - } - - return (unsigned char *) result; -} - -static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); - - if (result == NULL) - return NULL; - - // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. - STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); - - if (ri.bits_per_channel != 16) { - result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 16; - } - - // @TODO: move stbi__convert_format16 to here - // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision - - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); - } - - return (stbi__uint16 *) result; -} - -#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) -static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) -{ - if (stbi__vertically_flip_on_load && result != NULL) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); - } -} -#endif - -#ifndef STBI_NO_STDIO - -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) -STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); -STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); -#endif - -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) -STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) -{ - return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); -} -#endif - -static FILE *stbi__fopen(char const *filename, char const *mode) -{ - FILE *f; -#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) - wchar_t wMode[64]; - wchar_t wFilename[1024]; - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) - return 0; - - if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) - return 0; - -#if _MSC_VER >= 1400 - if (0 != _wfopen_s(&f, wFilename, wMode)) - f = 0; -#else - f = _wfopen(wFilename, wMode); -#endif - -#elif defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != fopen_s(&f, filename, mode)) - f=0; -#else - f = fopen(filename, mode); -#endif - return f; -} - - -STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - unsigned char *result; - if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} - -STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__uint16 *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} - -STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - stbi__uint16 *result; - if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file_16(f,x,y,comp,req_comp); - fclose(f); - return result; -} - - -#endif //!STBI_NO_STDIO - -STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); -} - -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); -} - -STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); -} - -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_mem(&s,buffer,len); - - result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); - if (stbi__vertically_flip_on_load) { - stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); - } - - return result; -} -#endif - -#ifndef STBI_NO_LINEAR -static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *data; - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - stbi__result_info ri; - float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); - if (hdr_data) - stbi__float_postprocess(hdr_data,x,y,comp,req_comp); - return hdr_data; - } - #endif - data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); - if (data) - return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); - return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); -} - -STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_STDIO -STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - float *result; - FILE *f = stbi__fopen(filename, "rb"); - if (!f) return stbi__errpf("can't fopen", "Unable to open file"); - result = stbi_loadf_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_file(&s,f); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} -#endif // !STBI_NO_STDIO - -#endif // !STBI_NO_LINEAR - -// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is -// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always -// reports false! - -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(buffer); - STBI_NOTUSED(len); - return 0; - #endif -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result=0; - if (f) { - result = stbi_is_hdr_from_file(f); - fclose(f); - } - return result; -} - -STBIDEF int stbi_is_hdr_from_file(FILE *f) -{ - #ifndef STBI_NO_HDR - long pos = ftell(f); - int res; - stbi__context s; - stbi__start_file(&s,f); - res = stbi__hdr_test(&s); - fseek(f, pos, SEEK_SET); - return res; - #else - STBI_NOTUSED(f); - return 0; - #endif -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(clbk); - STBI_NOTUSED(user); - return 0; - #endif -} - -#ifndef STBI_NO_LINEAR -static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; - -STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } -STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } -#endif - -static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; - -STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } -STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } - - -////////////////////////////////////////////////////////////////////////////// -// -// Common code used by all image loaders -// - -enum -{ - STBI__SCAN_load=0, - STBI__SCAN_type, - STBI__SCAN_header -}; - -static void stbi__refill_buffer(stbi__context *s) -{ - int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); - s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); - if (n == 0) { - // at end of file, treat same as if from memory, but need to handle case - // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file - s->read_from_callbacks = 0; - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start+1; - *s->img_buffer = 0; - } else { - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start + n; - } -} - -stbi_inline static stbi_uc stbi__get8(stbi__context *s) -{ - if (s->img_buffer < s->img_buffer_end) - return *s->img_buffer++; - if (s->read_from_callbacks) { - stbi__refill_buffer(s); - return *s->img_buffer++; - } - return 0; -} - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else -stbi_inline static int stbi__at_eof(stbi__context *s) -{ - if (s->io.read) { - if (!(s->io.eof)(s->io_user_data)) return 0; - // if feof() is true, check if buffer = end - // special case: we've only got the special 0 character at the end - if (s->read_from_callbacks == 0) return 1; - } - - return s->img_buffer >= s->img_buffer_end; -} -#endif - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) -// nothing -#else -static void stbi__skip(stbi__context *s, int n) -{ - if (n == 0) return; // already there! - if (n < 0) { - s->img_buffer = s->img_buffer_end; - return; - } - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - s->img_buffer = s->img_buffer_end; - (s->io.skip)(s->io_user_data, n - blen); - return; - } - } - s->img_buffer += n; -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) -// nothing -#else -static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) -{ - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - int res, count; - - memcpy(buffer, s->img_buffer, blen); - - count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); - res = (count == (n-blen)); - s->img_buffer = s->img_buffer_end; - return res; - } - } - - if (s->img_buffer+n <= s->img_buffer_end) { - memcpy(buffer, s->img_buffer, n); - s->img_buffer += n; - return 1; - } else - return 0; -} -#endif - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) -// nothing -#else -static int stbi__get16be(stbi__context *s) -{ - int z = stbi__get8(s); - return (z << 8) + stbi__get8(s); -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) -// nothing -#else -static stbi__uint32 stbi__get32be(stbi__context *s) -{ - stbi__uint32 z = stbi__get16be(s); - return (z << 16) + stbi__get16be(s); -} -#endif - -#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) -// nothing -#else -static int stbi__get16le(stbi__context *s) -{ - int z = stbi__get8(s); - return z + (stbi__get8(s) << 8); -} -#endif - -#ifndef STBI_NO_BMP -static stbi__uint32 stbi__get32le(stbi__context *s) -{ - stbi__uint32 z = stbi__get16le(s); - return z + (stbi__get16le(s) << 16); -} -#endif - -#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings - -#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else -////////////////////////////////////////////////////////////////////////////// -// -// generic converter from built-in img_n to req_comp -// individual types do this automatically as much as possible (e.g. jpeg -// does all cases internally since it needs to colorspace convert anyway, -// and it never has alpha, so very few cases ). png can automatically -// interleave an alpha=255 channel, but falls back to this for other cases -// -// assume data buffer is malloced, so malloc a new one and free that one -// only failure mode is malloc failing - -static stbi_uc stbi__compute_y(int r, int g, int b) -{ - return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) -// nothing -#else -static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - unsigned char *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); - if (good == NULL) { - STBI_FREE(data); - return stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - unsigned char *src = data + j * x * img_n ; - unsigned char *dest = good + j * x * req_comp; - - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; - default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); - } - #undef STBI__CASE - } - - STBI_FREE(data); - return good; -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) -// nothing -#else -static stbi__uint16 stbi__compute_y_16(int r, int g, int b) -{ - return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); -} -#endif - -#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) -// nothing -#else -static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - stbi__uint16 *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); - if (good == NULL) { - STBI_FREE(data); - return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - stbi__uint16 *src = data + j * x * img_n ; - stbi__uint16 *dest = good + j * x * req_comp; - - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; - default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); - } - #undef STBI__CASE - } - - STBI_FREE(data); - return good; -} -#endif - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) -{ - int i,k,n; - float *output; - if (!data) return NULL; - output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); - if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); - } - } - if (n < comp) { - for (i=0; i < x*y; ++i) { - output[i*comp + n] = data[i*comp + n]/255.0f; - } - } - STBI_FREE(data); - return output; -} -#endif - -#ifndef STBI_NO_HDR -#define stbi__float2int(x) ((int) (x)) -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) -{ - int i,k,n; - stbi_uc *output; - if (!data) return NULL; - output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); - if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - if (k < comp) { - float z = data[i*comp+k] * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - } - STBI_FREE(data); - return output; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// "baseline" JPEG/JFIF decoder -// -// simple implementation -// - doesn't support delayed output of y-dimension -// - simple interface (only one output format: 8-bit interleaved RGB) -// - doesn't try to recover corrupt jpegs -// - doesn't allow partial loading, loading multiple at once -// - still fast on x86 (copying globals into locals doesn't help x86) -// - allocates lots of intermediate memory (full size of all components) -// - non-interleaved case requires this anyway -// - allows good upsampling (see next) -// high-quality -// - upsampled channels are bilinearly interpolated, even across blocks -// - quality integer IDCT derived from IJG's 'slow' -// performance -// - fast huffman; reasonable integer IDCT -// - some SIMD kernels for common paths on targets with SSE2/NEON -// - uses a lot of intermediate memory, could cache poorly - -#ifndef STBI_NO_JPEG - -// huffman decoding acceleration -#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache - -typedef struct -{ - stbi_uc fast[1 << FAST_BITS]; - // weirdly, repacking this into AoS is a 10% speed loss, instead of a win - stbi__uint16 code[256]; - stbi_uc values[256]; - stbi_uc size[257]; - unsigned int maxcode[18]; - int delta[17]; // old 'firstsymbol' - old 'firstcode' -} stbi__huffman; - -typedef struct -{ - stbi__context *s; - stbi__huffman huff_dc[4]; - stbi__huffman huff_ac[4]; - stbi__uint16 dequant[4][64]; - stbi__int16 fast_ac[4][1 << FAST_BITS]; - -// sizes for components, interleaved MCUs - int img_h_max, img_v_max; - int img_mcu_x, img_mcu_y; - int img_mcu_w, img_mcu_h; - -// definition of jpeg image component - struct - { - int id; - int h,v; - int tq; - int hd,ha; - int dc_pred; - - int x,y,w2,h2; - stbi_uc *data; - void *raw_data, *raw_coeff; - stbi_uc *linebuf; - short *coeff; // progressive only - int coeff_w, coeff_h; // number of 8x8 coefficient blocks - } img_comp[4]; - - stbi__uint32 code_buffer; // jpeg entropy-coded buffer - int code_bits; // number of valid bits - unsigned char marker; // marker seen while filling entropy buffer - int nomore; // flag if we saw a marker so must stop - - int progressive; - int spec_start; - int spec_end; - int succ_high; - int succ_low; - int eob_run; - int jfif; - int app14_color_transform; // Adobe APP14 tag - int rgb; - - int scan_n, order[4]; - int restart_interval, todo; - -// kernels - void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); - void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); - stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); -} stbi__jpeg; - -static int stbi__build_huffman(stbi__huffman *h, int *count) -{ - int i,j,k=0; - unsigned int code; - // build size list for each symbol (from JPEG spec) - for (i=0; i < 16; ++i) - for (j=0; j < count[i]; ++j) - h->size[k++] = (stbi_uc) (i+1); - h->size[k] = 0; - - // compute actual symbols (from jpeg spec) - code = 0; - k = 0; - for(j=1; j <= 16; ++j) { - // compute delta to add to code to compute symbol id - h->delta[j] = k - code; - if (h->size[k] == j) { - while (h->size[k] == j) - h->code[k++] = (stbi__uint16) (code++); - if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); - } - // compute largest code + 1 for this size, preshifted as needed later - h->maxcode[j] = code << (16-j); - code <<= 1; - } - h->maxcode[j] = 0xffffffff; - - // build non-spec acceleration table; 255 is flag for not-accelerated - memset(h->fast, 255, 1 << FAST_BITS); - for (i=0; i < k; ++i) { - int s = h->size[i]; - if (s <= FAST_BITS) { - int c = h->code[i] << (FAST_BITS-s); - int m = 1 << (FAST_BITS-s); - for (j=0; j < m; ++j) { - h->fast[c+j] = (stbi_uc) i; - } - } - } - return 1; -} - -// build a table that decodes both magnitude and value of small ACs in -// one go. -static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) -{ - int i; - for (i=0; i < (1 << FAST_BITS); ++i) { - stbi_uc fast = h->fast[i]; - fast_ac[i] = 0; - if (fast < 255) { - int rs = h->values[fast]; - int run = (rs >> 4) & 15; - int magbits = rs & 15; - int len = h->size[fast]; - - if (magbits && len + magbits <= FAST_BITS) { - // magnitude code followed by receive_extend code - int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); - int m = 1 << (magbits - 1); - if (k < m) k += (~0U << magbits) + 1; - // if the result is small enough, we can fit it in fast_ac table - if (k >= -128 && k <= 127) - fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); - } - } - } -} - -static void stbi__grow_buffer_unsafe(stbi__jpeg *j) -{ - do { - unsigned int b = j->nomore ? 0 : stbi__get8(j->s); - if (b == 0xff) { - int c = stbi__get8(j->s); - while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes - if (c != 0) { - j->marker = (unsigned char) c; - j->nomore = 1; - return; - } - } - j->code_buffer |= b << (24 - j->code_bits); - j->code_bits += 8; - } while (j->code_bits <= 24); -} - -// (1 << n) - 1 -static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; - -// decode a jpeg huffman value from the bitstream -stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) -{ - unsigned int temp; - int c,k; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - // look at the top FAST_BITS and determine what symbol ID it is, - // if the code is <= FAST_BITS - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - k = h->fast[c]; - if (k < 255) { - int s = h->size[k]; - if (s > j->code_bits) - return -1; - j->code_buffer <<= s; - j->code_bits -= s; - return h->values[k]; - } - - // naive test is to shift the code_buffer down so k bits are - // valid, then test against maxcode. To speed this up, we've - // preshifted maxcode left so that it has (16-k) 0s at the - // end; in other words, regardless of the number of bits, it - // wants to be compared against something shifted to have 16; - // that way we don't need to shift inside the loop. - temp = j->code_buffer >> 16; - for (k=FAST_BITS+1 ; ; ++k) - if (temp < h->maxcode[k]) - break; - if (k == 17) { - // error! code not found - j->code_bits -= 16; - return -1; - } - - if (k > j->code_bits) - return -1; - - // convert the huffman code to the symbol id - c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; - STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); - - // convert the id to a symbol - j->code_bits -= k; - j->code_buffer <<= k; - return h->values[c]; -} - -// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); - - sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB - k = stbi_lrot(j->code_buffer, n); - if (n < 0 || n >= (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))) return 0; - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k + (stbi__jbias[n] & ~sgn); -} - -// get some unsigned bits -stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) -{ - unsigned int k; - if (j->code_bits < n) stbi__grow_buffer_unsafe(j); - k = stbi_lrot(j->code_buffer, n); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k; -} - -stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) -{ - unsigned int k; - if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); - k = j->code_buffer; - j->code_buffer <<= 1; - --j->code_bits; - return k & 0x80000000; -} - -// given a value that's at position X in the zigzag stream, -// where does it appear in the 8x8 matrix coded as row-major? -static const stbi_uc stbi__jpeg_dezigzag[64+15] = -{ - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63, - // let corrupt input sample past end - 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63 -}; - -// decode one 64-entry block-- -static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) -{ - int diff,dc,k; - int t; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - - // 0 all the ac values now so we can do it 32-bits at a time - memset(data,0,64*sizeof(data[0])); - - diff = t ? stbi__extend_receive(j, t) : 0; - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc * dequant[0]); - - // decode AC components, see JPEG spec - k = 1; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) * dequant[zig]); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (rs != 0xf0) break; // end block - k += 16; - } else { - k += r; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); - } - } - } while (k < 64); - return 1; -} - -static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) -{ - int diff,dc; - int t; - if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - if (j->succ_high == 0) { - // first scan for DC coefficient, must be first - memset(data,0,64*sizeof(data[0])); // 0 all the ac values now - t = stbi__jpeg_huff_decode(j, hdc); - if (t == -1) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - diff = t ? stbi__extend_receive(j, t) : 0; - - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc << j->succ_low); - } else { - // refinement scan for DC coefficient - if (stbi__jpeg_get_bit(j)) - data[0] += (short) (1 << j->succ_low); - } - return 1; -} - -// @OPTIMIZE: store non-zigzagged during the decode passes, -// and only de-zigzag when dequantizing -static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) -{ - int k; - if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->succ_high == 0) { - int shift = j->succ_low; - - if (j->eob_run) { - --j->eob_run; - return 1; - } - - k = j->spec_start; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) << shift); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r); - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - --j->eob_run; - break; - } - k += 16; - } else { - k += r; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) << shift); - } - } - } while (k <= j->spec_end); - } else { - // refinement scan for these AC coefficients - - short bit = (short) (1 << j->succ_low); - - if (j->eob_run) { - --j->eob_run; - for (k = j->spec_start; k <= j->spec_end; ++k) { - short *p = &data[stbi__jpeg_dezigzag[k]]; - if (*p != 0) - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } - } else { - k = j->spec_start; - do { - int r,s; - int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r) - 1; - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - r = 64; // force end of block - } else { - // r=15 s=0 should write 16 0s, so we just do - // a run of 15 0s and then write s (which is 0), - // so we don't have to do anything special here - } - } else { - if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); - // sign bit - if (stbi__jpeg_get_bit(j)) - s = bit; - else - s = -bit; - } - - // advance by r - while (k <= j->spec_end) { - short *p = &data[stbi__jpeg_dezigzag[k++]]; - if (*p != 0) { - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } else { - if (r == 0) { - *p = (short) s; - break; - } - --r; - } - } - } while (k <= j->spec_end); - } - } - return 1; -} - -// take a -128..127 value and stbi__clamp it and convert to 0..255 -stbi_inline static stbi_uc stbi__clamp(int x) -{ - // trick to use a single test to catch both cases - if ((unsigned int) x > 255) { - if (x < 0) return 0; - if (x > 255) return 255; - } - return (stbi_uc) x; -} - -#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) -#define stbi__fsh(x) ((x) * 4096) - -// derived from jidctint -- DCT_ISLOW -#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ - int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ - p2 = s2; \ - p3 = s6; \ - p1 = (p2+p3) * stbi__f2f(0.5411961f); \ - t2 = p1 + p3*stbi__f2f(-1.847759065f); \ - t3 = p1 + p2*stbi__f2f( 0.765366865f); \ - p2 = s0; \ - p3 = s4; \ - t0 = stbi__fsh(p2+p3); \ - t1 = stbi__fsh(p2-p3); \ - x0 = t0+t3; \ - x3 = t0-t3; \ - x1 = t1+t2; \ - x2 = t1-t2; \ - t0 = s7; \ - t1 = s5; \ - t2 = s3; \ - t3 = s1; \ - p3 = t0+t2; \ - p4 = t1+t3; \ - p1 = t0+t3; \ - p2 = t1+t2; \ - p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ - t0 = t0*stbi__f2f( 0.298631336f); \ - t1 = t1*stbi__f2f( 2.053119869f); \ - t2 = t2*stbi__f2f( 3.072711026f); \ - t3 = t3*stbi__f2f( 1.501321110f); \ - p1 = p5 + p1*stbi__f2f(-0.899976223f); \ - p2 = p5 + p2*stbi__f2f(-2.562915447f); \ - p3 = p3*stbi__f2f(-1.961570560f); \ - p4 = p4*stbi__f2f(-0.390180644f); \ - t3 += p1+p4; \ - t2 += p2+p3; \ - t1 += p2+p4; \ - t0 += p1+p3; - -static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) -{ - int i,val[64],*v=val; - stbi_uc *o; - short *d = data; - - // columns - for (i=0; i < 8; ++i,++d, ++v) { - // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing - if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 - && d[40]==0 && d[48]==0 && d[56]==0) { - // no shortcut 0 seconds - // (1|2|3|4|5|6|7)==0 0 seconds - // all separate -0.047 seconds - // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds - int dcterm = d[0]*4; - v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; - } else { - STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) - // constants scaled things up by 1<<12; let's bring them back - // down, but keep 2 extra bits of precision - x0 += 512; x1 += 512; x2 += 512; x3 += 512; - v[ 0] = (x0+t3) >> 10; - v[56] = (x0-t3) >> 10; - v[ 8] = (x1+t2) >> 10; - v[48] = (x1-t2) >> 10; - v[16] = (x2+t1) >> 10; - v[40] = (x2-t1) >> 10; - v[24] = (x3+t0) >> 10; - v[32] = (x3-t0) >> 10; - } - } - - for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { - // no fast case since the first 1D IDCT spread components out - STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) - // constants scaled things up by 1<<12, plus we had 1<<2 from first - // loop, plus horizontal and vertical each scale by sqrt(8) so together - // we've got an extra 1<<3, so 1<<17 total we need to remove. - // so we want to round that, which means adding 0.5 * 1<<17, - // aka 65536. Also, we'll end up with -128 to 127 that we want - // to encode as 0..255 by adding 128, so we'll add that before the shift - x0 += 65536 + (128<<17); - x1 += 65536 + (128<<17); - x2 += 65536 + (128<<17); - x3 += 65536 + (128<<17); - // tried computing the shifts into temps, or'ing the temps to see - // if any were out of range, but that was slower - o[0] = stbi__clamp((x0+t3) >> 17); - o[7] = stbi__clamp((x0-t3) >> 17); - o[1] = stbi__clamp((x1+t2) >> 17); - o[6] = stbi__clamp((x1-t2) >> 17); - o[2] = stbi__clamp((x2+t1) >> 17); - o[5] = stbi__clamp((x2-t1) >> 17); - o[3] = stbi__clamp((x3+t0) >> 17); - o[4] = stbi__clamp((x3-t0) >> 17); - } -} - -#ifdef STBI_SSE2 -// sse2 integer IDCT. not the fastest possible implementation but it -// produces bit-identical results to the generic C version so it's -// fully "transparent". -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - // This is constructed to match our regular (generic) integer IDCT exactly. - __m128i row0, row1, row2, row3, row4, row5, row6, row7; - __m128i tmp; - - // dot product constant: even elems=x, odd elems=y - #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) - - // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) - // out(1) = c1[even]*x + c1[odd]*y - #define dct_rot(out0,out1, x,y,c0,c1) \ - __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ - __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ - __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ - __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ - __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ - __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) - - // out = in << 12 (in 16-bit, out 32-bit) - #define dct_widen(out, in) \ - __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ - __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) - - // wide add - #define dct_wadd(out, a, b) \ - __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_add_epi32(a##_h, b##_h) - - // wide sub - #define dct_wsub(out, a, b) \ - __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) - - // butterfly a/b, add bias, then shift by "s" and pack - #define dct_bfly32o(out0, out1, a,b,bias,s) \ - { \ - __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ - __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ - dct_wadd(sum, abiased, b); \ - dct_wsub(dif, abiased, b); \ - out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ - out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ - } - - // 8-bit interleave step (for transposes) - #define dct_interleave8(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi8(a, b); \ - b = _mm_unpackhi_epi8(tmp, b) - - // 16-bit interleave step (for transposes) - #define dct_interleave16(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi16(a, b); \ - b = _mm_unpackhi_epi16(tmp, b) - - #define dct_pass(bias,shift) \ - { \ - /* even part */ \ - dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ - __m128i sum04 = _mm_add_epi16(row0, row4); \ - __m128i dif04 = _mm_sub_epi16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ - dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ - __m128i sum17 = _mm_add_epi16(row1, row7); \ - __m128i sum35 = _mm_add_epi16(row3, row5); \ - dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ - dct_wadd(x4, y0o, y4o); \ - dct_wadd(x5, y1o, y5o); \ - dct_wadd(x6, y2o, y5o); \ - dct_wadd(x7, y3o, y4o); \ - dct_bfly32o(row0,row7, x0,x7,bias,shift); \ - dct_bfly32o(row1,row6, x1,x6,bias,shift); \ - dct_bfly32o(row2,row5, x2,x5,bias,shift); \ - dct_bfly32o(row3,row4, x3,x4,bias,shift); \ - } - - __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); - __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); - __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); - __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); - __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); - __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); - __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); - __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); - - // rounding biases in column/row passes, see stbi__idct_block for explanation. - __m128i bias_0 = _mm_set1_epi32(512); - __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); - - // load - row0 = _mm_load_si128((const __m128i *) (data + 0*8)); - row1 = _mm_load_si128((const __m128i *) (data + 1*8)); - row2 = _mm_load_si128((const __m128i *) (data + 2*8)); - row3 = _mm_load_si128((const __m128i *) (data + 3*8)); - row4 = _mm_load_si128((const __m128i *) (data + 4*8)); - row5 = _mm_load_si128((const __m128i *) (data + 5*8)); - row6 = _mm_load_si128((const __m128i *) (data + 6*8)); - row7 = _mm_load_si128((const __m128i *) (data + 7*8)); - - // column pass - dct_pass(bias_0, 10); - - { - // 16bit 8x8 transpose pass 1 - dct_interleave16(row0, row4); - dct_interleave16(row1, row5); - dct_interleave16(row2, row6); - dct_interleave16(row3, row7); - - // transpose pass 2 - dct_interleave16(row0, row2); - dct_interleave16(row1, row3); - dct_interleave16(row4, row6); - dct_interleave16(row5, row7); - - // transpose pass 3 - dct_interleave16(row0, row1); - dct_interleave16(row2, row3); - dct_interleave16(row4, row5); - dct_interleave16(row6, row7); - } - - // row pass - dct_pass(bias_1, 17); - - { - // pack - __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 - __m128i p1 = _mm_packus_epi16(row2, row3); - __m128i p2 = _mm_packus_epi16(row4, row5); - __m128i p3 = _mm_packus_epi16(row6, row7); - - // 8bit 8x8 transpose pass 1 - dct_interleave8(p0, p2); // a0e0a1e1... - dct_interleave8(p1, p3); // c0g0c1g1... - - // transpose pass 2 - dct_interleave8(p0, p1); // a0c0e0g0... - dct_interleave8(p2, p3); // b0d0f0h0... - - // transpose pass 3 - dct_interleave8(p0, p2); // a0b0c0d0... - dct_interleave8(p1, p3); // a4b4c4d4... - - // store - _mm_storel_epi64((__m128i *) out, p0); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p2); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p1); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p3); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); - } - -#undef dct_const -#undef dct_rot -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_interleave8 -#undef dct_interleave16 -#undef dct_pass -} - -#endif // STBI_SSE2 - -#ifdef STBI_NEON - -// NEON integer IDCT. should produce bit-identical -// results to the generic C version. -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; - - int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); - int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); - int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); - int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); - int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); - int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); - int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); - int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); - int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); - int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); - int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); - int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); - -#define dct_long_mul(out, inq, coeff) \ - int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) - -#define dct_long_mac(out, acc, inq, coeff) \ - int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) - -#define dct_widen(out, inq) \ - int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ - int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) - -// wide add -#define dct_wadd(out, a, b) \ - int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vaddq_s32(a##_h, b##_h) - -// wide sub -#define dct_wsub(out, a, b) \ - int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vsubq_s32(a##_h, b##_h) - -// butterfly a/b, then shift using "shiftop" by "s" and pack -#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ - { \ - dct_wadd(sum, a, b); \ - dct_wsub(dif, a, b); \ - out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ - out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ - } - -#define dct_pass(shiftop, shift) \ - { \ - /* even part */ \ - int16x8_t sum26 = vaddq_s16(row2, row6); \ - dct_long_mul(p1e, sum26, rot0_0); \ - dct_long_mac(t2e, p1e, row6, rot0_1); \ - dct_long_mac(t3e, p1e, row2, rot0_2); \ - int16x8_t sum04 = vaddq_s16(row0, row4); \ - int16x8_t dif04 = vsubq_s16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - int16x8_t sum15 = vaddq_s16(row1, row5); \ - int16x8_t sum17 = vaddq_s16(row1, row7); \ - int16x8_t sum35 = vaddq_s16(row3, row5); \ - int16x8_t sum37 = vaddq_s16(row3, row7); \ - int16x8_t sumodd = vaddq_s16(sum17, sum35); \ - dct_long_mul(p5o, sumodd, rot1_0); \ - dct_long_mac(p1o, p5o, sum17, rot1_1); \ - dct_long_mac(p2o, p5o, sum35, rot1_2); \ - dct_long_mul(p3o, sum37, rot2_0); \ - dct_long_mul(p4o, sum15, rot2_1); \ - dct_wadd(sump13o, p1o, p3o); \ - dct_wadd(sump24o, p2o, p4o); \ - dct_wadd(sump23o, p2o, p3o); \ - dct_wadd(sump14o, p1o, p4o); \ - dct_long_mac(x4, sump13o, row7, rot3_0); \ - dct_long_mac(x5, sump24o, row5, rot3_1); \ - dct_long_mac(x6, sump23o, row3, rot3_2); \ - dct_long_mac(x7, sump14o, row1, rot3_3); \ - dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ - dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ - dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ - dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ - } - - // load - row0 = vld1q_s16(data + 0*8); - row1 = vld1q_s16(data + 1*8); - row2 = vld1q_s16(data + 2*8); - row3 = vld1q_s16(data + 3*8); - row4 = vld1q_s16(data + 4*8); - row5 = vld1q_s16(data + 5*8); - row6 = vld1q_s16(data + 6*8); - row7 = vld1q_s16(data + 7*8); - - // add DC bias - row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); - - // column pass - dct_pass(vrshrn_n_s32, 10); - - // 16bit 8x8 transpose - { -// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. -// whether compilers actually get this is another story, sadly. -#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } -#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } - - // pass 1 - dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 - dct_trn16(row2, row3); - dct_trn16(row4, row5); - dct_trn16(row6, row7); - - // pass 2 - dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 - dct_trn32(row1, row3); - dct_trn32(row4, row6); - dct_trn32(row5, row7); - - // pass 3 - dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 - dct_trn64(row1, row5); - dct_trn64(row2, row6); - dct_trn64(row3, row7); - -#undef dct_trn16 -#undef dct_trn32 -#undef dct_trn64 - } - - // row pass - // vrshrn_n_s32 only supports shifts up to 16, we need - // 17. so do a non-rounding shift of 16 first then follow - // up with a rounding shift by 1. - dct_pass(vshrn_n_s32, 16); - - { - // pack and round - uint8x8_t p0 = vqrshrun_n_s16(row0, 1); - uint8x8_t p1 = vqrshrun_n_s16(row1, 1); - uint8x8_t p2 = vqrshrun_n_s16(row2, 1); - uint8x8_t p3 = vqrshrun_n_s16(row3, 1); - uint8x8_t p4 = vqrshrun_n_s16(row4, 1); - uint8x8_t p5 = vqrshrun_n_s16(row5, 1); - uint8x8_t p6 = vqrshrun_n_s16(row6, 1); - uint8x8_t p7 = vqrshrun_n_s16(row7, 1); - - // again, these can translate into one instruction, but often don't. -#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } -#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } - - // sadly can't use interleaved stores here since we only write - // 8 bytes to each scan line! - - // 8x8 8-bit transpose pass 1 - dct_trn8_8(p0, p1); - dct_trn8_8(p2, p3); - dct_trn8_8(p4, p5); - dct_trn8_8(p6, p7); - - // pass 2 - dct_trn8_16(p0, p2); - dct_trn8_16(p1, p3); - dct_trn8_16(p4, p6); - dct_trn8_16(p5, p7); - - // pass 3 - dct_trn8_32(p0, p4); - dct_trn8_32(p1, p5); - dct_trn8_32(p2, p6); - dct_trn8_32(p3, p7); - - // store - vst1_u8(out, p0); out += out_stride; - vst1_u8(out, p1); out += out_stride; - vst1_u8(out, p2); out += out_stride; - vst1_u8(out, p3); out += out_stride; - vst1_u8(out, p4); out += out_stride; - vst1_u8(out, p5); out += out_stride; - vst1_u8(out, p6); out += out_stride; - vst1_u8(out, p7); - -#undef dct_trn8_8 -#undef dct_trn8_16 -#undef dct_trn8_32 - } - -#undef dct_long_mul -#undef dct_long_mac -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_pass -} - -#endif // STBI_NEON - -#define STBI__MARKER_none 0xff -// if there's a pending marker from the entropy stream, return that -// otherwise, fetch from the stream and get a marker. if there's no -// marker, return 0xff, which is never a valid marker value -static stbi_uc stbi__get_marker(stbi__jpeg *j) -{ - stbi_uc x; - if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } - x = stbi__get8(j->s); - if (x != 0xff) return STBI__MARKER_none; - while (x == 0xff) - x = stbi__get8(j->s); // consume repeated 0xff fill bytes - return x; -} - -// in each scan, we'll have scan_n components, and the order -// of the components is specified by order[] -#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) - -// after a restart interval, stbi__jpeg_reset the entropy decoder and -// the dc prediction -static void stbi__jpeg_reset(stbi__jpeg *j) -{ - j->code_bits = 0; - j->code_buffer = 0; - j->nomore = 0; - j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; - j->marker = STBI__MARKER_none; - j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; - j->eob_run = 0; - // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, - // since we don't even allow 1<<30 pixels -} - -static int stbi__parse_entropy_coded_data(stbi__jpeg *z) -{ - stbi__jpeg_reset(z); - if (!z->progressive) { - if (z->scan_n == 1) { - int i,j; - STBI_SIMD_ALIGN(short, data[64]); - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - // if it's NOT a restart, then just bail, so we get corrupt data - // rather than no data - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - STBI_SIMD_ALIGN(short, data[64]); - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x)*8; - int y2 = (j*z->img_comp[n].v + y)*8; - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } else { - if (z->scan_n == 1) { - int i,j; - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - if (z->spec_start == 0) { - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } else { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) - return 0; - } - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x); - int y2 = (j*z->img_comp[n].v + y); - short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } -} - -static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) -{ - int i; - for (i=0; i < 64; ++i) - data[i] *= dequant[i]; -} - -static void stbi__jpeg_finish(stbi__jpeg *z) -{ - if (z->progressive) { - // dequantize and idct the data - int i,j,n; - for (n=0; n < z->s->img_n; ++n) { - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - } - } - } - } -} - -static int stbi__process_marker(stbi__jpeg *z, int m) -{ - int L; - switch (m) { - case STBI__MARKER_none: // no marker found - return stbi__err("expected marker","Corrupt JPEG"); - - case 0xDD: // DRI - specify restart interval - if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); - z->restart_interval = stbi__get16be(z->s); - return 1; - - case 0xDB: // DQT - define quantization table - L = stbi__get16be(z->s)-2; - while (L > 0) { - int q = stbi__get8(z->s); - int p = q >> 4, sixteen = (p != 0); - int t = q & 15,i; - if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); - if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); - - for (i=0; i < 64; ++i) - z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); - L -= (sixteen ? 129 : 65); - } - return L==0; - - case 0xC4: // DHT - define huffman table - L = stbi__get16be(z->s)-2; - while (L > 0) { - stbi_uc *v; - int sizes[16],i,n=0; - int q = stbi__get8(z->s); - int tc = q >> 4; - int th = q & 15; - if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); - for (i=0; i < 16; ++i) { - sizes[i] = stbi__get8(z->s); - n += sizes[i]; - } - L -= 17; - if (tc == 0) { - if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; - v = z->huff_dc[th].values; - } else { - if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; - v = z->huff_ac[th].values; - } - for (i=0; i < n; ++i) - v[i] = stbi__get8(z->s); - if (tc != 0) - stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); - L -= n; - } - return L==0; - } - - // check for comment block or APP blocks - if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { - L = stbi__get16be(z->s); - if (L < 2) { - if (m == 0xFE) - return stbi__err("bad COM len","Corrupt JPEG"); - else - return stbi__err("bad APP len","Corrupt JPEG"); - } - L -= 2; - - if (m == 0xE0 && L >= 5) { // JFIF APP0 segment - static const unsigned char tag[5] = {'J','F','I','F','\0'}; - int ok = 1; - int i; - for (i=0; i < 5; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 5; - if (ok) - z->jfif = 1; - } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment - static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; - int ok = 1; - int i; - for (i=0; i < 6; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 6; - if (ok) { - stbi__get8(z->s); // version - stbi__get16be(z->s); // flags0 - stbi__get16be(z->s); // flags1 - z->app14_color_transform = stbi__get8(z->s); // color transform - L -= 6; - } - } - - stbi__skip(z->s, L); - return 1; - } - - return stbi__err("unknown marker","Corrupt JPEG"); -} - -// after we see SOS -static int stbi__process_scan_header(stbi__jpeg *z) -{ - int i; - int Ls = stbi__get16be(z->s); - z->scan_n = stbi__get8(z->s); - if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); - if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); - for (i=0; i < z->scan_n; ++i) { - int id = stbi__get8(z->s), which; - int q = stbi__get8(z->s); - for (which = 0; which < z->s->img_n; ++which) - if (z->img_comp[which].id == id) - break; - if (which == z->s->img_n) return 0; // no match - z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); - z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); - z->order[i] = which; - } - - { - int aa; - z->spec_start = stbi__get8(z->s); - z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 - aa = stbi__get8(z->s); - z->succ_high = (aa >> 4); - z->succ_low = (aa & 15); - if (z->progressive) { - if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) - return stbi__err("bad SOS", "Corrupt JPEG"); - } else { - if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); - if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); - z->spec_end = 63; - } - } - - return 1; -} - -static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) -{ - int i; - for (i=0; i < ncomp; ++i) { - if (z->img_comp[i].raw_data) { - STBI_FREE(z->img_comp[i].raw_data); - z->img_comp[i].raw_data = NULL; - z->img_comp[i].data = NULL; - } - if (z->img_comp[i].raw_coeff) { - STBI_FREE(z->img_comp[i].raw_coeff); - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].coeff = 0; - } - if (z->img_comp[i].linebuf) { - STBI_FREE(z->img_comp[i].linebuf); - z->img_comp[i].linebuf = NULL; - } - } - return why; -} - -static int stbi__process_frame_header(stbi__jpeg *z, int scan) -{ - stbi__context *s = z->s; - int Lf,p,i,q, h_max=1,v_max=1,c; - Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG - p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline - s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG - s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - c = stbi__get8(s); - if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); - s->img_n = c; - for (i=0; i < c; ++i) { - z->img_comp[i].data = NULL; - z->img_comp[i].linebuf = NULL; - } - - if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); - - z->rgb = 0; - for (i=0; i < s->img_n; ++i) { - static const unsigned char rgb[3] = { 'R', 'G', 'B' }; - z->img_comp[i].id = stbi__get8(s); - if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) - ++z->rgb; - q = stbi__get8(s); - z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); - z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); - z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); - } - - if (scan != STBI__SCAN_load) return 1; - - if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); - - for (i=0; i < s->img_n; ++i) { - if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; - if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; - } - - // compute interleaved mcu info - z->img_h_max = h_max; - z->img_v_max = v_max; - z->img_mcu_w = h_max * 8; - z->img_mcu_h = v_max * 8; - // these sizes can't be more than 17 bits - z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; - z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; - - for (i=0; i < s->img_n; ++i) { - // number of effective pixels (e.g. for non-interleaved MCU) - z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; - z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; - // to simplify generation, we'll allocate enough memory to decode - // the bogus oversized data from using interleaved MCUs and their - // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't - // discard the extra data until colorspace conversion - // - // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) - // so these muls can't overflow with 32-bit ints (which we require) - z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; - z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; - z->img_comp[i].coeff = 0; - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].linebuf = NULL; - z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); - if (z->img_comp[i].raw_data == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - // align blocks for idct using mmx/sse - z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); - if (z->progressive) { - // w2, h2 are multiples of 8 (see above) - z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; - z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; - z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); - if (z->img_comp[i].raw_coeff == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); - } - } - - return 1; -} - -// use comparisons since in some cases we handle more than one case (e.g. SOF) -#define stbi__DNL(x) ((x) == 0xdc) -#define stbi__SOI(x) ((x) == 0xd8) -#define stbi__EOI(x) ((x) == 0xd9) -#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) -#define stbi__SOS(x) ((x) == 0xda) - -#define stbi__SOF_progressive(x) ((x) == 0xc2) - -static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) -{ - int m; - z->jfif = 0; - z->app14_color_transform = -1; // valid values are 0,1,2 - z->marker = STBI__MARKER_none; // initialize cached marker to empty - m = stbi__get_marker(z); - if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); - if (scan == STBI__SCAN_type) return 1; - m = stbi__get_marker(z); - while (!stbi__SOF(m)) { - if (!stbi__process_marker(z,m)) return 0; - m = stbi__get_marker(z); - while (m == STBI__MARKER_none) { - // some files have extra padding after their blocks, so ok, we'll scan - if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); - m = stbi__get_marker(z); - } - } - z->progressive = stbi__SOF_progressive(m); - if (!stbi__process_frame_header(z, scan)) return 0; - return 1; -} - -// decode image to YCbCr format -static int stbi__decode_jpeg_image(stbi__jpeg *j) -{ - int m; - for (m = 0; m < 4; m++) { - j->img_comp[m].raw_data = NULL; - j->img_comp[m].raw_coeff = NULL; - } - j->restart_interval = 0; - if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; - m = stbi__get_marker(j); - while (!stbi__EOI(m)) { - if (stbi__SOS(m)) { - if (!stbi__process_scan_header(j)) return 0; - if (!stbi__parse_entropy_coded_data(j)) return 0; - if (j->marker == STBI__MARKER_none ) { - // handle 0s at the end of image data from IP Kamera 9060 - while (!stbi__at_eof(j->s)) { - int x = stbi__get8(j->s); - if (x == 255) { - j->marker = stbi__get8(j->s); - break; - } - } - // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 - } - } else if (stbi__DNL(m)) { - int Ld = stbi__get16be(j->s); - stbi__uint32 NL = stbi__get16be(j->s); - if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); - if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); - } else { - if (!stbi__process_marker(j, m)) return 0; - } - m = stbi__get_marker(j); - } - if (j->progressive) - stbi__jpeg_finish(j); - return 1; -} - -// static jfif-centered resampling (across block boundaries) - -typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, - int w, int hs); - -#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) - -static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - STBI_NOTUSED(out); - STBI_NOTUSED(in_far); - STBI_NOTUSED(w); - STBI_NOTUSED(hs); - return in_near; -} - -static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples vertically for every one in input - int i; - STBI_NOTUSED(hs); - for (i=0; i < w; ++i) - out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); - return out; -} - -static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples horizontally for every one in input - int i; - stbi_uc *input = in_near; - - if (w == 1) { - // if only one sample, can't do any interpolation - out[0] = out[1] = input[0]; - return out; - } - - out[0] = input[0]; - out[1] = stbi__div4(input[0]*3 + input[1] + 2); - for (i=1; i < w-1; ++i) { - int n = 3*input[i]+2; - out[i*2+0] = stbi__div4(n+input[i-1]); - out[i*2+1] = stbi__div4(n+input[i+1]); - } - out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); - out[i*2+1] = input[w-1]; - - STBI_NOTUSED(in_far); - STBI_NOTUSED(hs); - - return out; -} - -#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) - -static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i,t0,t1; - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - out[0] = stbi__div4(t1+2); - for (i=1; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i=0,t0,t1; - - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - // process groups of 8 pixels for as long as we can. - // note we can't handle the last pixel in a row in this loop - // because we need to handle the filter boundary conditions. - for (; i < ((w-1) & ~7); i += 8) { -#if defined(STBI_SSE2) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - __m128i zero = _mm_setzero_si128(); - __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); - __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); - __m128i farw = _mm_unpacklo_epi8(farb, zero); - __m128i nearw = _mm_unpacklo_epi8(nearb, zero); - __m128i diff = _mm_sub_epi16(farw, nearw); - __m128i nears = _mm_slli_epi16(nearw, 2); - __m128i curr = _mm_add_epi16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - __m128i prv0 = _mm_slli_si128(curr, 2); - __m128i nxt0 = _mm_srli_si128(curr, 2); - __m128i prev = _mm_insert_epi16(prv0, t1, 0); - __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - __m128i bias = _mm_set1_epi16(8); - __m128i curs = _mm_slli_epi16(curr, 2); - __m128i prvd = _mm_sub_epi16(prev, curr); - __m128i nxtd = _mm_sub_epi16(next, curr); - __m128i curb = _mm_add_epi16(curs, bias); - __m128i even = _mm_add_epi16(prvd, curb); - __m128i odd = _mm_add_epi16(nxtd, curb); - - // interleave even and odd pixels, then undo scaling. - __m128i int0 = _mm_unpacklo_epi16(even, odd); - __m128i int1 = _mm_unpackhi_epi16(even, odd); - __m128i de0 = _mm_srli_epi16(int0, 4); - __m128i de1 = _mm_srli_epi16(int1, 4); - - // pack and write output - __m128i outv = _mm_packus_epi16(de0, de1); - _mm_storeu_si128((__m128i *) (out + i*2), outv); -#elif defined(STBI_NEON) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - uint8x8_t farb = vld1_u8(in_far + i); - uint8x8_t nearb = vld1_u8(in_near + i); - int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); - int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); - int16x8_t curr = vaddq_s16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - int16x8_t prv0 = vextq_s16(curr, curr, 7); - int16x8_t nxt0 = vextq_s16(curr, curr, 1); - int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); - int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - int16x8_t curs = vshlq_n_s16(curr, 2); - int16x8_t prvd = vsubq_s16(prev, curr); - int16x8_t nxtd = vsubq_s16(next, curr); - int16x8_t even = vaddq_s16(curs, prvd); - int16x8_t odd = vaddq_s16(curs, nxtd); - - // undo scaling and round, then store with even/odd phases interleaved - uint8x8x2_t o; - o.val[0] = vqrshrun_n_s16(even, 4); - o.val[1] = vqrshrun_n_s16(odd, 4); - vst2_u8(out + i*2, o); -#endif - - // "previous" value for next iter - t1 = 3*in_near[i+7] + in_far[i+7]; - } - - t0 = t1; - t1 = 3*in_near[i] + in_far[i]; - out[i*2] = stbi__div16(3*t1 + t0 + 8); - - for (++i; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} -#endif - -static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // resample with nearest-neighbor - int i,j; - STBI_NOTUSED(in_far); - for (i=0; i < w; ++i) - for (j=0; j < hs; ++j) - out[i*hs+j] = in_near[i]; - return out; -} - -// this is a reduced-precision calculation of YCbCr-to-RGB introduced -// to make sure the code produces the same results in both SIMD and scalar -#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) -static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) -{ - int i; - for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) -{ - int i = 0; - -#ifdef STBI_SSE2 - // step == 3 is pretty ugly on the final interleave, and i'm not convinced - // it's useful in practice (you wouldn't use it for textures, for example). - // so just accelerate step == 4 case. - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - __m128i signflip = _mm_set1_epi8(-0x80); - __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); - __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); - __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); - __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); - __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); - __m128i xw = _mm_set1_epi16(255); // alpha channel - - for (; i+7 < count; i += 8) { - // load - __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); - __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); - __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); - __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 - __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 - - // unpack to short (and left-shift cr, cb by 8) - __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); - __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); - __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); - - // color transform - __m128i yws = _mm_srli_epi16(yw, 4); - __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); - __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); - __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); - __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); - __m128i rws = _mm_add_epi16(cr0, yws); - __m128i gwt = _mm_add_epi16(cb0, yws); - __m128i bws = _mm_add_epi16(yws, cb1); - __m128i gws = _mm_add_epi16(gwt, cr1); - - // descale - __m128i rw = _mm_srai_epi16(rws, 4); - __m128i bw = _mm_srai_epi16(bws, 4); - __m128i gw = _mm_srai_epi16(gws, 4); - - // back to byte, set up for transpose - __m128i brb = _mm_packus_epi16(rw, bw); - __m128i gxb = _mm_packus_epi16(gw, xw); - - // transpose to interleave channels - __m128i t0 = _mm_unpacklo_epi8(brb, gxb); - __m128i t1 = _mm_unpackhi_epi8(brb, gxb); - __m128i o0 = _mm_unpacklo_epi16(t0, t1); - __m128i o1 = _mm_unpackhi_epi16(t0, t1); - - // store - _mm_storeu_si128((__m128i *) (out + 0), o0); - _mm_storeu_si128((__m128i *) (out + 16), o1); - out += 32; - } - } -#endif - -#ifdef STBI_NEON - // in this version, step=3 support would be easy to add. but is there demand? - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - uint8x8_t signflip = vdup_n_u8(0x80); - int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); - int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); - int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); - int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); - - for (; i+7 < count; i += 8) { - // load - uint8x8_t y_bytes = vld1_u8(y + i); - uint8x8_t cr_bytes = vld1_u8(pcr + i); - uint8x8_t cb_bytes = vld1_u8(pcb + i); - int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); - int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); - - // expand to s16 - int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); - int16x8_t crw = vshll_n_s8(cr_biased, 7); - int16x8_t cbw = vshll_n_s8(cb_biased, 7); - - // color transform - int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); - int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); - int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); - int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); - int16x8_t rws = vaddq_s16(yws, cr0); - int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); - int16x8_t bws = vaddq_s16(yws, cb1); - - // undo scaling, round, convert to byte - uint8x8x4_t o; - o.val[0] = vqrshrun_n_s16(rws, 4); - o.val[1] = vqrshrun_n_s16(gws, 4); - o.val[2] = vqrshrun_n_s16(bws, 4); - o.val[3] = vdup_n_u8(255); - - // store, interleaving r/g/b/a - vst4_u8(out, o); - out += 8*4; - } - } -#endif - - for (; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} -#endif - -// set up the kernels -static void stbi__setup_jpeg(stbi__jpeg *j) -{ - j->idct_block_kernel = stbi__idct_block; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; - -#ifdef STBI_SSE2 - if (stbi__sse2_available()) { - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; - } -#endif - -#ifdef STBI_NEON - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; -#endif -} - -// clean up the temporary component buffers -static void stbi__cleanup_jpeg(stbi__jpeg *j) -{ - stbi__free_jpeg_components(j, j->s->img_n, 0); -} - -typedef struct -{ - resample_row_func resample; - stbi_uc *line0,*line1; - int hs,vs; // expansion factor in each axis - int w_lores; // horizontal pixels pre-expansion - int ystep; // how far through vertical expansion we are - int ypos; // which pre-expansion row we're on -} stbi__resample; - -// fast 0..255 * 0..255 => 0..255 rounded multiplication -static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) -{ - unsigned int t = x*y + 128; - return (stbi_uc) ((t + (t >>8)) >> 8); -} - -static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) -{ - int n, decode_n, is_rgb; - z->s->img_n = 0; // make stbi__cleanup_jpeg safe - - // validate req_comp - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - - // load a jpeg image from whichever source, but leave in YCbCr format - if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } - - // determine actual number of components to generate - n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; - - is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); - - if (z->s->img_n == 3 && n < 3 && !is_rgb) - decode_n = 1; - else - decode_n = z->s->img_n; - - // resample and color-convert - { - int k; - unsigned int i,j; - stbi_uc *output; - stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; - - stbi__resample res_comp[4]; - - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - - // allocate line buffer big enough for upsampling off the edges - // with upsample factor of 4 - z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); - if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - r->hs = z->img_h_max / z->img_comp[k].h; - r->vs = z->img_v_max / z->img_comp[k].v; - r->ystep = r->vs >> 1; - r->w_lores = (z->s->img_x + r->hs-1) / r->hs; - r->ypos = 0; - r->line0 = r->line1 = z->img_comp[k].data; - - if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; - else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; - else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; - else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; - else r->resample = stbi__resample_row_generic; - } - - // can't error after this so, this is safe - output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); - if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - // now go ahead and resample - for (j=0; j < z->s->img_y; ++j) { - stbi_uc *out = output + n * z->s->img_x * j; - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - int y_bot = r->ystep >= (r->vs >> 1); - coutput[k] = r->resample(z->img_comp[k].linebuf, - y_bot ? r->line1 : r->line0, - y_bot ? r->line0 : r->line1, - r->w_lores, r->hs); - if (++r->ystep >= r->vs) { - r->ystep = 0; - r->line0 = r->line1; - if (++r->ypos < z->img_comp[k].y) - r->line1 += z->img_comp[k].w2; - } - } - if (n >= 3) { - stbi_uc *y = coutput[0]; - if (z->s->img_n == 3) { - if (is_rgb) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = y[i]; - out[1] = coutput[1][i]; - out[2] = coutput[2][i]; - out[3] = 255; - out += n; - } - } else { - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else if (z->s->img_n == 4) { - if (z->app14_color_transform == 0) { // CMYK - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(coutput[0][i], m); - out[1] = stbi__blinn_8x8(coutput[1][i], m); - out[2] = stbi__blinn_8x8(coutput[2][i], m); - out[3] = 255; - out += n; - } - } else if (z->app14_color_transform == 2) { // YCCK - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(255 - out[0], m); - out[1] = stbi__blinn_8x8(255 - out[1], m); - out[2] = stbi__blinn_8x8(255 - out[2], m); - out += n; - } - } else { // YCbCr + alpha? Ignore the fourth channel for now - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else - for (i=0; i < z->s->img_x; ++i) { - out[0] = out[1] = out[2] = y[i]; - out[3] = 255; // not used if n==3 - out += n; - } - } else { - if (is_rgb) { - if (n == 1) - for (i=0; i < z->s->img_x; ++i) - *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - else { - for (i=0; i < z->s->img_x; ++i, out += 2) { - out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - out[1] = 255; - } - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); - stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); - stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); - out[0] = stbi__compute_y(r, g, b); - out[1] = 255; - out += n; - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); - out[1] = 255; - out += n; - } - } else { - stbi_uc *y = coutput[0]; - if (n == 1) - for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; - else - for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } - } - } - } - stbi__cleanup_jpeg(z); - *out_x = z->s->img_x; - *out_y = z->s->img_y; - if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output - return output; - } -} - -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - unsigned char* result; - stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); - STBI_NOTUSED(ri); - j->s = s; - stbi__setup_jpeg(j); - result = load_jpeg_image(j, x,y,comp,req_comp); - STBI_FREE(j); - return result; -} - -static int stbi__jpeg_test(stbi__context *s) -{ - int r; - stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); - j->s = s; - stbi__setup_jpeg(j); - r = stbi__decode_jpeg_header(j, STBI__SCAN_type); - stbi__rewind(s); - STBI_FREE(j); - return r; -} - -static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) -{ - if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { - stbi__rewind( j->s ); - return 0; - } - if (x) *x = j->s->img_x; - if (y) *y = j->s->img_y; - if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; - return 1; -} - -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) -{ - int result; - stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); - j->s = s; - result = stbi__jpeg_info_raw(j, x, y, comp); - STBI_FREE(j); - return result; -} -#endif - -// public domain zlib decode v0.2 Sean Barrett 2006-11-18 -// simple implementation -// - all input must be provided in an upfront buffer -// - all output is written to a single output buffer (can malloc/realloc) -// performance -// - fast huffman - -#ifndef STBI_NO_ZLIB - -// fast-way is faster to check than jpeg huffman, but slow way is slower -#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables -#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) - -// zlib-style huffman encoding -// (jpegs packs from left, zlib from right, so can't share code) -typedef struct -{ - stbi__uint16 fast[1 << STBI__ZFAST_BITS]; - stbi__uint16 firstcode[16]; - int maxcode[17]; - stbi__uint16 firstsymbol[16]; - stbi_uc size[288]; - stbi__uint16 value[288]; -} stbi__zhuffman; - -stbi_inline static int stbi__bitreverse16(int n) -{ - n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); - n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); - n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); - n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); - return n; -} - -stbi_inline static int stbi__bit_reverse(int v, int bits) -{ - STBI_ASSERT(bits <= 16); - // to bit reverse n bits, reverse 16 and shift - // e.g. 11 bits, bit reverse and shift away 5 - return stbi__bitreverse16(v) >> (16-bits); -} - -static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) -{ - int i,k=0; - int code, next_code[16], sizes[17]; - - // DEFLATE spec for generating codes - memset(sizes, 0, sizeof(sizes)); - memset(z->fast, 0, sizeof(z->fast)); - for (i=0; i < num; ++i) - ++sizes[sizelist[i]]; - sizes[0] = 0; - for (i=1; i < 16; ++i) - if (sizes[i] > (1 << i)) - return stbi__err("bad sizes", "Corrupt PNG"); - code = 0; - for (i=1; i < 16; ++i) { - next_code[i] = code; - z->firstcode[i] = (stbi__uint16) code; - z->firstsymbol[i] = (stbi__uint16) k; - code = (code + sizes[i]); - if (sizes[i]) - if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); - z->maxcode[i] = code << (16-i); // preshift for inner loop - code <<= 1; - k += sizes[i]; - } - z->maxcode[16] = 0x10000; // sentinel - for (i=0; i < num; ++i) { - int s = sizelist[i]; - if (s) { - int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; - stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); - z->size [c] = (stbi_uc ) s; - z->value[c] = (stbi__uint16) i; - if (s <= STBI__ZFAST_BITS) { - int j = stbi__bit_reverse(next_code[s],s); - while (j < (1 << STBI__ZFAST_BITS)) { - z->fast[j] = fastv; - j += (1 << s); - } - } - ++next_code[s]; - } - } - return 1; -} - -// zlib-from-memory implementation for PNG reading -// because PNG allows splitting the zlib stream arbitrarily, -// and it's annoying structurally to have PNG call ZLIB call PNG, -// we require PNG read all the IDATs and combine them into a single -// memory buffer - -typedef struct -{ - stbi_uc *zbuffer, *zbuffer_end; - int num_bits; - stbi__uint32 code_buffer; - - char *zout; - char *zout_start; - char *zout_end; - int z_expandable; - - stbi__zhuffman z_length, z_distance; -} stbi__zbuf; - -stbi_inline static int stbi__zeof(stbi__zbuf *z) -{ - return (z->zbuffer >= z->zbuffer_end); -} - -stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) -{ - return stbi__zeof(z) ? 0 : *z->zbuffer++; -} - -static void stbi__fill_bits(stbi__zbuf *z) -{ - do { - if (z->code_buffer >= (1U << z->num_bits)) { - z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ - return; - } - z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; - z->num_bits += 8; - } while (z->num_bits <= 24); -} - -stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) -{ - unsigned int k; - if (z->num_bits < n) stbi__fill_bits(z); - k = z->code_buffer & ((1 << n) - 1); - z->code_buffer >>= n; - z->num_bits -= n; - return k; -} - -static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s,k; - // not resolved by fast table, so compute it the slow way - // use jpeg approach, which requires MSbits at top - k = stbi__bit_reverse(a->code_buffer, 16); - for (s=STBI__ZFAST_BITS+1; ; ++s) - if (k < z->maxcode[s]) - break; - if (s >= 16) return -1; // invalid code! - // code size is s, so: - b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - if ((unsigned int)b >= sizeof (z->size)) return -1; // some data was corrupt somewhere! - if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; -} - -stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s; - if (a->num_bits < 16) { - if (stbi__zeof(a)) { - return -1; /* report error for unexpected end of data. */ - } - stbi__fill_bits(a); - } - b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; - if (b) { - s = b >> 9; - a->code_buffer >>= s; - a->num_bits -= s; - return b & 511; - } - return stbi__zhuffman_decode_slowpath(a, z); -} - -static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes -{ - char *q; - unsigned int cur, limit, old_limit; - z->zout = zout; - if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); - cur = (unsigned int) (z->zout - z->zout_start); - limit = old_limit = (unsigned) (z->zout_end - z->zout_start); - if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); - while (cur + n > limit) { - if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); - limit *= 2; - } - q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); - STBI_NOTUSED(old_limit); - if (q == NULL) return stbi__err("outofmem", "Out of memory"); - z->zout_start = q; - z->zout = q + cur; - z->zout_end = q + limit; - return 1; -} - -static const int stbi__zlength_base[31] = { - 3,4,5,6,7,8,9,10,11,13, - 15,17,19,23,27,31,35,43,51,59, - 67,83,99,115,131,163,195,227,258,0,0 }; - -static const int stbi__zlength_extra[31]= -{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; - -static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, -257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; - -static const int stbi__zdist_extra[32] = -{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; - -static int stbi__parse_huffman_block(stbi__zbuf *a) -{ - char *zout = a->zout; - for(;;) { - int z = stbi__zhuffman_decode(a, &a->z_length); - if (z < 256) { - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes - if (zout >= a->zout_end) { - if (!stbi__zexpand(a, zout, 1)) return 0; - zout = a->zout; - } - *zout++ = (char) z; - } else { - stbi_uc *p; - int len,dist; - if (z == 256) { - a->zout = zout; - return 1; - } - z -= 257; - len = stbi__zlength_base[z]; - if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); - z = stbi__zhuffman_decode(a, &a->z_distance); - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); - dist = stbi__zdist_base[z]; - if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); - if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); - if (zout + len > a->zout_end) { - if (!stbi__zexpand(a, zout, len)) return 0; - zout = a->zout; - } - p = (stbi_uc *) (zout - dist); - if (dist == 1) { // run of one byte; common in images. - stbi_uc v = *p; - if (len) { do *zout++ = v; while (--len); } - } else { - if (len) { do *zout++ = *p++; while (--len); } - } - } - } -} - -static int stbi__compute_huffman_codes(stbi__zbuf *a) -{ - static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; - stbi__zhuffman z_codelength; - stbi_uc lencodes[286+32+137];//padding for maximum single op - stbi_uc codelength_sizes[19]; - int i,n; - - int hlit = stbi__zreceive(a,5) + 257; - int hdist = stbi__zreceive(a,5) + 1; - int hclen = stbi__zreceive(a,4) + 4; - int ntot = hlit + hdist; - - memset(codelength_sizes, 0, sizeof(codelength_sizes)); - for (i=0; i < hclen; ++i) { - int s = stbi__zreceive(a,3); - codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; - } - if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; - - n = 0; - while (n < ntot) { - int c = stbi__zhuffman_decode(a, &z_codelength); - if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); - if (c < 16) - lencodes[n++] = (stbi_uc) c; - else { - stbi_uc fill = 0; - if (c == 16) { - c = stbi__zreceive(a,2)+3; - if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); - fill = lencodes[n-1]; - } else if (c == 17) { - c = stbi__zreceive(a,3)+3; - } else if (c == 18) { - c = stbi__zreceive(a,7)+11; - } else { - return stbi__err("bad codelengths", "Corrupt PNG"); - } - if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); - memset(lencodes+n, fill, c); - n += c; - } - } - if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); - if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; - return 1; -} - -static int stbi__parse_uncompressed_block(stbi__zbuf *a) -{ - stbi_uc header[4]; - int len,nlen,k; - if (a->num_bits & 7) - stbi__zreceive(a, a->num_bits & 7); // discard - // drain the bit-packed data into header - k = 0; - while (a->num_bits > 0) { - header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check - a->code_buffer >>= 8; - a->num_bits -= 8; - } - if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); - // now fill header the normal way - while (k < 4) - header[k++] = stbi__zget8(a); - len = header[1] * 256 + header[0]; - nlen = header[3] * 256 + header[2]; - if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); - if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); - if (a->zout + len > a->zout_end) - if (!stbi__zexpand(a, a->zout, len)) return 0; - memcpy(a->zout, a->zbuffer, len); - a->zbuffer += len; - a->zout += len; - return 1; -} - -static int stbi__parse_zlib_header(stbi__zbuf *a) -{ - int cmf = stbi__zget8(a); - int cm = cmf & 15; - /* int cinfo = cmf >> 4; */ - int flg = stbi__zget8(a); - if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec - if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec - if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png - if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png - // window = 1 << (8 + cinfo)... but who cares, we fully buffer output - return 1; -} - -static const stbi_uc stbi__zdefault_length[288] = -{ - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 -}; -static const stbi_uc stbi__zdefault_distance[32] = -{ - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 -}; -/* -Init algorithm: -{ - int i; // use <= to match clearly with spec - for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; - for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; - for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; - for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; - - for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; -} -*/ - -static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) -{ - int final, type; - if (parse_header) - if (!stbi__parse_zlib_header(a)) return 0; - a->num_bits = 0; - a->code_buffer = 0; - do { - final = stbi__zreceive(a,1); - type = stbi__zreceive(a,2); - if (type == 0) { - if (!stbi__parse_uncompressed_block(a)) return 0; - } else if (type == 3) { - return 0; - } else { - if (type == 1) { - // use fixed code lengths - if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; - } else { - if (!stbi__compute_huffman_codes(a)) return 0; - } - if (!stbi__parse_huffman_block(a)) return 0; - } - } while (!final); - return 1; -} - -static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) -{ - a->zout_start = obuf; - a->zout = obuf; - a->zout_end = obuf + olen; - a->z_expandable = exp; - - return stbi__parse_zlib(a, parse_header); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) -{ - return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) - return (int) (a.zout - a.zout_start); - else - return -1; -} - -STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(16384); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer+len; - if (stbi__do_zlib(&a, p, 16384, 1, 0)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) - return (int) (a.zout - a.zout_start); - else - return -1; -} -#endif - -// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 -// simple implementation -// - only 8-bit samples -// - no CRC checking -// - allocates lots of intermediate memory -// - avoids problem of streaming data between subsystems -// - avoids explicit window management -// performance -// - uses stb_zlib, a PD zlib implementation with fast huffman decoding - -#ifndef STBI_NO_PNG -typedef struct -{ - stbi__uint32 length; - stbi__uint32 type; -} stbi__pngchunk; - -static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) -{ - stbi__pngchunk c; - c.length = stbi__get32be(s); - c.type = stbi__get32be(s); - return c; -} - -static int stbi__check_png_header(stbi__context *s) -{ - static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; - int i; - for (i=0; i < 8; ++i) - if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); - return 1; -} - -typedef struct -{ - stbi__context *s; - stbi_uc *idata, *expanded, *out; - int depth; -} stbi__png; - - -enum { - STBI__F_none=0, - STBI__F_sub=1, - STBI__F_up=2, - STBI__F_avg=3, - STBI__F_paeth=4, - // synthetic filters used for first scanline to avoid needing a dummy row of 0s - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static stbi_uc first_row_filter[5] = -{ - STBI__F_none, - STBI__F_sub, - STBI__F_none, - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static int stbi__paeth(int a, int b, int c) -{ - int p = a + b - c; - int pa = abs(p-a); - int pb = abs(p-b); - int pc = abs(p-c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; -} - -static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; - -// create the png data from post-deflated data -static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) -{ - int bytes = (depth == 16? 2 : 1); - stbi__context *s = a->s; - stbi__uint32 i,j,stride = x*out_n*bytes; - stbi__uint32 img_len, img_width_bytes; - int k; - int img_n = s->img_n; // copy it into a local for later - - int output_bytes = out_n*bytes; - int filter_bytes = img_n*bytes; - int width = x; - - STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); - a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into - if (!a->out) return stbi__err("outofmem", "Out of memory"); - - if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); - img_width_bytes = (((img_n * x * depth) + 7) >> 3); - img_len = (img_width_bytes + 1) * y; - - // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, - // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), - // so just check for raw_len < img_len always. - if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); - - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *prior; - int filter = *raw++; - - if (filter > 4) - return stbi__err("invalid filter","Corrupt PNG"); - - if (depth < 8) { - if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); - cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place - filter_bytes = 1; - width = img_width_bytes; - } - prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above - - // if first row, use special filter that doesn't sample previous row - if (j == 0) filter = first_row_filter[filter]; - - // handle first byte explicitly - for (k=0; k < filter_bytes; ++k) { - switch (filter) { - case STBI__F_none : cur[k] = raw[k]; break; - case STBI__F_sub : cur[k] = raw[k]; break; - case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; - case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; - case STBI__F_avg_first : cur[k] = raw[k]; break; - case STBI__F_paeth_first: cur[k] = raw[k]; break; - } - } - - if (depth == 8) { - if (img_n != out_n) - cur[img_n] = 255; // first pixel - raw += img_n; - cur += out_n; - prior += out_n; - } else if (depth == 16) { - if (img_n != out_n) { - cur[filter_bytes] = 255; // first pixel top byte - cur[filter_bytes+1] = 255; // first pixel bottom byte - } - raw += filter_bytes; - cur += output_bytes; - prior += output_bytes; - } else { - raw += 1; - cur += 1; - prior += 1; - } - - // this is a little gross, so that we don't switch per-pixel or per-component - if (depth < 8 || img_n == out_n) { - int nk = (width - 1)*filter_bytes; - #define STBI__CASE(f) \ - case f: \ - for (k=0; k < nk; ++k) - switch (filter) { - // "none" filter turns into a memcpy here; make that explicit. - case STBI__F_none: memcpy(cur, raw, nk); break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; - } - #undef STBI__CASE - raw += nk; - } else { - STBI_ASSERT(img_n+1 == out_n); - #define STBI__CASE(f) \ - case f: \ - for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ - for (k=0; k < filter_bytes; ++k) - switch (filter) { - STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; - } - #undef STBI__CASE - - // the loop above sets the high byte of the pixels' alpha, but for - // 16 bit png files we also need the low byte set. we'll do that here. - if (depth == 16) { - cur = a->out + stride*j; // start at the beginning of the row again - for (i=0; i < x; ++i,cur+=output_bytes) { - cur[filter_bytes+1] = 255; - } - } - } - } - - // we make a separate pass to expand bits to pixels; for performance, - // this could run two scanlines behind the above code, so it won't - // intefere with filtering but will still be in the cache. - if (depth < 8) { - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; - // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit - // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop - stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range - - // note that the final byte might overshoot and write more data than desired. - // we can allocate enough data that this never writes out of memory, but it - // could also overwrite the next scanline. can it overwrite non-empty data - // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. - // so we need to explicitly clamp the final ones - - if (depth == 4) { - for (k=x*img_n; k >= 2; k-=2, ++in) { - *cur++ = scale * ((*in >> 4) ); - *cur++ = scale * ((*in ) & 0x0f); - } - if (k > 0) *cur++ = scale * ((*in >> 4) ); - } else if (depth == 2) { - for (k=x*img_n; k >= 4; k-=4, ++in) { - *cur++ = scale * ((*in >> 6) ); - *cur++ = scale * ((*in >> 4) & 0x03); - *cur++ = scale * ((*in >> 2) & 0x03); - *cur++ = scale * ((*in ) & 0x03); - } - if (k > 0) *cur++ = scale * ((*in >> 6) ); - if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); - if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); - } else if (depth == 1) { - for (k=x*img_n; k >= 8; k-=8, ++in) { - *cur++ = scale * ((*in >> 7) ); - *cur++ = scale * ((*in >> 6) & 0x01); - *cur++ = scale * ((*in >> 5) & 0x01); - *cur++ = scale * ((*in >> 4) & 0x01); - *cur++ = scale * ((*in >> 3) & 0x01); - *cur++ = scale * ((*in >> 2) & 0x01); - *cur++ = scale * ((*in >> 1) & 0x01); - *cur++ = scale * ((*in ) & 0x01); - } - if (k > 0) *cur++ = scale * ((*in >> 7) ); - if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); - if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); - if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); - if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); - if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); - if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); - } - if (img_n != out_n) { - int q; - // insert alpha = 255 - cur = a->out + stride*j; - if (img_n == 1) { - for (q=x-1; q >= 0; --q) { - cur[q*2+1] = 255; - cur[q*2+0] = cur[q]; - } - } else { - STBI_ASSERT(img_n == 3); - for (q=x-1; q >= 0; --q) { - cur[q*4+3] = 255; - cur[q*4+2] = cur[q*3+2]; - cur[q*4+1] = cur[q*3+1]; - cur[q*4+0] = cur[q*3+0]; - } - } - } - } - } else if (depth == 16) { - // force the image data from big-endian to platform-native. - // this is done in a separate pass due to the decoding relying - // on the data being untouched, but could probably be done - // per-line during decode if care is taken. - stbi_uc *cur = a->out; - stbi__uint16 *cur16 = (stbi__uint16*)cur; - - for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { - *cur16 = (cur[0] << 8) | cur[1]; - } - } - - return 1; -} - -static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) -{ - int bytes = (depth == 16 ? 2 : 1); - int out_bytes = out_n * bytes; - stbi_uc *final; - int p; - if (!interlaced) - return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); - - // de-interlacing - final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); - for (p=0; p < 7; ++p) { - int xorig[] = { 0,4,0,2,0,1,0 }; - int yorig[] = { 0,0,4,0,2,0,1 }; - int xspc[] = { 8,8,4,4,2,2,1 }; - int yspc[] = { 8,8,8,4,4,2,2 }; - int i,j,x,y; - // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 - x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; - y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; - if (x && y) { - stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; - if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { - STBI_FREE(final); - return 0; - } - for (j=0; j < y; ++j) { - for (i=0; i < x; ++i) { - int out_y = j*yspc[p]+yorig[p]; - int out_x = i*xspc[p]+xorig[p]; - memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, - a->out + (j*x+i)*out_bytes, out_bytes); - } - } - STBI_FREE(a->out); - image_data += img_len; - image_data_len -= img_len; - } - } - a->out = final; - - return 1; -} - -static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - // compute color-based transparency, assuming we've - // already got 255 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i=0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 255); - p += 2; - } - } else { - for (i=0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi__uint16 *p = (stbi__uint16*) z->out; - - // compute color-based transparency, assuming we've - // already got 65535 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i = 0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 65535); - p += 2; - } - } else { - for (i = 0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) -{ - stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; - stbi_uc *p, *temp_out, *orig = a->out; - - p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); - if (p == NULL) return stbi__err("outofmem", "Out of memory"); - - // between here and free(out) below, exitting would leak - temp_out = p; - - if (pal_img_n == 3) { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p += 3; - } - } else { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p[3] = palette[n+3]; - p += 4; - } - } - STBI_FREE(a->out); - a->out = temp_out; - - STBI_NOTUSED(len); - - return 1; -} - -static int stbi__unpremultiply_on_load = 0; -static int stbi__de_iphone_flag = 0; - -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) -{ - stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; -} - -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) -{ - stbi__de_iphone_flag = flag_true_if_should_convert; -} - -static void stbi__de_iphone(stbi__png *z) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - if (s->img_out_n == 3) { // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 3; - } - } else { - STBI_ASSERT(s->img_out_n == 4); - if (stbi__unpremultiply_on_load) { - // convert bgr to rgb and unpremultiply - for (i=0; i < pixel_count; ++i) { - stbi_uc a = p[3]; - stbi_uc t = p[0]; - if (a) { - stbi_uc half = a / 2; - p[0] = (p[2] * 255 + half) / a; - p[1] = (p[1] * 255 + half) / a; - p[2] = ( t * 255 + half) / a; - } else { - p[0] = p[2]; - p[2] = t; - } - p += 4; - } - } else { - // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 4; - } - } - } -} - -#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) - -static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) -{ - stbi_uc palette[1024], pal_img_n=0; - stbi_uc has_trans=0, tc[3]={0}; - stbi__uint16 tc16[3]; - stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; - int first=1,k,interlace=0, color=0, is_iphone=0; - stbi__context *s = z->s; - - z->expanded = NULL; - z->idata = NULL; - z->out = NULL; - - if (!stbi__check_png_header(s)) return 0; - - if (scan == STBI__SCAN_type) return 1; - - for (;;) { - stbi__pngchunk c = stbi__get_chunk_header(s); - switch (c.type) { - case STBI__PNG_TYPE('C','g','B','I'): - is_iphone = 1; - stbi__skip(s, c.length); - break; - case STBI__PNG_TYPE('I','H','D','R'): { - int comp,filter; - if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); - first = 0; - if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); - s->img_x = stbi__get32be(s); - s->img_y = stbi__get32be(s); - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); - color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); - comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); - filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); - interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); - if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); - if (!pal_img_n) { - s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - if (scan == STBI__SCAN_header) return 1; - } else { - // if paletted, then pal_n is our final components, and - // img_n is # components to decompress/filter. - s->img_n = 1; - if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); - // if SCAN_header, have to scan to see if we have a tRNS - } - break; - } - - case STBI__PNG_TYPE('P','L','T','E'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); - pal_len = c.length / 3; - if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); - for (i=0; i < pal_len; ++i) { - palette[i*4+0] = stbi__get8(s); - palette[i*4+1] = stbi__get8(s); - palette[i*4+2] = stbi__get8(s); - palette[i*4+3] = 255; - } - break; - } - - case STBI__PNG_TYPE('t','R','N','S'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); - if (pal_img_n) { - if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } - if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); - if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); - pal_img_n = 4; - for (i=0; i < c.length; ++i) - palette[i*4+3] = stbi__get8(s); - } else { - if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); - if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); - has_trans = 1; - if (z->depth == 16) { - for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is - } else { - for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger - } - } - break; - } - - case STBI__PNG_TYPE('I','D','A','T'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); - if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } - if ((int)(ioff + c.length) < (int)ioff) return 0; - if (ioff + c.length > idata_limit) { - stbi__uint32 idata_limit_old = idata_limit; - stbi_uc *p; - if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; - while (ioff + c.length > idata_limit) - idata_limit *= 2; - STBI_NOTUSED(idata_limit_old); - p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); - z->idata = p; - } - if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); - ioff += c.length; - break; - } - - case STBI__PNG_TYPE('I','E','N','D'): { - stbi__uint32 raw_len, bpl; - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (scan != STBI__SCAN_load) return 1; - if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); - // initial guess for decoded data size to avoid unnecessary reallocs - bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component - raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; - z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); - if (z->expanded == NULL) return 0; // zlib should set error - STBI_FREE(z->idata); z->idata = NULL; - if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) - s->img_out_n = s->img_n+1; - else - s->img_out_n = s->img_n; - if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; - if (has_trans) { - if (z->depth == 16) { - if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; - } else { - if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; - } - } - if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) - stbi__de_iphone(z); - if (pal_img_n) { - // pal_img_n == 3 or 4 - s->img_n = pal_img_n; // record the actual colors we had - s->img_out_n = pal_img_n; - if (req_comp >= 3) s->img_out_n = req_comp; - if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) - return 0; - } else if (has_trans) { - // non-paletted image with tRNS -> source image has (constant) alpha - ++s->img_n; - } - STBI_FREE(z->expanded); z->expanded = NULL; - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - return 1; - } - - default: - // if critical, fail - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if ((c.type & (1 << 29)) == 0) { - #ifndef STBI_NO_FAILURE_STRINGS - // not threadsafe - static char invalid_chunk[] = "XXXX PNG chunk not known"; - invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); - invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); - invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); - invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); - #endif - return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); - } - stbi__skip(s, c.length); - break; - } - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - } -} - -static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) -{ - void *result=NULL; - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth <= 8) - ri->bits_per_channel = 8; - else if (p->depth == 16) - ri->bits_per_channel = 16; - else - return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); - result = p->out; - p->out = NULL; - if (req_comp && req_comp != p->s->img_out_n) { - if (ri->bits_per_channel == 8) - result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - else - result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - p->s->img_out_n = req_comp; - if (result == NULL) return result; - } - *x = p->s->img_x; - *y = p->s->img_y; - if (n) *n = p->s->img_n; - } - STBI_FREE(p->out); p->out = NULL; - STBI_FREE(p->expanded); p->expanded = NULL; - STBI_FREE(p->idata); p->idata = NULL; - - return result; -} - -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi__png p; - p.s = s; - return stbi__do_png(&p, x,y,comp,req_comp, ri); -} - -static int stbi__png_test(stbi__context *s) -{ - int r; - r = stbi__check_png_header(s); - stbi__rewind(s); - return r; -} - -static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) -{ - if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { - stbi__rewind( p->s ); - return 0; - } - if (x) *x = p->s->img_x; - if (y) *y = p->s->img_y; - if (comp) *comp = p->s->img_n; - return 1; -} - -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__png p; - p.s = s; - return stbi__png_info_raw(&p, x, y, comp); -} - -static int stbi__png_is16(stbi__context *s) -{ - stbi__png p; - p.s = s; - if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) - return 0; - if (p.depth != 16) { - stbi__rewind(p.s); - return 0; - } - return 1; -} -#endif - -// Microsoft/Windows BMP image - -#ifndef STBI_NO_BMP -static int stbi__bmp_test_raw(stbi__context *s) -{ - int r; - int sz; - if (stbi__get8(s) != 'B') return 0; - if (stbi__get8(s) != 'M') return 0; - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - stbi__get32le(s); // discard data offset - sz = stbi__get32le(s); - r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); - return r; -} - -static int stbi__bmp_test(stbi__context *s) -{ - int r = stbi__bmp_test_raw(s); - stbi__rewind(s); - return r; -} - - -// returns 0..31 for the highest set bit -static int stbi__high_bit(unsigned int z) -{ - int n=0; - if (z == 0) return -1; - if (z >= 0x10000) { n += 16; z >>= 16; } - if (z >= 0x00100) { n += 8; z >>= 8; } - if (z >= 0x00010) { n += 4; z >>= 4; } - if (z >= 0x00004) { n += 2; z >>= 2; } - if (z >= 0x00002) { n += 1;/* >>= 1;*/ } - return n; -} - -static int stbi__bitcount(unsigned int a) -{ - a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 - a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 - a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits - a = (a + (a >> 8)); // max 16 per 8 bits - a = (a + (a >> 16)); // max 32 per 8 bits - return a & 0xff; -} - -// extract an arbitrarily-aligned N-bit value (N=bits) -// from v, and then make it 8-bits long and fractionally -// extend it to full full range. -static int stbi__shiftsigned(unsigned int v, int shift, int bits) -{ - static unsigned int mul_table[9] = { - 0, - 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, - 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, - }; - static unsigned int shift_table[9] = { - 0, 0,0,1,0,2,4,6,0, - }; - if (shift < 0) - v <<= -shift; - else - v >>= shift; - STBI_ASSERT(v < 256); - v >>= (8-bits); - STBI_ASSERT(bits >= 0 && bits <= 8); - return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; -} - -typedef struct -{ - int bpp, offset, hsz; - unsigned int mr,mg,mb,ma, all_a; - int extra_read; -} stbi__bmp_data; - -static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) -{ - int hsz; - if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - info->offset = stbi__get32le(s); - info->hsz = hsz = stbi__get32le(s); - info->mr = info->mg = info->mb = info->ma = 0; - info->extra_read = 14; - - if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); - - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); - if (hsz == 12) { - s->img_x = stbi__get16le(s); - s->img_y = stbi__get16le(s); - } else { - s->img_x = stbi__get32le(s); - s->img_y = stbi__get32le(s); - } - if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); - info->bpp = stbi__get16le(s); - if (hsz != 12) { - int compress = stbi__get32le(s); - if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); - stbi__get32le(s); // discard sizeof - stbi__get32le(s); // discard hres - stbi__get32le(s); // discard vres - stbi__get32le(s); // discard colorsused - stbi__get32le(s); // discard max important - if (hsz == 40 || hsz == 56) { - if (hsz == 56) { - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - } - if (info->bpp == 16 || info->bpp == 32) { - if (compress == 0) { - if (info->bpp == 32) { - info->mr = 0xffu << 16; - info->mg = 0xffu << 8; - info->mb = 0xffu << 0; - info->ma = 0xffu << 24; - info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 - } else { - info->mr = 31u << 10; - info->mg = 31u << 5; - info->mb = 31u << 0; - } - } else if (compress == 3) { - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->extra_read += 12; - // not documented, but generated by photoshop and handled by mspaint - if (info->mr == info->mg && info->mg == info->mb) { - // ?!?!? - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else { - int i; - if (hsz != 108 && hsz != 124) - return stbi__errpuc("bad BMP", "bad BMP"); - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->ma = stbi__get32le(s); - stbi__get32le(s); // discard color space - for (i=0; i < 12; ++i) - stbi__get32le(s); // discard color space parameters - if (hsz == 124) { - stbi__get32le(s); // discard rendering intent - stbi__get32le(s); // discard offset of profile data - stbi__get32le(s); // discard size of profile data - stbi__get32le(s); // discard reserved - } - } - } - return (void *) 1; -} - - -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - unsigned int mr=0,mg=0,mb=0,ma=0, all_a; - stbi_uc pal[256][4]; - int psize=0,i,j,width; - int flip_vertically, pad, target; - stbi__bmp_data info; - STBI_NOTUSED(ri); - - info.all_a = 255; - if (stbi__bmp_parse_header(s, &info) == NULL) - return NULL; // error code already set - - flip_vertically = ((int) s->img_y) > 0; - s->img_y = abs((int) s->img_y); - - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - mr = info.mr; - mg = info.mg; - mb = info.mb; - ma = info.ma; - all_a = info.all_a; - - if (info.hsz == 12) { - if (info.bpp < 24) - psize = (info.offset - info.extra_read - 24) / 3; - } else { - if (info.bpp < 16) - psize = (info.offset - info.extra_read - info.hsz) >> 2; - } - if (psize == 0) { - STBI_ASSERT(info.offset == s->callback_already_read + (int) (s->img_buffer - s->img_buffer_original)); - if (info.offset != s->callback_already_read + (s->img_buffer - s->buffer_start)) { - return stbi__errpuc("bad offset", "Corrupt BMP"); - } - } - - if (info.bpp == 24 && ma == 0xff000000) - s->img_n = 3; - else - s->img_n = ma ? 4 : 3; - if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 - target = req_comp; - else - target = s->img_n; // if they want monochrome, we'll post-convert - - // sanity-check size - if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) - return stbi__errpuc("too large", "Corrupt BMP"); - - out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - if (info.bpp < 16) { - int z=0; - if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } - for (i=0; i < psize; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - if (info.hsz != 12) stbi__get8(s); - pal[i][3] = 255; - } - stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); - if (info.bpp == 1) width = (s->img_x + 7) >> 3; - else if (info.bpp == 4) width = (s->img_x + 1) >> 1; - else if (info.bpp == 8) width = s->img_x; - else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } - pad = (-width)&3; - if (info.bpp == 1) { - for (j=0; j < (int) s->img_y; ++j) { - int bit_offset = 7, v = stbi__get8(s); - for (i=0; i < (int) s->img_x; ++i) { - int color = (v>>bit_offset)&0x1; - out[z++] = pal[color][0]; - out[z++] = pal[color][1]; - out[z++] = pal[color][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - if((--bit_offset) < 0) { - bit_offset = 7; - v = stbi__get8(s); - } - } - stbi__skip(s, pad); - } - } else { - for (j=0; j < (int) s->img_y; ++j) { - for (i=0; i < (int) s->img_x; i += 2) { - int v=stbi__get8(s),v2=0; - if (info.bpp == 4) { - v2 = v & 15; - v >>= 4; - } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - v = (info.bpp == 8) ? stbi__get8(s) : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - } - stbi__skip(s, pad); - } - } - } else { - int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; - int z = 0; - int easy=0; - stbi__skip(s, info.offset - info.extra_read - info.hsz); - if (info.bpp == 24) width = 3 * s->img_x; - else if (info.bpp == 16) width = 2*s->img_x; - else /* bpp = 32 and pad = 0 */ width=0; - pad = (-width) & 3; - if (info.bpp == 24) { - easy = 1; - } else if (info.bpp == 32) { - if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) - easy = 2; - } - if (!easy) { - if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - // right shift amt to put high bit in position #7 - rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); - gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); - bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); - ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); - if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - } - for (j=0; j < (int) s->img_y; ++j) { - if (easy) { - for (i=0; i < (int) s->img_x; ++i) { - unsigned char a; - out[z+2] = stbi__get8(s); - out[z+1] = stbi__get8(s); - out[z+0] = stbi__get8(s); - z += 3; - a = (easy == 2 ? stbi__get8(s) : 255); - all_a |= a; - if (target == 4) out[z++] = a; - } - } else { - int bpp = info.bpp; - for (i=0; i < (int) s->img_x; ++i) { - stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); - unsigned int a; - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); - a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); - all_a |= a; - if (target == 4) out[z++] = STBI__BYTECAST(a); - } - } - stbi__skip(s, pad); - } - } - - // if alpha channel is all 0s, replace with all 255s - if (target == 4 && all_a == 0) - for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) - out[i] = 255; - - if (flip_vertically) { - stbi_uc t; - for (j=0; j < (int) s->img_y>>1; ++j) { - stbi_uc *p1 = out + j *s->img_x*target; - stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; - for (i=0; i < (int) s->img_x*target; ++i) { - t = p1[i]; p1[i] = p2[i]; p2[i] = t; - } - } - } - - if (req_comp && req_comp != target) { - out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - return out; -} -#endif - -// Targa Truevision - TGA -// by Jonathan Dummer -#ifndef STBI_NO_TGA -// returns STBI_rgb or whatever, 0 on error -static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) -{ - // only RGB or RGBA (incl. 16bit) or grey allowed - if (is_rgb16) *is_rgb16 = 0; - switch(bits_per_pixel) { - case 8: return STBI_grey; - case 16: if(is_grey) return STBI_grey_alpha; - // fallthrough - case 15: if(is_rgb16) *is_rgb16 = 1; - return STBI_rgb; - case 24: // fallthrough - case 32: return bits_per_pixel/8; - default: return 0; - } -} - -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) -{ - int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; - int sz, tga_colormap_type; - stbi__get8(s); // discard Offset - tga_colormap_type = stbi__get8(s); // colormap type - if( tga_colormap_type > 1 ) { - stbi__rewind(s); - return 0; // only RGB or indexed allowed - } - tga_image_type = stbi__get8(s); // image type - if ( tga_colormap_type == 1 ) { // colormapped (paletted) image - if (tga_image_type != 1 && tga_image_type != 9) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip image x and y origin - tga_colormap_bpp = sz; - } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE - if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { - stbi__rewind(s); - return 0; // only RGB or grey allowed, +/- RLE - } - stbi__skip(s,9); // skip colormap specification and image x/y origin - tga_colormap_bpp = 0; - } - tga_w = stbi__get16le(s); - if( tga_w < 1 ) { - stbi__rewind(s); - return 0; // test width - } - tga_h = stbi__get16le(s); - if( tga_h < 1 ) { - stbi__rewind(s); - return 0; // test height - } - tga_bits_per_pixel = stbi__get8(s); // bits per pixel - stbi__get8(s); // ignore alpha bits - if (tga_colormap_bpp != 0) { - if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { - // when using a colormap, tga_bits_per_pixel is the size of the indexes - // I don't think anything but 8 or 16bit indexes makes sense - stbi__rewind(s); - return 0; - } - tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); - } else { - tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); - } - if(!tga_comp) { - stbi__rewind(s); - return 0; - } - if (x) *x = tga_w; - if (y) *y = tga_h; - if (comp) *comp = tga_comp; - return 1; // seems to have passed everything -} - -static int stbi__tga_test(stbi__context *s) -{ - int res = 0; - int sz, tga_color_type; - stbi__get8(s); // discard Offset - tga_color_type = stbi__get8(s); // color type - if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed - sz = stbi__get8(s); // image type - if ( tga_color_type == 1 ) { // colormapped (paletted) image - if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - stbi__skip(s,4); // skip image x and y origin - } else { // "normal" image w/o colormap - if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE - stbi__skip(s,9); // skip colormap specification and image x/y origin - } - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height - sz = stbi__get8(s); // bits per pixel - if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - - res = 1; // if we got this far, everything's good and we can return 1 instead of 0 - -errorEnd: - stbi__rewind(s); - return res; -} - -// read 16bit value and convert to 24bit RGB -static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) -{ - stbi__uint16 px = (stbi__uint16)stbi__get16le(s); - stbi__uint16 fiveBitMask = 31; - // we have 3 channels with 5bits each - int r = (px >> 10) & fiveBitMask; - int g = (px >> 5) & fiveBitMask; - int b = px & fiveBitMask; - // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later - out[0] = (stbi_uc)((r * 255)/31); - out[1] = (stbi_uc)((g * 255)/31); - out[2] = (stbi_uc)((b * 255)/31); - - // some people claim that the most significant bit might be used for alpha - // (possibly if an alpha-bit is set in the "image descriptor byte") - // but that only made 16bit test images completely translucent.. - // so let's treat all 15 and 16bit TGAs as RGB with no alpha. -} - -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - // read in the TGA header stuff - int tga_offset = stbi__get8(s); - int tga_indexed = stbi__get8(s); - int tga_image_type = stbi__get8(s); - int tga_is_RLE = 0; - int tga_palette_start = stbi__get16le(s); - int tga_palette_len = stbi__get16le(s); - int tga_palette_bits = stbi__get8(s); - int tga_x_origin = stbi__get16le(s); - int tga_y_origin = stbi__get16le(s); - int tga_width = stbi__get16le(s); - int tga_height = stbi__get16le(s); - int tga_bits_per_pixel = stbi__get8(s); - int tga_comp, tga_rgb16=0; - int tga_inverted = stbi__get8(s); - // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) - // image data - unsigned char *tga_data; - unsigned char *tga_palette = NULL; - int i, j; - unsigned char raw_data[4] = {0}; - int RLE_count = 0; - int RLE_repeating = 0; - int read_next_pixel = 1; - STBI_NOTUSED(ri); - STBI_NOTUSED(tga_x_origin); // @TODO - STBI_NOTUSED(tga_y_origin); // @TODO - - if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - // do a tiny bit of precessing - if ( tga_image_type >= 8 ) - { - tga_image_type -= 8; - tga_is_RLE = 1; - } - tga_inverted = 1 - ((tga_inverted >> 5) & 1); - - // If I'm paletted, then I'll use the number of bits from the palette - if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); - else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); - - if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency - return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); - - // tga info - *x = tga_width; - *y = tga_height; - if (comp) *comp = tga_comp; - - if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) - return stbi__errpuc("too large", "Corrupt TGA"); - - tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); - if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); - - // skip to the data's starting position (offset usually = 0) - stbi__skip(s, tga_offset ); - - if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { - for (i=0; i < tga_height; ++i) { - int row = tga_inverted ? tga_height -i - 1 : i; - stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; - stbi__getn(s, tga_row, tga_width * tga_comp); - } - } else { - // do I need to load a palette? - if ( tga_indexed) - { - if (tga_palette_len == 0) { /* you have to have at least one entry! */ - STBI_FREE(tga_data); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - - // any data to skip? (offset usually = 0) - stbi__skip(s, tga_palette_start ); - // load the palette - tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); - if (!tga_palette) { - STBI_FREE(tga_data); - return stbi__errpuc("outofmem", "Out of memory"); - } - if (tga_rgb16) { - stbi_uc *pal_entry = tga_palette; - STBI_ASSERT(tga_comp == STBI_rgb); - for (i=0; i < tga_palette_len; ++i) { - stbi__tga_read_rgb16(s, pal_entry); - pal_entry += tga_comp; - } - } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { - STBI_FREE(tga_data); - STBI_FREE(tga_palette); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - } - // load the data - for (i=0; i < tga_width * tga_height; ++i) - { - // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? - if ( tga_is_RLE ) - { - if ( RLE_count == 0 ) - { - // yep, get the next byte as a RLE command - int RLE_cmd = stbi__get8(s); - RLE_count = 1 + (RLE_cmd & 127); - RLE_repeating = RLE_cmd >> 7; - read_next_pixel = 1; - } else if ( !RLE_repeating ) - { - read_next_pixel = 1; - } - } else - { - read_next_pixel = 1; - } - // OK, if I need to read a pixel, do it now - if ( read_next_pixel ) - { - // load however much data we did have - if ( tga_indexed ) - { - // read in index, then perform the lookup - int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); - if ( pal_idx >= tga_palette_len ) { - // invalid index - pal_idx = 0; - } - pal_idx *= tga_comp; - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = tga_palette[pal_idx+j]; - } - } else if(tga_rgb16) { - STBI_ASSERT(tga_comp == STBI_rgb); - stbi__tga_read_rgb16(s, raw_data); - } else { - // read in the data raw - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = stbi__get8(s); - } - } - // clear the reading flag for the next pixel - read_next_pixel = 0; - } // end of reading a pixel - - // copy data - for (j = 0; j < tga_comp; ++j) - tga_data[i*tga_comp+j] = raw_data[j]; - - // in case we're in RLE mode, keep counting down - --RLE_count; - } - // do I need to invert the image? - if ( tga_inverted ) - { - for (j = 0; j*2 < tga_height; ++j) - { - int index1 = j * tga_width * tga_comp; - int index2 = (tga_height - 1 - j) * tga_width * tga_comp; - for (i = tga_width * tga_comp; i > 0; --i) - { - unsigned char temp = tga_data[index1]; - tga_data[index1] = tga_data[index2]; - tga_data[index2] = temp; - ++index1; - ++index2; - } - } - } - // clear my palette, if I had one - if ( tga_palette != NULL ) - { - STBI_FREE( tga_palette ); - } - } - - // swap RGB - if the source data was RGB16, it already is in the right order - if (tga_comp >= 3 && !tga_rgb16) - { - unsigned char* tga_pixel = tga_data; - for (i=0; i < tga_width * tga_height; ++i) - { - unsigned char temp = tga_pixel[0]; - tga_pixel[0] = tga_pixel[2]; - tga_pixel[2] = temp; - tga_pixel += tga_comp; - } - } - - // convert to target component count - if (req_comp && req_comp != tga_comp) - tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); - - // the things I do to get rid of an error message, and yet keep - // Microsoft's C compilers happy... [8^( - tga_palette_start = tga_palette_len = tga_palette_bits = - tga_x_origin = tga_y_origin = 0; - STBI_NOTUSED(tga_palette_start); - // OK, done - return tga_data; -} -#endif - -// ************************************************************************************************* -// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s) -{ - int r = (stbi__get32be(s) == 0x38425053); - stbi__rewind(s); - return r; -} - -static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) -{ - int count, nleft, len; - - count = 0; - while ((nleft = pixelCount - count) > 0) { - len = stbi__get8(s); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - if (len > nleft) return 0; // corrupt data - count += len; - while (len) { - *p = stbi__get8(s); - p += 4; - len--; - } - } else if (len > 128) { - stbi_uc val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len = 257 - len; - if (len > nleft) return 0; // corrupt data - val = stbi__get8(s); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } - } - - return 1; -} - -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - int pixelCount; - int channelCount, compression; - int channel, i; - int bitdepth; - int w,h; - stbi_uc *out; - STBI_NOTUSED(ri); - - // Check identifier - if (stbi__get32be(s) != 0x38425053) // "8BPS" - return stbi__errpuc("not PSD", "Corrupt PSD image"); - - // Check file type version. - if (stbi__get16be(s) != 1) - return stbi__errpuc("wrong version", "Unsupported version of PSD image"); - - // Skip 6 reserved bytes. - stbi__skip(s, 6 ); - - // Read the number of channels (R, G, B, A, etc). - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) - return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); - - // Read the rows and columns of the image. - h = stbi__get32be(s); - w = stbi__get32be(s); - - if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - // Make sure the depth is 8 bits. - bitdepth = stbi__get16be(s); - if (bitdepth != 8 && bitdepth != 16) - return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); - - // Make sure the color mode is RGB. - // Valid options are: - // 0: Bitmap - // 1: Grayscale - // 2: Indexed color - // 3: RGB color - // 4: CMYK color - // 7: Multichannel - // 8: Duotone - // 9: Lab color - if (stbi__get16be(s) != 3) - return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); - - // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) - stbi__skip(s,stbi__get32be(s) ); - - // Skip the image resources. (resolution, pen tool paths, etc) - stbi__skip(s, stbi__get32be(s) ); - - // Skip the reserved data. - stbi__skip(s, stbi__get32be(s) ); - - // Find out if the data is compressed. - // Known values: - // 0: no compression - // 1: RLE compressed - compression = stbi__get16be(s); - if (compression > 1) - return stbi__errpuc("bad compression", "PSD has an unknown compression format"); - - // Check size - if (!stbi__mad3sizes_valid(4, w, h, 0)) - return stbi__errpuc("too large", "Corrupt PSD"); - - // Create the destination image. - - if (!compression && bitdepth == 16 && bpc == 16) { - out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); - ri->bits_per_channel = 16; - } else - out = (stbi_uc *) stbi__malloc(4 * w*h); - - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - pixelCount = w*h; - - // Initialize the data to zero. - //memset( out, 0, pixelCount * 4 ); - - // Finally, the image data. - if (compression) { - // RLE as used by .PSD and .TIFF - // Loop until you get the number of unpacked bytes you are expecting: - // Read the next source byte into n. - // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. - // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. - // Else if n is 128, noop. - // Endloop - - // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, - // which we're going to just skip. - stbi__skip(s, h * channelCount * 2 ); - - // Read the RLE data by channel. - for (channel = 0; channel < 4; channel++) { - stbi_uc *p; - - p = out+channel; - if (channel >= channelCount) { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++, p += 4) - *p = (channel == 3 ? 255 : 0); - } else { - // Read the RLE data. - if (!stbi__psd_decode_rle(s, p, pixelCount)) { - STBI_FREE(out); - return stbi__errpuc("corrupt", "bad RLE data"); - } - } - } - - } else { - // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. - - // Read the data by channel. - for (channel = 0; channel < 4; channel++) { - if (channel >= channelCount) { - // Fill this channel with default data. - if (bitdepth == 16 && bpc == 16) { - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - stbi__uint16 val = channel == 3 ? 65535 : 0; - for (i = 0; i < pixelCount; i++, q += 4) - *q = val; - } else { - stbi_uc *p = out+channel; - stbi_uc val = channel == 3 ? 255 : 0; - for (i = 0; i < pixelCount; i++, p += 4) - *p = val; - } - } else { - if (ri->bits_per_channel == 16) { // output bpc - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - for (i = 0; i < pixelCount; i++, q += 4) - *q = (stbi__uint16) stbi__get16be(s); - } else { - stbi_uc *p = out+channel; - if (bitdepth == 16) { // input bpc - for (i = 0; i < pixelCount; i++, p += 4) - *p = (stbi_uc) (stbi__get16be(s) >> 8); - } else { - for (i = 0; i < pixelCount; i++, p += 4) - *p = stbi__get8(s); - } - } - } - } - } - - // remove weird white matte from PSD - if (channelCount >= 4) { - if (ri->bits_per_channel == 16) { - for (i=0; i < w*h; ++i) { - stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; - if (pixel[3] != 0 && pixel[3] != 65535) { - float a = pixel[3] / 65535.0f; - float ra = 1.0f / a; - float inv_a = 65535.0f * (1 - ra); - pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); - pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); - pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); - } - } - } else { - for (i=0; i < w*h; ++i) { - unsigned char *pixel = out + 4*i; - if (pixel[3] != 0 && pixel[3] != 255) { - float a = pixel[3] / 255.0f; - float ra = 1.0f / a; - float inv_a = 255.0f * (1 - ra); - pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); - pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); - pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); - } - } - } - } - - // convert to desired output format - if (req_comp && req_comp != 4) { - if (ri->bits_per_channel == 16) - out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); - else - out = stbi__convert_format(out, 4, req_comp, w, h); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - if (comp) *comp = 4; - *y = h; - *x = w; - - return out; -} -#endif - -// ************************************************************************************************* -// Softimage PIC loader -// by Tom Seddon -// -// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format -// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ - -#ifndef STBI_NO_PIC -static int stbi__pic_is4(stbi__context *s,const char *str) -{ - int i; - for (i=0; i<4; ++i) - if (stbi__get8(s) != (stbi_uc)str[i]) - return 0; - - return 1; -} - -static int stbi__pic_test_core(stbi__context *s) -{ - int i; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) - return 0; - - for(i=0;i<84;++i) - stbi__get8(s); - - if (!stbi__pic_is4(s,"PICT")) - return 0; - - return 1; -} - -typedef struct -{ - stbi_uc size,type,channel; -} stbi__pic_packet; - -static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) -{ - int mask=0x80, i; - - for (i=0; i<4; ++i, mask>>=1) { - if (channel & mask) { - if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); - dest[i]=stbi__get8(s); - } - } - - return dest; -} - -static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) -{ - int mask=0x80,i; - - for (i=0;i<4; ++i, mask>>=1) - if (channel&mask) - dest[i]=src[i]; -} - -static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) -{ - int act_comp=0,num_packets=0,y,chained; - stbi__pic_packet packets[10]; - - // this will (should...) cater for even some bizarre stuff like having data - // for the same channel in multiple packets. - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return stbi__errpuc("bad format","too many packets"); - - packet = &packets[num_packets++]; - - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - - act_comp |= packet->channel; - - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); - if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? - - for(y=0; ytype) { - default: - return stbi__errpuc("bad format","packet has bad compression type"); - - case 0: {//uncompressed - int x; - - for(x=0;xchannel,dest)) - return 0; - break; - } - - case 1://Pure RLE - { - int left=width, i; - - while (left>0) { - stbi_uc count,value[4]; - - count=stbi__get8(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); - - if (count > left) - count = (stbi_uc) left; - - if (!stbi__readval(s,packet->channel,value)) return 0; - - for(i=0; ichannel,dest,value); - left -= count; - } - } - break; - - case 2: {//Mixed RLE - int left=width; - while (left>0) { - int count = stbi__get8(s), i; - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); - - if (count >= 128) { // Repeated - stbi_uc value[4]; - - if (count==128) - count = stbi__get16be(s); - else - count -= 127; - if (count > left) - return stbi__errpuc("bad file","scanline overrun"); - - if (!stbi__readval(s,packet->channel,value)) - return 0; - - for(i=0;ichannel,dest,value); - } else { // Raw - ++count; - if (count>left) return stbi__errpuc("bad file","scanline overrun"); - - for(i=0;ichannel,dest)) - return 0; - } - left-=count; - } - break; - } - } - } - } - - return result; -} - -static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) -{ - stbi_uc *result; - int i, x,y, internal_comp; - STBI_NOTUSED(ri); - - if (!comp) comp = &internal_comp; - - for (i=0; i<92; ++i) - stbi__get8(s); - - x = stbi__get16be(s); - y = stbi__get16be(s); - - if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); - if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); - - stbi__get32be(s); //skip `ratio' - stbi__get16be(s); //skip `fields' - stbi__get16be(s); //skip `pad' - - // intermediate buffer is RGBA - result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); - memset(result, 0xff, x*y*4); - - if (!stbi__pic_load_core(s,x,y,comp, result)) { - STBI_FREE(result); - result=0; - } - *px = x; - *py = y; - if (req_comp == 0) req_comp = *comp; - result=stbi__convert_format(result,4,req_comp,x,y); - - return result; -} - -static int stbi__pic_test(stbi__context *s) -{ - int r = stbi__pic_test_core(s); - stbi__rewind(s); - return r; -} -#endif - -// ************************************************************************************************* -// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb - -#ifndef STBI_NO_GIF -typedef struct -{ - stbi__int16 prefix; - stbi_uc first; - stbi_uc suffix; -} stbi__gif_lzw; - -typedef struct -{ - int w,h; - stbi_uc *out; // output buffer (always 4 components) - stbi_uc *background; // The current "background" as far as a gif is concerned - stbi_uc *history; - int flags, bgindex, ratio, transparent, eflags; - stbi_uc pal[256][4]; - stbi_uc lpal[256][4]; - stbi__gif_lzw codes[8192]; - stbi_uc *color_table; - int parse, step; - int lflags; - int start_x, start_y; - int max_x, max_y; - int cur_x, cur_y; - int line_size; - int delay; -} stbi__gif; - -static int stbi__gif_test_raw(stbi__context *s) -{ - int sz; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; - sz = stbi__get8(s); - if (sz != '9' && sz != '7') return 0; - if (stbi__get8(s) != 'a') return 0; - return 1; -} - -static int stbi__gif_test(stbi__context *s) -{ - int r = stbi__gif_test_raw(s); - stbi__rewind(s); - return r; -} - -static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) -{ - int i; - for (i=0; i < num_entries; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - pal[i][3] = transp == i ? 0 : 255; - } -} - -static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) -{ - stbi_uc version; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') - return stbi__err("not GIF", "Corrupt GIF"); - - version = stbi__get8(s); - if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); - if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); - - stbi__g_failure_reason = ""; - g->w = stbi__get16le(s); - g->h = stbi__get16le(s); - g->flags = stbi__get8(s); - g->bgindex = stbi__get8(s); - g->ratio = stbi__get8(s); - g->transparent = -1; - - if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); - - if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments - - if (is_info) return 1; - - if (g->flags & 0x80) - stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); - - return 1; -} - -static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); - if (!stbi__gif_header(s, g, comp, 1)) { - STBI_FREE(g); - stbi__rewind( s ); - return 0; - } - if (x) *x = g->w; - if (y) *y = g->h; - STBI_FREE(g); - return 1; -} - -static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) -{ - stbi_uc *p, *c; - int idx; - - // recurse to decode the prefixes, since the linked-list is backwards, - // and working backwards through an interleaved image would be nasty - if (g->codes[code].prefix >= 0) - stbi__out_gif_code(g, g->codes[code].prefix); - - if (g->cur_y >= g->max_y) return; - - idx = g->cur_x + g->cur_y; - p = &g->out[idx]; - g->history[idx / 4] = 1; - - c = &g->color_table[g->codes[code].suffix * 4]; - if (c[3] > 128) { // don't render transparent pixels; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; - } - g->cur_x += 4; - - if (g->cur_x >= g->max_x) { - g->cur_x = g->start_x; - g->cur_y += g->step; - - while (g->cur_y >= g->max_y && g->parse > 0) { - g->step = (1 << g->parse) * g->line_size; - g->cur_y = g->start_y + (g->step >> 1); - --g->parse; - } - } -} - -static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) -{ - stbi_uc lzw_cs; - stbi__int32 len, init_code; - stbi__uint32 first; - stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; - stbi__gif_lzw *p; - - lzw_cs = stbi__get8(s); - if (lzw_cs > 12) return NULL; - clear = 1 << lzw_cs; - first = 1; - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - bits = 0; - valid_bits = 0; - for (init_code = 0; init_code < clear; init_code++) { - g->codes[init_code].prefix = -1; - g->codes[init_code].first = (stbi_uc) init_code; - g->codes[init_code].suffix = (stbi_uc) init_code; - } - - // support no starting clear code - avail = clear+2; - oldcode = -1; - - len = 0; - for(;;) { - if (valid_bits < codesize) { - if (len == 0) { - len = stbi__get8(s); // start new block - if (len == 0) - return g->out; - } - --len; - bits |= (stbi__int32) stbi__get8(s) << valid_bits; - valid_bits += 8; - } else { - stbi__int32 code = bits & codemask; - bits >>= codesize; - valid_bits -= codesize; - // @OPTIMIZE: is there some way we can accelerate the non-clear path? - if (code == clear) { // clear code - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - avail = clear + 2; - oldcode = -1; - first = 0; - } else if (code == clear + 1) { // end of stream code - stbi__skip(s, len); - while ((len = stbi__get8(s)) > 0) - stbi__skip(s,len); - return g->out; - } else if (code <= avail) { - if (first) { - return stbi__errpuc("no clear code", "Corrupt GIF"); - } - - if (oldcode >= 0) { - p = &g->codes[avail++]; - if (avail > 8192) { - return stbi__errpuc("too many codes", "Corrupt GIF"); - } - - p->prefix = (stbi__int16) oldcode; - p->first = g->codes[oldcode].first; - p->suffix = (code == avail) ? p->first : g->codes[code].first; - } else if (code == avail) - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - - stbi__out_gif_code(g, (stbi__uint16) code); - - if ((avail & codemask) == 0 && avail <= 0x0FFF) { - codesize++; - codemask = (1 << codesize) - 1; - } - - oldcode = code; - } else { - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - } - } - } -} - -// this function is designed to support animated gifs, although stb_image doesn't support it -// two back is the image from two frames ago, used for a very specific disposal format -static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) -{ - int dispose; - int first_frame; - int pi; - int pcount; - STBI_NOTUSED(req_comp); - - // on first frame, any non-written pixels get the background colour (non-transparent) - first_frame = 0; - if (g->out == 0) { - if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header - if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) - return stbi__errpuc("too large", "GIF image is too large"); - pcount = g->w * g->h; - g->out = (stbi_uc *) stbi__malloc(4 * pcount); - g->background = (stbi_uc *) stbi__malloc(4 * pcount); - g->history = (stbi_uc *) stbi__malloc(pcount); - if (!g->out || !g->background || !g->history) - return stbi__errpuc("outofmem", "Out of memory"); - - // image is treated as "transparent" at the start - ie, nothing overwrites the current background; - // background colour is only used for pixels that are not rendered first frame, after that "background" - // color refers to the color that was there the previous frame. - memset(g->out, 0x00, 4 * pcount); - memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) - memset(g->history, 0x00, pcount); // pixels that were affected previous frame - first_frame = 1; - } else { - // second frame - how do we dispose of the previous one? - dispose = (g->eflags & 0x1C) >> 2; - pcount = g->w * g->h; - - if ((dispose == 3) && (two_back == 0)) { - dispose = 2; // if I don't have an image to revert back to, default to the old background - } - - if (dispose == 3) { // use previous graphic - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); - } - } - } else if (dispose == 2) { - // restore what was changed last frame to background before that frame; - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); - } - } - } else { - // This is a non-disposal case eithe way, so just - // leave the pixels as is, and they will become the new background - // 1: do not dispose - // 0: not specified. - } - - // background is what out is after the undoing of the previou frame; - memcpy( g->background, g->out, 4 * g->w * g->h ); - } - - // clear my history; - memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame - - for (;;) { - int tag = stbi__get8(s); - switch (tag) { - case 0x2C: /* Image Descriptor */ - { - stbi__int32 x, y, w, h; - stbi_uc *o; - - x = stbi__get16le(s); - y = stbi__get16le(s); - w = stbi__get16le(s); - h = stbi__get16le(s); - if (((x + w) > (g->w)) || ((y + h) > (g->h))) - return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); - - g->line_size = g->w * 4; - g->start_x = x * 4; - g->start_y = y * g->line_size; - g->max_x = g->start_x + w * 4; - g->max_y = g->start_y + h * g->line_size; - g->cur_x = g->start_x; - g->cur_y = g->start_y; - - // if the width of the specified rectangle is 0, that means - // we may not see *any* pixels or the image is malformed; - // to make sure this is caught, move the current y down to - // max_y (which is what out_gif_code checks). - if (w == 0) - g->cur_y = g->max_y; - - g->lflags = stbi__get8(s); - - if (g->lflags & 0x40) { - g->step = 8 * g->line_size; // first interlaced spacing - g->parse = 3; - } else { - g->step = g->line_size; - g->parse = 0; - } - - if (g->lflags & 0x80) { - stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); - g->color_table = (stbi_uc *) g->lpal; - } else if (g->flags & 0x80) { - g->color_table = (stbi_uc *) g->pal; - } else - return stbi__errpuc("missing color table", "Corrupt GIF"); - - o = stbi__process_gif_raster(s, g); - if (!o) return NULL; - - // if this was the first frame, - pcount = g->w * g->h; - if (first_frame && (g->bgindex > 0)) { - // if first frame, any pixel not drawn to gets the background color - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi] == 0) { - g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; - memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); - } - } - } - - return o; - } - - case 0x21: // Comment Extension. - { - int len; - int ext = stbi__get8(s); - if (ext == 0xF9) { // Graphic Control Extension. - len = stbi__get8(s); - if (len == 4) { - g->eflags = stbi__get8(s); - g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. - - // unset old transparent - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 255; - } - if (g->eflags & 0x01) { - g->transparent = stbi__get8(s); - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 0; - } - } else { - // don't need transparent - stbi__skip(s, 1); - g->transparent = -1; - } - } else { - stbi__skip(s, len); - break; - } - } - while ((len = stbi__get8(s)) != 0) { - stbi__skip(s, len); - } - break; - } - - case 0x3B: // gif stream termination code - return (stbi_uc *) s; // using '1' causes warning on some compilers - - default: - return stbi__errpuc("unknown code", "Corrupt GIF"); - } - } -} - -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - if (stbi__gif_test(s)) { - int layers = 0; - stbi_uc *u = 0; - stbi_uc *out = 0; - stbi_uc *two_back = 0; - stbi__gif g; - int stride; - memset(&g, 0, sizeof(g)); - if (delays) { - *delays = 0; - } - - do { - u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - - if (u) { - *x = g.w; - *y = g.h; - ++layers; - stride = g.w * g.h * 4; - - if (out) { - void *tmp = (stbi_uc*) STBI_REALLOC( out, layers * stride ); - if (NULL == tmp) { - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); - return stbi__errpuc("outofmem", "Out of memory"); - } - else { - out = (stbi_uc*) tmp; - } - - if (delays) { - *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); - } - } else { - out = (stbi_uc*)stbi__malloc( layers * stride ); - if (delays) { - *delays = (int*) stbi__malloc( layers * sizeof(int) ); - } - } - memcpy( out + ((layers - 1) * stride), u, stride ); - if (layers >= 2) { - two_back = out - 2 * stride; - } - - if (delays) { - (*delays)[layers - 1U] = g.delay; - } - } - } while (u != 0); - - // free temp buffer; - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); - - // do the final conversion after loading everything; - if (req_comp && req_comp != 4) - out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); - - *z = layers; - return out; - } else { - return stbi__errpuc("not GIF", "Image was not as a gif type."); - } -} - -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *u = 0; - stbi__gif g; - memset(&g, 0, sizeof(g)); - STBI_NOTUSED(ri); - - u = stbi__gif_load_next(s, &g, comp, req_comp, 0); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - if (u) { - *x = g.w; - *y = g.h; - - // moved conversion to after successful load so that the same - // can be done for multiple frames. - if (req_comp && req_comp != 4) - u = stbi__convert_format(u, 4, req_comp, g.w, g.h); - } else if (g.out) { - // if there was an error and we allocated an image buffer, free it! - STBI_FREE(g.out); - } - - // free buffers needed for multiple frame loading; - STBI_FREE(g.history); - STBI_FREE(g.background); - - return u; -} - -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) -{ - return stbi__gif_info_raw(s,x,y,comp); -} -#endif - -// ************************************************************************************************* -// Radiance RGBE HDR loader -// originally by Nicolas Schulz -#ifndef STBI_NO_HDR -static int stbi__hdr_test_core(stbi__context *s, const char *signature) -{ - int i; - for (i=0; signature[i]; ++i) - if (stbi__get8(s) != signature[i]) - return 0; - stbi__rewind(s); - return 1; -} - -static int stbi__hdr_test(stbi__context* s) -{ - int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); - stbi__rewind(s); - if(!r) { - r = stbi__hdr_test_core(s, "#?RGBE\n"); - stbi__rewind(s); - } - return r; -} - -#define STBI__HDR_BUFLEN 1024 -static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) -{ - int len=0; - char c = '\0'; - - c = (char) stbi__get8(z); - - while (!stbi__at_eof(z) && c != '\n') { - buffer[len++] = c; - if (len == STBI__HDR_BUFLEN-1) { - // flush to end of line - while (!stbi__at_eof(z) && stbi__get8(z) != '\n') - ; - break; - } - c = (char) stbi__get8(z); - } - - buffer[len] = 0; - return buffer; -} - -static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) -{ - if ( input[3] != 0 ) { - float f1; - // Exponent - f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); - if (req_comp <= 2) - output[0] = (input[0] + input[1] + input[2]) * f1 / 3; - else { - output[0] = input[0] * f1; - output[1] = input[1] * f1; - output[2] = input[2] * f1; - } - if (req_comp == 2) output[1] = 1; - if (req_comp == 4) output[3] = 1; - } else { - switch (req_comp) { - case 4: output[3] = 1; /* fallthrough */ - case 3: output[0] = output[1] = output[2] = 0; - break; - case 2: output[1] = 1; /* fallthrough */ - case 1: output[0] = 0; - break; - } - } -} - -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int width, height; - stbi_uc *scanline; - float *hdr_data; - int len; - unsigned char count, value; - int i, j, k, c1,c2, z; - const char *headerToken; - STBI_NOTUSED(ri); - - // Check identifier - headerToken = stbi__hdr_gettoken(s,buffer); - if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) - return stbi__errpf("not HDR", "Corrupt HDR image"); - - // Parse header - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); - - // Parse width and height - // can't use sscanf() if we're not using stdio! - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - height = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - width = (int) strtol(token, NULL, 10); - - if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); - if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); - - *x = width; - *y = height; - - if (comp) *comp = 3; - if (req_comp == 0) req_comp = 3; - - if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) - return stbi__errpf("too large", "HDR image is too large"); - - // Read data - hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); - if (!hdr_data) - return stbi__errpf("outofmem", "Out of memory"); - - // Load image data - // image data is stored as some number of sca - if ( width < 8 || width >= 32768) { - // Read flat data - for (j=0; j < height; ++j) { - for (i=0; i < width; ++i) { - stbi_uc rgbe[4]; - main_decode_loop: - stbi__getn(s, rgbe, 4); - stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); - } - } - } else { - // Read RLE-encoded data - scanline = NULL; - - for (j = 0; j < height; ++j) { - c1 = stbi__get8(s); - c2 = stbi__get8(s); - len = stbi__get8(s); - if (c1 != 2 || c2 != 2 || (len & 0x80)) { - // not run-length encoded, so we have to actually use THIS data as a decoded - // pixel (note this can't be a valid pixel--one of RGB must be >= 128) - stbi_uc rgbe[4]; - rgbe[0] = (stbi_uc) c1; - rgbe[1] = (stbi_uc) c2; - rgbe[2] = (stbi_uc) len; - rgbe[3] = (stbi_uc) stbi__get8(s); - stbi__hdr_convert(hdr_data, rgbe, req_comp); - i = 1; - j = 0; - STBI_FREE(scanline); - goto main_decode_loop; // yes, this makes no sense - } - len <<= 8; - len |= stbi__get8(s); - if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } - if (scanline == NULL) { - scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); - if (!scanline) { - STBI_FREE(hdr_data); - return stbi__errpf("outofmem", "Out of memory"); - } - } - - for (k = 0; k < 4; ++k) { - int nleft; - i = 0; - while ((nleft = width - i) > 0) { - count = stbi__get8(s); - if (count > 128) { - // Run - value = stbi__get8(s); - count -= 128; - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = value; - } else { - // Dump - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = stbi__get8(s); - } - } - } - for (i=0; i < width; ++i) - stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); - } - if (scanline) - STBI_FREE(scanline); - } - - return hdr_data; -} - -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int dummy; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - if (stbi__hdr_test(s) == 0) { - stbi__rewind( s ); - return 0; - } - - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) { - stbi__rewind( s ); - return 0; - } - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *y = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *x = (int) strtol(token, NULL, 10); - *comp = 3; - return 1; -} -#endif // STBI_NO_HDR - -#ifndef STBI_NO_BMP -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) -{ - void *p; - stbi__bmp_data info; - - info.all_a = 255; - p = stbi__bmp_parse_header(s, &info); - stbi__rewind( s ); - if (p == NULL) - return 0; - if (x) *x = s->img_x; - if (y) *y = s->img_y; - if (comp) { - if (info.bpp == 24 && info.ma == 0xff000000) - *comp = 3; - else - *comp = info.ma ? 4 : 3; - } - return 1; -} -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) -{ - int channelCount, dummy, depth; - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - *y = stbi__get32be(s); - *x = stbi__get32be(s); - depth = stbi__get16be(s); - if (depth != 8 && depth != 16) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 3) { - stbi__rewind( s ); - return 0; - } - *comp = 4; - return 1; -} - -static int stbi__psd_is16(stbi__context *s) -{ - int channelCount, depth; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - (void) stbi__get32be(s); - (void) stbi__get32be(s); - depth = stbi__get16be(s); - if (depth != 16) { - stbi__rewind( s ); - return 0; - } - return 1; -} -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) -{ - int act_comp=0,num_packets=0,chained,dummy; - stbi__pic_packet packets[10]; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { - stbi__rewind(s); - return 0; - } - - stbi__skip(s, 88); - - *x = stbi__get16be(s); - *y = stbi__get16be(s); - if (stbi__at_eof(s)) { - stbi__rewind( s); - return 0; - } - if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { - stbi__rewind( s ); - return 0; - } - - stbi__skip(s, 8); - - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return 0; - - packet = &packets[num_packets++]; - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - act_comp |= packet->channel; - - if (stbi__at_eof(s)) { - stbi__rewind( s ); - return 0; - } - if (packet->size != 8) { - stbi__rewind( s ); - return 0; - } - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); - - return 1; -} -#endif - -// ************************************************************************************************* -// Portable Gray Map and Portable Pixel Map loader -// by Ken Miller -// -// PGM: http://netpbm.sourceforge.net/doc/pgm.html -// PPM: http://netpbm.sourceforge.net/doc/ppm.html -// -// Known limitations: -// Does not support comments in the header section -// Does not support ASCII image data (formats P2 and P3) -// Does not support 16-bit-per-channel - -#ifndef STBI_NO_PNM - -static int stbi__pnm_test(stbi__context *s) -{ - char p, t; - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind( s ); - return 0; - } - return 1; -} - -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - STBI_NOTUSED(ri); - - if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) - return 0; - - if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - - if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) - return stbi__errpuc("too large", "PNM too large"); - - out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - stbi__getn(s, out, s->img_n * s->img_x * s->img_y); - - if (req_comp && req_comp != s->img_n) { - out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - return out; -} - -static int stbi__pnm_isspace(char c) -{ - return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; -} - -static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) -{ - for (;;) { - while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) - *c = (char) stbi__get8(s); - - if (stbi__at_eof(s) || *c != '#') - break; - - while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) - *c = (char) stbi__get8(s); - } -} - -static int stbi__pnm_isdigit(char c) -{ - return c >= '0' && c <= '9'; -} - -static int stbi__pnm_getinteger(stbi__context *s, char *c) -{ - int value = 0; - - while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { - value = value*10 + (*c - '0'); - *c = (char) stbi__get8(s); - } - - return value; -} - -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) -{ - int maxv, dummy; - char c, p, t; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - stbi__rewind(s); - - // Get identifier - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind(s); - return 0; - } - - *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm - - c = (char) stbi__get8(s); - stbi__pnm_skip_whitespace(s, &c); - - *x = stbi__pnm_getinteger(s, &c); // read width - stbi__pnm_skip_whitespace(s, &c); - - *y = stbi__pnm_getinteger(s, &c); // read height - stbi__pnm_skip_whitespace(s, &c); - - maxv = stbi__pnm_getinteger(s, &c); // read max value - - if (maxv > 255) - return stbi__err("max value > 255", "PPM image not 8-bit"); - else - return 1; -} -#endif - -static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) -{ - #ifndef STBI_NO_JPEG - if (stbi__jpeg_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNG - if (stbi__png_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_GIF - if (stbi__gif_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_BMP - if (stbi__bmp_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PSD - if (stbi__psd_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PIC - if (stbi__pic_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNM - if (stbi__pnm_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_info(s, x, y, comp)) return 1; - #endif - - // test tga last because it's a crappy test! - #ifndef STBI_NO_TGA - if (stbi__tga_info(s, x, y, comp)) - return 1; - #endif - return stbi__err("unknown image type", "Image not of any known type, or corrupt"); -} - -static int stbi__is_16_main(stbi__context *s) -{ - #ifndef STBI_NO_PNG - if (stbi__png_is16(s)) return 1; - #endif - - #ifndef STBI_NO_PSD - if (stbi__psd_is16(s)) return 1; - #endif - - return 0; -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_info_from_file(f, x, y, comp); - fclose(f); - return result; -} - -STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__info_main(&s,x,y,comp); - fseek(f,pos,SEEK_SET); - return r; -} - -STBIDEF int stbi_is_16_bit(char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_is_16_bit_from_file(f); - fclose(f); - return result; -} - -STBIDEF int stbi_is_16_bit_from_file(FILE *f) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__is_16_main(&s); - fseek(f,pos,SEEK_SET); - return r; -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__is_16_main(&s); -} - -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__is_16_main(&s); -} - -#endif // STB_IMAGE_IMPLEMENTATION - -/* - revision history: - 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug - 1-bit BMP - *_is_16_bit api - avoid warnings - 2.16 (2017-07-23) all functions have 16-bit variants; - STBI_NO_STDIO works again; - compilation fixes; - fix rounding in unpremultiply; - optimize vertical flip; - disable raw_len validation; - documentation fixes - 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; - warning fixes; disable run-time SSE detection on gcc; - uniform handling of optional "return" values; - thread-safe initialization of zlib tables - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) allocate large structures on the stack - remove white matting for transparent PSD - fix reported channel count for PNG & BMP - re-enable SSE2 in non-gcc 64-bit - support RGB-formatted JPEG - read 16-bit PNGs (only as 8-bit) - 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED - 2.09 (2016-01-16) allow comments in PNM files - 16-bit-per-pixel TGA (not bit-per-component) - info() for TGA could break due to .hdr handling - info() for BMP to shares code instead of sloppy parse - can use STBI_REALLOC_SIZED if allocator doesn't support realloc - code cleanup - 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA - 2.07 (2015-09-13) fix compiler warnings - partial animated GIF support - limited 16-bpc PSD support - #ifdef unused functions - bug with < 92 byte PIC,PNM,HDR,TGA - 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value - 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning - 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit - 2.03 (2015-04-12) extra corruption checking (mmozeiko) - stbi_set_flip_vertically_on_load (nguillemot) - fix NEON support; fix mingw support - 2.02 (2015-01-19) fix incorrect assert, fix warning - 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 - 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG - 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) - progressive JPEG (stb) - PGM/PPM support (Ken Miller) - STBI_MALLOC,STBI_REALLOC,STBI_FREE - GIF bugfix -- seemingly never worked - STBI_NO_*, STBI_ONLY_* - 1.48 (2014-12-14) fix incorrectly-named assert() - 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) - optimize PNG (ryg) - fix bug in interlaced PNG with user-specified channel count (stb) - 1.46 (2014-08-26) - fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG - 1.45 (2014-08-16) - fix MSVC-ARM internal compiler error by wrapping malloc - 1.44 (2014-08-07) - various warning fixes from Ronny Chevalier - 1.43 (2014-07-15) - fix MSVC-only compiler problem in code changed in 1.42 - 1.42 (2014-07-09) - don't define _CRT_SECURE_NO_WARNINGS (affects user code) - fixes to stbi__cleanup_jpeg path - added STBI_ASSERT to avoid requiring assert.h - 1.41 (2014-06-25) - fix search&replace from 1.36 that messed up comments/error messages - 1.40 (2014-06-22) - fix gcc struct-initialization warning - 1.39 (2014-06-15) - fix to TGA optimization when req_comp != number of components in TGA; - fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) - add support for BMP version 5 (more ignored fields) - 1.38 (2014-06-06) - suppress MSVC warnings on integer casts truncating values - fix accidental rename of 'skip' field of I/O - 1.37 (2014-06-04) - remove duplicate typedef - 1.36 (2014-06-03) - convert to header file single-file library - if de-iphone isn't set, load iphone images color-swapped instead of returning NULL - 1.35 (2014-05-27) - various warnings - fix broken STBI_SIMD path - fix bug where stbi_load_from_file no longer left file pointer in correct place - fix broken non-easy path for 32-bit BMP (possibly never used) - TGA optimization by Arseny Kapoulkine - 1.34 (unknown) - use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case - 1.33 (2011-07-14) - make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements - 1.32 (2011-07-13) - support for "info" function for all supported filetypes (SpartanJ) - 1.31 (2011-06-20) - a few more leak fixes, bug in PNG handling (SpartanJ) - 1.30 (2011-06-11) - added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) - removed deprecated format-specific test/load functions - removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway - error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) - fix inefficiency in decoding 32-bit BMP (David Woo) - 1.29 (2010-08-16) - various warning fixes from Aurelien Pocheville - 1.28 (2010-08-01) - fix bug in GIF palette transparency (SpartanJ) - 1.27 (2010-08-01) - cast-to-stbi_uc to fix warnings - 1.26 (2010-07-24) - fix bug in file buffering for PNG reported by SpartanJ - 1.25 (2010-07-17) - refix trans_data warning (Won Chun) - 1.24 (2010-07-12) - perf improvements reading from files on platforms with lock-heavy fgetc() - minor perf improvements for jpeg - deprecated type-specific functions so we'll get feedback if they're needed - attempt to fix trans_data warning (Won Chun) - 1.23 fixed bug in iPhone support - 1.22 (2010-07-10) - removed image *writing* support - stbi_info support from Jetro Lauha - GIF support from Jean-Marc Lienher - iPhone PNG-extensions from James Brown - warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) - 1.21 fix use of 'stbi_uc' in header (reported by jon blow) - 1.20 added support for Softimage PIC, by Tom Seddon - 1.19 bug in interlaced PNG corruption check (found by ryg) - 1.18 (2008-08-02) - fix a threading bug (local mutable static) - 1.17 support interlaced PNG - 1.16 major bugfix - stbi__convert_format converted one too many pixels - 1.15 initialize some fields for thread safety - 1.14 fix threadsafe conversion bug - header-file-only version (#define STBI_HEADER_FILE_ONLY before including) - 1.13 threadsafe - 1.12 const qualifiers in the API - 1.11 Support installable IDCT, colorspace conversion routines - 1.10 Fixes for 64-bit (don't use "unsigned long") - optimized upsampling by Fabian "ryg" Giesen - 1.09 Fix format-conversion for PSD code (bad global variables!) - 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz - 1.07 attempt to fix C++ warning/errors again - 1.06 attempt to fix C++ warning/errors again - 1.05 fix TGA loading to return correct *comp and use good luminance calc - 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free - 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR - 1.02 support for (subset of) HDR files, float interface for preferred access to them - 1.01 fix bug: possible bug in handling right-side up bmps... not sure - fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all - 1.00 interface to zlib that skips zlib header - 0.99 correct handling of alpha in palette - 0.98 TGA loader by lonesock; dynamically add loaders (untested) - 0.97 jpeg errors on too large a file; also catch another malloc failure - 0.96 fix detection of invalid v value - particleman@mollyrocket forum - 0.95 during header scan, seek to markers in case of padding - 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same - 0.93 handle jpegtran output; verbose errors - 0.92 read 4,8,16,24,32-bit BMP files of several formats - 0.91 output 24-bit Windows 3.0 BMP files - 0.90 fix a few more warnings; bump version number to approach 1.0 - 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd - 0.60 fix compiling as c++ - 0.59 fix warnings: merge Dave Moore's -Wall fixes - 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian - 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available - 0.56 fix bug: zlib uncompressed mode len vs. nlen - 0.55 fix bug: restart_interval not initialized to 0 - 0.54 allow NULL for 'int *comp' - 0.53 fix bug in png 3->4; speedup png decoding - 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments - 0.51 obey req_comp requests, 1-component jpegs return as 1-component, - on 'test' only check type, not whether we support this variant - 0.50 (2006-11-19) - first released version -*/ - - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- -*/ diff --git a/contrib/stb_image/stb_image.h b/contrib/stb_image/stb_image.h index d9c21bc81..accef4839 100644 --- a/contrib/stb_image/stb_image.h +++ b/contrib/stb_image/stb_image.h @@ -1,4 +1,4 @@ -/* stb_image - v2.19 - public domain image loader - http://nothings.org/stb +/* stb_image - v2.26 - public domain image loader - http://nothings.org/stb no warranty implied; use at your own risk Do this: @@ -48,6 +48,13 @@ LICENSE RECENT REVISION HISTORY: + 2.26 (2020-07-13) many minor fixes + 2.25 (2020-02-02) fix warnings + 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically + 2.23 (2019-08-11) fix clang static analysis warning + 2.22 (2019-03-04) gif fixes, fix warnings + 2.21 (2019-02-25) fix typo in comment + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs 2.19 (2018-02-11) fix warning 2.18 (2018-01-30) fix warnings 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings @@ -84,23 +91,33 @@ RECENT REVISION HISTORY: Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) Arseny Kapoulkine John-Mark Allen + Carmelo J Fdez-Aguera Bug & warning fixes - Marc LeBlanc David Woo Guillaume George Martins Mozeiko - Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan - Dave Moore Roy Eltham Hayaki Saito Nathan Reed - Won Chun Luke Graham Johan Duparc Nick Verigakis - the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh - Janez Zemva John Bartholomew Michal Cichon github:romigrou - Jonathan Blow Ken Hamada Tero Hanninen github:svdijk - Laurent Gomila Cort Stratton Sergio Gonzalez github:snagar - Aruelien Pocheville Thibault Reuille Cass Everitt github:Zelex - Ryamond Barbiero Paul Du Bois Engin Manap github:grim210 - Aldo Culquicondor Philipp Wiesemann Dale Weiler github:sammyhw - Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus - Julian Raschke Gregory Mullen Baldur Karlsson github:poppolopoppo - Christian Floisand Kevin Schmidt github:darealshinji - Blazej Dariusz Roszkowski github:Michaelangel007 + Marc LeBlanc David Woo Guillaume George Martins Mozeiko + Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski + Phil Jordan Dave Moore Roy Eltham + Hayaki Saito Nathan Reed Won Chun + Luke Graham Johan Duparc Nick Verigakis the Horde3D community + Thomas Ruf Ronny Chevalier github:rlyeh + Janez Zemva John Bartholomew Michal Cichon github:romigrou + Jonathan Blow Ken Hamada Tero Hanninen github:svdijk + Laurent Gomila Cort Stratton github:snagar + Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex + Cass Everitt Ryamond Barbiero github:grim210 + Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw + Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus + Josh Tobin Matthew Gregan github:poppolopoppo + Julian Raschke Gregory Mullen Christian Floisand github:darealshinji + Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 + Brad Weinberger Matvey Cherevko [reserved] + Luca Sas Alexander Veselov Zack Middleton [reserved] + Ryan C. Gordon [reserved] [reserved] + DO NOT ADD YOUR NAME HERE + + To add your name to the credits, pick a random blank space in the middle and fill it. + 80% of merge conflicts on stb PRs are due to people adding their name at the end + of the credits. */ #ifndef STBI_INCLUDE_STB_IMAGE_H @@ -161,6 +178,16 @@ RECENT REVISION HISTORY: // // =========================================================================== // +// UNICODE: +// +// If compiling for Windows and you wish to use Unicode filenames, compile +// with +// #define STBI_WINDOWS_UTF8 +// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert +// Windows wchar_t filenames to utf8. +// +// =========================================================================== +// // Philosophy // // stb libraries are designed with the following priorities: @@ -171,12 +198,12 @@ RECENT REVISION HISTORY: // // Sometimes I let "good performance" creep up in priority over "easy to maintain", // and for best performance I may provide less-easy-to-use APIs that give higher -// performance, in addition to the easy to use ones. Nevertheless, it's important +// performance, in addition to the easy-to-use ones. Nevertheless, it's important // to keep in mind that from the standpoint of you, a client of this library, // all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. // // Some secondary priorities arise directly from the first two, some of which -// make more explicit reasons why performance can't be emphasized. +// provide more explicit reasons why performance can't be emphasized. // // - Portable ("ease of use") // - Small source code footprint ("easy to maintain") @@ -219,11 +246,10 @@ RECENT REVISION HISTORY: // // HDR image support (disable by defining STBI_NO_HDR) // -// stb_image now supports loading HDR images in general, and currently -// the Radiance .HDR file format, although the support is provided -// generically. You can still load any file through the existing interface; -// if you attempt to load an HDR file, it will be automatically remapped to -// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; +// stb_image supports loading HDR images in general, and currently the Radiance +// .HDR file format specifically. You can still load any file through the existing +// interface; if you attempt to load an HDR file, it will be automatically remapped +// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; // both of these constants can be reconfigured through this interface: // // stbi_hdr_to_ldr_gamma(2.2f); @@ -257,7 +283,7 @@ RECENT REVISION HISTORY: // // By default we convert iphone-formatted PNGs back to RGB, even though // they are internally encoded differently. You can disable this conversion -// by by calling stbi_convert_iphone_png_to_rgb(0), in which case +// by calling stbi_convert_iphone_png_to_rgb(0), in which case // you will always just get the native iphone "format" through (which // is BGR stored in RGB). // @@ -301,7 +327,14 @@ RECENT REVISION HISTORY: // - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still // want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB // - +// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater +// than that size (in either width or height) without further processing. +// This is to let programs in the wild set an upper bound to prevent +// denial-of-service attacks on untrusted data, as one could generate a +// valid image of gigantic dimensions and force stb_image to allocate a +// huge block of memory and spend disproportionate time decoding it. By +// default this is set to (1 << 24), which is 16777216, but that's still +// very big. #ifndef STBI_NO_STDIO #include @@ -319,6 +352,7 @@ enum STBI_rgb_alpha = 4 }; +#include typedef unsigned char stbi_uc; typedef unsigned short stbi_us; @@ -326,11 +360,13 @@ typedef unsigned short stbi_us; extern "C" { #endif +#ifndef STBIDEF #ifdef STB_IMAGE_STATIC #define STBIDEF static #else #define STBIDEF extern #endif +#endif ////////////////////////////////////////////////////////////////////////////// // @@ -355,10 +391,6 @@ typedef struct STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -#endif - #ifndef STBI_NO_STDIO STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); @@ -366,6 +398,14 @@ STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in // for stbi_load_from_file, file pointer is left pointing immediately after image #endif +#ifndef STBI_NO_GIF +STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); +#endif + +#ifdef STBI_WINDOWS_UTF8 +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input); +#endif + //////////////////////////////////// // // 16-bits-per-channel interface @@ -413,7 +453,7 @@ STBIDEF int stbi_is_hdr_from_file(FILE *f); // get a VERY brief reason for failure -// NOT THREADSAFE +// on most compilers (and ALL modern mainstream compilers) this is threadsafe STBIDEF const char *stbi_failure_reason (void); // free the loaded image -- this is just free() @@ -446,6 +486,11 @@ STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); // flip the image vertically, so the first pixel in the output array is the bottom left STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); +// as above, but only applies to images loaded on the thread that calls the function +// this function is only available if your compiler supports thread-local variables; +// calling it will fail to link if your compiler doesn't +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip); + // ZLIB client - used by PNG, available for other purposes STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); @@ -525,6 +570,12 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #define STBI_ASSERT(x) assert(x) #endif +#ifdef __cplusplus +#define STBI_EXTERN extern "C" +#else +#define STBI_EXTERN extern +#endif + #ifndef _MSC_VER #ifdef __cplusplus @@ -536,6 +587,23 @@ STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const ch #define stbi_inline __forceinline #endif +#ifndef STBI_NO_THREAD_LOCALS + #if defined(__cplusplus) && __cplusplus >= 201103L + #define STBI_THREAD_LOCAL thread_local + #elif defined(__GNUC__) && __GNUC__ < 5 + #define STBI_THREAD_LOCAL __thread + #elif defined(_MSC_VER) + #define STBI_THREAD_LOCAL __declspec(thread) + #elif defined (__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && !defined(__STDC_NO_THREADS__) + #define STBI_THREAD_LOCAL _Thread_local + #endif + + #ifndef STBI_THREAD_LOCAL + #if defined(__GNUC__) + #define STBI_THREAD_LOCAL __thread + #endif + #endif +#endif #ifdef _MSC_VER typedef unsigned short stbi__uint16; @@ -649,14 +717,18 @@ static int stbi__cpuid3(void) #define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) static int stbi__sse2_available(void) { int info3 = stbi__cpuid3(); return ((info3 >> 26) & 1) != 0; } +#endif + #else // assume GCC-style if not VC++ #define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) +#if !defined(STBI_NO_JPEG) && defined(STBI_SSE2) static int stbi__sse2_available(void) { // If we're even attempting to compile this on GCC/Clang, that means @@ -664,6 +736,8 @@ static int stbi__sse2_available(void) // instructions at will, and so are we. return 1; } +#endif + #endif #endif @@ -682,6 +756,10 @@ static int stbi__sse2_available(void) #define STBI_SIMD_ALIGN(type, name) type name #endif +#ifndef STBI_MAX_DIMENSIONS +#define STBI_MAX_DIMENSIONS (1 << 24) +#endif + /////////////////////////////////////////////// // // stbi__context struct and start_xxx functions @@ -699,6 +777,7 @@ typedef struct int read_from_callbacks; int buflen; stbi_uc buffer_start[128]; + int callback_already_read; stbi_uc *img_buffer, *img_buffer_end; stbi_uc *img_buffer_original, *img_buffer_original_end; @@ -712,6 +791,7 @@ static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) { s->io.read = NULL; s->read_from_callbacks = 0; + s->callback_already_read = 0; s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; } @@ -723,7 +803,8 @@ static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void * s->io_user_data = user; s->buflen = sizeof(s->buffer_start); s->read_from_callbacks = 1; - s->img_buffer_original = s->buffer_start; + s->callback_already_read = 0; + s->img_buffer = s->img_buffer_original = s->buffer_start; stbi__refill_buffer(s); s->img_buffer_original_end = s->img_buffer_end; } @@ -737,12 +818,17 @@ static int stbi__stdio_read(void *user, char *data, int size) static void stbi__stdio_skip(void *user, int n) { + int ch; fseek((FILE*) user, n, SEEK_CUR); + ch = fgetc((FILE*) user); /* have to read a byte to reset feof()'s flag */ + if (ch != EOF) { + ungetc(ch, (FILE *) user); /* push byte back onto stream if valid. */ + } } static int stbi__stdio_eof(void *user) { - return feof((FILE*) user); + return feof((FILE*) user) || ferror((FILE *) user); } static stbi_io_callbacks stbi__stdio_callbacks = @@ -840,19 +926,24 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); #endif -// this is not threadsafe -static const char *stbi__g_failure_reason; +static +#ifdef STBI_THREAD_LOCAL +STBI_THREAD_LOCAL +#endif +const char *stbi__g_failure_reason; STBIDEF const char *stbi_failure_reason(void) { return stbi__g_failure_reason; } +#ifndef STBI_NO_FAILURE_STRINGS static int stbi__err(const char *str) { stbi__g_failure_reason = str; return 0; } +#endif static void *stbi__malloc(size_t size) { @@ -891,11 +982,13 @@ static int stbi__mul2sizes_valid(int a, int b) return a <= INT_MAX/b; } +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) // returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow static int stbi__mad2sizes_valid(int a, int b, int add) { return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); } +#endif // returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow static int stbi__mad3sizes_valid(int a, int b, int c, int add) @@ -913,12 +1006,14 @@ static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) } #endif +#if !defined(STBI_NO_JPEG) || !defined(STBI_NO_PNG) || !defined(STBI_NO_TGA) || !defined(STBI_NO_HDR) // mallocs with size overflow checking static void *stbi__malloc_mad2(int a, int b, int add) { if (!stbi__mad2sizes_valid(a, b, add)) return NULL; return stbi__malloc(a*b + add); } +#endif static void *stbi__malloc_mad3(int a, int b, int c, int add) { @@ -962,13 +1057,29 @@ static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); #endif -static int stbi__vertically_flip_on_load = 0; +static int stbi__vertically_flip_on_load_global = 0; STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) { - stbi__vertically_flip_on_load = flag_true_if_should_flip; + stbi__vertically_flip_on_load_global = flag_true_if_should_flip; } +#ifndef STBI_THREAD_LOCAL +#define stbi__vertically_flip_on_load stbi__vertically_flip_on_load_global +#else +static STBI_THREAD_LOCAL int stbi__vertically_flip_on_load_local, stbi__vertically_flip_on_load_set; + +STBIDEF void stbi_set_flip_vertically_on_load_thread(int flag_true_if_should_flip) +{ + stbi__vertically_flip_on_load_local = flag_true_if_should_flip; + stbi__vertically_flip_on_load_set = 1; +} + +#define stbi__vertically_flip_on_load (stbi__vertically_flip_on_load_set \ + ? stbi__vertically_flip_on_load_local \ + : stbi__vertically_flip_on_load_global) +#endif // STBI_THREAD_LOCAL + static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) { memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields @@ -990,6 +1101,8 @@ static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int re #endif #ifndef STBI_NO_PSD if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); + #else + STBI_NOTUSED(bpc); #endif #ifndef STBI_NO_PIC if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); @@ -1070,6 +1183,7 @@ static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) } } +#ifndef STBI_NO_GIF static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) { int slice; @@ -1077,10 +1191,11 @@ static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int byt stbi_uc *bytes = (stbi_uc *)image; for (slice = 0; slice < z; ++slice) { - stbi__vertical_flip(bytes, w, h, bytes_per_pixel); - bytes += slice_size; + stbi__vertical_flip(bytes, w, h, bytes_per_pixel); + bytes += slice_size; } } +#endif static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) { @@ -1090,8 +1205,10 @@ static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, if (result == NULL) return NULL; + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + if (ri.bits_per_channel != 8) { - STBI_ASSERT(ri.bits_per_channel == 16); result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); ri.bits_per_channel = 8; } @@ -1114,8 +1231,10 @@ static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, if (result == NULL) return NULL; + // it is the responsibility of the loaders to make sure we get either 8 or 16 bit. + STBI_ASSERT(ri.bits_per_channel == 8 || ri.bits_per_channel == 16); + if (ri.bits_per_channel != 16) { - STBI_ASSERT(ri.bits_per_channel == 8); result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); ri.bits_per_channel = 16; } @@ -1131,7 +1250,7 @@ static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, return (stbi__uint16 *) result; } -#if !defined(STBI_NO_HDR) || !defined(STBI_NO_LINEAR) +#if !defined(STBI_NO_HDR) && !defined(STBI_NO_LINEAR) static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) { if (stbi__vertically_flip_on_load && result != NULL) { @@ -1143,10 +1262,38 @@ static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, in #ifndef STBI_NO_STDIO +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +STBI_EXTERN __declspec(dllimport) int __stdcall MultiByteToWideChar(unsigned int cp, unsigned long flags, const char *str, int cbmb, wchar_t *widestr, int cchwide); +STBI_EXTERN __declspec(dllimport) int __stdcall WideCharToMultiByte(unsigned int cp, unsigned long flags, const wchar_t *widestr, int cchwide, char *str, int cbmb, const char *defchar, int *used_default); +#endif + +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) +STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input) +{ + return WideCharToMultiByte(65001 /* UTF8 */, 0, input, -1, buffer, (int) bufferlen, NULL, NULL); +} +#endif + static FILE *stbi__fopen(char const *filename, char const *mode) { FILE *f; -#if defined(_MSC_VER) && _MSC_VER >= 1400 +#if defined(_MSC_VER) && defined(STBI_WINDOWS_UTF8) + wchar_t wMode[64]; + wchar_t wFilename[1024]; + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, filename, -1, wFilename, sizeof(wFilename))) + return 0; + + if (0 == MultiByteToWideChar(65001 /* UTF8 */, 0, mode, -1, wMode, sizeof(wMode))) + return 0; + +#if _MSC_VER >= 1400 + if (0 != _wfopen_s(&f, wFilename, wMode)) + f = 0; +#else + f = _wfopen(wFilename, wMode); +#endif + +#elif defined(_MSC_VER) && _MSC_VER >= 1400 if (0 != fopen_s(&f, filename, mode)) f=0; #else @@ -1237,15 +1384,15 @@ STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *u STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { unsigned char *result; - stbi__context s; - stbi__start_mem(&s,buffer,len); - + stbi__context s; + stbi__start_mem(&s,buffer,len); + result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); if (stbi__vertically_flip_on_load) { - stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); + stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); } - return result; + return result; } #endif @@ -1390,6 +1537,7 @@ enum static void stbi__refill_buffer(stbi__context *s) { int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); + s->callback_already_read += (int) (s->img_buffer - s->img_buffer_original); if (n == 0) { // at end of file, treat same as if from memory, but need to handle case // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file @@ -1414,6 +1562,9 @@ stbi_inline static stbi_uc stbi__get8(stbi__context *s) return 0; } +#if defined(STBI_NO_JPEG) && defined(STBI_NO_HDR) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else stbi_inline static int stbi__at_eof(stbi__context *s) { if (s->io.read) { @@ -1425,9 +1576,14 @@ stbi_inline static int stbi__at_eof(stbi__context *s) return s->img_buffer >= s->img_buffer_end; } +#endif +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) +// nothing +#else static void stbi__skip(stbi__context *s, int n) { + if (n == 0) return; // already there! if (n < 0) { s->img_buffer = s->img_buffer_end; return; @@ -1442,7 +1598,11 @@ static void stbi__skip(stbi__context *s, int n) } s->img_buffer += n; } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_TGA) && defined(STBI_NO_HDR) && defined(STBI_NO_PNM) +// nothing +#else static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) { if (s->io.read) { @@ -1466,18 +1626,27 @@ static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) } else return 0; } +#endif +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else static int stbi__get16be(stbi__context *s) { int z = stbi__get8(s); return (z << 8) + stbi__get8(s); } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) && defined(STBI_NO_PIC) +// nothing +#else static stbi__uint32 stbi__get32be(stbi__context *s) { stbi__uint32 z = stbi__get16be(s); return (z << 16) + stbi__get16be(s); } +#endif #if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) // nothing @@ -1499,7 +1668,9 @@ static stbi__uint32 stbi__get32le(stbi__context *s) #define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings - +#if defined(STBI_NO_JPEG) && defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else ////////////////////////////////////////////////////////////////////////////// // // generic converter from built-in img_n to req_comp @@ -1515,7 +1686,11 @@ static stbi_uc stbi__compute_y(int r, int g, int b) { return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_BMP) && defined(STBI_NO_PSD) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) && defined(STBI_NO_PIC) && defined(STBI_NO_PNM) +// nothing +#else static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) { int i,j; @@ -1539,19 +1714,19 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r // convert source image with img_n components to one with req_comp components; // avoid switch per pixel, so use switch per scanline and massive macros switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0], dest[1]=255; } break; + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=255; } break; STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=255; } break; STBI__CASE(2,1) { dest[0]=src[0]; } break; STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=255; } break; STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = 255; } break; STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; - default: STBI_ASSERT(0); + STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return stbi__errpuc("unsupported", "Unsupported format conversion"); } #undef STBI__CASE } @@ -1559,12 +1734,20 @@ static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int r STBI_FREE(data); return good; } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else static stbi__uint16 stbi__compute_y_16(int r, int g, int b) { return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); } +#endif +#if defined(STBI_NO_PNG) && defined(STBI_NO_PSD) +// nothing +#else static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) { int i,j; @@ -1588,19 +1771,19 @@ static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int r // convert source image with img_n components to one with req_comp components; // avoid switch per pixel, so use switch per scanline and massive macros switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0], dest[1]=0xffff; } break; + STBI__CASE(1,2) { dest[0]=src[0]; dest[1]=0xffff; } break; STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=0xffff; } break; + STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=0xffff; } break; STBI__CASE(2,1) { dest[0]=src[0]; } break; STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=0xffff; } break; + STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0]; dest[3]=src[1]; } break; + STBI__CASE(3,4) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2];dest[3]=0xffff; } break; STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = 0xffff; } break; + STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = 0xffff; } break; STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; - default: STBI_ASSERT(0); + STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); dest[1] = src[3]; } break; + STBI__CASE(4,3) { dest[0]=src[0];dest[1]=src[1];dest[2]=src[2]; } break; + default: STBI_ASSERT(0); STBI_FREE(data); STBI_FREE(good); return (stbi__uint16*) stbi__errpuc("unsupported", "Unsupported format conversion"); } #undef STBI__CASE } @@ -1608,6 +1791,7 @@ static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int r STBI_FREE(data); return good; } +#endif #ifndef STBI_NO_LINEAR static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) @@ -1623,7 +1807,11 @@ static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) for (k=0; k < n; ++k) { output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); } - if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; + } + if (n < comp) { + for (i=0; i < x*y; ++i) { + output[i*comp + n] = data[i*comp + n]/255.0f; + } } STBI_FREE(data); return output; @@ -1904,7 +2092,7 @@ stbi_inline static int stbi__extend_receive(stbi__jpeg *j, int n) sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB k = stbi_lrot(j->code_buffer, n); - STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); + if (n < 0 || n >= (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))) return 0; j->code_buffer = k & ~stbi__bmask[n]; k &= stbi__bmask[n]; j->code_bits -= n; @@ -2015,6 +2203,7 @@ static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__ // first scan for DC coefficient, must be first memset(data,0,64*sizeof(data[0])); // 0 all the ac values now t = stbi__jpeg_huff_decode(j, hdc); + if (t == -1) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); diff = t ? stbi__extend_receive(j, t) : 0; dc = j->img_comp[b].dc_pred + diff; @@ -3005,6 +3194,8 @@ static int stbi__process_frame_header(stbi__jpeg *z, int scan) p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); c = stbi__get8(s); if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); s->img_n = c; @@ -3596,7 +3787,7 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp int k; unsigned int i,j; stbi_uc *output; - stbi_uc *coutput[4]; + stbi_uc *coutput[4] = { NULL, NULL, NULL, NULL }; stbi__resample res_comp[4]; @@ -3717,7 +3908,7 @@ static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp if (n == 1) for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; else - for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; + for (i=0; i < z->s->img_x; ++i) { *out++ = y[i]; *out++ = 255; } } } } @@ -3885,16 +4076,23 @@ typedef struct stbi__zhuffman z_length, z_distance; } stbi__zbuf; +stbi_inline static int stbi__zeof(stbi__zbuf *z) +{ + return (z->zbuffer >= z->zbuffer_end); +} + stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) { - if (z->zbuffer >= z->zbuffer_end) return 0; - return *z->zbuffer++; + return stbi__zeof(z) ? 0 : *z->zbuffer++; } static void stbi__fill_bits(stbi__zbuf *z) { do { - STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); + if (z->code_buffer >= (1U << z->num_bits)) { + z->zbuffer = z->zbuffer_end; /* treat this as EOF so we fail. */ + return; + } z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; z->num_bits += 8; } while (z->num_bits <= 24); @@ -3919,10 +4117,11 @@ static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) for (s=STBI__ZFAST_BITS+1; ; ++s) if (k < z->maxcode[s]) break; - if (s == 16) return -1; // invalid code! + if (s >= 16) return -1; // invalid code! // code size is s, so: b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - STBI_ASSERT(z->size[b] == s); + if (b >= sizeof (z->size)) return -1; // some data was corrupt somewhere! + if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. a->code_buffer >>= s; a->num_bits -= s; return z->value[b]; @@ -3931,7 +4130,12 @@ static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) { int b,s; - if (a->num_bits < 16) stbi__fill_bits(a); + if (a->num_bits < 16) { + if (stbi__zeof(a)) { + return -1; /* report error for unexpected end of data. */ + } + stbi__fill_bits(a); + } b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; if (b) { s = b >> 9; @@ -3945,13 +4149,16 @@ stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes { char *q; - int cur, limit, old_limit; + unsigned int cur, limit, old_limit; z->zout = zout; if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); - cur = (int) (z->zout - z->zout_start); - limit = old_limit = (int) (z->zout_end - z->zout_start); - while (cur + n > limit) + cur = (unsigned int) (z->zout - z->zout_start); + limit = old_limit = (unsigned) (z->zout_end - z->zout_start); + if (UINT_MAX - cur < (unsigned) n) return stbi__err("outofmem", "Out of memory"); + while (cur + n > limit) { + if(limit > UINT_MAX / 2) return stbi__err("outofmem", "Out of memory"); limit *= 2; + } q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); STBI_NOTUSED(old_limit); if (q == NULL) return stbi__err("outofmem", "Out of memory"); @@ -4049,11 +4256,12 @@ static int stbi__compute_huffman_codes(stbi__zbuf *a) c = stbi__zreceive(a,2)+3; if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); fill = lencodes[n-1]; - } else if (c == 17) + } else if (c == 17) { c = stbi__zreceive(a,3)+3; - else { - STBI_ASSERT(c == 18); + } else if (c == 18) { c = stbi__zreceive(a,7)+11; + } else { + return stbi__err("bad codelengths", "Corrupt PNG"); } if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); memset(lencodes+n, fill, c); @@ -4079,7 +4287,7 @@ static int stbi__parse_uncompressed_block(stbi__zbuf *a) a->code_buffer >>= 8; a->num_bits -= 8; } - STBI_ASSERT(a->num_bits == 0); + if (a->num_bits < 0) return stbi__err("zlib corrupt","Corrupt PNG"); // now fill header the normal way while (k < 4) header[k++] = stbi__zget8(a); @@ -4101,6 +4309,7 @@ static int stbi__parse_zlib_header(stbi__zbuf *a) int cm = cmf & 15; /* int cinfo = cmf >> 4; */ int flg = stbi__zget8(a); + if (stbi__zeof(a)) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png @@ -4362,7 +4571,7 @@ static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 r return stbi__err("invalid filter","Corrupt PNG"); if (depth < 8) { - STBI_ASSERT(img_width_bytes <= x); + if (img_width_bytes > x) return stbi__err("invalid width","Corrupt PNG"); cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place filter_bytes = 1; width = img_width_bytes; @@ -4731,7 +4940,7 @@ static void stbi__de_iphone(stbi__png *z) static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) { stbi_uc palette[1024], pal_img_n=0; - stbi_uc has_trans=0, tc[3]; + stbi_uc has_trans=0, tc[3]={0}; stbi__uint16 tc16[3]; stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; int first=1,k,interlace=0, color=0, is_iphone=0; @@ -4757,8 +4966,10 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); first = 0; if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); - s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); - s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); + s->img_x = stbi__get32be(s); + s->img_y = stbi__get32be(s); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); @@ -4875,6 +5086,8 @@ static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) ++s->img_n; } STBI_FREE(z->expanded); z->expanded = NULL; + // end of PNG chunk, read and skip CRC + stbi__get32be(s); return 1; } @@ -4905,10 +5118,12 @@ static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, st void *result=NULL; if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth < 8) + if (p->depth <= 8) ri->bits_per_channel = 8; + else if (p->depth == 16) + ri->bits_per_channel = 16; else - ri->bits_per_channel = p->depth; + return stbi__errpuc("bad bits_per_channel", "PNG not supported: unsupported color depth"); result = p->out; p->out = NULL; if (req_comp && req_comp != p->s->img_out_n) { @@ -5009,11 +5224,11 @@ static int stbi__high_bit(unsigned int z) { int n=0; if (z == 0) return -1; - if (z >= 0x10000) n += 16, z >>= 16; - if (z >= 0x00100) n += 8, z >>= 8; - if (z >= 0x00010) n += 4, z >>= 4; - if (z >= 0x00004) n += 2, z >>= 2; - if (z >= 0x00002) n += 1, z >>= 1; + if (z >= 0x10000) { n += 16; z >>= 16; } + if (z >= 0x00100) { n += 8; z >>= 8; } + if (z >= 0x00010) { n += 4; z >>= 4; } + if (z >= 0x00004) { n += 2; z >>= 2; } + if (z >= 0x00002) { n += 1;/* >>= 1;*/ } return n; } @@ -5030,7 +5245,7 @@ static int stbi__bitcount(unsigned int a) // extract an arbitrarily-aligned N-bit value (N=bits) // from v, and then make it 8-bits long and fractionally // extend it to full full range. -static int stbi__shiftsigned(int v, int shift, int bits) +static int stbi__shiftsigned(unsigned int v, int shift, int bits) { static unsigned int mul_table[9] = { 0, @@ -5044,7 +5259,7 @@ static int stbi__shiftsigned(int v, int shift, int bits) v <<= -shift; else v >>= shift; - STBI_ASSERT(v >= 0 && v < 256); + STBI_ASSERT(v < 256); v >>= (8-bits); STBI_ASSERT(bits >= 0 && bits <= 8); return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; @@ -5054,6 +5269,7 @@ typedef struct { int bpp, offset, hsz; unsigned int mr,mg,mb,ma, all_a; + int extra_read; } stbi__bmp_data; static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) @@ -5066,6 +5282,9 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) info->offset = stbi__get32le(s); info->hsz = hsz = stbi__get32le(s); info->mr = info->mg = info->mb = info->ma = 0; + info->extra_read = 14; + + if (info->offset < 0) return stbi__errpuc("bad BMP", "bad BMP"); if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); if (hsz == 12) { @@ -5109,6 +5328,7 @@ static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) info->mr = stbi__get32le(s); info->mg = stbi__get32le(s); info->mb = stbi__get32le(s); + info->extra_read += 12; // not documented, but generated by photoshop and handled by mspaint if (info->mr == info->mg && info->mg == info->mb) { // ?!?!? @@ -5157,6 +5377,9 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req flip_vertically = ((int) s->img_y) > 0; s->img_y = abs((int) s->img_y); + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + mr = info.mr; mg = info.mg; mb = info.mb; @@ -5165,13 +5388,22 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req if (info.hsz == 12) { if (info.bpp < 24) - psize = (info.offset - 14 - 24) / 3; + psize = (info.offset - info.extra_read - 24) / 3; } else { if (info.bpp < 16) - psize = (info.offset - 14 - info.hsz) >> 2; + psize = (info.offset - info.extra_read - info.hsz) >> 2; + } + if (psize == 0) { + STBI_ASSERT(info.offset == s->callback_already_read + (int) (s->img_buffer - s->img_buffer_original)); + if (info.offset != s->callback_already_read + (s->img_buffer - s->buffer_start)) { + return stbi__errpuc("bad offset", "Corrupt BMP"); + } } - s->img_n = ma ? 4 : 3; + if (info.bpp == 24 && ma == 0xff000000) + s->img_n = 3; + else + s->img_n = ma ? 4 : 3; if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 target = req_comp; else @@ -5193,7 +5425,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req if (info.hsz != 12) stbi__get8(s); pal[i][3] = 255; } - stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); + stbi__skip(s, info.offset - info.extra_read - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); if (info.bpp == 1) width = (s->img_x + 7) >> 3; else if (info.bpp == 4) width = (s->img_x + 1) >> 1; else if (info.bpp == 8) width = s->img_x; @@ -5207,6 +5439,8 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req out[z++] = pal[color][0]; out[z++] = pal[color][1]; out[z++] = pal[color][2]; + if (target == 4) out[z++] = 255; + if (i+1 == (int) s->img_x) break; if((--bit_offset) < 0) { bit_offset = 7; v = stbi__get8(s); @@ -5240,7 +5474,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; int z = 0; int easy=0; - stbi__skip(s, info.offset - 14 - info.hsz); + stbi__skip(s, info.offset - info.extra_read - info.hsz); if (info.bpp == 24) width = 3 * s->img_x; else if (info.bpp == 16) width = 2*s->img_x; else /* bpp = 32 and pad = 0 */ width=0; @@ -5258,6 +5492,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); + if (rcount > 8 || gcount > 8 || bcount > 8 || acount > 8) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } } for (j=0; j < (int) s->img_y; ++j) { if (easy) { @@ -5299,7 +5534,7 @@ static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req stbi_uc *p1 = out + j *s->img_x*target; stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; for (i=0; i < (int) s->img_x*target; ++i) { - t = p1[i], p1[i] = p2[i], p2[i] = t; + t = p1[i]; p1[i] = p2[i]; p2[i] = t; } } } @@ -5479,6 +5714,11 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req int RLE_repeating = 0; int read_next_pixel = 1; STBI_NOTUSED(ri); + STBI_NOTUSED(tga_x_origin); // @TODO + STBI_NOTUSED(tga_y_origin); // @TODO + + if (tga_height > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (tga_width > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); // do a tiny bit of precessing if ( tga_image_type >= 8 ) @@ -5519,6 +5759,11 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req // do I need to load a palette? if ( tga_indexed) { + if (tga_palette_len == 0) { /* you have to have at least one entry! */ + STBI_FREE(tga_data); + return stbi__errpuc("bad palette", "Corrupt TGA"); + } + // any data to skip? (offset usually = 0) stbi__skip(s, tga_palette_start ); // load the palette @@ -5642,6 +5887,7 @@ static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req // Microsoft's C compilers happy... [8^( tga_palette_start = tga_palette_len = tga_palette_bits = tga_x_origin = tga_y_origin = 0; + STBI_NOTUSED(tga_palette_start); // OK, done return tga_data; } @@ -5726,6 +5972,9 @@ static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req h = stbi__get32be(s); w = stbi__get32be(s); + if (h > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (w > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + // Make sure the depth is 8 bits. bitdepth = stbi__get16be(s); if (bitdepth != 8 && bitdepth != 16) @@ -5789,7 +6038,7 @@ static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req // Else if n is 128, noop. // Endloop - // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, + // The RLE-compressed data is preceded by a 2-byte data count for each row in the data, // which we're going to just skip. stbi__skip(s, h * channelCount * 2 ); @@ -6080,6 +6329,10 @@ static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_c x = stbi__get16be(s); y = stbi__get16be(s); + + if (y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); @@ -6127,7 +6380,7 @@ typedef struct int w,h; stbi_uc *out; // output buffer (always 4 components) stbi_uc *background; // The current "background" as far as a gif is concerned - stbi_uc *history; + stbi_uc *history; int flags, bgindex, ratio, transparent, eflags; stbi_uc pal[256][4]; stbi_uc lpal[256][4]; @@ -6188,6 +6441,9 @@ static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_in g->ratio = stbi__get8(s); g->transparent = -1; + if (g->w > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (g->h > STBI_MAX_DIMENSIONS) return stbi__err("too large","Very large image (corrupt?)"); + if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments if (is_info) return 1; @@ -6215,7 +6471,7 @@ static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) { stbi_uc *p, *c; - int idx; + int idx; // recurse to decode the prefixes, since the linked-list is backwards, // and working backwards through an interleaved image would be nasty @@ -6224,12 +6480,12 @@ static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) if (g->cur_y >= g->max_y) return; - idx = g->cur_x + g->cur_y; + idx = g->cur_x + g->cur_y; p = &g->out[idx]; - g->history[idx / 4] = 1; + g->history[idx / 4] = 1; c = &g->color_table[g->codes[code].suffix * 4]; - if (c[3] > 128) { // don't render transparent pixels; + if (c[3] > 128) { // don't render transparent pixels; p[0] = c[2]; p[1] = c[1]; p[2] = c[0]; @@ -6338,31 +6594,36 @@ static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) // two back is the image from two frames ago, used for a very specific disposal format static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) { - int dispose; - int first_frame; - int pi; - int pcount; + int dispose; + int first_frame; + int pi; + int pcount; + STBI_NOTUSED(req_comp); // on first frame, any non-written pixels get the background colour (non-transparent) - first_frame = 0; + first_frame = 0; if (g->out == 0) { - if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header - g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - g->background = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - g->history = (stbi_uc *) stbi__malloc(g->w * g->h); - if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); + if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header + if (!stbi__mad3sizes_valid(4, g->w, g->h, 0)) + return stbi__errpuc("too large", "GIF image is too large"); + pcount = g->w * g->h; + g->out = (stbi_uc *) stbi__malloc(4 * pcount); + g->background = (stbi_uc *) stbi__malloc(4 * pcount); + g->history = (stbi_uc *) stbi__malloc(pcount); + if (!g->out || !g->background || !g->history) + return stbi__errpuc("outofmem", "Out of memory"); - // image is treated as "tranparent" at the start - ie, nothing overwrites the current background; + // image is treated as "transparent" at the start - ie, nothing overwrites the current background; // background colour is only used for pixels that are not rendered first frame, after that "background" - // color refers to teh color that was there the previous frame. - memset( g->out, 0x00, 4 * g->w * g->h ); - memset( g->background, 0x00, 4 * g->w * g->h ); // state of the background (starts transparent) - memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame - first_frame = 1; + // color refers to the color that was there the previous frame. + memset(g->out, 0x00, 4 * pcount); + memset(g->background, 0x00, 4 * pcount); // state of the background (starts transparent) + memset(g->history, 0x00, pcount); // pixels that were affected previous frame + first_frame = 1; } else { - // second frame - how do we dispoase of the previous one? - dispose = (g->eflags & 0x1C) >> 2; - pcount = g->w * g->h; + // second frame - how do we dispose of the previous one? + dispose = (g->eflags & 0x1C) >> 2; + pcount = g->w * g->h; if ((dispose == 3) && (two_back == 0)) { dispose = 2; // if I don't have an image to revert back to, default to the old background @@ -6371,32 +6632,32 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i if (dispose == 3) { // use previous graphic for (pi = 0; pi < pcount; ++pi) { if (g->history[pi]) { - memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); + memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); } } - } else if (dispose == 2) { - // restore what was changed last frame to background before that frame; + } else if (dispose == 2) { + // restore what was changed last frame to background before that frame; for (pi = 0; pi < pcount; ++pi) { if (g->history[pi]) { - memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); + memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); } } } else { - // This is a non-disposal case eithe way, so just + // This is a non-disposal case eithe way, so just // leave the pixels as is, and they will become the new background // 1: do not dispose // 0: not specified. } - // background is what out is after the undoing of the previou frame; - memcpy( g->background, g->out, 4 * g->w * g->h ); + // background is what out is after the undoing of the previou frame; + memcpy( g->background, g->out, 4 * g->w * g->h ); } - // clear my history; + // clear my history; memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame for (;;) { - int tag = stbi__get8(s); + int tag = stbi__get8(s); switch (tag) { case 0x2C: /* Image Descriptor */ { @@ -6418,6 +6679,13 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i g->cur_x = g->start_x; g->cur_y = g->start_y; + // if the width of the specified rectangle is 0, that means + // we may not see *any* pixels or the image is malformed; + // to make sure this is caught, move the current y down to + // max_y (which is what out_gif_code checks). + if (w == 0) + g->cur_y = g->max_y; + g->lflags = stbi__get8(s); if (g->lflags & 0x40) { @@ -6434,19 +6702,19 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i } else if (g->flags & 0x80) { g->color_table = (stbi_uc *) g->pal; } else - return stbi__errpuc("missing color table", "Corrupt GIF"); - - o = stbi__process_gif_raster(s, g); - if (o == NULL) return NULL; + return stbi__errpuc("missing color table", "Corrupt GIF"); - // if this was the first frame, - pcount = g->w * g->h; + o = stbi__process_gif_raster(s, g); + if (!o) return NULL; + + // if this was the first frame, + pcount = g->w * g->h; if (first_frame && (g->bgindex > 0)) { // if first frame, any pixel not drawn to gets the background color for (pi = 0; pi < pcount; ++pi) { if (g->history[pi] == 0) { - g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; - memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); + g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; + memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); } } } @@ -6457,7 +6725,7 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i case 0x21: // Comment Extension. { int len; - int ext = stbi__get8(s); + int ext = stbi__get8(s); if (ext == 0xF9) { // Graphic Control Extension. len = stbi__get8(s); if (len == 4) { @@ -6466,23 +6734,23 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i // unset old transparent if (g->transparent >= 0) { - g->pal[g->transparent][3] = 255; - } + g->pal[g->transparent][3] = 255; + } if (g->eflags & 0x01) { g->transparent = stbi__get8(s); if (g->transparent >= 0) { - g->pal[g->transparent][3] = 0; + g->pal[g->transparent][3] = 0; } } else { // don't need transparent - stbi__skip(s, 1); - g->transparent = -1; + stbi__skip(s, 1); + g->transparent = -1; } } else { stbi__skip(s, len); break; } - } + } while ((len = stbi__get8(s)) != 0) { stbi__skip(s, len); } @@ -6501,15 +6769,17 @@ static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, i static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) { if (stbi__gif_test(s)) { - int layers = 0; + int layers = 0; stbi_uc *u = 0; stbi_uc *out = 0; - stbi_uc *two_back = 0; + stbi_uc *two_back = 0; stbi__gif g; - int stride; + int stride; + int out_size = 0; + int delays_size = 0; memset(&g, 0, sizeof(g)); if (delays) { - *delays = 0; + *delays = 0; } do { @@ -6519,44 +6789,58 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, if (u) { *x = g.w; *y = g.h; - ++layers; - stride = g.w * g.h * 4; - + ++layers; + stride = g.w * g.h * 4; + if (out) { - out = (stbi_uc*) STBI_REALLOC( out, layers * stride ); + void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); + if (NULL == tmp) { + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); + return stbi__errpuc("outofmem", "Out of memory"); + } + else { + out = (stbi_uc*) tmp; + out_size = layers * stride; + } + if (delays) { - *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); + *delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); + delays_size = layers * sizeof(int); } } else { - out = (stbi_uc*)stbi__malloc( layers * stride ); + out = (stbi_uc*)stbi__malloc( layers * stride ); + out_size = layers * stride; if (delays) { - *delays = (int*) stbi__malloc( layers * sizeof(int) ); + *delays = (int*) stbi__malloc( layers * sizeof(int) ); + delays_size = layers * sizeof(int); } } - memcpy( out + ((layers - 1) * stride), u, stride ); + memcpy( out + ((layers - 1) * stride), u, stride ); if (layers >= 2) { - two_back = out - 2 * stride; + two_back = out - 2 * stride; } if (delays) { - (*delays)[layers - 1U] = g.delay; + (*delays)[layers - 1U] = g.delay; } } - } while (u != 0); + } while (u != 0); - // free temp buffer; - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); + // free temp buffer; + STBI_FREE(g.out); + STBI_FREE(g.history); + STBI_FREE(g.background); - // do the final conversion after loading everything; + // do the final conversion after loading everything; if (req_comp && req_comp != 4) out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); - *z = layers; + *z = layers; return out; } else { - return stbi__errpuc("not GIF", "Image was not as a gif type."); + return stbi__errpuc("not GIF", "Image was not as a gif type."); } } @@ -6565,6 +6849,7 @@ static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req stbi_uc *u = 0; stbi__gif g; memset(&g, 0, sizeof(g)); + STBI_NOTUSED(ri); u = stbi__gif_load_next(s, &g, comp, req_comp, 0); if (u == (stbi_uc *) s) u = 0; // end of animated gif marker @@ -6573,14 +6858,17 @@ static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req *y = g.h; // moved conversion to after successful load so that the same - // can be done for multiple frames. + // can be done for multiple frames. if (req_comp && req_comp != 4) u = stbi__convert_format(u, 4, req_comp, g.w, g.h); + } else if (g.out) { + // if there was an error and we allocated an image buffer, free it! + STBI_FREE(g.out); } - // free buffers needed for multiple frame loading; + // free buffers needed for multiple frame loading; STBI_FREE(g.history); - STBI_FREE(g.background); + STBI_FREE(g.background); return u; } @@ -6705,6 +6993,9 @@ static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int re token += 3; width = (int) strtol(token, NULL, 10); + if (height > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + if (width > STBI_MAX_DIMENSIONS) return stbi__errpf("too large","Very large image (corrupt?)"); + *x = width; *y = height; @@ -6852,7 +7143,12 @@ static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) return 0; if (x) *x = s->img_x; if (y) *y = s->img_y; - if (comp) *comp = info.ma ? 4 : 3; + if (comp) { + if (info.bpp == 24 && info.ma == 0xff000000) + *comp = 3; + else + *comp = info.ma ? 4 : 3; + } return 1; } #endif @@ -7014,6 +7310,9 @@ static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) return 0; + if (s->img_y > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + if (s->img_x > STBI_MAX_DIMENSIONS) return stbi__errpuc("too large","Very large image (corrupt?)"); + *x = s->img_x; *y = s->img_y; if (comp) *comp = s->img_n; @@ -7238,6 +7537,7 @@ STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user /* revision history: + 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs 2.19 (2018-02-11) fix warning 2.18 (2018-01-30) fix warnings 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug From 116ebf6e10c39fdb2c88b31445b68d51fdfb5c5a Mon Sep 17 00:00:00 2001 From: Jason C Date: Wed, 5 May 2021 17:30:05 -0400 Subject: [PATCH 076/335] [3ds] Fix assertion failure when file could not be opened Check result of IOSystem::Open before constructing stream. Partially addresses #3888. --- code/AssetLib/3DS/3DSLoader.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/code/AssetLib/3DS/3DSLoader.cpp b/code/AssetLib/3DS/3DSLoader.cpp index a25355ccc..92fe72bbf 100644 --- a/code/AssetLib/3DS/3DSLoader.cpp +++ b/code/AssetLib/3DS/3DSLoader.cpp @@ -143,7 +143,13 @@ void Discreet3DSImporter::SetupProperties(const Importer * /*pImp*/) { // Imports the given file into the given scene structure. void Discreet3DSImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { - StreamReaderLE theStream(pIOHandler->Open(pFile, "rb")); + + auto theFile = pIOHandler->Open(pFile, "rb"); + if (!theFile) { + throw DeadlyImportError("3DS: Could not open ", pFile); + } + + StreamReaderLE theStream(theFile); // We should have at least one chunk if (theStream.GetRemainingSize() < 16) { From 7f13387487d9650f4212489b86e9a8be4beb2a0f Mon Sep 17 00:00:00 2001 From: Jason C Date: Wed, 5 May 2021 17:30:29 -0400 Subject: [PATCH 077/335] [cob] Fix assertion failure when file could not be opened. Check result of IOSystem::Open before constructing stream. Partially addresses #3888. --- code/AssetLib/COB/COBLoader.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/code/AssetLib/COB/COBLoader.cpp b/code/AssetLib/COB/COBLoader.cpp index efe7fc446..a6e9d4218 100644 --- a/code/AssetLib/COB/COBLoader.cpp +++ b/code/AssetLib/COB/COBLoader.cpp @@ -137,7 +137,13 @@ void COBImporter::SetupProperties(const Importer * /*pImp*/) { // Imports the given file into the given scene structure. void COBImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { COB::Scene scene; - std::unique_ptr stream(new StreamReaderLE(pIOHandler->Open(pFile, "rb"))); + + auto file = pIOHandler->Open(pFile, "rb"); + if (!file) { + ThrowException("Could not open " + pFile); + } + + std::unique_ptr stream(new StreamReaderLE(file)); // check header char head[32]; From 1cd3752ec6b64d584ebce84c0a6d0ae437a6e5c4 Mon Sep 17 00:00:00 2001 From: Jason C Date: Wed, 5 May 2021 17:31:06 -0400 Subject: [PATCH 078/335] [ms3d] Fix assertion failure when file could not be opened. Check result of IOSystem::Open before constructing stream. Partially addresses #3888. --- code/AssetLib/MS3D/MS3DLoader.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/code/AssetLib/MS3D/MS3DLoader.cpp b/code/AssetLib/MS3D/MS3DLoader.cpp index 192bcbe41..31cbca83b 100644 --- a/code/AssetLib/MS3D/MS3DLoader.cpp +++ b/code/AssetLib/MS3D/MS3DLoader.cpp @@ -215,7 +215,12 @@ void MS3DImporter :: CollectChildJoints(const std::vector& joints, ai void MS3DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { - StreamReaderLE stream(pIOHandler->Open(pFile,"rb")); + + auto file = pIOHandler->Open(pFile, "rb"); + if (!file) + throw DeadlyImportError("MS3D: Could not open ", pFile); + + StreamReaderLE stream(file); // CanRead() should have done this already char head[10]; From e52c2972841afc1806d0f0a7a528cbe4af4fbbf3 Mon Sep 17 00:00:00 2001 From: Jason C Date: Wed, 5 May 2021 17:31:24 -0400 Subject: [PATCH 079/335] [nendo] Fix assertion failure when file could not be opened. Check result of IOSystem::Open before constructing stream. Partially addresses #3888. --- code/AssetLib/NDO/NDOLoader.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/code/AssetLib/NDO/NDOLoader.cpp b/code/AssetLib/NDO/NDOLoader.cpp index 77fe3e36c..df3a9b15d 100644 --- a/code/AssetLib/NDO/NDOLoader.cpp +++ b/code/AssetLib/NDO/NDOLoader.cpp @@ -116,7 +116,13 @@ void NDOImporter::SetupProperties(const Importer* /*pImp*/) void NDOImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) { - StreamReaderBE reader(pIOHandler->Open( pFile, "rb")); + + auto file = pIOHandler->Open( pFile, "rb"); + if (!file) { + throw DeadlyImportError("Nendo: Could not open ", pFile); + } + + StreamReaderBE reader(file); // first 9 bytes are nendo file format ("nendo 1.n") const char* head = (const char*)reader.GetPtr(); From a80b3b25ebba38a04afc8a72ad46c66759b4a640 Mon Sep 17 00:00:00 2001 From: Jason C Date: Wed, 5 May 2021 17:31:50 -0400 Subject: [PATCH 080/335] [quick3d] Fix assertion failure when file could not be opened. Check result of IOSystem::Open before constructing stream. Partially addresses #3888. --- code/AssetLib/Q3D/Q3DLoader.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/code/AssetLib/Q3D/Q3DLoader.cpp b/code/AssetLib/Q3D/Q3DLoader.cpp index b52f86672..710dd52ac 100644 --- a/code/AssetLib/Q3D/Q3DLoader.cpp +++ b/code/AssetLib/Q3D/Q3DLoader.cpp @@ -106,7 +106,12 @@ const aiImporterDesc *Q3DImporter::GetInfo() const { // Imports the given file into the given scene structure. void Q3DImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { - StreamReaderLE stream(pIOHandler->Open(pFile, "rb")); + + auto file = pIOHandler->Open(pFile, "rb"); + if (!file) + throw DeadlyImportError("Quick3D: Could not open ", pFile); + + StreamReaderLE stream(file); // The header is 22 bytes large if (stream.GetRemainingSize() < 22) From 0d3e8b52be6f6c1465fc1ff885fa16f7b221b7d2 Mon Sep 17 00:00:00 2001 From: Jason C Date: Wed, 5 May 2021 17:32:10 -0400 Subject: [PATCH 081/335] [sib] Fix assertion failure when file could not be opened. Check result of IOSystem::Open before constructing stream. Partially addresses #3888. --- code/AssetLib/SIB/SIBImporter.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/code/AssetLib/SIB/SIBImporter.cpp b/code/AssetLib/SIB/SIBImporter.cpp index 6898fb65c..825f20ee2 100644 --- a/code/AssetLib/SIB/SIBImporter.cpp +++ b/code/AssetLib/SIB/SIBImporter.cpp @@ -804,7 +804,12 @@ static void ReadScene(SIB *sib, StreamReaderLE *stream) { // Imports the given file into the given scene structure. void SIBImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { - StreamReaderLE stream(pIOHandler->Open(pFile, "rb")); + + auto file = pIOHandler->Open(pFile, "rb"); + if (!file) + throw DeadlyImportError("SIB: Could not open ", pFile); + + StreamReaderLE stream(file); // We should have at least one chunk if (stream.GetRemainingSize() < 16) From 470913bf27e74a6fa6f6ef0f0d0fccd4ebd675de Mon Sep 17 00:00:00 2001 From: Jason C Date: Wed, 5 May 2021 17:46:24 -0400 Subject: [PATCH 082/335] [assbin] Fail if file could not be opened Fail instead of returning empty scene. Partially addresses #3888. --- code/AssetLib/Assbin/AssbinLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/Assbin/AssbinLoader.cpp b/code/AssetLib/Assbin/AssbinLoader.cpp index d94a9ed7d..d94407e03 100644 --- a/code/AssetLib/Assbin/AssbinLoader.cpp +++ b/code/AssetLib/Assbin/AssbinLoader.cpp @@ -671,7 +671,7 @@ void AssbinImporter::ReadBinaryScene(IOStream *stream, aiScene *scene) { void AssbinImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { IOStream *stream = pIOHandler->Open(pFile, "rb"); if (nullptr == stream) { - return; + throw DeadlyImportError("ASSBIN: Could not open ", pFile); } // signature From 98f586c8d443d46b14aa551baa00fbb03188903f Mon Sep 17 00:00:00 2001 From: Jason C Date: Wed, 5 May 2021 17:48:44 -0400 Subject: [PATCH 083/335] [irr] Fail if file could not be parsed. Fail instead of returning empty scene. Partially addresses #3888. TODO: Propagate XML error detail through exception (depends on #3881). --- code/AssetLib/Irr/IRRLoader.cpp | 4 ++-- code/AssetLib/Irr/IRRMeshLoader.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/code/AssetLib/Irr/IRRLoader.cpp b/code/AssetLib/Irr/IRRLoader.cpp index ed92c93bb..9ec0a9244 100644 --- a/code/AssetLib/Irr/IRRLoader.cpp +++ b/code/AssetLib/Irr/IRRLoader.cpp @@ -859,13 +859,13 @@ void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy // Check whether we can read from the file if (file.get() == nullptr) { - throw DeadlyImportError("Failed to open IRR file " + pFile); + throw DeadlyImportError("Failed to open IRR file ", pFile); } // Construct the irrXML parser XmlParser st; if (!st.parse( file.get() )) { - return; + throw DeadlyImportError("XML parse error while loading IRR file ", pFile); } pugi::xml_node rootElement = st.getRootNode(); diff --git a/code/AssetLib/Irr/IRRMeshLoader.cpp b/code/AssetLib/Irr/IRRMeshLoader.cpp index edcff6c83..9350e07b8 100644 --- a/code/AssetLib/Irr/IRRMeshLoader.cpp +++ b/code/AssetLib/Irr/IRRMeshLoader.cpp @@ -135,12 +135,12 @@ void IRRMeshImporter::InternReadFile(const std::string &pFile, // Check whether we can read from the file if (file.get() == NULL) - throw DeadlyImportError("Failed to open IRRMESH file " + pFile); + throw DeadlyImportError("Failed to open IRRMESH file ", pFile); // Construct the irrXML parser XmlParser parser; if (!parser.parse( file.get() )) { - return; + throw DeadlyImportError("XML parse error while loading IRRMESH file ", pFile); } XmlNode root = parser.getRootNode(); From de5c8ece6f53f5d22ac1c872780cd7518bbb2bb8 Mon Sep 17 00:00:00 2001 From: Jason C Date: Wed, 5 May 2021 17:49:10 -0400 Subject: [PATCH 084/335] [xgl] Fail if file could not be parsed. Fail instead of returning empty scene. Partially addresses #3888. TODO: Propagate XML error detail through exception (depends on #3881). --- code/AssetLib/XGL/XGLLoader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/XGL/XGLLoader.cpp b/code/AssetLib/XGL/XGLLoader.cpp index 00e8bafb2..3b84d7ba9 100644 --- a/code/AssetLib/XGL/XGLLoader.cpp +++ b/code/AssetLib/XGL/XGLLoader.cpp @@ -200,7 +200,7 @@ void XGLImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy // parse the XML file mXmlParser = new XmlParser; if (!mXmlParser->parse(stream.get())) { - return; + throw DeadlyImportError("XML parse error while loading XGL file ", pFile); } TempScope scope; From 7da9c42c81d9a15664c47e2281a84931bc36eb72 Mon Sep 17 00:00:00 2001 From: Jason C Date: Wed, 5 May 2021 19:30:29 -0400 Subject: [PATCH 085/335] [blender] Disable creation of "dna.txt" Developers who want to enable it can either: - *Temporarily* set ASSIMP_BUILD_BLENDER_DEBUG_DNA=1 in BlenderDNA.h, or - *Temporarily* define ASSIMP_BUILD_BLENDER_DEBUG_DNA=1 on the build command line. Addresses #3886. --- code/AssetLib/Blender/BlenderDNA.cpp | 6 +++--- code/AssetLib/Blender/BlenderDNA.h | 9 ++++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/code/AssetLib/Blender/BlenderDNA.cpp b/code/AssetLib/Blender/BlenderDNA.cpp index 4dcb3d654..c58041771 100644 --- a/code/AssetLib/Blender/BlenderDNA.cpp +++ b/code/AssetLib/Blender/BlenderDNA.cpp @@ -200,7 +200,7 @@ void DNAParser::Parse() { ASSIMP_LOG_DEBUG_F("BlenderDNA: Got ", dna.structures.size(), " structures with totally ", fields, " fields"); -#ifdef ASSIMP_BUILD_BLENDER_DEBUG +#if ASSIMP_BUILD_BLENDER_DEBUG_DNA dna.DumpToFile(); #endif @@ -208,7 +208,7 @@ void DNAParser::Parse() { dna.RegisterConverters(); } -#ifdef ASSIMP_BUILD_BLENDER_DEBUG +#if ASSIMP_BUILD_BLENDER_DEBUG_DNA #include // ------------------------------------------------------------------------------------------------ @@ -237,7 +237,7 @@ void DNA ::DumpToFile() { ASSIMP_LOG_INFO("BlenderDNA: Dumped dna to dna.txt"); } -#endif +#endif // ASSIMP_BUILD_BLENDER_DEBUG_DNA // ------------------------------------------------------------------------------------------------ /*static*/ void DNA ::ExtractArraySize( diff --git a/code/AssetLib/Blender/BlenderDNA.h b/code/AssetLib/Blender/BlenderDNA.h index 090d1be04..f566554b8 100644 --- a/code/AssetLib/Blender/BlenderDNA.h +++ b/code/AssetLib/Blender/BlenderDNA.h @@ -59,6 +59,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define ASSIMP_BUILD_BLENDER_DEBUG #endif +// set this to non-zero to dump BlenderDNA stuff to dna.txt. +// you could set it on the assimp build command line too without touching it here. +// !!! please make sure this is set to 0 in the repo !!! +#ifndef ASSIMP_BUILD_BLENDER_DEBUG_DNA +#define ASSIMP_BUILD_BLENDER_DEBUG_DNA 0 +#endif + // #define ASSIMP_BUILD_BLENDER_NO_STATS namespace Assimp { @@ -495,7 +502,7 @@ public: const Structure &structure, const FileDatabase &db) const; -#ifdef ASSIMP_BUILD_BLENDER_DEBUG +#if ASSIMP_BUILD_BLENDER_DEBUG_DNA // -------------------------------------------------------- /** Dump the DNA to a text file. This is for debugging purposes. * The output file is `dna.txt` in the current working folder*/ From 55056d11fd201484d141e4c80f1141c9cb1ac47c Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 6 May 2021 11:50:08 +0200 Subject: [PATCH 086/335] Add missing include --- include/assimp/XmlParser.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/assimp/XmlParser.h b/include/assimp/XmlParser.h index f525d3549..91fb9907f 100644 --- a/include/assimp/XmlParser.h +++ b/include/assimp/XmlParser.h @@ -43,8 +43,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define INCLUDED_AI_IRRXML_WRAPPER #include +#include + #include "BaseImporter.h" #include "IOStream.hpp" + #include #include From 52228a93f8215ec6bb4d89f313d66049546ed884 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 6 May 2021 21:07:38 +0200 Subject: [PATCH 087/335] Fix X3DGeohelper. --- code/AssetLib/X3D/X3DGeoHelper.cpp | 530 ++++++++++++++++++++++++++ code/AssetLib/X3D/X3DGeoHelper.h | 38 ++ code/AssetLib/X3D/X3DImporter.cpp | 179 ++++++++- code/AssetLib/X3D/X3DImporter.hpp | 101 ++++- code/AssetLib/glTF/glTFExporter.cpp | 9 +- code/AssetLib/glTF2/glTF2Exporter.cpp | 50 +-- code/CMakeLists.txt | 2 + code/Common/scene.cpp | 65 ++-- code/PostProcessing/ProcessHelper.h | 4 +- include/assimp/scene.h | 2 - 10 files changed, 895 insertions(+), 85 deletions(-) create mode 100644 code/AssetLib/X3D/X3DGeoHelper.cpp create mode 100644 code/AssetLib/X3D/X3DGeoHelper.h diff --git a/code/AssetLib/X3D/X3DGeoHelper.cpp b/code/AssetLib/X3D/X3DGeoHelper.cpp new file mode 100644 index 000000000..8a078d0f5 --- /dev/null +++ b/code/AssetLib/X3D/X3DGeoHelper.cpp @@ -0,0 +1,530 @@ +#include "X3DGeoHelper.h" +#include "X3DImporter.hpp" + +#include +#include + +#include + +namespace Assimp { + +aiVector3D X3DGeoHelper::make_point2D(float angle, float radius) { + return aiVector3D(radius * std::cos(angle), radius * std::sin(angle), 0); +} + +void X3DGeoHelper::make_arc2D(float pStartAngle, float pEndAngle, float pRadius, size_t numSegments, std::list &pVertices) { + // check argument values ranges. + if ((pStartAngle < -AI_MATH_TWO_PI_F) || (pStartAngle > AI_MATH_TWO_PI_F)) { + throw DeadlyImportError("GeometryHelper_Make_Arc2D.pStartAngle"); + } + if ((pEndAngle < -AI_MATH_TWO_PI_F) || (pEndAngle > AI_MATH_TWO_PI_F)) { + throw DeadlyImportError("GeometryHelper_Make_Arc2D.pEndAngle"); + } + if (pRadius <= 0) { + throw DeadlyImportError("GeometryHelper_Make_Arc2D.pRadius"); + } + + // calculate arc angle and check type of arc + float angle_full = std::fabs(pEndAngle - pStartAngle); + if ((angle_full > AI_MATH_TWO_PI_F) || (angle_full == 0.0f)) { + angle_full = AI_MATH_TWO_PI_F; + } + + // calculate angle for one step - angle to next point of line. + float angle_step = angle_full / (float)numSegments; + // make points + for (size_t pi = 0; pi <= numSegments; pi++) { + float tangle = pStartAngle + pi * angle_step; + pVertices.emplace_back(make_point2D(tangle, pRadius)); + } // for(size_t pi = 0; pi <= pNumSegments; pi++) + + // if we making full circle then add last vertex equal to first vertex + if (angle_full == AI_MATH_TWO_PI_F) pVertices.push_back(*pVertices.begin()); +} + +void X3DGeoHelper::extend_point_to_line(const std::list &pPoint, std::list &pLine) { + std::list::const_iterator pit = pPoint.begin(); + std::list::const_iterator pit_last = pPoint.end(); + + --pit_last; + + if (pPoint.size() < 2) { + throw DeadlyImportError("GeometryHelper_Extend_PointToLine.pPoint.size() can not be less than 2."); + } + + // add first point of first line. + pLine.push_back(*pit++); + // add internal points + while (pit != pit_last) { + pLine.push_back(*pit); // second point of previous line + pLine.push_back(*pit); // first point of next line + ++pit; + } + // add last point of last line + pLine.push_back(*pit); +} + +void X3DGeoHelper::polylineIdx_to_lineIdx(const std::list &pPolylineCoordIdx, std::list &pLineCoordIdx) { + std::list::const_iterator plit = pPolylineCoordIdx.begin(); + + while (plit != pPolylineCoordIdx.end()) { + // add first point of polyline + pLineCoordIdx.push_back(*plit++); + while ((*plit != (-1)) && (plit != pPolylineCoordIdx.end())) { + std::list::const_iterator plit_next; + + plit_next = plit, ++plit_next; + pLineCoordIdx.push_back(*plit); // second point of previous line. + pLineCoordIdx.push_back(-1); // delimiter + if ((*plit_next == (-1)) || (plit_next == pPolylineCoordIdx.end())) break; // current polyline is finished + + pLineCoordIdx.push_back(*plit); // first point of next line. + plit = plit_next; + } // while((*plit != (-1)) && (plit != pPolylineCoordIdx.end())) + } // while(plit != pPolylineCoordIdx.end()) +} + +#define MACRO_FACE_ADD_QUAD_FA(pCCW, pOut, pIn, pP1, pP2, pP3, pP4) \ + do { \ + if (pCCW) { \ + pOut.push_back(pIn[pP1]); \ + pOut.push_back(pIn[pP2]); \ + pOut.push_back(pIn[pP3]); \ + pOut.push_back(pIn[pP4]); \ + } else { \ + pOut.push_back(pIn[pP4]); \ + pOut.push_back(pIn[pP3]); \ + pOut.push_back(pIn[pP2]); \ + pOut.push_back(pIn[pP1]); \ + } \ + } while (false) + +#define MESH_RectParallelepiped_CREATE_VERT \ + aiVector3D vert_set[8]; \ + float x1, x2, y1, y2, z1, z2, hs; \ + \ + hs = pSize.x / 2, x1 = -hs, x2 = hs; \ + hs = pSize.y / 2, y1 = -hs, y2 = hs; \ + hs = pSize.z / 2, z1 = -hs, z2 = hs; \ + vert_set[0].Set(x2, y1, z2); \ + vert_set[1].Set(x2, y2, z2); \ + vert_set[2].Set(x2, y2, z1); \ + vert_set[3].Set(x2, y1, z1); \ + vert_set[4].Set(x1, y1, z2); \ + vert_set[5].Set(x1, y2, z2); \ + vert_set[6].Set(x1, y2, z1); \ + vert_set[7].Set(x1, y1, z1) + +void X3DGeoHelper::rect_parallele_piped(const aiVector3D &pSize, std::list &pVertices) { + MESH_RectParallelepiped_CREATE_VERT; + MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 3, 2, 1, 0); // front + MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 6, 7, 4, 5); // back + MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 7, 3, 0, 4); // left + MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 2, 6, 5, 1); // right + MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 0, 1, 5, 4); // top + MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 7, 6, 2, 3); // bottom +} + +#undef MESH_RectParallelepiped_CREATE_VERT + +void X3DGeoHelper::coordIdx_str2faces_arr(const std::vector &pCoordIdx, std::vector &pFaces, unsigned int &pPrimitiveTypes) { + std::vector f_data(pCoordIdx); + std::vector inds; + unsigned int prim_type = 0; + + if (f_data.back() != (-1)) { + f_data.push_back(-1); + } + + // reserve average size. + pFaces.reserve(f_data.size() / 3); + inds.reserve(4); + //PrintVectorSet("build. ci", pCoordIdx); + for (std::vector::iterator it = f_data.begin(); it != f_data.end(); ++it) { + // when face is got count how many indices in it. + if (*it == (-1)) { + aiFace tface; + size_t ts; + + ts = inds.size(); + switch (ts) { + case 0: + goto mg_m_err; + case 1: + prim_type |= aiPrimitiveType_POINT; + break; + case 2: + prim_type |= aiPrimitiveType_LINE; + break; + case 3: + prim_type |= aiPrimitiveType_TRIANGLE; + break; + default: + prim_type |= aiPrimitiveType_POLYGON; + break; + } + + tface.mNumIndices = static_cast(ts); + tface.mIndices = new unsigned int[ts]; + memcpy(tface.mIndices, inds.data(), ts * sizeof(unsigned int)); + pFaces.push_back(tface); + inds.clear(); + } // if(*it == (-1)) + else { + inds.push_back(*it); + } // if(*it == (-1)) else + } // for(std::list::iterator it = f_data.begin(); it != f_data.end(); it++) + //PrintVectorSet("build. faces", pCoordIdx); + + pPrimitiveTypes = prim_type; + + return; + +mg_m_err: + for (size_t i = 0, i_e = pFaces.size(); i < i_e; i++) + delete[] pFaces.at(i).mIndices; + + pFaces.clear(); +} + +void X3DGeoHelper::add_color(aiMesh &pMesh, const std::list &pColors, const bool pColorPerVertex) { + std::list tcol; + + // create RGBA array from RGB. + for (std::list::const_iterator it = pColors.begin(); it != pColors.end(); ++it) + tcol.push_back(aiColor4D((*it).r, (*it).g, (*it).b, 1)); + + // call existing function for adding RGBA colors + add_color(pMesh, tcol, pColorPerVertex); +} + +void X3DGeoHelper::add_color(aiMesh &pMesh, const std::list &pColors, const bool pColorPerVertex) { + std::list::const_iterator col_it = pColors.begin(); + + if (pColorPerVertex) { + if (pColors.size() < pMesh.mNumVertices) { + throw DeadlyImportError("MeshGeometry_AddColor1. Colors count(" + to_string(pColors.size()) + ") can not be less than Vertices count(" + + to_string(pMesh.mNumVertices) + ")."); + } + + // copy colors to mesh + pMesh.mColors[0] = new aiColor4D[pMesh.mNumVertices]; + for (size_t i = 0; i < pMesh.mNumVertices; i++) + pMesh.mColors[0][i] = *col_it++; + } // if(pColorPerVertex) + else { + if (pColors.size() < pMesh.mNumFaces) { + throw DeadlyImportError("MeshGeometry_AddColor1. Colors count(" + to_string(pColors.size()) + ") can not be less than Faces count(" + + to_string(pMesh.mNumFaces) + ")."); + } + + // copy colors to mesh + pMesh.mColors[0] = new aiColor4D[pMesh.mNumVertices]; + for (size_t fi = 0; fi < pMesh.mNumFaces; fi++) { + // apply color to all vertices of face + for (size_t vi = 0, vi_e = pMesh.mFaces[fi].mNumIndices; vi < vi_e; vi++) { + pMesh.mColors[0][pMesh.mFaces[fi].mIndices[vi]] = *col_it; + } + + ++col_it; + } + } // if(pColorPerVertex) else +} + +void X3DGeoHelper::add_color(aiMesh &pMesh, const std::vector &pCoordIdx, const std::vector &pColorIdx, + const std::list &pColors, const bool pColorPerVertex) { + std::list tcol; + + // create RGBA array from RGB. + for (std::list::const_iterator it = pColors.begin(); it != pColors.end(); ++it) { + tcol.push_back(aiColor4D((*it).r, (*it).g, (*it).b, 1)); + } + + // call existing function for adding RGBA colors + add_color(pMesh, pCoordIdx, pColorIdx, tcol, pColorPerVertex); +} + +void X3DGeoHelper::add_color(aiMesh &pMesh, const std::vector &coordIdx, const std::vector &colorIdx, + const std::list &colors, bool pColorPerVertex) { + std::vector col_tgt_arr; + std::list col_tgt_list; + std::vector col_arr_copy; + + if (coordIdx.size() == 0) { + throw DeadlyImportError("MeshGeometry_AddColor2. pCoordIdx can not be empty."); + } + + // copy list to array because we are need indexed access to colors. + col_arr_copy.reserve(colors.size()); + for (std::list::const_iterator it = colors.begin(); it != colors.end(); ++it) { + col_arr_copy.push_back(*it); + } + + if (pColorPerVertex) { + if (colorIdx.size() > 0) { + // check indices array count. + if (colorIdx.size() < coordIdx.size()) { + throw DeadlyImportError("MeshGeometry_AddColor2. Colors indices count(" + to_string(colorIdx.size()) + + ") can not be less than Coords inidces count(" + to_string(coordIdx.size()) + ")."); + } + // create list with colors for every vertex. + col_tgt_arr.resize(pMesh.mNumVertices); + for (std::vector::const_iterator colidx_it = colorIdx.begin(), coordidx_it = coordIdx.begin(); colidx_it != colorIdx.end(); ++colidx_it, ++coordidx_it) { + if (*colidx_it == (-1)) { + continue; // skip faces delimiter + } + if ((unsigned int)(*coordidx_it) > pMesh.mNumVertices) { + throw DeadlyImportError("MeshGeometry_AddColor2. Coordinate idx is out of range."); + } + if ((unsigned int)*colidx_it > pMesh.mNumVertices) { + throw DeadlyImportError("MeshGeometry_AddColor2. Color idx is out of range."); + } + + col_tgt_arr[*coordidx_it] = col_arr_copy[*colidx_it]; + } + } // if(pColorIdx.size() > 0) + else { + // when color indices list is absent use CoordIdx. + // check indices array count. + if (colors.size() < pMesh.mNumVertices) { + throw DeadlyImportError("MeshGeometry_AddColor2. Colors count(" + to_string(colors.size()) + ") can not be less than Vertices count(" + + to_string(pMesh.mNumVertices) + ")."); + } + // create list with colors for every vertex. + col_tgt_arr.resize(pMesh.mNumVertices); + for (size_t i = 0; i < pMesh.mNumVertices; i++) { + col_tgt_arr[i] = col_arr_copy[i]; + } + } // if(pColorIdx.size() > 0) else + } // if(pColorPerVertex) + else { + if (colorIdx.size() > 0) { + // check indices array count. + if (colorIdx.size() < pMesh.mNumFaces) { + throw DeadlyImportError("MeshGeometry_AddColor2. Colors indices count(" + to_string(colorIdx.size()) + + ") can not be less than Faces count(" + to_string(pMesh.mNumFaces) + ")."); + } + // create list with colors for every vertex using faces indices. + col_tgt_arr.resize(pMesh.mNumFaces); + + std::vector::const_iterator colidx_it = colorIdx.begin(); + for (size_t fi = 0; fi < pMesh.mNumFaces; fi++) { + if ((unsigned int)*colidx_it > pMesh.mNumFaces) throw DeadlyImportError("MeshGeometry_AddColor2. Face idx is out of range."); + + col_tgt_arr[fi] = col_arr_copy[*colidx_it++]; + } + } // if(pColorIdx.size() > 0) + else { + // when color indices list is absent use CoordIdx. + // check indices array count. + if (colors.size() < pMesh.mNumFaces) { + throw DeadlyImportError("MeshGeometry_AddColor2. Colors count(" + to_string(colors.size()) + ") can not be less than Faces count(" + + to_string(pMesh.mNumFaces) + ")."); + } + // create list with colors for every vertex using faces indices. + col_tgt_arr.resize(pMesh.mNumFaces); + for (size_t fi = 0; fi < pMesh.mNumFaces; fi++) + col_tgt_arr[fi] = col_arr_copy[fi]; + + } // if(pColorIdx.size() > 0) else + } // if(pColorPerVertex) else + + // copy array to list for calling function that add colors. + for (std::vector::const_iterator it = col_tgt_arr.begin(); it != col_tgt_arr.end(); ++it) + col_tgt_list.push_back(*it); + // add prepared colors list to mesh. + add_color(pMesh, col_tgt_list, pColorPerVertex); +} + +void X3DGeoHelper::add_normal(aiMesh &pMesh, const std::vector &pCoordIdx, const std::vector &pNormalIdx, + const std::list &pNormals, const bool pNormalPerVertex) { + std::vector tind; + std::vector norm_arr_copy; + + // copy list to array because we are need indexed access to normals. + norm_arr_copy.reserve(pNormals.size()); + for (std::list::const_iterator it = pNormals.begin(); it != pNormals.end(); ++it) { + norm_arr_copy.push_back(*it); + } + + if (pNormalPerVertex) { + if (pNormalIdx.size() > 0) { + // check indices array count. + if (pNormalIdx.size() != pCoordIdx.size()) throw DeadlyImportError("Normals and Coords inidces count must be equal."); + + tind.reserve(pNormalIdx.size()); + for (std::vector::const_iterator it = pNormalIdx.begin(); it != pNormalIdx.end(); ++it) { + if (*it != (-1)) tind.push_back(*it); + } + + // copy normals to mesh + pMesh.mNormals = new aiVector3D[pMesh.mNumVertices]; + for (size_t i = 0; (i < pMesh.mNumVertices) && (i < tind.size()); i++) { + if (tind[i] >= norm_arr_copy.size()) + throw DeadlyImportError("MeshGeometry_AddNormal. Normal index(" + to_string(tind[i]) + + ") is out of range. Normals count: " + to_string(norm_arr_copy.size()) + "."); + + pMesh.mNormals[i] = norm_arr_copy[tind[i]]; + } + } else { + if (pNormals.size() != pMesh.mNumVertices) throw DeadlyImportError("MeshGeometry_AddNormal. Normals and vertices count must be equal."); + + // copy normals to mesh + pMesh.mNormals = new aiVector3D[pMesh.mNumVertices]; + std::list::const_iterator norm_it = pNormals.begin(); + for (size_t i = 0; i < pMesh.mNumVertices; i++) + pMesh.mNormals[i] = *norm_it++; + } + } // if(pNormalPerVertex) + else { + if (pNormalIdx.size() > 0) { + if (pMesh.mNumFaces != pNormalIdx.size()) throw DeadlyImportError("Normals faces count must be equal to mesh faces count."); + + std::vector::const_iterator normidx_it = pNormalIdx.begin(); + + tind.reserve(pNormalIdx.size()); + for (size_t i = 0, i_e = pNormalIdx.size(); i < i_e; i++) + tind.push_back(*normidx_it++); + + } else { + tind.reserve(pMesh.mNumFaces); + for (size_t i = 0; i < pMesh.mNumFaces; i++) + tind.push_back(i); + } + + // copy normals to mesh + pMesh.mNormals = new aiVector3D[pMesh.mNumVertices]; + for (size_t fi = 0; fi < pMesh.mNumFaces; fi++) { + aiVector3D tnorm; + + tnorm = norm_arr_copy[tind[fi]]; + for (size_t vi = 0, vi_e = pMesh.mFaces[fi].mNumIndices; vi < vi_e; vi++) + pMesh.mNormals[pMesh.mFaces[fi].mIndices[vi]] = tnorm; + } + } // if(pNormalPerVertex) else +} + +void X3DGeoHelper::add_normal(aiMesh &pMesh, const std::list &pNormals, const bool pNormalPerVertex) { + std::list::const_iterator norm_it = pNormals.begin(); + + if (pNormalPerVertex) { + if (pNormals.size() != pMesh.mNumVertices) throw DeadlyImportError("MeshGeometry_AddNormal. Normals and vertices count must be equal."); + + // copy normals to mesh + pMesh.mNormals = new aiVector3D[pMesh.mNumVertices]; + for (size_t i = 0; i < pMesh.mNumVertices; i++) + pMesh.mNormals[i] = *norm_it++; + } // if(pNormalPerVertex) + else { + if (pNormals.size() != pMesh.mNumFaces) throw DeadlyImportError("MeshGeometry_AddNormal. Normals and faces count must be equal."); + + // copy normals to mesh + pMesh.mNormals = new aiVector3D[pMesh.mNumVertices]; + for (size_t fi = 0; fi < pMesh.mNumFaces; fi++) { + // apply color to all vertices of face + for (size_t vi = 0, vi_e = pMesh.mFaces[fi].mNumIndices; vi < vi_e; vi++) + pMesh.mNormals[pMesh.mFaces[fi].mIndices[vi]] = *norm_it; + + ++norm_it; + } + } // if(pNormalPerVertex) else +} + +void X3DGeoHelper::add_tex_coord(aiMesh &pMesh, const std::vector &pCoordIdx, const std::vector &pTexCoordIdx, + const std::list &pTexCoords) { + std::vector texcoord_arr_copy; + std::vector faces; + unsigned int prim_type; + + // copy list to array because we are need indexed access to normals. + texcoord_arr_copy.reserve(pTexCoords.size()); + for (std::list::const_iterator it = pTexCoords.begin(); it != pTexCoords.end(); ++it) { + texcoord_arr_copy.push_back(aiVector3D((*it).x, (*it).y, 0)); + } + + if (pTexCoordIdx.size() > 0) { + coordIdx_str2faces_arr(pTexCoordIdx, faces, prim_type); + if (faces.empty()) { + throw DeadlyImportError("Failed to add texture coordinates to mesh, faces list is empty."); + } + if (faces.size() != pMesh.mNumFaces) { + throw DeadlyImportError("Texture coordinates faces count must be equal to mesh faces count."); + } + } else { + coordIdx_str2faces_arr(pCoordIdx, faces, prim_type); + } + + pMesh.mTextureCoords[0] = new aiVector3D[pMesh.mNumVertices]; + pMesh.mNumUVComponents[0] = 2; + for (size_t fi = 0, fi_e = faces.size(); fi < fi_e; fi++) { + if (pMesh.mFaces[fi].mNumIndices != faces.at(fi).mNumIndices) + throw DeadlyImportError("Number of indices in texture face and mesh face must be equal. Invalid face index: " + to_string(fi) + "."); + + for (size_t ii = 0; ii < pMesh.mFaces[fi].mNumIndices; ii++) { + size_t vert_idx = pMesh.mFaces[fi].mIndices[ii]; + size_t tc_idx = faces.at(fi).mIndices[ii]; + + pMesh.mTextureCoords[0][vert_idx] = texcoord_arr_copy.at(tc_idx); + } + } // for(size_t fi = 0, fi_e = faces.size(); fi < fi_e; fi++) +} + +void X3DGeoHelper::add_tex_coord(aiMesh &pMesh, const std::list &pTexCoords) { + std::vector tc_arr_copy; + + if (pTexCoords.size() != pMesh.mNumVertices) { + throw DeadlyImportError("MeshGeometry_AddTexCoord. Texture coordinates and vertices count must be equal."); + } + + // copy list to array because we are need convert aiVector2D to aiVector3D and also get indexed access as a bonus. + tc_arr_copy.reserve(pTexCoords.size()); + for (std::list::const_iterator it = pTexCoords.begin(); it != pTexCoords.end(); ++it) { + tc_arr_copy.push_back(aiVector3D((*it).x, (*it).y, 0)); + } + + // copy texture coordinates to mesh + pMesh.mTextureCoords[0] = new aiVector3D[pMesh.mNumVertices]; + pMesh.mNumUVComponents[0] = 2; + for (size_t i = 0; i < pMesh.mNumVertices; i++) { + pMesh.mTextureCoords[0][i] = tc_arr_copy[i]; + } +} + +aiMesh *X3DGeoHelper::make_mesh(const std::vector &pCoordIdx, const std::list &pVertices) { + std::vector faces; + unsigned int prim_type = 0; + + // create faces array from input string with vertices indices. + X3DGeoHelper::coordIdx_str2faces_arr(pCoordIdx, faces, prim_type); + if (!faces.size()) { + throw DeadlyImportError("Failed to create mesh, faces list is empty."); + } + + // + // Create new mesh and copy geometry data. + // + aiMesh *tmesh = new aiMesh; + size_t ts = faces.size(); + // faces + tmesh->mFaces = new aiFace[ts]; + tmesh->mNumFaces = static_cast(ts); + for (size_t i = 0; i < ts; i++) + tmesh->mFaces[i] = faces.at(i); + + // vertices + std::list::const_iterator vit = pVertices.begin(); + + ts = pVertices.size(); + tmesh->mVertices = new aiVector3D[ts]; + tmesh->mNumVertices = static_cast(ts); + for (size_t i = 0; i < ts; i++) { + tmesh->mVertices[i] = *vit++; + } + + // set primitives type and return result. + tmesh->mPrimitiveTypes = prim_type; + + return tmesh; +} + +} // namespace Assimp diff --git a/code/AssetLib/X3D/X3DGeoHelper.h b/code/AssetLib/X3D/X3DGeoHelper.h new file mode 100644 index 000000000..38b6de4dc --- /dev/null +++ b/code/AssetLib/X3D/X3DGeoHelper.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#include + +#include +#include + +struct aiFace; +struct aiMesh; + +namespace Assimp { + +class X3DGeoHelper { +public: + static aiVector3D make_point2D(float angle, float radius); + static void make_arc2D(float pStartAngle, float pEndAngle, float pRadius, size_t numSegments, std::list &pVertices); + static void extend_point_to_line(const std::list &pPoint, std::list &pLine); + static void polylineIdx_to_lineIdx(const std::list &pPolylineCoordIdx, std::list &pLineCoordIdx); + static void rect_parallele_piped(const aiVector3D &pSize, std::list &pVertices); + static void coordIdx_str2faces_arr(const std::vector &pCoordIdx, std::vector &pFaces, unsigned int &pPrimitiveTypes); + static void add_color(aiMesh &pMesh, const std::list &pColors, const bool pColorPerVertex); + static void add_color(aiMesh &pMesh, const std::list &pColors, const bool pColorPerVertex); + static void add_color(aiMesh &pMesh, const std::vector &pCoordIdx, const std::vector &pColorIdx, + const std::list &pColors, const bool pColorPerVertex); + static void add_color(aiMesh &pMesh, const std::vector &pCoordIdx, const std::vector &pColorIdx, + const std::list &pColors, const bool pColorPerVertex); + static void add_normal(aiMesh &pMesh, const std::vector &pCoordIdx, const std::vector &pNormalIdx, + const std::list &pNormals, const bool pNormalPerVertex); + static void add_normal(aiMesh &pMesh, const std::list &pNormals, const bool pNormalPerVertex); + static void add_tex_coord(aiMesh &pMesh, const std::vector &pCoordIdx, const std::vector &pTexCoordIdx, + const std::list &pTexCoords); + static void add_tex_coord(aiMesh &pMesh, const std::list &pTexCoords); + static aiMesh *make_mesh(const std::vector &pCoordIdx, const std::list &pVertices); +}; + +} // namespace Assimp diff --git a/code/AssetLib/X3D/X3DImporter.cpp b/code/AssetLib/X3D/X3DImporter.cpp index ff6e4f40e..341cc9d99 100644 --- a/code/AssetLib/X3D/X3DImporter.cpp +++ b/code/AssetLib/X3D/X3DImporter.cpp @@ -46,16 +46,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER #include "X3DImporter.hpp" -#include -// Header files, Assimp. +#include +#include #include #include // Header files, stdlib. #include #include -#include namespace Assimp { @@ -126,7 +125,8 @@ struct WordIterator { const char *WordIterator::whitespace = ", \t\r\n"; X3DImporter::X3DImporter() : - mNodeElementCur(nullptr) { + mNodeElementCur(nullptr), + mScene(nullptr) { // empty } @@ -153,10 +153,29 @@ void X3DImporter::ParseFile(const std::string &file, IOSystem *pIOHandler) { std::unique_ptr fileStream(pIOHandler->Open(file, mode)); if (!fileStream.get()) { throw DeadlyImportError("Failed to open file " + file + "."); - } + } + + XmlParser theParser; + if (!theParser.parse(fileStream.get())) { + return; + } + + XmlNode *node = theParser.findNode("X3D"); + if (nullptr == node) { + return; + } + + for (auto ¤tNode : node->children()) { + const std::string ¤tName = currentNode.name(); + if (currentName == "head") { + readMetadata(currentNode); + } else if (currentName == "Scene") { + readScene(currentNode); + } + } } -bool X3DImporter::CanRead( const std::string &pFile, IOSystem * /*pIOHandler*/, bool checkSig ) const { +bool X3DImporter::CanRead(const std::string &pFile, IOSystem * /*pIOHandler*/, bool checkSig) const { if (checkSig) { std::string::size_type pos = pFile.find_last_of(".x3d"); if (pos != std::string::npos) { @@ -167,16 +186,17 @@ bool X3DImporter::CanRead( const std::string &pFile, IOSystem * /*pIOHandler*/, return false; } -void X3DImporter::GetExtensionList( std::set &extensionList ) { +void X3DImporter::GetExtensionList(std::set &extensionList) { extensionList.insert("x3d"); } -void X3DImporter::InternReadFile( const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler ) { +void X3DImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { std::shared_ptr stream(pIOHandler->Open(pFile, "rb")); if (!stream) { throw DeadlyImportError("Could not open file for reading"); } + mScene = pScene; pScene->mRootNode = new aiNode(pFile); } @@ -184,6 +204,147 @@ const aiImporterDesc *X3DImporter::GetInfo() const { return &Description; } -} +struct meta_entry { + std::string name; + std::string value; +}; + +void X3DImporter::readMetadata(XmlNode &node) { + std::vector metaArray; + for (auto currentNode : node.children()) { + const std::string ¤tName = currentNode.name(); + if (currentName == "meta") { + meta_entry entry; + if (XmlParser::getStdStrAttribute(currentNode, "name", entry.name)) { + XmlParser::getStdStrAttribute(currentNode, "content", entry.value); + metaArray.emplace_back(entry); + } + } + } + mScene->mMetaData = aiMetadata::Alloc(static_cast(metaArray.size())); + unsigned int i = 0; + for (auto currentMeta : metaArray) { + mScene->mMetaData->Set(i, currentMeta.name, currentMeta.value); + ++i; + } +} + +void X3DImporter::readScene(XmlNode &node) { + for (auto currentNode : node.children()) { + const std::string ¤tName = currentNode.name(); + if (currentName == "Viewpoint") { + readViewpoint(currentNode); + } + } +} + +void X3DImporter::readViewpoint(XmlNode &node) { + for (auto currentNode : node.children()) { + //const std::string ¤tName = currentNode.name(); + } +} + +void readMetadataBoolean(XmlNode &node, X3DNodeElementBase *parent) { + std::string val; + X3DNodeElementMetaBoolean *boolean = nullptr; + if (XmlParser::getStdStrAttribute(node, "value", val)) { + std::vector values; + tokenize(val, values, " "); + boolean = new X3DNodeElementMetaBoolean(parent); + for (size_t i = 0; i < values.size(); ++i) { + bool current_boolean = false; + if (values[i] == "true") { + current_boolean = true; + } + boolean->Value.emplace_back(current_boolean); + } + } +} + +void readMetadataDouble(XmlNode &node, X3DNodeElementBase *parent) { + std::string val; + X3DNodeElementMetaDouble *doubleNode = nullptr; + if (XmlParser::getStdStrAttribute(node, "value", val)) { + std::vector values; + tokenize(val, values, " "); + doubleNode = new X3DNodeElementMetaDouble(parent); + for (size_t i = 0; i < values.size(); ++i) { + double current_double = static_cast(fast_atof(values[i].c_str())); + doubleNode->Value.emplace_back(current_double); + } + } +} + +void readMetadataFloat(XmlNode &node, X3DNodeElementBase *parent) { + std::string val; + X3DNodeElementMetaFloat *floatNode = nullptr; + if (XmlParser::getStdStrAttribute(node, "value", val)) { + std::vector values; + tokenize(val, values, " "); + floatNode = new X3DNodeElementMetaFloat(parent); + for (size_t i = 0; i < values.size(); ++i) { + float current_float = static_cast(fast_atof(values[i].c_str())); + floatNode->Value.emplace_back(current_float); + } + } +} + +void readMetadataInteger(XmlNode &node, X3DNodeElementBase *parent) { + std::string val; + X3DNodeElementMetaInt *intNode = nullptr; + if (XmlParser::getStdStrAttribute(node, "value", val)) { + std::vector values; + tokenize(val, values, " "); + intNode = new X3DNodeElementMetaInt(parent); + for (size_t i = 0; i < values.size(); ++i) { + int current_int = static_cast(std::atoi(values[i].c_str())); + intNode->Value.emplace_back(current_int); + } + } +} + +void readMetadataSet(XmlNode &node, X3DNodeElementBase *parent) { + std::string val; + X3DNodeElementMetaSet *setNode = new X3DNodeElementMetaSet(parent); + if (XmlParser::getStdStrAttribute(node, "name", val)) { + setNode->Name = val; + } + + if (XmlParser::getStdStrAttribute(node, "reference", val)) { + setNode->Reference = val; + } +} + +void readMetadataString(XmlNode &node, X3DNodeElementBase *parent) { + std::string val; + X3DNodeElementMetaString *strNode = nullptr; + if (XmlParser::getStdStrAttribute(node, "value", val)) { + std::vector values; + tokenize(val, values, " "); + strNode = new X3DNodeElementMetaString(parent); + for (size_t i = 0; i < values.size(); ++i) { + strNode->Value.emplace_back(values[i]); + } + } +} + +void X3DImporter::readMetadataObject(XmlNode &node) { + const std::string &name = node.name(); + if (name == "MetadataBoolean") { + readMetadataBoolean(node, mNodeElementCur); + } else if (name == "MetadataDouble") { + readMetadataDouble(node, mNodeElementCur); + } else if (name == "MetadataFloat") { + readMetadataFloat(node, mNodeElementCur); + } else if (name == "MetadataInteger") { + readMetadataInteger(node, mNodeElementCur); + } else if (name == "MetadataSet") { + readMetadataSet(node, mNodeElementCur); + } else if (name == "MetadataString") { + readMetadataString(node, mNodeElementCur); + } +} + +} // namespace Assimp #endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter.hpp b/code/AssetLib/X3D/X3DImporter.hpp index 4444bacd0..1b6410c36 100644 --- a/code/AssetLib/X3D/X3DImporter.hpp +++ b/code/AssetLib/X3D/X3DImporter.hpp @@ -38,16 +38,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ -/// \file X3DImporter.hpp -/// \brief X3D-format files importer for Assimp. -/// \date 2015-2016 -/// \author smal.root@gmail.com -// Thanks to acorn89 for support. - #ifndef INCLUDED_AI_X3D_IMPORTER_H #define INCLUDED_AI_X3D_IMPORTER_H -// Header files, Assimp. + #include #include #include @@ -56,7 +50,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include #include +#include namespace Assimp { @@ -282,8 +278,90 @@ enum class X3DElemType { struct X3DNodeElementBase { X3DNodeElementBase *Parent; std::string ID; - std::list Child; + std::list Children; X3DElemType Type; + +protected: + X3DNodeElementBase(X3DElemType type, X3DNodeElementBase *pParent) : + Type(type), Parent(pParent) { + // empty + } +}; + +struct CX3DNodeElementGroup : X3DNodeElementBase { + aiMatrix4x4 Transformation; ///< Transformation matrix. + bool Static; + bool UseChoice; ///< Flag: if true then use number from \ref Choice to choose what the child will be kept. + int32_t Choice; ///< Number of the child which will be kept. +}; + +struct X3DNodeElementMeta : X3DNodeElementBase { + std::string Name; ///< Name of metadata object. + std::string Reference; + + virtual ~X3DNodeElementMeta() { + // empty + } + +protected: + X3DNodeElementMeta(X3DElemType type, X3DNodeElementBase *parent) : + X3DNodeElementBase(type, parent) { + // empty + } +}; + +struct X3DNodeElementMetaBoolean : X3DNodeElementMeta { + std::vector Value; ///< Stored value. + + X3DNodeElementMetaBoolean(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaBoolean, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaDouble : X3DNodeElementMeta { + std::vector Value; ///< Stored value. + + X3DNodeElementMetaDouble(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaDouble, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaFloat : public X3DNodeElementMeta { + std::vector Value; ///< Stored value. + + X3DNodeElementMetaFloat(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaFloat, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaInt : public X3DNodeElementMeta { + std::vector Value; ///< Stored value. + + X3DNodeElementMetaInt(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaInteger, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaSet : public X3DNodeElementMeta { + std::list Value; ///< Stored value. + + X3DNodeElementMetaSet(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaSet, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaString : public X3DNodeElementMeta { + std::list Value; ///< Stored value. + + X3DNodeElementMetaString(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaString, pParent) { + // empty + } }; class X3DImporter : public BaseImporter { @@ -311,10 +389,15 @@ public: void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler); const aiImporterDesc *GetInfo() const; void Clear(); + void readMetadata(XmlNode &node); + void readScene(XmlNode &node); + void readViewpoint(XmlNode &node); + void readMetadataObject(XmlNode &node); private: static const aiImporterDesc Description; - X3DNodeElementBase *mNodeElementCur; ///< Current element. + X3DNodeElementBase *mNodeElementCur; + aiScene *mScene; }; // class X3DImporter } // namespace Assimp diff --git a/code/AssetLib/glTF/glTFExporter.cpp b/code/AssetLib/glTF/glTFExporter.cpp index 1951167c6..528604cd8 100644 --- a/code/AssetLib/glTF/glTFExporter.cpp +++ b/code/AssetLib/glTF/glTFExporter.cpp @@ -322,8 +322,8 @@ void glTFExporter::GetTexSampler(const aiMaterial* mat, glTF::TexProperty& prop) prop.texture->sampler->minFilter = SamplerMinFilter_Linear; } -void glTFExporter::GetMatColorOrTex(const aiMaterial* mat, glTF::TexProperty& prop, const char* propName, int type, int idx, aiTextureType tt) -{ +void glTFExporter::GetMatColorOrTex(const aiMaterial* mat, glTF::TexProperty& prop, + const char* propName, int type, int idx, aiTextureType tt) { aiString tex; aiColor4D col; if (mat->GetTextureCount(tt) > 0) { @@ -370,7 +370,10 @@ void glTFExporter::GetMatColorOrTex(const aiMaterial* mat, glTF::TexProperty& pr } if (mat->Get(propName, type, idx, col) == AI_SUCCESS) { - prop.color[0] = col.r; prop.color[1] = col.g; prop.color[2] = col.b; prop.color[3] = col.a; + prop.color[0] = col.r; + prop.color[1] = col.g; + prop.color[2] = col.b; + prop.color[3] = col.a; } } diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 565117ddb..8eaf3c169 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -1297,24 +1297,24 @@ void glTF2Exporter::ExportMetadata() } } -inline Ref GetSamplerInputRef(Asset& asset, std::string& animId, Ref& buffer, std::vector& times) -{ +inline Ref GetSamplerInputRef(Asset& asset, std::string& animId, + Ref& buffer, std::vector& times) { return ExportData(asset, animId, buffer, (unsigned int)times.size(), ×[0], AttribType::SCALAR, AttribType::SCALAR, ComponentType_FLOAT); } -inline void ExtractTranslationSampler(Asset& asset, std::string& animId, Ref& buffer, const aiNodeAnim* nodeChannel, float ticksPerSecond, Animation::Sampler& sampler) -{ +inline void ExtractTranslationSampler(Asset& asset, std::string& animId, Ref& buffer, + const aiNodeAnim* nodeChannel, float ticksPerSecond, Animation::Sampler& sampler) { const unsigned int numKeyframes = nodeChannel->mNumPositionKeys; - std::vector times(numKeyframes); - std::vector values(numKeyframes * 3); + std::vector times(numKeyframes); + std::vector values(numKeyframes * 3); for (unsigned int i = 0; i < numKeyframes; ++i) { const aiVectorKey& key = nodeChannel->mPositionKeys[i]; // mTime is measured in ticks, but GLTF time is measured in seconds, so convert. times[i] = static_cast(key.mTime / ticksPerSecond); - values[(i * 3) + 0] = key.mValue.x; - values[(i * 3) + 1] = key.mValue.y; - values[(i * 3) + 2] = key.mValue.z; + values[(i * 3) + 0] = (ai_real) key.mValue.x; + values[(i * 3) + 1] = (ai_real) key.mValue.y; + values[(i * 3) + 2] = (ai_real) key.mValue.z; } sampler.input = GetSamplerInputRef(asset, animId, buffer, times); @@ -1322,19 +1322,19 @@ inline void ExtractTranslationSampler(Asset& asset, std::string& animId, Ref& buffer, const aiNodeAnim* nodeChannel, float ticksPerSecond, Animation::Sampler& sampler) -{ +inline void ExtractScaleSampler(Asset& asset, std::string& animId, Ref& buffer, + const aiNodeAnim* nodeChannel, float ticksPerSecond, Animation::Sampler& sampler) { const unsigned int numKeyframes = nodeChannel->mNumScalingKeys; - std::vector times(numKeyframes); - std::vector values(numKeyframes * 3); + std::vector times(numKeyframes); + std::vector values(numKeyframes * 3); for (unsigned int i = 0; i < numKeyframes; ++i) { const aiVectorKey& key = nodeChannel->mScalingKeys[i]; // mTime is measured in ticks, but GLTF time is measured in seconds, so convert. times[i] = static_cast(key.mTime / ticksPerSecond); - values[(i * 3) + 0] = key.mValue.x; - values[(i * 3) + 1] = key.mValue.y; - values[(i * 3) + 2] = key.mValue.z; + values[(i * 3) + 0] = (ai_real) key.mValue.x; + values[(i * 3) + 1] = (ai_real) key.mValue.y; + values[(i * 3) + 2] = (ai_real) key.mValue.z; } sampler.input = GetSamplerInputRef(asset, animId, buffer, times); @@ -1342,20 +1342,20 @@ inline void ExtractScaleSampler(Asset& asset, std::string& animId, Ref& sampler.interpolation = Interpolation_LINEAR; } -inline void ExtractRotationSampler(Asset& asset, std::string& animId, Ref& buffer, const aiNodeAnim* nodeChannel, float ticksPerSecond, Animation::Sampler& sampler) -{ +inline void ExtractRotationSampler(Asset& asset, std::string& animId, Ref& buffer, + const aiNodeAnim* nodeChannel, float ticksPerSecond, Animation::Sampler& sampler) { const unsigned int numKeyframes = nodeChannel->mNumRotationKeys; - std::vector times(numKeyframes); - std::vector values(numKeyframes * 4); + std::vector times(numKeyframes); + std::vector values(numKeyframes * 4); for (unsigned int i = 0; i < numKeyframes; ++i) { const aiQuatKey& key = nodeChannel->mRotationKeys[i]; // mTime is measured in ticks, but GLTF time is measured in seconds, so convert. times[i] = static_cast(key.mTime / ticksPerSecond); - values[(i * 4) + 0] = key.mValue.x; - values[(i * 4) + 1] = key.mValue.y; - values[(i * 4) + 2] = key.mValue.z; - values[(i * 4) + 3] = key.mValue.w; + values[(i * 4) + 0] = (ai_real) key.mValue.x; + values[(i * 4) + 1] = (ai_real) key.mValue.y; + values[(i * 4) + 2] = (ai_real) key.mValue.z; + values[(i * 4) + 3] = (ai_real) key.mValue.w; } sampler.input = GetSamplerInputRef(asset, animId, buffer, times); @@ -1417,7 +1417,7 @@ void glTF2Exporter::ExportAnimations() } } - // Assimp documentation staes this is not used (not implemented) + // Assimp documentation states this is not used (not implemented) // for (unsigned int channelIndex = 0; channelIndex < anim->mNumMeshChannels; ++channelIndex) { // const aiMeshAnim* meshChannel = anim->mMeshChannels[channelIndex]; // } diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index bbcad86e5..0dbbd85c4 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -798,6 +798,8 @@ ADD_ASSIMP_IMPORTER( X ADD_ASSIMP_IMPORTER( X3D AssetLib/X3D/X3DImporter.cpp AssetLib/X3D/X3DImporter.hpp + AssetLib/X3D/X3DGeoHelper.cpp + AssetLib/X3D/X3DGeoHelper.h ) ADD_ASSIMP_IMPORTER( GLTF diff --git a/code/Common/scene.cpp b/code/Common/scene.cpp index f56562b1c..12667f530 100644 --- a/code/Common/scene.cpp +++ b/code/Common/scene.cpp @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2020, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, @@ -42,25 +40,25 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include -aiNode::aiNode() -: mName("") -, mParent(nullptr) -, mNumChildren(0) -, mChildren(nullptr) -, mNumMeshes(0) -, mMeshes(nullptr) -, mMetaData(nullptr) { +aiNode::aiNode() : + mName(""), + mParent(nullptr), + mNumChildren(0), + mChildren(nullptr), + mNumMeshes(0), + mMeshes(nullptr), + mMetaData(nullptr) { // empty } -aiNode::aiNode(const std::string& name) -: mName(name) -, mParent(nullptr) -, mNumChildren(0) -, mChildren(nullptr) -, mNumMeshes(0) -, mMeshes(nullptr) -, mMetaData(nullptr) { +aiNode::aiNode(const std::string &name) : + mName(name), + mParent(nullptr), + mNumChildren(0), + mChildren(nullptr), + mNumMeshes(0), + mMeshes(nullptr), + mMetaData(nullptr) { // empty } @@ -68,8 +66,7 @@ aiNode::aiNode(const std::string& name) aiNode::~aiNode() { // delete all children recursively // to make sure we won't crash if the data is invalid ... - if (mNumChildren && mChildren) - { + if (mNumChildren && mChildren) { for (unsigned int a = 0; a < mNumChildren; a++) delete mChildren[a]; } @@ -78,7 +75,7 @@ aiNode::~aiNode() { delete mMetaData; } -const aiNode *aiNode::FindNode(const char* name) const { +const aiNode *aiNode::FindNode(const char *name) const { if (nullptr == name) { return nullptr; } @@ -86,7 +83,7 @@ const aiNode *aiNode::FindNode(const char* name) const { return this; } for (unsigned int i = 0; i < mNumChildren; ++i) { - const aiNode* const p = mChildren[i]->FindNode(name); + const aiNode *const p = mChildren[i]->FindNode(name); if (p) { return p; } @@ -95,11 +92,10 @@ const aiNode *aiNode::FindNode(const char* name) const { return nullptr; } -aiNode *aiNode::FindNode(const char* name) { - if (!::strcmp(mName.data, name))return this; - for (unsigned int i = 0; i < mNumChildren; ++i) - { - aiNode* const p = mChildren[i]->FindNode(name); +aiNode *aiNode::FindNode(const char *name) { + if (!::strcmp(mName.data, name)) return this; + for (unsigned int i = 0; i < mNumChildren; ++i) { + aiNode *const p = mChildren[i]->FindNode(name); if (p) { return p; } @@ -121,17 +117,16 @@ void aiNode::addChildren(unsigned int numChildren, aiNode **children) { } if (mNumChildren > 0) { - aiNode **tmp = new aiNode*[mNumChildren]; - ::memcpy(tmp, mChildren, sizeof(aiNode*) * mNumChildren); + aiNode **tmp = new aiNode *[mNumChildren]; + ::memcpy(tmp, mChildren, sizeof(aiNode *) * mNumChildren); delete[] mChildren; - mChildren = new aiNode*[mNumChildren + numChildren]; - ::memcpy(mChildren, tmp, sizeof(aiNode*) * mNumChildren); - ::memcpy(&mChildren[mNumChildren], children, sizeof(aiNode*)* numChildren); + mChildren = new aiNode *[mNumChildren + numChildren]; + ::memcpy(mChildren, tmp, sizeof(aiNode *) * mNumChildren); + ::memcpy(&mChildren[mNumChildren], children, sizeof(aiNode *) * numChildren); mNumChildren += numChildren; delete[] tmp; - } - else { - mChildren = new aiNode*[numChildren]; + } else { + mChildren = new aiNode *[numChildren]; for (unsigned int i = 0; i < numChildren; i++) { mChildren[i] = children[i]; } diff --git a/code/PostProcessing/ProcessHelper.h b/code/PostProcessing/ProcessHelper.h index 8520b21ec..e851c97e9 100644 --- a/code/PostProcessing/ProcessHelper.h +++ b/code/PostProcessing/ProcessHelper.h @@ -133,12 +133,12 @@ inline ::aiQuatKey max(const ::aiQuatKey &a, const ::aiQuatKey &b) { // std::min for aiVertexWeight inline ::aiVertexWeight min(const ::aiVertexWeight &a, const ::aiVertexWeight &b) { - return ::aiVertexWeight(min(a.mVertexId, b.mVertexId), min(a.mWeight, b.mWeight)); + return ::aiVertexWeight(min(a.mVertexId, b.mVertexId),static_cast(min(a.mWeight, b.mWeight))); } // std::max for aiVertexWeight inline ::aiVertexWeight max(const ::aiVertexWeight &a, const ::aiVertexWeight &b) { - return ::aiVertexWeight(max(a.mVertexId, b.mVertexId), max(a.mWeight, b.mWeight)); + return ::aiVertexWeight(static_cast(max(a.mVertexId, b.mVertexId)), static_cast(max(a.mWeight, b.mWeight))); } } // end namespace std diff --git a/include/assimp/scene.h b/include/assimp/scene.h index 2a9a77b02..b8f034ebf 100644 --- a/include/assimp/scene.h +++ b/include/assimp/scene.h @@ -5,8 +5,6 @@ Open Asset Import Library (assimp) Copyright (c) 2006-2020, assimp team - - All rights reserved. Redistribution and use of this software in source and binary forms, From 2b9d88c9434b13a3c0824bb5b402eb2e83857b0b Mon Sep 17 00:00:00 2001 From: ywang Date: Thu, 6 May 2021 15:10:06 -0700 Subject: [PATCH 088/335] support basis universal --- code/AssetLib/glTF2/glTF2Asset.h | 2 ++ code/AssetLib/glTF2/glTF2Asset.inl | 2 ++ code/AssetLib/glTF2/glTF2AssetWriter.inl | 13 +++++++++ code/AssetLib/glTF2/glTF2Exporter.cpp | 36 ++++++++++++++++++++---- code/AssetLib/glTF2/glTF2Importer.cpp | 6 ++++ 5 files changed, 53 insertions(+), 6 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index fad5cba83..f2c0369d6 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -1118,11 +1118,13 @@ public: bool KHR_materials_transmission; bool KHR_draco_mesh_compression; bool FB_ngon_encoding; + bool KHR_texture_basisu; } extensionsUsed; //! Keeps info about the required extensions struct RequiredExtensions { bool KHR_draco_mesh_compression; + bool KHR_texture_basisu; } extensionsRequired; AssetMetadata asset; diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 77537028f..42d3f060e 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -1121,6 +1121,7 @@ inline Image::Image() : } inline void Image::Read(Value &obj, Asset &r) { + //basisu: no need to handle .ktx2, .basis, load as is if (!mDataLength) { Value *curUri = FindString(obj, "uri"); if (nullptr != curUri) { @@ -2101,6 +2102,7 @@ inline void Asset::ReadExtensionsUsed(Document &doc) { CHECK_EXT(KHR_materials_clearcoat); CHECK_EXT(KHR_materials_transmission); CHECK_EXT(KHR_draco_mesh_compression); + CHECK_EXT(KHR_texture_basisu); #undef CHECK_EXT } diff --git a/code/AssetLib/glTF2/glTF2AssetWriter.inl b/code/AssetLib/glTF2/glTF2AssetWriter.inl index 01a28d4b7..bf7dbbb2e 100644 --- a/code/AssetLib/glTF2/glTF2AssetWriter.inl +++ b/code/AssetLib/glTF2/glTF2AssetWriter.inl @@ -250,6 +250,7 @@ namespace glTF2 { inline void Write(Value& obj, Image& img, AssetWriter& w) { + //basisu: no need to handle .ktx2, .basis, write as is if (img.bufferView) { obj.AddMember("bufferView", img.bufferView->index, w.mAl); obj.AddMember("mimeType", Value(img.mimeType, w.mAl).Move(), w.mAl); @@ -892,10 +893,22 @@ namespace glTF2 { if (this->mAsset.extensionsUsed.FB_ngon_encoding) { exts.PushBack(StringRef("FB_ngon_encoding"), mAl); } + + if (this->mAsset.extensionsUsed.KHR_texture_basisu) { + exts.PushBack(StringRef("KHR_texture_basisu"), mAl); + } } if (!exts.Empty()) mDoc.AddMember("extensionsUsed", exts, mAl); + + //basisu extensionRequired + Value extsReq; + extsReq.SetArray(); + if (this->mAsset.extensionsUsed.KHR_texture_basisu) { + extsReq.PushBack(StringRef("KHR_texture_basisu"), mAl); + mDoc.AddMember("extensionsRequired", extsReq, mAl); + } } template diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 51aef013d..e039bf88a 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -494,7 +494,6 @@ void glTF2Exporter::GetMatTexProp(const aiMaterial* mat, float& prop, const char void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTextureType tt, unsigned int slot = 0) { - if (mat->GetTextureCount(tt) > 0) { aiString tex; @@ -507,6 +506,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe texture = mAsset->textures.Get(it->second); } + bool useBasisUniversal = false; if (!texture) { std::string texId = mAsset->FindUniqueID("", "texture"); texture = mAsset->textures.Create(texId); @@ -519,18 +519,42 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe aiTexture* curTex = mScene->mTextures[atoi(&path[1])]; texture->source->name = curTex->mFilename.C_Str(); - - // The asset has its own buffer, see Image::SetData - texture->source->SetData(reinterpret_cast(curTex->pcData), curTex->mWidth, *mAsset); - + + //basisu: embedded ktx2, bu if (curTex->achFormatHint[0]) { std::string mimeType = "image/"; - mimeType += (memcmp(curTex->achFormatHint, "jpg", 3) == 0) ? "jpeg" : curTex->achFormatHint; + if(memcmp(curTex->achFormatHint, "jpg", 3) == 0) + mimeType += "jpeg"; + else if(memcmp(curTex->achFormatHint, "ktx", 3) == 0) { + useBasisUniversal = true; + mimeType += "ktx2"; + } + else if(memcmp(curTex->achFormatHint, "bu", 3) == 0) { + useBasisUniversal = true; + mimeType += "basis"; + } + else + mimeType += curTex->achFormatHint; texture->source->mimeType = mimeType; } + + // The asset has its own buffer, see Image::SetData + //basisu: "image/ktx2", "image/basis" as is + texture->source->SetData(reinterpret_cast(curTex->pcData), curTex->mWidth, *mAsset); } else { texture->source->uri = path; + if(texture->source->uri.find(".ktx2")!=std::string::npos || + texture->source->uri.find(".basis")!=std::string::npos) + { + useBasisUniversal = true; + } + } + + //basisu + if(useBasisUniversal) { + mAsset->extensionsUsed.KHR_texture_basisu = true; + mAsset->extensionsRequired.KHR_texture_basisu = true; } GetTexSampler(mat, texture, tt, slot); diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index ab1f01bf8..b109891cb 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1476,6 +1476,12 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) { if (strcmp(ext, "jpeg") == 0) { ext = "jpg"; } + else if(strcmp(ext, "ktx2") == 0) { //basisu + ext = "ktx"; + } + else if(strcmp(ext, "basis") == 0) { //basisu + ext = "bu"; + } size_t len = strlen(ext); if (len <= 3) { From 56bfa1ce5c0dc472b2faaa531d0587b0c9fa3363 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 7 May 2021 11:36:21 +0200 Subject: [PATCH 089/335] Make constructros with one arg explicit --- code/AssetLib/X3D/X3DImporter.hpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/code/AssetLib/X3D/X3DImporter.hpp b/code/AssetLib/X3D/X3DImporter.hpp index b3a7c4d06..360dfe6c5 100644 --- a/code/AssetLib/X3D/X3DImporter.hpp +++ b/code/AssetLib/X3D/X3DImporter.hpp @@ -313,7 +313,7 @@ protected: struct X3DNodeElementMetaBoolean : X3DNodeElementMeta { std::vector Value; ///< Stored value. - X3DNodeElementMetaBoolean(X3DNodeElementBase *pParent) : + explicit X3DNodeElementMetaBoolean(X3DNodeElementBase *pParent) : X3DNodeElementMeta(X3DElemType::ENET_MetaBoolean, pParent) { // empty } @@ -322,7 +322,7 @@ struct X3DNodeElementMetaBoolean : X3DNodeElementMeta { struct X3DNodeElementMetaDouble : X3DNodeElementMeta { std::vector Value; ///< Stored value. - X3DNodeElementMetaDouble(X3DNodeElementBase *pParent) : + explicit X3DNodeElementMetaDouble(X3DNodeElementBase *pParent) : X3DNodeElementMeta(X3DElemType::ENET_MetaDouble, pParent) { // empty } @@ -331,7 +331,7 @@ struct X3DNodeElementMetaDouble : X3DNodeElementMeta { struct X3DNodeElementMetaFloat : public X3DNodeElementMeta { std::vector Value; ///< Stored value. - X3DNodeElementMetaFloat(X3DNodeElementBase *pParent) : + explicit X3DNodeElementMetaFloat(X3DNodeElementBase *pParent) : X3DNodeElementMeta(X3DElemType::ENET_MetaFloat, pParent) { // empty } @@ -340,7 +340,7 @@ struct X3DNodeElementMetaFloat : public X3DNodeElementMeta { struct X3DNodeElementMetaInt : public X3DNodeElementMeta { std::vector Value; ///< Stored value. - X3DNodeElementMetaInt(X3DNodeElementBase *pParent) : + explicit X3DNodeElementMetaInt(X3DNodeElementBase *pParent) : X3DNodeElementMeta(X3DElemType::ENET_MetaInteger, pParent) { // empty } @@ -349,7 +349,7 @@ struct X3DNodeElementMetaInt : public X3DNodeElementMeta { struct X3DNodeElementMetaSet : public X3DNodeElementMeta { std::list Value; ///< Stored value. - X3DNodeElementMetaSet(X3DNodeElementBase *pParent) : + explicit X3DNodeElementMetaSet(X3DNodeElementBase *pParent) : X3DNodeElementMeta(X3DElemType::ENET_MetaSet, pParent) { // empty } @@ -358,7 +358,7 @@ struct X3DNodeElementMetaSet : public X3DNodeElementMeta { struct X3DNodeElementMetaString : public X3DNodeElementMeta { std::list Value; ///< Stored value. - X3DNodeElementMetaString(X3DNodeElementBase *pParent) : + explicit X3DNodeElementMetaString(X3DNodeElementBase *pParent) : X3DNodeElementMeta(X3DElemType::ENET_MetaString, pParent) { // empty } From 8cae8c5461e61f02de404a415f43c17a730c5cb3 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 7 May 2021 11:52:16 +0200 Subject: [PATCH 090/335] Fix static code analysis findings --- code/AssetLib/X3D/X3DImporter.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/code/AssetLib/X3D/X3DImporter.hpp b/code/AssetLib/X3D/X3DImporter.hpp index 360dfe6c5..f65dda559 100644 --- a/code/AssetLib/X3D/X3DImporter.hpp +++ b/code/AssetLib/X3D/X3DImporter.hpp @@ -290,9 +290,6 @@ protected: struct CX3DNodeElementGroup : X3DNodeElementBase { aiMatrix4x4 Transformation; ///< Transformation matrix. - bool Static; - bool UseChoice; ///< Flag: if true then use number from \ref Choice to choose what the child will be kept. - int32_t Choice; ///< Number of the child which will be kept. }; struct X3DNodeElementMeta : X3DNodeElementBase { From 964778cac1a98fd333d00ab18f43577ff69ce39b Mon Sep 17 00:00:00 2001 From: Carsten Rudolph <18394207+crud89@users.noreply.github.com> Date: Fri, 7 May 2021 17:30:26 +0200 Subject: [PATCH 091/335] Add AI_CONFIG_EXPORT_BLOB_NAME export property. --- include/assimp/config.h.in | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/include/assimp/config.h.in b/include/assimp/config.h.in index d78568da7..8ea82482f 100644 --- a/include/assimp/config.h.in +++ b/include/assimp/config.h.in @@ -1066,6 +1066,23 @@ enum aiComponent */ #define AI_CONFIG_EXPORT_POINT_CLOUDS "EXPORT_POINT_CLOUDS" +/** + * @brief Specifies the blob name, assimp uses for exporting. + * + * Some formats require auxiliary files to be written, that need to be linked back into + * the original file. For example, OBJ files export materials to a separate MTL file and + * use the `mtllib` keyword to reference this file. + * + * When exporting blobs using #ExportToBlob, assimp does not know the name of the blob + * file and thus outputs `mtllib $blobfile.mtl`, which might not be desired, since the + * MTL file might be called differently. + * + * This property can be used to give the exporter a hint on how to use the magic + * `$blobfile` keyword. If the exporter detects the keyword and is provided with a name + * for the blob, it instead uses this name. + */ +#define AI_CONFIG_EXPORT_BLOB_NAME "EXPORT_BLOB_NAME" + /** * @brief Specifies a gobal key factor for scale, float value */ From be85f238f4dd96f1182678d23e997b2e675484a7 Mon Sep 17 00:00:00 2001 From: Carsten Rudolph <18394207+crud89@users.noreply.github.com> Date: Fri, 7 May 2021 17:30:58 +0200 Subject: [PATCH 092/335] Add optional blob base name to blob IO system. --- include/assimp/BlobIOSystem.h | 33 +++++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/include/assimp/BlobIOSystem.h b/include/assimp/BlobIOSystem.h index 4e3d5c2a3..081ccf32a 100644 --- a/include/assimp/BlobIOSystem.h +++ b/include/assimp/BlobIOSystem.h @@ -194,8 +194,14 @@ class BlobIOSystem : public IOSystem { friend class BlobIOStream; typedef std::pair BlobEntry; + public: - BlobIOSystem() { + BlobIOSystem() : + baseName{} { + } + + BlobIOSystem(const std::string &baseName) : + baseName(baseName) { } virtual ~BlobIOSystem() { @@ -207,27 +213,32 @@ public: public: // ------------------------------------------------------------------- const char *GetMagicFileName() const { - return AI_BLOBIO_MAGIC; + return baseName.empty() ? AI_BLOBIO_MAGIC : baseName.c_str(); } // ------------------------------------------------------------------- aiExportDataBlob *GetBlobChain() { + const auto magicName = std::string(this->GetMagicFileName()); + const bool hasBaseName = baseName.empty(); + // one must be the master aiExportDataBlob *master = nullptr, *cur; + for (const BlobEntry &blobby : blobs) { - if (blobby.first == AI_BLOBIO_MAGIC) { + if (blobby.first == magicName) { master = blobby.second; + master->name.Set(hasBaseName ? blobby.first : ""); break; } } + if (!master) { ASSIMP_LOG_ERROR("BlobIOSystem: no data written or master file was not closed properly."); return nullptr; } - master->name.Set(""); - cur = master; + for (const BlobEntry &blobby : blobs) { if (blobby.second == master) { continue; @@ -236,9 +247,14 @@ public: cur->next = blobby.second; cur = cur->next; - // extract the file extension from the file written - const std::string::size_type s = blobby.first.find_first_of('.'); - cur->name.Set(s == std::string::npos ? blobby.first : blobby.first.substr(s + 1)); + if (hasBaseName) { + cur->name.Set(blobby.first); + } + else { + // extract the file extension from the file written + const std::string::size_type s = blobby.first.find_first_of('.'); + cur->name.Set(s == std::string::npos ? blobby.first : blobby.first.substr(s + 1)); + } } // give up blob ownership @@ -283,6 +299,7 @@ private: } private: + std::string baseName; std::set created; std::vector blobs; }; From 8ff52c0f89fe52d52b6fc0ac6f111c9355616139 Mon Sep 17 00:00:00 2001 From: Carsten Rudolph <18394207+crud89@users.noreply.github.com> Date: Fri, 7 May 2021 17:31:30 +0200 Subject: [PATCH 093/335] Pass base name from export properties to the IO system. --- code/Common/Exporter.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/Common/Exporter.cpp b/code/Common/Exporter.cpp index 20cbb05d6..ebcc955df 100644 --- a/code/Common/Exporter.cpp +++ b/code/Common/Exporter.cpp @@ -343,9 +343,11 @@ const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const cha delete pimpl->blob; pimpl->blob = nullptr; } + + auto baseName = pProperties ? pProperties->GetPropertyString(AI_CONFIG_EXPORT_BLOB_NAME, AI_BLOBIO_MAGIC) : AI_BLOBIO_MAGIC; std::shared_ptr old = pimpl->mIOSystem; - BlobIOSystem* blobio = new BlobIOSystem(); + BlobIOSystem *blobio = new BlobIOSystem(baseName); pimpl->mIOSystem = std::shared_ptr( blobio ); if (AI_SUCCESS != Export(pScene,pFormatId,blobio->GetMagicFileName(), pPreprocessing, pProperties)) { From 1b33dd1965dd0a58b20fa4e270d1809abb920c58 Mon Sep 17 00:00:00 2001 From: Carsten Rudolph <18394207+crud89@users.noreply.github.com> Date: Fri, 7 May 2021 17:31:38 +0200 Subject: [PATCH 094/335] Document AI_CONFIG_EXPORT_BLOB_NAME. --- include/assimp/cexport.h | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/include/assimp/cexport.h b/include/assimp/cexport.h index 2e84b1f30..44843af0e 100644 --- a/include/assimp/cexport.h +++ b/include/assimp/cexport.h @@ -205,16 +205,22 @@ struct aiExportDataBlob { void *data; /** Name of the blob. An empty string always - indicates the first (and primary) blob, - which contains the actual file data. - Any other blobs are auxiliary files produced - by exporters (i.e. material files). Existence - of such files depends on the file format. Most - formats don't split assets across multiple files. - - If used, blob names usually contain the file - extension that should be used when writing - the data to disc. + * indicates the first (and primary) blob, + * which contains the actual file data. + * Any other blobs are auxiliary files produced + * by exporters (i.e. material files). Existence + * of such files depends on the file format. Most + * formats don't split assets across multiple files. + * + * If used, blob names usually contain the file + * extension that should be used when writing + * the data to disc. + * + * The blob names generated can be influenced by + * setting the #AI_CONFIG_EXPORT_BLOB_NAME export + * property to the name that is used for the master + * blob. All other names are typically derived from + * the base name, by the file format exporter. */ C_STRUCT aiString name; From a19b708144b91d6936339b40f93ab7540ee25689 Mon Sep 17 00:00:00 2001 From: ywang Date: Fri, 7 May 2021 16:27:23 -0700 Subject: [PATCH 095/335] support both ktx and ktx2 --- code/AssetLib/glTF2/glTF2Exporter.cpp | 8 ++++++-- code/AssetLib/glTF2/glTF2Importer.cpp | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index e039bf88a..751508225 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -526,10 +526,14 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe if(memcmp(curTex->achFormatHint, "jpg", 3) == 0) mimeType += "jpeg"; else if(memcmp(curTex->achFormatHint, "ktx", 3) == 0) { + useBasisUniversal = true; + mimeType += "ktx"; + } + else if(memcmp(curTex->achFormatHint, "kx2", 3) == 0) { useBasisUniversal = true; mimeType += "ktx2"; } - else if(memcmp(curTex->achFormatHint, "bu", 3) == 0) { + else if(memcmp(curTex->achFormatHint, "bu", 2) == 0) { useBasisUniversal = true; mimeType += "basis"; } @@ -544,7 +548,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe } else { texture->source->uri = path; - if(texture->source->uri.find(".ktx2")!=std::string::npos || + if(texture->source->uri.find(".ktx")!=std::string::npos || texture->source->uri.find(".basis")!=std::string::npos) { useBasisUniversal = true; diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index b109891cb..db5da8813 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1476,8 +1476,8 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) { if (strcmp(ext, "jpeg") == 0) { ext = "jpg"; } - else if(strcmp(ext, "ktx2") == 0) { //basisu - ext = "ktx"; + else if(strcmp(ext, "ktx2") == 0) { //basisu: ktx remains + ext = "kx2"; } else if(strcmp(ext, "basis") == 0) { //basisu ext = "bu"; From e37e00c51fa48d459165ef57caf033e9fe73c3e6 Mon Sep 17 00:00:00 2001 From: Jason C Date: Fri, 7 May 2021 20:21:56 -0400 Subject: [PATCH 096/335] [assimp/fast_atof] Fixed garbage in exception messages. Also reduced 100 byte context output to 30 (which is still probably excessive, but not *as* excessive). --- include/assimp/fast_atof.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/assimp/fast_atof.h b/include/assimp/fast_atof.h index 9ea49c85c..441fe5652 100644 --- a/include/assimp/fast_atof.h +++ b/include/assimp/fast_atof.h @@ -29,6 +29,7 @@ #include "StringComparison.h" #include #include +#include #ifdef _MSC_VER # include @@ -193,7 +194,7 @@ uint64_t strtoul10_64( const char* in, const char** out=0, unsigned int* max_ino if ( *in < '0' || *in > '9' ) { // The string is known to be bad, so don't risk printing the whole thing. - throw ExceptionType("The string \"", std::string(in).substr(0, 100), "\" cannot be converted into a value." ); + throw ExceptionType("The string \"", ai_str_toprintable(in, 30), "\" cannot be converted into a value." ); } for ( ;; ) { @@ -293,7 +294,7 @@ const char* fast_atoreal_move(const char* c, Real& out, bool check_comma = true) if (!(c[0] >= '0' && c[0] <= '9') && !((c[0] == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9')) { // The string is known to be bad, so don't risk printing the whole thing. - throw ExceptionType("Cannot parse string \"", std::string(c).substr(0, 100), + throw ExceptionType("Cannot parse string \"", ai_str_toprintable(c, 30), "\" as a real number: does not start with digit " "or decimal point followed by digit."); } From 859b32c04581644a9f54c6f18860dd7927a1d127 Mon Sep 17 00:00:00 2001 From: Jason C Date: Fri, 7 May 2021 22:32:32 -0400 Subject: [PATCH 097/335] [Logger] Log a notification instead of silently dropping long log messages. Logs a notification instead of silently dropping long log messages, which can complicate debugging. This way, if you don't see a message you expect to see, you'll immediately know why. The *correct* approach would be to eliminate length filtering here entirely and use `snprintf` appropriately (also there's a tiny -- probably negligible -- performance hit here in calling `strlen` regardless of whether or not the verbosity level matches). Failing that, the second best option is to copy and truncate messages here. However, for now, this should be OK. --- code/Common/DefaultLogger.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/code/Common/DefaultLogger.cpp b/code/Common/DefaultLogger.cpp index aa13ca5ce..3f6d2d7ed 100644 --- a/code/Common/DefaultLogger.cpp +++ b/code/Common/DefaultLogger.cpp @@ -169,7 +169,7 @@ void Logger::debug(const char *message) { // sometimes importers will include data from the input file // (i.e. node names) in their messages. if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { - return; + return OnDebug(""); } return OnDebug(message); } @@ -181,7 +181,7 @@ void Logger::verboseDebug(const char *message) { // sometimes importers will include data from the input file // (i.e. node names) in their messages. if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { - return; + return OnVerboseDebug(""); } return OnVerboseDebug(message); } @@ -191,7 +191,7 @@ void Logger::info(const char *message) { // SECURITY FIX: see above if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { - return; + return OnInfo(""); } return OnInfo(message); } @@ -201,7 +201,7 @@ void Logger::warn(const char *message) { // SECURITY FIX: see above if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { - return; + return OnWarn(""); } return OnWarn(message); } @@ -210,7 +210,7 @@ void Logger::warn(const char *message) { void Logger::error(const char *message) { // SECURITY FIX: see above if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { - return; + return OnError(""); } return OnError(message); } From d75f8bd5b0e74d0c93245b2e96092a9675c2111a Mon Sep 17 00:00:00 2001 From: Krishty Date: Mon, 10 May 2021 20:44:43 +0200 Subject: [PATCH 098/335] silence warnings in stb_image.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-applies 09c5564d5b1123125ba0a7f07f7f8c1dfcdf7a86 to stb_image.h’s new path. --- contrib/stb_image/stb_image.h | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/contrib/stb_image/stb_image.h b/contrib/stb_image/stb_image.h index accef4839..65a205f6e 100644 --- a/contrib/stb_image/stb_image.h +++ b/contrib/stb_image/stb_image.h @@ -4120,7 +4120,7 @@ static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) if (s >= 16) return -1; // invalid code! // code size is s, so: b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - if (b >= sizeof (z->size)) return -1; // some data was corrupt somewhere! + if ((unsigned int)b >= sizeof (z->size)) return -1; // some data was corrupt somewhere! if (z->size[b] != s) return -1; // was originally an assert, but report failure instead. a->code_buffer >>= s; a->num_bits -= s; @@ -6775,8 +6775,6 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, stbi_uc *two_back = 0; stbi__gif g; int stride; - int out_size = 0; - int delays_size = 0; memset(&g, 0, sizeof(g)); if (delays) { *delays = 0; @@ -6793,7 +6791,7 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, stride = g.w * g.h * 4; if (out) { - void *tmp = (stbi_uc*) STBI_REALLOC_SIZED( out, out_size, layers * stride ); + void *tmp = (stbi_uc*) STBI_REALLOC( out, layers * stride ); if (NULL == tmp) { STBI_FREE(g.out); STBI_FREE(g.history); @@ -6802,19 +6800,15 @@ static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, } else { out = (stbi_uc*) tmp; - out_size = layers * stride; } if (delays) { - *delays = (int*) STBI_REALLOC_SIZED( *delays, delays_size, sizeof(int) * layers ); - delays_size = layers * sizeof(int); + *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); } } else { out = (stbi_uc*)stbi__malloc( layers * stride ); - out_size = layers * stride; if (delays) { *delays = (int*) stbi__malloc( layers * sizeof(int) ); - delays_size = layers * sizeof(int); } } memcpy( out + ((layers - 1) * stride), u, stride ); From 00b625a2ae75f344bafc85baf03a1b2ea73f8462 Mon Sep 17 00:00:00 2001 From: Jason C Date: Mon, 10 May 2021 19:01:15 -0400 Subject: [PATCH 099/335] [amf] Fix crash when file could not be parsed. Fix double free of mXmlParser (deleted but not reset in ParseFile, then deleted again in ~AMFImporter). Should probably use a smart pointer instead, though. --- This change was previously made in 785cca1bb43f9e6047cb566a910fcd0e3d49951f, as part of PR #3890, but was lost in a merge. --- code/AssetLib/AMF/AMFImporter.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/AssetLib/AMF/AMFImporter.cpp b/code/AssetLib/AMF/AMFImporter.cpp index e77b65f77..615882b6a 100644 --- a/code/AssetLib/AMF/AMFImporter.cpp +++ b/code/AssetLib/AMF/AMFImporter.cpp @@ -268,7 +268,8 @@ void AMFImporter::ParseFile(const std::string &pFile, IOSystem *pIOHandler) { mXmlParser = new XmlParser(); if (!mXmlParser->parse(file.get())) { delete mXmlParser; - throw DeadlyImportError("Failed to create XML reader for file" + pFile + "."); + mXmlParser = nullptr; + throw DeadlyImportError("Failed to create XML reader for file ", pFile, "."); } // Start reading, search for root tag From 632e4a20a989d2e2849b81d828ab22fba3b8d6da Mon Sep 17 00:00:00 2001 From: Salvage <29021710+Saalvage@users.noreply.github.com> Date: Tue, 11 May 2021 05:29:51 +0200 Subject: [PATCH 100/335] Utilize decltype for slightly improved syntax --- include/assimp/Bitmap.h | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/include/assimp/Bitmap.h b/include/assimp/Bitmap.h index d56bb4c73..a455e2ecf 100644 --- a/include/assimp/Bitmap.h +++ b/include/assimp/Bitmap.h @@ -75,13 +75,12 @@ protected: uint32_t offset; // We define the struct size because sizeof(Header) might return a wrong result because of structure padding. - // Moreover, we must use this ugly and error prone syntax because Visual Studio neither support constexpr or sizeof(name_of_field). - static const std::size_t header_size = - sizeof(uint16_t) + // type - sizeof(uint32_t) + // size - sizeof(uint16_t) + // reserved1 - sizeof(uint16_t) + // reserved2 - sizeof(uint32_t); // offset + static constexpr std::size_t header_size = + sizeof(decltype(type)) + + sizeof(decltype(size)) + + sizeof(decltype(reserved1)) + + sizeof(decltype(reserved2)) + + sizeof(decltype(offset)); }; struct DIB { @@ -98,22 +97,21 @@ protected: uint32_t nb_important_colors; // We define the struct size because sizeof(DIB) might return a wrong result because of structure padding. - // Moreover, we must use this ugly and error prone syntax because Visual Studio neither support constexpr or sizeof(name_of_field). - static const std::size_t dib_size = - sizeof(uint32_t) + // size - sizeof(int32_t) + // width - sizeof(int32_t) + // height - sizeof(uint16_t) + // planes - sizeof(uint16_t) + // bits_per_pixel - sizeof(uint32_t) + // compression - sizeof(uint32_t) + // image_size - sizeof(int32_t) + // x_resolution - sizeof(int32_t) + // y_resolution - sizeof(uint32_t) + // nb_colors - sizeof(uint32_t); // nb_important_colors + static constexpr std::size_t dib_size = + sizeof(decltype(size)) + + sizeof(decltype(width)) + + sizeof(decltype(height)) + + sizeof(decltype(planes)) + + sizeof(decltype(bits_per_pixel)) + + sizeof(decltype(compression)) + + sizeof(decltype(image_size)) + + sizeof(decltype(x_resolution)) + + sizeof(decltype(y_resolution)) + + sizeof(decltype(nb_colors)) + + sizeof(decltype(nb_important_colors)); }; - static const std::size_t mBytesPerPixel = 4; + static constexpr std::size_t mBytesPerPixel = 4; public: static void Save(aiTexture* texture, IOStream* file); From 8d20460ae43728b31d390a49a81b655cfe856ad9 Mon Sep 17 00:00:00 2001 From: Salvage <29021710+Saalvage@users.noreply.github.com> Date: Tue, 11 May 2021 19:06:21 +0200 Subject: [PATCH 101/335] Ditch decltype --- include/assimp/Bitmap.h | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/include/assimp/Bitmap.h b/include/assimp/Bitmap.h index a455e2ecf..e4ce194d9 100644 --- a/include/assimp/Bitmap.h +++ b/include/assimp/Bitmap.h @@ -76,11 +76,11 @@ protected: // We define the struct size because sizeof(Header) might return a wrong result because of structure padding. static constexpr std::size_t header_size = - sizeof(decltype(type)) + - sizeof(decltype(size)) + - sizeof(decltype(reserved1)) + - sizeof(decltype(reserved2)) + - sizeof(decltype(offset)); + sizeof(type) + + sizeof(size) + + sizeof(reserved1) + + sizeof(reserved2) + + sizeof(offset); }; struct DIB { @@ -98,17 +98,17 @@ protected: // We define the struct size because sizeof(DIB) might return a wrong result because of structure padding. static constexpr std::size_t dib_size = - sizeof(decltype(size)) + - sizeof(decltype(width)) + - sizeof(decltype(height)) + - sizeof(decltype(planes)) + - sizeof(decltype(bits_per_pixel)) + - sizeof(decltype(compression)) + - sizeof(decltype(image_size)) + - sizeof(decltype(x_resolution)) + - sizeof(decltype(y_resolution)) + - sizeof(decltype(nb_colors)) + - sizeof(decltype(nb_important_colors)); + sizeof(size) + + sizeof(width) + + sizeof(height) + + sizeof(planes) + + sizeof(bits_per_pixel) + + sizeof(compression) + + sizeof(image_size) + + sizeof(x_resolution) + + sizeof(y_resolution) + + sizeof(nb_colors) + + sizeof(nb_important_colors); }; static constexpr std::size_t mBytesPerPixel = 4; From 813d0aecdda2627d3bdd612332ce5efc2f5f0afa Mon Sep 17 00:00:00 2001 From: Malcolm Tyrrell Date: Wed, 12 May 2021 12:43:24 +0100 Subject: [PATCH 102/335] Adjust warn --- code/AssetLib/Ogre/OgreStructs.cpp | 4 ++-- code/AssetLib/SIB/SIBImporter.cpp | 2 +- code/Common/DefaultLogger.cpp | 9 +++++---- include/assimp/Logger.hpp | 22 +++++++++++++--------- 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/code/AssetLib/Ogre/OgreStructs.cpp b/code/AssetLib/Ogre/OgreStructs.cpp index 90e2d4465..ce289c79c 100644 --- a/code/AssetLib/Ogre/OgreStructs.cpp +++ b/code/AssetLib/Ogre/OgreStructs.cpp @@ -545,7 +545,7 @@ aiMesh *SubMesh::ConvertToAssimpMesh(Mesh *parent) { dest->mNumUVComponents[0] = static_cast(uv1Element->ComponentCount()); dest->mTextureCoords[0] = new aiVector3D[dest->mNumVertices]; } else { - ASSIMP_LOG_WARN(Formatter::format() << "Ogre imported UV0 type " << uv1Element->TypeToString() << " is not compatible with Assimp. Ignoring UV."); + ASSIMP_LOG_WARN_F("Ogre imported UV0 type ", uv1Element->TypeToString(), " is not compatible with Assimp. Ignoring UV."); uv1 = 0; } } @@ -554,7 +554,7 @@ aiMesh *SubMesh::ConvertToAssimpMesh(Mesh *parent) { dest->mNumUVComponents[1] = static_cast(uv2Element->ComponentCount()); dest->mTextureCoords[1] = new aiVector3D[dest->mNumVertices]; } else { - ASSIMP_LOG_WARN(Formatter::format() << "Ogre imported UV0 type " << uv2Element->TypeToString() << " is not compatible with Assimp. Ignoring UV."); + ASSIMP_LOG_WARN_F("Ogre imported UV0 type ", uv2Element->TypeToString(), " is not compatible with Assimp. Ignoring UV."); uv2 = 0; } } diff --git a/code/AssetLib/SIB/SIBImporter.cpp b/code/AssetLib/SIB/SIBImporter.cpp index 6c1e5950b..1426fb73c 100644 --- a/code/AssetLib/SIB/SIBImporter.cpp +++ b/code/AssetLib/SIB/SIBImporter.cpp @@ -174,7 +174,7 @@ static void UnknownChunk(StreamReaderLE * /*stream*/, const SIBChunk &chunk) { static_cast(chunk.Tag & 0xff) }; - ASSIMP_LOG_WARN((Formatter::format(), "SIB: Skipping unknown '", ai_str_toprintable(temp, 4), "' chunk.")); + ASSIMP_LOG_WARN_F("SIB: Skipping unknown '", ai_str_toprintable(temp, 4), "' chunk."); } // Reads a UTF-16LE string and returns it at UTF-8. diff --git a/code/Common/DefaultLogger.cpp b/code/Common/DefaultLogger.cpp index aa13ca5ce..d2e9a2caf 100644 --- a/code/Common/DefaultLogger.cpp +++ b/code/Common/DefaultLogger.cpp @@ -197,13 +197,14 @@ void Logger::info(const char *message) { } // ---------------------------------------------------------------------------------- -void Logger::warn(const char *message) { - +void Logger::warnInternal(Assimp::Formatter::format f) { + std::string message = f; + // TODO: Should limit sizes in the formatter. // SECURITY FIX: see above - if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { + if (message.length() > MAX_LOG_MESSAGE_LENGTH) { return; } - return OnWarn(message); + return OnWarn(message.c_str()); } // ---------------------------------------------------------------------------------- diff --git a/include/assimp/Logger.hpp b/include/assimp/Logger.hpp index ee6eab507..f68c951fd 100644 --- a/include/assimp/Logger.hpp +++ b/include/assimp/Logger.hpp @@ -119,8 +119,10 @@ public: // ---------------------------------------------------------------------- /** @brief Writes a warning message * @param message Warn message*/ - void warn(const char* message); - void warn(const std::string &message); + template + void warn(T&&... args) { + warnInternal(Assimp::Formatter::format(), std::forward(args)...); + } // ---------------------------------------------------------------------- /** @brief Writes an error message @@ -225,6 +227,14 @@ protected: */ virtual void OnError(const char* message) = 0; +protected: + void warnInternal(Assimp::Formatter::format f); + + template + void warnInternal(Assimp::Formatter::format f, U&& u, T&&... args) { + warnInternal(std::move(f << std::forward(u)), std::forward(args)...); + } + protected: LogSeverity m_Severity; }; @@ -283,12 +293,6 @@ void Logger::error(const std::string &message) { return error(message.c_str()); } -// ---------------------------------------------------------------------------------- -inline -void Logger::warn(const std::string &message) { - return warn(message.c_str()); -} - // ---------------------------------------------------------------------------------- inline void Logger::info(const std::string &message) { @@ -299,7 +303,7 @@ void Logger::info(const std::string &message) { // ------------------------------------------------------------------------------------------------ #define ASSIMP_LOG_WARN_F(string, ...) \ - Assimp::DefaultLogger::get()->warn((Assimp::Formatter::format(string), __VA_ARGS__)) + Assimp::DefaultLogger::get()->warn((string, __VA_ARGS__)) #define ASSIMP_LOG_ERROR_F(string, ...) \ Assimp::DefaultLogger::get()->error((Assimp::Formatter::format(string), __VA_ARGS__)) From 58bc4bcb63924ae10bd9c5a8a8f6639c681f5eea Mon Sep 17 00:00:00 2001 From: Malcolm Tyrrell Date: Wed, 12 May 2021 12:55:21 +0100 Subject: [PATCH 103/335] log info --- code/AssetLib/3DS/3DSLoader.cpp | 2 +- code/AssetLib/AC/ACLoader.cpp | 4 ++-- code/Common/DefaultLogger.cpp | 9 +++++---- include/assimp/Logger.hpp | 21 ++++++++++++--------- 4 files changed, 20 insertions(+), 16 deletions(-) diff --git a/code/AssetLib/3DS/3DSLoader.cpp b/code/AssetLib/3DS/3DSLoader.cpp index 92fe72bbf..2d77e0f66 100644 --- a/code/AssetLib/3DS/3DSLoader.cpp +++ b/code/AssetLib/3DS/3DSLoader.cpp @@ -305,7 +305,7 @@ void Discreet3DSImporter::ParseEditorChunk() { // print the version number char buff[10]; ASSIMP_itoa10(buff, stream->GetI2()); - ASSIMP_LOG_INFO_F(std::string("3DS file format version: "), buff); + ASSIMP_LOG_INFO_F("3DS file format version: ", buff); } break; }; ASSIMP_3DS_END_CHUNK(); diff --git a/code/AssetLib/AC/ACLoader.cpp b/code/AssetLib/AC/ACLoader.cpp index cba84e8b0..59cfc30dd 100644 --- a/code/AssetLib/AC/ACLoader.cpp +++ b/code/AssetLib/AC/ACLoader.cpp @@ -690,7 +690,7 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object, if (object.subDiv) { if (configEvalSubdivision) { std::unique_ptr div(Subdivider::Create(Subdivider::CATMULL_CLARKE)); - ASSIMP_LOG_INFO("AC3D: Evaluating subdivision surface: " + object.name); + ASSIMP_LOG_INFO_F("AC3D: Evaluating subdivision surface: ", object.name); std::vector cpy(meshes.size() - oldm, nullptr); div->Subdivide(&meshes[oldm], cpy.size(), &cpy.front(), object.subDiv, true); @@ -698,7 +698,7 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object, // previous meshes are deleted vy Subdivide(). } else { - ASSIMP_LOG_INFO("AC3D: Letting the subdivision surface untouched due to my configuration: " + object.name); + ASSIMP_LOG_INFO_F("AC3D: Letting the subdivision surface untouched due to my configuration: ", object.name); } } } diff --git a/code/Common/DefaultLogger.cpp b/code/Common/DefaultLogger.cpp index d2e9a2caf..a5c378e03 100644 --- a/code/Common/DefaultLogger.cpp +++ b/code/Common/DefaultLogger.cpp @@ -187,13 +187,14 @@ void Logger::verboseDebug(const char *message) { } // ---------------------------------------------------------------------------------- -void Logger::info(const char *message) { - +void Logger::infoInternal(Assimp::Formatter::format f) { + std::string message = f; + // TODO: Should limit sizes in the formatter. // SECURITY FIX: see above - if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { + if (message.length() > MAX_LOG_MESSAGE_LENGTH) { return; } - return OnInfo(message); + return OnInfo(message.c_str()); } // ---------------------------------------------------------------------------------- diff --git a/include/assimp/Logger.hpp b/include/assimp/Logger.hpp index f68c951fd..2c3ee1085 100644 --- a/include/assimp/Logger.hpp +++ b/include/assimp/Logger.hpp @@ -113,8 +113,10 @@ public: // ---------------------------------------------------------------------- /** @brief Writes a info message * @param message Info message*/ - void info(const char* message); - void info(const std::string &message); + template + void info(T&&... args) { + infoInternal(Assimp::Formatter::format(), std::forward(args)...); + } // ---------------------------------------------------------------------- /** @brief Writes a warning message @@ -235,6 +237,13 @@ protected: warnInternal(std::move(f << std::forward(u)), std::forward(args)...); } + void infoInternal(Assimp::Formatter::format f); + + template + void infoInternal(Assimp::Formatter::format f, U&& u, T&&... args) { + infoInternal(std::move(f << std::forward(u)), std::forward(args)...); + } + protected: LogSeverity m_Severity; }; @@ -293,12 +302,6 @@ void Logger::error(const std::string &message) { return error(message.c_str()); } -// ---------------------------------------------------------------------------------- -inline -void Logger::info(const std::string &message) { - return info(message.c_str()); -} - } // Namespace Assimp // ------------------------------------------------------------------------------------------------ @@ -315,7 +318,7 @@ void Logger::info(const std::string &message) { Assimp::DefaultLogger::get()->verboseDebug((Assimp::Formatter::format(string), __VA_ARGS__)) #define ASSIMP_LOG_INFO_F(string, ...) \ - Assimp::DefaultLogger::get()->info((Assimp::Formatter::format(string), __VA_ARGS__)) + Assimp::DefaultLogger::get()->info((string, __VA_ARGS__)) #define ASSIMP_LOG_WARN(string) \ Assimp::DefaultLogger::get()->warn(string) From 18beae988cb12cbc9db15d3fdce35b18187047af Mon Sep 17 00:00:00 2001 From: Chuck Claunch Date: Wed, 12 May 2021 21:57:24 +0000 Subject: [PATCH 104/335] Add support for arm 64 bit --- port/PyAssimp/pyassimp/helper.py | 1 + 1 file changed, 1 insertion(+) diff --git a/port/PyAssimp/pyassimp/helper.py b/port/PyAssimp/pyassimp/helper.py index 5c1aca827..7a4b2bdcb 100644 --- a/port/PyAssimp/pyassimp/helper.py +++ b/port/PyAssimp/pyassimp/helper.py @@ -27,6 +27,7 @@ if os.name=='posix': additional_dirs.append('./') additional_dirs.append('/usr/lib/') additional_dirs.append('/usr/lib/x86_64-linux-gnu/') + additional_dirs.append('/usr/lib/aarch64-linux-gnu/') additional_dirs.append('/usr/local/lib/') if 'LD_LIBRARY_PATH' in os.environ: From ca698c3e491a5681c1959a61e944a1a267158373 Mon Sep 17 00:00:00 2001 From: Malcolm Tyrrell Date: Thu, 13 May 2021 09:36:42 +0100 Subject: [PATCH 105/335] Log error --- code/Common/DefaultLogger.cpp | 9 ++++---- code/PostProcessing/CalcTangentsProcess.cpp | 2 +- include/assimp/Logger.hpp | 23 ++++++++++++--------- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/code/Common/DefaultLogger.cpp b/code/Common/DefaultLogger.cpp index a5c378e03..e468d72ca 100644 --- a/code/Common/DefaultLogger.cpp +++ b/code/Common/DefaultLogger.cpp @@ -176,7 +176,6 @@ void Logger::debug(const char *message) { // ---------------------------------------------------------------------------------- void Logger::verboseDebug(const char *message) { - // SECURITY FIX: otherwise it's easy to produce overruns since // sometimes importers will include data from the input file // (i.e. node names) in their messages. @@ -209,12 +208,14 @@ void Logger::warnInternal(Assimp::Formatter::format f) { } // ---------------------------------------------------------------------------------- -void Logger::error(const char *message) { +void Logger::errorInternal(Assimp::Formatter::format f) { + std::string message = f; + // TODO: Should limit sizes in the formatter. // SECURITY FIX: see above - if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { + if (message.length() > MAX_LOG_MESSAGE_LENGTH) { return; } - return OnError(message); + return OnError(message.c_str()); } // ---------------------------------------------------------------------------------- diff --git a/code/PostProcessing/CalcTangentsProcess.cpp b/code/PostProcessing/CalcTangentsProcess.cpp index cdafda3b6..721567857 100644 --- a/code/PostProcessing/CalcTangentsProcess.cpp +++ b/code/PostProcessing/CalcTangentsProcess.cpp @@ -129,7 +129,7 @@ bool CalcTangentsProcess::ProcessMesh(aiMesh *pMesh, unsigned int meshIndex) { return false; } if (configSourceUV >= AI_MAX_NUMBER_OF_TEXTURECOORDS || !pMesh->mTextureCoords[configSourceUV]) { - ASSIMP_LOG_ERROR((Formatter::format("Failed to compute tangents; need UV data in channel"), configSourceUV)); + ASSIMP_LOG_ERROR_F("Failed to compute tangents; need UV data in channel", configSourceUV); return false; } diff --git a/include/assimp/Logger.hpp b/include/assimp/Logger.hpp index 2c3ee1085..07a26c053 100644 --- a/include/assimp/Logger.hpp +++ b/include/assimp/Logger.hpp @@ -128,9 +128,11 @@ public: // ---------------------------------------------------------------------- /** @brief Writes an error message - * @param message Error message*/ - void error(const char* message); - void error(const std::string &message); + * @param message Info message*/ + template + void error(T&&... args) { + errorInternal(Assimp::Formatter::format(), std::forward(args)...); + } // ---------------------------------------------------------------------- /** @brief Set a new log severity. @@ -244,6 +246,13 @@ protected: infoInternal(std::move(f << std::forward(u)), std::forward(args)...); } + void errorInternal(Assimp::Formatter::format f); + + template + void errorInternal(Assimp::Formatter::format f, U&& u, T&&... args) { + errorInternal(std::move(f << std::forward(u)), std::forward(args)...); + } + protected: LogSeverity m_Severity; }; @@ -296,12 +305,6 @@ inline void Logger::verboseDebug(const std::string &message) { return verboseDebug(message.c_str()); } -// ---------------------------------------------------------------------------------- -inline -void Logger::error(const std::string &message) { - return error(message.c_str()); -} - } // Namespace Assimp // ------------------------------------------------------------------------------------------------ @@ -309,7 +312,7 @@ void Logger::error(const std::string &message) { Assimp::DefaultLogger::get()->warn((string, __VA_ARGS__)) #define ASSIMP_LOG_ERROR_F(string, ...) \ - Assimp::DefaultLogger::get()->error((Assimp::Formatter::format(string), __VA_ARGS__)) + Assimp::DefaultLogger::get()->error((string, __VA_ARGS__)) #define ASSIMP_LOG_DEBUG_F(string, ...) \ Assimp::DefaultLogger::get()->debug((Assimp::Formatter::format(string), __VA_ARGS__)) From 89584c167aaf341a6d717083bb9a52a4c1b0ace1 Mon Sep 17 00:00:00 2001 From: Malcolm Tyrrell Date: Thu, 13 May 2021 09:56:42 +0100 Subject: [PATCH 106/335] Log debug --- code/AssetLib/STEPParser/STEPFileReader.cpp | 4 +-- code/Common/DefaultLogger.cpp | 13 ++++---- .../SplitByBoneCountProcess.cpp | 4 +-- include/assimp/Logger.hpp | 31 ++++++++++--------- include/assimp/Profiler.h | 4 +-- 5 files changed, 28 insertions(+), 28 deletions(-) diff --git a/code/AssetLib/STEPParser/STEPFileReader.cpp b/code/AssetLib/STEPParser/STEPFileReader.cpp index cbf8c07c2..1ed5ab7d2 100644 --- a/code/AssetLib/STEPParser/STEPFileReader.cpp +++ b/code/AssetLib/STEPParser/STEPFileReader.cpp @@ -297,8 +297,8 @@ void STEP::ReadFile(DB& db,const EXPRESS::ConversionSchema& scheme, } if ( !DefaultLogger::isNullLogger()){ - ASSIMP_LOG_DEBUG((Formatter::format(),"STEP: got ",map.size()," object records with ", - db.GetRefs().size()," inverse index entries")); + ASSIMP_LOG_DEBUG_F("STEP: got ",map.size()," object records with ", + db.GetRefs().size()," inverse index entries"); } } diff --git a/code/Common/DefaultLogger.cpp b/code/Common/DefaultLogger.cpp index e468d72ca..03a0a80de 100644 --- a/code/Common/DefaultLogger.cpp +++ b/code/Common/DefaultLogger.cpp @@ -163,15 +163,14 @@ Logger *DefaultLogger::create(const char *name /*= "AssimpLog.txt"*/, } // ---------------------------------------------------------------------------------- -void Logger::debug(const char *message) { - - // SECURITY FIX: otherwise it's easy to produce overruns since - // sometimes importers will include data from the input file - // (i.e. node names) in their messages. - if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { +void Logger::debugInternal(Assimp::Formatter::format f) { + std::string message = f; + // TODO: Should limit sizes in the formatter. + // SECURITY FIX: see above + if (message.length() > MAX_LOG_MESSAGE_LENGTH) { return; } - return OnDebug(message); + return OnDebug(message.c_str()); } // ---------------------------------------------------------------------------------- diff --git a/code/PostProcessing/SplitByBoneCountProcess.cpp b/code/PostProcessing/SplitByBoneCountProcess.cpp index 1f4170a6a..f7dffb8f2 100644 --- a/code/PostProcessing/SplitByBoneCountProcess.cpp +++ b/code/PostProcessing/SplitByBoneCountProcess.cpp @@ -103,7 +103,7 @@ void SplitByBoneCountProcess::Execute( aiScene* pScene) if( !isNecessary ) { - ASSIMP_LOG_DEBUG( format() << "SplitByBoneCountProcess early-out: no meshes with more than " << mMaxBoneCount << " bones." ); + ASSIMP_LOG_DEBUG_F("SplitByBoneCountProcess early-out: no meshes with more than ", mMaxBoneCount, " bones." ); return; } @@ -151,7 +151,7 @@ void SplitByBoneCountProcess::Execute( aiScene* pScene) // recurse through all nodes and translate the node's mesh indices to fit the new mesh array UpdateNode( pScene->mRootNode); - ASSIMP_LOG_DEBUG( format() << "SplitByBoneCountProcess end: split " << mSubMeshIndices.size() << " meshes into " << meshes.size() << " submeshes." ); + ASSIMP_LOG_DEBUG_F( "SplitByBoneCountProcess end: split ", mSubMeshIndices.size(), " meshes into ", meshes.size(), " submeshes." ); } // ------------------------------------------------------------------------------------------------ diff --git a/include/assimp/Logger.hpp b/include/assimp/Logger.hpp index 07a26c053..475b19aa6 100644 --- a/include/assimp/Logger.hpp +++ b/include/assimp/Logger.hpp @@ -99,10 +99,12 @@ public: virtual ~Logger(); // ---------------------------------------------------------------------- - /** @brief Writes a debug message - * @param message Debug message*/ - void debug(const char* message); - void debug(const std::string &message); + /** @brief Writes a info message + * @param message Info message*/ + template + void debug(T&&... args) { + debugInternal(Assimp::Formatter::format(), std::forward(args)...); + } // ---------------------------------------------------------------------- /** @brief Writes a debug message @@ -232,22 +234,27 @@ protected: virtual void OnError(const char* message) = 0; protected: + + void debugInternal(Assimp::Formatter::format f); void warnInternal(Assimp::Formatter::format f); + void infoInternal(Assimp::Formatter::format f); + void errorInternal(Assimp::Formatter::format f); + + template + void debugInternal(Assimp::Formatter::format f, U&& u, T&&... args) { + warnInternal(std::move(f << std::forward(u)), std::forward(args)...); + } template void warnInternal(Assimp::Formatter::format f, U&& u, T&&... args) { warnInternal(std::move(f << std::forward(u)), std::forward(args)...); } - void infoInternal(Assimp::Formatter::format f); - template void infoInternal(Assimp::Formatter::format f, U&& u, T&&... args) { infoInternal(std::move(f << std::forward(u)), std::forward(args)...); } - void errorInternal(Assimp::Formatter::format f); - template void errorInternal(Assimp::Formatter::format f, U&& u, T&&... args) { errorInternal(std::move(f << std::forward(u)), std::forward(args)...); @@ -294,12 +301,6 @@ Logger::LogSeverity Logger::getLogSeverity() const { return m_Severity; } -// ---------------------------------------------------------------------------------- -inline -void Logger::debug(const std::string &message) { - return debug(message.c_str()); -} - // ---------------------------------------------------------------------------------- inline void Logger::verboseDebug(const std::string &message) { return verboseDebug(message.c_str()); @@ -315,7 +316,7 @@ inline void Logger::verboseDebug(const std::string &message) { Assimp::DefaultLogger::get()->error((string, __VA_ARGS__)) #define ASSIMP_LOG_DEBUG_F(string, ...) \ - Assimp::DefaultLogger::get()->debug((Assimp::Formatter::format(string), __VA_ARGS__)) + Assimp::DefaultLogger::get()->debug((string, __VA_ARGS__)) #define ASSIMP_LOG_VERBOSE_DEBUG_F(string, ...) \ Assimp::DefaultLogger::get()->verboseDebug((Assimp::Formatter::format(string), __VA_ARGS__)) diff --git a/include/assimp/Profiler.h b/include/assimp/Profiler.h index 5f9fa52aa..566ea84e1 100644 --- a/include/assimp/Profiler.h +++ b/include/assimp/Profiler.h @@ -76,7 +76,7 @@ public: /** Start a named timer */ void BeginRegion(const std::string& region) { regions[region] = std::chrono::system_clock::now(); - ASSIMP_LOG_DEBUG((format("START `"),region,"`")); + ASSIMP_LOG_DEBUG_F("START `",region,"`"); } @@ -88,7 +88,7 @@ public: } std::chrono::duration elapsedSeconds = std::chrono::system_clock::now() - regions[region]; - ASSIMP_LOG_DEBUG((format("END `"),region,"`, dt= ", elapsedSeconds.count()," s")); + ASSIMP_LOG_DEBUG_F("END `",region,"`, dt= ", elapsedSeconds.count()," s"); } private: From 78145f1425b0ad81f39c1a2c5c26e70fb9174749 Mon Sep 17 00:00:00 2001 From: Malcolm Tyrrell Date: Thu, 13 May 2021 10:08:59 +0100 Subject: [PATCH 107/335] log verboseDebug --- code/AssetLib/ASE/ASELoader.cpp | 2 +- code/AssetLib/COB/COBLoader.cpp | 3 +-- code/AssetLib/DXF/DXFHelper.h | 2 +- code/Common/DefaultLogger.cpp | 12 ++++++------ include/assimp/Logger.hpp | 21 ++++++++++++--------- 5 files changed, 21 insertions(+), 19 deletions(-) diff --git a/code/AssetLib/ASE/ASELoader.cpp b/code/AssetLib/ASE/ASELoader.cpp index e1b9c5b83..a05c3c5e9 100644 --- a/code/AssetLib/ASE/ASELoader.cpp +++ b/code/AssetLib/ASE/ASELoader.cpp @@ -614,7 +614,7 @@ void ASEImporter::AddNodes(const std::vector &nodes, node->mNumChildren++; // What we did is so great, it is at least worth a debug message - ASSIMP_LOG_VERBOSE_DEBUG("ASE: Generating separate target node (" + snode->mName + ")"); + ASSIMP_LOG_VERBOSE_DEBUG_F("ASE: Generating separate target node (", snode->mName, ")"); } } diff --git a/code/AssetLib/COB/COBLoader.cpp b/code/AssetLib/COB/COBLoader.cpp index a6e9d4218..29e8e9de1 100644 --- a/code/AssetLib/COB/COBLoader.cpp +++ b/code/AssetLib/COB/COBLoader.cpp @@ -301,8 +301,7 @@ aiNode *COBImporter::BuildNodes(const Node &root, const Scene &scin, aiScene *fi } std::unique_ptr defmat; if (!min) { - ASSIMP_LOG_VERBOSE_DEBUG(format() << "Could not resolve material index " - << reflist.first << " - creating default material for this slot"); + ASSIMP_LOG_VERBOSE_DEBUG_F("Could not resolve material index ", reflist.first, " - creating default material for this slot"); defmat.reset(min = new Material()); } diff --git a/code/AssetLib/DXF/DXFHelper.h b/code/AssetLib/DXF/DXFHelper.h index f7fc470e8..5099b43ee 100644 --- a/code/AssetLib/DXF/DXFHelper.h +++ b/code/AssetLib/DXF/DXFHelper.h @@ -135,7 +135,7 @@ public: for(;splitter->length() && splitter->at(0) != '}'; splitter++, cnt++); splitter++; - ASSIMP_LOG_VERBOSE_DEBUG((Formatter::format("DXF: skipped over control group ("),cnt," lines)")); + ASSIMP_LOG_VERBOSE_DEBUG_F("DXF: skipped over control group (",cnt," lines)"); } } catch(std::logic_error&) { ai_assert(!splitter); diff --git a/code/Common/DefaultLogger.cpp b/code/Common/DefaultLogger.cpp index 03a0a80de..e12276ea9 100644 --- a/code/Common/DefaultLogger.cpp +++ b/code/Common/DefaultLogger.cpp @@ -174,14 +174,14 @@ void Logger::debugInternal(Assimp::Formatter::format f) { } // ---------------------------------------------------------------------------------- -void Logger::verboseDebug(const char *message) { - // SECURITY FIX: otherwise it's easy to produce overruns since - // sometimes importers will include data from the input file - // (i.e. node names) in their messages. - if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { +void Logger::verboseDebugInternal(Assimp::Formatter::format f) { + std::string message = f; + // TODO: Should limit sizes in the formatter. + // SECURITY FIX: see above + if (message.length() > MAX_LOG_MESSAGE_LENGTH) { return; } - return OnVerboseDebug(message); + return OnVerboseDebug(message.c_str()); } // ---------------------------------------------------------------------------------- diff --git a/include/assimp/Logger.hpp b/include/assimp/Logger.hpp index 475b19aa6..3d1588f61 100644 --- a/include/assimp/Logger.hpp +++ b/include/assimp/Logger.hpp @@ -109,8 +109,10 @@ public: // ---------------------------------------------------------------------- /** @brief Writes a debug message * @param message Debug message*/ - void verboseDebug(const char *message); - void verboseDebug(const std::string &message); + template + void verboseDebug(T&&... args) { + verboseDebugInternal(Assimp::Formatter::format(), std::forward(args)...); + } // ---------------------------------------------------------------------- /** @brief Writes a info message @@ -236,13 +238,19 @@ protected: protected: void debugInternal(Assimp::Formatter::format f); + void verboseDebugInternal(Assimp::Formatter::format f); void warnInternal(Assimp::Formatter::format f); void infoInternal(Assimp::Formatter::format f); void errorInternal(Assimp::Formatter::format f); template void debugInternal(Assimp::Formatter::format f, U&& u, T&&... args) { - warnInternal(std::move(f << std::forward(u)), std::forward(args)...); + debugInternal(std::move(f << std::forward(u)), std::forward(args)...); + } + + template + void verboseDebugInternal(Assimp::Formatter::format f, U&& u, T&&... args) { + verboseDebugInternal(std::move(f << std::forward(u)), std::forward(args)...); } template @@ -301,11 +309,6 @@ Logger::LogSeverity Logger::getLogSeverity() const { return m_Severity; } -// ---------------------------------------------------------------------------------- -inline void Logger::verboseDebug(const std::string &message) { - return verboseDebug(message.c_str()); -} - } // Namespace Assimp // ------------------------------------------------------------------------------------------------ @@ -319,7 +322,7 @@ inline void Logger::verboseDebug(const std::string &message) { Assimp::DefaultLogger::get()->debug((string, __VA_ARGS__)) #define ASSIMP_LOG_VERBOSE_DEBUG_F(string, ...) \ - Assimp::DefaultLogger::get()->verboseDebug((Assimp::Formatter::format(string), __VA_ARGS__)) + Assimp::DefaultLogger::get()->verboseDebug((string, __VA_ARGS__)) #define ASSIMP_LOG_INFO_F(string, ...) \ Assimp::DefaultLogger::get()->info((string, __VA_ARGS__)) From 6957d3473396c9f94f6fd7456a8718293da89831 Mon Sep 17 00:00:00 2001 From: Malcolm Tyrrell Date: Thu, 13 May 2021 10:14:24 +0100 Subject: [PATCH 108/335] Simplify the macros. --- include/assimp/Logger.hpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/include/assimp/Logger.hpp b/include/assimp/Logger.hpp index 3d1588f61..71f458731 100644 --- a/include/assimp/Logger.hpp +++ b/include/assimp/Logger.hpp @@ -312,20 +312,20 @@ Logger::LogSeverity Logger::getLogSeverity() const { } // Namespace Assimp // ------------------------------------------------------------------------------------------------ -#define ASSIMP_LOG_WARN_F(string, ...) \ - Assimp::DefaultLogger::get()->warn((string, __VA_ARGS__)) +#define ASSIMP_LOG_WARN_F(...) \ + Assimp::DefaultLogger::get()->warn(__VA_ARGS__) -#define ASSIMP_LOG_ERROR_F(string, ...) \ - Assimp::DefaultLogger::get()->error((string, __VA_ARGS__)) +#define ASSIMP_LOG_ERROR_F(...) \ + Assimp::DefaultLogger::get()->error(__VA_ARGS__) -#define ASSIMP_LOG_DEBUG_F(string, ...) \ - Assimp::DefaultLogger::get()->debug((string, __VA_ARGS__)) +#define ASSIMP_LOG_DEBUG_F(...) \ + Assimp::DefaultLogger::get()->debug(__VA_ARGS__) -#define ASSIMP_LOG_VERBOSE_DEBUG_F(string, ...) \ - Assimp::DefaultLogger::get()->verboseDebug((string, __VA_ARGS__)) +#define ASSIMP_LOG_VERBOSE_DEBUG_F(...) \ + Assimp::DefaultLogger::get()->verboseDebug(__VA_ARGS__) -#define ASSIMP_LOG_INFO_F(string, ...) \ - Assimp::DefaultLogger::get()->info((string, __VA_ARGS__)) +#define ASSIMP_LOG_INFO_F(...) \ + Assimp::DefaultLogger::get()->info(__VA_ARGS__) #define ASSIMP_LOG_WARN(string) \ Assimp::DefaultLogger::get()->warn(string) From 5cd3bdd5c22c1695174cd92639333ef5e5d30291 Mon Sep 17 00:00:00 2001 From: Malcolm Tyrrell Date: Thu, 13 May 2021 10:25:27 +0100 Subject: [PATCH 109/335] No need to distinguish formatting log functions. --- code/AssetLib/3DS/3DSLoader.cpp | 4 +-- code/AssetLib/3MF/D3MFOpcPackage.cpp | 4 +-- code/AssetLib/AC/ACLoader.cpp | 8 ++--- code/AssetLib/ASE/ASELoader.cpp | 2 +- code/AssetLib/ASE/ASEParser.cpp | 2 +- code/AssetLib/B3D/B3DImporter.cpp | 8 ++--- code/AssetLib/Blender/BlenderDNA.cpp | 2 +- code/AssetLib/Blender/BlenderDNA.inl | 2 +- code/AssetLib/Blender/BlenderLoader.cpp | 2 +- code/AssetLib/Blender/BlenderModifier.cpp | 12 +++---- code/AssetLib/Blender/BlenderModifier.h | 2 +- code/AssetLib/COB/COBLoader.cpp | 36 +++++++++---------- code/AssetLib/Collada/ColladaLoader.cpp | 16 ++++----- code/AssetLib/Collada/ColladaParser.cpp | 4 +-- code/AssetLib/DXF/DXFHelper.h | 2 +- code/AssetLib/DXF/DXFLoader.cpp | 14 ++++---- code/AssetLib/FBX/FBXBinaryTokenizer.cpp | 2 +- code/AssetLib/FBX/FBXConverter.cpp | 6 ++-- code/AssetLib/FBX/FBXDocument.cpp | 2 +- code/AssetLib/FBX/FBXDocumentUtil.cpp | 2 +- code/AssetLib/FBX/FBXMaterial.cpp | 2 +- code/AssetLib/LWO/LWOBLoader.cpp | 2 +- code/AssetLib/LWO/LWOLoader.cpp | 8 ++--- code/AssetLib/LWO/LWOMaterial.cpp | 2 +- code/AssetLib/M3D/M3DImporter.cpp | 14 ++++---- code/AssetLib/MD3/MD3Loader.cpp | 6 ++-- code/AssetLib/MS3D/MS3DLoader.cpp | 2 +- code/AssetLib/NDO/NDOLoader.cpp | 2 +- code/AssetLib/NFF/NFFLoader.cpp | 8 ++--- code/AssetLib/Ogre/OgreBinarySerializer.cpp | 28 +++++++-------- code/AssetLib/Ogre/OgreMaterial.cpp | 30 ++++++++-------- code/AssetLib/Ogre/OgreStructs.cpp | 4 +-- code/AssetLib/Ogre/OgreXmlSerializer.cpp | 22 ++++++------ code/AssetLib/OpenGEX/OpenGEXImporter.cpp | 2 +- code/AssetLib/Q3D/Q3DLoader.cpp | 2 +- code/AssetLib/SIB/SIBImporter.cpp | 2 +- code/AssetLib/STEPParser/STEPFileReader.cpp | 2 +- code/AssetLib/Unreal/UnrealLoader.cpp | 6 ++-- code/AssetLib/X/XFileImporter.cpp | 2 +- code/AssetLib/glTF/glTFExporter.cpp | 2 +- code/AssetLib/glTF2/glTF2Importer.cpp | 18 +++++----- code/Common/BaseImporter.cpp | 6 ++-- code/Common/DefaultIOSystem.cpp | 2 +- code/Common/Importer.cpp | 6 ++-- code/Common/SceneCombiner.cpp | 2 +- code/Common/Subdivision.cpp | 2 +- code/PostProcessing/ArmaturePopulate.cpp | 12 +++---- code/PostProcessing/CalcTangentsProcess.cpp | 2 +- code/PostProcessing/DeboneProcess.cpp | 2 +- code/PostProcessing/EmbedTexturesProcess.cpp | 6 ++-- code/PostProcessing/FindDegenerates.cpp | 2 +- code/PostProcessing/FindInstancesProcess.cpp | 2 +- .../PostProcessing/FindInvalidDataProcess.cpp | 2 +- code/PostProcessing/FixNormalsStep.cpp | 2 +- code/PostProcessing/ImproveCacheLocality.cpp | 4 +-- code/PostProcessing/JoinVerticesProcess.cpp | 4 +-- .../LimitBoneWeightsProcess.cpp | 2 +- code/PostProcessing/OptimizeGraph.cpp | 2 +- code/PostProcessing/OptimizeMeshes.cpp | 2 +- code/PostProcessing/PretransformVertices.cpp | 6 ++-- .../RemoveRedundantMaterials.cpp | 4 +-- .../SplitByBoneCountProcess.cpp | 4 +-- code/PostProcessing/TextureTransform.cpp | 6 ++-- include/assimp/LineSplitter.h | 2 +- include/assimp/Logger.hpp | 25 +++---------- include/assimp/Profiler.h | 4 +-- include/assimp/fast_atof.h | 2 +- 67 files changed, 199 insertions(+), 214 deletions(-) diff --git a/code/AssetLib/3DS/3DSLoader.cpp b/code/AssetLib/3DS/3DSLoader.cpp index 2d77e0f66..b5e6f749b 100644 --- a/code/AssetLib/3DS/3DSLoader.cpp +++ b/code/AssetLib/3DS/3DSLoader.cpp @@ -305,7 +305,7 @@ void Discreet3DSImporter::ParseEditorChunk() { // print the version number char buff[10]; ASSIMP_itoa10(buff, stream->GetI2()); - ASSIMP_LOG_INFO_F("3DS file format version: ", buff); + ASSIMP_LOG_INFO("3DS file format version: ", buff); } break; }; ASSIMP_3DS_END_CHUNK(); @@ -934,7 +934,7 @@ void Discreet3DSImporter::ParseFaceChunk() { } } if (0xcdcdcdcd == idx) { - ASSIMP_LOG_ERROR_F("3DS: Unknown material: ", sz); + ASSIMP_LOG_ERROR("3DS: Unknown material: ", sz); } // Now continue and read all material indices diff --git a/code/AssetLib/3MF/D3MFOpcPackage.cpp b/code/AssetLib/3MF/D3MFOpcPackage.cpp index d5bbcd618..dbf4f2e10 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.cpp +++ b/code/AssetLib/3MF/D3MFOpcPackage.cpp @@ -160,9 +160,9 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : } } else if (file == D3MF::XmlTag::CONTENT_TYPES_ARCHIVE) { - ASSIMP_LOG_WARN_F("Ignored file of unsupported type CONTENT_TYPES_ARCHIVES", file); + ASSIMP_LOG_WARN("Ignored file of unsupported type CONTENT_TYPES_ARCHIVES", file); } else { - ASSIMP_LOG_WARN_F("Ignored file of unknown type: ", file); + ASSIMP_LOG_WARN("Ignored file of unknown type: ", file); } } } diff --git a/code/AssetLib/AC/ACLoader.cpp b/code/AssetLib/AC/ACLoader.cpp index 59cfc30dd..974e77825 100644 --- a/code/AssetLib/AC/ACLoader.cpp +++ b/code/AssetLib/AC/ACLoader.cpp @@ -492,7 +492,7 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object, default: // Coerce unknowns to a polygon and warn - ASSIMP_LOG_WARN_F("AC3D: The type flag of a surface is unknown: ", (*it).flags); + ASSIMP_LOG_WARN("AC3D: The type flag of a surface is unknown: ", (*it).flags); (*it).flags &= ~(Surface::Mask); // fallthrough @@ -690,7 +690,7 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object, if (object.subDiv) { if (configEvalSubdivision) { std::unique_ptr div(Subdivider::Create(Subdivider::CATMULL_CLARKE)); - ASSIMP_LOG_INFO_F("AC3D: Evaluating subdivision surface: ", object.name); + ASSIMP_LOG_INFO("AC3D: Evaluating subdivision surface: ", object.name); std::vector cpy(meshes.size() - oldm, nullptr); div->Subdivide(&meshes[oldm], cpy.size(), &cpy.front(), object.subDiv, true); @@ -698,7 +698,7 @@ aiNode *AC3DImporter::ConvertObjectSection(Object &object, // previous meshes are deleted vy Subdivide(). } else { - ASSIMP_LOG_INFO_F("AC3D: Letting the subdivision surface untouched due to my configuration: ", object.name); + ASSIMP_LOG_INFO("AC3D: Letting the subdivision surface untouched due to my configuration: ", object.name); } } } @@ -782,7 +782,7 @@ void AC3DImporter::InternReadFile(const std::string &pFile, unsigned int version = HexDigitToDecimal(buffer[4]); char msg[3]; ASSIMP_itoa10(msg, 3, version); - ASSIMP_LOG_INFO_F("AC3D file format version: ", msg); + ASSIMP_LOG_INFO("AC3D file format version: ", msg); std::vector materials; materials.reserve(5); diff --git a/code/AssetLib/ASE/ASELoader.cpp b/code/AssetLib/ASE/ASELoader.cpp index a05c3c5e9..1a42c2f52 100644 --- a/code/AssetLib/ASE/ASELoader.cpp +++ b/code/AssetLib/ASE/ASELoader.cpp @@ -614,7 +614,7 @@ void ASEImporter::AddNodes(const std::vector &nodes, node->mNumChildren++; // What we did is so great, it is at least worth a debug message - ASSIMP_LOG_VERBOSE_DEBUG_F("ASE: Generating separate target node (", snode->mName, ")"); + ASSIMP_LOG_VERBOSE_DEBUG("ASE: Generating separate target node (", snode->mName, ")"); } } diff --git a/code/AssetLib/ASE/ASEParser.cpp b/code/AssetLib/ASE/ASEParser.cpp index 00155805f..4e4af8ed8 100644 --- a/code/AssetLib/ASE/ASEParser.cpp +++ b/code/AssetLib/ASE/ASEParser.cpp @@ -677,7 +677,7 @@ void Parser::ParseLV3MapBlock(Texture &map) { if (!ParseString(temp, "*MAP_CLASS")) SkipToNextToken(); if (temp != "Bitmap" && temp != "Normal Bump") { - ASSIMP_LOG_WARN_F("ASE: Skipping unknown map type: ", temp); + ASSIMP_LOG_WARN("ASE: Skipping unknown map type: ", temp); parsePath = false; } continue; diff --git a/code/AssetLib/B3D/B3DImporter.cpp b/code/AssetLib/B3D/B3DImporter.cpp index bcdb286be..3d9a5075a 100644 --- a/code/AssetLib/B3D/B3DImporter.cpp +++ b/code/AssetLib/B3D/B3DImporter.cpp @@ -145,7 +145,7 @@ AI_WONT_RETURN void B3DImporter::Oops() { // ------------------------------------------------------------------------------------------------ AI_WONT_RETURN void B3DImporter::Fail(string str) { #ifdef DEBUG_B3D - ASSIMP_LOG_ERROR_F("Error in B3D file data: ", str); + ASSIMP_LOG_ERROR("Error in B3D file data: ", str); #endif throw DeadlyImportError("B3D Importer - error in B3D file data: ", str); } @@ -233,7 +233,7 @@ string B3DImporter::ReadChunk() { tag += char(ReadByte()); } #ifdef DEBUG_B3D - ASSIMP_LOG_DEBUG_F("ReadChunk: ", tag); + ASSIMP_LOG_DEBUG("ReadChunk: ", tag); #endif unsigned sz = (unsigned)ReadInt(); _stack.push_back(_pos + sz); @@ -397,7 +397,7 @@ void B3DImporter::ReadTRIS(int v0) { matid = 0; } else if (matid < 0 || matid >= (int)_materials.size()) { #ifdef DEBUG_B3D - ASSIMP_LOG_ERROR_F("material id=", matid); + ASSIMP_LOG_ERROR("material id=", matid); #endif Fail("Bad material id"); } @@ -417,7 +417,7 @@ void B3DImporter::ReadTRIS(int v0) { int i2 = ReadInt() + v0; if (i0 < 0 || i0 >= (int)_vertices.size() || i1 < 0 || i1 >= (int)_vertices.size() || i2 < 0 || i2 >= (int)_vertices.size()) { #ifdef DEBUG_B3D - ASSIMP_LOG_ERROR_F("Bad triangle index: i0=", i0, ", i1=", i1, ", i2=", i2); + ASSIMP_LOG_ERROR("Bad triangle index: i0=", i0, ", i1=", i1, ", i2=", i2); #endif Fail("Bad triangle index"); continue; diff --git a/code/AssetLib/Blender/BlenderDNA.cpp b/code/AssetLib/Blender/BlenderDNA.cpp index 4dcb3d654..99e9173aa 100644 --- a/code/AssetLib/Blender/BlenderDNA.cpp +++ b/code/AssetLib/Blender/BlenderDNA.cpp @@ -198,7 +198,7 @@ void DNAParser::Parse() { s.size = offset; } - ASSIMP_LOG_DEBUG_F("BlenderDNA: Got ", dna.structures.size(), " structures with totally ", fields, " fields"); + ASSIMP_LOG_DEBUG("BlenderDNA: Got ", dna.structures.size(), " structures with totally ", fields, " fields"); #ifdef ASSIMP_BUILD_BLENDER_DEBUG dna.DumpToFile(); diff --git a/code/AssetLib/Blender/BlenderDNA.inl b/code/AssetLib/Blender/BlenderDNA.inl index aebcefa84..a46532057 100644 --- a/code/AssetLib/Blender/BlenderDNA.inl +++ b/code/AssetLib/Blender/BlenderDNA.inl @@ -565,7 +565,7 @@ template <> bool Structure :: ResolvePointer(std::shar // this might happen if DNA::RegisterConverters hasn't been called so far // or if the target type is not contained in `our` DNA. out.reset(); - ASSIMP_LOG_WARN_F( "Failed to find a converter for the `",s.name,"` structure" ); + ASSIMP_LOG_WARN( "Failed to find a converter for the `",s.name,"` structure" ); return false; } diff --git a/code/AssetLib/Blender/BlenderLoader.cpp b/code/AssetLib/Blender/BlenderLoader.cpp index 56f4e985f..8cc0162f5 100644 --- a/code/AssetLib/Blender/BlenderLoader.cpp +++ b/code/AssetLib/Blender/BlenderLoader.cpp @@ -310,7 +310,7 @@ void BlenderImporter::ExtractScene(Scene &out, const FileDatabase &file) { ss.Convert(out, file); #ifndef ASSIMP_BUILD_BLENDER_NO_STATS - ASSIMP_LOG_INFO_F( + ASSIMP_LOG_INFO( "(Stats) Fields read: ", file.stats().fields_read, ", pointers resolved: ", file.stats().pointers_resolved, ", cache hits: ", file.stats().cache_hits, diff --git a/code/AssetLib/Blender/BlenderModifier.cpp b/code/AssetLib/Blender/BlenderModifier.cpp index 995e20b1c..bc5c8c2d9 100644 --- a/code/AssetLib/Blender/BlenderModifier.cpp +++ b/code/AssetLib/Blender/BlenderModifier.cpp @@ -90,7 +90,7 @@ void BlenderModifierShowcase::ApplyModifiers(aiNode &out, ConversionData &conv_d const Structure *s = conv_data.db.dna.Get(cur->dna_type); if (!s) { - ASSIMP_LOG_WARN_F("BlendModifier: could not resolve DNA name: ", cur->dna_type); + ASSIMP_LOG_WARN("BlendModifier: could not resolve DNA name: ", cur->dna_type); continue; } @@ -132,7 +132,7 @@ void BlenderModifierShowcase::ApplyModifiers(aiNode &out, ConversionData &conv_d } } if (curgod) { - ASSIMP_LOG_WARN_F("Couldn't find a handler for modifier: ", dat.name); + ASSIMP_LOG_WARN("Couldn't find a handler for modifier: ", dat.name); } } @@ -140,7 +140,7 @@ void BlenderModifierShowcase::ApplyModifiers(aiNode &out, ConversionData &conv_d // object, we still can't say whether our modifier implementations were // able to fully do their job. if (ful) { - ASSIMP_LOG_DEBUG_F("BlendModifier: found handlers for ", cnt, " of ", ful, " modifiers on `", orig_object.id.name, + ASSIMP_LOG_DEBUG("BlendModifier: found handlers for ", cnt, " of ", ful, " modifiers on `", orig_object.id.name, "`, check log messages above for errors"); } } @@ -248,7 +248,7 @@ void BlenderModifier_Mirror ::DoIt(aiNode &out, ConversionData &conv_data, const out.mMeshes = nind; out.mNumMeshes *= 2; - ASSIMP_LOG_INFO_F("BlendModifier: Applied the `Mirror` modifier to `", + ASSIMP_LOG_INFO("BlendModifier: Applied the `Mirror` modifier to `", orig_object.id.name, "`"); } @@ -277,7 +277,7 @@ void BlenderModifier_Subdivision ::DoIt(aiNode &out, ConversionData &conv_data, break; default: - ASSIMP_LOG_WARN_F("BlendModifier: Unrecognized subdivision algorithm: ", mir.subdivType); + ASSIMP_LOG_WARN("BlendModifier: Unrecognized subdivision algorithm: ", mir.subdivType); return; }; @@ -292,7 +292,7 @@ void BlenderModifier_Subdivision ::DoIt(aiNode &out, ConversionData &conv_data, subd->Subdivide(meshes, out.mNumMeshes, tempmeshes.get(), std::max(mir.renderLevels, mir.levels), true); std::copy(tempmeshes.get(), tempmeshes.get() + out.mNumMeshes, meshes); - ASSIMP_LOG_INFO_F("BlendModifier: Applied the `Subdivision` modifier to `", + ASSIMP_LOG_INFO("BlendModifier: Applied the `Subdivision` modifier to `", orig_object.id.name, "`"); } diff --git a/code/AssetLib/Blender/BlenderModifier.h b/code/AssetLib/Blender/BlenderModifier.h index 7d4096cde..daf120087 100644 --- a/code/AssetLib/Blender/BlenderModifier.h +++ b/code/AssetLib/Blender/BlenderModifier.h @@ -86,7 +86,7 @@ public: const Scene& /*in*/, const Object& /*orig_object*/ ) { - ASSIMP_LOG_INFO_F("This modifier is not supported, skipping: ",orig_modifier.dna_type ); + ASSIMP_LOG_INFO("This modifier is not supported, skipping: ",orig_modifier.dna_type ); return; } }; diff --git a/code/AssetLib/COB/COBLoader.cpp b/code/AssetLib/COB/COBLoader.cpp index 29e8e9de1..94327c683 100644 --- a/code/AssetLib/COB/COBLoader.cpp +++ b/code/AssetLib/COB/COBLoader.cpp @@ -152,7 +152,7 @@ void COBImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy ThrowException("Could not found magic id: `Caligari`"); } - ASSIMP_LOG_INFO_F("File format tag: ", std::string(head + 9, 6)); + ASSIMP_LOG_INFO("File format tag: ", std::string(head + 9, 6)); if (head[16] != 'L') { ThrowException("File is big-endian, which is not supported"); } @@ -301,7 +301,7 @@ aiNode *COBImporter::BuildNodes(const Node &root, const Scene &scin, aiScene *fi } std::unique_ptr defmat; if (!min) { - ASSIMP_LOG_VERBOSE_DEBUG_F("Could not resolve material index ", reflist.first, " - creating default material for this slot"); + ASSIMP_LOG_VERBOSE_DEBUG("Could not resolve material index ", reflist.first, " - creating default material for this slot"); defmat.reset(min = new Material()); } @@ -526,7 +526,7 @@ void COBImporter::ReadMat1_Ascii(Scene &out, LineSplitter &splitter, const Chunk ++splitter; if (!splitter.match_start("mat# ")) { - ASSIMP_LOG_WARN_F("Expected `mat#` line in `Mat1` chunk ", nfo.id); + ASSIMP_LOG_WARN("Expected `mat#` line in `Mat1` chunk ", nfo.id); return; } @@ -538,7 +538,7 @@ void COBImporter::ReadMat1_Ascii(Scene &out, LineSplitter &splitter, const Chunk ++splitter; if (!splitter.match_start("shader: ")) { - ASSIMP_LOG_WARN_F("Expected `mat#` line in `Mat1` chunk ", nfo.id); + ASSIMP_LOG_WARN("Expected `mat#` line in `Mat1` chunk ", nfo.id); return; } std::string shader = std::string(splitter[1]); @@ -549,12 +549,12 @@ void COBImporter::ReadMat1_Ascii(Scene &out, LineSplitter &splitter, const Chunk } else if (shader == "phong") { mat.shader = Material::PHONG; } else if (shader != "flat") { - ASSIMP_LOG_WARN_F("Unknown value for `shader` in `Mat1` chunk ", nfo.id); + ASSIMP_LOG_WARN("Unknown value for `shader` in `Mat1` chunk ", nfo.id); } ++splitter; if (!splitter.match_start("rgb ")) { - ASSIMP_LOG_WARN_F("Expected `rgb` line in `Mat1` chunk ", nfo.id); + ASSIMP_LOG_WARN("Expected `rgb` line in `Mat1` chunk ", nfo.id); } const char *rgb = splitter[1]; @@ -562,7 +562,7 @@ void COBImporter::ReadMat1_Ascii(Scene &out, LineSplitter &splitter, const Chunk ++splitter; if (!splitter.match_start("alpha ")) { - ASSIMP_LOG_WARN_F("Expected `alpha` line in `Mat1` chunk ", nfo.id); + ASSIMP_LOG_WARN("Expected `alpha` line in `Mat1` chunk ", nfo.id); } const char *tokens[10]; @@ -582,7 +582,7 @@ void COBImporter::ReadUnit_Ascii(Scene &out, LineSplitter &splitter, const Chunk } ++splitter; if (!splitter.match_start("Units ")) { - ASSIMP_LOG_WARN_F("Expected `Units` line in `Unit` chunk ", nfo.id); + ASSIMP_LOG_WARN("Expected `Units` line in `Unit` chunk ", nfo.id); return; } @@ -593,12 +593,12 @@ void COBImporter::ReadUnit_Ascii(Scene &out, LineSplitter &splitter, const Chunk const unsigned int t = strtoul10(splitter[1]); nd->unit_scale = t >= sizeof(units) / sizeof(units[0]) ? ( - ASSIMP_LOG_WARN_F(t, " is not a valid value for `Units` attribute in `Unit chunk` ", nfo.id), 1.f) : + ASSIMP_LOG_WARN(t, " is not a valid value for `Units` attribute in `Unit chunk` ", nfo.id), 1.f) : units[t]; return; } } - ASSIMP_LOG_WARN_F("`Unit` chunk ", nfo.id, " is a child of ", nfo.parent_id, " which does not exist"); + ASSIMP_LOG_WARN("`Unit` chunk ", nfo.id, " is a child of ", nfo.parent_id, " which does not exist"); } // ------------------------------------------------------------------------------------------------ @@ -627,13 +627,13 @@ void COBImporter::ReadLght_Ascii(Scene &out, LineSplitter &splitter, const Chunk } else if (splitter.match_start("Spot ")) { msh.ltype = Light::SPOT; } else { - ASSIMP_LOG_WARN_F("Unknown kind of light source in `Lght` chunk ", nfo.id, " : ", *splitter); + ASSIMP_LOG_WARN("Unknown kind of light source in `Lght` chunk ", nfo.id, " : ", *splitter); msh.ltype = Light::SPOT; } ++splitter; if (!splitter.match_start("color ")) { - ASSIMP_LOG_WARN_F("Expected `color` line in `Lght` chunk ", nfo.id); + ASSIMP_LOG_WARN("Expected `color` line in `Lght` chunk ", nfo.id); } const char *rgb = splitter[1]; @@ -641,14 +641,14 @@ void COBImporter::ReadLght_Ascii(Scene &out, LineSplitter &splitter, const Chunk SkipSpaces(&rgb); if (strncmp(rgb, "cone angle", 10) != 0) { - ASSIMP_LOG_WARN_F("Expected `cone angle` entity in `color` line in `Lght` chunk ", nfo.id); + ASSIMP_LOG_WARN("Expected `cone angle` entity in `color` line in `Lght` chunk ", nfo.id); } SkipSpaces(rgb + 10, &rgb); msh.angle = fast_atof(&rgb); SkipSpaces(&rgb); if (strncmp(rgb, "inner angle", 11) != 0) { - ASSIMP_LOG_WARN_F("Expected `inner angle` entity in `color` line in `Lght` chunk ", nfo.id); + ASSIMP_LOG_WARN("Expected `inner angle` entity in `color` line in `Lght` chunk ", nfo.id); } SkipSpaces(rgb + 11, &rgb); msh.inner_angle = fast_atof(&rgb); @@ -1032,7 +1032,7 @@ void COBImporter::ReadMat1_Binary(COB::Scene &out, StreamReaderLE &reader, const mat.type = Material::METAL; break; default: - ASSIMP_LOG_ERROR_F("Unrecognized shader type in `Mat1` chunk with id ", nfo.id); + ASSIMP_LOG_ERROR("Unrecognized shader type in `Mat1` chunk with id ", nfo.id); mat.type = Material::FLAT; } @@ -1047,7 +1047,7 @@ void COBImporter::ReadMat1_Binary(COB::Scene &out, StreamReaderLE &reader, const mat.autofacet = Material::SMOOTH; break; default: - ASSIMP_LOG_ERROR_F("Unrecognized faceting mode in `Mat1` chunk with id ", nfo.id); + ASSIMP_LOG_ERROR("Unrecognized faceting mode in `Mat1` chunk with id ", nfo.id); mat.autofacet = Material::FACETED; } mat.autofacet_angle = static_cast(reader.GetI1()); @@ -1175,13 +1175,13 @@ void COBImporter::ReadUnit_Binary(COB::Scene &out, StreamReaderLE &reader, const if (nd->id == nfo.parent_id) { const unsigned int t = reader.GetI2(); nd->unit_scale = t >= sizeof(units) / sizeof(units[0]) ? ( - ASSIMP_LOG_WARN_F(t, " is not a valid value for `Units` attribute in `Unit chunk` ", nfo.id), 1.f) : + ASSIMP_LOG_WARN(t, " is not a valid value for `Units` attribute in `Unit chunk` ", nfo.id), 1.f) : units[t]; return; } } - ASSIMP_LOG_WARN_F("`Unit` chunk ", nfo.id, " is a child of ", nfo.parent_id, " which does not exist"); + ASSIMP_LOG_WARN("`Unit` chunk ", nfo.id, " is a child of ", nfo.parent_id, " which does not exist"); } #endif // ASSIMP_BUILD_NO_COB_IMPORTER diff --git a/code/AssetLib/Collada/ColladaLoader.cpp b/code/AssetLib/Collada/ColladaLoader.cpp index 736d20ce5..492e6971f 100644 --- a/code/AssetLib/Collada/ColladaLoader.cpp +++ b/code/AssetLib/Collada/ColladaLoader.cpp @@ -317,7 +317,7 @@ void ColladaLoader::ResolveNodeInstances(const ColladaParser &pParser, const Nod nd = FindNode(pParser.mRootNode, nodeInst.mNode); } if (nullptr == nd) { - ASSIMP_LOG_ERROR_F("Collada: Unable to resolve reference to instanced node ", nodeInst.mNode); + ASSIMP_LOG_ERROR("Collada: Unable to resolve reference to instanced node ", nodeInst.mNode); } else { // attach this node to the list of children resolved.push_back(nd); @@ -347,7 +347,7 @@ void ColladaLoader::BuildLightsForNode(const ColladaParser &pParser, const Node // find the referred light ColladaParser::LightLibrary::const_iterator srcLightIt = pParser.mLightLibrary.find(lid.mLight); if (srcLightIt == pParser.mLightLibrary.end()) { - ASSIMP_LOG_WARN_F("Collada: Unable to find light for ID \"", lid.mLight, "\". Skipping."); + ASSIMP_LOG_WARN("Collada: Unable to find light for ID \"", lid.mLight, "\". Skipping."); continue; } const Collada::Light *srcLight = &srcLightIt->second; @@ -412,7 +412,7 @@ void ColladaLoader::BuildCamerasForNode(const ColladaParser &pParser, const Node // find the referred light ColladaParser::CameraLibrary::const_iterator srcCameraIt = pParser.mCameraLibrary.find(cid.mCamera); if (srcCameraIt == pParser.mCameraLibrary.end()) { - ASSIMP_LOG_WARN_F("Collada: Unable to find camera for ID \"", cid.mCamera, "\". Skipping."); + ASSIMP_LOG_WARN("Collada: Unable to find camera for ID \"", cid.mCamera, "\". Skipping."); continue; } const Collada::Camera *srcCamera = &srcCameraIt->second; @@ -486,7 +486,7 @@ void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Node } if (nullptr == srcMesh) { - ASSIMP_LOG_WARN_F("Collada: Unable to find geometry for ID \"", mid.mMeshOrController, "\". Skipping."); + ASSIMP_LOG_WARN("Collada: Unable to find geometry for ID \"", mid.mMeshOrController, "\". Skipping."); continue; } } else { @@ -511,7 +511,7 @@ void ColladaLoader::BuildMeshesForNode(const ColladaParser &pParser, const Node table = &meshMatIt->second; meshMaterial = table->mMatName; } else { - ASSIMP_LOG_WARN_F("Collada: No material specified for subgroup <", submesh.mMaterial, "> in geometry <", + ASSIMP_LOG_WARN("Collada: No material specified for subgroup <", submesh.mMaterial, "> in geometry <", mid.mMeshOrController, ">."); if (!mid.mMaterials.empty()) { meshMaterial = mid.mMaterials.begin()->second.mMatName; @@ -883,7 +883,7 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrc if (nullptr != bnode) { bone->mName.Set(FindNameForNode(bnode)); } else { - ASSIMP_LOG_WARN_F("ColladaLoader::CreateMesh(): could not find corresponding node for joint \"", bone->mName.data, "\"."); + ASSIMP_LOG_WARN("ColladaLoader::CreateMesh(): could not find corresponding node for joint \"", bone->mName.data, "\"."); } // and insert bone @@ -1184,7 +1184,7 @@ void ColladaLoader::CreateAnimation(aiScene *pScene, const ColladaParser &pParse else if (subElement == "Z") entry.mSubElement = 2; else - ASSIMP_LOG_WARN_F("Unknown anim subelement <", subElement, ">. Ignoring"); + ASSIMP_LOG_WARN("Unknown anim subelement <", subElement, ">. Ignoring"); } else { // no sub-element following, transformId is remaining string entry.mTransformId = srcChannel.mTarget.substr(slashPos + 1); @@ -1711,7 +1711,7 @@ aiString ColladaLoader::FindFilenameForEffectTexture(const ColladaParser &pParse // find the image referred by this name in the image library of the scene ColladaParser::ImageLibrary::const_iterator imIt = pParser.mImageLibrary.find(name); if (imIt == pParser.mImageLibrary.end()) { - ASSIMP_LOG_WARN_F("Collada: Unable to resolve effect texture entry \"", pName, "\", ended up at ID \"", name, "\"."); + ASSIMP_LOG_WARN("Collada: Unable to resolve effect texture entry \"", pName, "\", ended up at ID \"", name, "\"."); //set default texture file name result.Set(name + ".jpg"); diff --git a/code/AssetLib/Collada/ColladaParser.cpp b/code/AssetLib/Collada/ColladaParser.cpp index 1ef109f11..fc9a45802 100644 --- a/code/AssetLib/Collada/ColladaParser.cpp +++ b/code/AssetLib/Collada/ColladaParser.cpp @@ -71,7 +71,7 @@ static void ReportWarning(const char *msg, ...) { ai_assert(iLen > 0); va_end(args); - ASSIMP_LOG_WARN_F("Validation warning: ", std::string(szBuffer, iLen)); + ASSIMP_LOG_WARN("Validation warning: ", std::string(szBuffer, iLen)); } static bool FindCommonKey(const std::string &collada_key, const MetaKeyPairVector &key_renaming, size_t &found_index) { @@ -2400,7 +2400,7 @@ Collada::InputType ColladaParser::GetTypeForSemantic(const std::string &semantic else if (semantic == "TANGENT" || semantic == "TEXTANGENT") return IT_Tangent; - ASSIMP_LOG_WARN_F("Unknown vertex input type \"", semantic, "\". Ignoring."); + ASSIMP_LOG_WARN("Unknown vertex input type \"", semantic, "\". Ignoring."); return IT_Invalid; } diff --git a/code/AssetLib/DXF/DXFHelper.h b/code/AssetLib/DXF/DXFHelper.h index 5099b43ee..c7e75c3b1 100644 --- a/code/AssetLib/DXF/DXFHelper.h +++ b/code/AssetLib/DXF/DXFHelper.h @@ -135,7 +135,7 @@ public: for(;splitter->length() && splitter->at(0) != '}'; splitter++, cnt++); splitter++; - ASSIMP_LOG_VERBOSE_DEBUG_F("DXF: skipped over control group (",cnt," lines)"); + ASSIMP_LOG_VERBOSE_DEBUG("DXF: skipped over control group (",cnt," lines)"); } } catch(std::logic_error&) { ai_assert(!splitter); diff --git a/code/AssetLib/DXF/DXFLoader.cpp b/code/AssetLib/DXF/DXFLoader.cpp index d4a6be4ad..09c229265 100644 --- a/code/AssetLib/DXF/DXFLoader.cpp +++ b/code/AssetLib/DXF/DXFLoader.cpp @@ -202,7 +202,7 @@ void DXFImporter::InternReadFile( const std::string& filename, aiScene* pScene, // comments else if (reader.Is(999)) { - ASSIMP_LOG_INFO_F("DXF Comment: ", reader.Value()); + ASSIMP_LOG_INFO("DXF Comment: ", reader.Value()); } // don't read past the official EOF sign @@ -241,7 +241,7 @@ void DXFImporter::ConvertMeshes(aiScene* pScene, DXF::FileData& output) { } } - ASSIMP_LOG_VERBOSE_DEBUG_F("DXF: Unexpanded polycount is ", icount, ", vertex count is ", vcount); + ASSIMP_LOG_VERBOSE_DEBUG("DXF: Unexpanded polycount is ", icount, ", vertex count is ", vcount); } if (! output.blocks.size() ) { @@ -372,7 +372,7 @@ void DXFImporter::ExpandBlockReferences(DXF::Block& bl,const DXF::BlockMap& bloc // first check if the referenced blocks exists ... const DXF::BlockMap::const_iterator it = blocks_by_name.find(insert.name); if (it == blocks_by_name.end()) { - ASSIMP_LOG_ERROR_F("DXF: Failed to resolve block reference: ", insert.name,"; skipping" ); + ASSIMP_LOG_ERROR("DXF: Failed to resolve block reference: ", insert.name,"; skipping" ); continue; } @@ -473,7 +473,7 @@ void DXFImporter::ParseBlocks(DXF::LineReader& reader, DXF::FileData& output) { ++reader; } - ASSIMP_LOG_VERBOSE_DEBUG_F("DXF: got ", output.blocks.size()," entries in BLOCKS" ); + ASSIMP_LOG_VERBOSE_DEBUG("DXF: got ", output.blocks.size()," entries in BLOCKS" ); } // ------------------------------------------------------------------------------------------------ @@ -549,7 +549,7 @@ void DXFImporter::ParseEntities(DXF::LineReader& reader, DXF::FileData& output) ++reader; } - ASSIMP_LOG_VERBOSE_DEBUG_F( "DXF: got ", block.lines.size()," polylines and ", block.insertions.size(), + ASSIMP_LOG_VERBOSE_DEBUG( "DXF: got ", block.lines.size()," polylines and ", block.insertions.size(), " inserted blocks in ENTITIES" ); } @@ -654,7 +654,7 @@ void DXFImporter::ParsePolyLine(DXF::LineReader& reader, DXF::FileData& output) //} if (vguess && line.positions.size() != vguess) { - ASSIMP_LOG_WARN_F("DXF: unexpected vertex count in polymesh: ", + ASSIMP_LOG_WARN("DXF: unexpected vertex count in polymesh: ", line.positions.size(),", expected ", vguess ); } @@ -670,7 +670,7 @@ void DXFImporter::ParsePolyLine(DXF::LineReader& reader, DXF::FileData& output) // to set the 71 and 72 fields, respectively, to valid values. // So just fire a warning. if (iguess && line.counts.size() != iguess) { - ASSIMP_LOG_WARN_F( "DXF: unexpected face count in polymesh: ", line.counts.size(),", expected ", iguess ); + ASSIMP_LOG_WARN( "DXF: unexpected face count in polymesh: ", line.counts.size(),", expected ", iguess ); } } else if (!line.indices.size() && !line.counts.size()) { diff --git a/code/AssetLib/FBX/FBXBinaryTokenizer.cpp b/code/AssetLib/FBX/FBXBinaryTokenizer.cpp index 800d0014d..348812054 100644 --- a/code/AssetLib/FBX/FBXBinaryTokenizer.cpp +++ b/code/AssetLib/FBX/FBXBinaryTokenizer.cpp @@ -459,7 +459,7 @@ void TokenizeBinary(TokenList& output_tokens, const char* input, size_t length) /*Result ignored*/ ReadByte(input, cursor, input + length); /*Result ignored*/ ReadByte(input, cursor, input + length); const uint32_t version = ReadWord(input, cursor, input + length); - ASSIMP_LOG_DEBUG_F("FBX version: ", version); + ASSIMP_LOG_DEBUG("FBX version: ", version); const bool is64bits = version >= 7500; const char *end = input + length; try diff --git a/code/AssetLib/FBX/FBXConverter.cpp b/code/AssetLib/FBX/FBXConverter.cpp index cb033a651..88d7ad626 100644 --- a/code/AssetLib/FBX/FBXConverter.cpp +++ b/code/AssetLib/FBX/FBXConverter.cpp @@ -1542,10 +1542,10 @@ void FBXConverter::ConvertCluster(std::vector &local_mesh_bones, const aiBone *bone = nullptr; if (bone_map.count(deformer_name)) { - ASSIMP_LOG_VERBOSE_DEBUG_F("retrieved bone from lookup ", bone_name.C_Str(), ". Deformer:", deformer_name); + ASSIMP_LOG_VERBOSE_DEBUG("retrieved bone from lookup ", bone_name.C_Str(), ". Deformer:", deformer_name); bone = bone_map[deformer_name]; } else { - ASSIMP_LOG_VERBOSE_DEBUG_F("created new bone ", bone_name.C_Str(), ". Deformer: ", deformer_name); + ASSIMP_LOG_VERBOSE_DEBUG("created new bone ", bone_name.C_Str(), ". Deformer: ", deformer_name); bone = new aiBone(); bone->mName = bone_name; @@ -1591,7 +1591,7 @@ void FBXConverter::ConvertCluster(std::vector &local_mesh_bones, const bone_map.insert(std::pair(deformer_name, bone)); } - ASSIMP_LOG_DEBUG_F("bone research: Indicies size: ", out_indices.size()); + ASSIMP_LOG_DEBUG("bone research: Indicies size: ", out_indices.size()); // lookup must be populated in case something goes wrong // this also allocates bones to mesh instance outside diff --git a/code/AssetLib/FBX/FBXDocument.cpp b/code/AssetLib/FBX/FBXDocument.cpp index a9eb3ad15..7adaadf6c 100644 --- a/code/AssetLib/FBX/FBXDocument.cpp +++ b/code/AssetLib/FBX/FBXDocument.cpp @@ -312,7 +312,7 @@ void Document::ReadHeader() { const Scope& shead = *ehead->Compound(); fbxVersion = ParseTokenAsInt(GetRequiredToken(GetRequiredElement(shead,"FBXVersion",ehead),0)); - ASSIMP_LOG_DEBUG_F("FBX Version: ", fbxVersion); + ASSIMP_LOG_DEBUG("FBX Version: ", fbxVersion); // While we may have some success with newer files, we don't support // the older 6.n fbx format diff --git a/code/AssetLib/FBX/FBXDocumentUtil.cpp b/code/AssetLib/FBX/FBXDocumentUtil.cpp index 6ba01aa47..77455198f 100644 --- a/code/AssetLib/FBX/FBXDocumentUtil.cpp +++ b/code/AssetLib/FBX/FBXDocumentUtil.cpp @@ -79,7 +79,7 @@ void DOMError(const std::string& message, const Element* element /*= nullptr*/) void DOMWarning(const std::string& message, const Token& token) { if(DefaultLogger::get()) { - ASSIMP_LOG_WARN_F("FBX-DOM", Util::GetTokenText(&token), message); + ASSIMP_LOG_WARN("FBX-DOM", Util::GetTokenText(&token), message); } } diff --git a/code/AssetLib/FBX/FBXMaterial.cpp b/code/AssetLib/FBX/FBXMaterial.cpp index 409d7304a..6ada9630b 100644 --- a/code/AssetLib/FBX/FBXMaterial.cpp +++ b/code/AssetLib/FBX/FBXMaterial.cpp @@ -352,7 +352,7 @@ Video::Video(uint64_t id, const Element& element, const Document& doc, const std } } catch (const runtime_error& runtimeError) { //we don't need the content data for contents that has already been loaded - ASSIMP_LOG_VERBOSE_DEBUG_F("Caught exception in FBXMaterial (likely because content was already loaded): ", + ASSIMP_LOG_VERBOSE_DEBUG("Caught exception in FBXMaterial (likely because content was already loaded): ", runtimeError.what()); } } diff --git a/code/AssetLib/LWO/LWOBLoader.cpp b/code/AssetLib/LWO/LWOBLoader.cpp index 2654eccf4..8ef40fc21 100644 --- a/code/AssetLib/LWO/LWOBLoader.cpp +++ b/code/AssetLib/LWO/LWOBLoader.cpp @@ -242,7 +242,7 @@ LWO::Texture* LWOImporter::SetupNewTextureLWOB(LWO::TextureList& list,unsigned i else { // procedural or gradient, not supported - ASSIMP_LOG_ERROR_F("LWOB: Unsupported legacy texture: ", type); + ASSIMP_LOG_ERROR("LWOB: Unsupported legacy texture: ", type); } return tex; diff --git a/code/AssetLib/LWO/LWOLoader.cpp b/code/AssetLib/LWO/LWOLoader.cpp index a9fa08f11..7a2916424 100644 --- a/code/AssetLib/LWO/LWOLoader.cpp +++ b/code/AssetLib/LWO/LWOLoader.cpp @@ -1006,7 +1006,7 @@ void LWOImporter::LoadLWO2VertexMap(unsigned int length, bool perPoly) { if (name == "APS.Level") { // XXX handle this (seems to be subdivision-related). } - ASSIMP_LOG_WARN_F("LWO2: Skipping unknown VMAP/VMAD channel \'", name, "\'"); + ASSIMP_LOG_WARN("LWO2: Skipping unknown VMAP/VMAD channel \'", name, "\'"); return; }; base->Allocate((unsigned int)mCurLayer->mTempPoints.size()); @@ -1026,7 +1026,7 @@ void LWOImporter::LoadLWO2VertexMap(unsigned int length, bool perPoly) { unsigned int idx = ReadVSizedIntLWO2(mFileBuffer) + mCurLayer->mPointIDXOfs; if (idx >= numPoints) { - ASSIMP_LOG_WARN_F("LWO2: Failure evaluating VMAP/VMAD entry \'", name, "\', vertex index is out of range"); + ASSIMP_LOG_WARN("LWO2: Failure evaluating VMAP/VMAD entry \'", name, "\', vertex index is out of range"); mFileBuffer += base->dims << 2u; continue; } @@ -1036,7 +1036,7 @@ void LWOImporter::LoadLWO2VertexMap(unsigned int length, bool perPoly) { // we have already a VMAP entry for this vertex - thus // we need to duplicate the corresponding polygon. if (polyIdx >= numFaces) { - ASSIMP_LOG_WARN_F("LWO2: Failure evaluating VMAD entry \'", name, "\', polygon index is out of range"); + ASSIMP_LOG_WARN("LWO2: Failure evaluating VMAD entry \'", name, "\', polygon index is out of range"); mFileBuffer += base->dims << 2u; continue; } @@ -1076,7 +1076,7 @@ void LWOImporter::LoadLWO2VertexMap(unsigned int length, bool perPoly) { CreateNewEntry(mCurLayer->mNormals, srcIdx); } if (!had) { - ASSIMP_LOG_WARN_F("LWO2: Failure evaluating VMAD entry \'", name, "\', vertex index wasn't found in that polygon"); + ASSIMP_LOG_WARN("LWO2: Failure evaluating VMAD entry \'", name, "\', vertex index wasn't found in that polygon"); ai_assert(had); } } diff --git a/code/AssetLib/LWO/LWOMaterial.cpp b/code/AssetLib/LWO/LWOMaterial.cpp index 9d51438d7..186aa8a42 100644 --- a/code/AssetLib/LWO/LWOMaterial.cpp +++ b/code/AssetLib/LWO/LWOMaterial.cpp @@ -335,7 +335,7 @@ void LWOImporter::ConvertMaterial(const LWO::Surface &surf, aiMaterial *pcMat) { m = aiShadingMode_Fresnel; break; } else { - ASSIMP_LOG_WARN_F("LWO2: Unknown surface shader: ", shader.functionName); + ASSIMP_LOG_WARN("LWO2: Unknown surface shader: ", shader.functionName); } } if (surf.mMaximumSmoothAngle <= 0.0) diff --git a/code/AssetLib/M3D/M3DImporter.cpp b/code/AssetLib/M3D/M3DImporter.cpp index 1fb21d73e..5485b1bff 100644 --- a/code/AssetLib/M3D/M3DImporter.cpp +++ b/code/AssetLib/M3D/M3DImporter.cpp @@ -193,7 +193,7 @@ void M3DImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSys } //DefaultLogger::create("/dev/stderr", Logger::VERBOSE); - ASSIMP_LOG_DEBUG_F("M3D: loading ", file); + ASSIMP_LOG_DEBUG("M3D: loading ", file); // let the C SDK do the hard work for us M3DWrapper m3d(pIOHandler, buffer); @@ -239,7 +239,7 @@ void M3DImporter::importMaterials(const M3DWrapper &m3d) { mScene->mNumMaterials = m3d->nummaterial + 1; mScene->mMaterials = new aiMaterial *[mScene->mNumMaterials]; - ASSIMP_LOG_DEBUG_F("M3D: importMaterials ", mScene->mNumMaterials); + ASSIMP_LOG_DEBUG("M3D: importMaterials ", mScene->mNumMaterials); // add a default material as first aiMaterial *mat = new aiMaterial; @@ -334,7 +334,7 @@ void M3DImporter::importTextures(const M3DWrapper &m3d) { ai_assert(m3d); mScene->mNumTextures = m3d->numtexture; - ASSIMP_LOG_DEBUG_F("M3D: importTextures ", mScene->mNumTextures); + ASSIMP_LOG_DEBUG("M3D: importTextures ", mScene->mNumTextures); if (!m3d->numtexture || !m3d->texture) { return; @@ -389,7 +389,7 @@ void M3DImporter::importTextures(const M3DWrapper &m3d) { // individually. In assimp there're per mesh vertex and UV lists, and they must be // indexed simultaneously. void M3DImporter::importMeshes(const M3DWrapper &m3d) { - ASSIMP_LOG_DEBUG_F("M3D: importMeshes ", m3d->numface); + ASSIMP_LOG_DEBUG("M3D: importMeshes ", m3d->numface); if (!m3d->numface || !m3d->face || !m3d->numvertex || !m3d->vertex) { return; @@ -508,7 +508,7 @@ void M3DImporter::importBones(const M3DWrapper &m3d, unsigned int parentid, aiNo ai_assert(mScene != nullptr); ai_assert(m3d); - ASSIMP_LOG_DEBUG_F("M3D: importBones ", m3d->numbone, " parentid ", (int)parentid); + ASSIMP_LOG_DEBUG("M3D: importBones ", m3d->numbone, " parentid ", (int)parentid); if (!m3d->numbone || !m3d->bone) { return; @@ -549,7 +549,7 @@ void M3DImporter::importAnimations(const M3DWrapper &m3d) { mScene->mNumAnimations = m3d->numaction; - ASSIMP_LOG_DEBUG_F("M3D: importAnimations ", mScene->mNumAnimations); + ASSIMP_LOG_DEBUG("M3D: importAnimations ", mScene->mNumAnimations); if (!m3d->numaction || !m3d->action || !m3d->numbone || !m3d->bone || !m3d->vertex) { return; @@ -712,7 +712,7 @@ void M3DImporter::populateMesh(const M3DWrapper &m3d, aiMesh *pMesh, std::vector ai_assert(vertexids != nullptr); ai_assert(m3d); - ASSIMP_LOG_DEBUG_F("M3D: populateMesh numvertices ", vertices->size(), " numfaces ", faces->size(), + ASSIMP_LOG_DEBUG("M3D: populateMesh numvertices ", vertices->size(), " numfaces ", faces->size(), " numnormals ", normals->size(), " numtexcoord ", texcoords->size(), " numbones ", m3d->numbone); if (vertices->size() && faces->size()) { diff --git a/code/AssetLib/MD3/MD3Loader.cpp b/code/AssetLib/MD3/MD3Loader.cpp index 3002aff67..18c3d4228 100644 --- a/code/AssetLib/MD3/MD3Loader.cpp +++ b/code/AssetLib/MD3/MD3Loader.cpp @@ -112,7 +112,7 @@ bool Q3Shader::LoadShader(ShaderData &fill, const std::string &pFile, IOSystem * if (!file.get()) return false; // if we can't access the file, don't worry and return - ASSIMP_LOG_INFO_F("Loading Quake3 shader file ", pFile); + ASSIMP_LOG_INFO("Loading Quake3 shader file ", pFile); // read file in memory const size_t s = file->FileSize(); @@ -851,7 +851,7 @@ void MD3Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy if (it != skins.textures.end()) { texture_name = &*(_texture_name = (*it).second).begin(); - ASSIMP_LOG_VERBOSE_DEBUG_F("MD3: Assigning skin texture ", (*it).second, " to surface ", pcSurfaces->NAME); + ASSIMP_LOG_VERBOSE_DEBUG("MD3: Assigning skin texture ", (*it).second, " to surface ", pcSurfaces->NAME); (*it).resolved = true; // mark entry as resolved } @@ -1000,7 +1000,7 @@ void MD3Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy if (!DefaultLogger::isNullLogger()) { for (std::list::const_iterator it = skins.textures.begin(); it != skins.textures.end(); ++it) { if (!(*it).resolved) { - ASSIMP_LOG_ERROR_F("MD3: Failed to match skin ", (*it).first, " to surface ", (*it).second); + ASSIMP_LOG_ERROR("MD3: Failed to match skin ", (*it).first, " to surface ", (*it).second); } } } diff --git a/code/AssetLib/MS3D/MS3DLoader.cpp b/code/AssetLib/MS3D/MS3DLoader.cpp index 31cbca83b..e4057a43a 100644 --- a/code/AssetLib/MS3D/MS3DLoader.cpp +++ b/code/AssetLib/MS3D/MS3DLoader.cpp @@ -387,7 +387,7 @@ void MS3DImporter::InternReadFile( const std::string& pFile, } const std::string& s = std::string(reinterpret_cast(stream.GetPtr()),len); - ASSIMP_LOG_DEBUG_F("MS3D: Model comment: ", s); + ASSIMP_LOG_DEBUG("MS3D: Model comment: ", s); } if(stream.GetRemainingSize() > 4 && inrange((stream >> subversion,subversion),1u,3u)) { diff --git a/code/AssetLib/NDO/NDOLoader.cpp b/code/AssetLib/NDO/NDOLoader.cpp index df3a9b15d..0abc09dc8 100644 --- a/code/AssetLib/NDO/NDOLoader.cpp +++ b/code/AssetLib/NDO/NDOLoader.cpp @@ -147,7 +147,7 @@ void NDOImporter::InternReadFile( const std::string& pFile, ASSIMP_LOG_INFO("NDO file format is 1.2"); } else { - ASSIMP_LOG_WARN_F( "Unrecognized nendo file format version, continuing happily ... :", (head+6)); + ASSIMP_LOG_WARN( "Unrecognized nendo file format version, continuing happily ... :", (head+6)); } reader.IncPtr(2); /* skip flags */ diff --git a/code/AssetLib/NFF/NFFLoader.cpp b/code/AssetLib/NFF/NFFLoader.cpp index 11cce0990..65f2dc069 100644 --- a/code/AssetLib/NFF/NFFLoader.cpp +++ b/code/AssetLib/NFF/NFFLoader.cpp @@ -150,7 +150,7 @@ void NFFImporter::LoadNFF2MaterialTable(std::vector &output, // The file should start with the magic sequence "mat" if (!TokenMatch(buffer, "mat", 3)) { - ASSIMP_LOG_ERROR_F("NFF2: Not a valid material library ", path, "."); + ASSIMP_LOG_ERROR("NFF2: Not a valid material library ", path, "."); return; } @@ -164,7 +164,7 @@ void NFFImporter::LoadNFF2MaterialTable(std::vector &output, // 'version' defines the version of the file format if (TokenMatch(sz, "version", 7)) { - ASSIMP_LOG_INFO_F("NFF (Sense8) material library file format: ", std::string(sz)); + ASSIMP_LOG_INFO("NFF (Sense8) material library file format: ", std::string(sz)); } // 'matdef' starts a new material in the file else if (TokenMatch(sz, "matdef", 6)) { @@ -177,7 +177,7 @@ void NFFImporter::LoadNFF2MaterialTable(std::vector &output, // check whether we have an active material at the moment if (!IsLineEnd(*sz)) { if (!curShader) { - ASSIMP_LOG_ERROR_F("NFF2 material library: Found element ", sz, "but there is no active material"); + ASSIMP_LOG_ERROR("NFF2 material library: Found element ", sz, "but there is no active material"); continue; } } else @@ -277,7 +277,7 @@ void NFFImporter::InternReadFile(const std::string &pFile, while (GetNextLine(buffer, line)) { SkipSpaces(line, &sz); if (TokenMatch(sz, "version", 7)) { - ASSIMP_LOG_INFO_F("NFF (Sense8) file format: ", sz); + ASSIMP_LOG_INFO("NFF (Sense8) file format: ", sz); } else if (TokenMatch(sz, "viewpos", 7)) { AI_NFF_PARSE_TRIPLE(camPos); hasCam = true; diff --git a/code/AssetLib/Ogre/OgreBinarySerializer.cpp b/code/AssetLib/Ogre/OgreBinarySerializer.cpp index 0fc18feb9..b54316514 100644 --- a/code/AssetLib/Ogre/OgreBinarySerializer.cpp +++ b/code/AssetLib/Ogre/OgreBinarySerializer.cpp @@ -168,7 +168,7 @@ void OgreBinarySerializer::RollbackHeader() { void OgreBinarySerializer::SkipBytes(size_t numBytes) { #if (OGRE_BINARY_SERIALIZER_DEBUG == 1) - ASSIMP_LOG_VERBOSE_DEBUG_F("Skipping ", numBytes, " bytes"); + ASSIMP_LOG_VERBOSE_DEBUG("Skipping ", numBytes, " bytes"); #endif m_reader->IncPtr(numBytes); @@ -208,7 +208,7 @@ void OgreBinarySerializer::ReadMesh(Mesh *mesh) { mesh->hasSkeletalAnimations = Read(); ASSIMP_LOG_VERBOSE_DEBUG("Reading Mesh"); - ASSIMP_LOG_VERBOSE_DEBUG_F(" - Skeletal animations: ", mesh->hasSkeletalAnimations ? "true" : "false"); + ASSIMP_LOG_VERBOSE_DEBUG(" - Skeletal animations: ", mesh->hasSkeletalAnimations ? "true" : "false"); if (!AtEnd()) { uint16_t id = ReadHeader(); @@ -364,9 +364,9 @@ void OgreBinarySerializer::ReadSubMesh(Mesh *mesh) { submesh->indexData->faceCount = static_cast(submesh->indexData->count / 3); submesh->indexData->is32bit = Read(); - ASSIMP_LOG_VERBOSE_DEBUG_F("Reading SubMesh ", mesh->subMeshes.size()); - ASSIMP_LOG_VERBOSE_DEBUG_F(" - Material: '", submesh->materialRef, "'"); - ASSIMP_LOG_VERBOSE_DEBUG_F(" - Uses shared geometry: ", submesh->usesSharedVertexData ? "true" : "false"); + ASSIMP_LOG_VERBOSE_DEBUG("Reading SubMesh ", mesh->subMeshes.size()); + ASSIMP_LOG_VERBOSE_DEBUG(" - Material: '", submesh->materialRef, "'"); + ASSIMP_LOG_VERBOSE_DEBUG(" - Uses shared geometry: ", submesh->usesSharedVertexData ? "true" : "false"); // Index buffer if (submesh->indexData->count > 0) { @@ -374,7 +374,7 @@ void OgreBinarySerializer::ReadSubMesh(Mesh *mesh) { uint8_t *indexBuffer = ReadBytes(numBytes); submesh->indexData->buffer = MemoryStreamPtr(new Assimp::MemoryIOStream(indexBuffer, numBytes, true)); - ASSIMP_LOG_VERBOSE_DEBUG_F(" - ", submesh->indexData->faceCount, + ASSIMP_LOG_VERBOSE_DEBUG(" - ", submesh->indexData->faceCount, " faces from ", submesh->indexData->count, (submesh->indexData->is32bit ? " 32bit" : " 16bit"), " indexes of ", numBytes, " bytes"); } @@ -475,7 +475,7 @@ void OgreBinarySerializer::ReadSubMeshNames(Mesh *mesh) { } submesh->name = ReadLine(); - ASSIMP_LOG_VERBOSE_DEBUG_F(" - SubMesh ", submesh->index, " name '", submesh->name, "'"); + ASSIMP_LOG_VERBOSE_DEBUG(" - SubMesh ", submesh->index, " name '", submesh->name, "'"); if (!AtEnd()) id = ReadHeader(); @@ -488,7 +488,7 @@ void OgreBinarySerializer::ReadSubMeshNames(Mesh *mesh) { void OgreBinarySerializer::ReadGeometry(VertexData *dest) { dest->count = Read(); - ASSIMP_LOG_VERBOSE_DEBUG_F(" - Reading geometry of ", dest->count, " vertices"); + ASSIMP_LOG_VERBOSE_DEBUG(" - Reading geometry of ", dest->count, " vertices"); if (!AtEnd()) { uint16_t id = ReadHeader(); @@ -536,7 +536,7 @@ void OgreBinarySerializer::ReadGeometryVertexElement(VertexData *dest) { element.offset = Read(); element.index = Read(); - ASSIMP_LOG_VERBOSE_DEBUG_F(" - Vertex element ", element.SemanticToString(), " of type ", + ASSIMP_LOG_VERBOSE_DEBUG(" - Vertex element ", element.SemanticToString(), " of type ", element.TypeToString(), " index=", element.index, " source=", element.source); dest->vertexElements.push_back(element); @@ -557,7 +557,7 @@ void OgreBinarySerializer::ReadGeometryVertexBuffer(VertexData *dest) { uint8_t *vertexBuffer = ReadBytes(numBytes); dest->vertexBindings[bindIndex] = MemoryStreamPtr(new Assimp::MemoryIOStream(vertexBuffer, numBytes, true)); - ASSIMP_LOG_VERBOSE_DEBUG_F(" - Read vertex buffer for source ", bindIndex, " of ", numBytes, " bytes"); + ASSIMP_LOG_VERBOSE_DEBUG(" - Read vertex buffer for source ", bindIndex, " of ", numBytes, " bytes"); } void OgreBinarySerializer::ReadEdgeList(Mesh * /*mesh*/) { @@ -777,12 +777,12 @@ bool OgreBinarySerializer::ImportSkeleton(Assimp::IOSystem *pIOHandler, MeshXml MemoryStreamReaderPtr OgreBinarySerializer::OpenReader(Assimp::IOSystem *pIOHandler, const std::string &filename) { if (!EndsWith(filename, ".skeleton", false)) { - ASSIMP_LOG_ERROR_F("Imported Mesh is referencing to unsupported '", filename, "' skeleton file."); + ASSIMP_LOG_ERROR("Imported Mesh is referencing to unsupported '", filename, "' skeleton file."); return MemoryStreamReaderPtr(); } if (!pIOHandler->Exists(filename)) { - ASSIMP_LOG_ERROR_F("Failed to find skeleton file '", filename, "' that is referenced by imported Mesh."); + ASSIMP_LOG_ERROR("Failed to find skeleton file '", filename, "' that is referenced by imported Mesh."); return MemoryStreamReaderPtr(); } @@ -874,7 +874,7 @@ void OgreBinarySerializer::ReadBone(Skeleton *skeleton) { throw DeadlyImportError("Ogre Skeleton bone indexes not contiguous. Error at bone index ", bone->id); } - ASSIMP_LOG_VERBOSE_DEBUG_F(" ", bone->id, " ", bone->name); + ASSIMP_LOG_VERBOSE_DEBUG(" ", bone->id, " ", bone->name); skeleton->bones.push_back(bone); } @@ -919,7 +919,7 @@ void OgreBinarySerializer::ReadSkeletonAnimation(Skeleton *skeleton) { skeleton->animations.push_back(anim); - ASSIMP_LOG_VERBOSE_DEBUG_F(" ", anim->name, " (", anim->length, " sec, ", anim->tracks.size(), " tracks)"); + ASSIMP_LOG_VERBOSE_DEBUG(" ", anim->name, " (", anim->length, " sec, ", anim->tracks.size(), " tracks)"); } void OgreBinarySerializer::ReadSkeletonAnimationTrack(Skeleton * /*skeleton*/, Animation *dest) { diff --git a/code/AssetLib/Ogre/OgreMaterial.cpp b/code/AssetLib/Ogre/OgreMaterial.cpp index eced651f6..cafdda795 100644 --- a/code/AssetLib/Ogre/OgreMaterial.cpp +++ b/code/AssetLib/Ogre/OgreMaterial.cpp @@ -160,16 +160,16 @@ aiMaterial *OgreImporter::ReadMaterial(const std::string &pFile, Assimp::IOSyste if (materialFile) { break; } - ASSIMP_LOG_VERBOSE_DEBUG_F("Source file for material '", materialName, "' ", potentialFiles[i], " does not exist"); + ASSIMP_LOG_VERBOSE_DEBUG("Source file for material '", materialName, "' ", potentialFiles[i], " does not exist"); } if (!materialFile) { - ASSIMP_LOG_ERROR_F("Failed to find source file for material '", materialName, "'"); + ASSIMP_LOG_ERROR("Failed to find source file for material '", materialName, "'"); return 0; } std::unique_ptr stream(materialFile); if (stream->FileSize() == 0) { - ASSIMP_LOG_WARN_F("Source file for material '", materialName, "' is empty (size is 0 bytes)"); + ASSIMP_LOG_WARN("Source file for material '", materialName, "' is empty (size is 0 bytes)"); return 0; } @@ -184,7 +184,7 @@ aiMaterial *OgreImporter::ReadMaterial(const std::string &pFile, Assimp::IOSyste ss << &data[0]; } - ASSIMP_LOG_VERBOSE_DEBUG_F("Reading material '", materialName, "'"); + ASSIMP_LOG_VERBOSE_DEBUG("Reading material '", materialName, "'"); aiMaterial *material = new aiMaterial(); m_textures.clear(); @@ -219,11 +219,11 @@ aiMaterial *OgreImporter::ReadMaterial(const std::string &pFile, Assimp::IOSyste NextAfterNewLine(ss, linePart); if (linePart != partBlockStart) { - ASSIMP_LOG_ERROR_F("Invalid material: block start missing near index ", ss.tellg()); + ASSIMP_LOG_ERROR("Invalid material: block start missing near index ", ss.tellg()); return material; } - ASSIMP_LOG_VERBOSE_DEBUG_F("material '", materialName, "'"); + ASSIMP_LOG_VERBOSE_DEBUG("material '", materialName, "'"); while (linePart != partBlockEnd) { // Proceed to the first technique @@ -303,11 +303,11 @@ bool OgreImporter::ReadTechnique(const std::string &techniqueName, stringstream ss >> linePart; if (linePart != partBlockStart) { - ASSIMP_LOG_ERROR_F("Invalid material: Technique block start missing near index ", ss.tellg()); + ASSIMP_LOG_ERROR("Invalid material: Technique block start missing near index ", ss.tellg()); return false; } - ASSIMP_LOG_VERBOSE_DEBUG_F(" technique '", techniqueName, "'"); + ASSIMP_LOG_VERBOSE_DEBUG(" technique '", techniqueName, "'"); const string partPass = "pass"; @@ -334,11 +334,11 @@ bool OgreImporter::ReadPass(const std::string &passName, stringstream &ss, aiMat ss >> linePart; if (linePart != partBlockStart) { - ASSIMP_LOG_ERROR_F("Invalid material: Pass block start missing near index ", ss.tellg()); + ASSIMP_LOG_ERROR("Invalid material: Pass block start missing near index ", ss.tellg()); return false; } - ASSIMP_LOG_VERBOSE_DEBUG_F(" pass '", passName, "'"); + ASSIMP_LOG_VERBOSE_DEBUG(" pass '", passName, "'"); const string partAmbient = "ambient"; const string partDiffuse = "diffuse"; @@ -362,7 +362,7 @@ bool OgreImporter::ReadPass(const std::string &passName, stringstream &ss, aiMat ss >> r >> g >> b; const aiColor3D color(r, g, b); - ASSIMP_LOG_VERBOSE_DEBUG_F(" ", linePart, " ", r, " ", g, " ", b); + ASSIMP_LOG_VERBOSE_DEBUG(" ", linePart, " ", r, " ", g, " ", b); if (linePart == partAmbient) { material->AddProperty(&color, 1, AI_MATKEY_COLOR_AMBIENT); @@ -386,11 +386,11 @@ bool OgreImporter::ReadTextureUnit(const std::string &textureUnitName, stringstr ss >> linePart; if (linePart != partBlockStart) { - ASSIMP_LOG_ERROR_F("Invalid material: Texture unit block start missing near index ", ss.tellg()); + ASSIMP_LOG_ERROR("Invalid material: Texture unit block start missing near index ", ss.tellg()); return false; } - ASSIMP_LOG_VERBOSE_DEBUG_F(" texture_unit '", textureUnitName, "'"); + ASSIMP_LOG_VERBOSE_DEBUG(" texture_unit '", textureUnitName, "'"); const string partTexture = "texture"; const string partTextCoordSet = "tex_coord_set"; @@ -420,7 +420,7 @@ bool OgreImporter::ReadTextureUnit(const std::string &textureUnitName, stringstr if (posSuffix != string::npos && posUnderscore != string::npos && posSuffix > posUnderscore) { string identifier = ai_tolower(textureRef.substr(posUnderscore, posSuffix - posUnderscore)); - ASSIMP_LOG_VERBOSE_DEBUG_F("Detecting texture type from filename postfix '", identifier, "'"); + ASSIMP_LOG_VERBOSE_DEBUG("Detecting texture type from filename postfix '", identifier, "'"); if (identifier == "_n" || identifier == "_nrm" || identifier == "_nrml" || identifier == "_normal" || identifier == "_normals" || identifier == "_normalmap") { textureType = aiTextureType_NORMALS; @@ -484,7 +484,7 @@ bool OgreImporter::ReadTextureUnit(const std::string &textureUnitName, stringstr unsigned int textureTypeIndex = m_textures[textureType]; m_textures[textureType]++; - ASSIMP_LOG_VERBOSE_DEBUG_F(" texture '", textureRef, "' type ", textureType, + ASSIMP_LOG_VERBOSE_DEBUG(" texture '", textureRef, "' type ", textureType, " index ", textureTypeIndex, " UV ", uvCoord); aiString assimpTextureRef(textureRef); diff --git a/code/AssetLib/Ogre/OgreStructs.cpp b/code/AssetLib/Ogre/OgreStructs.cpp index ce289c79c..a2d861172 100644 --- a/code/AssetLib/Ogre/OgreStructs.cpp +++ b/code/AssetLib/Ogre/OgreStructs.cpp @@ -545,7 +545,7 @@ aiMesh *SubMesh::ConvertToAssimpMesh(Mesh *parent) { dest->mNumUVComponents[0] = static_cast(uv1Element->ComponentCount()); dest->mTextureCoords[0] = new aiVector3D[dest->mNumVertices]; } else { - ASSIMP_LOG_WARN_F("Ogre imported UV0 type ", uv1Element->TypeToString(), " is not compatible with Assimp. Ignoring UV."); + ASSIMP_LOG_WARN("Ogre imported UV0 type ", uv1Element->TypeToString(), " is not compatible with Assimp. Ignoring UV."); uv1 = 0; } } @@ -554,7 +554,7 @@ aiMesh *SubMesh::ConvertToAssimpMesh(Mesh *parent) { dest->mNumUVComponents[1] = static_cast(uv2Element->ComponentCount()); dest->mTextureCoords[1] = new aiVector3D[dest->mNumVertices]; } else { - ASSIMP_LOG_WARN_F("Ogre imported UV0 type ", uv2Element->TypeToString(), " is not compatible with Assimp. Ignoring UV."); + ASSIMP_LOG_WARN("Ogre imported UV0 type ", uv2Element->TypeToString(), " is not compatible with Assimp. Ignoring UV."); uv2 = 0; } } diff --git a/code/AssetLib/Ogre/OgreXmlSerializer.cpp b/code/AssetLib/Ogre/OgreXmlSerializer.cpp index 938ae48f3..70671f251 100644 --- a/code/AssetLib/Ogre/OgreXmlSerializer.cpp +++ b/code/AssetLib/Ogre/OgreXmlSerializer.cpp @@ -256,7 +256,7 @@ void OgreXmlSerializer::ReadMesh(MeshXml *mesh) { void OgreXmlSerializer::ReadGeometry(XmlNode &node, VertexDataXml *dest) { dest->count = ReadAttribute(node, "vertexcount"); - ASSIMP_LOG_VERBOSE_DEBUG_F(" - Reading geometry of ", dest->count, " vertices"); + ASSIMP_LOG_VERBOSE_DEBUG(" - Reading geometry of ", dest->count, " vertices"); for (XmlNode currentNode : node.children()) { const std::string ¤tName = currentNode.name(); @@ -290,7 +290,7 @@ void OgreXmlSerializer::ReadGeometryVertexBuffer(XmlNode &node, VertexDataXml *d dest->tangents.reserve(dest->count); } if (uvs > 0) { - ASSIMP_LOG_VERBOSE_DEBUG_F(" - Contains ", uvs, " texture coords"); + ASSIMP_LOG_VERBOSE_DEBUG(" - Contains ", uvs, " texture coords"); dest->uvs.resize(uvs); for (size_t i = 0, len = dest->uvs.size(); i < len; ++i) { dest->uvs[i].reserve(dest->count); @@ -365,9 +365,9 @@ void OgreXmlSerializer::ReadSubMesh(XmlNode &node, MeshXml *mesh) { submesh->usesSharedVertexData = ReadAttribute(node, anUseSharedVertices); } - ASSIMP_LOG_VERBOSE_DEBUG_F("Reading SubMesh ", mesh->subMeshes.size()); - ASSIMP_LOG_VERBOSE_DEBUG_F(" - Material: '", submesh->materialRef, "'"); - ASSIMP_LOG_VERBOSE_DEBUG_F(" - Uses shared geometry: ", (submesh->usesSharedVertexData ? "true" : "false")); + ASSIMP_LOG_VERBOSE_DEBUG("Reading SubMesh ", mesh->subMeshes.size()); + ASSIMP_LOG_VERBOSE_DEBUG(" - Material: '", submesh->materialRef, "'"); + ASSIMP_LOG_VERBOSE_DEBUG(" - Uses shared geometry: ", (submesh->usesSharedVertexData ? "true" : "false")); // TODO: maybe we have always just 1 faces and 1 geometry and always in this order. this loop will only work correct, when the order // of faces and geometry changed, and not if we have more than one of one @@ -398,7 +398,7 @@ void OgreXmlSerializer::ReadSubMesh(XmlNode &node, MeshXml *mesh) { } } if (submesh->indexData->faces.size() == submesh->indexData->faceCount) { - ASSIMP_LOG_VERBOSE_DEBUG_F(" - Faces ", submesh->indexData->faceCount); + ASSIMP_LOG_VERBOSE_DEBUG(" - Faces ", submesh->indexData->faceCount); } else { throw DeadlyImportError("Read only ", submesh->indexData->faces.size(), " faces when should have read ", submesh->indexData->faceCount); } @@ -459,7 +459,7 @@ void OgreXmlSerializer::ReadBoneAssignments(XmlNode &node, VertexDataXml *dest) } } - ASSIMP_LOG_VERBOSE_DEBUG_F(" - ", dest->boneAssignments.size(), " bone assignments"); + ASSIMP_LOG_VERBOSE_DEBUG(" - ", dest->boneAssignments.size(), " bone assignments"); } // Skeleton @@ -515,12 +515,12 @@ bool OgreXmlSerializer::ImportSkeleton(Assimp::IOSystem *pIOHandler, Mesh *mesh) XmlParserPtr OgreXmlSerializer::OpenXmlParser(Assimp::IOSystem *pIOHandler, const std::string &filename) { if (!EndsWith(filename, ".skeleton.xml", false)) { - ASSIMP_LOG_ERROR_F("Imported Mesh is referencing to unsupported '", filename, "' skeleton file."); + ASSIMP_LOG_ERROR("Imported Mesh is referencing to unsupported '", filename, "' skeleton file."); return XmlParserPtr(); } if (!pIOHandler->Exists(filename)) { - ASSIMP_LOG_ERROR_F("Failed to find skeleton file '", filename, "' that is referenced by imported Mesh."); + ASSIMP_LOG_ERROR("Failed to find skeleton file '", filename, "' that is referenced by imported Mesh."); return XmlParserPtr(); } @@ -631,7 +631,7 @@ void OgreXmlSerializer::ReadAnimationKeyFrames(XmlNode &node, Animation *anim, V if (axis.Equal(zeroVec)) { axis.x = 1.0f; if (angle != 0) { - ASSIMP_LOG_WARN_F("Found invalid a key frame with a zero rotation axis in animation: ", anim->name); + ASSIMP_LOG_WARN("Found invalid a key frame with a zero rotation axis in animation: ", anim->name); } } keyframe.rotation = aiQuaternion(axis, angle); @@ -741,7 +741,7 @@ void OgreXmlSerializer::ReadBones(XmlNode &node, Skeleton *skeleton) { as per the Ogre skeleton spec. It might be more that other (later) code in this imported does not break. */ for (size_t i = 0, len = skeleton->bones.size(); i < len; ++i) { Bone *b = skeleton->bones[i]; - ASSIMP_LOG_VERBOSE_DEBUG_F(" ", b->id, " ", b->name); + ASSIMP_LOG_VERBOSE_DEBUG(" ", b->id, " ", b->name); if (b->id != static_cast(i)) { throw DeadlyImportError("Bone ids are not in sequence starting from 0. Missing index ", i); diff --git a/code/AssetLib/OpenGEX/OpenGEXImporter.cpp b/code/AssetLib/OpenGEX/OpenGEXImporter.cpp index 44b0bbf7b..9baaa4a12 100644 --- a/code/AssetLib/OpenGEX/OpenGEXImporter.cpp +++ b/code/AssetLib/OpenGEX/OpenGEXImporter.cpp @@ -726,7 +726,7 @@ void OpenGEXImporter::handleMeshNode(ODDLParser::DDLNode *node, aiScene *pScene) } else if ("quads" == propKey) { m_currentMesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; } else { - ASSIMP_LOG_WARN_F(propKey, " is not supported primitive type."); + ASSIMP_LOG_WARN(propKey, " is not supported primitive type."); } } } diff --git a/code/AssetLib/Q3D/Q3DLoader.cpp b/code/AssetLib/Q3D/Q3DLoader.cpp index e96ec0f82..f81026547 100644 --- a/code/AssetLib/Q3D/Q3DLoader.cpp +++ b/code/AssetLib/Q3D/Q3DLoader.cpp @@ -125,7 +125,7 @@ void Q3DImporter::InternReadFile(const std::string &pFile, } // Print the file format version - ASSIMP_LOG_INFO_F("Quick3D File format version: ", + ASSIMP_LOG_INFO("Quick3D File format version: ", std::string(&((const char *)stream.GetPtr())[8], 2)); // ... an store it diff --git a/code/AssetLib/SIB/SIBImporter.cpp b/code/AssetLib/SIB/SIBImporter.cpp index 1426fb73c..c62730cdd 100644 --- a/code/AssetLib/SIB/SIBImporter.cpp +++ b/code/AssetLib/SIB/SIBImporter.cpp @@ -174,7 +174,7 @@ static void UnknownChunk(StreamReaderLE * /*stream*/, const SIBChunk &chunk) { static_cast(chunk.Tag & 0xff) }; - ASSIMP_LOG_WARN_F("SIB: Skipping unknown '", ai_str_toprintable(temp, 4), "' chunk."); + ASSIMP_LOG_WARN("SIB: Skipping unknown '", ai_str_toprintable(temp, 4), "' chunk."); } // Reads a UTF-16LE string and returns it at UTF-8. diff --git a/code/AssetLib/STEPParser/STEPFileReader.cpp b/code/AssetLib/STEPParser/STEPFileReader.cpp index 1ed5ab7d2..e97ea1e28 100644 --- a/code/AssetLib/STEPParser/STEPFileReader.cpp +++ b/code/AssetLib/STEPParser/STEPFileReader.cpp @@ -297,7 +297,7 @@ void STEP::ReadFile(DB& db,const EXPRESS::ConversionSchema& scheme, } if ( !DefaultLogger::isNullLogger()){ - ASSIMP_LOG_DEBUG_F("STEP: got ",map.size()," object records with ", + ASSIMP_LOG_DEBUG("STEP: got ",map.size()," object records with ", db.GetRefs().size()," inverse index entries"); } } diff --git a/code/AssetLib/Unreal/UnrealLoader.cpp b/code/AssetLib/Unreal/UnrealLoader.cpp index 499b49c05..e3ebc05c3 100644 --- a/code/AssetLib/Unreal/UnrealLoader.cpp +++ b/code/AssetLib/Unreal/UnrealLoader.cpp @@ -228,9 +228,9 @@ void UnrealImporter::InternReadFile(const std::string &pFile, a_path = extension + "_a.3d"; uc_path = extension + ".uc"; - ASSIMP_LOG_DEBUG_F("UNREAL: data file is ", d_path); - ASSIMP_LOG_DEBUG_F("UNREAL: aniv file is ", a_path); - ASSIMP_LOG_DEBUG_F("UNREAL: uc file is ", uc_path); + ASSIMP_LOG_DEBUG("UNREAL: data file is ", d_path); + ASSIMP_LOG_DEBUG("UNREAL: aniv file is ", a_path); + ASSIMP_LOG_DEBUG("UNREAL: uc file is ", uc_path); // and open the files ... we can't live without them std::unique_ptr p(pIOHandler->Open(d_path)); diff --git a/code/AssetLib/X/XFileImporter.cpp b/code/AssetLib/X/XFileImporter.cpp index 1fcd6b3db..df1aba331 100644 --- a/code/AssetLib/X/XFileImporter.cpp +++ b/code/AssetLib/X/XFileImporter.cpp @@ -602,7 +602,7 @@ void XFileImporter::ConvertMaterials( aiScene* pScene, std::vector &embeddedTexIdxs, Asset &r, M void glTF2Importer::ImportMaterials(glTF2::Asset &r) { const unsigned int numImportedMaterials = unsigned(r.materials.Size()); - ASSIMP_LOG_DEBUG_F("Importing ", numImportedMaterials, " materials"); + ASSIMP_LOG_DEBUG("Importing ", numImportedMaterials, " materials"); Material defaultMaterial; mScene->mNumMaterials = numImportedMaterials + 1; @@ -402,7 +402,7 @@ aiColor4D* GetVertexColorsForType(glTF2::Ref input) { } void glTF2Importer::ImportMeshes(glTF2::Asset &r) { - ASSIMP_LOG_DEBUG_F("Importing ", r.meshes.Size(), " meshes"); + ASSIMP_LOG_DEBUG("Importing ", r.meshes.Size(), " meshes"); std::vector> meshes; unsigned int k = 0; @@ -539,7 +539,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { if (needPositions) { if (target.position[0]->count != aim->mNumVertices) { - ASSIMP_LOG_WARN_F("Positions of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count"); + ASSIMP_LOG_WARN("Positions of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count"); } else { aiVector3D *positionDiff = nullptr; target.position[0]->ExtractData(positionDiff); @@ -551,7 +551,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { } if (needNormals) { if (target.normal[0]->count != aim->mNumVertices) { - ASSIMP_LOG_WARN_F("Normals of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count"); + ASSIMP_LOG_WARN("Normals of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count"); } else { aiVector3D *normalDiff = nullptr; target.normal[0]->ExtractData(normalDiff); @@ -563,7 +563,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { } if (needTangents) { if (target.tangent[0]->count != aim->mNumVertices) { - ASSIMP_LOG_WARN_F("Tangents of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count"); + ASSIMP_LOG_WARN("Tangents of target ", i, " in mesh \"", mesh.name, "\" does not match the vertex count"); } else { Tangent *tangent = nullptr; attr.tangent[0]->ExtractData(tangent); @@ -785,7 +785,7 @@ void glTF2Importer::ImportCameras(glTF2::Asset &r) { if (!r.cameras.Size()) return; const unsigned int numCameras = r.cameras.Size(); - ASSIMP_LOG_DEBUG_F("Importing ", numCameras, " cameras"); + ASSIMP_LOG_DEBUG("Importing ", numCameras, " cameras"); mScene->mNumCameras = numCameras; mScene->mCameras = new aiCamera *[numCameras]; std::fill(mScene->mCameras, mScene->mCameras + numCameras, nullptr); @@ -822,7 +822,7 @@ void glTF2Importer::ImportLights(glTF2::Asset &r) { return; const unsigned int numLights = r.lights.Size(); - ASSIMP_LOG_DEBUG_F("Importing ", numLights, " lights"); + ASSIMP_LOG_DEBUG("Importing ", numLights, " lights"); mScene->mNumLights = numLights; mScene->mLights = new aiLight *[numLights]; std::fill(mScene->mLights, mScene->mLights + numLights, nullptr); @@ -1329,7 +1329,7 @@ void glTF2Importer::ImportAnimations(glTF2::Asset &r) { if (!r.scene) return; const unsigned numAnimations = r.animations.Size(); - ASSIMP_LOG_DEBUG_F("Importing ", numAnimations, " animations"); + ASSIMP_LOG_DEBUG("Importing ", numAnimations, " animations"); mScene->mNumAnimations = numAnimations; if (mScene->mNumAnimations == 0) { return; @@ -1445,7 +1445,7 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) { if (numEmbeddedTexs == 0) return; - ASSIMP_LOG_DEBUG_F("Importing ", numEmbeddedTexs, " embedded textures"); + ASSIMP_LOG_DEBUG("Importing ", numEmbeddedTexs, " embedded textures"); mScene->mTextures = new aiTexture *[numEmbeddedTexs]; std::fill(mScene->mTextures, mScene->mTextures + numEmbeddedTexs, nullptr); diff --git a/code/Common/BaseImporter.cpp b/code/Common/BaseImporter.cpp index 02aa47326..0f41e9e65 100644 --- a/code/Common/BaseImporter.cpp +++ b/code/Common/BaseImporter.cpp @@ -83,7 +83,7 @@ void BaseImporter::UpdateImporterScale(Importer *pImp) { // Set active scaling pImp->SetPropertyFloat(AI_CONFIG_APP_SCALE_KEY, static_cast(activeScale)); - ASSIMP_LOG_DEBUG_F("UpdateImporterScale scale set: ", activeScale); + ASSIMP_LOG_DEBUG("UpdateImporterScale scale set: ", activeScale); } // ------------------------------------------------------------------------------------------------ @@ -215,7 +215,7 @@ void BaseImporter::GetExtensionList(std::set &extensions) { // We got a match, either we don't care where it is, or it happens to // be in the beginning of the file / line if (!tokensSol || r == buffer || r[-1] == '\r' || r[-1] == '\n') { - ASSIMP_LOG_DEBUG_F("Found positive match for header keyword: ", tokens[i]); + ASSIMP_LOG_DEBUG("Found positive match for header keyword: ", tokens[i]); return true; } } @@ -604,7 +604,7 @@ void BatchLoader::LoadAll() { if (!DefaultLogger::isNullLogger()) { ASSIMP_LOG_INFO("%%% BEGIN EXTERNAL FILE %%%"); - ASSIMP_LOG_INFO_F("File: ", (*it).file); + ASSIMP_LOG_INFO("File: ", (*it).file); } m_data->pImporter->ReadFile((*it).file, pp); (*it).scene = m_data->pImporter->GetOrphanedScene(); diff --git a/code/Common/DefaultIOSystem.cpp b/code/Common/DefaultIOSystem.cpp index ec7a311cf..98d51a17d 100644 --- a/code/Common/DefaultIOSystem.cpp +++ b/code/Common/DefaultIOSystem.cpp @@ -176,7 +176,7 @@ inline static std::string MakeAbsolutePath(const char *in) { if (!ret) { // preserve the input path, maybe someone else is able to fix // the path before it is accessed (e.g. our file system filter) - ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in)); + ASSIMP_LOG_WARN("Invalid path: ", std::string(in)); out = in; } return out; diff --git a/code/Common/Importer.cpp b/code/Common/Importer.cpp index 8cea2cd09..61e5938f6 100644 --- a/code/Common/Importer.cpp +++ b/code/Common/Importer.cpp @@ -232,7 +232,7 @@ aiReturn Importer::RegisterLoader(BaseImporter* pImp) { #ifdef ASSIMP_BUILD_DEBUG if (IsExtensionSupported(*it)) { - ASSIMP_LOG_WARN_F("The file extension ", *it, " is already in use"); + ASSIMP_LOG_WARN("The file extension ", *it, " is already in use"); } #endif baked += *it; @@ -240,7 +240,7 @@ aiReturn Importer::RegisterLoader(BaseImporter* pImp) { // add the loader pimpl->mImporter.push_back(pImp); - ASSIMP_LOG_INFO_F("Registering custom importer for these file extensions: ", baked); + ASSIMP_LOG_INFO("Registering custom importer for these file extensions: ", baked); ASSIMP_END_EXCEPTION_REGION(aiReturn); return AI_SUCCESS; @@ -519,7 +519,7 @@ const aiScene* Importer::ReadFileFromMemory( const void* pBuffer, // ------------------------------------------------------------------------------------------------ void WriteLogOpening(const std::string& file) { - ASSIMP_LOG_INFO_F("Load ", file); + ASSIMP_LOG_INFO("Load ", file); // print a full version dump. This is nice because we don't // need to ask the authors of incoming bug reports for diff --git a/code/Common/SceneCombiner.cpp b/code/Common/SceneCombiner.cpp index fe00dfe1f..555d46b6a 100644 --- a/code/Common/SceneCombiner.cpp +++ b/code/Common/SceneCombiner.cpp @@ -612,7 +612,7 @@ void SceneCombiner::MergeScenes(aiScene **_dest, aiScene *master, std::vectormName.data, + ASSIMP_LOG_ERROR("SceneCombiner: Failed to resolve attachment ", (*it).node->mName.data, " ", (*it).attachToNode->mName.data); } } diff --git a/code/Common/Subdivision.cpp b/code/Common/Subdivision.cpp index 76fc83404..9e7577a04 100644 --- a/code/Common/Subdivision.cpp +++ b/code/Common/Subdivision.cpp @@ -336,7 +336,7 @@ void CatmullClarkSubdivider::InternSubdivide( // Report the number of bad edges. bad edges are referenced by less than two // faces in the mesh. They occur at outer model boundaries in non-closed // shapes. - ASSIMP_LOG_VERBOSE_DEBUG_F("Catmull-Clark Subdivider: got ", bad_cnt, " bad edges touching only one face (totally ", + ASSIMP_LOG_VERBOSE_DEBUG("Catmull-Clark Subdivider: got ", bad_cnt, " bad edges touching only one face (totally ", static_cast(edges.size()), " edges). "); } } diff --git a/code/PostProcessing/ArmaturePopulate.cpp b/code/PostProcessing/ArmaturePopulate.cpp index 88b0b2d7c..fdf7508d0 100644 --- a/code/PostProcessing/ArmaturePopulate.cpp +++ b/code/PostProcessing/ArmaturePopulate.cpp @@ -77,12 +77,12 @@ void ArmaturePopulate::Execute(aiScene *out) { BuildBoneStack(out->mRootNode, out->mRootNode, out, bones, bone_stack, nodes); - ASSIMP_LOG_DEBUG_F("Bone stack size: ", bone_stack.size()); + ASSIMP_LOG_DEBUG("Bone stack size: ", bone_stack.size()); for (std::pair kvp : bone_stack) { aiBone *bone = kvp.first; aiNode *bone_node = kvp.second; - ASSIMP_LOG_VERBOSE_DEBUG_F("active node lookup: ", bone->mName.C_Str()); + ASSIMP_LOG_VERBOSE_DEBUG("active node lookup: ", bone->mName.C_Str()); // lcl transform grab - done in generate_nodes :) // bone->mOffsetMatrix = bone_node->mTransformation; @@ -179,7 +179,7 @@ void ArmaturePopulate::BuildBoneStack(aiNode *, if (node == nullptr) { node_stack.clear(); BuildNodeList(root_node, node_stack); - ASSIMP_LOG_VERBOSE_DEBUG_F("Resetting bone stack: nullptr element ", bone->mName.C_Str()); + ASSIMP_LOG_VERBOSE_DEBUG("Resetting bone stack: nullptr element ", bone->mName.C_Str()); node = GetNodeFromStack(bone->mName, node_stack); @@ -189,7 +189,7 @@ void ArmaturePopulate::BuildBoneStack(aiNode *, } } - ASSIMP_LOG_VERBOSE_DEBUG_F("Successfully added bone[", bone->mName.C_Str(), "] to stack and bone node is: ", node->mName.C_Str()); + ASSIMP_LOG_VERBOSE_DEBUG("Successfully added bone[", bone->mName.C_Str(), "] to stack and bone node is: ", node->mName.C_Str()); bone_stack.insert(std::pair(bone, node)); } @@ -203,7 +203,7 @@ aiNode *ArmaturePopulate::GetArmatureRoot(aiNode *bone_node, std::vector &bone_list) { while (nullptr != bone_node) { if (!IsBoneNode(bone_node->mName, bone_list)) { - ASSIMP_LOG_VERBOSE_DEBUG_F("GetArmatureRoot() Found valid armature: ", bone_node->mName.C_Str()); + ASSIMP_LOG_VERBOSE_DEBUG("GetArmatureRoot() Found valid armature: ", bone_node->mName.C_Str()); return bone_node; } @@ -247,7 +247,7 @@ aiNode *ArmaturePopulate::GetNodeFromStack(const aiString &node_name, } if (found != nullptr) { - ASSIMP_LOG_INFO_F("Removed node from stack: ", found->mName.C_Str()); + ASSIMP_LOG_INFO("Removed node from stack: ", found->mName.C_Str()); // now pop the element from the node list nodes.erase(iter); diff --git a/code/PostProcessing/CalcTangentsProcess.cpp b/code/PostProcessing/CalcTangentsProcess.cpp index 721567857..46feff4a1 100644 --- a/code/PostProcessing/CalcTangentsProcess.cpp +++ b/code/PostProcessing/CalcTangentsProcess.cpp @@ -129,7 +129,7 @@ bool CalcTangentsProcess::ProcessMesh(aiMesh *pMesh, unsigned int meshIndex) { return false; } if (configSourceUV >= AI_MAX_NUMBER_OF_TEXTURECOORDS || !pMesh->mTextureCoords[configSourceUV]) { - ASSIMP_LOG_ERROR_F("Failed to compute tangents; need UV data in channel", configSourceUV); + ASSIMP_LOG_ERROR("Failed to compute tangents; need UV data in channel", configSourceUV); return false; } diff --git a/code/PostProcessing/DeboneProcess.cpp b/code/PostProcessing/DeboneProcess.cpp index b5ed66268..3783bb65c 100644 --- a/code/PostProcessing/DeboneProcess.cpp +++ b/code/PostProcessing/DeboneProcess.cpp @@ -148,7 +148,7 @@ void DeboneProcess::Execute( aiScene* pScene) } if(!DefaultLogger::isNullLogger()) { - ASSIMP_LOG_INFO_F("Removed %u bones. Input bones:", in - out, ". Output bones: ", out); + ASSIMP_LOG_INFO("Removed %u bones. Input bones:", in - out, ". Output bones: ", out); } // and destroy the source mesh. It should be completely contained inside the new submeshes diff --git a/code/PostProcessing/EmbedTexturesProcess.cpp b/code/PostProcessing/EmbedTexturesProcess.cpp index dc304ff9c..7e435e556 100644 --- a/code/PostProcessing/EmbedTexturesProcess.cpp +++ b/code/PostProcessing/EmbedTexturesProcess.cpp @@ -93,7 +93,7 @@ void EmbedTexturesProcess::Execute(aiScene* pScene) { } } - ASSIMP_LOG_INFO_F("EmbedTexturesProcess finished. Embedded ", embeddedTexturesCount, " textures." ); + ASSIMP_LOG_INFO("EmbedTexturesProcess finished. Embedded ", embeddedTexturesCount, " textures." ); } bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const { @@ -103,7 +103,7 @@ bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const { // Test path directly std::ifstream file(imagePath, std::ios::binary | std::ios::ate); if ((imageSize = file.tellg()) == std::streampos(-1)) { - ASSIMP_LOG_WARN_F("EmbedTexturesProcess: Cannot find image: ", imagePath, ". Will try to find it in root folder."); + ASSIMP_LOG_WARN("EmbedTexturesProcess: Cannot find image: ", imagePath, ". Will try to find it in root folder."); // Test path in root path imagePath = mRootPath + path; @@ -113,7 +113,7 @@ bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const { imagePath = mRootPath + path.substr(path.find_last_of("\\/") + 1u); file.open(imagePath, std::ios::binary | std::ios::ate); if ((imageSize = file.tellg()) == std::streampos(-1)) { - ASSIMP_LOG_ERROR_F("EmbedTexturesProcess: Unable to embed texture: ", path, "."); + ASSIMP_LOG_ERROR("EmbedTexturesProcess: Unable to embed texture: ", path, "."); return false; } } diff --git a/code/PostProcessing/FindDegenerates.cpp b/code/PostProcessing/FindDegenerates.cpp index 1fc58cf59..eae91ff02 100644 --- a/code/PostProcessing/FindDegenerates.cpp +++ b/code/PostProcessing/FindDegenerates.cpp @@ -291,7 +291,7 @@ evil_jump_outside: } if (deg && !DefaultLogger::isNullLogger()) { - ASSIMP_LOG_WARN_F( "Found ", deg, " degenerated primitives"); + ASSIMP_LOG_WARN( "Found ", deg, " degenerated primitives"); } return false; } diff --git a/code/PostProcessing/FindInstancesProcess.cpp b/code/PostProcessing/FindInstancesProcess.cpp index 98527e531..ab5f52b78 100644 --- a/code/PostProcessing/FindInstancesProcess.cpp +++ b/code/PostProcessing/FindInstancesProcess.cpp @@ -267,7 +267,7 @@ void FindInstancesProcess::Execute( aiScene* pScene) // write to log if (!DefaultLogger::isNullLogger()) { - ASSIMP_LOG_INFO_F( "FindInstancesProcess finished. Found ", (pScene->mNumMeshes - numMeshesOut), " instances" ); + ASSIMP_LOG_INFO( "FindInstancesProcess finished. Found ", (pScene->mNumMeshes - numMeshesOut), " instances" ); } pScene->mNumMeshes = numMeshesOut; } else { diff --git a/code/PostProcessing/FindInvalidDataProcess.cpp b/code/PostProcessing/FindInvalidDataProcess.cpp index 9c19c606a..ea7d99094 100644 --- a/code/PostProcessing/FindInvalidDataProcess.cpp +++ b/code/PostProcessing/FindInvalidDataProcess.cpp @@ -200,7 +200,7 @@ inline bool ProcessArray(T *&in, unsigned int num, const char *name, const std::vector &dirtyMask, bool mayBeIdentical = false, bool mayBeZero = true) { const char *err = ValidateArrayContents(in, num, dirtyMask, mayBeIdentical, mayBeZero); if (err) { - ASSIMP_LOG_ERROR_F("FindInvalidDataProcess fails on mesh ", name, ": ", err); + ASSIMP_LOG_ERROR("FindInvalidDataProcess fails on mesh ", name, ": ", err); delete[] in; in = nullptr; return true; diff --git a/code/PostProcessing/FixNormalsStep.cpp b/code/PostProcessing/FixNormalsStep.cpp index 850da5e25..09b1f1908 100644 --- a/code/PostProcessing/FixNormalsStep.cpp +++ b/code/PostProcessing/FixNormalsStep.cpp @@ -164,7 +164,7 @@ bool FixInfacingNormalsProcess::ProcessMesh( aiMesh* pcMesh, unsigned int index) // now compare the volumes of the bounding boxes if (std::fabs(fDelta0_x * fDelta0_y * fDelta0_z) < std::fabs(fDelta1_x * fDelta1_yz)) { if (!DefaultLogger::isNullLogger()) { - ASSIMP_LOG_INFO_F("Mesh ", index, ": Normals are facing inwards (or the mesh is planar)", index); + ASSIMP_LOG_INFO("Mesh ", index, ": Normals are facing inwards (or the mesh is planar)", index); } // Invert normals diff --git a/code/PostProcessing/ImproveCacheLocality.cpp b/code/PostProcessing/ImproveCacheLocality.cpp index 38d5c5a5b..3243c40b4 100644 --- a/code/PostProcessing/ImproveCacheLocality.cpp +++ b/code/PostProcessing/ImproveCacheLocality.cpp @@ -109,7 +109,7 @@ void ImproveCacheLocalityProcess::Execute( aiScene* pScene) { } if (!DefaultLogger::isNullLogger()) { if (numf > 0) { - ASSIMP_LOG_INFO_F("Cache relevant are ", numm, " meshes (", numf, " faces). Average output ACMR is ", out / numf); + ASSIMP_LOG_INFO("Cache relevant are ", numm, " meshes (", numf, " faces). Average output ACMR is ", out / numf); } ASSIMP_LOG_DEBUG("ImproveCacheLocalityProcess finished. "); } @@ -355,7 +355,7 @@ ai_real ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int me // very intense verbose logging ... prepare for much text if there are many meshes if ( DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE) { - ASSIMP_LOG_VERBOSE_DEBUG_F("Mesh %u | ACMR in: ", meshNum, " out: ", fACMR, " | ~", fACMR2, ((fACMR - fACMR2) / fACMR) * 100.f); + ASSIMP_LOG_VERBOSE_DEBUG("Mesh %u | ACMR in: ", meshNum, " out: ", fACMR, " | ~", fACMR2, ((fACMR - fACMR2) / fACMR) * 100.f); } fACMR2 *= pMesh->mNumFaces; diff --git a/code/PostProcessing/JoinVerticesProcess.cpp b/code/PostProcessing/JoinVerticesProcess.cpp index 22b399471..60c64f3bf 100644 --- a/code/PostProcessing/JoinVerticesProcess.cpp +++ b/code/PostProcessing/JoinVerticesProcess.cpp @@ -100,7 +100,7 @@ void JoinVerticesProcess::Execute( aiScene* pScene) if (iNumOldVertices == iNumVertices) { ASSIMP_LOG_DEBUG("JoinVerticesProcess finished "); } else { - ASSIMP_LOG_INFO_F("JoinVerticesProcess finished | Verts in: ", iNumOldVertices, + ASSIMP_LOG_INFO("JoinVerticesProcess finished | Verts in: ", iNumOldVertices, " out: ", iNumVertices, " | ~", ((iNumOldVertices - iNumVertices) / (float)iNumOldVertices) * 100.f ); } @@ -373,7 +373,7 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) } if (!DefaultLogger::isNullLogger() && DefaultLogger::get()->getLogSeverity() == Logger::VERBOSE) { - ASSIMP_LOG_VERBOSE_DEBUG_F( + ASSIMP_LOG_VERBOSE_DEBUG( "Mesh ",meshIndex, " (", (pMesh->mName.length ? pMesh->mName.data : "unnamed"), diff --git a/code/PostProcessing/LimitBoneWeightsProcess.cpp b/code/PostProcessing/LimitBoneWeightsProcess.cpp index c7ecf1b00..fb8b49b91 100644 --- a/code/PostProcessing/LimitBoneWeightsProcess.cpp +++ b/code/PostProcessing/LimitBoneWeightsProcess.cpp @@ -191,6 +191,6 @@ void LimitBoneWeightsProcess::ProcessMesh(aiMesh* pMesh) pMesh->mNumBones = writeBone; if (!DefaultLogger::isNullLogger()) { - ASSIMP_LOG_INFO_F("Removed ", removed, " weights. Input bones: ", old_bones, ". Output bones: ", pMesh->mNumBones); + ASSIMP_LOG_INFO("Removed ", removed, " weights. Input bones: ", old_bones, ". Output bones: ", pMesh->mNumBones); } } diff --git a/code/PostProcessing/OptimizeGraph.cpp b/code/PostProcessing/OptimizeGraph.cpp index ad49b635a..e33c2ab18 100644 --- a/code/PostProcessing/OptimizeGraph.cpp +++ b/code/PostProcessing/OptimizeGraph.cpp @@ -335,7 +335,7 @@ void OptimizeGraphProcess::Execute(aiScene *pScene) { pScene->mRootNode->mParent = nullptr; if (!DefaultLogger::isNullLogger()) { if (nodes_in != nodes_out) { - ASSIMP_LOG_INFO_F("OptimizeGraphProcess finished; Input nodes: ", nodes_in, ", Output nodes: ", nodes_out); + ASSIMP_LOG_INFO("OptimizeGraphProcess finished; Input nodes: ", nodes_in, ", Output nodes: ", nodes_out); } else { ASSIMP_LOG_DEBUG("OptimizeGraphProcess finished"); } diff --git a/code/PostProcessing/OptimizeMeshes.cpp b/code/PostProcessing/OptimizeMeshes.cpp index dd81644de..939284ee4 100644 --- a/code/PostProcessing/OptimizeMeshes.cpp +++ b/code/PostProcessing/OptimizeMeshes.cpp @@ -151,7 +151,7 @@ void OptimizeMeshesProcess::Execute( aiScene* pScene) std::copy(output.begin(),output.end(),mScene->mMeshes); if (output.size() != num_old) { - ASSIMP_LOG_DEBUG_F("OptimizeMeshesProcess finished. Input meshes: ", num_old, ", Output meshes: ", pScene->mNumMeshes); + ASSIMP_LOG_DEBUG("OptimizeMeshesProcess finished. Input meshes: ", num_old, ", Output meshes: ", pScene->mNumMeshes); } else { ASSIMP_LOG_DEBUG( "OptimizeMeshesProcess finished" ); } diff --git a/code/PostProcessing/PretransformVertices.cpp b/code/PostProcessing/PretransformVertices.cpp index 2691ed488..e9a3af0d2 100644 --- a/code/PostProcessing/PretransformVertices.cpp +++ b/code/PostProcessing/PretransformVertices.cpp @@ -680,9 +680,9 @@ void PretransformVertices::Execute(aiScene *pScene) { if (!DefaultLogger::isNullLogger()) { ASSIMP_LOG_DEBUG("PretransformVerticesProcess finished"); - ASSIMP_LOG_INFO_F("Removed ", iOldNodes, " nodes and ", iOldAnimationChannels, " animation channels (", + ASSIMP_LOG_INFO("Removed ", iOldNodes, " nodes and ", iOldAnimationChannels, " animation channels (", CountNodes(pScene->mRootNode), " output nodes)"); - ASSIMP_LOG_INFO_F("Kept ", pScene->mNumLights, " lights and ", pScene->mNumCameras, " cameras."); - ASSIMP_LOG_INFO_F("Moved ", iOldMeshes, " meshes to WCS (number of output meshes: ", pScene->mNumMeshes, ")"); + ASSIMP_LOG_INFO("Kept ", pScene->mNumLights, " lights and ", pScene->mNumCameras, " cameras."); + ASSIMP_LOG_INFO("Moved ", iOldMeshes, " meshes to WCS (number of output meshes: ", pScene->mNumMeshes, ")"); } } diff --git a/code/PostProcessing/RemoveRedundantMaterials.cpp b/code/PostProcessing/RemoveRedundantMaterials.cpp index 5264d3608..c252f37a5 100644 --- a/code/PostProcessing/RemoveRedundantMaterials.cpp +++ b/code/PostProcessing/RemoveRedundantMaterials.cpp @@ -122,7 +122,7 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene) // Keep this material even if no mesh references it abReferenced[i] = true; - ASSIMP_LOG_VERBOSE_DEBUG_F( "Found positive match in exclusion list: \'", name.data, "\'"); + ASSIMP_LOG_VERBOSE_DEBUG( "Found positive match in exclusion list: \'", name.data, "\'"); } } } @@ -215,7 +215,7 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene) } else { - ASSIMP_LOG_INFO_F("RemoveRedundantMatsProcess finished. Removed ", redundantRemoved, " redundant and ", + ASSIMP_LOG_INFO("RemoveRedundantMatsProcess finished. Removed ", redundantRemoved, " redundant and ", unreferencedRemoved, " unused materials."); } } diff --git a/code/PostProcessing/SplitByBoneCountProcess.cpp b/code/PostProcessing/SplitByBoneCountProcess.cpp index f7dffb8f2..2613d8561 100644 --- a/code/PostProcessing/SplitByBoneCountProcess.cpp +++ b/code/PostProcessing/SplitByBoneCountProcess.cpp @@ -103,7 +103,7 @@ void SplitByBoneCountProcess::Execute( aiScene* pScene) if( !isNecessary ) { - ASSIMP_LOG_DEBUG_F("SplitByBoneCountProcess early-out: no meshes with more than ", mMaxBoneCount, " bones." ); + ASSIMP_LOG_DEBUG("SplitByBoneCountProcess early-out: no meshes with more than ", mMaxBoneCount, " bones." ); return; } @@ -151,7 +151,7 @@ void SplitByBoneCountProcess::Execute( aiScene* pScene) // recurse through all nodes and translate the node's mesh indices to fit the new mesh array UpdateNode( pScene->mRootNode); - ASSIMP_LOG_DEBUG_F( "SplitByBoneCountProcess end: split ", mSubMeshIndices.size(), " meshes into ", meshes.size(), " submeshes." ); + ASSIMP_LOG_DEBUG( "SplitByBoneCountProcess end: split ", mSubMeshIndices.size(), " meshes into ", meshes.size(), " submeshes." ); } // ------------------------------------------------------------------------------------------------ diff --git a/code/PostProcessing/TextureTransform.cpp b/code/PostProcessing/TextureTransform.cpp index a2f20a082..681b047c0 100644 --- a/code/PostProcessing/TextureTransform.cpp +++ b/code/PostProcessing/TextureTransform.cpp @@ -108,7 +108,7 @@ void TextureTransformStep::PreProcessUVTransform(STransformVecInfo& info) if (rounded) { out -= rounded * static_cast(AI_MATH_PI); - ASSIMP_LOG_INFO_F("Texture coordinate rotation ", info.mRotation, " can be simplified to ", out); + ASSIMP_LOG_INFO("Texture coordinate rotation ", info.mRotation, " can be simplified to ", out); } // Next step - convert negative rotation angles to positives @@ -448,7 +448,7 @@ void TextureTransformStep::Execute( aiScene* pScene) if (size > AI_MAX_NUMBER_OF_TEXTURECOORDS) { if (!DefaultLogger::isNullLogger()) { - ASSIMP_LOG_ERROR_F(static_cast(trafo.size()), " UV channels required but just ", + ASSIMP_LOG_ERROR(static_cast(trafo.size()), " UV channels required but just ", AI_MAX_NUMBER_OF_TEXTURECOORDS, " available"); } size = AI_MAX_NUMBER_OF_TEXTURECOORDS; @@ -557,7 +557,7 @@ void TextureTransformStep::Execute( aiScene* pScene) if (!DefaultLogger::isNullLogger()) { if (transformedChannels) { - ASSIMP_LOG_INFO_F("TransformUVCoordsProcess end: ", outChannels, " output channels (in: ", inChannels, ", modified: ", transformedChannels,")"); + ASSIMP_LOG_INFO("TransformUVCoordsProcess end: ", outChannels, " output channels (in: ", inChannels, ", modified: ", transformedChannels,")"); } else { ASSIMP_LOG_DEBUG("TransformUVCoordsProcess finished"); } diff --git a/include/assimp/LineSplitter.h b/include/assimp/LineSplitter.h index 7cb71476c..44e6f0f1e 100644 --- a/include/assimp/LineSplitter.h +++ b/include/assimp/LineSplitter.h @@ -72,7 +72,7 @@ for(LineSplitter splitter(stream);splitter;++splitter) { if (strtol(splitter[2]) > 5) { .. } } - ASSIMP_LOG_VERBOSE_DEBUG_F("Current line is: ", splitter.get_index()); + ASSIMP_LOG_VERBOSE_DEBUG("Current line is: ", splitter.get_index()); } @endcode */ diff --git a/include/assimp/Logger.hpp b/include/assimp/Logger.hpp index 71f458731..ecbd9df9b 100644 --- a/include/assimp/Logger.hpp +++ b/include/assimp/Logger.hpp @@ -312,34 +312,19 @@ Logger::LogSeverity Logger::getLogSeverity() const { } // Namespace Assimp // ------------------------------------------------------------------------------------------------ -#define ASSIMP_LOG_WARN_F(...) \ +#define ASSIMP_LOG_WARN(...) \ Assimp::DefaultLogger::get()->warn(__VA_ARGS__) -#define ASSIMP_LOG_ERROR_F(...) \ +#define ASSIMP_LOG_ERROR(...) \ Assimp::DefaultLogger::get()->error(__VA_ARGS__) -#define ASSIMP_LOG_DEBUG_F(...) \ +#define ASSIMP_LOG_DEBUG(...) \ Assimp::DefaultLogger::get()->debug(__VA_ARGS__) -#define ASSIMP_LOG_VERBOSE_DEBUG_F(...) \ +#define ASSIMP_LOG_VERBOSE_DEBUG(...) \ Assimp::DefaultLogger::get()->verboseDebug(__VA_ARGS__) -#define ASSIMP_LOG_INFO_F(...) \ +#define ASSIMP_LOG_INFO(...) \ Assimp::DefaultLogger::get()->info(__VA_ARGS__) -#define ASSIMP_LOG_WARN(string) \ - Assimp::DefaultLogger::get()->warn(string) - -#define ASSIMP_LOG_ERROR(string) \ - Assimp::DefaultLogger::get()->error(string) - -#define ASSIMP_LOG_DEBUG(string) \ - Assimp::DefaultLogger::get()->debug(string) - -#define ASSIMP_LOG_VERBOSE_DEBUG(string) \ - Assimp::DefaultLogger::get()->verboseDebug(string) - -#define ASSIMP_LOG_INFO(string) \ - Assimp::DefaultLogger::get()->info(string) - #endif // !! INCLUDED_AI_LOGGER_H diff --git a/include/assimp/Profiler.h b/include/assimp/Profiler.h index 566ea84e1..2f2c72bf2 100644 --- a/include/assimp/Profiler.h +++ b/include/assimp/Profiler.h @@ -76,7 +76,7 @@ public: /** Start a named timer */ void BeginRegion(const std::string& region) { regions[region] = std::chrono::system_clock::now(); - ASSIMP_LOG_DEBUG_F("START `",region,"`"); + ASSIMP_LOG_DEBUG("START `",region,"`"); } @@ -88,7 +88,7 @@ public: } std::chrono::duration elapsedSeconds = std::chrono::system_clock::now() - regions[region]; - ASSIMP_LOG_DEBUG_F("END `",region,"`, dt= ", elapsedSeconds.count()," s"); + ASSIMP_LOG_DEBUG("END `",region,"`, dt= ", elapsedSeconds.count()," s"); } private: diff --git a/include/assimp/fast_atof.h b/include/assimp/fast_atof.h index 441fe5652..aea793f35 100644 --- a/include/assimp/fast_atof.h +++ b/include/assimp/fast_atof.h @@ -206,7 +206,7 @@ uint64_t strtoul10_64( const char* in, const char** out=0, unsigned int* max_ino // numeric overflow, we rely on you if ( new_value < value ) { - ASSIMP_LOG_WARN_F( "Converting the string \"", in, "\" into a value resulted in overflow." ); + ASSIMP_LOG_WARN( "Converting the string \"", in, "\" into a value resulted in overflow." ); return 0; } From 7abfd134b60eeed6d7f775eea2e6509051fdd079 Mon Sep 17 00:00:00 2001 From: Malcolm Tyrrell Date: Thu, 13 May 2021 10:33:16 +0100 Subject: [PATCH 110/335] LogAux warn --- code/AssetLib/Blender/BlenderLoader.cpp | 4 ++-- code/AssetLib/FBX/FBXMeshGeometry.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/code/AssetLib/Blender/BlenderLoader.cpp b/code/AssetLib/Blender/BlenderLoader.cpp index 8cc0162f5..f38831567 100644 --- a/code/AssetLib/Blender/BlenderLoader.cpp +++ b/code/AssetLib/Blender/BlenderLoader.cpp @@ -516,7 +516,7 @@ void BlenderImporter::ResolveTexture(aiMaterial *out, const Material *mat, const case Tex::Type_POINTDENSITY: case Tex::Type_VOXELDATA: - LogWarn(std::string("Encountered a texture with an unsupported type: ") + dispnam); + LogWarn("Encountered a texture with an unsupported type: ", dispnam); AddSentinelTexture(out, mat, tex, conv_data); break; @@ -752,7 +752,7 @@ void BlenderImporter::CheckActualType(const ElemBase *dt, const char *check) { // ------------------------------------------------------------------------------------------------ void BlenderImporter::NotSupportedObjectType(const Object *obj, const char *type) { - LogWarn((format(), "Object `", obj->id.name, "` - type is unsupported: `", type, "`, skipping")); + LogWarn("Object `", obj->id.name, "` - type is unsupported: `", type, "`, skipping"); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/FBX/FBXMeshGeometry.cpp b/code/AssetLib/FBX/FBXMeshGeometry.cpp index 2bca8dff2..a88045210 100644 --- a/code/AssetLib/FBX/FBXMeshGeometry.cpp +++ b/code/AssetLib/FBX/FBXMeshGeometry.cpp @@ -509,8 +509,8 @@ void ResolveVertexDataArray(std::vector& data_out, const Scope& source, ParseVectorDataArray(uvIndices,GetRequiredElement(source,indexDataElementName)); if (uvIndices.size() > vertex_count) { - FBXImporter::LogWarn(Formatter::format("trimming length of input array for ByPolygonVertex mapping: ") - << uvIndices.size() << ", expected " << vertex_count); + FBXImporter::LogWarn("trimming length of input array for ByPolygonVertex mapping: ", + uvIndices.size(), ", expected ", vertex_count); uvIndices.resize(vertex_count); } @@ -645,7 +645,7 @@ void MeshGeometry::ReadVertexDataMaterials(std::vector& materials_out, cons FBXImporter::LogError(Formatter::format("expected material index, ignoring")); return; } else if (materials_out.size() > 1) { - FBXImporter::LogWarn(Formatter::format("expected only a single material index, ignoring all except the first one")); + FBXImporter::LogWarn("expected only a single material index, ignoring all except the first one"); materials_out.clear(); } From c5f22269a8ae5ea491f36d9fc5a92ddb495433bc Mon Sep 17 00:00:00 2001 From: Malcolm Tyrrell Date: Thu, 13 May 2021 10:33:25 +0100 Subject: [PATCH 111/335] LogAux --- include/assimp/LogAux.h | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/include/assimp/LogAux.h b/include/assimp/LogAux.h index b0a1f9ddd..6ca1912d3 100644 --- a/include/assimp/LogAux.h +++ b/include/assimp/LogAux.h @@ -64,13 +64,14 @@ public: template static void ThrowException(T&&... args) { - throw DeadlyImportError(Prefix(), args...); + throw DeadlyImportError(Prefix(), std::forward(args)...); } // ------------------------------------------------------------------------------------------------ - static void LogWarn(const Formatter::format& message) { + template + static void LogWarn(T&&... args) { if (!DefaultLogger::isNullLogger()) { - ASSIMP_LOG_WARN(Prefix()+(std::string)message); + ASSIMP_LOG_WARN(Prefix(), std::forward(args)...); } } @@ -104,13 +105,6 @@ public: // https://sourceforge.net/tracker/?func=detail&atid=1067632&aid=3358562&group_id=226462 #if !defined(__GNUC__) || !defined(__APPLE__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) - // ------------------------------------------------------------------------------------------------ - static void LogWarn (const char* message) { - if (!DefaultLogger::isNullLogger()) { - LogWarn(Formatter::format(message)); - } - } - // ------------------------------------------------------------------------------------------------ static void LogError (const char* message) { if (!DefaultLogger::isNullLogger()) { From ad6f300b1dbfefdd67097b0ed8ba8eeab1725a1e Mon Sep 17 00:00:00 2001 From: Malcolm Tyrrell Date: Thu, 13 May 2021 10:43:28 +0100 Subject: [PATCH 112/335] Other LogAux functions --- code/AssetLib/Blender/BlenderLoader.cpp | 6 +-- code/AssetLib/FBX/FBXMeshGeometry.cpp | 42 ++++++++++---------- code/AssetLib/IFC/IFCLoader.cpp | 6 +-- include/assimp/LogAux.h | 51 ++++++------------------- 4 files changed, 39 insertions(+), 66 deletions(-) diff --git a/code/AssetLib/Blender/BlenderLoader.cpp b/code/AssetLib/Blender/BlenderLoader.cpp index f38831567..7cf4e070e 100644 --- a/code/AssetLib/Blender/BlenderLoader.cpp +++ b/code/AssetLib/Blender/BlenderLoader.cpp @@ -235,9 +235,9 @@ void BlenderImporter::InternReadFile(const std::string &pFile, stream->Read(magic, 3, 1); magic[3] = '\0'; - LogInfo((format(), "Blender version is ", magic[0], ".", magic + 1, + LogInfo("Blender version is ", magic[0], ".", magic + 1, " (64bit: ", file.i64bit ? "true" : "false", - ", little endian: ", file.little ? "true" : "false", ")")); + ", little endian: ", file.little ? "true" : "false", ")"); ParseBlendFile(file, stream); @@ -434,7 +434,7 @@ void BlenderImporter::ResolveImage(aiMaterial *out, const Material *mat, const M curTex->pcData = reinterpret_cast(ch); - LogInfo("Reading embedded texture, original file was " + std::string(img->name)); + LogInfo("Reading embedded texture, original file was ", img->name); } else { name = aiString(img->name); } diff --git a/code/AssetLib/FBX/FBXMeshGeometry.cpp b/code/AssetLib/FBX/FBXMeshGeometry.cpp index a88045210..9bafcbd2a 100644 --- a/code/AssetLib/FBX/FBXMeshGeometry.cpp +++ b/code/AssetLib/FBX/FBXMeshGeometry.cpp @@ -307,8 +307,8 @@ void MeshGeometry::ReadLayerElement(const Scope& layerElement) } } - FBXImporter::LogError(Formatter::format("failed to resolve vertex layer element: ") - << type << ", index: " << typedIndex); + FBXImporter::LogError("failed to resolve vertex layer element: ", + type, ", index: ", typedIndex); } // ------------------------------------------------------------------------------------------------ @@ -324,8 +324,8 @@ void MeshGeometry::ReadVertexData(const std::string& type, int index, const Scop if (type == "LayerElementUV") { if(index >= AI_MAX_NUMBER_OF_TEXTURECOORDS) { - FBXImporter::LogError(Formatter::format("ignoring UV layer, maximum number of UV channels exceeded: ") - << index << " (limit is " << AI_MAX_NUMBER_OF_TEXTURECOORDS << ")" ); + FBXImporter::LogError("ignoring UV layer, maximum number of UV channels exceeded: ", + index, " (limit is ", AI_MAX_NUMBER_OF_TEXTURECOORDS, ")" ); return; } @@ -402,8 +402,8 @@ void MeshGeometry::ReadVertexData(const std::string& type, int index, const Scop } else if (type == "LayerElementColor") { if(index >= AI_MAX_NUMBER_OF_COLOR_SETS) { - FBXImporter::LogError(Formatter::format("ignoring vertex color layer, maximum number of color sets exceeded: ") - << index << " (limit is " << AI_MAX_NUMBER_OF_COLOR_SETS << ")" ); + FBXImporter::LogError("ignoring vertex color layer, maximum number of color sets exceeded: ", + index, " (limit is ", AI_MAX_NUMBER_OF_COLOR_SETS, ")" ); return; } @@ -449,8 +449,8 @@ void ResolveVertexDataArray(std::vector& data_out, const Scope& source, ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName)); if (tempData.size() != mapping_offsets.size()) { - FBXImporter::LogError(Formatter::format("length of input data unexpected for ByVertice mapping: ") - << tempData.size() << ", expected " << mapping_offsets.size()); + FBXImporter::LogError("length of input data unexpected for ByVertice mapping: ", + tempData.size(), ", expected ", mapping_offsets.size()); return; } @@ -470,8 +470,8 @@ void ResolveVertexDataArray(std::vector& data_out, const Scope& source, ParseVectorDataArray(uvIndices,GetRequiredElement(source,indexDataElementName)); if (uvIndices.size() != vertex_count) { - FBXImporter::LogError(Formatter::format("length of input data unexpected for ByVertice mapping: ") - << uvIndices.size() << ", expected " << vertex_count); + FBXImporter::LogError("length of input data unexpected for ByVertice mapping: ", + uvIndices.size(), ", expected ", vertex_count); return; } @@ -493,8 +493,8 @@ void ResolveVertexDataArray(std::vector& data_out, const Scope& source, ParseVectorDataArray(tempData, GetRequiredElement(source, dataElementName)); if (tempData.size() != vertex_count) { - FBXImporter::LogError(Formatter::format("length of input data unexpected for ByPolygon mapping: ") - << tempData.size() << ", expected " << vertex_count + FBXImporter::LogError("length of input data unexpected for ByPolygon mapping: ", + tempData.size(), ", expected ", vertex_count ); return; } @@ -515,8 +515,8 @@ void ResolveVertexDataArray(std::vector& data_out, const Scope& source, } if (uvIndices.size() != vertex_count) { - FBXImporter::LogError(Formatter::format("length of input data unexpected for ByPolygonVertex mapping: ") - << uvIndices.size() << ", expected " << vertex_count); + FBXImporter::LogError("length of input data unexpected for ByPolygonVertex mapping: ", + uvIndices.size(), ", expected ", vertex_count); return; } @@ -537,8 +537,8 @@ void ResolveVertexDataArray(std::vector& data_out, const Scope& source, } } else { - FBXImporter::LogError(Formatter::format("ignoring vertex data channel, access type not implemented: ") - << MappingInformationType << "," << ReferenceInformationType); + FBXImporter::LogError("ignoring vertex data channel, access type not implemented: ", + MappingInformationType, ",", ReferenceInformationType); } } @@ -642,7 +642,7 @@ void MeshGeometry::ReadVertexDataMaterials(std::vector& materials_out, cons if (MappingInformationType == "AllSame") { // easy - same material for all faces if (materials_out.empty()) { - FBXImporter::LogError(Formatter::format("expected material index, ignoring")); + FBXImporter::LogError("expected material index, ignoring"); return; } else if (materials_out.size() > 1) { FBXImporter::LogWarn("expected only a single material index, ignoring all except the first one"); @@ -655,14 +655,14 @@ void MeshGeometry::ReadVertexDataMaterials(std::vector& materials_out, cons materials_out.resize(face_count); if(materials_out.size() != face_count) { - FBXImporter::LogError(Formatter::format("length of input data unexpected for ByPolygon mapping: ") - << materials_out.size() << ", expected " << face_count + FBXImporter::LogError("length of input data unexpected for ByPolygon mapping: ", + materials_out.size(), ", expected ", face_count ); return; } } else { - FBXImporter::LogError(Formatter::format("ignoring material assignments, access type not implemented: ") - << MappingInformationType << "," << ReferenceInformationType); + FBXImporter::LogError("ignoring material assignments, access type not implemented: ", + MappingInformationType, ",", ReferenceInformationType); } } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/IFC/IFCLoader.cpp b/code/AssetLib/IFC/IFCLoader.cpp index bdac1801a..46b36bd51 100644 --- a/code/AssetLib/IFC/IFCLoader.cpp +++ b/code/AssetLib/IFC/IFCLoader.cpp @@ -315,7 +315,7 @@ void IFCImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy // this must be last because objects are evaluated lazily as we process them if (!DefaultLogger::isNullLogger()) { - LogDebug((Formatter::format(), "STEP: evaluated ", db->GetEvaluatedObjectCount(), " object records")); + LogDebug("STEP: evaluated ", db->GetEvaluatedObjectCount(), " object records"); } } @@ -438,7 +438,7 @@ bool ProcessMappedItem(const Schema_2x3::IfcMappedItem &mapped, aiNode *nd_src, bool got = false; for (const Schema_2x3::IfcRepresentationItem &item : repr.Items) { if (!ProcessRepresentationItem(item, localmatid, meshes, conv)) { - IFCImporter::LogWarn("skipping mapped entity of type " + item.GetClassName() + ", no representations could be generated"); + IFCImporter::LogWarn("skipping mapped entity of type ", item.GetClassName(), ", no representations could be generated"); } else got = true; } @@ -856,7 +856,7 @@ void ProcessSpatialStructures(ConversionData &conv) { if (!prod) { continue; } - IFCImporter::LogVerboseDebug("looking at spatial structure `" + (prod->Name ? prod->Name.Get() : "unnamed") + "`" + (prod->ObjectType ? " which is of type " + prod->ObjectType.Get() : "")); + IFCImporter::LogVerboseDebug("looking at spatial structure `", (prod->Name ? prod->Name.Get() : "unnamed"), "`", (prod->ObjectType ? " which is of type " + prod->ObjectType.Get() : "")); // the primary sites are referenced by an IFCRELAGGREGATES element which assigns them to the IFCPRODUCT const STEP::DB::RefMap &refs = conv.db.GetRefs(); diff --git a/include/assimp/LogAux.h b/include/assimp/LogAux.h index 6ca1912d3..065433648 100644 --- a/include/assimp/LogAux.h +++ b/include/assimp/LogAux.h @@ -76,64 +76,37 @@ public: } // ------------------------------------------------------------------------------------------------ - static void LogError(const Formatter::format& message) { + template + static void LogError(T&&... args) { if (!DefaultLogger::isNullLogger()) { - ASSIMP_LOG_ERROR(Prefix()+(std::string)message); + ASSIMP_LOG_ERROR(Prefix(), std::forward(args)...); } } // ------------------------------------------------------------------------------------------------ - static void LogInfo(const Formatter::format& message) { + template + static void LogInfo(T&&... args) { if (!DefaultLogger::isNullLogger()) { - ASSIMP_LOG_INFO(Prefix()+(std::string)message); + ASSIMP_LOG_INFO(Prefix(), std::forward(args)...); } } // ------------------------------------------------------------------------------------------------ - static void LogDebug(const Formatter::format& message) { + template + static void LogDebug(T&&... args) { if (!DefaultLogger::isNullLogger()) { - ASSIMP_LOG_DEBUG(Prefix()+(std::string)message); - } - } - - static void LogVerboseDebug(const Formatter::format& message) { - if (!DefaultLogger::isNullLogger()) { - ASSIMP_LOG_VERBOSE_DEBUG(Prefix()+(std::string)message); - } - } - - // https://sourceforge.net/tracker/?func=detail&atid=1067632&aid=3358562&group_id=226462 -#if !defined(__GNUC__) || !defined(__APPLE__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) - - // ------------------------------------------------------------------------------------------------ - static void LogError (const char* message) { - if (!DefaultLogger::isNullLogger()) { - LogError(Formatter::format(message)); + ASSIMP_LOG_DEBUG(Prefix(), std::forward(args)...); } } // ------------------------------------------------------------------------------------------------ - static void LogInfo (const char* message) { + template + static void LogVerboseDebug(T&&... args) { if (!DefaultLogger::isNullLogger()) { - LogInfo(Formatter::format(message)); + ASSIMP_LOG_VERBOSE_DEBUG(Prefix(), std::forward(args)...); } } - // ------------------------------------------------------------------------------------------------ - static void LogDebug (const char* message) { - if (!DefaultLogger::isNullLogger()) { - LogDebug(Formatter::format(message)); - } - } - - // ------------------------------------------------------------------------------------------------ - static void LogVerboseDebug (const char* message) { - if (!DefaultLogger::isNullLogger()) { - LogVerboseDebug(Formatter::format(message)); - } - } -#endif - private: static const char* Prefix(); From 4ec01cfdcdb6f0e917e98e09c64e721d26eebc89 Mon Sep 17 00:00:00 2001 From: Malcolm Tyrrell Date: Thu, 13 May 2021 12:05:31 +0100 Subject: [PATCH 113/335] Improve use of logging --- code/AssetLib/3DS/3DSExporter.cpp | 2 +- code/AssetLib/ASE/ASEParser.cpp | 2 +- code/AssetLib/C4D/C4DImporter.cpp | 6 +-- code/AssetLib/FBX/FBXConverter.cpp | 40 +++++++++---------- code/AssetLib/FBX/FBXDocumentUtil.cpp | 2 +- code/AssetLib/IFC/IFCBoolean.cpp | 4 +- code/AssetLib/IFC/IFCGeometry.cpp | 6 +-- code/AssetLib/IFC/IFCLoader.cpp | 8 ++-- code/AssetLib/IFC/IFCMaterial.cpp | 4 +- code/AssetLib/IFC/IFCProfile.cpp | 8 ++-- code/AssetLib/IFC/IFCUtil.cpp | 2 +- code/AssetLib/Irr/IRRLoader.cpp | 8 ++-- code/AssetLib/Irr/IRRShared.cpp | 2 +- code/AssetLib/LWO/LWOLoader.cpp | 6 +-- code/AssetLib/LWO/LWOMaterial.cpp | 2 +- code/AssetLib/LWS/LWSLoader.cpp | 4 +- code/AssetLib/M3D/M3DImporter.cpp | 2 +- code/AssetLib/MD3/MD3Loader.cpp | 8 ++-- code/AssetLib/MD5/MD5Loader.cpp | 4 +- code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp | 2 +- code/AssetLib/NFF/NFFLoader.cpp | 2 +- code/AssetLib/Obj/ObjFileParser.cpp | 10 ++--- code/AssetLib/Ogre/OgreBinarySerializer.cpp | 2 +- code/AssetLib/Ogre/OgreMaterial.cpp | 2 +- code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp | 2 +- code/AssetLib/glTF2/glTF2Importer.cpp | 8 ++-- code/Common/FileSystemFilter.h | 2 +- code/Common/Importer.cpp | 2 +- code/Material/MaterialSystem.cpp | 7 ++-- code/PostProcessing/ValidateDataStructure.cpp | 2 +- 30 files changed, 80 insertions(+), 81 deletions(-) diff --git a/code/AssetLib/3DS/3DSExporter.cpp b/code/AssetLib/3DS/3DSExporter.cpp index 07e9ccfc7..92a6d5aa7 100644 --- a/code/AssetLib/3DS/3DSExporter.cpp +++ b/code/AssetLib/3DS/3DSExporter.cpp @@ -379,7 +379,7 @@ void Discreet3DSExporter::WriteTexture(const aiMaterial &mat, aiTextureType type // TODO: handle embedded textures properly if (path.data[0] == '*') { - ASSIMP_LOG_ERROR("Ignoring embedded texture for export: " + std::string(path.C_Str())); + ASSIMP_LOG_ERROR("Ignoring embedded texture for export: ", path.C_Str()); return; } diff --git a/code/AssetLib/ASE/ASEParser.cpp b/code/AssetLib/ASE/ASEParser.cpp index 4e4af8ed8..1f7018b11 100644 --- a/code/AssetLib/ASE/ASEParser.cpp +++ b/code/AssetLib/ASE/ASEParser.cpp @@ -1125,7 +1125,7 @@ void Parser::ParseLV2NodeTransformBlock(ASE::BaseNode &mesh) { "this is no spot light or target camera"); } } else { - ASSIMP_LOG_ERROR("ASE: Unknown node transformation: " + temp); + ASSIMP_LOG_ERROR("ASE: Unknown node transformation: ", temp); // mode = 0 } continue; diff --git a/code/AssetLib/C4D/C4DImporter.cpp b/code/AssetLib/C4D/C4DImporter.cpp index 594bcfddd..434d1429e 100644 --- a/code/AssetLib/C4D/C4DImporter.cpp +++ b/code/AssetLib/C4D/C4DImporter.cpp @@ -238,7 +238,7 @@ bool C4DImporter::ReadShader(aiMaterial* out, BaseShader* shader) { out->AddProperty(&path, AI_MATKEY_TEXTURE_DIFFUSE(0)); return true; } else { - LogWarn("ignoring shader type: " + std::string(GetObjectTypeName(shader->GetType()))); + LogWarn("ignoring shader type: ", GetObjectTypeName(shader->GetType())); } shader = shader->GetNext(); } @@ -281,7 +281,7 @@ void C4DImporter::ReadMaterials(BaseMaterial* mat) { ReadShader(out, shader); } } else { - LogWarn("ignoring plugin material: " + std::string(GetObjectTypeName(mat->GetType()))); + LogWarn("ignoring plugin material: ", GetObjectTypeName(mat->GetType())); } mat = mat->GetNext(); } @@ -335,7 +335,7 @@ void C4DImporter::RecurseHierarchy(BaseObject* object, aiNode* parent) { meshes.push_back(mesh); } } else { - LogWarn("ignoring object: " + std::string(GetObjectTypeName(type))); + LogWarn("ignoring object: ", GetObjectTypeName(type)); } RecurseHierarchy(object->GetDown(), nd); diff --git a/code/AssetLib/FBX/FBXConverter.cpp b/code/AssetLib/FBX/FBXConverter.cpp index 88d7ad626..8d37d3790 100644 --- a/code/AssetLib/FBX/FBXConverter.cpp +++ b/code/AssetLib/FBX/FBXConverter.cpp @@ -811,7 +811,7 @@ bool FBXConverter::GenerateTransformationNodeChain(const Model &model, const std // we need to generate a full node chain to accommodate for assimp's // lack to express pivots and offsets. if ((chainBits & chainMaskComplex) && doc.Settings().preservePivots) { - FBXImporter::LogInfo("generating full transformation chain for node: " + name); + FBXImporter::LogInfo("generating full transformation chain for node: ", name); // query the anim_chain_bits dictionary to find out which chain elements // have associated node animation channels. These can not be dropped @@ -918,7 +918,7 @@ void FBXConverter::ConvertModel(const Model &model, aiNode *parent, aiNode *root const std::vector &indices = ConvertLine(*line, root_node); std::copy(indices.begin(), indices.end(), std::back_inserter(meshes)); } else { - FBXImporter::LogWarn("ignoring unrecognized geometry: " + geo->Name()); + FBXImporter::LogWarn("ignoring unrecognized geometry: ", geo->Name()); } } @@ -944,7 +944,7 @@ FBXConverter::ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode * const std::vector &vertices = mesh.GetVertices(); const std::vector &faces = mesh.GetFaceIndexCounts(); if (vertices.empty() || faces.empty()) { - FBXImporter::LogWarn("ignoring empty geometry: " + mesh.Name()); + FBXImporter::LogWarn("ignoring empty geometry: ", mesh.Name()); return temp; } @@ -971,7 +971,7 @@ std::vector FBXConverter::ConvertLine(const LineGeometry &line, ai const std::vector &vertices = line.GetVertices(); const std::vector &indices = line.GetIndices(); if (vertices.empty() || indices.empty()) { - FBXImporter::LogWarn("ignoring empty line: " + line.Name()); + FBXImporter::LogWarn("ignoring empty line: ", line.Name()); return temp; } @@ -1815,14 +1815,14 @@ void FBXConverter::TrySetTextureProperties(aiMaterial *out_mat, const TextureMap } } if (index == -1) { - FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); + FBXImporter::LogWarn("did not find UV channel named ", uvSet, " in a mesh using this material"); continue; } if (uvIndex == -1) { uvIndex = index; } else { - FBXImporter::LogWarn("the UV channel named " + uvSet + + FBXImporter::LogWarn("the UV channel named ", uvSet, " appears at different positions in meshes, results will be wrong"); } } @@ -1839,7 +1839,7 @@ void FBXConverter::TrySetTextureProperties(aiMaterial *out_mat, const TextureMap } } if (index == -1) { - FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); + FBXImporter::LogWarn("did not find UV channel named ", uvSet, " in a mesh using this material"); } if (uvIndex == -1) { @@ -1848,7 +1848,7 @@ void FBXConverter::TrySetTextureProperties(aiMaterial *out_mat, const TextureMap } if (uvIndex == -1) { - FBXImporter::LogWarn("failed to resolve UV channel " + uvSet + ", using first UV channel"); + FBXImporter::LogWarn("failed to resolve UV channel ", uvSet, ", using first UV channel"); uvIndex = 0; } } @@ -1934,14 +1934,14 @@ void FBXConverter::TrySetTextureProperties(aiMaterial *out_mat, const LayeredTex } } if (index == -1) { - FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); + FBXImporter::LogWarn("did not find UV channel named ", uvSet, " in a mesh using this material"); continue; } if (uvIndex == -1) { uvIndex = index; } else { - FBXImporter::LogWarn("the UV channel named " + uvSet + + FBXImporter::LogWarn("the UV channel named ", uvSet, " appears at different positions in meshes, results will be wrong"); } } @@ -1958,7 +1958,7 @@ void FBXConverter::TrySetTextureProperties(aiMaterial *out_mat, const LayeredTex } } if (index == -1) { - FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); + FBXImporter::LogWarn("did not find UV channel named ", uvSet, " in a mesh using this material"); } if (uvIndex == -1) { @@ -1967,7 +1967,7 @@ void FBXConverter::TrySetTextureProperties(aiMaterial *out_mat, const LayeredTex } if (uvIndex == -1) { - FBXImporter::LogWarn("failed to resolve UV channel " + uvSet + ", using first UV channel"); + FBXImporter::LogWarn("failed to resolve UV channel ", uvSet, ", using first UV channel"); uvIndex = 0; } } @@ -2319,14 +2319,14 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial *out_mat, const PropertyTa } } if (index == -1) { - FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); + FBXImporter::LogWarn("did not find UV channel named ", uvSet, " in a mesh using this material"); continue; } if (uvIndex == -1) { uvIndex = index; } else { - FBXImporter::LogWarn("the UV channel named " + uvSet + " appears at different positions in meshes, results will be wrong"); + FBXImporter::LogWarn("the UV channel named ", uvSet, " appears at different positions in meshes, results will be wrong"); } } } else { @@ -2342,7 +2342,7 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial *out_mat, const PropertyTa } } if (index == -1) { - FBXImporter::LogWarn("did not find UV channel named " + uvSet + " in a mesh using this material"); + FBXImporter::LogWarn("did not find UV channel named ", uvSet, " in a mesh using this material"); } if (uvIndex == -1) { @@ -2351,7 +2351,7 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial *out_mat, const PropertyTa } if (uvIndex == -1) { - FBXImporter::LogWarn("failed to resolve UV channel " + uvSet + ", using first UV channel"); + FBXImporter::LogWarn("failed to resolve UV channel ", uvSet, ", using first UV channel"); uvIndex = 0; } } @@ -2574,7 +2574,7 @@ void FBXConverter::ConvertAnimationStack(const AnimationStack &st) { // empty animations would fail validation, so drop them delete anim; animations.pop_back(); - FBXImporter::LogInfo("ignoring empty AnimationStack (using IK?): " + name); + FBXImporter::LogInfo("ignoring empty AnimationStack (using IK?): ", name); return; } @@ -2707,13 +2707,13 @@ void FBXConverter::GenerateNodeAnimations(std::vector &node_anims, ai_assert(node); if (node->TargetProperty().empty()) { - FBXImporter::LogWarn("target property for animation curve not set: " + node->Name()); + FBXImporter::LogWarn("target property for animation curve not set: ", node->Name()); continue; } curve_node = node; if (node->Curves().empty()) { - FBXImporter::LogWarn("no animation curves assigned to AnimationCurveNode: " + node->Name()); + FBXImporter::LogWarn("no animation curves assigned to AnimationCurveNode: ", node->Name()); continue; } @@ -2748,7 +2748,7 @@ void FBXConverter::GenerateNodeAnimations(std::vector &node_anims, if (doc.Settings().optimizeEmptyAnimationCurves && IsRedundantAnimationData(target, comp, (chain[i]->second))) { - FBXImporter::LogVerboseDebug("dropping redundant animation channel for node " + target.Name()); + FBXImporter::LogVerboseDebug("dropping redundant animation channel for node ", target.Name()); continue; } diff --git a/code/AssetLib/FBX/FBXDocumentUtil.cpp b/code/AssetLib/FBX/FBXDocumentUtil.cpp index 77455198f..ab15298a8 100644 --- a/code/AssetLib/FBX/FBXDocumentUtil.cpp +++ b/code/AssetLib/FBX/FBXDocumentUtil.cpp @@ -91,7 +91,7 @@ void DOMWarning(const std::string& message, const Element* element /*= nullptr*/ return; } if(DefaultLogger::get()) { - ASSIMP_LOG_WARN("FBX-DOM: " + message); + ASSIMP_LOG_WARN("FBX-DOM: ", message); } } diff --git a/code/AssetLib/IFC/IFCBoolean.cpp b/code/AssetLib/IFC/IFCBoolean.cpp index 6015920c1..86cac7f46 100644 --- a/code/AssetLib/IFC/IFCBoolean.cpp +++ b/code/AssetLib/IFC/IFCBoolean.cpp @@ -715,7 +715,7 @@ void ProcessBoolean(const Schema_2x3::IfcBooleanResult &boolean, TempMesh &resul // DIFFERENCE if (const Schema_2x3::IfcBooleanResult *const clip = boolean.ToPtr()) { if (clip->Operator != "DIFFERENCE") { - IFCImporter::LogWarn("encountered unsupported boolean operator: " + (std::string)clip->Operator); + IFCImporter::LogWarn("encountered unsupported boolean operator: ", (std::string)clip->Operator); return; } @@ -756,7 +756,7 @@ void ProcessBoolean(const Schema_2x3::IfcBooleanResult &boolean, TempMesh &resul ProcessBooleanExtrudedAreaSolidDifference(as, result, first_operand, conv); } } else { - IFCImporter::LogWarn("skipping unknown IfcBooleanResult entity, type is " + boolean.GetClassName()); + IFCImporter::LogWarn("skipping unknown IfcBooleanResult entity, type is ", boolean.GetClassName()); } } diff --git a/code/AssetLib/IFC/IFCGeometry.cpp b/code/AssetLib/IFC/IFCGeometry.cpp index 664444443..6e645f1ae 100644 --- a/code/AssetLib/IFC/IFCGeometry.cpp +++ b/code/AssetLib/IFC/IFCGeometry.cpp @@ -216,7 +216,7 @@ void ProcessConnectedFaceSet(const Schema_2x3::IfcConnectedFaceSet& fset, TempMe } } else { - IFCImporter::LogWarn("skipping unknown IfcFaceBound entity, type is " + bound.Bound->GetClassName()); + IFCImporter::LogWarn("skipping unknown IfcFaceBound entity, type is ", bound.Bound->GetClassName()); continue; } @@ -729,7 +729,7 @@ void ProcessSweptAreaSolid(const Schema_2x3::IfcSweptAreaSolid& swept, TempMesh& ProcessRevolvedAreaSolid(*rev,meshout,conv); } else { - IFCImporter::LogWarn("skipping unknown IfcSweptAreaSolid entity, type is " + swept.GetClassName()); + IFCImporter::LogWarn("skipping unknown IfcSweptAreaSolid entity, type is ", swept.GetClassName()); } } @@ -781,7 +781,7 @@ bool ProcessGeometricItem(const Schema_2x3::IfcRepresentationItem& geo, unsigned return false; } else { - IFCImporter::LogWarn("skipping unknown IfcGeometricRepresentationItem entity, type is " + geo.GetClassName()); + IFCImporter::LogWarn("skipping unknown IfcGeometricRepresentationItem entity, type is ", geo.GetClassName()); return false; } diff --git a/code/AssetLib/IFC/IFCLoader.cpp b/code/AssetLib/IFC/IFCLoader.cpp index 46b36bd51..daf2cc946 100644 --- a/code/AssetLib/IFC/IFCLoader.cpp +++ b/code/AssetLib/IFC/IFCLoader.cpp @@ -243,12 +243,12 @@ void IFCImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy } if (!DefaultLogger::isNullLogger()) { - LogDebug("File schema is \'" + head.fileSchema + '\''); + LogDebug("File schema is \'", head.fileSchema, '\''); if (head.timestamp.length()) { - LogDebug("Timestamp \'" + head.timestamp + '\''); + LogDebug("Timestamp \'", head.timestamp, '\''); } if (head.app.length()) { - LogDebug("Application/Exporter identline is \'" + head.app + '\''); + LogDebug("Application/Exporter identline is \'", head.app, '\''); } } @@ -403,7 +403,7 @@ void ResolveObjectPlacement(aiMatrix4x4 &m, const Schema_2x3::IfcObjectPlacement m = tmpM * m; } } else { - IFCImporter::LogWarn("skipping unknown IfcObjectPlacement entity, type is " + place.GetClassName()); + IFCImporter::LogWarn("skipping unknown IfcObjectPlacement entity, type is ", place.GetClassName()); } } diff --git a/code/AssetLib/IFC/IFCMaterial.cpp b/code/AssetLib/IFC/IFCMaterial.cpp index e0146b887..2a79f0754 100644 --- a/code/AssetLib/IFC/IFCMaterial.cpp +++ b/code/AssetLib/IFC/IFCMaterial.cpp @@ -64,7 +64,7 @@ static int ConvertShadingMode(const std::string& name) { else if (name == "PHONG") { return aiShadingMode_Phong; } - IFCImporter::LogWarn("shading mode "+name+" not recognized by Assimp, using Phong instead"); + IFCImporter::LogWarn("shading mode ", name, " not recognized by Assimp, using Phong instead"); return aiShadingMode_Phong; } @@ -145,7 +145,7 @@ unsigned int ProcessMaterials(uint64_t id, unsigned int prevMatId, ConversionDat // not found, create new material const std::string side = static_cast(surf->Side); if( side != "BOTH" ) { - IFCImporter::LogWarn("ignoring surface side marker on IFC::IfcSurfaceStyle: " + side); + IFCImporter::LogWarn("ignoring surface side marker on IFC::IfcSurfaceStyle: ", side); } std::unique_ptr mat(new aiMaterial()); diff --git a/code/AssetLib/IFC/IFCProfile.cpp b/code/AssetLib/IFC/IFCProfile.cpp index f33f8cdbe..4235be181 100644 --- a/code/AssetLib/IFC/IFCProfile.cpp +++ b/code/AssetLib/IFC/IFCProfile.cpp @@ -68,7 +68,7 @@ bool ProcessCurve(const Schema_2x3::IfcCurve& curve, TempMesh& meshout, Convers { std::unique_ptr cv(Curve::Convert(curve,conv)); if (!cv) { - IFCImporter::LogWarn("skipping unknown IfcCurve entity, type is " + curve.GetClassName()); + IFCImporter::LogWarn("skipping unknown IfcCurve entity, type is ", curve.GetClassName()); return false; } @@ -78,7 +78,7 @@ bool ProcessCurve(const Schema_2x3::IfcCurve& curve, TempMesh& meshout, Convers bc->SampleDiscrete(meshout); } catch(const CurveError& cv) { - IFCImporter::LogError(cv.mStr + " (error occurred while processing curve)"); + IFCImporter::LogError(cv.mStr, " (error occurred while processing curve)"); return false; } meshout.mVertcnt.push_back(static_cast(meshout.mVerts.size())); @@ -152,7 +152,7 @@ void ProcessParametrizedProfile(const Schema_2x3::IfcParameterizedProfileDef& de meshout.mVertcnt.push_back(12); } else { - IFCImporter::LogWarn("skipping unknown IfcParameterizedProfileDef entity, type is " + def.GetClassName()); + IFCImporter::LogWarn("skipping unknown IfcParameterizedProfileDef entity, type is ", def.GetClassName()); return; } @@ -174,7 +174,7 @@ bool ProcessProfile(const Schema_2x3::IfcProfileDef& prof, TempMesh& meshout, Co ProcessParametrizedProfile(*cparam,meshout,conv); } else { - IFCImporter::LogWarn("skipping unknown IfcProfileDef entity, type is " + prof.GetClassName()); + IFCImporter::LogWarn("skipping unknown IfcProfileDef entity, type is ", prof.GetClassName()); return false; } meshout.RemoveAdjacentDuplicates(); diff --git a/code/AssetLib/IFC/IFCUtil.cpp b/code/AssetLib/IFC/IFCUtil.cpp index b451262e9..8c37cebed 100644 --- a/code/AssetLib/IFC/IFCUtil.cpp +++ b/code/AssetLib/IFC/IFCUtil.cpp @@ -506,7 +506,7 @@ IfcFloat ConvertSIPrefix(const std::string& prefix) return 1e-18f; } else { - IFCImporter::LogError("Unrecognized SI prefix: " + prefix); + IFCImporter::LogError("Unrecognized SI prefix: ", prefix); return 1; } } diff --git a/code/AssetLib/Irr/IRRLoader.cpp b/code/AssetLib/Irr/IRRLoader.cpp index 9ec0a9244..cb383e936 100644 --- a/code/AssetLib/Irr/IRRLoader.cpp +++ b/code/AssetLib/Irr/IRRLoader.cpp @@ -639,7 +639,7 @@ void IRRImporter::GenerateGraph(Node *root, aiNode *rootOut, aiScene *scene, // graph we're currently building aiScene *localScene = batch.GetImport(root->id); if (!localScene) { - ASSIMP_LOG_ERROR("IRR: Unable to load external file: " + root->meshPath); + ASSIMP_LOG_ERROR("IRR: Unable to load external file: ", root->meshPath); break; } attach.push_back(AttachmentInfo(localScene, rootOut)); @@ -963,7 +963,7 @@ void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy ASSIMP_LOG_ERROR("IRR: Billboards are not supported by Assimp"); nd = new Node(Node::DUMMY); } else { - ASSIMP_LOG_WARN("IRR: Found unknown node: " + std::string(attrib.name())); + ASSIMP_LOG_WARN("IRR: Found unknown node: ", attrib.name()); /* We skip the contents of nodes we don't know. * We parse the transformation and all animators @@ -1181,7 +1181,7 @@ void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy lights.pop_back(); curNode->type = Node::DUMMY; - ASSIMP_LOG_ERROR("Ignoring light of unknown type: " + prop.value); + ASSIMP_LOG_ERROR("Ignoring light of unknown type: ", prop.value); } } else if ((prop.name == "Mesh" && Node::MESH == curNode->type) || Node::ANIMMESH == curNode->type) { @@ -1225,7 +1225,7 @@ void IRRImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy } else if (prop.value == "followSpline") { curAnim->type = Animator::FOLLOW_SPLINE; } else { - ASSIMP_LOG_WARN("IRR: Ignoring unknown animator: " + prop.value); + ASSIMP_LOG_WARN("IRR: Ignoring unknown animator: ", prop.value); curAnim->type = Animator::UNKNOWN; } diff --git a/code/AssetLib/Irr/IRRShared.cpp b/code/AssetLib/Irr/IRRShared.cpp index b119b063a..b42f34b65 100644 --- a/code/AssetLib/Irr/IRRShared.cpp +++ b/code/AssetLib/Irr/IRRShared.cpp @@ -260,7 +260,7 @@ aiMaterial* IrrlichtBase::ParseMaterial(unsigned int& matFlags) { prop.value == "parallaxmap_trans_add") { matFlags = AI_IRRMESH_MAT_normalmap_ta; } else { - ASSIMP_LOG_WARN("IRRMat: Unrecognized material type: " + prop.value); + ASSIMP_LOG_WARN("IRRMat: Unrecognized material type: ", prop.value); } } diff --git a/code/AssetLib/LWO/LWOLoader.cpp b/code/AssetLib/LWO/LWOLoader.cpp index 7a2916424..bc62152c5 100644 --- a/code/AssetLib/LWO/LWOLoader.cpp +++ b/code/AssetLib/LWO/LWOLoader.cpp @@ -961,7 +961,7 @@ void LWOImporter::LoadLWO2VertexMap(unsigned int length, bool perPoly) { switch (type) { case AI_LWO_TXUV: if (dims != 2) { - ASSIMP_LOG_WARN("LWO2: Skipping UV channel \'" + name + "\' with !2 components"); + ASSIMP_LOG_WARN("LWO2: Skipping UV channel \'", name, "\' with !2 components"); return; } base = FindEntry(mCurLayer->mUVChannels, name, perPoly); @@ -969,7 +969,7 @@ void LWOImporter::LoadLWO2VertexMap(unsigned int length, bool perPoly) { case AI_LWO_WGHT: case AI_LWO_MNVW: if (dims != 1) { - ASSIMP_LOG_WARN("LWO2: Skipping Weight Channel \'" + name + "\' with !1 components"); + ASSIMP_LOG_WARN("LWO2: Skipping Weight Channel \'", name, "\' with !1 components"); return; } base = FindEntry((type == AI_LWO_WGHT ? mCurLayer->mWeightChannels : mCurLayer->mSWeightChannels), name, perPoly); @@ -977,7 +977,7 @@ void LWOImporter::LoadLWO2VertexMap(unsigned int length, bool perPoly) { case AI_LWO_RGB: case AI_LWO_RGBA: if (dims != 3 && dims != 4) { - ASSIMP_LOG_WARN("LWO2: Skipping Color Map \'" + name + "\' with a dimension > 4 or < 3"); + ASSIMP_LOG_WARN("LWO2: Skipping Color Map \'", name, "\' with a dimension > 4 or < 3"); return; } base = FindEntry(mCurLayer->mVColorChannels, name, perPoly); diff --git a/code/AssetLib/LWO/LWOMaterial.cpp b/code/AssetLib/LWO/LWOMaterial.cpp index 186aa8a42..178f24265 100644 --- a/code/AssetLib/LWO/LWOMaterial.cpp +++ b/code/AssetLib/LWO/LWOMaterial.cpp @@ -711,7 +711,7 @@ void LWOImporter::LoadLWO2Surface(unsigned int size) { } } if (derived.size()) { - ASSIMP_LOG_WARN("LWO2: Unable to find source surface: " + derived); + ASSIMP_LOG_WARN("LWO2: Unable to find source surface: ", derived); } } diff --git a/code/AssetLib/LWS/LWSLoader.cpp b/code/AssetLib/LWS/LWSLoader.cpp index a75b90ef9..d469a1064 100644 --- a/code/AssetLib/LWS/LWSLoader.cpp +++ b/code/AssetLib/LWS/LWSLoader.cpp @@ -346,7 +346,7 @@ void LWSImporter::BuildGraph(aiNode *nd, LWS::NodeDesc &src, std::vectormRootNode->mNumChildren == 1) { @@ -538,7 +538,7 @@ void LWSImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy // get file format version and print to log ++it; unsigned int version = strtoul10((*it).tokens[0].c_str()); - ASSIMP_LOG_INFO("LWS file format version is " + (*it).tokens[0]); + ASSIMP_LOG_INFO("LWS file format version is ", (*it).tokens[0]); first = 0.; last = 60.; fps = 25.; // seems to be a good default frame rate diff --git a/code/AssetLib/M3D/M3DImporter.cpp b/code/AssetLib/M3D/M3DImporter.cpp index 5485b1bff..380e92c4f 100644 --- a/code/AssetLib/M3D/M3DImporter.cpp +++ b/code/AssetLib/M3D/M3DImporter.cpp @@ -209,7 +209,7 @@ void M3DImporter::InternReadFile(const std::string &file, aiScene *pScene, IOSys pScene->mRootNode->mNumChildren = 0; mScene = pScene; - ASSIMP_LOG_DEBUG("M3D: root node " + m3d.Name()); + ASSIMP_LOG_DEBUG("M3D: root node ", m3d.Name()); // now we just have to fill up the Assimp structures in pScene importMaterials(m3d); diff --git a/code/AssetLib/MD3/MD3Loader.cpp b/code/AssetLib/MD3/MD3Loader.cpp index 18c3d4228..a3d36f00d 100644 --- a/code/AssetLib/MD3/MD3Loader.cpp +++ b/code/AssetLib/MD3/MD3Loader.cpp @@ -101,7 +101,7 @@ Q3Shader::BlendFunc StringToBlendFunc(const std::string &m) { if (m == "GL_ONE_MINUS_DST_COLOR") { return Q3Shader::BLEND_GL_ONE_MINUS_DST_COLOR; } - ASSIMP_LOG_ERROR("Q3Shader: Unknown blend function: " + m); + ASSIMP_LOG_ERROR("Q3Shader: Unknown blend function: ", m); return Q3Shader::BLEND_NONE; } @@ -226,7 +226,7 @@ bool Q3Shader::LoadSkin(SkinData &fill, const std::string &pFile, IOSystem *io) if (!file.get()) return false; // if we can't access the file, don't worry and return - ASSIMP_LOG_INFO("Loading Quake3 skin file " + pFile); + ASSIMP_LOG_INFO("Loading Quake3 skin file ", pFile); // read file in memory const size_t s = file->FileSize(); @@ -880,9 +880,9 @@ void MD3Importer::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy if (dit != shaders.blocks.end()) { // We made it! shader = &*dit; - ASSIMP_LOG_INFO("Found shader record for " + without_ext); + ASSIMP_LOG_INFO("Found shader record for ", without_ext); } else { - ASSIMP_LOG_WARN("Unable to find shader record for " + without_ext); + ASSIMP_LOG_WARN("Unable to find shader record for ", without_ext); } } diff --git a/code/AssetLib/MD5/MD5Loader.cpp b/code/AssetLib/MD5/MD5Loader.cpp index b99638cd2..0d9c77b71 100644 --- a/code/AssetLib/MD5/MD5Loader.cpp +++ b/code/AssetLib/MD5/MD5Loader.cpp @@ -345,7 +345,7 @@ void MD5Importer::LoadMD5MeshFile() { // Check whether we can read from the file if (file.get() == nullptr || !file->FileSize()) { - ASSIMP_LOG_WARN("Failed to access MD5MESH file: " + filename); + ASSIMP_LOG_WARN("Failed to access MD5MESH file: ", filename); return; } mHadMD5Mesh = true; @@ -567,7 +567,7 @@ void MD5Importer::LoadMD5AnimFile() { // Check whether we can read from the file if (!file.get() || !file->FileSize()) { - ASSIMP_LOG_WARN("Failed to read MD5ANIM file: " + pFile); + ASSIMP_LOG_WARN("Failed to read MD5ANIM file: ", pFile); return; } diff --git a/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp b/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp index 2d2fd7427..e6576b344 100644 --- a/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp +++ b/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp @@ -1343,7 +1343,7 @@ bool HL1MDLLoader::get_num_blend_controllers(const int num_blend_animations, int return true; default: num_blend_controllers = 0; - ASSIMP_LOG_WARN(MDL_HALFLIFE_LOG_HEADER "Unsupported number of blend animations (" + std::to_string(num_blend_animations) + ")"); + ASSIMP_LOG_WARN(MDL_HALFLIFE_LOG_HEADER "Unsupported number of blend animations (", num_blend_animations, ")"); return false; } } diff --git a/code/AssetLib/NFF/NFFLoader.cpp b/code/AssetLib/NFF/NFFLoader.cpp index 65f2dc069..15f46b5e6 100644 --- a/code/AssetLib/NFF/NFFLoader.cpp +++ b/code/AssetLib/NFF/NFFLoader.cpp @@ -132,7 +132,7 @@ void NFFImporter::LoadNFF2MaterialTable(std::vector &output, // Check whether we can read from the file if (!file.get()) { - ASSIMP_LOG_ERROR("NFF2: Unable to open material library " + path + "."); + ASSIMP_LOG_ERROR("NFF2: Unable to open material library ", path, "."); return; } diff --git a/code/AssetLib/Obj/ObjFileParser.cpp b/code/AssetLib/Obj/ObjFileParser.cpp index a8039ae23..767805c10 100644 --- a/code/AssetLib/Obj/ObjFileParser.cpp +++ b/code/AssetLib/Obj/ObjFileParser.cpp @@ -556,7 +556,7 @@ void ObjFileParser::getMaterialDesc() { // This may be the case if the material library is missing. We don't want to lose all // materials if that happens, so create a new named material instead of discarding it // completely. - ASSIMP_LOG_ERROR("OBJ: failed to locate material " + strName + ", creating new material"); + ASSIMP_LOG_ERROR("OBJ: failed to locate material ", strName, ", creating new material"); m_pModel->m_pCurrentMaterial = new ObjFile::Material(); m_pModel->m_pCurrentMaterial->MaterialName.Set(strName); m_pModel->m_MaterialLib.push_back(strName); @@ -620,12 +620,12 @@ void ObjFileParser::getMaterialLib() { IOStream *pFile = m_pIO->Open(absName); if (nullptr == pFile) { - ASSIMP_LOG_ERROR("OBJ: Unable to locate material file " + strMatName); + ASSIMP_LOG_ERROR("OBJ: Unable to locate material file ", strMatName); std::string strMatFallbackName = m_originalObjFileName.substr(0, m_originalObjFileName.length() - 3) + "mtl"; - ASSIMP_LOG_INFO("OBJ: Opening fallback material file " + strMatFallbackName); + ASSIMP_LOG_INFO("OBJ: Opening fallback material file ", strMatFallbackName); pFile = m_pIO->Open(strMatFallbackName); if (!pFile) { - ASSIMP_LOG_ERROR("OBJ: Unable to locate fallback material file " + strMatFallbackName); + ASSIMP_LOG_ERROR("OBJ: Unable to locate fallback material file ", strMatFallbackName); m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); return; } @@ -660,7 +660,7 @@ void ObjFileParser::getNewMaterial() { std::map::iterator it = m_pModel->m_MaterialMap.find(strMat); if (it == m_pModel->m_MaterialMap.end()) { // Show a warning, if material was not found - ASSIMP_LOG_WARN("OBJ: Unsupported material requested: " + strMat); + ASSIMP_LOG_WARN("OBJ: Unsupported material requested: ", strMat); m_pModel->m_pCurrentMaterial = m_pModel->m_pDefaultMaterial; } else { // Set new material diff --git a/code/AssetLib/Ogre/OgreBinarySerializer.cpp b/code/AssetLib/Ogre/OgreBinarySerializer.cpp index b54316514..1d20799a1 100644 --- a/code/AssetLib/Ogre/OgreBinarySerializer.cpp +++ b/code/AssetLib/Ogre/OgreBinarySerializer.cpp @@ -155,7 +155,7 @@ uint16_t OgreBinarySerializer::ReadHeader(bool readLen) { #if (OGRE_BINARY_SERIALIZER_DEBUG == 1) if (id != HEADER_CHUNK_ID) { - ASSIMP_LOG_DEBUG(Formatter::format() << (assetMode == AM_Mesh ? MeshHeaderToString(static_cast(id)) : SkeletonHeaderToString(static_cast(id)))); + ASSIMP_LOG_DEBUG((assetMode == AM_Mesh ? MeshHeaderToString(static_cast(id)) : SkeletonHeaderToString(static_cast(id)))); } #endif diff --git a/code/AssetLib/Ogre/OgreMaterial.cpp b/code/AssetLib/Ogre/OgreMaterial.cpp index cafdda795..295dedde6 100644 --- a/code/AssetLib/Ogre/OgreMaterial.cpp +++ b/code/AssetLib/Ogre/OgreMaterial.cpp @@ -477,7 +477,7 @@ bool OgreImporter::ReadTextureUnit(const std::string &textureUnitName, stringstr return false; } if (textureType == aiTextureType_NONE) { - ASSIMP_LOG_WARN("Failed to detect texture type for '" + textureRef + "', ignoring texture_unit."); + ASSIMP_LOG_WARN("Failed to detect texture type for '", textureRef, "', ignoring texture_unit."); return false; } diff --git a/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp b/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp index 7d9e1f6eb..becfa41fc 100644 --- a/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp +++ b/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp @@ -446,7 +446,7 @@ void Q3BSPFileImporter::createMaterials(const Q3BSP::Q3BSPModel *pModel, aiScene normalizePathName(tmp, texName); if (!importTextureFromArchive(pModel, pArchive, pScene, pMatHelper, textureId)) { - ASSIMP_LOG_ERROR("Cannot import texture from archive " + texName); + ASSIMP_LOG_ERROR("Cannot import texture from archive ", texName); } } } diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index 1edfc602b..dbc8267d3 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -455,14 +455,14 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { if (attr.normal.size() > 0 && attr.normal[0]) { if (attr.normal[0]->count != aim->mNumVertices) { - DefaultLogger::get()->warn("Normal count in mesh \"" + mesh.name + "\" does not match the vertex count, normals ignored."); + DefaultLogger::get()->warn("Normal count in mesh \"", mesh.name, "\" does not match the vertex count, normals ignored."); } else { attr.normal[0]->ExtractData(aim->mNormals); // only extract tangents if normals are present if (attr.tangent.size() > 0 && attr.tangent[0]) { if (attr.tangent[0]->count != aim->mNumVertices) { - DefaultLogger::get()->warn("Tangent count in mesh \"" + mesh.name + "\" does not match the vertex count, tangents ignored."); + DefaultLogger::get()->warn("Tangent count in mesh \"", mesh.name, "\" does not match the vertex count, tangents ignored."); } else { // generate bitangents from normals and tangents according to spec Tangent *tangents = nullptr; @@ -485,7 +485,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { for (size_t c = 0; c < attr.color.size() && c < AI_MAX_NUMBER_OF_COLOR_SETS; ++c) { if (attr.color[c]->count != aim->mNumVertices) { - DefaultLogger::get()->warn("Color stream size in mesh \"" + mesh.name + + DefaultLogger::get()->warn("Color stream size in mesh \"", mesh.name, "\" does not match the vertex count"); continue; } @@ -508,7 +508,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { } if (attr.texcoord[tc]->count != aim->mNumVertices) { - DefaultLogger::get()->warn("Texcoord stream size in mesh \"" + mesh.name + + DefaultLogger::get()->warn("Texcoord stream size in mesh \"", mesh.name, "\" does not match the vertex count"); continue; } diff --git a/code/Common/FileSystemFilter.h b/code/Common/FileSystemFilter.h index 92f199870..6585f9df6 100644 --- a/code/Common/FileSystemFilter.h +++ b/code/Common/FileSystemFilter.h @@ -89,7 +89,7 @@ public: mBase += getOsSeparator(); } - DefaultLogger::get()->info("Import root directory is \'" + mBase + "\'"); + DefaultLogger::get()->info("Import root directory is \'", mBase, "\'"); } /** Destructor. */ diff --git a/code/Common/Importer.cpp b/code/Common/Importer.cpp index 61e5938f6..a2ad041fb 100644 --- a/code/Common/Importer.cpp +++ b/code/Common/Importer.cpp @@ -665,7 +665,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) { if ( nullptr != desc ) { ext = desc->mName; } - ASSIMP_LOG_INFO("Found a matching importer for this file format: " + ext + "." ); + ASSIMP_LOG_INFO("Found a matching importer for this file format: ", ext, "." ); pimpl->mProgressHandler->UpdateFileRead( 0, fileSize ); if (profiler) { diff --git a/code/Material/MaterialSystem.cpp b/code/Material/MaterialSystem.cpp index 310dea29d..c35a1aa93 100644 --- a/code/Material/MaterialSystem.cpp +++ b/code/Material/MaterialSystem.cpp @@ -160,7 +160,7 @@ aiReturn aiGetMaterialFloatArray(const aiMaterial *pMat, break; } if (!IsSpace(*cur)) { - ASSIMP_LOG_ERROR("Material property" + std::string(pKey) + + ASSIMP_LOG_ERROR("Material property", pKey, " is a string; failed to parse a float array out of it."); return AI_FAILURE; } @@ -238,7 +238,7 @@ aiReturn aiGetMaterialIntegerArray(const aiMaterial *pMat, break; } if (!IsSpace(*cur)) { - ASSIMP_LOG_ERROR("Material property" + std::string(pKey) + + ASSIMP_LOG_ERROR("Material property", pKey, " is a string; failed to parse an integer array out of it."); return AI_FAILURE; } @@ -306,8 +306,7 @@ aiReturn aiGetMaterialString(const aiMaterial *pMat, memcpy(pOut->data, prop->mData + 4, pOut->length + 1); } else { // TODO - implement lexical cast as well - ASSIMP_LOG_ERROR("Material property" + std::string(pKey) + - " was found, but is no string"); + ASSIMP_LOG_ERROR("Material property", pKey, " was found, but is no string"); return AI_FAILURE; } return AI_SUCCESS; diff --git a/code/PostProcessing/ValidateDataStructure.cpp b/code/PostProcessing/ValidateDataStructure.cpp index 3058ca6ce..6a872ef11 100644 --- a/code/PostProcessing/ValidateDataStructure.cpp +++ b/code/PostProcessing/ValidateDataStructure.cpp @@ -99,7 +99,7 @@ void ValidateDSProcess::ReportWarning(const char *msg, ...) { ai_assert(iLen > 0); va_end(args); - ASSIMP_LOG_WARN("Validation warning: " + std::string(szBuffer, iLen)); + ASSIMP_LOG_WARN("Validation warning: ", std::string(szBuffer, iLen)); } // ------------------------------------------------------------------------------------------------ From 8ab2e466f52c46afdf2740e787cbdc1648966a61 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 13 May 2021 18:51:12 +0200 Subject: [PATCH 114/335] Update Readme.md - Add folder AssetLib to readme in structure --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 81fd9fd9d..680f7e446 100644 --- a/Readme.md +++ b/Readme.md @@ -68,7 +68,7 @@ The source code is organized in the following way: code/Common The base implementation for importers and the infrastructure code/PostProcessing The post-processing steps - code/ Implementation for import and export for the format + code/AssetLib/ Implementation for import and export for the format ### Where to get help ### For more information, visit [our website](http://assimp.org/). Or check out the `./doc`- folder, which contains the official documentation in HTML format. From 348ae42212d9b0f6263e286054502ae6505d1048 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 13 May 2021 18:54:21 +0200 Subject: [PATCH 115/335] Update Readme.md --- Readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Readme.md b/Readme.md index 680f7e446..f2ea4b094 100644 --- a/Readme.md +++ b/Readme.md @@ -66,8 +66,8 @@ Open Asset Import Library is implemented in C++. The directory structure looks l The source code is organized in the following way: - code/Common The base implementation for importers and the infrastructure - code/PostProcessing The post-processing steps + code/Common The base implementation for importers and the infrastructure + code/PostProcessing The post-processing steps code/AssetLib/ Implementation for import and export for the format ### Where to get help ### From 27135bd3e7ec444bb7000a2d8be179a52f77a5cf Mon Sep 17 00:00:00 2001 From: ogjamesfranco Date: Sat, 15 May 2021 15:27:24 -0400 Subject: [PATCH 116/335] changed the assimp output directory vars to cached vars --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7027e3300..7b9d6fc55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -334,9 +334,9 @@ INCLUDE (FindPkgMacros) INCLUDE (PrecompiledHeader) # Set Assimp project output directory variables. -SET(ASSIMP_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin") -SET(ASSIMP_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin") -SET(ASSIMP_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib") +SET(ASSIMP_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin" CACHE STRING "Path for runtime output files") +SET(ASSIMP_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin" CACHE STRING "Path for library output files") +SET(ASSIMP_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib" CACHE STRING "Path for archive output files") # Macro used to set the output directories of a target to the # respective Assimp output directories. From 6e4b9d267bbf69f3e9f054f5d1f4b237ced578cf Mon Sep 17 00:00:00 2001 From: Malcolm Tyrrell Date: Mon, 17 May 2021 10:29:06 +0100 Subject: [PATCH 117/335] Remove TODO. Typo fix. --- code/AssetLib/AC/ACLoader.cpp | 2 +- code/Common/DefaultLogger.cpp | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/code/AssetLib/AC/ACLoader.cpp b/code/AssetLib/AC/ACLoader.cpp index 974e77825..078c96e32 100644 --- a/code/AssetLib/AC/ACLoader.cpp +++ b/code/AssetLib/AC/ACLoader.cpp @@ -116,7 +116,7 @@ inline const char *TAcCheckedLoadFloatArray(const char *buffer, const char *name buffer = AcSkipToNextToken(buffer); if (0 != name_length) { if (0 != strncmp(buffer, name, name_length) || !IsSpace(buffer[name_length])) { - ASSIMP_LOG_ERROR("AC3D: Unexpexted token. " + std::string(name) + " was expected."); + ASSIMP_LOG_ERROR("AC3D: Unexpected token. ", name, " was expected."); return buffer; } buffer += name_length + 1; diff --git a/code/Common/DefaultLogger.cpp b/code/Common/DefaultLogger.cpp index e12276ea9..253cae47b 100644 --- a/code/Common/DefaultLogger.cpp +++ b/code/Common/DefaultLogger.cpp @@ -165,7 +165,6 @@ Logger *DefaultLogger::create(const char *name /*= "AssimpLog.txt"*/, // ---------------------------------------------------------------------------------- void Logger::debugInternal(Assimp::Formatter::format f) { std::string message = f; - // TODO: Should limit sizes in the formatter. // SECURITY FIX: see above if (message.length() > MAX_LOG_MESSAGE_LENGTH) { return; @@ -176,7 +175,6 @@ void Logger::debugInternal(Assimp::Formatter::format f) { // ---------------------------------------------------------------------------------- void Logger::verboseDebugInternal(Assimp::Formatter::format f) { std::string message = f; - // TODO: Should limit sizes in the formatter. // SECURITY FIX: see above if (message.length() > MAX_LOG_MESSAGE_LENGTH) { return; @@ -187,7 +185,6 @@ void Logger::verboseDebugInternal(Assimp::Formatter::format f) { // ---------------------------------------------------------------------------------- void Logger::infoInternal(Assimp::Formatter::format f) { std::string message = f; - // TODO: Should limit sizes in the formatter. // SECURITY FIX: see above if (message.length() > MAX_LOG_MESSAGE_LENGTH) { return; @@ -198,7 +195,6 @@ void Logger::infoInternal(Assimp::Formatter::format f) { // ---------------------------------------------------------------------------------- void Logger::warnInternal(Assimp::Formatter::format f) { std::string message = f; - // TODO: Should limit sizes in the formatter. // SECURITY FIX: see above if (message.length() > MAX_LOG_MESSAGE_LENGTH) { return; @@ -209,7 +205,6 @@ void Logger::warnInternal(Assimp::Formatter::format f) { // ---------------------------------------------------------------------------------- void Logger::errorInternal(Assimp::Formatter::format f) { std::string message = f; - // TODO: Should limit sizes in the formatter. // SECURITY FIX: see above if (message.length() > MAX_LOG_MESSAGE_LENGTH) { return; From fd5d1211f9aa4012127d6a92370fa00f4c49ffe2 Mon Sep 17 00:00:00 2001 From: Malcolm Tyrrell Date: Mon, 17 May 2021 10:33:00 +0100 Subject: [PATCH 118/335] Recover comment which got dropped --- code/Common/DefaultLogger.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/Common/DefaultLogger.cpp b/code/Common/DefaultLogger.cpp index 253cae47b..913857f85 100644 --- a/code/Common/DefaultLogger.cpp +++ b/code/Common/DefaultLogger.cpp @@ -165,7 +165,9 @@ Logger *DefaultLogger::create(const char *name /*= "AssimpLog.txt"*/, // ---------------------------------------------------------------------------------- void Logger::debugInternal(Assimp::Formatter::format f) { std::string message = f; - // SECURITY FIX: see above + // SECURITY FIX: otherwise it's easy to produce overruns since + // sometimes importers will include data from the input file + // (i.e. node names) in their messages. if (message.length() > MAX_LOG_MESSAGE_LENGTH) { return; } From 084dc73b91c65e84db83b4bd94e79b1437c88834 Mon Sep 17 00:00:00 2001 From: Malcolm Tyrrell Date: Mon, 17 May 2021 11:27:21 +0100 Subject: [PATCH 119/335] Fast path for unformatted calls. --- code/Common/DefaultLogger.cpp | 39 ++++++++++---------- include/assimp/Logger.hpp | 69 +++++++++++++++++++++++------------ 2 files changed, 65 insertions(+), 43 deletions(-) diff --git a/code/Common/DefaultLogger.cpp b/code/Common/DefaultLogger.cpp index 913857f85..4fa9c865b 100644 --- a/code/Common/DefaultLogger.cpp +++ b/code/Common/DefaultLogger.cpp @@ -163,55 +163,54 @@ Logger *DefaultLogger::create(const char *name /*= "AssimpLog.txt"*/, } // ---------------------------------------------------------------------------------- -void Logger::debugInternal(Assimp::Formatter::format f) { - std::string message = f; +void Logger::debug(const char *message) { + // SECURITY FIX: otherwise it's easy to produce overruns since // sometimes importers will include data from the input file // (i.e. node names) in their messages. - if (message.length() > MAX_LOG_MESSAGE_LENGTH) { + if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { return; } - return OnDebug(message.c_str()); + return OnDebug(message); } // ---------------------------------------------------------------------------------- -void Logger::verboseDebugInternal(Assimp::Formatter::format f) { - std::string message = f; +void Logger::verboseDebug(const char *message) { + // SECURITY FIX: see above - if (message.length() > MAX_LOG_MESSAGE_LENGTH) { + if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { return; } - return OnVerboseDebug(message.c_str()); + return OnVerboseDebug(message); } // ---------------------------------------------------------------------------------- -void Logger::infoInternal(Assimp::Formatter::format f) { - std::string message = f; +void Logger::info(const char *message) { + // SECURITY FIX: see above - if (message.length() > MAX_LOG_MESSAGE_LENGTH) { + if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { return; } - return OnInfo(message.c_str()); + return OnInfo(message); } // ---------------------------------------------------------------------------------- -void Logger::warnInternal(Assimp::Formatter::format f) { - std::string message = f; +void Logger::warn(const char *message) { + // SECURITY FIX: see above - if (message.length() > MAX_LOG_MESSAGE_LENGTH) { + if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { return; } - return OnWarn(message.c_str()); + return OnWarn(message); } // ---------------------------------------------------------------------------------- -void Logger::errorInternal(Assimp::Formatter::format f) { - std::string message = f; +void Logger::error(const char *message) { // SECURITY FIX: see above - if (message.length() > MAX_LOG_MESSAGE_LENGTH) { + if (strlen(message) > MAX_LOG_MESSAGE_LENGTH) { return; } - return OnError(message.c_str()); + return OnError(message); } // ---------------------------------------------------------------------------------- diff --git a/include/assimp/Logger.hpp b/include/assimp/Logger.hpp index ecbd9df9b..cb9ef3770 100644 --- a/include/assimp/Logger.hpp +++ b/include/assimp/Logger.hpp @@ -101,41 +101,51 @@ public: // ---------------------------------------------------------------------- /** @brief Writes a info message * @param message Info message*/ + void debug(const char* message); + template void debug(T&&... args) { - debugInternal(Assimp::Formatter::format(), std::forward(args)...); + debugFormat(Assimp::Formatter::format(), std::forward(args)...); } // ---------------------------------------------------------------------- /** @brief Writes a debug message * @param message Debug message*/ + void verboseDebug(const char* message); + template void verboseDebug(T&&... args) { - verboseDebugInternal(Assimp::Formatter::format(), std::forward(args)...); + verboseDebugFormat(Assimp::Formatter::format(), std::forward(args)...); } // ---------------------------------------------------------------------- /** @brief Writes a info message * @param message Info message*/ + void info(const char* message); + template void info(T&&... args) { - infoInternal(Assimp::Formatter::format(), std::forward(args)...); + infoFormat(Assimp::Formatter::format(), std::forward(args)...); } // ---------------------------------------------------------------------- /** @brief Writes a warning message * @param message Warn message*/ + void warn(const char* message); + template void warn(T&&... args) { - warnInternal(Assimp::Formatter::format(), std::forward(args)...); + warnFormat(Assimp::Formatter::format(), std::forward(args)...); } // ---------------------------------------------------------------------- /** @brief Writes an error message * @param message Info message*/ + void error(const char* message); + template void error(T&&... args) { - errorInternal(Assimp::Formatter::format(), std::forward(args)...); + errorFormat(Assimp::Formatter::format(), std::forward(args)...); } // ---------------------------------------------------------------------- @@ -236,36 +246,49 @@ protected: virtual void OnError(const char* message) = 0; protected: - - void debugInternal(Assimp::Formatter::format f); - void verboseDebugInternal(Assimp::Formatter::format f); - void warnInternal(Assimp::Formatter::format f); - void infoInternal(Assimp::Formatter::format f); - void errorInternal(Assimp::Formatter::format f); - - template - void debugInternal(Assimp::Formatter::format f, U&& u, T&&... args) { - debugInternal(std::move(f << std::forward(u)), std::forward(args)...); + void debugFormat(Assimp::Formatter::format f) { + debug(std::string(f).c_str()); } template - void verboseDebugInternal(Assimp::Formatter::format f, U&& u, T&&... args) { - verboseDebugInternal(std::move(f << std::forward(u)), std::forward(args)...); + void debugFormat(Assimp::Formatter::format f, U&& u, T&&... args) { + debugFormat(std::move(f << std::forward(u)), std::forward(args)...); + } + + void verboseDebugFormat(Assimp::Formatter::format f) { + verboseDebug(std::string(f).c_str()); } template - void warnInternal(Assimp::Formatter::format f, U&& u, T&&... args) { - warnInternal(std::move(f << std::forward(u)), std::forward(args)...); + void verboseDebugFormat(Assimp::Formatter::format f, U&& u, T&&... args) { + verboseDebugFormat(std::move(f << std::forward(u)), std::forward(args)...); + } + + void warnFormat(Assimp::Formatter::format f) { + warn(std::string(f).c_str()); } template - void infoInternal(Assimp::Formatter::format f, U&& u, T&&... args) { - infoInternal(std::move(f << std::forward(u)), std::forward(args)...); + void warnFormat(Assimp::Formatter::format f, U&& u, T&&... args) { + warnFormat(std::move(f << std::forward(u)), std::forward(args)...); + } + + void infoFormat(Assimp::Formatter::format f) { + info(std::string(f).c_str()); } template - void errorInternal(Assimp::Formatter::format f, U&& u, T&&... args) { - errorInternal(std::move(f << std::forward(u)), std::forward(args)...); + void infoFormat(Assimp::Formatter::format f, U&& u, T&&... args) { + infoFormat(std::move(f << std::forward(u)), std::forward(args)...); + } + + void errorFormat(Assimp::Formatter::format f) { + error(std::string(f).c_str()); + } + + template + void errorFormat(Assimp::Formatter::format f, U&& u, T&&... args) { + errorFormat(std::move(f << std::forward(u)), std::forward(args)...); } protected: From 3d3462a621bae404d9704de4523bec9e39da53e1 Mon Sep 17 00:00:00 2001 From: Malcolm Tyrrell Date: Mon, 17 May 2021 11:54:43 +0100 Subject: [PATCH 120/335] Simplify formatting templates. --- include/assimp/Logger.hpp | 55 +++++++-------------------------------- 1 file changed, 9 insertions(+), 46 deletions(-) diff --git a/include/assimp/Logger.hpp b/include/assimp/Logger.hpp index cb9ef3770..3ca4a6cb2 100644 --- a/include/assimp/Logger.hpp +++ b/include/assimp/Logger.hpp @@ -105,7 +105,7 @@ public: template void debug(T&&... args) { - debugFormat(Assimp::Formatter::format(), std::forward(args)...); + debug(formatMessage(std::forward(args)...).c_str()); } // ---------------------------------------------------------------------- @@ -115,7 +115,7 @@ public: template void verboseDebug(T&&... args) { - verboseDebugFormat(Assimp::Formatter::format(), std::forward(args)...); + verboseDebug(formatMessage(std::forward(args)...).c_str()); } // ---------------------------------------------------------------------- @@ -125,7 +125,7 @@ public: template void info(T&&... args) { - infoFormat(Assimp::Formatter::format(), std::forward(args)...); + info(formatMessage(std::forward(args)...).c_str()); } // ---------------------------------------------------------------------- @@ -135,7 +135,7 @@ public: template void warn(T&&... args) { - warnFormat(Assimp::Formatter::format(), std::forward(args)...); + warn(formatMessage(std::forward(args)...).c_str()); } // ---------------------------------------------------------------------- @@ -145,7 +145,7 @@ public: template void error(T&&... args) { - errorFormat(Assimp::Formatter::format(), std::forward(args)...); + error(formatMessage(std::forward(args)...).c_str()); } // ---------------------------------------------------------------------- @@ -244,51 +244,14 @@ protected: * the function is left. */ virtual void OnError(const char* message) = 0; - protected: - void debugFormat(Assimp::Formatter::format f) { - debug(std::string(f).c_str()); + std::string formatMessage(Assimp::Formatter::format f) { + return f; } template - void debugFormat(Assimp::Formatter::format f, U&& u, T&&... args) { - debugFormat(std::move(f << std::forward(u)), std::forward(args)...); - } - - void verboseDebugFormat(Assimp::Formatter::format f) { - verboseDebug(std::string(f).c_str()); - } - - template - void verboseDebugFormat(Assimp::Formatter::format f, U&& u, T&&... args) { - verboseDebugFormat(std::move(f << std::forward(u)), std::forward(args)...); - } - - void warnFormat(Assimp::Formatter::format f) { - warn(std::string(f).c_str()); - } - - template - void warnFormat(Assimp::Formatter::format f, U&& u, T&&... args) { - warnFormat(std::move(f << std::forward(u)), std::forward(args)...); - } - - void infoFormat(Assimp::Formatter::format f) { - info(std::string(f).c_str()); - } - - template - void infoFormat(Assimp::Formatter::format f, U&& u, T&&... args) { - infoFormat(std::move(f << std::forward(u)), std::forward(args)...); - } - - void errorFormat(Assimp::Formatter::format f) { - error(std::string(f).c_str()); - } - - template - void errorFormat(Assimp::Formatter::format f, U&& u, T&&... args) { - errorFormat(std::move(f << std::forward(u)), std::forward(args)...); + std::string formatMessage(Assimp::Formatter::format f, U&& u, T&&... args) { + return formatMessage(std::move(f << std::forward(u)), std::forward(args)...); } protected: From e01a6b427648f30144957c1add05baac21273d17 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 18 May 2021 21:15:48 +0200 Subject: [PATCH 121/335] Add xml doc. --- code/AssetLib/Collada/ColladaParser.cpp | 40 +++----- include/assimp/Exceptional.h | 3 + include/assimp/XmlParser.h | 131 ++++++++++++++++++++++-- test/unit/Common/utXmlParser.cpp | 4 +- test/unit/utImporter.cpp | 116 +++++++++------------ 5 files changed, 189 insertions(+), 105 deletions(-) diff --git a/code/AssetLib/Collada/ColladaParser.cpp b/code/AssetLib/Collada/ColladaParser.cpp index 42166fdd4..8e2cb3f9f 100644 --- a/code/AssetLib/Collada/ColladaParser.cpp +++ b/code/AssetLib/Collada/ColladaParser.cpp @@ -623,8 +623,7 @@ void ColladaParser::ReadController(XmlNode &node, Collada::Controller &controlle controller.mType = Skin; controller.mMethod = Normalized; - XmlNodeIterator xmlIt(node); - xmlIt.collectChildrenPreOrder(node); + XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); XmlNode currentNode; while (xmlIt.getNext(currentNode)) { @@ -929,8 +928,7 @@ void ColladaParser::ReadMaterial(XmlNode &node, Collada::Material &pMaterial) { // ------------------------------------------------------------------------------------------------ // Reads a light entry into the given light void ColladaParser::ReadLight(XmlNode &node, Collada::Light &pLight) { - XmlNodeIterator xmlIt(node); - xmlIt.collectChildrenPreOrder(node); + XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); XmlNode currentNode; while (xmlIt.getNext(currentNode)) { const std::string ¤tName = currentNode.name(); @@ -991,10 +989,8 @@ void ColladaParser::ReadLight(XmlNode &node, Collada::Light &pLight) { // ------------------------------------------------------------------------------------------------ // Reads a camera entry into the given light void ColladaParser::ReadCamera(XmlNode &node, Collada::Camera &camera) { - XmlNodeIterator xmlIt(node); - xmlIt.collectChildrenPreOrder(node); + XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); XmlNode currentNode; - while (xmlIt.getNext(currentNode)) { const std::string ¤tName = currentNode.name(); if (currentName == "orthographic") { @@ -1050,11 +1046,10 @@ void ColladaParser::ReadEffect(XmlNode &node, Collada::Effect &pEffect) { // ------------------------------------------------------------------------------------------------ // Reads an COMMON effect profile void ColladaParser::ReadEffectProfileCommon(XmlNode &node, Collada::Effect &pEffect) { - XmlNodeIterator xmlIt(node); - xmlIt.collectChildrenPreOrder(node); + XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); XmlNode currentNode; while (xmlIt.getNext(currentNode)) { - const std::string ¤tName = currentNode.name(); + const std::string currentName = currentNode.name(); if (currentName == "newparam") { // save ID std::string sid = currentNode.attribute("sid").as_string(); @@ -1145,10 +1140,9 @@ void ColladaParser::ReadSamplerProperties(XmlNode &node, Sampler &out) { if (node.empty()) { return; } - XmlNodeIterator xmlIt(node); - xmlIt.collectChildrenPreOrder(node); - XmlNode currentNode; + XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); + XmlNode currentNode; while (xmlIt.getNext(currentNode)) { const std::string ¤tName = currentNode.name(); // MAYA extensions @@ -1208,10 +1202,9 @@ void ColladaParser::ReadEffectColor(XmlNode &node, aiColor4D &pColor, Sampler &p if (node.empty()) { return; } - XmlNodeIterator xmlIt(node); - xmlIt.collectChildrenPreOrder(node); - XmlNode currentNode; + XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); + XmlNode currentNode; while (xmlIt.getNext(currentNode)) { const std::string ¤tName = currentNode.name(); if (currentName == "color") { @@ -1273,8 +1266,7 @@ void ColladaParser::ReadEffectParam(XmlNode &node, Collada::EffectParam &pParam) return; } - XmlNodeIterator xmlIt(node); - xmlIt.collectChildrenPreOrder(node); + XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); XmlNode currentNode; while (xmlIt.getNext(currentNode)) { const std::string ¤tName = currentNode.name(); @@ -1360,8 +1352,7 @@ void ColladaParser::ReadMesh(XmlNode &node, Mesh &pMesh) { return; } - XmlNodeIterator xmlIt(node); - xmlIt.collectChildrenPreOrder(node); + XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); XmlNode currentNode; while (xmlIt.getNext(currentNode)) { const std::string ¤tName = currentNode.name(); @@ -1386,8 +1377,7 @@ void ColladaParser::ReadSource(XmlNode &node) { std::string sourceID; XmlParser::getStdStrAttribute(node, "id", sourceID); - XmlNodeIterator xmlIt(node); - xmlIt.collectChildrenPreOrder(node); + XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); XmlNode currentNode; while (xmlIt.getNext(currentNode)) { const std::string ¤tName = currentNode.name(); @@ -1490,8 +1480,7 @@ void ColladaParser::ReadAccessor(XmlNode &node, const std::string &pID) { acc.mSource = source.c_str() + 1; // ignore the leading '#' acc.mSize = 0; // gets incremented with every param - XmlNodeIterator xmlIt(node); - xmlIt.collectChildrenPreOrder(node); + XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); XmlNode currentNode; while (xmlIt.getNext(currentNode)) { const std::string ¤tName = currentNode.name(); @@ -1608,8 +1597,7 @@ void ColladaParser::ReadIndexData(XmlNode &node, Mesh &pMesh) { ai_assert(primType != Prim_Invalid); // also a number of elements, but in addition a

primitive collection and probably index counts for all primitives - XmlNodeIterator xmlIt(node); - xmlIt.collectChildrenPreOrder(node); + XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); XmlNode currentNode; while (xmlIt.getNext(currentNode)) { const std::string ¤tName = currentNode.name(); diff --git a/include/assimp/Exceptional.h b/include/assimp/Exceptional.h index 36d60d63a..98e2a3321 100644 --- a/include/assimp/Exceptional.h +++ b/include/assimp/Exceptional.h @@ -71,6 +71,9 @@ protected: * nullptr instead of a valid aiScene then. */ class ASSIMP_API DeadlyImportError : public DeadlyErrorBase { public: + DeadlyImportError(const char *message) : + DeadlyErrorBase(Assimp::Formatter::format(), std::forward(message)) {} + /** Constructor with arguments */ template explicit DeadlyImportError(T&&... args) : diff --git a/include/assimp/XmlParser.h b/include/assimp/XmlParser.h index 91fb9907f..5e63a4e79 100644 --- a/include/assimp/XmlParser.h +++ b/include/assimp/XmlParser.h @@ -66,6 +66,8 @@ struct find_node_by_name_predicate { } }; +/// @brief Will convert an attribute to its int value. +/// @tparam TNodeType The node type. template struct NodeConverter { public: @@ -78,19 +80,37 @@ public: using XmlNode = pugi::xml_node; using XmlAttribute = pugi::xml_attribute; +/// @brief The Xml-Parser class. +/// +/// Use this parser if you have to import any kind of xml-format. +/// +/// An example: +/// @code +/// TXmlParser theParser; +/// if (theParser.parse(fileStream)) { +/// auto node = theParser.getRootNode(); +/// for ( auto currentNode : node.children()) { +/// // Will loop over all children +/// } +/// } +/// @endcode +/// @tparam TNodeType template class TXmlParser { public: + /// @brief The default class constructor. TXmlParser() : mDoc(nullptr), mData() { // empty } + /// @brief The class destructor. ~TXmlParser() { clear(); } + /// @brief Will clear the parsed xml-file. void clear() { if(mData.empty()) { mDoc = nullptr; @@ -101,6 +121,9 @@ public: mDoc = nullptr; } + /// @brief Will search for a child-node by its name + /// @param name [in] The name of the child-node. + /// @return The node instance or nullptr, if nothing was found. TNodeType *findNode(const std::string &name) { if (name.empty()) { return nullptr; @@ -119,10 +142,16 @@ public: return &mCurrent; } + /// @brief Will return true, if the node is a child-node. + /// @param name [in] The name of the child node to look for. + /// @return true, if the node is a child-node or false if not. bool hasNode(const std::string &name) { return nullptr != findNode(name); } + /// @brief Will parse an xml-file from a given stream. + /// @param stream The input stream. + /// @return true, if the parsing was successful, false if not. bool parse(IOStream *stream) { if (nullptr == stream) { ASSIMP_LOG_DEBUG("Stream is nullptr."); @@ -138,34 +167,53 @@ public: pugi::xml_parse_result parse_result = mDoc->load_string(&mData[0], pugi::parse_full); if (parse_result.status == pugi::status_ok) { return true; - } else { - ASSIMP_LOG_DEBUG("Error while parse xml."); - return false; - } + } + + ASSIMP_LOG_DEBUG("Error while parse xml."); + return false; } + /// @brief Will return the document pointer, is nullptr if no xml-file was parsed. + /// @return The pointer showing to the document. pugi::xml_document *getDocument() const { return mDoc; } + /// @brief Will return the root node, const version. + /// @return The root node. const TNodeType getRootNode() const { return mDoc->root(); } + /// @brief Will return the root node, non-const version. + /// @return The root node. TNodeType getRootNode() { return mDoc->root(); } + /// @brief Will check if a node with the given name is in. + /// @param node [in] The node to look in. + /// @param name [in] The name of the child-node. + /// @return true, if node was found, false if not. static inline bool hasNode(XmlNode &node, const char *name) { pugi::xml_node child = node.find_child(find_node_by_name_predicate(name)); return !child.empty(); } + /// @brief Will check if an attribute is part of the XmlNode. + /// @param xmlNode [in] The node to search in. + /// @param name [in} The attribute name to look for. + /// @return true, if the was found, false if not. static inline bool hasAttribute(XmlNode &xmlNode, const char *name) { pugi::xml_attribute attr = xmlNode.attribute(name); return !attr.empty(); } + /// @brief Will try to get an unsigned int attribute value. + /// @param xmlNode [in] The node to search in. + /// @param name [in] The attribute name to look for. + /// @param val [out] The unsigned int value from the attribute. + /// @return true, if the node contains an attribute with the given name and if the value is an unsigned int. static inline bool getUIntAttribute(XmlNode &xmlNode, const char *name, unsigned int &val) { pugi::xml_attribute attr = xmlNode.attribute(name); if (attr.empty()) { @@ -176,6 +224,11 @@ public: return true; } + /// @brief Will try to get an int attribute value. + /// @param xmlNode [in] The node to search in. + /// @param name [in] The attribute name to look for. + /// @param val [out] The int value from the attribute. + /// @return true, if the node contains an attribute with the given name and if the value is an int. static inline bool getIntAttribute(XmlNode &xmlNode, const char *name, int &val ) { pugi::xml_attribute attr = xmlNode.attribute(name); if (attr.empty()) { @@ -186,6 +239,11 @@ public: return true; } + /// @brief Will try to get a real attribute value. + /// @param xmlNode [in] The node to search in. + /// @param name [in] The attribute name to look for. + /// @param val [out] The real value from the attribute. + /// @return true, if the node contains an attribute with the given name and if the value is a real. static inline bool getRealAttribute( XmlNode &xmlNode, const char *name, ai_real &val ) { pugi::xml_attribute attr = xmlNode.attribute(name); if (attr.empty()) { @@ -199,7 +257,12 @@ public: return true; } - static inline bool getFloatAttribute(XmlNode &xmlNode, const char *name, float &val ) { + /// @brief Will try to get a float attribute value. + /// @param xmlNode [in] The node to search in. + /// @param name [in] The attribute name to look for. + /// @param val [out] The float value from the attribute. + /// @return true, if the node contains an attribute with the given name and if the value is a float. + static inline bool getFloatAttribute(XmlNode &xmlNode, const char *name, float &val) { pugi::xml_attribute attr = xmlNode.attribute(name); if (attr.empty()) { return false; @@ -210,7 +273,12 @@ public: } - static inline bool getDoubleAttribute( XmlNode &xmlNode, const char *name, double &val ) { + /// @brief Will try to get a double attribute value. + /// @param xmlNode [in] The node to search in. + /// @param name [in] The attribute name to look for. + /// @param val [out] The double value from the attribute. + /// @return true, if the node contains an attribute with the given name and if the value is a double. + static inline bool getDoubleAttribute(XmlNode &xmlNode, const char *name, double &val) { pugi::xml_attribute attr = xmlNode.attribute(name); if (attr.empty()) { return false; @@ -220,6 +288,11 @@ public: return true; } + /// @brief Will try to get a std::string attribute value. + /// @param xmlNode [in] The node to search in. + /// @param name [in] The attribute name to look for. + /// @param val [out] The std::string value from the attribute. + /// @return true, if the node contains an attribute with the given name and if the value is a std::string. static inline bool getStdStrAttribute(XmlNode &xmlNode, const char *name, std::string &val) { pugi::xml_attribute attr = xmlNode.attribute(name); if (attr.empty()) { @@ -230,6 +303,11 @@ public: return true; } + /// @brief Will try to get a bool attribute value. + /// @param xmlNode [in] The node to search in. + /// @param name [in] The attribute name to look for. + /// @param val [out] The bool value from the attribute. + /// @return true, if the node contains an attribute with the given name and if the value is a bool. static inline bool getBoolAttribute( XmlNode &xmlNode, const char *name, bool &val ) { pugi::xml_attribute attr = xmlNode.attribute(name); if (attr.empty()) { @@ -241,6 +319,10 @@ public: } + /// @brief Will try to get the value of the node as a string. + /// @param node [in] The node to search in. + /// @param text [out] The value as a text. + /// @return true, if the value can be read out. static inline bool getValueAsString( XmlNode &node, std::string &text ) { text = std::string(); if (node.empty()) { @@ -252,6 +334,10 @@ public: return true; } + /// @brief Will try to get the value of the node as a float. + /// @param node [in] The node to search in. + /// @param text [out] The value as a float. + /// @return true, if the value can be read out. static inline bool getValueAsFloat( XmlNode &node, ai_real &v ) { if (node.empty()) { return false; @@ -271,17 +357,36 @@ public: using XmlParser = TXmlParser; +/// @brief This class declares an iterator to loop through all children of the root node. class XmlNodeIterator { public: - XmlNodeIterator(XmlNode &parent) : + /// @brief The iteration mode. + enum IterationMode { + PreOrderMode, ///< Pre-ordering, get the values, continue the iteration. + PostOrderMode ///< Post-ordering, continue the iteration, get the values. + }; + /// @brief The class constructor + /// @param parent [in] The xml parent to to iterate through. + /// @param mode [in] The iteration mode. + explicit XmlNodeIterator(XmlNode &parent, IterationMode mode) : mParent(parent), mNodes(), mIndex(0) { + if (mode == PreOrderMode) { + collectChildrenPreOrder(parent); + } else { + collectChildrenPostOrder(parent); + } + } + + /// @brief The class destructor. + ~XmlNodeIterator() { // empty } + /// @brief Will iterate through all children in pre-order iteration. + /// @param node [in] The nod to iterate through. void collectChildrenPreOrder( XmlNode &node ) { - if (node != mParent && node.type() == pugi::node_element) { mNodes.push_back(node); } @@ -290,6 +395,8 @@ public: } } + /// @brief Will iterate through all children in post-order iteration. + /// @param node [in] The nod to iterate through. void collectChildrenPostOrder(XmlNode &node) { for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { collectChildrenPostOrder(currentNode); @@ -299,6 +406,9 @@ public: } } + /// @brief Will iterate through all collected nodes. + /// @param next The next node, if there is any. + /// @return true, if there is a node left. bool getNext(XmlNode &next) { if (mIndex == mNodes.size()) { return false; @@ -310,14 +420,19 @@ public: return true; } + /// @brief Will return the number of collected nodes. + /// @return The number of collected nodes. size_t size() const { return mNodes.size(); } + /// @brief Returns true, if the node is empty. + /// @return true, if the node is empty, false if not. bool isEmpty() const { return mNodes.empty(); } + /// @brief Will clear all collected nodes. void clear() { if (mNodes.empty()) { return; diff --git a/test/unit/Common/utXmlParser.cpp b/test/unit/Common/utXmlParser.cpp index c27669dd1..7e992f9df 100644 --- a/test/unit/Common/utXmlParser.cpp +++ b/test/unit/Common/utXmlParser.cpp @@ -73,9 +73,7 @@ TEST_F(utXmlParser, parse_xml_and_traverse_test) { EXPECT_TRUE(result); XmlNode root = parser.getRootNode(); - XmlNodeIterator nodeIt(root); - EXPECT_TRUE(nodeIt.isEmpty()); - nodeIt.collectChildrenPreOrder(root); + XmlNodeIterator nodeIt(root, XmlNodeIterator::PreOrderMode); const size_t numNodes = nodeIt.size(); bool empty = nodeIt.isEmpty(); EXPECT_FALSE(empty); diff --git a/test/unit/utImporter.cpp b/test/unit/utImporter.cpp index 65b7f99ee..9d3b971a3 100644 --- a/test/unit/utImporter.cpp +++ b/test/unit/utImporter.cpp @@ -280,104 +280,84 @@ TEST_F(ImporterTest, SearchFileHeaderForTokenTest) { // BaseImporter::SearchFileHeaderForToken( &ioSystem, assetPath, Token, 2 ) } +namespace { +// Description for an importer which fails in specific ways. +aiImporterDesc s_failingImporterDescription = { + "Failing importer", + "assimp team", + "", + "", + 0, + 1, + 0, + 1, + 0, + "fail" +}; -namespace -{ - // Description for an importer which fails in specific ways. - aiImporterDesc s_failingImporterDescription = { - "Failing importer", - "assimp team", - "", - "", - 0, - 1, - 0, - 1, - 0, - "fail" - }; +// This importer fails in specific ways. +class FailingImporter : public Assimp::BaseImporter { +public: + virtual ~FailingImporter() = default; + virtual bool CanRead(const std::string &, Assimp::IOSystem *, bool) const override { + return true; + } - // This importer fails in specific ways. - class FailingImporter : public Assimp::BaseImporter { - public: - virtual ~FailingImporter() = default; - virtual bool CanRead( const std::string&, Assimp::IOSystem*, bool ) const override - { - return true; +protected: + const aiImporterDesc *GetInfo() const override { + return &s_failingImporterDescription; + } + + void InternReadFile(const std::string &pFile, aiScene *, Assimp::IOSystem *) override { + if (pFile == "deadlyImportError.fail") { + throw DeadlyImportError("Deadly import error test. Details: ", 42, " More Details: ", "Failure"); + } else if (pFile == "stdException.fail") { + throw std::runtime_error("std::exception test"); + } else if (pFile == "unexpectedException.fail") { + throw 5; } + } +}; +} // namespace - protected: - virtual const aiImporterDesc* GetInfo() const override { return &s_failingImporterDescription; } - - virtual void InternReadFile( const std::string& pFile, aiScene*, Assimp::IOSystem* ) override - { - if (pFile == "deadlyImportError.fail") - { - throw DeadlyImportError("Deadly import error test. Details: ", 42, " More Details: ", "Failure"); - } - else if (pFile == "stdException.fail") - { - throw std::runtime_error("std::exception test"); - } - else if (pFile == "unexpectedException.fail") - { - throw 5; - } - } - }; -} - -TEST_F(ImporterTest, deadlyImportError) -{ +TEST_F(ImporterTest, deadlyImportError) { pImp->RegisterLoader(new FailingImporter); pImp->SetIOHandler(new TestIOSystem); - const aiScene* scene = pImp->ReadFile("deadlyImportError.fail", 0); + const aiScene *scene = pImp->ReadFile("deadlyImportError.fail", 0); EXPECT_EQ(scene, nullptr); EXPECT_STREQ(pImp->GetErrorString(), "Deadly import error test. Details: 42 More Details: Failure"); EXPECT_NE(pImp->GetException(), std::exception_ptr()); } -TEST_F(ImporterTest, stdException) -{ +TEST_F(ImporterTest, stdException) { pImp->RegisterLoader(new FailingImporter); pImp->SetIOHandler(new TestIOSystem); - const aiScene* scene = pImp->ReadFile("stdException.fail", 0); + const aiScene *scene = pImp->ReadFile("stdException.fail", 0); EXPECT_EQ(scene, nullptr); EXPECT_STREQ(pImp->GetErrorString(), "std::exception test"); EXPECT_NE(pImp->GetException(), std::exception_ptr()); - try - { + try { std::rethrow_exception(pImp->GetException()); - } - catch(const std::exception& e) - { + } catch (const std::exception &e) { EXPECT_STREQ(e.what(), "std::exception test"); - } - catch(...) - { + } catch (...) { EXPECT_TRUE(false); } } -TEST_F(ImporterTest, unexpectedException) -{ +TEST_F(ImporterTest, unexpectedException) { pImp->RegisterLoader(new FailingImporter); pImp->SetIOHandler(new TestIOSystem); - const aiScene* scene = pImp->ReadFile("unexpectedException.fail", 0); + const aiScene *scene = pImp->ReadFile("unexpectedException.fail", 0); EXPECT_EQ(scene, nullptr); EXPECT_STREQ(pImp->GetErrorString(), "Unknown exception"); ASSERT_NE(pImp->GetException(), std::exception_ptr()); - try - { + try { std::rethrow_exception(pImp->GetException()); - } - catch(int x) - { + } catch (int x) { EXPECT_EQ(x, 5); - } - catch(...) - { + } catch (...) { EXPECT_TRUE(false); } } From 3726b2eef462f2941d1479e16fc7eea424e3f11e Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 18 May 2021 21:21:43 +0200 Subject: [PATCH 122/335] fix the build --- include/assimp/XmlParser.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/assimp/XmlParser.h b/include/assimp/XmlParser.h index 11f937b56..35ab0471e 100644 --- a/include/assimp/XmlParser.h +++ b/include/assimp/XmlParser.h @@ -169,7 +169,7 @@ public: return true; } - ASSIMP_LOG_DEBUG("Error while parse xml.", parse_result.description() << " @ ", parse_result.offset); + ASSIMP_LOG_DEBUG("Error while parse xml.", std::string(parse_result.description()), " @ ", parse_result.offset); return false; } From 88ccfedd10d9fb3f90502b4ffed94657ac16d134 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 19 May 2021 00:16:15 +0200 Subject: [PATCH 123/335] Fix possible nullptr dereferences. --- include/assimp/XmlParser.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/assimp/XmlParser.h b/include/assimp/XmlParser.h index 35ab0471e..7578eff59 100644 --- a/include/assimp/XmlParser.h +++ b/include/assimp/XmlParser.h @@ -183,12 +183,18 @@ public: /// @brief Will return the root node, const version. /// @return The root node. const TNodeType getRootNode() const { + if (nullptr == mDoc) { + return nullptr; + } return mDoc->root(); } /// @brief Will return the root node, non-const version. /// @return The root node. TNodeType getRootNode() { + if (nullptr == mDoc) { + return nullptr; + } return mDoc->root(); } From 20ade095eaeac898d7ce0bc1c6a9d8a120e8e8d1 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 20 May 2021 13:40:44 +0200 Subject: [PATCH 124/335] Return null-type in case of an empty document --- include/assimp/XmlParser.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/include/assimp/XmlParser.h b/include/assimp/XmlParser.h index 7578eff59..18d48f337 100644 --- a/include/assimp/XmlParser.h +++ b/include/assimp/XmlParser.h @@ -174,6 +174,11 @@ public: return false; } + /// @brief Will return truem if a root node is there. + /// @return true in case of an existing root. + bool hasRoot() const { + return nullptr != mDoc; + } /// @brief Will return the document pointer, is nullptr if no xml-file was parsed. /// @return The pointer showing to the document. pugi::xml_document *getDocument() const { @@ -183,8 +188,9 @@ public: /// @brief Will return the root node, const version. /// @return The root node. const TNodeType getRootNode() const { + static pugi::xml_node none; if (nullptr == mDoc) { - return nullptr; + return none; } return mDoc->root(); } @@ -192,8 +198,9 @@ public: /// @brief Will return the root node, non-const version. /// @return The root node. TNodeType getRootNode() { + static pugi::xml_node none; if (nullptr == mDoc) { - return nullptr; + return none; } return mDoc->root(); } From 53790e82736c022b2dd935db957036e1342ea624 Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Fri, 21 May 2021 12:25:36 +0100 Subject: [PATCH 125/335] Update Draco to upstream e4103dc Fixes some MSVC and mingw compiler issues Sets VERSION and SO_VERSION https://github.com/google/draco/commit/e4103dc39fe1c70c6ad40d26a01248f4b5d3887b --- contrib/draco/.ruby-version | 1 - contrib/draco/.travis.yml | 31 --------- contrib/draco/CMakeLists.txt | 8 +-- contrib/draco/README.md | 6 +- .../draco/cmake/draco_build_definitions.cmake | 9 ++- contrib/draco/cmake/draco_features.cmake | 63 ------------------- contrib/draco/cmake/draco_flags.cmake | 9 +++ contrib/draco/cmake/draco_install.cmake | 2 +- contrib/draco/cmake/draco_sanitizer.cmake | 20 +++--- contrib/draco/cmake/draco_targets.cmake | 24 ++++--- contrib/draco/src/draco/core/cycle_timer.cc | 14 ++--- contrib/draco/src/draco/core/cycle_timer.h | 7 ++- contrib/draco/src/draco/io/parser_utils.cc | 3 +- contrib/draco/src/draco/io/ply_reader.cc | 4 +- .../draco/src/draco/io/stdio_file_reader.cc | 7 +++ 15 files changed, 69 insertions(+), 139 deletions(-) delete mode 100644 contrib/draco/.ruby-version delete mode 100644 contrib/draco/.travis.yml delete mode 100644 contrib/draco/cmake/draco_features.cmake diff --git a/contrib/draco/.ruby-version b/contrib/draco/.ruby-version deleted file mode 100644 index 276cbf9e2..000000000 --- a/contrib/draco/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -2.3.0 diff --git a/contrib/draco/.travis.yml b/contrib/draco/.travis.yml deleted file mode 100644 index e9ef7123f..000000000 --- a/contrib/draco/.travis.yml +++ /dev/null @@ -1,31 +0,0 @@ -cache: ccache -language: cpp -matrix: - include: - - os: linux - dist: xenial - compiler: clang - - os: linux - dist: xenial - compiler: gcc - - os: osx - compiler: clang - -addons: - apt: - packages: - - cmake - -script: - # Output version info for compilers, cmake, and make - - ${CC} -v - - ${CXX} -v - - cmake --version - - make --version - # Clone googletest - - pushd .. && git clone https://github.com/google/googletest.git && popd - # Configure and build - - mkdir _travis_build && cd _travis_build - - cmake -G "Unix Makefiles" -DENABLE_TESTS=ON .. - - make -j10 - - ./draco_tests diff --git a/contrib/draco/CMakeLists.txt b/contrib/draco/CMakeLists.txt index 3da2c664a..5526e7f60 100644 --- a/contrib/draco/CMakeLists.txt +++ b/contrib/draco/CMakeLists.txt @@ -804,7 +804,7 @@ else() draco_points_enc) # Library targets that consume the object collections. - if(MSVC OR WIN32) + if(MSVC) # In order to produce a DLL and import library the Windows tools require # that the exported symbols are part of the DLL target. The unfortunate side # effect of this is that a single configuration cannot output both the @@ -889,9 +889,6 @@ else() # For Mac, we need to build a .bundle for the unity plugin. if(APPLE) set_target_properties(dracodec_unity PROPERTIES BUNDLE true) - elseif(NOT unity_decoder_lib_type STREQUAL STATIC) - set_target_properties(dracodec_unity - PROPERTIES SOVERSION ${DRACO_SOVERSION}) endif() endif() @@ -916,9 +913,6 @@ else() # For Mac, we need to build a .bundle for the plugin. if(APPLE) set_target_properties(draco_maya_wrapper PROPERTIES BUNDLE true) - else() - set_target_properties(draco_maya_wrapper - PROPERTIES SOVERSION ${DRACO_SOVERSION}) endif() endif() diff --git a/contrib/draco/README.md b/contrib/draco/README.md index add66edcb..0d980b387 100644 --- a/contrib/draco/README.md +++ b/contrib/draco/README.md @@ -2,16 +2,16 @@

-![Build Status: master](https://travis-ci.org/google/draco.svg?branch=master) +[![Build Status](https://github.com/google/draco/workflows/Build/badge.svg)](https://github.com/google/draco/actions?query=workflow%3ABuild) News ======= ### Version 1.4.1 release -* Using the versioned gstatic.com WASM and Javascript decoders is now +* Using the versioned www.gstatic.com WASM and Javascript decoders is now recommended. To use v1.4.1, use this URL: * https://www.gstatic.com/draco/versioned/decoders/1.4.1/* * Replace the * with the files to load. E.g. - * https://gstatic.com/draco/versioned/decoders/1.4.1/draco_decoder.js + * https://www.gstatic.com/draco/versioned/decoders/1.4.1/draco_decoder.js * This works with the v1.3.6 and v1.4.0 releases, and will work with future Draco releases. * Bug fixes diff --git a/contrib/draco/cmake/draco_build_definitions.cmake b/contrib/draco/cmake/draco_build_definitions.cmake index c1ada6206..f7354c15f 100644 --- a/contrib/draco/cmake/draco_build_definitions.cmake +++ b/contrib/draco/cmake/draco_build_definitions.cmake @@ -6,7 +6,7 @@ set(DRACO_CMAKE_DRACO_BUILD_DEFINITIONS_CMAKE_ 1) # Utility for controlling the main draco library dependency. This changes in # shared builds, and when an optional target requires a shared library build. macro(set_draco_target) - if(MSVC OR WIN32) + if(MSVC) set(draco_dependency draco) set(draco_plugin_dependency ${draco_dependency}) else() @@ -63,6 +63,11 @@ macro(draco_set_build_definitions) if(BUILD_SHARED_LIBS) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE) endif() + else() + if(${CMAKE_SIZEOF_VOID_P} EQUAL 8) + # Ensure 64-bit platforms can support large files. + list(APPEND draco_defines "_LARGEFILE_SOURCE" "_FILE_OFFSET_BITS=64") + endif() endif() if(ANDROID) @@ -114,4 +119,6 @@ macro(draco_set_build_definitions) draco_check_emscripten_environment() draco_get_required_emscripten_flags(FLAG_LIST_VAR draco_base_cxx_flags) endif() + + draco_configure_sanitizer() endmacro() diff --git a/contrib/draco/cmake/draco_features.cmake b/contrib/draco/cmake/draco_features.cmake deleted file mode 100644 index be444bf24..000000000 --- a/contrib/draco/cmake/draco_features.cmake +++ /dev/null @@ -1,63 +0,0 @@ -if(DRACO_CMAKE_DRACO_FEATURES_CMAKE_) - return() -endif() -set(DRACO_CMAKE_DRACO_FEATURES_CMAKE_ 1) - -set(draco_features_file_name "${draco_build_dir}/draco/draco_features.h") -set(draco_features_list) - -# Macro that handles tracking of Draco preprocessor symbols for the purpose of -# producing draco_features.h. -# -# draco_enable_feature(FEATURE [TARGETS ]) FEATURE -# is required. It should be a Draco preprocessor symbol. TARGETS is optional. It -# can be one or more draco targets. -# -# When the TARGETS argument is not present the preproc symbol is added to -# draco_features.h. When it is draco_features.h is unchanged, and -# target_compile_options() is called for each target specified. -macro(draco_enable_feature) - set(def_flags) - set(def_single_arg_opts FEATURE) - set(def_multi_arg_opts TARGETS) - cmake_parse_arguments(DEF "${def_flags}" "${def_single_arg_opts}" - "${def_multi_arg_opts}" ${ARGN}) - if("${DEF_FEATURE}" STREQUAL "") - message(FATAL_ERROR "Empty FEATURE passed to draco_enable_feature().") - endif() - - # Do nothing/return early if $DEF_FEATURE is already in the list. - list(FIND draco_features_list ${DEF_FEATURE} df_index) - if(NOT df_index EQUAL -1) - return() - endif() - - list(LENGTH DEF_TARGETS df_targets_list_length) - if(${df_targets_list_length} EQUAL 0) - list(APPEND draco_features_list ${DEF_FEATURE}) - else() - foreach(target ${DEF_TARGETS}) - target_compile_definitions(${target} PRIVATE ${DEF_FEATURE}) - endforeach() - endif() -endmacro() - -# Function for generating draco_features.h. -function(draco_generate_features_h) - file(WRITE "${draco_features_file_name}.new" - "// GENERATED FILE -- DO NOT EDIT\n\n" "#ifndef DRACO_FEATURES_H_\n" - "#define DRACO_FEATURES_H_\n\n") - - foreach(feature ${draco_features_list}) - file(APPEND "${draco_features_file_name}.new" "#define ${feature}\n") - endforeach() - - file(APPEND "${draco_features_file_name}.new" - "\n#endif // DRACO_FEATURES_H_") - - # Will replace ${draco_features_file_name} only if the file content has - # changed. This prevents forced Draco rebuilds after CMake runs. - configure_file("${draco_features_file_name}.new" - "${draco_features_file_name}") - file(REMOVE "${draco_features_file_name}.new") -endfunction() diff --git a/contrib/draco/cmake/draco_flags.cmake b/contrib/draco/cmake/draco_flags.cmake index cb9d489e6..0397859a4 100644 --- a/contrib/draco/cmake/draco_flags.cmake +++ b/contrib/draco/cmake/draco_flags.cmake @@ -80,6 +80,12 @@ macro(draco_test_cxx_flag) # Run the actual compile test. unset(draco_all_cxx_flags_pass CACHE) message("--- Running combined CXX flags test, flags: ${all_cxx_flags}") + + # check_cxx_compiler_flag() requires that the flags are a string. When flags + # are passed as a list it will remove the list separators, and attempt to run + # a compile command using list entries concatenated together as a single + # argument. Avoid the problem by forcing the argument to be a string. + draco_set_and_stringify(SOURCE_VARS all_cxx_flags DEST all_cxx_flags) check_cxx_compiler_flag("${all_cxx_flags}" draco_all_cxx_flags_pass) if(cxx_test_FLAG_REQUIRED AND NOT draco_all_cxx_flags_pass) @@ -194,6 +200,9 @@ macro(draco_test_exe_linker_flag) else() unset(CMAKE_EXE_LINKER_FLAGS) endif() + + list(APPEND DRACO_EXE_LINKER_FLAGS ${${link_FLAG_LIST_VAR_NAME}}) + list(REMOVE_DUPLICATES DRACO_EXE_LINKER_FLAGS) endmacro() # Runs the draco compiler tests. This macro builds up the list of list var(s) diff --git a/contrib/draco/cmake/draco_install.cmake b/contrib/draco/cmake/draco_install.cmake index 5c63ecb4a..09bfb591d 100644 --- a/contrib/draco/cmake/draco_install.cmake +++ b/contrib/draco/cmake/draco_install.cmake @@ -55,7 +55,7 @@ macro(draco_setup_install_target) install(TARGETS draco_encoder DESTINATION "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}") - if(WIN32) + if(MSVC) install(TARGETS draco DESTINATION "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") else() diff --git a/contrib/draco/cmake/draco_sanitizer.cmake b/contrib/draco/cmake/draco_sanitizer.cmake index ca8e23176..d2e41a6cb 100644 --- a/contrib/draco/cmake/draco_sanitizer.cmake +++ b/contrib/draco/cmake/draco_sanitizer.cmake @@ -5,28 +5,28 @@ set(DRACO_CMAKE_DRACO_SANITIZER_CMAKE_ 1) # Handles the details of enabling sanitizers. macro(draco_configure_sanitizer) - if(DRACO_SANITIZE AND NOT MSVC) + if(DRACO_SANITIZE AND NOT EMSCRIPTEN AND NOT MSVC) if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") if(DRACO_SANITIZE MATCHES "cfi") - list(APPEND DRACO_CXX_FLAGS "-flto" "-fno-sanitize-trap=cfi") - list(APPEND DRACO_EXE_LINKER_FLAGS "-flto" "-fno-sanitize-trap=cfi" + list(APPEND SAN_CXX_FLAGS "-flto" "-fno-sanitize-trap=cfi") + list(APPEND SAN_LINKER_FLAGS "-flto" "-fno-sanitize-trap=cfi" "-fuse-ld=gold") endif() if(${CMAKE_SIZEOF_VOID_P} EQUAL 4 AND DRACO_SANITIZE MATCHES "integer|undefined") - list(APPEND DRACO_EXE_LINKER_FLAGS "--rtlib=compiler-rt" "-lgcc_s") + list(APPEND SAN_LINKER_FLAGS "--rtlib=compiler-rt" "-lgcc_s") endif() endif() - list(APPEND DRACO_CXX_FLAGS "-fsanitize=${DRACO_SANITIZE}") - list(APPEND DRACO_EXE_LINKER_FLAGS "-fsanitize=${DRACO_SANITIZE}") + list(APPEND SAN_CXX_FLAGS "-fsanitize=${DRACO_SANITIZE}") + list(APPEND SAN_LINKER_FLAGS "-fsanitize=${DRACO_SANITIZE}") # Make sanitizer callstacks accurate. - list(APPEND DRACO_CXX_FLAGS "-fno-omit-frame-pointer" - "-fno-optimize-sibling-calls") + list(APPEND SAN_CXX_FLAGS "-fno-omit-frame-pointer") + list(APPEND SAN_CXX_FLAGS "-fno-optimize-sibling-calls") - draco_test_cxx_flag(FLAG_LIST_VAR_NAMES DRACO_CXX_FLAGS FLAG_REQUIRED) - draco_test_exe_linker_flag(FLAG_LIST_VAR_NAME DRACO_EXE_LINKER_FLAGS) + draco_test_cxx_flag(FLAG_LIST_VAR_NAMES SAN_CXX_FLAGS FLAG_REQUIRED) + draco_test_exe_linker_flag(FLAG_LIST_VAR_NAME SAN_LINKER_FLAGS) endif() endmacro() diff --git a/contrib/draco/cmake/draco_targets.cmake b/contrib/draco/cmake/draco_targets.cmake index 6dfa6a0c4..0456c4d7b 100644 --- a/contrib/draco/cmake/draco_targets.cmake +++ b/contrib/draco/cmake/draco_targets.cmake @@ -87,6 +87,7 @@ macro(draco_add_executable) endif() add_executable(${exe_NAME} ${exe_SOURCES}) + set_target_properties(${exe_NAME} PROPERTIES VERSION ${DRACO_VERSION}) if(exe_OUTPUT_NAME) set_target_properties(${exe_NAME} PROPERTIES OUTPUT_NAME ${exe_OUTPUT_NAME}) @@ -109,10 +110,11 @@ macro(draco_add_executable) if(exe_LINK_FLAGS OR DRACO_EXE_LINKER_FLAGS) if(${CMAKE_VERSION} VERSION_LESS "3.13") - set(link_flags ${exe_LINK_FLAGS} ${DRACO_EXE_LINKER_FLAGS}) + list(APPEND exe_LINK_FLAGS "${DRACO_EXE_LINKER_FLAGS}") + # LINK_FLAGS is managed as a string. + draco_set_and_stringify(SOURCE "${exe_LINK_FLAGS}" DEST exe_LINK_FLAGS) set_target_properties(${exe_NAME} - PROPERTIES LINK_FLAGS ${exe_LINK_FLAGS} - ${DRACO_EXE_LINKER_FLAGS}) + PROPERTIES LINK_FLAGS "${exe_LINK_FLAGS}") else() target_link_options(${exe_NAME} PRIVATE ${exe_LINK_FLAGS} ${DRACO_EXE_LINKER_FLAGS}) @@ -130,7 +132,7 @@ macro(draco_add_executable) endif() if(BUILD_SHARED_LIBS AND (MSVC OR WIN32)) - target_compile_definitions(${lib_NAME} PRIVATE "DRACO_BUILDING_DLL=0") + target_compile_definitions(${exe_NAME} PRIVATE "DRACO_BUILDING_DLL=0") endif() if(exe_LIB_DEPS) @@ -163,8 +165,8 @@ endmacro() # cmake-format: off # - OUTPUT_NAME: Override output file basename. Target basename defaults to # NAME. OUTPUT_NAME is ignored when BUILD_SHARED_LIBS is enabled and CMake -# is generating a build for which MSVC or WIN32 are true. This is to avoid -# output basename collisions with DLL import libraries. +# is generating a build for which MSVC is true. This is to avoid output +# basename collisions with DLL import libraries. # - TEST: Flag. Presence means treat library as a test. # - DEFINES: List of preprocessor macro definitions. # - INCLUDES: list of include directories for the target. @@ -259,7 +261,7 @@ macro(draco_add_library) endif() if(lib_OUTPUT_NAME) - if(NOT (BUILD_SHARED_LIBS AND (MSVC OR WIN32))) + if(NOT (BUILD_SHARED_LIBS AND MSVC)) set_target_properties(${lib_NAME} PROPERTIES OUTPUT_NAME ${lib_OUTPUT_NAME}) endif() @@ -318,8 +320,12 @@ macro(draco_add_library) set_target_properties(${lib_NAME} PROPERTIES PREFIX "") endif() - if(lib_TYPE STREQUAL SHARED AND NOT MSVC) - set_target_properties(${lib_NAME} PROPERTIES SOVERSION ${DRACO_SOVERSION}) + # VERSION and SOVERSION as necessary + if(NOT lib_TYPE STREQUAL STATIC AND NOT lib_TYPE STREQUAL MODULE) + set_target_properties(${lib_NAME} PROPERTIES VERSION ${DRACO_VERSION}) + if(NOT MSVC) + set_target_properties(${lib_NAME} PROPERTIES SOVERSION ${DRACO_SOVERSION}) + endif() endif() if(BUILD_SHARED_LIBS AND (MSVC OR WIN32)) diff --git a/contrib/draco/src/draco/core/cycle_timer.cc b/contrib/draco/src/draco/core/cycle_timer.cc index 94b4b28b2..58df4df77 100644 --- a/contrib/draco/src/draco/core/cycle_timer.cc +++ b/contrib/draco/src/draco/core/cycle_timer.cc @@ -17,31 +17,31 @@ namespace draco { void DracoTimer::Start() { #ifdef _WIN32 - QueryPerformanceCounter(&tv_start); + QueryPerformanceCounter(&tv_start_); #else - gettimeofday(&tv_start, nullptr); + gettimeofday(&tv_start_, nullptr); #endif } void DracoTimer::Stop() { #ifdef _WIN32 - QueryPerformanceCounter(&tv_end); + QueryPerformanceCounter(&tv_end_); #else - gettimeofday(&tv_end, nullptr); + gettimeofday(&tv_end_, nullptr); #endif } int64_t DracoTimer::GetInMs() { #ifdef _WIN32 LARGE_INTEGER elapsed = {0}; - elapsed.QuadPart = tv_end.QuadPart - tv_start.QuadPart; + elapsed.QuadPart = tv_end_.QuadPart - tv_start_.QuadPart; LARGE_INTEGER frequency = {0}; QueryPerformanceFrequency(&frequency); return elapsed.QuadPart * 1000 / frequency.QuadPart; #else - const int64_t seconds = (tv_end.tv_sec - tv_start.tv_sec) * 1000; - const int64_t milliseconds = (tv_end.tv_usec - tv_start.tv_usec) / 1000; + const int64_t seconds = (tv_end_.tv_sec - tv_start_.tv_sec) * 1000; + const int64_t milliseconds = (tv_end_.tv_usec - tv_start_.tv_usec) / 1000; return seconds + milliseconds; #endif } diff --git a/contrib/draco/src/draco/core/cycle_timer.h b/contrib/draco/src/draco/core/cycle_timer.h index 172f1c2e9..f480cc9d3 100644 --- a/contrib/draco/src/draco/core/cycle_timer.h +++ b/contrib/draco/src/draco/core/cycle_timer.h @@ -20,9 +20,10 @@ #define WIN32_LEAN_AND_MEAN #endif #include -typedef LARGE_INTEGER timeval; +typedef LARGE_INTEGER DracoTimeVal; #else #include +typedef timeval DracoTimeVal; #endif #include @@ -39,8 +40,8 @@ class DracoTimer { int64_t GetInMs(); private: - timeval tv_start; - timeval tv_end; + DracoTimeVal tv_start_; + DracoTimeVal tv_end_; }; typedef DracoTimer CycleTimer; diff --git a/contrib/draco/src/draco/io/parser_utils.cc b/contrib/draco/src/draco/io/parser_utils.cc index 4f95f6f84..12afacff6 100644 --- a/contrib/draco/src/draco/io/parser_utils.cc +++ b/contrib/draco/src/draco/io/parser_utils.cc @@ -18,6 +18,7 @@ #include #include #include +#include namespace draco { namespace parser { @@ -252,7 +253,7 @@ DecoderBuffer ParseLineIntoDecoderBuffer(DecoderBuffer *buffer) { std::string ToLower(const std::string &str) { std::string out; - std::transform(str.begin(), str.end(), std::back_inserter(out), [](unsigned char c){return tolower(c);}); + std::transform(str.begin(), str.end(), std::back_inserter(out), tolower); return out; } diff --git a/contrib/draco/src/draco/io/ply_reader.cc b/contrib/draco/src/draco/io/ply_reader.cc index cb32df225..ea7f2689a 100644 --- a/contrib/draco/src/draco/io/ply_reader.cc +++ b/contrib/draco/src/draco/io/ply_reader.cc @@ -268,14 +268,14 @@ std::vector PlyReader::SplitWords(const std::string &line) { while ((end = line.find_first_of(" \t\n\v\f\r", start)) != std::string::npos) { const std::string word(line.substr(start, end - start)); - if (!std::all_of(word.begin(), word.end(), [](unsigned char c){return isspace(c);})) { + if (!std::all_of(word.begin(), word.end(), isspace)) { output.push_back(word); } start = end + 1; } const std::string last_word(line.substr(start)); - if (!std::all_of(last_word.begin(), last_word.end(), [](unsigned char c){return isspace(c);})) { + if (!std::all_of(last_word.begin(), last_word.end(), isspace)) { output.push_back(last_word); } return output; diff --git a/contrib/draco/src/draco/io/stdio_file_reader.cc b/contrib/draco/src/draco/io/stdio_file_reader.cc index 560c3e9e8..a99c96f8f 100644 --- a/contrib/draco/src/draco/io/stdio_file_reader.cc +++ b/contrib/draco/src/draco/io/stdio_file_reader.cc @@ -87,7 +87,14 @@ size_t StdioFileReader::GetFileSize() { return false; } +#if _FILE_OFFSET_BITS == 64 + const size_t file_size = static_cast(ftello(file_)); +#elif defined _WIN64 + const size_t file_size = static_cast(_ftelli64(file_)); +#else const size_t file_size = static_cast(ftell(file_)); +#endif + rewind(file_); return file_size; From 1f32743f8b455082432adb249426f17c39dfa756 Mon Sep 17 00:00:00 2001 From: dlyr Date: Sat, 22 May 2021 00:56:01 +0200 Subject: [PATCH 126/335] Fix camera fov comment since full fov is stored --- include/assimp/camera.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/assimp/camera.h b/include/assimp/camera.h index d7324d10d..6a7acadbb 100644 --- a/include/assimp/camera.h +++ b/include/assimp/camera.h @@ -137,7 +137,7 @@ struct aiCamera */ C_STRUCT aiVector3D mLookAt; - /** Half horizontal field of view angle, in radians. + /** Horizontal field of view angle, in radians. * * The field of view angle is the angle between the center * line of the screen and the left or right border. From 28e34878cb9d85627cce81ed20dcb6f8b493d83b Mon Sep 17 00:00:00 2001 From: Jagoon <5785026+jagoon@users.noreply.github.com> Date: Sat, 22 May 2021 23:20:34 +0900 Subject: [PATCH 127/335] Fix fbx exporter bug if root node contains meshes. --- code/AssetLib/FBX/FBXExporter.cpp | 32 +++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/code/AssetLib/FBX/FBXExporter.cpp b/code/AssetLib/FBX/FBXExporter.cpp index e519f7e77..b24360cd5 100644 --- a/code/AssetLib/FBX/FBXExporter.cpp +++ b/code/AssetLib/FBX/FBXExporter.cpp @@ -541,10 +541,17 @@ void FBXExporter::WriteReferences () // (before any actual data is written) // --------------------------------------------------------------- -size_t count_nodes(const aiNode* n) { - size_t count = 1; +size_t count_nodes(const aiNode* n, const aiNode* root) { + size_t count; + if (n == root) { + count = n->mNumMeshes; // (not counting root node) + } else if (n->mNumMeshes > 1) { + count = n->mNumMeshes + 1; + } else { + count = 1; + } for (size_t i = 0; i < n->mNumChildren; ++i) { - count += count_nodes(n->mChildren[i]); + count += count_nodes(n->mChildren[i], root); } return count; } @@ -714,7 +721,7 @@ void FBXExporter::WriteDefinitions () // Model / FbxNode // <~~ node hierarchy - count = int32_t(count_nodes(mScene->mRootNode)) - 1; // (not counting root node) + count = int32_t(count_nodes(mScene->mRootNode, mScene->mRootNode)); if (count) { n = FBX::Node("ObjectType", "Model"); n.AddChild("Count", count); @@ -2625,17 +2632,14 @@ void FBXExporter::WriteModelNodes( ], new_node_uid ); - // write model node - FBX::Node m("Model"); + + aiNode new_node; // take name from mesh name, if it exists - std::string name = mScene->mMeshes[node->mMeshes[i]]->mName.C_Str(); - name += FBX::SEPARATOR + "Model"; - m.AddProperties(new_node_uid, name, "Mesh"); - m.AddChild("Version", int32_t(232)); - FBX::Node p("Properties70"); - p.AddP70enum("InheritType", 1); - m.AddChild(p); - m.Dump(outstream, binary, 1); + new_node.mName = mScene->mMeshes[node->mMeshes[i]]->mName; + // write model node + WriteModelNode( + outstream, binary, &new_node, new_node_uid, "Mesh", transform_chain + ); } } From b7b3c6db7e3ba84814dcec1394787e40d3ff7e3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Verdon?= Date: Sat, 22 May 2021 16:57:07 +0200 Subject: [PATCH 128/335] Fixing GCC 4.9 compilation issues --- code/AssetLib/glTF2/glTF2Asset.inl | 2 ++ include/assimp/TinyFormatter.h | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 8a793c144..256fc8931 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -58,7 +58,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma clang diagnostic ignored "-Wsign-compare" #elif defined(__GNUC__) #pragma GCC diagnostic push +#if (__GNUC__ > 4) #pragma GCC diagnostic ignored "-Wbool-compare" +#endif #pragma GCC diagnostic ignored "-Wsign-compare" #endif diff --git a/include/assimp/TinyFormatter.h b/include/assimp/TinyFormatter.h index ace20be5c..112f19013 100644 --- a/include/assimp/TinyFormatter.h +++ b/include/assimp/TinyFormatter.h @@ -88,9 +88,17 @@ public: underlying << sin; } + // Same problem as the copy constructor below, but with root cause is that stream move + // is not permitted on older GCC versions. Small performance impact on those platforms. +#if defined(__GNUC__) && (__GNUC__ == 4 && __GNUC_MINOR__ <= 9) + basic_formatter(basic_formatter&& other) { + underlying << (string)other; + } +#else basic_formatter(basic_formatter&& other) : underlying(std::move(other.underlying)) { } +#endif // The problem described here: // https://sourceforge.net/tracker/?func=detail&atid=1067632&aid=3358562&group_id=226462 From f96e3cde2d299aa1bc1fd5126d55fbc4aec5cc73 Mon Sep 17 00:00:00 2001 From: Jagoon <5785026+jagoon@users.noreply.github.com> Date: Sun, 23 May 2021 00:06:05 +0900 Subject: [PATCH 129/335] Fix transform chain is applied twice --- code/AssetLib/FBX/FBXExporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/FBX/FBXExporter.cpp b/code/AssetLib/FBX/FBXExporter.cpp index b24360cd5..f7beee2e0 100644 --- a/code/AssetLib/FBX/FBXExporter.cpp +++ b/code/AssetLib/FBX/FBXExporter.cpp @@ -2638,7 +2638,7 @@ void FBXExporter::WriteModelNodes( new_node.mName = mScene->mMeshes[node->mMeshes[i]]->mName; // write model node WriteModelNode( - outstream, binary, &new_node, new_node_uid, "Mesh", transform_chain + outstream, binary, &new_node, new_node_uid, "Mesh", std::vector>() ); } } From 799384f2b85b8d3ee9c5b9e18bbe532b4dc7c63c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Verdon?= Date: Sat, 22 May 2021 17:36:39 +0200 Subject: [PATCH 130/335] Adding the required c flag to compile zip files using gcc 4.9 --- code/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index ebc3e0116..d3cb6e923 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -1137,6 +1137,9 @@ ELSE() TARGET_COMPILE_OPTIONS(assimp PRIVATE -Werror) ENDIF() +# adds C_FLAGS required to compile zip.c on old GCC 4.x compiler +TARGET_COMPILE_FEATURES(assimp PUBLIC c_std_99) + TARGET_INCLUDE_DIRECTORIES ( assimp PUBLIC $ $ From f13515a39109f717c13ee1201e9000da6eab786a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Fran=C3=A7ois=20Verdon?= Date: Sun, 23 May 2021 19:06:16 +0200 Subject: [PATCH 131/335] Adding basic support for lights in FBX exporter --- code/AssetLib/FBX/FBXExporter.cpp | 77 +++++++++++++++++++++++++++++-- code/AssetLib/FBX/FBXExporter.h | 2 + 2 files changed, 74 insertions(+), 5 deletions(-) diff --git a/code/AssetLib/FBX/FBXExporter.cpp b/code/AssetLib/FBX/FBXExporter.cpp index e519f7e77..f2cae00b2 100644 --- a/code/AssetLib/FBX/FBXExporter.cpp +++ b/code/AssetLib/FBX/FBXExporter.cpp @@ -2196,7 +2196,65 @@ void FBXExporter::WriteObjects () bpnode.Dump(outstream, binary, indent); }*/ - // TODO: cameras, lights + // lights + indent = 1; + lights_uids.clear(); + for (size_t li = 0; li < mScene->mNumLights; ++li) { + aiLight* l = mScene->mLights[li]; + + int64_t uid = generate_uid(); + const std::string lightNodeAttributeName = l->mName.C_Str() + FBX::SEPARATOR + "NodeAttribute"; + + FBX::Node lna("NodeAttribute"); + lna.AddProperties(uid, lightNodeAttributeName, "Light"); + FBX::Node lnap("Properties70"); + + // Light color. + lnap.AddP70colorA("Color", l->mColorDiffuse.r, l->mColorDiffuse.g, l->mColorDiffuse.b); + + // TODO Assimp light description is quite concise and do not handle light intensity. + // Default value to 1000W. + lnap.AddP70numberA("Intensity", 1000); + + // FBXLight::EType conversion + switch (l->mType) { + case aiLightSource_POINT: + lnap.AddP70enum("LightType", 0); + break; + case aiLightSource_DIRECTIONAL: + lnap.AddP70enum("LightType", 1); + break; + case aiLightSource_SPOT: + lnap.AddP70enum("LightType", 2); + lnap.AddP70numberA("InnerAngle", AI_RAD_TO_DEG(l->mAngleInnerCone)); + lnap.AddP70numberA("OuterAngle", AI_RAD_TO_DEG(l->mAngleOuterCone)); + break; + // TODO Assimp do not handle 'area' nor 'volume' lights, but FBX does. + /*case aiLightSource_AREA: + lnap.AddP70enum("LightType", 3); + lnap.AddP70enum("AreaLightShape", 0); // 0=Rectangle, 1=Sphere + break; + case aiLightSource_VOLUME: + lnap.AddP70enum("LightType", 4); + break;*/ + default: + break; + } + + // Did not understood how to configure the decay so disabling attenuation. + lnap.AddP70enum("DecayType", 0); + + // Dump to FBX stream + lna.AddChild(lnap); + lna.AddChild("TypeFlags", FBX::FBXExportProperty("Light")); + lna.AddChild("GeometryVersion", FBX::FBXExportProperty(int32_t(124))); + lna.Dump(outstream, binary, indent); + + // Store name and uid (will be used later when parsing scene nodes) + lights_uids[l->mName.C_Str()] = uid; + } + + // TODO: cameras // write nodes (i.e. model hierarchy) // start at root node @@ -2600,10 +2658,19 @@ void FBXExporter::WriteModelNodes( // and connect them connections.emplace_back("C", "OO", node_attribute_uid, node_uid); } else { - // generate a null node so we can add children to it - WriteModelNode( - outstream, binary, node, node_uid, "Null", transform_chain - ); + const auto& lightIt = lights_uids.find(node->mName.C_Str()); + if(lightIt != lights_uids.end()) { + // Node has a light connected to it. + WriteModelNode( + outstream, binary, node, node_uid, "Light", transform_chain + ); + connections.emplace_back("C", "OO", lightIt->second, node_uid); + } else { + // generate a null node so we can add children to it + WriteModelNode( + outstream, binary, node, node_uid, "Null", transform_chain + ); + } } // if more than one child mesh, make nodes for each mesh diff --git a/code/AssetLib/FBX/FBXExporter.h b/code/AssetLib/FBX/FBXExporter.h index dcd1d2727..563183268 100644 --- a/code/AssetLib/FBX/FBXExporter.h +++ b/code/AssetLib/FBX/FBXExporter.h @@ -63,6 +63,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. struct aiScene; struct aiNode; +struct aiLight; //struct aiMaterial; namespace Assimp @@ -95,6 +96,7 @@ namespace Assimp std::vector mesh_uids; std::vector material_uids; std::map node_uids; + std::map lights_uids; // this crude unique-ID system is actually fine int64_t last_uid = 999999; From 5468dd667e41069da6f6ca97141c953838107eef Mon Sep 17 00:00:00 2001 From: Evangel Date: Wed, 26 May 2021 18:36:56 +1000 Subject: [PATCH 132/335] Fix bug in aiMetadata constructor that overwrites an array of one of aiString, aiVector3D, or aiMetadata with the first entry aiMetadata copy constructor calls aiMetadata::Get on the copied from aiMetadata using the const aiString &key version. When this is called on the metadata of an array type, this overwrites all entries with the first entry. This is due to the key of all entries in an array being the name of the array. ie, in a glTF2 file with an extension: "Extension" : [ "Value1", "Value2", "Value3" ] the aiMetadata struct for the "Extension" entry will have 3 entries with key/value pairs as: "Extension"/"Value1" "Extension"/"Value2" "Extension"/"Value3" So when the copy constructor calls the key based aiMetadata::Get, it will find "Value1" for all three entries. This change simply replaces the key based aiMetadata::Get with the index based aiMetadata::Get --- include/assimp/metadata.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/assimp/metadata.h b/include/assimp/metadata.h index fdc88be56..39ac386e4 100644 --- a/include/assimp/metadata.h +++ b/include/assimp/metadata.h @@ -202,17 +202,17 @@ struct aiMetadata { } break; case AI_AISTRING: { aiString v; - rhs.Get(mKeys[i], v); + rhs.Get(i, v); mValues[i].mData = new aiString(v); } break; case AI_AIVECTOR3D: { aiVector3D v; - rhs.Get(mKeys[i], v); + rhs.Get(i, v); mValues[i].mData = new aiVector3D(v); } break; case AI_AIMETADATA: { aiMetadata v; - rhs.Get(mKeys[i], v); + rhs.Get(i, v); mValues[i].mData = new aiMetadata(v); } break; #ifndef SWIG From 2f4fba070364ec3ceb34104e884ee9bddcbd8acf Mon Sep 17 00:00:00 2001 From: Evangel Date: Wed, 26 May 2021 19:11:19 +1000 Subject: [PATCH 133/335] Static cast i back to unsigned int because MSVC complains otherwise. i will never be bigger than an unsigned int since that's what mNumProperties is to begin with. --- include/assimp/metadata.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/assimp/metadata.h b/include/assimp/metadata.h index 39ac386e4..551a9aba4 100644 --- a/include/assimp/metadata.h +++ b/include/assimp/metadata.h @@ -156,7 +156,7 @@ struct aiMetadata { #ifdef __cplusplus - /** + /** * @brief The default constructor, set all members to zero by default. */ aiMetadata() AI_NO_EXCEPT @@ -202,17 +202,17 @@ struct aiMetadata { } break; case AI_AISTRING: { aiString v; - rhs.Get(i, v); + rhs.Get(static_cast(i), v); mValues[i].mData = new aiString(v); } break; case AI_AIVECTOR3D: { aiVector3D v; - rhs.Get(i, v); + rhs.Get(static_cast(i), v); mValues[i].mData = new aiVector3D(v); } break; case AI_AIMETADATA: { aiMetadata v; - rhs.Get(i, v); + rhs.Get(static_cast(i), v); mValues[i].mData = new aiMetadata(v); } break; #ifndef SWIG From c33a4b26346fd1cdaf7133dad06fcc29f47990aa Mon Sep 17 00:00:00 2001 From: Carsten Rudolph <18394207+crud89@users.noreply.github.com> Date: Thu, 27 May 2021 10:10:55 +0200 Subject: [PATCH 134/335] Fixed base name check. --- include/assimp/BlobIOSystem.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/include/assimp/BlobIOSystem.h b/include/assimp/BlobIOSystem.h index 081ccf32a..0abf166bc 100644 --- a/include/assimp/BlobIOSystem.h +++ b/include/assimp/BlobIOSystem.h @@ -197,7 +197,7 @@ class BlobIOSystem : public IOSystem { public: BlobIOSystem() : - baseName{} { + baseName{AI_BLOBIO_MAGIC} { } BlobIOSystem(const std::string &baseName) : @@ -213,13 +213,13 @@ public: public: // ------------------------------------------------------------------- const char *GetMagicFileName() const { - return baseName.empty() ? AI_BLOBIO_MAGIC : baseName.c_str(); + return baseName.c_str(); } // ------------------------------------------------------------------- aiExportDataBlob *GetBlobChain() { const auto magicName = std::string(this->GetMagicFileName()); - const bool hasBaseName = baseName.empty(); + const bool hasBaseName = baseName != AI_BLOBIO_MAGIC; // one must be the master aiExportDataBlob *master = nullptr, *cur; @@ -249,8 +249,7 @@ public: if (hasBaseName) { cur->name.Set(blobby.first); - } - else { + } else { // extract the file extension from the file written const std::string::size_type s = blobby.first.find_first_of('.'); cur->name.Set(s == std::string::npos ? blobby.first : blobby.first.substr(s + 1)); From 59467b204a6f099ed13ab7234bee08c1d89d24e6 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 27 May 2021 15:50:28 +0200 Subject: [PATCH 135/335] Create tech_debt.md --- .github/ISSUE_TEMPLATE/tech_debt.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/tech_debt.md diff --git a/.github/ISSUE_TEMPLATE/tech_debt.md b/.github/ISSUE_TEMPLATE/tech_debt.md new file mode 100644 index 000000000..2cd84d975 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/tech_debt.md @@ -0,0 +1,25 @@ +--- +name: Technical debt +about: Create a report to help us to fix and detect tech debts +title: '' +labels: '' +assignees: '' + +--- + +**Describe the technical debt** +A clear and concise description of what the tech debt is about. + +**Better solution** +A clear and concise description of what you would expect. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. From 919ae69fe8208524301689371e43a5581b8889c6 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 28 May 2021 08:44:40 +0200 Subject: [PATCH 136/335] Update tech_debt.md --- .github/ISSUE_TEMPLATE/tech_debt.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/tech_debt.md b/.github/ISSUE_TEMPLATE/tech_debt.md index 2cd84d975..a1172d932 100644 --- a/.github/ISSUE_TEMPLATE/tech_debt.md +++ b/.github/ISSUE_TEMPLATE/tech_debt.md @@ -2,7 +2,7 @@ name: Technical debt about: Create a report to help us to fix and detect tech debts title: '' -labels: '' +labels: 'Techdebt' assignees: '' --- From 3c51ff771cfa001e96b78382e299788250647ef8 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 28 May 2021 09:34:03 +0200 Subject: [PATCH 137/335] Update bug_report.md - Add the bug label - Make platform config more easy --- .github/ISSUE_TEMPLATE/bug_report.md | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea782..0f9a5f105 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,8 +1,8 @@ --- name: Bug report about: Create a report to help us improve -title: '' -labels: '' +title: 'Bug:' +labels: 'Bug' assignees: '' --- @@ -23,16 +23,10 @@ A clear and concise description of what you expected to happen. **Screenshots** If applicable, add screenshots to help explain your problem. -**Desktop (please complete the following information):** +**Platform (please complete the following information):** - OS: [e.g. iOS] - Browser [e.g. chrome, safari] - Version [e.g. 22] -**Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] - **Additional context** Add any other context about the problem here. From 2559befaca621a94f6e911493519730d42f17775 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 28 May 2021 10:26:03 +0200 Subject: [PATCH 138/335] Update feature_request.md - Add label Feature-Request --- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index bbcbbe7d6..87302a0f0 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -2,7 +2,7 @@ name: Feature request about: Suggest an idea for this project title: '' -labels: '' +labels: 'Feature-Request' assignees: '' --- From 7534b149cff5ef287fce50da4f17cebf4c5a3608 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20M=C3=B6ller?= Date: Fri, 28 May 2021 11:55:46 +0200 Subject: [PATCH 139/335] fix non skipped CR in header parsing --- code/AssetLib/Ply/PlyParser.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/code/AssetLib/Ply/PlyParser.cpp b/code/AssetLib/Ply/PlyParser.cpp index 4b416d1a1..59cb6b976 100644 --- a/code/AssetLib/Ply/PlyParser.cpp +++ b/code/AssetLib/Ply/PlyParser.cpp @@ -419,7 +419,8 @@ bool PLY::DOM::ParseHeader(IOStreamBuffer &streamBuffer, std::vector if (PLY::Element::ParseElement(streamBuffer, buffer, &out)) { // add the element to the list of elements alElements.push_back(out); - } else if (TokenMatch(buffer, "end_header", 10)) { + } else if ( TokenMatch(buffer, "end_header\r", 11) || //checks for header end with /r/n ending + TokenMatch(buffer, "end_header", 10)) { //checks for /n ending, if it doesn't end with /r/n // we have reached the end of the header break; } else { From c776924adf904cc1cb6cd4121394cb5d538a2c13 Mon Sep 17 00:00:00 2001 From: Garux Date: Sun, 30 May 2021 11:45:50 +0300 Subject: [PATCH 140/335] fix -Waddress-of-packed-member --- code/AssetLib/MDL/MDLFileData.h | 4 ++-- code/AssetLib/MDL/MDLLoader.cpp | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/code/AssetLib/MDL/MDLFileData.h b/code/AssetLib/MDL/MDLFileData.h index 872cee7f8..473a06989 100644 --- a/code/AssetLib/MDL/MDLFileData.h +++ b/code/AssetLib/MDL/MDLFileData.h @@ -704,8 +704,8 @@ struct GroupFrame //! Maximum vertex for all single frames Vertex max; - //! Time for all single frames - float time; // float[numframes] + //! List of times for all single frames + float *times; //! List of single frames SimpleFrame *frames; diff --git a/code/AssetLib/MDL/MDLLoader.cpp b/code/AssetLib/MDL/MDLLoader.cpp index b5010a37f..0f84111af 100644 --- a/code/AssetLib/MDL/MDLLoader.cpp +++ b/code/AssetLib/MDL/MDLLoader.cpp @@ -427,8 +427,9 @@ void MDLImporter::InternReadFile_Quake1() { pcFirstFrame = (MDL::SimpleFrame *)&pcFrames->frame; } else { // get the first frame in the group - BE_NCONST MDL::GroupFrame *pcFrames2 = (BE_NCONST MDL::GroupFrame *)pcFrames; - pcFirstFrame = (MDL::SimpleFrame *)( &pcFrames2->time + pcFrames2->numframes ); + BE_NCONST MDL::GroupFrame *pcFrames2 = (BE_NCONST MDL::GroupFrame *)szCurrent; + pcFirstFrame = (MDL::SimpleFrame *)( szCurrent + sizeof(MDL::GroupFrame::type) + sizeof(MDL::GroupFrame::numframes) + + sizeof(MDL::GroupFrame::min) + sizeof(MDL::GroupFrame::max) + sizeof(*MDL::GroupFrame::times) * pcFrames2->numframes ); } BE_NCONST MDL::Vertex *pcVertices = (BE_NCONST MDL::Vertex *)((pcFirstFrame->name) + sizeof(pcFirstFrame->name)); VALIDATE_FILE_SIZE((const unsigned char *)(pcVertices + pcHeader->num_verts)); From 77ce4080b689d4fcee3435813c3811dfa5781682 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 30 May 2021 21:54:04 +0200 Subject: [PATCH 141/335] fix viewer in case of unknown primitives. --- code/AssetLib/DXF/DXFLoader.cpp | 2 -- tools/assimp_view/assimp_view.cpp | 7 +++---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/code/AssetLib/DXF/DXFLoader.cpp b/code/AssetLib/DXF/DXFLoader.cpp index 5d32ed121..49d572b0b 100644 --- a/code/AssetLib/DXF/DXFLoader.cpp +++ b/code/AssetLib/DXF/DXFLoader.cpp @@ -5,8 +5,6 @@ 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, diff --git a/tools/assimp_view/assimp_view.cpp b/tools/assimp_view/assimp_view.cpp index 260b22941..5ab7c53ad 100644 --- a/tools/assimp_view/assimp_view.cpp +++ b/tools/assimp_view/assimp_view.cpp @@ -489,7 +489,7 @@ int CreateAssetData() { nidx = 3; break; default: - ai_assert(false); + CLogWindow::Instance().WriteLine("Unknown primitiv type"); break; }; @@ -500,8 +500,7 @@ int CreateAssetData() { // check whether we can use 16 bit indices if (numIndices >= 65536) { // create 32 bit index buffer - if (FAILED(g_piDevice->CreateIndexBuffer(4 * - numIndices, + if (FAILED(g_piDevice->CreateIndexBuffer(4 * numIndices, D3DUSAGE_WRITEONLY | dwUsage, D3DFMT_INDEX32, D3DPOOL_DEFAULT, @@ -523,7 +522,7 @@ int CreateAssetData() { } else { // create 16 bit index buffer if (FAILED(g_piDevice->CreateIndexBuffer(2 * - numIndices, +numIndices, D3DUSAGE_WRITEONLY | dwUsage, D3DFMT_INDEX16, D3DPOOL_DEFAULT, From 84a2e1fc922542c3c60dff94c5db682f7ab95711 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 3 Jun 2021 20:43:37 +0200 Subject: [PATCH 142/335] Update unity plugin to trilib2 - closes https://github.com/assimp/assimp/issues/3872 --- Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index f2ea4b094..949d60966 100644 --- a/Readme.md +++ b/Readme.md @@ -42,7 +42,7 @@ Take a look into the https://github.com/assimp/assimp/blob/master/Build.md file. * [.NET](https://bitbucket.org/Starnick/assimpnet/src/master/) * [Pascal](port/AssimpPascal/Readme.md) * [Javascript (Alpha)](https://github.com/makc/assimp2json) -* [Unity 3d Plugin](https://www.assetstore.unity3d.com/en/#!/content/91777) +* [Unity 3d Plugin](https://ricardoreis.net/trilib-2/) * [JVM](https://github.com/kotlin-graphics/assimp) Full jvm port (current [status](https://github.com/kotlin-graphics/assimp/wiki/Status)) * [HAXE-Port](https://github.com/longde123/assimp-haxe) The Assimp-HAXE-port. * [Rust](https://github.com/jkvargas/russimp) From 3a32612b71d4b00ca100b332268b0b272ffc14ba Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 3 Jun 2021 20:55:31 +0200 Subject: [PATCH 143/335] Add skipping of unused nodes. --- code/AssetLib/X3D/X3DImporter.cpp | 97 +++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/code/AssetLib/X3D/X3DImporter.cpp b/code/AssetLib/X3D/X3DImporter.cpp index 24814876b..0fcfec726 100644 --- a/code/AssetLib/X3D/X3DImporter.cpp +++ b/code/AssetLib/X3D/X3DImporter.cpp @@ -124,6 +124,103 @@ struct WordIterator { const char *WordIterator::whitespace = ", \t\r\n"; +void skipUnsupportedNode(const std::string &pParentNodeName, XmlNode &node) { + static const size_t Uns_Skip_Len = 192; + static const char *Uns_Skip[Uns_Skip_Len] = { + // CAD geometry component + "CADAssembly", "CADFace", "CADLayer", "CADPart", "IndexedQuadSet", "QuadSet", + // Core + "ROUTE", "ExternProtoDeclare", "ProtoDeclare", "ProtoInstance", "ProtoInterface", "WorldInfo", + // Distributed interactive simulation (DIS) component + "DISEntityManager", "DISEntityTypeMapping", "EspduTransform", "ReceiverPdu", "SignalPdu", "TransmitterPdu", + // Cube map environmental texturing component + "ComposedCubeMapTexture", "GeneratedCubeMapTexture", "ImageCubeMapTexture", + // Environmental effects component + "Background", "Fog", "FogCoordinate", "LocalFog", "TextureBackground", + // Environmental sensor component + "ProximitySensor", "TransformSensor", "VisibilitySensor", + // Followers component + "ColorChaser", "ColorDamper", "CoordinateChaser", "CoordinateDamper", "OrientationChaser", "OrientationDamper", "PositionChaser", "PositionChaser2D", + "PositionDamper", "PositionDamper2D", "ScalarChaser", "ScalarDamper", "TexCoordChaser2D", "TexCoordDamper2D", + // Geospatial component + "GeoCoordinate", "GeoElevationGrid", "GeoLocation", "GeoLOD", "GeoMetadata", "GeoOrigin", "GeoPositionInterpolator", "GeoProximitySensor", + "GeoTouchSensor", "GeoTransform", "GeoViewpoint", + // Humanoid Animation (H-Anim) component + "HAnimDisplacer", "HAnimHumanoid", "HAnimJoint", "HAnimSegment", "HAnimSite", + // Interpolation component + "ColorInterpolator", "CoordinateInterpolator", "CoordinateInterpolator2D", "EaseInEaseOut", "NormalInterpolator", "OrientationInterpolator", + "PositionInterpolator", "PositionInterpolator2D", "ScalarInterpolator", "SplinePositionInterpolator", "SplinePositionInterpolator2D", + "SplineScalarInterpolator", "SquadOrientationInterpolator", + // Key device sensor component + "KeySensor", "StringSensor", + // Layering component + "Layer", "LayerSet", "Viewport", + // Layout component + "Layout", "LayoutGroup", "LayoutLayer", "ScreenFontStyle", "ScreenGroup", + // Navigation component + "Billboard", "Collision", "LOD", "NavigationInfo", "OrthoViewpoint", "Viewpoint", "ViewpointGroup", + // Networking component + "EXPORT", "IMPORT", "Anchor", "LoadSensor", + // NURBS component + "Contour2D", "ContourPolyline2D", "CoordinateDouble", "NurbsCurve", "NurbsCurve2D", "NurbsOrientationInterpolator", "NurbsPatchSurface", + "NurbsPositionInterpolator", "NurbsSet", "NurbsSurfaceInterpolator", "NurbsSweptSurface", "NurbsSwungSurface", "NurbsTextureCoordinate", + "NurbsTrimmedSurface", + // Particle systems component + "BoundedPhysicsModel", "ConeEmitter", "ExplosionEmitter", "ForcePhysicsModel", "ParticleSystem", "PointEmitter", "PolylineEmitter", "SurfaceEmitter", + "VolumeEmitter", "WindPhysicsModel", + // Picking component + "LinePickSensor", "PickableGroup", "PointPickSensor", "PrimitivePickSensor", "VolumePickSensor", + // Pointing device sensor component + "CylinderSensor", "PlaneSensor", "SphereSensor", "TouchSensor", + // Rendering component + "ClipPlane", + // Rigid body physics + "BallJoint", "CollidableOffset", "CollidableShape", "CollisionCollection", "CollisionSensor", "CollisionSpace", "Contact", "DoubleAxisHingeJoint", + "MotorJoint", "RigidBody", "RigidBodyCollection", "SingleAxisHingeJoint", "SliderJoint", "UniversalJoint", + // Scripting component + "Script", + // Programmable shaders component + "ComposedShader", "FloatVertexAttribute", "Matrix3VertexAttribute", "Matrix4VertexAttribute", "PackagedShader", "ProgramShader", "ShaderPart", + "ShaderProgram", + // Shape component + "FillProperties", "LineProperties", "TwoSidedMaterial", + // Sound component + "AudioClip", "Sound", + // Text component + "FontStyle", "Text", + // Texturing3D Component + "ComposedTexture3D", "ImageTexture3D", "PixelTexture3D", "TextureCoordinate3D", "TextureCoordinate4D", "TextureTransformMatrix3D", "TextureTransform3D", + // Texturing component + "MovieTexture", "MultiTexture", "MultiTextureCoordinate", "MultiTextureTransform", "PixelTexture", "TextureCoordinateGenerator", "TextureProperties", + // Time component + "TimeSensor", + // Event Utilities component + "BooleanFilter", "BooleanSequencer", "BooleanToggle", "BooleanTrigger", "IntegerSequencer", "IntegerTrigger", "TimeTrigger", + // Volume rendering component + "BlendedVolumeStyle", "BoundaryEnhancementVolumeStyle", "CartoonVolumeStyle", "ComposedVolumeStyle", "EdgeEnhancementVolumeStyle", "IsoSurfaceVolumeData", + "OpacityMapVolumeStyle", "ProjectionVolumeStyle", "SegmentedVolumeData", "ShadedVolumeStyle", "SilhouetteEnhancementVolumeStyle", "ToneMappedVolumeStyle", + "VolumeData" + }; + + const std::string nn = node.name(); + bool found = false; + bool close_found = false; + + for (size_t i = 0; i < Uns_Skip_Len; i++) { + if (nn == Uns_Skip[i]) { + found = true; + if (node.empty()) { + close_found = true; + break; + } + } + } + + if (!found) throw DeadlyImportError("Unknown node \"" + nn + "\" in " + pParentNodeName + "."); + + LogInfo("Skipping node \"" + nn + "\" in " + pParentNodeName + "."); +} + X3DImporter::X3DImporter() : mNodeElementCur(nullptr), mScene(nullptr) { From b4fc41bc094574400b0a688ec1381888e40ee99c Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 3 Jun 2021 22:52:10 +0200 Subject: [PATCH 144/335] Use corret attribute name - closes https://github.com/assimp/assimp/issues/3887 --- port/PyAssimp/pyassimp/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/port/PyAssimp/pyassimp/core.py b/port/PyAssimp/pyassimp/core.py index 37beac886..35ad882b3 100644 --- a/port/PyAssimp/pyassimp/core.py +++ b/port/PyAssimp/pyassimp/core.py @@ -211,7 +211,7 @@ def _init(self, target = None, parent = None): else: # starts with 'm' but not iterable - setattr(target, name, obj) + setattr(target, m, obj) logger.debug("Added " + name + " as self." + name + " (type: " + str(type(obj)) + ")") if _is_init_type(obj): From cc912f09f7cf1d0d7cbdff5d6d6617d5af904c3a Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 3 Jun 2021 23:16:20 +0200 Subject: [PATCH 145/335] update pugi_xml to 1.11 --- contrib/pugixml/readme.txt | 10 +- contrib/pugixml/src/pugiconfig.hpp | 21 +- contrib/pugixml/src/pugixml.cpp | 638 +++++++++++++++++++---------- contrib/pugixml/src/pugixml.hpp | 69 +++- 4 files changed, 496 insertions(+), 242 deletions(-) diff --git a/contrib/pugixml/readme.txt b/contrib/pugixml/readme.txt index 5beb08a90..bfb1875cb 100644 --- a/contrib/pugixml/readme.txt +++ b/contrib/pugixml/readme.txt @@ -1,7 +1,7 @@ -pugixml 1.9 - an XML processing library +pugixml 1.11 - an XML processing library -Copyright (C) 2006-2018, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) -Report bugs and download new versions at http://pugixml.org/ +Copyright (C) 2006-2020, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) +Report bugs and download new versions at https://pugixml.org/ This is the distribution of pugixml, which is a C++ XML processing library, which consists of a DOM-like interface with rich traversal/modification @@ -13,8 +13,6 @@ automatically during parsing/saving). The distribution contains the following folders: - contrib/ - various contributions to pugixml - docs/ - documentation docs/samples - pugixml usage examples docs/quickstart.html - quick start guide @@ -28,7 +26,7 @@ The distribution contains the following folders: This library is distributed under the MIT License: -Copyright (c) 2006-2018 Arseny Kapoulkine +Copyright (c) 2006-2019 Arseny Kapoulkine Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/contrib/pugixml/src/pugiconfig.hpp b/contrib/pugixml/src/pugiconfig.hpp index 065b3c8bb..405a66b65 100644 --- a/contrib/pugixml/src/pugiconfig.hpp +++ b/contrib/pugixml/src/pugiconfig.hpp @@ -1,8 +1,8 @@ /** - * pugixml parser - version 1.9 + * pugixml parser - version 1.11 * -------------------------------------------------------- - * Copyright (C) 2006-2018, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) - * Report bugs and download new versions at http://pugixml.org/ + * Copyright (C) 2006-2020, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + * Report bugs and download new versions at https://pugixml.org/ * * This library is distributed under the MIT License. See notice at the end * of this file. @@ -30,10 +30,8 @@ // #define PUGIXML_NO_EXCEPTIONS // Set this to control attributes for public classes/functions, i.e.: -//#ifdef _WIN32 -//#define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL -//#define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL -//#endif +// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL +// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL // #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall // In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead @@ -42,16 +40,19 @@ // #define PUGIXML_MEMORY_OUTPUT_STACK 10240 // #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096 +// Tune this constant to adjust max nesting for XPath queries +// #define PUGIXML_XPATH_DEPTH_LIMIT 1024 + // Uncomment this to switch to header-only version -#define PUGIXML_HEADER_ONLY +// #define PUGIXML_HEADER_ONLY // Uncomment this to enable long long support -//#define PUGIXML_HAS_LONG_LONG +// #define PUGIXML_HAS_LONG_LONG #endif /** - * Copyright (c) 2006-2018 Arseny Kapoulkine + * Copyright (c) 2006-2020 Arseny Kapoulkine * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation diff --git a/contrib/pugixml/src/pugixml.cpp b/contrib/pugixml/src/pugixml.cpp index 2afff09dd..efdcdf699 100644 --- a/contrib/pugixml/src/pugixml.cpp +++ b/contrib/pugixml/src/pugixml.cpp @@ -1,8 +1,8 @@ /** - * pugixml parser - version 1.9 + * pugixml parser - version 1.11 * -------------------------------------------------------- - * Copyright (C) 2006-2018, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) - * Report bugs and download new versions at http://pugixml.org/ + * Copyright (C) 2006-2020, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + * Report bugs and download new versions at https://pugixml.org/ * * This library is distributed under the MIT License. See notice at the end * of this file. @@ -378,7 +378,7 @@ PUGI__NS_BEGIN static PUGI__UNSIGNED_OVERFLOW unsigned int hash(const void* key) { - unsigned int h = static_cast(reinterpret_cast(key)); + unsigned int h = static_cast(reinterpret_cast(key) & 0xffffffff); // MurmurHash3 32-bit finalizer h ^= h >> 16; @@ -1861,7 +1861,7 @@ PUGI__NS_BEGIN enum chartypex_t { ctx_special_pcdata = 1, // Any symbol >= 0 and < 32 (except \t, \r, \n), &, <, > - ctx_special_attr = 2, // Any symbol >= 0 and < 32 (except \t), &, <, >, " + ctx_special_attr = 2, // Any symbol >= 0 and < 32, &, <, ", ' ctx_start_symbol = 4, // Any symbol > 127, a-z, A-Z, _ ctx_digit = 8, // 0-9 ctx_symbol = 16 // Any symbol > 127, a-z, A-Z, 0-9, _, -, . @@ -1869,10 +1869,10 @@ PUGI__NS_BEGIN static const unsigned char chartypex_table[256] = { - 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 2, 3, 3, // 0-15 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 3, 3, 2, 3, 3, // 0-15 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 16-31 - 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 16, 16, 0, // 32-47 - 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 3, 0, 3, 0, // 48-63 + 0, 0, 2, 0, 0, 0, 3, 2, 0, 0, 0, 0, 0, 16, 16, 0, // 32-47 + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 3, 0, 1, 0, // 48-63 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 64-79 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 20, // 80-95 @@ -2709,7 +2709,7 @@ PUGI__NS_BEGIN { PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_trim_pcdata == 0x0800); - switch (((optmask >> 4) & 3) | ((optmask >> 9) & 4)) // get bitmask for flags (eol escapes trim) + switch (((optmask >> 4) & 3) | ((optmask >> 9) & 4)) // get bitmask for flags (trim eol escapes); this simultaneously checks 3 options from assertion above { case 0: return strconv_pcdata_impl::parse; case 1: return strconv_pcdata_impl::parse; @@ -2878,7 +2878,7 @@ PUGI__NS_BEGIN { PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80); - switch ((optmask >> 4) & 15) // get bitmask for flags (wconv wnorm eol escapes) + switch ((optmask >> 4) & 15) // get bitmask for flags (wnorm wconv eol escapes); this simultaneously checks 4 options from assertion above { case 0: return strconv_attribute_impl::parse_simple; case 1: return strconv_attribute_impl::parse_simple; @@ -3903,7 +3903,7 @@ PUGI__NS_BEGIN xml_encoding encoding; }; - PUGI__FN void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type) + PUGI__FN void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags) { while (*s) { @@ -3930,7 +3930,17 @@ PUGI__NS_BEGIN ++s; break; case '"': - writer.write('&', 'q', 'u', 'o', 't', ';'); + if (flags & format_attribute_single_quote) + writer.write('"'); + else + writer.write('&', 'q', 'u', 'o', 't', ';'); + ++s; + break; + case '\'': + if (flags & format_attribute_single_quote) + writer.write('&', 'a', 'p', 'o', 's', ';'); + else + writer.write('\''); ++s; break; default: // s is not a usual symbol @@ -3938,7 +3948,8 @@ PUGI__NS_BEGIN unsigned int ch = static_cast(*s++); assert(ch < 32); - writer.write('&', '#', static_cast((ch / 10) + '0'), static_cast((ch % 10) + '0'), ';'); + if (!(flags & format_skip_control_chars)) + writer.write('&', '#', static_cast((ch / 10) + '0'), static_cast((ch % 10) + '0'), ';'); } } } @@ -3949,7 +3960,7 @@ PUGI__NS_BEGIN if (flags & format_no_escapes) writer.write_string(s); else - text_output_escaped(writer, s, type); + text_output_escaped(writer, s, type, flags); } PUGI__FN void text_output_cdata(xml_buffered_writer& writer, const char_t* s) @@ -4063,6 +4074,7 @@ PUGI__NS_BEGIN PUGI__FN void node_output_attributes(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth) { const char_t* default_name = PUGIXML_TEXT(":anonymous"); + const char_t enquotation_char = (flags & format_attribute_single_quote) ? '\'' : '"'; for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute) { @@ -4078,12 +4090,12 @@ PUGI__NS_BEGIN } writer.write_string(a->name ? a->name + 0 : default_name); - writer.write('=', '"'); + writer.write('=', enquotation_char); if (a->value) text_output(writer, a->value, ctx_special_attr, flags); - writer.write('"'); + writer.write(enquotation_char); } } @@ -4423,6 +4435,9 @@ PUGI__NS_BEGIN while (sit && sit != sn) { + // loop invariant: dit is inside the subtree rooted at dn + assert(dit); + // when a tree is copied into one of the descendants, we need to skip that subtree to avoid an infinite loop if (sit != dn) { @@ -4452,9 +4467,14 @@ PUGI__NS_BEGIN sit = sit->parent; dit = dit->parent; + + // loop invariant: dit is inside the subtree rooted at dn while sit is inside sn + assert(sit == sn || dit); } while (sit != sn); } + + assert(!sit || dit == dn->parent); } PUGI__FN void node_copy_attribute(xml_attribute_struct* da, xml_attribute_struct* sa) @@ -4653,19 +4673,19 @@ PUGI__NS_BEGIN } template - PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, float value) + PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, float value, int precision) { char buf[128]; - PUGI__SNPRINTF(buf, "%.9g", value); + PUGI__SNPRINTF(buf, "%.*g", precision, double(value)); return set_value_ascii(dest, header, header_mask, buf); } template - PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, double value) + PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, double value, int precision) { char buf[128]; - PUGI__SNPRINTF(buf, "%.17g", value); + PUGI__SNPRINTF(buf, "%.*g", precision, value); return set_value_ascii(dest, header, header_mask, buf); } @@ -4688,6 +4708,7 @@ PUGI__NS_BEGIN char_t* buffer = 0; size_t length = 0; + // coverity[var_deref_model] if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory); // delete original buffer if we performed a conversion @@ -4960,7 +4981,12 @@ PUGI__NS_BEGIN #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR))) PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) { +#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 + FILE* file = 0; + return _wfopen_s(&file, path, mode) == 0 ? file : 0; +#else return _wfopen(path, mode); +#endif } #else PUGI__FN char* convert_path_heap(const wchar_t* str) @@ -5004,6 +5030,16 @@ PUGI__NS_BEGIN } #endif + PUGI__FN FILE* open_file(const char* path, const char* mode) + { +#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 + FILE* file = 0; + return fopen_s(&file, path, mode) == 0 ? file : 0; +#else + return fopen(path, mode); +#endif + } + PUGI__FN bool save_file_impl(const xml_document& doc, FILE* file, const char_t* indent, unsigned int flags, xml_encoding encoding) { if (!file) return false; @@ -5329,14 +5365,28 @@ namespace pugi { if (!_attr) return false; - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); + return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, default_double_precision); + } + + PUGI__FN bool xml_attribute::set_value(double rhs, int precision) + { + if (!_attr) return false; + + return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, precision); } PUGI__FN bool xml_attribute::set_value(float rhs) { if (!_attr) return false; - return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); + return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, default_float_precision); + } + + PUGI__FN bool xml_attribute::set_value(float rhs, int precision) + { + if (!_attr) return false; + + return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, precision); } PUGI__FN bool xml_attribute::set_value(bool rhs) @@ -6046,6 +6096,27 @@ namespace pugi return true; } + PUGI__FN bool xml_node::remove_attributes() + { + if (!_root) return false; + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return false; + + for (xml_attribute_struct* attr = _root->first_attribute; attr; ) + { + xml_attribute_struct* next = attr->next_attribute; + + impl::destroy_attribute(attr, alloc); + + attr = next; + } + + _root->first_attribute = 0; + + return true; + } + PUGI__FN bool xml_node::remove_child(const char_t* name_) { return remove_child(child(name_)); @@ -6064,6 +6135,27 @@ namespace pugi return true; } + PUGI__FN bool xml_node::remove_children() + { + if (!_root) return false; + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return false; + + for (xml_node_struct* cur = _root->first_child; cur; ) + { + xml_node_struct* next = cur->next_sibling; + + impl::destroy_node(cur, alloc); + + cur = next; + } + + _root->first_child = 0; + + return true; + } + PUGI__FN xml_parse_result xml_node::append_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) { // append_buffer is only valid for elements/documents @@ -6164,16 +6256,9 @@ namespace pugi PUGI__FN xml_node xml_node::first_element_by_path(const char_t* path_, char_t delimiter) const { - xml_node found = *this; // Current search context. + xml_node context = path_[0] == delimiter ? root() : *this; - if (!_root || !path_[0]) return found; - - if (path_[0] == delimiter) - { - // Absolute path; e.g. '/foo/bar' - found = found.root(); - ++path_; - } + if (!context._root) return xml_node(); const char_t* path_segment = path_; @@ -6183,19 +6268,19 @@ namespace pugi while (*path_segment_end && *path_segment_end != delimiter) ++path_segment_end; - if (path_segment == path_segment_end) return found; + if (path_segment == path_segment_end) return context; const char_t* next_segment = path_segment_end; while (*next_segment == delimiter) ++next_segment; if (*path_segment == '.' && path_segment + 1 == path_segment_end) - return found.first_element_by_path(next_segment, delimiter); + return context.first_element_by_path(next_segment, delimiter); else if (*path_segment == '.' && *(path_segment+1) == '.' && path_segment + 2 == path_segment_end) - return found.parent().first_element_by_path(next_segment, delimiter); + return context.parent().first_element_by_path(next_segment, delimiter); else { - for (xml_node_struct* j = found._root->first_child; j; j = j->next_sibling) + for (xml_node_struct* j = context._root->first_child; j; j = j->next_sibling) { if (j->name && impl::strequalrange(j->name, path_segment, static_cast(path_segment_end - path_segment))) { @@ -6490,14 +6575,28 @@ namespace pugi { xml_node_struct* dn = _data_new(); - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; + return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, default_float_precision) : false; + } + + PUGI__FN bool xml_text::set(float rhs, int precision) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, precision) : false; } PUGI__FN bool xml_text::set(double rhs) { xml_node_struct* dn = _data_new(); - return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; + return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, default_double_precision) : false; + } + + PUGI__FN bool xml_text::set(double rhs, int precision) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, precision) : false; } PUGI__FN bool xml_text::set(bool rhs) @@ -6873,8 +6972,7 @@ namespace pugi { reset(); - for (xml_node cur = proto.first_child(); cur; cur = cur.next_sibling()) - append_copy(cur); + impl::node_copy_tree(_root, proto._root); } PUGI__FN void xml_document::_create() @@ -7104,7 +7202,7 @@ namespace pugi reset(); using impl::auto_deleter; // MSVC7 workaround - auto_deleter file(fopen(path_, "rb"), impl::close_file); + auto_deleter file(impl::open_file(path_, "rb"), impl::close_file); return impl::load_file_impl(static_cast(_root), file.data, options, encoding, &_buffer); } @@ -7187,7 +7285,7 @@ namespace pugi PUGI__FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const { using impl::auto_deleter; // MSVC7 workaround - auto_deleter file(fopen(path_, (flags & format_save_file_text) ? "w" : "wb"), impl::close_file); + auto_deleter file(impl::open_file(path_, (flags & format_save_file_text) ? "w" : "wb"), impl::close_file); return impl::save_file_impl(*this, file.data, indent, flags, encoding); } @@ -7331,14 +7429,14 @@ PUGI__NS_BEGIN } }; - template void swap(T& lhs, T& rhs) + template inline void swap(T& lhs, T& rhs) { T temp = lhs; lhs = rhs; rhs = temp; } - template I min_element(I begin, I end, const Pred& pred) + template PUGI__FN I min_element(I begin, I end, const Pred& pred) { I result = begin; @@ -7349,17 +7447,20 @@ PUGI__NS_BEGIN return result; } - template void reverse(I begin, I end) + template PUGI__FN void reverse(I begin, I end) { - while (end - begin > 1) swap(*begin++, *--end); + while (end - begin > 1) + swap(*begin++, *--end); } - template I unique(I begin, I end) + template PUGI__FN I unique(I begin, I end) { // fast skip head - while (end - begin > 1 && *begin != *(begin + 1)) begin++; + while (end - begin > 1 && *begin != *(begin + 1)) + begin++; - if (begin == end) return begin; + if (begin == end) + return begin; // last written element I write = begin++; @@ -7377,7 +7478,7 @@ PUGI__NS_BEGIN return write + 1; } - template void insertion_sort(T* begin, T* end, const Pred& pred) + template PUGI__FN void insertion_sort(T* begin, T* end, const Pred& pred) { if (begin == end) return; @@ -7399,16 +7500,19 @@ PUGI__NS_BEGIN } } - template I median3(I first, I middle, I last, const Pred& pred) + template inline I median3(I first, I middle, I last, const Pred& pred) { - if (pred(*middle, *first)) swap(middle, first); - if (pred(*last, *middle)) swap(last, middle); - if (pred(*middle, *first)) swap(middle, first); + if (pred(*middle, *first)) + swap(middle, first); + if (pred(*last, *middle)) + swap(last, middle); + if (pred(*middle, *first)) + swap(middle, first); return middle; } - template void partition3(T* begin, T* end, T pivot, const Pred& pred, T** out_eqbeg, T** out_eqend) + template PUGI__FN void partition3(T* begin, T* end, T pivot, const Pred& pred, T** out_eqbeg, T** out_eqend) { // invariant: array is split into 4 groups: = < ? > (each variable denotes the boundary between the groups) T* eq = begin; @@ -7435,7 +7539,7 @@ PUGI__NS_BEGIN *out_eqend = gt; } - template void sort(I begin, I end, const Pred& pred) + template PUGI__FN void sort(I begin, I end, const Pred& pred) { // sort large chunks while (end - begin > 16) @@ -7464,6 +7568,41 @@ PUGI__NS_BEGIN // insertion sort small chunk insertion_sort(begin, end, pred); } + + PUGI__FN bool hash_insert(const void** table, size_t size, const void* key) + { + assert(key); + + unsigned int h = static_cast(reinterpret_cast(key)); + + // MurmurHash3 32-bit finalizer + h ^= h >> 16; + h *= 0x85ebca6bu; + h ^= h >> 13; + h *= 0xc2b2ae35u; + h ^= h >> 16; + + size_t hashmod = size - 1; + size_t bucket = h & hashmod; + + for (size_t probe = 0; probe <= hashmod; ++probe) + { + if (table[bucket] == 0) + { + table[bucket] = key; + return true; + } + + if (table[bucket] == key) + return false; + + // hash collision, quadratic probing + bucket = (bucket + probe + 1) & hashmod; + } + + assert(false && "Hash table is full"); // unreachable + return false; + } PUGI__NS_END // Allocator used for AST and evaluation stacks @@ -8053,15 +8192,6 @@ PUGI__NS_BEGIN } }; - struct duplicate_comparator - { - bool operator()(const xpath_node& lhs, const xpath_node& rhs) const - { - if (lhs.attribute()) return rhs.attribute() ? lhs.attribute() < rhs.attribute() : true; - else return rhs.attribute() ? false : lhs.node() < rhs.node(); - } - }; - PUGI__FN double gen_nan() { #if defined(__STDC_IEC_559__) || ((FLT_RADIX - 0 == 2) && (FLT_MAX_EXP - 0 == 128) && (FLT_MANT_DIG - 0 == 24)) @@ -8069,7 +8199,7 @@ PUGI__NS_BEGIN typedef uint32_t UI; // BCC5 workaround union { float f; UI i; } u; u.i = 0x7fc00000; - return u.f; + return double(u.f); #else // fallback const volatile double zero = 0.0; @@ -8849,12 +8979,42 @@ PUGI__NS_BEGIN _end = pos; } - void remove_duplicates() + void remove_duplicates(xpath_allocator* alloc) { - if (_type == xpath_node_set::type_unsorted) - sort(_begin, _end, duplicate_comparator()); + if (_type == xpath_node_set::type_unsorted && _end - _begin > 2) + { + xpath_allocator_capture cr(alloc); - _end = unique(_begin, _end); + size_t size_ = static_cast(_end - _begin); + + size_t hash_size = 1; + while (hash_size < size_ + size_ / 2) hash_size *= 2; + + const void** hash_data = static_cast(alloc->allocate(hash_size * sizeof(void**))); + if (!hash_data) return; + + memset(hash_data, 0, hash_size * sizeof(const void**)); + + xpath_node* write = _begin; + + for (xpath_node* it = _begin; it != _end; ++it) + { + const void* attr = it->attribute().internal_object(); + const void* node = it->node().internal_object(); + const void* key = attr ? attr : node; + + if (key && hash_insert(hash_data, hash_size, key)) + { + *write++ = *it; + } + } + + _end = write; + } + else + { + _end = unique(_begin, _end); + } } xpath_node_set::type_t type() const @@ -9611,7 +9771,7 @@ PUGI__NS_BEGIN { xpath_context c(*it, i, size); - if (expr->eval_number(c, stack) == i) + if (expr->eval_number(c, stack) == static_cast(i)) { *last++ = *it; @@ -9635,11 +9795,11 @@ PUGI__NS_BEGIN double er = expr->eval_number(c, stack); - if (er >= 1.0 && er <= size) + if (er >= 1.0 && er <= static_cast(size)) { size_t eri = static_cast(er); - if (er == eri) + if (er == static_cast(eri)) { xpath_node r = last[eri - 1]; @@ -10083,6 +10243,7 @@ PUGI__NS_BEGIN bool once = (axis == axis_attribute && _test == nodetest_name) || (!_right && eval_once(axis_type, eval)) || + // coverity[mixed_enums] (_right && !_right->_next && _right->_test == predicate_constant_one); xpath_node_set_raw ns; @@ -10115,7 +10276,7 @@ PUGI__NS_BEGIN // child, attribute and self axes always generate unique set of nodes // for other axis, if the set stayed sorted, it stayed unique because the traversal algorithms do not visit the same node twice if (axis != axis_child && axis != axis_attribute && axis != axis_self && ns.type() == xpath_node_set::type_unsorted) - ns.remove_duplicates(); + ns.remove_duplicates(stack.temp); return ns; } @@ -10275,35 +10436,38 @@ PUGI__NS_BEGIN if (_rettype == xpath_type_boolean) return _data.variable->get_boolean(); + + // variable needs to be converted to the correct type, this is handled by the fallthrough block below + break; } - // fallthrough default: - { - switch (_rettype) - { - case xpath_type_number: - return convert_number_to_boolean(eval_number(c, stack)); - - case xpath_type_string: - { - xpath_allocator_capture cr(stack.result); - - return !eval_string(c, stack).empty(); - } - - case xpath_type_node_set: - { - xpath_allocator_capture cr(stack.result); - - return !eval_node_set(c, stack, nodeset_eval_any).empty(); - } - - default: - assert(false && "Wrong expression for return type boolean"); // unreachable - return false; - } + ; } + + // none of the ast types that return the value directly matched, we need to perform type conversion + switch (_rettype) + { + case xpath_type_number: + return convert_number_to_boolean(eval_number(c, stack)); + + case xpath_type_string: + { + xpath_allocator_capture cr(stack.result); + + return !eval_string(c, stack).empty(); + } + + case xpath_type_node_set: + { + xpath_allocator_capture cr(stack.result); + + return !eval_node_set(c, stack, nodeset_eval_any).empty(); + } + + default: + assert(false && "Wrong expression for return type boolean"); // unreachable + return false; } } @@ -10410,36 +10574,38 @@ PUGI__NS_BEGIN if (_rettype == xpath_type_number) return _data.variable->get_number(); + + // variable needs to be converted to the correct type, this is handled by the fallthrough block below + break; } - // fallthrough default: - { - switch (_rettype) - { - case xpath_type_boolean: - return eval_boolean(c, stack) ? 1 : 0; - - case xpath_type_string: - { - xpath_allocator_capture cr(stack.result); - - return convert_string_to_number(eval_string(c, stack).c_str()); - } - - case xpath_type_node_set: - { - xpath_allocator_capture cr(stack.result); - - return convert_string_to_number(eval_string(c, stack).c_str()); - } - - default: - assert(false && "Wrong expression for return type number"); // unreachable - return 0; - } - + ; } + + // none of the ast types that return the value directly matched, we need to perform type conversion + switch (_rettype) + { + case xpath_type_boolean: + return eval_boolean(c, stack) ? 1 : 0; + + case xpath_type_string: + { + xpath_allocator_capture cr(stack.result); + + return convert_string_to_number(eval_string(c, stack).c_str()); + } + + case xpath_type_node_set: + { + xpath_allocator_capture cr(stack.result); + + return convert_string_to_number(eval_string(c, stack).c_str()); + } + + default: + assert(false && "Wrong expression for return type number"); // unreachable + return 0; } } @@ -10596,7 +10762,7 @@ PUGI__NS_BEGIN double first = round_nearest(_right->eval_number(c, stack)); if (is_nan(first)) return xpath_string(); // NaN - else if (first >= s_length + 1) return xpath_string(); + else if (first >= static_cast(s_length + 1)) return xpath_string(); size_t pos = first < 1 ? 1 : static_cast(first); assert(1 <= pos && pos <= s_length + 1); @@ -10620,12 +10786,12 @@ PUGI__NS_BEGIN double last = first + round_nearest(_right->_next->eval_number(c, stack)); if (is_nan(first) || is_nan(last)) return xpath_string(); - else if (first >= s_length + 1) return xpath_string(); + else if (first >= static_cast(s_length + 1)) return xpath_string(); else if (first >= last) return xpath_string(); else if (last < 1) return xpath_string(); size_t pos = first < 1 ? 1 : static_cast(first); - size_t end = last >= s_length + 1 ? s_length + 1 : static_cast(last); + size_t end = last >= static_cast(s_length + 1) ? s_length + 1 : static_cast(last); assert(1 <= pos && pos <= end && end <= s_length + 1); const char_t* rbegin = s.c_str() + (pos - 1); @@ -10694,34 +10860,37 @@ PUGI__NS_BEGIN if (_rettype == xpath_type_string) return xpath_string::from_const(_data.variable->get_string()); + + // variable needs to be converted to the correct type, this is handled by the fallthrough block below + break; } - // fallthrough default: - { - switch (_rettype) - { - case xpath_type_boolean: - return xpath_string::from_const(eval_boolean(c, stack) ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false")); - - case xpath_type_number: - return convert_number_to_string(eval_number(c, stack), stack.result); - - case xpath_type_node_set: - { - xpath_allocator_capture cr(stack.temp); - - xpath_stack swapped_stack = {stack.temp, stack.result}; - - xpath_node_set_raw ns = eval_node_set(c, swapped_stack, nodeset_eval_first); - return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result); - } - - default: - assert(false && "Wrong expression for return type string"); // unreachable - return xpath_string(); - } + ; } + + // none of the ast types that return the value directly matched, we need to perform type conversion + switch (_rettype) + { + case xpath_type_boolean: + return xpath_string::from_const(eval_boolean(c, stack) ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false")); + + case xpath_type_number: + return convert_number_to_string(eval_number(c, stack), stack.result); + + case xpath_type_node_set: + { + xpath_allocator_capture cr(stack.temp); + + xpath_stack swapped_stack = {stack.temp, stack.result}; + + xpath_node_set_raw ns = eval_node_set(c, swapped_stack, nodeset_eval_first); + return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result); + } + + default: + assert(false && "Wrong expression for return type string"); // unreachable + return xpath_string(); } } @@ -10735,16 +10904,16 @@ PUGI__NS_BEGIN xpath_stack swapped_stack = {stack.temp, stack.result}; - xpath_node_set_raw ls = _left->eval_node_set(c, swapped_stack, eval); - xpath_node_set_raw rs = _right->eval_node_set(c, stack, eval); + xpath_node_set_raw ls = _left->eval_node_set(c, stack, eval); + xpath_node_set_raw rs = _right->eval_node_set(c, swapped_stack, eval); // we can optimize merging two sorted sets, but this is a very rare operation, so don't bother - rs.set_type(xpath_node_set::type_unsorted); + ls.set_type(xpath_node_set::type_unsorted); - rs.append(ls.begin(), ls.end(), stack.result); - rs.remove_duplicates(); + ls.append(rs.begin(), rs.end(), stack.result); + ls.remove_duplicates(stack.temp); - return rs; + return ls; } case ast_filter: @@ -10843,13 +11012,18 @@ PUGI__NS_BEGIN return ns; } + + // variable needs to be converted to the correct type, this is handled by the fallthrough block below + break; } - // fallthrough default: - assert(false && "Wrong expression for return type node set"); // unreachable - return xpath_node_set_raw(); + ; } + + // none of the ast types that return the value directly matched, but conversions to node set are invalid + assert(false && "Wrong expression for return type node set"); // unreachable + return xpath_node_set_raw(); } void optimize(xpath_allocator* alloc) @@ -10863,6 +11037,7 @@ PUGI__NS_BEGIN if (_next) _next->optimize(alloc); + // coverity[var_deref_model] optimize_self(alloc); } @@ -10871,13 +11046,14 @@ PUGI__NS_BEGIN // Rewrite [position()=expr] with [expr] // Note that this step has to go before classification to recognize [position()=1] if ((_type == ast_filter || _type == ast_predicate) && + _right && // workaround for clang static analyzer (_right is never null for ast_filter/ast_predicate) _right->_type == ast_op_equal && _right->_left->_type == ast_func_position && _right->_right->_rettype == xpath_type_number) { _right = _right->_right; } // Classify filter/predicate ops to perform various optimizations during evaluation - if (_type == ast_filter || _type == ast_predicate) + if ((_type == ast_filter || _type == ast_predicate) && _right) // workaround for clang static analyzer (_right is never null for ast_filter/ast_predicate) { assert(_test == predicate_default); @@ -10893,8 +11069,8 @@ PUGI__NS_BEGIN // The former is a full form of //foo, the latter is much faster since it executes the node test immediately // Do a similar kind of rewrite for self/descendant/descendant-or-self axes // Note that we only rewrite positionally invariant steps (//foo[1] != /descendant::foo[1]) - if (_type == ast_step && (_axis == axis_child || _axis == axis_self || _axis == axis_descendant || _axis == axis_descendant_or_self) && _left && - _left->_type == ast_step && _left->_axis == axis_descendant_or_self && _left->_test == nodetest_type_node && !_left->_right && + if (_type == ast_step && (_axis == axis_child || _axis == axis_self || _axis == axis_descendant || _axis == axis_descendant_or_self) && + _left && _left->_type == ast_step && _left->_axis == axis_descendant_or_self && _left->_test == nodetest_type_node && !_left->_right && is_posinv_step()) { if (_axis == axis_child || _axis == axis_descendant) @@ -10906,7 +11082,9 @@ PUGI__NS_BEGIN } // Use optimized lookup table implementation for translate() with constant arguments - if (_type == ast_func_translate && _right->_type == ast_string_constant && _right->_next->_type == ast_string_constant) + if (_type == ast_func_translate && + _right && // workaround for clang static analyzer (_right is never null for ast_func_translate) + _right->_type == ast_string_constant && _right->_next->_type == ast_string_constant) { unsigned char* table = translate_table_generate(alloc, _right->_data.string, _right->_next->_data.string); @@ -10919,6 +11097,8 @@ PUGI__NS_BEGIN // Use optimized path for @attr = 'value' or @attr = $value if (_type == ast_op_equal && + _left && _right && // workaround for clang static analyzer and Coverity (_left and _right are never null for ast_op_equal) + // coverity[mixed_enums] _left->_type == ast_step && _left->_axis == axis_attribute && _left->_test == nodetest_name && !_left->_left && !_left->_right && (_right->_type == ast_string_constant || (_right->_type == ast_variable && _right->_rettype == xpath_type_string))) { @@ -10978,6 +11158,14 @@ PUGI__NS_BEGIN } }; + static const size_t xpath_ast_depth_limit = + #ifdef PUGIXML_XPATH_DEPTH_LIMIT + PUGIXML_XPATH_DEPTH_LIMIT + #else + 1024 + #endif + ; + struct xpath_parser { xpath_allocator* _alloc; @@ -10990,6 +11178,8 @@ PUGI__NS_BEGIN char_t _scratch[32]; + size_t _depth; + xpath_ast_node* error(const char* message) { _result->error = message; @@ -11006,6 +11196,11 @@ PUGI__NS_BEGIN return 0; } + xpath_ast_node* error_rec() + { + return error("Exceeded maximum allowed query depth"); + } + void* alloc_node() { return _alloc->allocate(sizeof(xpath_ast_node)); @@ -11361,6 +11556,8 @@ PUGI__NS_BEGIN return error("Unrecognized function call"); _lexer.next(); + size_t old_depth = _depth; + while (_lexer.current() != lex_close_brace) { if (argc > 0) @@ -11370,6 +11567,9 @@ PUGI__NS_BEGIN _lexer.next(); } + if (++_depth > xpath_ast_depth_limit) + return error_rec(); + xpath_ast_node* n = parse_expression(); if (!n) return 0; @@ -11382,6 +11582,8 @@ PUGI__NS_BEGIN _lexer.next(); + _depth = old_depth; + return parse_function(function, argc, args); } @@ -11398,10 +11600,15 @@ PUGI__NS_BEGIN xpath_ast_node* n = parse_primary_expression(); if (!n) return 0; + size_t old_depth = _depth; + while (_lexer.current() == lex_open_square_brace) { _lexer.next(); + if (++_depth > xpath_ast_depth_limit) + return error_rec(); + if (n->rettype() != xpath_type_node_set) return error("Predicate has to be applied to node set"); @@ -11417,6 +11624,8 @@ PUGI__NS_BEGIN _lexer.next(); } + _depth = old_depth; + return n; } @@ -11568,12 +11777,17 @@ PUGI__NS_BEGIN xpath_ast_node* n = alloc_node(ast_step, set, axis, nt_type, nt_name_copy); if (!n) return 0; + size_t old_depth = _depth; + xpath_ast_node* last = 0; while (_lexer.current() == lex_open_square_brace) { _lexer.next(); + if (++_depth > xpath_ast_depth_limit) + return error_rec(); + xpath_ast_node* expr = parse_expression(); if (!expr) return 0; @@ -11590,6 +11804,8 @@ PUGI__NS_BEGIN last = pred; } + _depth = old_depth; + return n; } @@ -11599,11 +11815,16 @@ PUGI__NS_BEGIN xpath_ast_node* n = parse_step(set); if (!n) return 0; + size_t old_depth = _depth; + while (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) { lexeme_t l = _lexer.current(); _lexer.next(); + if (++_depth > xpath_ast_depth_limit) + return error_rec(); + if (l == lex_double_slash) { n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); @@ -11614,6 +11835,8 @@ PUGI__NS_BEGIN if (!n) return 0; } + _depth = old_depth; + return n; } @@ -11799,6 +12022,9 @@ PUGI__NS_BEGIN { _lexer.next(); + if (++_depth > xpath_ast_depth_limit) + return error_rec(); + xpath_ast_node* rhs = parse_path_or_unary_expression(); if (!rhs) return 0; @@ -11844,13 +12070,22 @@ PUGI__NS_BEGIN // | MultiplicativeExpr 'mod' UnaryExpr xpath_ast_node* parse_expression(int limit = 0) { + size_t old_depth = _depth; + + if (++_depth > xpath_ast_depth_limit) + return error_rec(); + xpath_ast_node* n = parse_path_or_unary_expression(); if (!n) return 0; - return parse_expression_rec(n, limit); + n = parse_expression_rec(n, limit); + + _depth = old_depth; + + return n; } - xpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result) + xpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result), _depth(0) { } @@ -11859,6 +12094,8 @@ PUGI__NS_BEGIN xpath_ast_node* n = parse_expression(); if (!n) return 0; + assert(_depth == 0); + // check if there are unparsed tokens left if (_lexer.current() != lex_eof) return error("Incorrect query"); @@ -12013,74 +12250,61 @@ namespace pugi size_t size_ = static_cast(end_ - begin_); - if (size_ <= 1) + // use internal buffer for 0 or 1 elements, heap buffer otherwise + xpath_node* storage = (size_ <= 1) ? _storage : static_cast(impl::xml_memory::allocate(size_ * sizeof(xpath_node))); + + if (!storage) { - // deallocate old buffer - if (_begin != &_storage) impl::xml_memory::deallocate(_begin); - - // use internal buffer - if (begin_ != end_) _storage = *begin_; - - _begin = &_storage; - _end = &_storage + size_; - _type = type_; + #ifdef PUGIXML_NO_EXCEPTIONS + return; + #else + throw std::bad_alloc(); + #endif } - else - { - // make heap copy - xpath_node* storage = static_cast(impl::xml_memory::allocate(size_ * sizeof(xpath_node))); - if (!storage) - { - #ifdef PUGIXML_NO_EXCEPTIONS - return; - #else - throw std::bad_alloc(); - #endif - } + // deallocate old buffer + if (_begin != _storage) + impl::xml_memory::deallocate(_begin); + // size check is necessary because for begin_ = end_ = nullptr, memcpy is UB + if (size_) memcpy(storage, begin_, size_ * sizeof(xpath_node)); - // deallocate old buffer - if (_begin != &_storage) impl::xml_memory::deallocate(_begin); - - // finalize - _begin = storage; - _end = storage + size_; - _type = type_; - } + _begin = storage; + _end = storage + size_; + _type = type_; } #ifdef PUGIXML_HAS_MOVE PUGI__FN void xpath_node_set::_move(xpath_node_set& rhs) PUGIXML_NOEXCEPT { _type = rhs._type; - _storage = rhs._storage; - _begin = (rhs._begin == &rhs._storage) ? &_storage : rhs._begin; + _storage[0] = rhs._storage[0]; + _begin = (rhs._begin == rhs._storage) ? _storage : rhs._begin; _end = _begin + (rhs._end - rhs._begin); rhs._type = type_unsorted; - rhs._begin = &rhs._storage; - rhs._end = rhs._begin; + rhs._begin = rhs._storage; + rhs._end = rhs._storage; } #endif - PUGI__FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(&_storage), _end(&_storage) + PUGI__FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(_storage), _end(_storage) { } - PUGI__FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_unsorted), _begin(&_storage), _end(&_storage) + PUGI__FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_unsorted), _begin(_storage), _end(_storage) { _assign(begin_, end_, type_); } PUGI__FN xpath_node_set::~xpath_node_set() { - if (_begin != &_storage) + if (_begin != _storage) impl::xml_memory::deallocate(_begin); } - PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(type_unsorted), _begin(&_storage), _end(&_storage) + PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(type_unsorted), _begin(_storage), _end(_storage) { _assign(ns._begin, ns._end, ns._type); } @@ -12095,7 +12319,7 @@ namespace pugi } #ifdef PUGIXML_HAS_MOVE - PUGI__FN xpath_node_set::xpath_node_set(xpath_node_set&& rhs) PUGIXML_NOEXCEPT: _type(type_unsorted), _begin(&_storage), _end(&_storage) + PUGI__FN xpath_node_set::xpath_node_set(xpath_node_set&& rhs) PUGIXML_NOEXCEPT: _type(type_unsorted), _begin(_storage), _end(_storage) { _move(rhs); } @@ -12104,7 +12328,7 @@ namespace pugi { if (this == &rhs) return *this; - if (_begin != &_storage) + if (_begin != _storage) impl::xml_memory::deallocate(_begin); _move(rhs); @@ -12771,7 +12995,7 @@ namespace pugi #endif /** - * Copyright (c) 2006-2018 Arseny Kapoulkine + * Copyright (c) 2006-2020 Arseny Kapoulkine * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation diff --git a/contrib/pugixml/src/pugixml.hpp b/contrib/pugixml/src/pugixml.hpp index 1775600f1..7e2ce7776 100644 --- a/contrib/pugixml/src/pugixml.hpp +++ b/contrib/pugixml/src/pugixml.hpp @@ -1,8 +1,8 @@ /** - * pugixml parser - version 1.9 + * pugixml parser - version 1.11 * -------------------------------------------------------- - * Copyright (C) 2006-2018, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) - * Report bugs and download new versions at http://pugixml.org/ + * Copyright (C) 2006-2020, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + * Report bugs and download new versions at https://pugixml.org/ * * This library is distributed under the MIT License. See notice at the end * of this file. @@ -12,8 +12,9 @@ */ #ifndef PUGIXML_VERSION -// Define version macro; evaluates to major * 100 + minor so that it's safe to use in less-than comparisons -# define PUGIXML_VERSION 190 +// Define version macro; evaluates to major * 1000 + minor * 10 + patch so that it's safe to use in less-than comparisons +// Note: pugixml used major * 100 + minor * 10 + patch format up until 1.9 (which had version identifier 190); starting from pugixml 1.10, the minor version number is two digits +# define PUGIXML_VERSION 1110 #endif // Include user configuration file (this can define various configuration macros) @@ -110,6 +111,15 @@ # endif #endif +// If C++ is 2011 or higher, use 'nullptr' +#ifndef PUGIXML_NULL +# if __cplusplus >= 201103 +# define PUGIXML_NULL nullptr +# else +# define PUGIXML_NULL 0 +# endif +#endif + // Character interface macros #ifdef PUGIXML_WCHAR_MODE # define PUGIXML_TEXT(t) L ## t @@ -252,10 +262,19 @@ namespace pugi // Don't output empty element tags, instead writing an explicit start and end tag even if there are no children. This flag is off by default. const unsigned int format_no_empty_element_tags = 0x80; + // Skip characters belonging to range [0; 32) instead of "&#xNN;" encoding. This flag is off by default. + const unsigned int format_skip_control_chars = 0x100; + + // Use single quotes ' instead of double quotes " for enclosing attribute values. This flag is off by default. + const unsigned int format_attribute_single_quote = 0x200; + // The default set of formatting flags. // Nodes are indented depending on their depth in DOM tree, a default declaration is output if document has none. const unsigned int format_default = format_indent; + const int default_double_precision = 17; + const int default_float_precision = 9; + // Forward declarations struct xml_attribute_struct; struct xml_node_struct; @@ -403,7 +422,9 @@ namespace pugi bool set_value(long rhs); bool set_value(unsigned long rhs); bool set_value(double rhs); + bool set_value(double rhs, int precision); bool set_value(float rhs); + bool set_value(float rhs, int precision); bool set_value(bool rhs); #ifdef PUGIXML_HAS_LONG_LONG @@ -569,10 +590,16 @@ namespace pugi bool remove_attribute(const xml_attribute& a); bool remove_attribute(const char_t* name); + // Remove all attributes + bool remove_attributes(); + // Remove specified child bool remove_child(const xml_node& n); bool remove_child(const char_t* name); + // Remove all children + bool remove_children(); + // Parses buffer as an XML document fragment and appends all nodes as children of the current node. // Copies/converts the buffer, so it may be deleted or changed after the function returns. // Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes does not immediately reclaim that memory. @@ -643,15 +670,15 @@ namespace pugi #ifndef PUGIXML_NO_XPATH // Select single node by evaluating XPath query. Returns first node from the resulting node set. - xpath_node select_node(const char_t* query, xpath_variable_set* variables = 0) const; + xpath_node select_node(const char_t* query, xpath_variable_set* variables = PUGIXML_NULL) const; xpath_node select_node(const xpath_query& query) const; // Select node set by evaluating XPath query - xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const; + xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = PUGIXML_NULL) const; xpath_node_set select_nodes(const xpath_query& query) const; // (deprecated: use select_node instead) Select single node by evaluating XPath query. - PUGIXML_DEPRECATED xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const; + PUGIXML_DEPRECATED xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = PUGIXML_NULL) const; PUGIXML_DEPRECATED xpath_node select_single_node(const xpath_query& query) const; #endif @@ -754,7 +781,9 @@ namespace pugi bool set(long rhs); bool set(unsigned long rhs); bool set(double rhs); + bool set(double rhs, int precision); bool set(float rhs); + bool set(float rhs, int precision); bool set(bool rhs); #ifdef PUGIXML_HAS_LONG_LONG @@ -1192,7 +1221,7 @@ namespace pugi public: // Construct a compiled object from XPath expression. // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors. - explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0); + explicit xpath_query(const char_t* query, xpath_variable_set* variables = PUGIXML_NULL); // Constructor xpath_query(); @@ -1251,11 +1280,12 @@ namespace pugi }; #ifndef PUGIXML_NO_EXCEPTIONS - -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning( disable: 4275 ) -#endif + #if defined(_MSC_VER) + // C4275 can be ignored in Visual C++ if you are deriving + // from a type in the Standard C++ Library + #pragma warning(push) + #pragma warning(disable: 4275) + #endif // XPath exception class class PUGIXML_CLASS xpath_exception: public std::exception { @@ -1272,10 +1302,11 @@ namespace pugi // Get parse result const xpath_parse_result& result() const; }; + #if defined(_MSC_VER) + #pragma warning(pop) + #endif #endif -#ifdef _MSC_VER -# pragma warning(pop) -#endif + // XPath node class (either xml_node or xml_attribute) class PUGIXML_CLASS xpath_node { @@ -1379,7 +1410,7 @@ namespace pugi private: type_t _type; - xpath_node _storage; + xpath_node _storage[1]; xpath_node* _begin; xpath_node* _end; @@ -1443,7 +1474,7 @@ namespace std #endif /** - * Copyright (c) 2006-2018 Arseny Kapoulkine + * Copyright (c) 2006-2020 Arseny Kapoulkine * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation From bf0f8d4c1b2f53a457b4e1834abf7f841ef30300 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 3 Jun 2021 23:29:53 +0200 Subject: [PATCH 146/335] Update pugiconfig.hpp --- contrib/pugixml/src/pugiconfig.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/pugixml/src/pugiconfig.hpp b/contrib/pugixml/src/pugiconfig.hpp index 405a66b65..03f854bf1 100644 --- a/contrib/pugixml/src/pugiconfig.hpp +++ b/contrib/pugixml/src/pugiconfig.hpp @@ -44,7 +44,7 @@ // #define PUGIXML_XPATH_DEPTH_LIMIT 1024 // Uncomment this to switch to header-only version -// #define PUGIXML_HEADER_ONLY +#define PUGIXML_HEADER_ONLY // Uncomment this to enable long long support // #define PUGIXML_HAS_LONG_LONG From a716f741d83b818e63ba38e76368957c6a0e2185 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 3 Jun 2021 23:35:12 +0200 Subject: [PATCH 147/335] fix the include --- code/Common/Assimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/Common/Assimp.cpp b/code/Common/Assimp.cpp index 6074b84bc..a6c539bca 100644 --- a/code/Common/Assimp.cpp +++ b/code/Common/Assimp.cpp @@ -1277,7 +1277,7 @@ ASSIMP_API void aiQuaternionInterpolate( # endif # define STB_IMAGE_IMPLEMENTATION -# include "../contrib/stb_image/stb_image.h" +# include "stb_image/stb_image.h" # if _MSC_VER # pragma warning(pop) From 742250c5fb595364ec6d532a61c43030417c9907 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 3 Jun 2021 23:42:07 +0200 Subject: [PATCH 148/335] Update CMakeLists.txt --- code/CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index d3cb6e923..e17657b53 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -1029,14 +1029,14 @@ ELSE () ENDIF () # RapidJSON +INCLUDE_DIRECTORIES( "../contrib" ) IF(ASSIMP_HUNTER_ENABLED) hunter_add_package(RapidJSON) find_package(RapidJSON CONFIG REQUIRED) ELSE() - INCLUDE_DIRECTORIES( "../contrib/rapidjson/include" ) - INCLUDE_DIRECTORIES( "../contrib" ) - INCLUDE_DIRECTORIES( "../contrib/pugixml/src" ) - ADD_DEFINITIONS( -DRAPIDJSON_HAS_STDSTRING=1 ) + INCLUDE_DIRECTORIES("../contrib/rapidjson/include") + INCLUDE_DIRECTORIES("../contrib/pugixml/src") + ADD_DEFINITIONS( -DRAPIDJSON_HAS_STDSTRING=1) option( ASSIMP_RAPIDJSON_NO_MEMBER_ITERATOR "Suppress rapidjson warning on MSVC (NOTE: breaks android build)" ON ) if(ASSIMP_RAPIDJSON_NO_MEMBER_ITERATOR) ADD_DEFINITIONS( -DRAPIDJSON_NOMEMBERITERATORCLASS ) From e6b83feb9fb43a3bc0779ad64bc54f13cc127873 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 3 Jun 2021 23:45:48 +0200 Subject: [PATCH 149/335] Update CMakeLists.txt --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b9d6fc55..15ceb2177 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -227,6 +227,7 @@ INCLUDE_DIRECTORIES( BEFORE include ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/include + contrib/ ) LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake-modules" ) From 84db4d3a08510e8709e3c23ff02ee0d48aef3668 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 3 Jun 2021 23:56:52 +0200 Subject: [PATCH 150/335] Update CMakeLists.txt --- code/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index e17657b53..fe4abc06f 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -1029,12 +1029,12 @@ ELSE () ENDIF () # RapidJSON -INCLUDE_DIRECTORIES( "../contrib" ) IF(ASSIMP_HUNTER_ENABLED) hunter_add_package(RapidJSON) find_package(RapidJSON CONFIG REQUIRED) ELSE() INCLUDE_DIRECTORIES("../contrib/rapidjson/include") + INCLUDE_DIRECTORIES( "../contrib" ) INCLUDE_DIRECTORIES("../contrib/pugixml/src") ADD_DEFINITIONS( -DRAPIDJSON_HAS_STDSTRING=1) option( ASSIMP_RAPIDJSON_NO_MEMBER_ITERATOR "Suppress rapidjson warning on MSVC (NOTE: breaks android build)" ON ) From 444fc9c373ae3fb407bc4bfe3a6c5558175e3fba Mon Sep 17 00:00:00 2001 From: Scott Baldric Date: Tue, 1 Jun 2021 17:08:26 -0500 Subject: [PATCH 151/335] Increasing length of mDataLength if rewriting the texture index increases magnitutde of index. --- code/Common/SceneCombiner.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/code/Common/SceneCombiner.cpp b/code/Common/SceneCombiner.cpp index 555d46b6a..8f10d6308 100644 --- a/code/Common/SceneCombiner.cpp +++ b/code/Common/SceneCombiner.cpp @@ -406,11 +406,25 @@ void SceneCombiner::MergeScenes(aiScene **_dest, aiScene *master, std::vector, // where n is the index of the texture. - aiString &s = *((aiString *)prop->mData); + // Copy here because we overwrite the string data in-place and the buffer inside of aiString + // will be a lie if we just reinterpret from prop->mData. The size of mData is not guaranteed to be + // MAXLEN in size. + aiString s(*(aiString *)prop->mData); if ('*' == s.data[0]) { // Offset the index and write it back .. const unsigned int idx = strtoul10(&s.data[1]) + offset[n]; - ASSIMP_itoa10(&s.data[1], sizeof(s.data) - 1, idx); + const unsigned int oldLen = s.length; + + s.length = 1 + ASSIMP_itoa10(&s.data[1], sizeof(s.data) - 1, idx); + + // The string changed in size so we need to reallocate the buffer for the property. + if (oldLen < s.length) { + prop->mDataLength += s.length - oldLen; + delete[] prop->mData; + prop->mData = new char[prop->mDataLength]; + } + + memcpy(prop->mData, static_cast(&s), prop->mDataLength); } } From 121c0e7d0c9db2db6c80897b46dfc4743f64d2b7 Mon Sep 17 00:00:00 2001 From: Hill Ma Date: Mon, 7 Jun 2021 21:53:28 -0700 Subject: [PATCH 152/335] Add GetEmbeddedTextureAndIndex() to aiScene. It allows the caller to get the index of the embedded texture that is always computed anyway. --- include/assimp/scene.h | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/include/assimp/scene.h b/include/assimp/scene.h index 769499a27..fb3b2ef6e 100644 --- a/include/assimp/scene.h +++ b/include/assimp/scene.h @@ -397,22 +397,27 @@ struct aiScene //! Returns an embedded texture const aiTexture* GetEmbeddedTexture(const char* filename) const { + return GetEmbeddedTextureAndIndex(filename).first; + } + + //! Returns an embedded texture and its index + std::pair GetEmbeddedTextureAndIndex(const char* filename) const { // lookup using texture ID (if referenced like: "*1", "*2", etc.) if ('*' == *filename) { int index = std::atoi(filename + 1); if (0 > index || mNumTextures <= static_cast(index)) - return nullptr; - return mTextures[index]; + return std::make_pair(nullptr, -1); + return std::make_pair(mTextures[index], index); } // lookup using filename const char* shortFilename = GetShortFilename(filename); for (unsigned int i = 0; i < mNumTextures; i++) { const char* shortTextureFilename = GetShortFilename(mTextures[i]->mFilename.C_Str()); if (strcmp(shortTextureFilename, shortFilename) == 0) { - return mTextures[i]; + return std::make_pair(mTextures[i], i); } } - return nullptr; + return std::make_pair(nullptr, -1); } #endif // __cplusplus From ef739c170312a1667e1b8fd2e54a2d1614631a56 Mon Sep 17 00:00:00 2001 From: Hill Ma Date: Tue, 8 Jun 2021 12:53:18 -0700 Subject: [PATCH 153/335] glTF2: Make handling of embedded textures safer. Previous code does not check whether the embedded texture exists. --- code/AssetLib/glTF2/glTF2Exporter.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 751508225..83356f7c2 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -118,14 +118,14 @@ glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const ai ExportScene(); ExportAnimations(); - + // export extras if(mProperties->HasPropertyCallback("extras")) { std::function ExportExtras = mProperties->GetPropertyCallback("extras"); mAsset->extras = (rapidjson::Value*)ExportExtras(0); } - + AssetWriter writer(*mAsset); if (isBinary) { @@ -515,11 +515,10 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe std::string imgId = mAsset->FindUniqueID("", "image"); texture->source = mAsset->images.Create(imgId); - if (path[0] == '*') { // embedded - aiTexture* curTex = mScene->mTextures[atoi(&path[1])]; - + const aiTexture* curTex = mScene->GetEmbeddedTexture(path.c_str()); + if (curTex != nullptr) { // embedded texture->source->name = curTex->mFilename.C_Str(); - + //basisu: embedded ktx2, bu if (curTex->achFormatHint[0]) { std::string mimeType = "image/"; @@ -541,7 +540,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe mimeType += curTex->achFormatHint; texture->source->mimeType = mimeType; } - + // The asset has its own buffer, see Image::SetData //basisu: "image/ktx2", "image/basis" as is texture->source->SetData(reinterpret_cast(curTex->pcData), curTex->mWidth, *mAsset); @@ -554,7 +553,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe useBasisUniversal = true; } } - + //basisu if(useBasisUniversal) { mAsset->extensionsUsed.KHR_texture_basisu = true; From 9f9d77f882efc445f46139ed2188ce21fc857032 Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Thu, 10 Jun 2021 18:13:46 +0100 Subject: [PATCH 154/335] First pass at simplifying glTFv2 PBR Removed 'core' set of GLTF-specific properties --- code/AssetLib/glTF2/glTF2Exporter.cpp | 60 ++++++++++++++------------- code/AssetLib/glTF2/glTF2Importer.cpp | 21 ++++++---- include/assimp/material.h | 40 +++++++++++++++++- include/assimp/pbrmaterial.h | 14 +++---- test/unit/utglTF2ImportExport.cpp | 55 ++++++++++++++++++++---- 5 files changed, 138 insertions(+), 52 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 751508225..dfa62372a 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -645,7 +645,7 @@ void glTF2Exporter::ExportMaterials() m->name = name; - GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE); + GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_BASE_COLOR); if (!m->pbrMetallicRoughness.baseColorTexture.texture) { //if there wasn't a baseColorTexture defined in the source, fallback to any diffuse texture @@ -654,19 +654,19 @@ void glTF2Exporter::ExportMaterials() GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE); - if (GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR) != AI_SUCCESS) { + if (GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_BASE_COLOR) != AI_SUCCESS) { // if baseColorFactor wasn't defined, then the source is likely not a metallic roughness material. //a fallback to any diffuse color should be used instead GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE); } - if (mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) { + if (mat->Get(AI_MATKEY_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) { //if metallicFactor wasn't defined, then the source is likely not a PBR file, and the metallicFactor should be 0 m->pbrMetallicRoughness.metallicFactor = 0; } // get roughness if source is gltf2 file - if (mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor) != AI_SUCCESS) { + if (mat->Get(AI_MATKEY_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor) != AI_SUCCESS) { // otherwise, try to derive and convert from specular + shininess values aiColor4D specularColor; ai_real shininess; @@ -712,36 +712,38 @@ void glTF2Exporter::ExportMaterials() } } - bool hasPbrSpecularGlossiness = false; - mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS, hasPbrSpecularGlossiness); - - if (hasPbrSpecularGlossiness) { - - if (!mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness) { - mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true; - } - + { + // If has a Specular color, use the KHR_materials_pbrSpecularGlossiness extension PbrSpecularGlossiness pbrSG; - - GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE); - GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR); - - if (mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) != AI_SUCCESS) { - float shininess; - - if (mat->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) { - pbrSG.glossinessFactor = shininess / 1000; + if (GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR) == AI_SUCCESS) { + if (!mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness) { + mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true; } + + GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE); + + // If don't have explicit glossiness then convert from roughness or shininess + if (mat->Get(AI_MATKEY_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) != AI_SUCCESS) { + float shininess; + if (mat->Get(AI_MATKEY_ROUGHNESS_FACTOR, shininess) == AI_SUCCESS) { + pbrSG.glossinessFactor = 1.0f - shininess; // Extension defines this way + } else if (mat->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) { + pbrSG.glossinessFactor = shininess / 1000; + } + } + + // Add any appropriate textures + GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE); + GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); + + m->pbrSpecularGlossiness = Nullable(pbrSG); } - - GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE); - GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); - - m->pbrSpecularGlossiness = Nullable(pbrSG); } - bool unlit; - if (mat->Get(AI_MATKEY_GLTF_UNLIT, unlit) == AI_SUCCESS && unlit) { + // glTFv2 is either PBR or Unlit + aiShadingMode shadingMode = aiShadingMode_PBR_BRDF; + mat->Get(AI_MATKEY_SHADING_MODEL, shadingMode); + if (shadingMode == aiShadingMode_Unlit) { mAsset->extensionsUsed.KHR_materials_unlit = true; m->unlit = true; } diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index c62989c3b..b5ba65857 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -238,16 +238,18 @@ static aiMaterial *ImportMaterial(std::vector &embeddedTexIdxs, Asset &r, M aimat->AddProperty(&str, AI_MATKEY_NAME); } + // Set Assimp DIFFUSE and BASE COLOR to the pbrMetallicRoughness base color and texture for backwards compatibility + // Technically should not load any pbrMetallicRoughness if extensionsRequired contains KHR_materials_pbrSpecularGlossiness SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); - SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR); + SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_BASE_COLOR); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_DIFFUSE); - SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_BASE_COLOR); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.metallicRoughnessTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE); - aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR); - aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR); + aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, AI_MATKEY_METALLIC_FACTOR); + aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, AI_MATKEY_ROUGHNESS_FACTOR); float roughnessAsShininess = 1 - mat.pbrMetallicRoughness.roughnessFactor; roughnessAsShininess *= roughnessAsShininess * 1000; @@ -268,22 +270,27 @@ static aiMaterial *ImportMaterial(std::vector &embeddedTexIdxs, Asset &r, M if (mat.pbrSpecularGlossiness.isPresent) { PbrSpecularGlossiness &pbrSG = mat.pbrSpecularGlossiness.value; - aimat->AddProperty(&mat.pbrSpecularGlossiness.isPresent, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS); SetMaterialColorProperty(r, pbrSG.diffuseFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); SetMaterialColorProperty(r, pbrSG.specularFactor, aimat, AI_MATKEY_COLOR_SPECULAR); float glossinessAsShininess = pbrSG.glossinessFactor * 1000.0f; aimat->AddProperty(&glossinessAsShininess, 1, AI_MATKEY_SHININESS); - aimat->AddProperty(&pbrSG.glossinessFactor, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR); + aimat->AddProperty(&pbrSG.glossinessFactor, 1, AI_MATKEY_GLOSSINESS_FACTOR); SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.diffuseTexture, aimat, aiTextureType_DIFFUSE); SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.specularGlossinessTexture, aimat, aiTextureType_SPECULAR); } + + // glTFv2 is either PBR or Unlit + aiShadingMode shadingMode = aiShadingMode_PBR_BRDF; if (mat.unlit) { - aimat->AddProperty(&mat.unlit, 1, AI_MATKEY_GLTF_UNLIT); + shadingMode = aiShadingMode_Unlit; } + aimat->AddProperty(&shadingMode, 1, AI_MATKEY_SHADING_MODEL); + + //KHR_materials_sheen if (mat.materialSheen.isPresent) { MaterialSheen &sheen = mat.materialSheen.value; diff --git a/include/assimp/material.h b/include/assimp/material.h index 08c0491c0..11cdef1f4 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -202,11 +202,15 @@ enum aiTextureType { /** The texture is combined with the result of the diffuse * lighting equation. + * OR + * PBR Specular/Glossiness */ aiTextureType_DIFFUSE = 1, /** The texture is combined with the result of the specular * lighting equation. + * OR + * PBR Specular/Glossiness */ aiTextureType_SPECULAR = 2, @@ -309,7 +313,9 @@ ASSIMP_API const char *TextureTypeToString(enum aiTextureType in); // --------------------------------------------------------------------------- /** @brief Defines all shading models supported by the library - * + * + * #AI_MATKEY_SHADING_MODEL + * * The list of shading modes has been taken from Blender. * See Blender documentation for more information. The API does * not distinguish between "specular" and "diffuse" shaders (thus the @@ -364,13 +370,27 @@ enum aiShadingMode { aiShadingMode_CookTorrance = 0x8, /** No shading at all. Constant light influence of 1.0. + * Also known as "Unlit" */ aiShadingMode_NoShading = 0x9, + aiShadingMode_Unlit = aiShadingMode_NoShading, // Alias /** Fresnel shading */ aiShadingMode_Fresnel = 0xa, + /** Physically-Based Rendering (PBR) shading using + * Bidirectional scattering/reflectance distribution function (BSDF/BRDF) + * There are multiple methods under this banner, and model files may provide + * data for more than one PBR-BRDF method. + * Applications should use the set of provided properties to determine which + * of their preferred PBDR methods are available + * eg: + * - If AI_MATKEY_METALLIC_FACTOR is set, then a Metallic/Roughness is available + * - If AI_MATKEY_COLOR_SPECULAR is set, then a Specular/Glossiness is available + */ + aiShadingMode_PBR_BRDF = 0xb, + #ifndef SWIG _aiShadingMode_Force32Bit = INT_MAX #endif @@ -923,11 +943,29 @@ extern "C" { // --------------------------------------------------------------------------- // PBR material support #define AI_MATKEY_USE_COLOR_MAP "$mat.useColorMap", 0, 0 + +// Metallic/Roughness Workflow +// --------------------------- +// Base color factor. Will be multiplied by final base color texture values if extant #define AI_MATKEY_BASE_COLOR "$clr.base", 0, 0 #define AI_MATKEY_USE_METALLIC_MAP "$mat.useMetallicMap", 0, 0 +// Metallic factor. 0.0 = Full Dielectric, 1.0 = Full Metal #define AI_MATKEY_METALLIC_FACTOR "$mat.metallicFactor", 0, 0 #define AI_MATKEY_USE_ROUGHNESS_MAP "$mat.useRoughnessMap", 0, 0 +// Roughness factor. 0.0 = Perfectly Smooth, 1.0 = Completely Rough #define AI_MATKEY_ROUGHNESS_FACTOR "$mat.roughnessFactor", 0, 0 + +// Specular/Glossiness Workflow +// --------------------------- +// Diffuse/Albedo Color. Note: Pure Metals have a diffuse of {0,0,0} +// AI_MATKEY_COLOR_DIFFUSE +// Specular Color +// AI_MATKEY_COLOR_SPECULAR +// Glossiness factor. 0.0 = Completely Rough, 1.0 = Perfectly Smooth +#define AI_MATKEY_GLOSSINESS_FACTOR "$mat.glossinessFactor", 0, 0 + +// Emissive +// -------- #define AI_MATKEY_USE_EMISSIVE_MAP "$mat.useEmissiveMap", 0, 0 #define AI_MATKEY_EMISSIVE_INTENSITY "$mat.emissiveIntensity", 0, 0 #define AI_MATKEY_USE_AO_MAP "$mat.useAOMap", 0, 0 diff --git a/include/assimp/pbrmaterial.h b/include/assimp/pbrmaterial.h index 2e41b8b6d..fd904e4fc 100644 --- a/include/assimp/pbrmaterial.h +++ b/include/assimp/pbrmaterial.h @@ -50,16 +50,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # pragma GCC system_header #endif -#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR "$mat.gltf.pbrMetallicRoughness.baseColorFactor", 0, 0 -#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0 -#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0 -#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE aiTextureType_DIFFUSE, 1 +//#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR "$mat.gltf.pbrMetallicRoughness.baseColorFactor", 0, 0 +//#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0 +//#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0 +//#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE aiTextureType_DIFFUSE, 1 #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 0 #define AI_MATKEY_GLTF_ALPHAMODE "$mat.gltf.alphaMode", 0, 0 #define AI_MATKEY_GLTF_ALPHACUTOFF "$mat.gltf.alphaCutoff", 0, 0 -#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0 -#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0 -#define AI_MATKEY_GLTF_UNLIT "$mat.gltf.unlit", 0, 0 +//#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0 +//#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0 +//#define AI_MATKEY_GLTF_UNLIT "$mat.gltf.unlit", 0, 0 #define AI_MATKEY_GLTF_MATERIAL_SHEEN "$mat.gltf.materialSheen", 0, 0 #define AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR "$mat.gltf.materialSheen.sheenColorFactor", 0, 0 #define AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR "$mat.gltf.materialSheen.sheenRoughnessFactor", 0, 0 diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index 4110edcfc..e0ac10ad5 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -57,10 +57,9 @@ using namespace Assimp; class utglTF2ImportExport : public AbstractImportExportBase { public: - virtual bool importerTest() { + virtual bool importerMatTest(const char *file, bool spec_gloss, std::array exp_modes = { aiTextureMapMode_Wrap, aiTextureMapMode_Wrap }) { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", - aiProcess_ValidateDataStructure); + const aiScene *scene = importer.ReadFile(file, aiProcess_ValidateDataStructure); EXPECT_NE(scene, nullptr); if (!scene) { return false; @@ -72,13 +71,49 @@ public: } const aiMaterial *material = scene->mMaterials[0]; + // This Material should be a PBR + aiShadingMode shadingMode; + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_SHADING_MODEL, shadingMode)); + EXPECT_EQ(aiShadingMode_PBR_BRDF, shadingMode); + + // Should import the texture as diffuse and as base color aiString path; - aiTextureMapMode modes[2]; + std::array modes; EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_DIFFUSE, 0, &path, nullptr, nullptr, - nullptr, nullptr, modes)); + nullptr, nullptr, modes.data())); EXPECT_STREQ(path.C_Str(), "CesiumLogoFlat.png"); - EXPECT_EQ(modes[0], aiTextureMapMode_Mirror); - EXPECT_EQ(modes[1], aiTextureMapMode_Clamp); + EXPECT_EQ(exp_modes, modes); + + // Also as Base Color + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_BASE_COLOR, 0, &path, nullptr, nullptr, + nullptr, nullptr, modes.data())); + EXPECT_STREQ(path.C_Str(), "CesiumLogoFlat.png"); + EXPECT_EQ(exp_modes, modes); + + // Should have a MetallicFactor (default is 1.0) + ai_real metal_factor = ai_real(0.5); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_METALLIC_FACTOR, metal_factor)); + EXPECT_EQ(ai_real(0.0), metal_factor); + + // And a roughness factor (default is 1.0) + ai_real roughness_factor = ai_real(0.5); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_ROUGHNESS_FACTOR, roughness_factor)); + EXPECT_EQ(ai_real(1.0), roughness_factor); + + aiColor3D spec_color = { 0, 0, 0 }; + ai_real glossiness = ai_real(0.5); + if (spec_gloss) { + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_COLOR_SPECULAR, spec_color)); + constexpr ai_real spec_val(0.20000000298023225); // From the file + EXPECT_EQ(spec_val, spec_color.r); + EXPECT_EQ(spec_val, spec_color.g); + EXPECT_EQ(spec_val, spec_color.b); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossiness)); + EXPECT_EQ(ai_real(1.0), glossiness); + } else { + EXPECT_EQ(aiReturn_FAILURE, material->Get(AI_MATKEY_COLOR_SPECULAR, spec_color)); + EXPECT_EQ(aiReturn_FAILURE, material->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossiness)); + } return true; } @@ -105,13 +140,17 @@ public: }; TEST_F(utglTF2ImportExport, importglTF2FromFileTest) { - EXPECT_TRUE(importerTest()); + EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", false, {aiTextureMapMode_Mirror, aiTextureMapMode_Clamp})); } TEST_F(utglTF2ImportExport, importBinaryglTF2FromFileTest) { EXPECT_TRUE(binaryImporterTest()); } +TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_pbrSpecularGlossiness) { + EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf", true)); +} + #ifndef ASSIMP_BUILD_NO_EXPORT TEST_F(utglTF2ImportExport, importglTF2AndExportToOBJ) { Assimp::Importer importer; From ebb9b1b2af421698ed92eba7436e21b933736fba Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 10 Jun 2021 23:36:07 +0200 Subject: [PATCH 155/335] Next iteration --- code/AssetLib/X3D/X3DImporter.cpp | 2455 ++++++++++++++++++++++++++++- code/AssetLib/X3D/X3DImporter.hpp | 278 +++- 2 files changed, 2729 insertions(+), 4 deletions(-) diff --git a/code/AssetLib/X3D/X3DImporter.cpp b/code/AssetLib/X3D/X3DImporter.cpp index 0fcfec726..4c6f24795 100644 --- a/code/AssetLib/X3D/X3DImporter.cpp +++ b/code/AssetLib/X3D/X3DImporter.cpp @@ -288,9 +288,61 @@ void X3DImporter::InternReadFile( const std::string &pFile, aiScene *pScene, IOS if (!stream) { throw DeadlyImportError("Could not open file for reading"); } + std::string::size_type slashPos = pFile.find_last_of("\\/"); + pIOHandler->PushDirectory(slashPos == std::string::npos ? std::string() : pFile.substr(0, slashPos + 1)); + ParseFile(pFile, pIOHandler); + pIOHandler->PopDirectory(); + + // mScene = pScene; pScene->mRootNode = new aiNode(pFile); + pScene->mRootNode->mParent = nullptr; + pScene->mFlags |= AI_SCENE_FLAGS_ALLOW_SHARED; + + //search for root node element + + mNodeElementCur = NodeElement_List.front(); + while (mNodeElementCur->Parent != nullptr) { + mNodeElementCur = mNodeElementCur->Parent; + } + + { // fill aiScene with objects. + std::list mesh_list; + std::list mat_list; + std::list light_list; + + // create nodes tree + Postprocess_BuildNode(*mNodeElementCur, *pScene->mRootNode, mesh_list, mat_list, light_list); + // copy needed data to scene + if (!mesh_list.empty()) { + std::list::const_iterator it = mesh_list.begin(); + + pScene->mNumMeshes = static_cast(mesh_list.size()); + pScene->mMeshes = new aiMesh *[pScene->mNumMeshes]; + for (size_t i = 0; i < pScene->mNumMeshes; i++) + pScene->mMeshes[i] = *it++; + } + + if (!mat_list.empty()) { + std::list::const_iterator it = mat_list.begin(); + + pScene->mNumMaterials = static_cast(mat_list.size()); + pScene->mMaterials = new aiMaterial *[pScene->mNumMaterials]; + for (size_t i = 0; i < pScene->mNumMaterials; i++) + pScene->mMaterials[i] = *it++; + } + + if (!light_list.empty()) { + std::list::const_iterator it = light_list.begin(); + + pScene->mNumLights = static_cast(light_list.size()); + pScene->mLights = new aiLight *[pScene->mNumLights]; + for (size_t i = 0; i < pScene->mNumLights; i++) + pScene->mLights[i] = *it++; + } + } + } const aiImporterDesc *X3DImporter::GetInfo() const { @@ -421,6 +473,1741 @@ void readMetadataString(XmlNode &node, X3DNodeElementBase *parent) { } } +void X3DImporter::ParseDirectionalLight(XmlNode &node) { + std::string def, use; + float ambientIntensity = 0; + aiColor3D color(1, 1, 1); + aiVector3D direction(0, 0, -1); + bool global = false; + float intensity = 1; + bool on = true; + X3DNodeElementBase *ne = nullptr; + + //MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + XmlParser::getFloatAttribute(node, "ambientIntensity", ambientIntensity); + //MACRO_ATTRREAD_CHECK_RET("ambientIntensity", ambientIntensity, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsCol3f); + MACRO_ATTRREAD_CHECK_REF("direction", direction, XML_ReadNode_GetAttrVal_AsVec3f); + MACRO_ATTRREAD_CHECK_RET("global", global, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("intensity", intensity, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_RET("on", on, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_DirectionalLight, ne); + } else { + if (on) { + // create and if needed - define new geometry object. + ne = new X3DNodeNodeElementLight(CX3DImporter_NodeElement::ENET_DirectionalLight, NodeElement_Cur); + if (!def.empty()) + ne->ID = def; + else + ne->ID = "DirectionalLight_" + to_string((size_t)ne); // make random name + + ((X3DNodeNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; + ((X3DNodeNodeElementLight *)ne)->Color = color; + ((X3DNodeNodeElementLight *)ne)->Direction = direction; + ((X3DNodeNodeElementLight *)ne)->Global = global; + ((X3DNodeNodeElementLight *)ne)->Intensity = intensity; + // Assimp want a node with name similar to a light. "Why? I don't no." ) + ParseHelper_Group_Begin(false); + + mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. + ParseHelper_Node_Exit(); + // check for child nodes + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "DirectionalLight"); + else + mNodeElementCur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(on) + } // if(!use.empty()) else +} + +// +void X3DImporter::ParseNode_Lighting_PointLight() { + std::string def, use; + float ambientIntensity = 0; + aiVector3D attenuation(1, 0, 0); + aiColor3D color(1, 1, 1); + bool global = true; + float intensity = 1; + aiVector3D location(0, 0, 0); + bool on = true; + float radius = 100; + X3DNodeElementBase *ne = nullptr; + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_RET("ambientIntensity", ambientIntensity, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_REF("attenuation", attenuation, XML_ReadNode_GetAttrVal_AsVec3f); + MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsCol3f); + MACRO_ATTRREAD_CHECK_RET("global", global, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("intensity", intensity, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_REF("location", location, XML_ReadNode_GetAttrVal_AsVec3f); + MACRO_ATTRREAD_CHECK_RET("on", on, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_PointLight, ne); + } else { + if (on) { + // create and if needed - define new geometry object. + ne = new X3DNodeNodeElementLight(X3DElemType::ENET_PointLight, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; + ((X3DNodeNodeElementLight *)ne)->Attenuation = attenuation; + ((X3DNodeNodeElementLight *)ne)->Color = color; + ((X3DNodeNodeElementLight *)ne)->Global = global; + ((X3DNodeNodeElementLight *)ne)->Intensity = intensity; + ((X3DNodeNodeElementLight *)ne)->Location = location; + ((X3DNodeNodeElementLight *)ne)->Radius = radius; + // Assimp want a node with name similar to a light. "Why? I don't no." ) + ParseHelper_Group_Begin(false); + // make random name + if (ne->ID.empty()) ne->ID = "PointLight_" + to_string((size_t)ne); + + mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. + ParseHelper_Node_Exit(); + // check for child nodes + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "PointLight"); + else + mNodeElementCur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(on) + } // if(!use.empty()) else +} + +// +void X3DImporter::ParseNode_Lighting_SpotLight() { + std::string def, use; + float ambientIntensity = 0; + aiVector3D attenuation(1, 0, 0); + float beamWidth = 0.7854f; + aiColor3D color(1, 1, 1); + float cutOffAngle = 1.570796f; + aiVector3D direction(0, 0, -1); + bool global = true; + float intensity = 1; + aiVector3D location(0, 0, 0); + bool on = true; + float radius = 100; + X3DNodeElementBase *ne = nullptr; + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_RET("ambientIntensity", ambientIntensity, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_REF("attenuation", attenuation, XML_ReadNode_GetAttrVal_AsVec3f); + MACRO_ATTRREAD_CHECK_RET("beamWidth", beamWidth, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsCol3f); + MACRO_ATTRREAD_CHECK_RET("cutOffAngle", cutOffAngle, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_REF("direction", direction, XML_ReadNode_GetAttrVal_AsVec3f); + MACRO_ATTRREAD_CHECK_RET("global", global, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("intensity", intensity, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_REF("location", location, XML_ReadNode_GetAttrVal_AsVec3f); + MACRO_ATTRREAD_CHECK_RET("on", on, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_SpotLight, ne); + } else { + if (on) { + // create and if needed - define new geometry object. + ne = new X3DNodeNodeElementLight(X3DElemType::ENET_SpotLight, mNodeElementCur); + if (!def.empty()) + ne->ID = def; + + if (beamWidth > cutOffAngle) + beamWidth = cutOffAngle; + + ((X3DNodeNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; + ((X3DNodeNodeElementLight *)ne)->Attenuation = attenuation; + ((X3DNodeNodeElementLight *)ne)->BeamWidth = beamWidth; + ((X3DNodeNodeElementLight *)ne)->Color = color; + ((X3DNodeNodeElementLight *)ne)->CutOffAngle = cutOffAngle; + ((X3DNodeNodeElementLight *)ne)->Direction = direction; + ((X3DNodeNodeElementLight *)ne)->Global = global; + ((X3DNodeNodeElementLight *)ne)->Intensity = intensity; + ((X3DNodeNodeElementLight *)ne)->Location = location; + ((X3DNodeNodeElementLight *)ne)->Radius = radius; + + // Assimp want a node with name similar to a light. "Why? I don't no." ) + ParseHelper_Group_Begin(false); + // make random name + if (ne->ID.empty()) ne->ID = "SpotLight_" + to_string((size_t)ne); + + mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. + ParseHelper_Node_Exit(); + // check for child nodes + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "SpotLight"); + else + mNodeElementCur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(on) + } // if(!use.empty()) else +} + +void X3DImporter::ParseNode_Grouping_Group() { + std::string def, use; + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + X3DNodeElementBase *ne = nullptr; + + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) mNodeElementCur->ID = def; + // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. + + // for empty element exit from node in that place + if (mReader->isEmptyElement()) ParseHelper_Node_Exit(); + } // if(!use.empty()) else +} + +void X3DImporter::ParseNode_Grouping_GroupEnd() { + ParseHelper_Node_Exit(); // go up in scene graph +} + +// +// +// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, +// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the +// precise palette of legal nodes that are available depends on assigned profile and components. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +// The StaticGroup node contains children nodes which cannot be modified. StaticGroup children are guaranteed to not change, send events, receive events or +// contain any USE references outside the StaticGroup. +void X3DImporter::ParseNode_Grouping_StaticGroup() { + std::string def, use; + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + X3DNodeElementBase *ne = nullptr; + + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(true); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) mNodeElementCur->ID = def; + // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. + + // for empty element exit from node in that place + if (mReader->isEmptyElement()) ParseHelper_Node_Exit(); + } // if(!use.empty()) else +} + +void X3DImporter::ParseNode_Grouping_StaticGroupEnd() { + ParseHelper_Node_Exit(); // go up in scene graph +} + +// +// +// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, +// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the +// precise palette of legal nodes that are available depends on assigned profile and components. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +// The Switch grouping node traverses zero or one of the nodes specified in the children field. The whichChoice field specifies the index of the child +// to traverse, with the first child having index 0. If whichChoice is less than zero or greater than the number of nodes in the children field, nothing +// is chosen. +void X3DImporter::ParseNode_Grouping_Switch() { + std::string def, use; + int32_t whichChoice = -1; + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_RET("whichChoice", whichChoice, XML_ReadNode_GetAttrVal_AsI32); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + CX3DImporter_NodeElement *ne; + + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) NodeElement_Cur->ID = def; + + // also set values specific to this type of group + ((CX3DNodeElementGroup *)NodeElement_Cur)->UseChoice = true; + ((CX3DNodeElementGroup *)NodeElement_Cur)->Choice = whichChoice; + // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. + + // for empty element exit from node in that place + if (mReader->isEmptyElement()) ParseHelper_Node_Exit(); + } // if(!use.empty()) else +} + +void X3DImporter::ParseNode_Grouping_SwitchEnd() { + // just exit from node. Defined choice will be accepted at postprocessing stage. + ParseHelper_Node_Exit(); // go up in scene graph +} + +// +// +// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, +// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the +// precise palette of legal nodes that are available depends on assigned profile and components. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +// The Transform node is a grouping node that defines a coordinate system for its children that is relative to the coordinate systems of its ancestors. +// Given a 3-dimensional point P and Transform node, P is transformed into point P' in its parent's coordinate system by a series of intermediate +// transformations. In matrix transformation notation, where C (center), SR (scaleOrientation), T (translation), R (rotation), and S (scale) are the +// equivalent transformation matrices, +// P' = T * C * R * SR * S * -SR * -C * P +void X3DImporter::ParseNode_Grouping_Transform() { + aiVector3D center(0, 0, 0); + float rotation[4] = { 0, 0, 1, 0 }; + aiVector3D scale(1, 1, 1); // A value of zero indicates that any child geometry shall not be displayed + float scale_orientation[4] = { 0, 0, 1, 0 }; + aiVector3D translation(0, 0, 0); + aiMatrix4x4 matr, tmatr; + std::string use, def; + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_REF("center", center, XML_ReadNode_GetAttrVal_AsVec3f); + MACRO_ATTRREAD_CHECK_REF("scale", scale, XML_ReadNode_GetAttrVal_AsVec3f); + MACRO_ATTRREAD_CHECK_REF("translation", translation, XML_ReadNode_GetAttrVal_AsVec3f); + if (an == "rotation") { + std::vector tvec; + + XML_ReadNode_GetAttrVal_AsArrF(idx, tvec); + if (tvec.size() != 4) throw DeadlyImportError(": rotation vector must have 4 elements."); + + memcpy(rotation, tvec.data(), sizeof(rotation)); + + continue; + } + + if (an == "scaleOrientation") { + std::vector tvec; + XML_ReadNode_GetAttrVal_AsArrF(idx, tvec); + if (tvec.size() != 4) { + throw DeadlyImportError(": scaleOrientation vector must have 4 elements."); + } + + ::memcpy(scale_orientation, tvec.data(), sizeof(scale_orientation)); + + continue; + } + + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + CX3DImporter_NodeElement *ne(nullptr); + + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) { + NodeElement_Cur->ID = def; + } + + // + // also set values specific to this type of group + // + // calculate transformation matrix + aiMatrix4x4::Translation(translation, matr); // T + aiMatrix4x4::Translation(center, tmatr); // C + matr *= tmatr; + aiMatrix4x4::Rotation(rotation[3], aiVector3D(rotation[0], rotation[1], rotation[2]), tmatr); // R + matr *= tmatr; + aiMatrix4x4::Rotation(scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr); // SR + matr *= tmatr; + aiMatrix4x4::Scaling(scale, tmatr); // S + matr *= tmatr; + aiMatrix4x4::Rotation(-scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr); // -SR + matr *= tmatr; + aiMatrix4x4::Translation(-center, tmatr); // -C + matr *= tmatr; + // and assign it + ((CX3DNodeElementGroup *)mNodeElementCur)->Transformation = matr; + // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. + + // for empty element exit from node in that place + if (mReader->isEmptyElement()) { + ParseHelper_Node_Exit(); + } + } // if(!use.empty()) else +} + +void X3DImporter::ParseNode_Grouping_TransformEnd() { + ParseHelper_Node_Exit(); // go up in scene graph +} + +void X3DImporter::ParseNode_Geometry2D_Arc2D() { + std::string def, use; + float endAngle = AI_MATH_HALF_PI_F; + float radius = 1; + float startAngle = 0; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_RET("endAngle", endAngle, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_RET("startAngle", startAngle, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Arc2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Arc2D, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + // create point list of geometry object and convert it to line set. + std::list tlist; + + GeometryHelper_Make_Arc2D(startAngle, endAngle, radius, 10, tlist); ///TODO: IME - AI_CONFIG for NumSeg + GeometryHelper_Extend_PointToLine(tlist, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices); + ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 2; + // check for X3DMetadataObject childs. + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "Arc2D"); + else + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// The ArcClose node specifies a portion of a circle whose center is at (0,0) and whose angles are measured starting at the positive x-axis and sweeping +// towards the positive y-axis. The end points of the arc specified are connected as defined by the closureType field. The radius field specifies the radius +// of the circle of which the arc is a portion. The arc extends from the startAngle counterclockwise to the endAngle. The value of radius shall be greater +// than zero. The values of startAngle and endAngle shall be in the range [-2pi, 2pi] radians (or the equivalent if a different default angle base unit has +// been specified). If startAngle and endAngle have the same value, a circle is specified and closureType is ignored. If the absolute difference between +// startAngle and endAngle is greater than or equal to 2pi, a complete circle is produced with no chord or radial line(s) drawn from the center. +// A closureType of "PIE" connects the end point to the start point by defining two straight line segments first from the end point to the center and then +// the center to the start point. A closureType of "CHORD" connects the end point to the start point by defining a straight line segment from the end point +// to the start point. Textures are applied individually to each face of the ArcClose2D. On the front (+Z) and back (-Z) faces of the ArcClose2D, when +// viewed from the +Z-axis, the texture is mapped onto each face with the same orientation as if the image were displayed normally in 2D. +void X3DImporter::ParseNode_Geometry2D_ArcClose2D() { + std::string def, use; + std::string closureType("PIE"); + float endAngle = AI_MATH_HALF_PI_F; + float radius = 1; + bool solid = false; + float startAngle = 0; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_RET("closureType", closureType, mReader->getAttributeValue); + MACRO_ATTRREAD_CHECK_RET("endAngle", endAngle, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("startAngle", startAngle, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_ArcClose2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_ArcClose2D, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + ((CX3DImporter_NodeElement_Geometry2D *)ne)->Solid = solid; + // create point list of geometry object. + GeometryHelper_Make_Arc2D(startAngle, endAngle, radius, 10, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices); ///TODO: IME - AI_CONFIG for NumSeg + // add chord or two radiuses only if not a circle was defined + if (!((std::fabs(endAngle - startAngle) >= AI_MATH_TWO_PI_F) || (endAngle == startAngle))) { + std::list &vlist = ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices; // just short alias. + + if ((closureType == "PIE") || (closureType == "\"PIE\"")) + vlist.push_back(aiVector3D(0, 0, 0)); // center point - first radial line + else if ((closureType != "CHORD") && (closureType != "\"CHORD\"")) + Throw_IncorrectAttrValue("closureType"); + + vlist.push_back(*vlist.begin()); // arc first point - chord from first to last point of arc(if CHORD) or second radial line(if PIE). + } + + ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices.size(); + // check for X3DMetadataObject childs. + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "ArcClose2D"); + else + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::ParseNode_Geometry2D_Circle2D() { + std::string def, use; + float radius = 1; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Circle2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Circle2D, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + // create point list of geometry object and convert it to line set. + std::list tlist; + + GeometryHelper_Make_Arc2D(0, 0, radius, 10, tlist); ///TODO: IME - AI_CONFIG for NumSeg + GeometryHelper_Extend_PointToLine(tlist, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices); + ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 2; + // check for X3DMetadataObject childs. + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "Circle2D"); + else + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// The Disk2D node specifies a circular disk which is centred at (0, 0) in the local coordinate system. The outerRadius field specifies the radius of the +// outer dimension of the Disk2D. The innerRadius field specifies the inner dimension of the Disk2D. The value of outerRadius shall be greater than zero. +// The value of innerRadius shall be greater than or equal to zero and less than or equal to outerRadius. If innerRadius is zero, the Disk2D is completely +// filled. Otherwise, the area within the innerRadius forms a hole in the Disk2D. If innerRadius is equal to outerRadius, a solid circular line shall +// be drawn using the current line properties. Textures are applied individually to each face of the Disk2D. On the front (+Z) and back (-Z) faces of +// the Disk2D, when viewed from the +Z-axis, the texture is mapped onto each face with the same orientation as if the image were displayed normally in 2D. +void X3DImporter::ParseNode_Geometry2D_Disk2D() { + std::string def, use; + float innerRadius = 0; + float outerRadius = 1; + bool solid = false; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_RET("innerRadius", innerRadius, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_RET("outerRadius", outerRadius, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Disk2D, ne); + } else { + std::list tlist_o, tlist_i; + + if (innerRadius > outerRadius) Throw_IncorrectAttrValue("innerRadius"); + + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Disk2D, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + // create point list of geometry object. + ///TODO: IME - AI_CONFIG for NumSeg + GeometryHelper_Make_Arc2D(0, 0, outerRadius, 10, tlist_o); // outer circle + if (innerRadius == 0.0f) { // make filled disk + // in tlist_o we already have points of circle. just copy it and sign as polygon. + ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices = tlist_o; + ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = tlist_o.size(); + } else if (innerRadius == outerRadius) { // make circle + // in tlist_o we already have points of circle. convert it to line set. + GeometryHelper_Extend_PointToLine(tlist_o, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices); + ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 2; + } else { // make disk + std::list &vlist = ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices; // just short alias. + + GeometryHelper_Make_Arc2D(0, 0, innerRadius, 10, tlist_i); // inner circle + // + // create quad list from two point lists + // + if (tlist_i.size() < 2) throw DeadlyImportError("Disk2D. Not enough points for creating quad list."); // tlist_i and tlist_o has equal size. + + // add all quads except last + for (std::list::iterator it_i = tlist_i.begin(), it_o = tlist_o.begin(); it_i != tlist_i.end();) { + // do not forget - CCW direction + vlist.push_back(*it_i++); // 1st point + vlist.push_back(*it_o++); // 2nd point + vlist.push_back(*it_o); // 3rd point + vlist.push_back(*it_i); // 4th point + } + + // add last quad + vlist.push_back(*tlist_i.end()); // 1st point + vlist.push_back(*tlist_o.end()); // 2nd point + vlist.push_back(*tlist_o.begin()); // 3rd point + vlist.push_back(*tlist_o.begin()); // 4th point + + ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 4; + } + + ((CX3DImporter_NodeElement_Geometry2D *)ne)->Solid = solid; + // check for X3DMetadataObject childs. + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "Disk2D"); + else + mNodeElementCur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::ParseNode_Geometry2D_Polyline2D() { + std::string def, use; + std::list lineSegments; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_REF("lineSegments", lineSegments, XML_ReadNode_GetAttrVal_AsListVec2f); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Polyline2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Polyline2D, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + // + // convert read point list of geometry object to line set. + // + std::list tlist; + + // convert vec2 to vec3 + for (std::list::iterator it2 = lineSegments.begin(); it2 != lineSegments.end(); ++it2) + tlist.push_back(aiVector3D(it2->x, it2->y, 0)); + + // convert point set to line set + GeometryHelper_Extend_PointToLine(tlist, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices); + ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 2; + // check for X3DMetadataObject childs. + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "Polyline2D"); + else + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::ParseNode_Geometry2D_Polypoint2D() { + std::string def, use; + std::list point; + CX3DImporter_NodeElement *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_REF("point", point, XML_ReadNode_GetAttrVal_AsListVec2f); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Polypoint2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Polypoint2D, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + // convert vec2 to vec3 + for (std::list::iterator it2 = point.begin(); it2 != point.end(); ++it2) { + ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0)); + } + + ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 1; + // check for X3DMetadataObject childs. + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "Polypoint2D"); + else + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::ParseNode_Geometry2D_Rectangle2D() { + std::string def, use; + aiVector2D size(2, 2); + bool solid = false; + CX3DImporter_NodeElement *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_REF("size", size, XML_ReadNode_GetAttrVal_AsVec2f); + MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Rectangle2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Rectangle2D, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + float x1 = -size.x / 2.0f; + float x2 = size.x / 2.0f; + float y1 = -size.y / 2.0f; + float y2 = size.y / 2.0f; + std::list &vlist = ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices; // just short alias. + + vlist.push_back(aiVector3D(x2, y1, 0)); // 1st point + vlist.push_back(aiVector3D(x2, y2, 0)); // 2nd point + vlist.push_back(aiVector3D(x1, y2, 0)); // 3rd point + vlist.push_back(aiVector3D(x1, y1, 0)); // 4th point + ((CX3DImporter_NodeElement_Geometry2D *)ne)->Solid = solid; + ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 4; + // check for X3DMetadataObject childs. + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "Rectangle2D"); + else + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::ParseNode_Geometry2D_TriangleSet2D() { + std::string def, use; + bool solid = false; + std::list vertices; + CX3DImporter_NodeElement *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_REF("vertices", vertices, XML_ReadNode_GetAttrVal_AsListVec2f); + MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_TriangleSet2D, ne); + } else { + if (vertices.size() % 3) throw DeadlyImportError("TriangleSet2D. Not enough points for defining triangle."); + + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_TriangleSet2D, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + // convert vec2 to vec3 + for (std::list::iterator it2 = vertices.begin(); it2 != vertices.end(); ++it2) { + ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0)); + } + + ((CX3DImporter_NodeElement_Geometry2D *)ne)->Solid = solid; + ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 3; + // check for X3DMetadataObject childs. + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "TriangleSet2D"); + else + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// The Box node specifies a rectangular parallelepiped box centred at (0, 0, 0) in the local coordinate system and aligned with the local coordinate axes. +// By default, the box measures 2 units in each dimension, from -1 to +1. The size field specifies the extents of the box along the X-, Y-, and Z-axes +// respectively and each component value shall be greater than zero. +void X3DImporter::ParseNode_Geometry3D_Box() { + std::string def, use; + bool solid = true; + aiVector3D size(2, 2, 2); + CX3DImporter_NodeElement *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_REF("size", size, XML_ReadNode_GetAttrVal_AsVec3f); + MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Box, ne); + } else { + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Box, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + GeometryHelper_MakeQL_RectParallelepiped(size, ((CX3DImporter_NodeElement_Geometry3D *)ne)->Vertices); // get quad list + ((CX3DImporter_NodeElement_Geometry3D *)ne)->Solid = solid; + ((CX3DImporter_NodeElement_Geometry3D *)ne)->NumIndices = 4; + // check for X3DMetadataObject childs. + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "Box"); + else + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::ParseNode_Geometry3D_Cone() { + std::string use, def; + bool bottom = true; + float bottomRadius = 1; + float height = 2; + bool side = true; + bool solid = true; + CX3DImporter_NodeElement *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("side", side, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("bottom", bottom, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("height", height, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_RET("bottomRadius", bottomRadius, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Cone, ne); + } else { + const unsigned int tess = 30; ///TODO: IME tessellation factor through ai_property + + std::vector tvec; // temp array for vertices. + + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Cone, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + // make cone or parts according to flags. + if (side) { + StandardShapes::MakeCone(height, 0, bottomRadius, tess, tvec, !bottom); + } else if (bottom) { + StandardShapes::MakeCircle(bottomRadius, tess, tvec); + height = -(height / 2); + for (std::vector::iterator it = tvec.begin(); it != tvec.end(); ++it) + it->y = height; // y - because circle made in oXZ. + } + + // copy data from temp array + for (std::vector::iterator it = tvec.begin(); it != tvec.end(); ++it) + ((CX3DImporter_NodeElement_Geometry3D *)ne)->Vertices.push_back(*it); + + ((CX3DImporter_NodeElement_Geometry3D *)ne)->Solid = solid; + ((CX3DImporter_NodeElement_Geometry3D *)ne)->NumIndices = 3; + // check for X3DMetadataObject childs. + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "Cone"); + else + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::ParseNode_Geometry3D_Cylinder() { + std::string use, def; + bool bottom = true; + float height = 2; + float radius = 1; + bool side = true; + bool solid = true; + bool top = true; + CX3DImporter_NodeElement *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("bottom", bottom, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("top", top, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("side", side, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("height", height, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Cylinder, ne); + } else { + const unsigned int tess = 30; ///TODO: IME tessellation factor through ai_property + + std::vector tside; // temp array for vertices of side. + std::vector tcir; // temp array for vertices of circle. + + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Cylinder, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + // make cilynder or parts according to flags. + if (side) StandardShapes::MakeCone(height, radius, radius, tess, tside, true); + + height /= 2; // height defined for whole cylinder, when creating top and bottom circle we are using just half of height. + if (top || bottom) StandardShapes::MakeCircle(radius, tess, tcir); + // copy data from temp arrays + std::list &vlist = ((CX3DImporter_NodeElement_Geometry3D *)ne)->Vertices; // just short alias. + + for (std::vector::iterator it = tside.begin(); it != tside.end(); ++it) + vlist.push_back(*it); + + if (top) { + for (std::vector::iterator it = tcir.begin(); it != tcir.end(); ++it) { + (*it).y = height; // y - because circle made in oXZ. + vlist.push_back(*it); + } + } // if(top) + + if (bottom) { + for (std::vector::iterator it = tcir.begin(); it != tcir.end(); ++it) { + (*it).y = -height; // y - because circle made in oXZ. + vlist.push_back(*it); + } + } // if(top) + + ((CX3DImporter_NodeElement_Geometry3D *)ne)->Solid = solid; + ((CX3DImporter_NodeElement_Geometry3D *)ne)->NumIndices = 3; + // check for X3DMetadataObject childs. + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "Cylinder"); + else + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ColorNormalTexCoordContentModel can contain Color (or ColorRGBA), Normal and TextureCoordinate, in any order. No more than one instance of any single +// node type is allowed. A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +// The ElevationGrid node specifies a uniform rectangular grid of varying height in the Y=0 plane of the local coordinate system. The geometry is described +// by a scalar array of height values that specify the height of a surface above each point of the grid. The xDimension and zDimension fields indicate +// the number of elements of the grid height array in the X and Z directions. Both xDimension and zDimension shall be greater than or equal to zero. +// If either the xDimension or the zDimension is less than two, the ElevationGrid contains no quadrilaterals. +void X3DImporter::ParseNode_Geometry3D_ElevationGrid() { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + float creaseAngle = 0; + std::vector height; + bool normalPerVertex = true; + bool solid = true; + int32_t xDimension = 0; + float xSpacing = 1; + int32_t zDimension = 0; + float zSpacing = 1; + CX3DImporter_NodeElement *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_REF("height", height, XML_ReadNode_GetAttrVal_AsArrF); + MACRO_ATTRREAD_CHECK_RET("xDimension", xDimension, XML_ReadNode_GetAttrVal_AsI32); + MACRO_ATTRREAD_CHECK_RET("xSpacing", xSpacing, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_RET("zDimension", zDimension, XML_ReadNode_GetAttrVal_AsI32); + MACRO_ATTRREAD_CHECK_RET("zSpacing", zSpacing, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_ElevationGrid, ne); + } else { + if ((xSpacing == 0.0f) || (zSpacing == 0.0f)) throw DeadlyImportError("Spacing in must be grater than zero."); + if ((xDimension <= 0) || (zDimension <= 0)) throw DeadlyImportError("Dimension in must be grater than zero."); + if ((size_t)(xDimension * zDimension) != height.size()) Throw_IncorrectAttrValue("Heights count must be equal to \"xDimension * zDimension\""); + + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_ElevationGrid(CX3DImporter_NodeElement::ENET_ElevationGrid, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + CX3DImporter_NodeElement_ElevationGrid &grid_alias = *((CX3DImporter_NodeElement_ElevationGrid *)ne); // create alias for conveience + + { // create grid vertices list + std::vector::const_iterator he_it = height.begin(); + + for (int32_t zi = 0; zi < zDimension; zi++) // rows + { + for (int32_t xi = 0; xi < xDimension; xi++) // columns + { + aiVector3D tvec(xSpacing * xi, *he_it, zSpacing * zi); + + grid_alias.Vertices.push_back(tvec); + ++he_it; + } + } + } // END: create grid vertices list + // + // create faces list. In "coordIdx" format + // + // check if we have quads + if ((xDimension < 2) || (zDimension < 2)) // only one element in dimension is set, create line set. + { + ((CX3DImporter_NodeElement_ElevationGrid *)ne)->NumIndices = 2; // will be holded as line set. + for (size_t i = 0, i_e = (grid_alias.Vertices.size() - 1); i < i_e; i++) { + grid_alias.CoordIdx.push_back(static_cast(i)); + grid_alias.CoordIdx.push_back(static_cast(i + 1)); + grid_alias.CoordIdx.push_back(-1); + } + } else // two or more elements in every dimension is set. create quad set. + { + ((CX3DImporter_NodeElement_ElevationGrid *)ne)->NumIndices = 4; + for (int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++) // rows + { + for (int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++) // columns + { + // points direction in face. + if (ccw) { + // CCW: + // 3 2 + // 0 1 + grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi); + grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1)); + grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1)); + grid_alias.CoordIdx.push_back(fzi * xDimension + fxi); + } else { + // CW: + // 0 1 + // 3 2 + grid_alias.CoordIdx.push_back(fzi * xDimension + fxi); + grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1)); + grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1)); + grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi); + } // if(ccw) else + + grid_alias.CoordIdx.push_back(-1); + } // for(int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++) + } // for(int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++) + } // if((xDimension < 2) || (zDimension < 2)) else + + grid_alias.ColorPerVertex = colorPerVertex; + grid_alias.NormalPerVertex = normalPerVertex; + grid_alias.CreaseAngle = creaseAngle; + grid_alias.Solid = solid; + // check for child nodes + if (!mReader->isEmptyElement()) { + ParseHelper_Node_Enter(ne); + MACRO_NODECHECK_LOOPBEGIN("ElevationGrid"); + // check for X3DComposedGeometryNodes + if (XML_CheckNode_NameEqual("Color")) { + ParseNode_Rendering_Color(); + continue; + } + if (XML_CheckNode_NameEqual("ColorRGBA")) { + ParseNode_Rendering_ColorRGBA(); + continue; + } + if (XML_CheckNode_NameEqual("Normal")) { + ParseNode_Rendering_Normal(); + continue; + } + if (XML_CheckNode_NameEqual("TextureCoordinate")) { + ParseNode_Texturing_TextureCoordinate(); + continue; + } + // check for X3DMetadataObject + if (!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("ElevationGrid"); + + MACRO_NODECHECK_LOOPEND("ElevationGrid"); + ParseHelper_Node_Exit(); + } // if(!mReader->isEmptyElement()) + else { + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + } // if(!mReader->isEmptyElement()) else + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +template +static void GeometryHelper_Extrusion_CurveIsClosed(std::vector &pCurve, const bool pDropTail, const bool pRemoveLastPoint, bool &pCurveIsClosed) { + size_t cur_sz = pCurve.size(); + + pCurveIsClosed = false; + // for curve with less than four points checking is have no sense, + if (cur_sz < 4) return; + + for (size_t s = 3, s_e = cur_sz; s < s_e; s++) { + // search for first point of duplicated part. + if (pCurve[0] == pCurve[s]) { + bool found = true; + + // check if tail(indexed by b2) is duplicate of head(indexed by b1). + for (size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++) { + if (pCurve[b1] != pCurve[b2]) { // points not match: clear flag and break loop. + found = false; + + break; + } + } // for(size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++) + + // if duplicate tail is found then drop or not it depending on flags. + if (found) { + pCurveIsClosed = true; + if (pDropTail) { + if (!pRemoveLastPoint) s++; // prepare value for iterator's arithmetics. + + pCurve.erase(pCurve.begin() + s, pCurve.end()); // remove tail + } + + break; + } // if(found) + } // if(pCurve[0] == pCurve[s]) + } // for(size_t s = 3, s_e = (cur_sz - 1); s < s_e; s++) +} + +static aiVector3D GeometryHelper_Extrusion_GetNextY(const size_t pSpine_PointIdx, const std::vector &pSpine, const bool pSpine_Closed) { + const size_t spine_idx_last = pSpine.size() - 1; + aiVector3D tvec; + + if ((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) // at first special cases + { + if (pSpine_Closed) { // If the spine curve is closed: The SCP for the first and last points is the same and is found using (spine[1] - spine[n - 2]) to compute the Y-axis. + // As we even for closed spine curve last and first point in pSpine are not the same: duplicates(spine[n - 1] which are equivalent to spine[0]) + // in tail are removed. + // So, last point in pSpine is a spine[n - 2] + tvec = pSpine[1] - pSpine[spine_idx_last]; + } else if (pSpine_PointIdx == 0) { // The Y-axis used for the first point is the vector from spine[0] to spine[1] + tvec = pSpine[1] - pSpine[0]; + } else { // The Y-axis used for the last point it is the vector from spine[n-2] to spine[n-1]. In our case(see above about dropping tail) spine[n - 1] is + // the spine[0]. + tvec = pSpine[spine_idx_last] - pSpine[spine_idx_last - 1]; + } + } // if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) + else { // For all points other than the first or last: The Y-axis for spine[i] is found by normalizing the vector defined by (spine[i+1] - spine[i-1]). + tvec = pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx - 1]; + } // if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) else + + return tvec.Normalize(); +} + +static aiVector3D GeometryHelper_Extrusion_GetNextZ(const size_t pSpine_PointIdx, const std::vector &pSpine, const bool pSpine_Closed, + const aiVector3D pVecZ_Prev) { + const aiVector3D zero_vec(0); + const size_t spine_idx_last = pSpine.size() - 1; + + aiVector3D tvec; + + // at first special cases + if (pSpine.size() < 3) // spine have not enough points for vector calculations. + { + tvec.Set(0, 0, 1); + } else if (pSpine_PointIdx == 0) // special case: first point + { + if (pSpine_Closed) // for calculating use previous point in curve s[n - 2]. In list it's a last point, because point s[n - 1] was removed as duplicate. + { + tvec = (pSpine[1] - pSpine[0]) ^ (pSpine[spine_idx_last] - pSpine[0]); + } else // for not closed curve first and next point(s[0] and s[1]) has the same vector Z. + { + bool found = false; + + // As said: "If the Z-axis of the first point is undefined (because the spine is not closed and the first two spine segments are collinear) + // then the Z-axis for the first spine point with a defined Z-axis is used." + // Walk through spine and find Z. + for (size_t next_point = 2; (next_point <= spine_idx_last) && !found; next_point++) { + // (pSpine[2] - pSpine[1]) ^ (pSpine[0] - pSpine[1]) + tvec = (pSpine[next_point] - pSpine[next_point - 1]) ^ (pSpine[next_point - 2] - pSpine[next_point - 1]); + found = !tvec.Equal(zero_vec); + } + + // if entire spine are collinear then use OZ axis. + if (!found) tvec.Set(0, 0, 1); + } // if(pSpine_Closed) else + } // else if(pSpine_PointIdx == 0) + else if (pSpine_PointIdx == spine_idx_last) // special case: last point + { + if (pSpine_Closed) { // do not forget that real last point s[n - 1] is removed as duplicated. And in this case we are calculating vector Z for point s[n - 2]. + tvec = (pSpine[0] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]); + // if taken spine vectors are collinear then use previous vector Z. + if (tvec.Equal(zero_vec)) tvec = pVecZ_Prev; + } else { // vector Z for last point of not closed curve is previous vector Z. + tvec = pVecZ_Prev; + } + } else // regular point + { + tvec = (pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]); + // if taken spine vectors are collinear then use previous vector Z. + if (tvec.Equal(zero_vec)) tvec = pVecZ_Prev; + } + + // After determining the Z-axis, its dot product with the Z-axis of the previous spine point is computed. If this value is negative, the Z-axis + // is flipped (multiplied by -1). + if ((tvec * pVecZ_Prev) < 0) tvec = -tvec; + + return tvec.Normalize(); +} + +// +void X3DImporter::ParseNode_Geometry3D_Extrusion() { + std::string use, def; + bool beginCap = true; + bool ccw = true; + bool convex = true; + float creaseAngle = 0; + std::vector crossSection; + bool endCap = true; + std::vector orientation; + std::vector scale; + bool solid = true; + std::vector spine; + CX3DImporter_NodeElement *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_RET("beginCap", beginCap, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("convex", convex, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_REF("crossSection", crossSection, XML_ReadNode_GetAttrVal_AsArrVec2f); + MACRO_ATTRREAD_CHECK_RET("endCap", endCap, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_REF("orientation", orientation, XML_ReadNode_GetAttrVal_AsArrF); + MACRO_ATTRREAD_CHECK_REF("scale", scale, XML_ReadNode_GetAttrVal_AsArrVec2f); + MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_REF("spine", spine, XML_ReadNode_GetAttrVal_AsArrVec3f); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Extrusion, ne); + } else { + // + // check if default values must be assigned + // + if (spine.size() == 0) { + spine.resize(2); + spine[0].Set(0, 0, 0), spine[1].Set(0, 1, 0); + } else if (spine.size() == 1) { + throw DeadlyImportError("ParseNode_Geometry3D_Extrusion. Spine must have at least two points."); + } + + if (crossSection.size() == 0) { + crossSection.resize(5); + crossSection[0].Set(1, 1), crossSection[1].Set(1, -1), crossSection[2].Set(-1, -1), crossSection[3].Set(-1, 1), crossSection[4].Set(1, 1); + } + + { // orientation + size_t ori_size = orientation.size() / 4; + + if (ori_size < spine.size()) { + float add_ori[4]; // values that will be added + + if (ori_size == 1) // if "orientation" has one element(means one MFRotation with four components) then use it value for all spine points. + { + add_ori[0] = orientation[0], add_ori[1] = orientation[1], add_ori[2] = orientation[2], add_ori[3] = orientation[3]; + } else // else - use default values + { + add_ori[0] = 0, add_ori[1] = 0, add_ori[2] = 1, add_ori[3] = 0; + } + + orientation.reserve(spine.size() * 4); + for (size_t i = 0, i_e = (spine.size() - ori_size); i < i_e; i++) + orientation.push_back(add_ori[0]), orientation.push_back(add_ori[1]), orientation.push_back(add_ori[2]), orientation.push_back(add_ori[3]); + } + + if (orientation.size() % 4) throw DeadlyImportError("Attribute \"orientation\" in must has multiple four quantity of numbers."); + } // END: orientation + + { // scale + if (scale.size() < spine.size()) { + aiVector2D add_sc; + + if (scale.size() == 1) // if "scale" has one element then use it value for all spine points. + add_sc = scale[0]; + else // else - use default values + add_sc.Set(1, 1); + + scale.reserve(spine.size()); + for (size_t i = 0, i_e = (spine.size() - scale.size()); i < i_e; i++) + scale.push_back(add_sc); + } + } // END: scale + // + // create and if needed - define new geometry object. + // + ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_Extrusion, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + CX3DImporter_NodeElement_IndexedSet &ext_alias = *((CX3DImporter_NodeElement_IndexedSet *)ne); // create alias for conveience + // assign part of input data + ext_alias.CCW = ccw; + ext_alias.Convex = convex; + ext_alias.CreaseAngle = creaseAngle; + ext_alias.Solid = solid; + + // + // How we done it at all? + // 1. At first we will calculate array of basises for every point in spine(look SCP in ISO-dic). Also "orientation" vector + // are applied vor every basis. + // 2. After that we can create array of point sets: which are scaled, transferred to basis of relative basis and at final translated to real position + // using relative spine point. + // 3. Next step is creating CoordIdx array(do not forget "-1" delimiter). While creating CoordIdx also created faces for begin and end caps, if + // needed. While createing CootdIdx is taking in account CCW flag. + // 4. The last step: create Vertices list. + // + bool spine_closed; // flag: true if spine curve is closed. + bool cross_closed; // flag: true if cross curve is closed. + std::vector basis_arr; // array of basises. ROW_a - X, ROW_b - Y, ROW_c - Z. + std::vector> pointset_arr; // array of point sets: cross curves. + + // detect closed curves + GeometryHelper_Extrusion_CurveIsClosed(crossSection, true, true, cross_closed); // true - drop tail, true - remove duplicate end. + GeometryHelper_Extrusion_CurveIsClosed(spine, true, true, spine_closed); // true - drop tail, true - remove duplicate end. + // If both cap are requested and spine curve is closed then we can make only one cap. Because second cap will be the same surface. + if (spine_closed) { + beginCap |= endCap; + endCap = false; + } + + { // 1. Calculate array of basises. + aiMatrix4x4 rotmat; + aiVector3D vecX(0), vecY(0), vecZ(0); + + basis_arr.resize(spine.size()); + for (size_t i = 0, i_e = spine.size(); i < i_e; i++) { + aiVector3D tvec; + + // get axises of basis. + vecY = GeometryHelper_Extrusion_GetNextY(i, spine, spine_closed); + vecZ = GeometryHelper_Extrusion_GetNextZ(i, spine, spine_closed, vecZ); + vecX = (vecY ^ vecZ).Normalize(); + // get rotation matrix and apply "orientation" to basis + aiMatrix4x4::Rotation(orientation[i * 4 + 3], aiVector3D(orientation[i * 4], orientation[i * 4 + 1], orientation[i * 4 + 2]), rotmat); + tvec = vecX, tvec *= rotmat, basis_arr[i].a1 = tvec.x, basis_arr[i].a2 = tvec.y, basis_arr[i].a3 = tvec.z; + tvec = vecY, tvec *= rotmat, basis_arr[i].b1 = tvec.x, basis_arr[i].b2 = tvec.y, basis_arr[i].b3 = tvec.z; + tvec = vecZ, tvec *= rotmat, basis_arr[i].c1 = tvec.x, basis_arr[i].c2 = tvec.y, basis_arr[i].c3 = tvec.z; + } // for(size_t i = 0, i_e = spine.size(); i < i_e; i++) + } // END: 1. Calculate array of basises + + { // 2. Create array of point sets. + aiMatrix4x4 scmat; + std::vector tcross(crossSection.size()); + + pointset_arr.resize(spine.size()); + for (size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++) { + aiVector3D tc23vec; + + tc23vec.Set(scale[spi].x, 0, scale[spi].y); + aiMatrix4x4::Scaling(tc23vec, scmat); + for (size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++) { + aiVector3D tvecX, tvecY, tvecZ; + + tc23vec.Set(crossSection[cri].x, 0, crossSection[cri].y); + // apply scaling to point + tcross[cri] = scmat * tc23vec; + // + // transfer point to new basis + // calculate coordinate in new basis + tvecX.Set(basis_arr[spi].a1, basis_arr[spi].a2, basis_arr[spi].a3), tvecX *= tcross[cri].x; + tvecY.Set(basis_arr[spi].b1, basis_arr[spi].b2, basis_arr[spi].b3), tvecY *= tcross[cri].y; + tvecZ.Set(basis_arr[spi].c1, basis_arr[spi].c2, basis_arr[spi].c3), tvecZ *= tcross[cri].z; + // apply new coordinates and translate it to spine point. + tcross[cri] = tvecX + tvecY + tvecZ + spine[spi]; + } // for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; i++) + + pointset_arr[spi] = tcross; // store transferred point set + } // for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; i++) + } // END: 2. Create array of point sets. + + { // 3. Create CoordIdx. + // add caps if needed + if (beginCap) { + // add cap as polygon. vertices of cap are places at begin, so just add numbers from zero. + for (size_t i = 0, i_e = crossSection.size(); i < i_e; i++) + ext_alias.CoordIndex.push_back(static_cast(i)); + + // add delimiter + ext_alias.CoordIndex.push_back(-1); + } // if(beginCap) + + if (endCap) { + // add cap as polygon. vertices of cap are places at end, as for beginCap use just sequence of numbers but with offset. + size_t beg = (pointset_arr.size() - 1) * crossSection.size(); + + for (size_t i = beg, i_e = (beg + crossSection.size()); i < i_e; i++) + ext_alias.CoordIndex.push_back(static_cast(i)); + + // add delimiter + ext_alias.CoordIndex.push_back(-1); + } // if(beginCap) + + // add quads + for (size_t spi = 0, spi_e = (spine.size() - 1); spi <= spi_e; spi++) { + const size_t cr_sz = crossSection.size(); + const size_t cr_last = crossSection.size() - 1; + + size_t right_col; // hold index basis for points of quad placed in right column; + + if (spi != spi_e) + right_col = spi + 1; + else if (spine_closed) // if spine curve is closed then one more quad is needed: between first and last points of curve. + right_col = 0; + else + break; // if spine curve is not closed then break the loop, because spi is out of range for that type of spine. + + for (size_t cri = 0; cri < cr_sz; cri++) { + if (cri != cr_last) { + MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex, + static_cast(spi * cr_sz + cri), + static_cast(right_col * cr_sz + cri), + static_cast(right_col * cr_sz + cri + 1), + static_cast(spi * cr_sz + cri + 1)); + // add delimiter + ext_alias.CoordIndex.push_back(-1); + } else if (cross_closed) // if cross curve is closed then one more quad is needed: between first and last points of curve. + { + MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex, + static_cast(spi * cr_sz + cri), + static_cast(right_col * cr_sz + cri), + static_cast(right_col * cr_sz + 0), + static_cast(spi * cr_sz + 0)); + // add delimiter + ext_alias.CoordIndex.push_back(-1); + } + } // for(size_t cri = 0; cri < cr_sz; cri++) + } // for(size_t spi = 0, spi_e = (spine.size() - 2); spi < spi_e; spi++) + } // END: 3. Create CoordIdx. + + { // 4. Create vertices list. + // just copy all vertices + for (size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++) { + for (size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++) { + ext_alias.Vertices.push_back(pointset_arr[spi][cri]); + } + } + } // END: 4. Create vertices list. + //PrintVectorSet("Ext. CoordIdx", ext_alias.CoordIndex); + //PrintVectorSet("Ext. Vertices", ext_alias.Vertices); + // check for child nodes + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "Extrusion"); + else + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::ParseNode_Geometry3D_IndexedFaceSet() { + std::string use, def; + bool ccw = true; + std::vector colorIndex; + bool colorPerVertex = true; + bool convex = true; + std::vector coordIndex; + float creaseAngle = 0; + std::vector normalIndex; + bool normalPerVertex = true; + bool solid = true; + std::vector texCoordIndex; + CX3DImporter_NodeElement *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_REF("colorIndex", colorIndex, XML_ReadNode_GetAttrVal_AsArrI32); + MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("convex", convex, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_REF("coordIndex", coordIndex, XML_ReadNode_GetAttrVal_AsArrI32); + MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_REF("normalIndex", normalIndex, XML_ReadNode_GetAttrVal_AsArrI32); + MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_CHECK_REF("texCoordIndex", texCoordIndex, XML_ReadNode_GetAttrVal_AsArrI32); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_IndexedFaceSet, ne); + } else { + // check data + if (coordIndex.size() == 0) throw DeadlyImportError("IndexedFaceSet must contain not empty \"coordIndex\" attribute."); + + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_IndexedFaceSet, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + CX3DImporter_NodeElement_IndexedSet &ne_alias = *((CX3DImporter_NodeElement_IndexedSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorIndex = colorIndex; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.Convex = convex; + ne_alias.CoordIndex = coordIndex; + ne_alias.CreaseAngle = creaseAngle; + ne_alias.NormalIndex = normalIndex; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + ne_alias.TexCoordIndex = texCoordIndex; + // check for child nodes + if (!mReader->isEmptyElement()) { + ParseHelper_Node_Enter(ne); + MACRO_NODECHECK_LOOPBEGIN("IndexedFaceSet"); + // check for X3DComposedGeometryNodes + if (XML_CheckNode_NameEqual("Color")) { + ParseNode_Rendering_Color(); + continue; + } + if (XML_CheckNode_NameEqual("ColorRGBA")) { + ParseNode_Rendering_ColorRGBA(); + continue; + } + if (XML_CheckNode_NameEqual("Coordinate")) { + ParseNode_Rendering_Coordinate(); + continue; + } + if (XML_CheckNode_NameEqual("Normal")) { + ParseNode_Rendering_Normal(); + continue; + } + if (XML_CheckNode_NameEqual("TextureCoordinate")) { + ParseNode_Texturing_TextureCoordinate(); + continue; + } + // check for X3DMetadataObject + if (!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("IndexedFaceSet"); + + MACRO_NODECHECK_LOOPEND("IndexedFaceSet"); + ParseHelper_Node_Exit(); + } // if(!mReader->isEmptyElement()) + else { + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::ParseNode_Geometry3D_Sphere() { + std::string use, def; + ai_real radius = 1; + bool solid = true; + CX3DImporter_NodeElement *ne(nullptr); + + MACRO_ATTRREAD_LOOPBEG; + MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); + MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); + MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); + MACRO_ATTRREAD_LOOPEND; + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(def, use, ENET_Sphere, ne); + } else { + const unsigned int tess = 3; ///TODO: IME tessellation factor through ai_property + + std::vector tlist; + + // create and if needed - define new geometry object. + ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Sphere, NodeElement_Cur); + if (!def.empty()) ne->ID = def; + + StandardShapes::MakeSphere(tess, tlist); + // copy data from temp array and apply scale + for (std::vector::iterator it = tlist.begin(); it != tlist.end(); ++it) { + ((CX3DImporter_NodeElement_Geometry3D *)ne)->Vertices.push_back(*it * radius); + } + + ((CX3DImporter_NodeElement_Geometry3D *)ne)->Solid = solid; + ((CX3DImporter_NodeElement_Geometry3D *)ne)->NumIndices = 3; + // check for X3DMetadataObject childs. + if (!mReader->isEmptyElement()) + ParseNode_Metadata(ne, "Sphere"); + else + NodeElement_Cur->Child.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + + + void X3DImporter::readMetadataObject(XmlNode &node) { const std::string &name = node.name(); if (name == "MetadataBoolean") { @@ -438,6 +2225,672 @@ void X3DImporter::readMetadataObject(XmlNode &node) { } } -} // namespace Assimp + +aiMatrix4x4 PostprocessHelper_Matrix_GlobalToCurrent() { + X3DNodeElementBase *cur_node = nullptr; + std::list matr; + aiMatrix4x4 out_matr; + + // starting walk from current element to root + cur_node = cur_node; + if (cur_node != nullptr) { + do { + // if cur_node is group then store group transformation matrix in list. + if (cur_node->Type == X3DNodeElementBase::ENET_Group) matr.push_back(((X3DNodeElementBase *)cur_node)->Transformation); + + cur_node = cur_node->Parent; + } while (cur_node != nullptr); + } + + // multiplicate all matrices in reverse order + for (std::list::reverse_iterator rit = matr.rbegin(); rit != matr.rend(); ++rit) + out_matr = out_matr * (*rit); + + return out_matr; +} + +void X3DImporter::PostprocessHelper_CollectMetadata(const CX3DImporter_NodeElement &pNodeElement, std::list &pList) const { + // walk through childs and find for metadata. + for (std::list::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); ++el_it) { + if (((*el_it)->Type == X3DElemType::ENET_MetaBoolean) || ((*el_it)->Type == X3DElemType::ENET_MetaDouble) || + ((*el_it)->Type == X3DElemType::ENET_MetaFloat) || ((*el_it)->Type == X3DElemType::ENET_MetaInteger) || + ((*el_it)->Type == X3DElemType::ENET_MetaString)) { + pList.push_back(*el_it); + } else if ((*el_it)->Type == X3DElemType::ENET_MetaSet) { + PostprocessHelper_CollectMetadata(**el_it, pList); + } + } // for(std::list::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); el_it++) +} + +bool X3DImporter::PostprocessHelper_ElementIsMetadata(const CX3DImporter_NodeElement::EType pType) const { + if ((pType == X3DNodeElementBase::ENET_MetaBoolean) || (pType == X3DElemType::ENET_MetaDouble) || + (pType == X3DElemType::ENET_MetaFloat) || (pType == X3DElemType::ENET_MetaInteger) || + (pType == X3DElemType::ENET_MetaString) || (pType == X3DElemType::ENET_MetaSet)) { + return true; + } + return false; +} + +bool X3DImporter::PostprocessHelper_ElementIsMesh(const CX3DImporter_NodeElement::EType pType) const { + if ((pType == X3DElemType::ENET_Arc2D) || (pType == X3DElemType::ENET_ArcClose2D) || + (pType == X3DElemType::ENET_Box) || (pType == X3DElemType::ENET_Circle2D) || + (pType == X3DElemType::ENET_Cone) || (pType == X3DElemType::ENET_Cylinder) || + (pType == X3DElemType::ENET_Disk2D) || (pType == X3DElemType::ENET_ElevationGrid) || + (pType == X3DElemType::ENET_Extrusion) || (pType == X3DElemType::ENET_IndexedFaceSet) || + (pType == X3DElemType::ENET_IndexedLineSet) || (pType == X3DElemType::ENET_IndexedTriangleFanSet) || + (pType == X3DElemType::ENET_IndexedTriangleSet) || (pType == X3DElemType::ENET_IndexedTriangleStripSet) || + (pType == X3DElemType::ENET_PointSet) || (pType == X3DElemType::ENET_LineSet) || + (pType == X3DElemType::ENET_Polyline2D) || (pType == X3DElemType::ENET_Polypoint2D) || + (pType == X3DElemType::ENET_Rectangle2D) || (pType == X3DElemType::ENET_Sphere) || + (pType == X3DElemType::ENET_TriangleFanSet) || (pType == X3DElemType::ENET_TriangleSet) || + (pType == X3DElemType::ENET_TriangleSet2D) || (pType == X3DElemType::ENET_TriangleStripSet)) { + return true; + } else { + return false; + } +} + +void X3DImporter::Postprocess_BuildLight(const CX3DImporter_NodeElement &pNodeElement, std::list &pSceneLightList) const { + const CX3DImporter_NodeElement_Light &ne = *((CX3DImporter_NodeElement_Light *)&pNodeElement); + aiMatrix4x4 transform_matr = PostprocessHelper_Matrix_GlobalToCurrent(); + aiLight *new_light = new aiLight; + + new_light->mName = ne.ID; + new_light->mColorAmbient = ne.Color * ne.AmbientIntensity; + new_light->mColorDiffuse = ne.Color * ne.Intensity; + new_light->mColorSpecular = ne.Color * ne.Intensity; + switch (pNodeElement.Type) { + case CX3DImporter_NodeElement::ENET_DirectionalLight: + new_light->mType = aiLightSource_DIRECTIONAL; + new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr; + + break; + case CX3DImporter_NodeElement::ENET_PointLight: + new_light->mType = aiLightSource_POINT; + new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr; + new_light->mAttenuationConstant = ne.Attenuation.x; + new_light->mAttenuationLinear = ne.Attenuation.y; + new_light->mAttenuationQuadratic = ne.Attenuation.z; + + break; + case CX3DImporter_NodeElement::ENET_SpotLight: + new_light->mType = aiLightSource_SPOT; + new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr; + new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr; + new_light->mAttenuationConstant = ne.Attenuation.x; + new_light->mAttenuationLinear = ne.Attenuation.y; + new_light->mAttenuationQuadratic = ne.Attenuation.z; + new_light->mAngleInnerCone = ne.BeamWidth; + new_light->mAngleOuterCone = ne.CutOffAngle; + + break; + default: + throw DeadlyImportError("Postprocess_BuildLight. Unknown type of light: " + to_string(pNodeElement.Type) + "."); + } + + pSceneLightList.push_back(new_light); +} + +void X3DImporter::Postprocess_BuildMaterial(const CX3DImporter_NodeElement &pNodeElement, aiMaterial **pMaterial) const { + // check argument + if (pMaterial == nullptr) throw DeadlyImportError("Postprocess_BuildMaterial. pMaterial is nullptr."); + if (*pMaterial != nullptr) throw DeadlyImportError("Postprocess_BuildMaterial. *pMaterial must be nullptr."); + + *pMaterial = new aiMaterial; + aiMaterial &taimat = **pMaterial; // creating alias for convenience. + + // at this point pNodeElement point to node. Walk through childs and add all stored data. + for (std::list::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); ++el_it) { + if ((*el_it)->Type == CX3DImporter_NodeElement::ENET_Material) { + aiColor3D tcol3; + float tvalf; + CX3DImporter_NodeElement_Material &tnemat = *((CX3DImporter_NodeElement_Material *)*el_it); + + tcol3.r = tnemat.AmbientIntensity, tcol3.g = tnemat.AmbientIntensity, tcol3.b = tnemat.AmbientIntensity; + taimat.AddProperty(&tcol3, 1, AI_MATKEY_COLOR_AMBIENT); + taimat.AddProperty(&tnemat.DiffuseColor, 1, AI_MATKEY_COLOR_DIFFUSE); + taimat.AddProperty(&tnemat.EmissiveColor, 1, AI_MATKEY_COLOR_EMISSIVE); + taimat.AddProperty(&tnemat.SpecularColor, 1, AI_MATKEY_COLOR_SPECULAR); + tvalf = 1; + taimat.AddProperty(&tvalf, 1, AI_MATKEY_SHININESS_STRENGTH); + taimat.AddProperty(&tnemat.Shininess, 1, AI_MATKEY_SHININESS); + tvalf = 1.0f - tnemat.Transparency; + taimat.AddProperty(&tvalf, 1, AI_MATKEY_OPACITY); + } // if((*el_it)->Type == CX3DImporter_NodeElement::ENET_Material) + else if ((*el_it)->Type == CX3DImporter_NodeElement::ENET_ImageTexture) { + CX3DImporter_NodeElement_ImageTexture &tnetex = *((CX3DImporter_NodeElement_ImageTexture *)*el_it); + aiString url_str(tnetex.URL.c_str()); + int mode = aiTextureOp_Multiply; + + taimat.AddProperty(&url_str, AI_MATKEY_TEXTURE_DIFFUSE(0)); + taimat.AddProperty(&tnetex.RepeatS, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0)); + taimat.AddProperty(&tnetex.RepeatT, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0)); + taimat.AddProperty(&mode, 1, AI_MATKEY_TEXOP_DIFFUSE(0)); + } // else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_ImageTexture) + else if ((*el_it)->Type == CX3DImporter_NodeElement::ENET_TextureTransform) { + aiUVTransform trans; + CX3DImporter_NodeElement_TextureTransform &tnetextr = *((CX3DImporter_NodeElement_TextureTransform *)*el_it); + + trans.mTranslation = tnetextr.Translation - tnetextr.Center; + trans.mScaling = tnetextr.Scale; + trans.mRotation = tnetextr.Rotation; + taimat.AddProperty(&trans, 1, AI_MATKEY_UVTRANSFORM_DIFFUSE(0)); + } // else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_TextureTransform) + } // for(std::list::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); el_it++) +} + +void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement &pNodeElement, aiMesh **pMesh) const { + // check argument + if (pMesh == nullptr) throw DeadlyImportError("Postprocess_BuildMesh. pMesh is nullptr."); + if (*pMesh != nullptr) throw DeadlyImportError("Postprocess_BuildMesh. *pMesh must be nullptr."); + + /************************************************************************************************************************************/ + /************************************************************ Geometry2D ************************************************************/ + /************************************************************************************************************************************/ + if ((pNodeElement.Type == CX3DImporter_NodeElement::ENET_Arc2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_ArcClose2D) || + (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Circle2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Disk2D) || + (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Polyline2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Polypoint2D) || + (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Rectangle2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet2D)) { + CX3DImporter_NodeElement_Geometry2D &tnemesh = *((CX3DImporter_NodeElement_Geometry2D *)&pNodeElement); // create alias for convenience + std::vector tarr; + + tarr.reserve(tnemesh.Vertices.size()); + for (std::list::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); ++it) + tarr.push_back(*it); + *pMesh = StandardShapes::MakeMesh(tarr, static_cast(tnemesh.NumIndices)); // create mesh from vertices using Assimp help. + + return; // mesh is build, nothing to do anymore. + } + /************************************************************************************************************************************/ + /************************************************************ Geometry3D ************************************************************/ + /************************************************************************************************************************************/ + // + // Predefined figures + // + if ((pNodeElement.Type == CX3DImporter_NodeElement::ENET_Box) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Cone) || + (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Cylinder) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Sphere)) { + CX3DImporter_NodeElement_Geometry3D &tnemesh = *((CX3DImporter_NodeElement_Geometry3D *)&pNodeElement); // create alias for convenience + std::vector tarr; + + tarr.reserve(tnemesh.Vertices.size()); + for (std::list::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); ++it) + tarr.push_back(*it); + + *pMesh = StandardShapes::MakeMesh(tarr, static_cast(tnemesh.NumIndices)); // create mesh from vertices using Assimp help. + + return; // mesh is build, nothing to do anymore. + } + // + // Parametric figures + // + if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_ElevationGrid) { + CX3DImporter_NodeElement_ElevationGrid &tnemesh = *((CX3DImporter_NodeElement_ElevationGrid *)&pNodeElement); // create alias for convenience + + // at first create mesh from existing vertices. + *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIdx, tnemesh.Vertices); + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) + MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) + MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) + MeshGeometry_AddNormal(**pMesh, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) + MeshGeometry_AddTexCoord(**pMesh, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of ElevationGrid: " + to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_ElevationGrid) + // + // Indexed primitives sets + // + if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedFaceSet) { + CX3DImporter_NodeElement_IndexedSet &tnemesh = *((CX3DImporter_NodeElement_IndexedSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) + MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) + MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, + tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) + MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) + MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedFaceSet: " + to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedFaceSet) + + if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedLineSet) { + CX3DImporter_NodeElement_IndexedSet &tnemesh = *((CX3DImporter_NodeElement_IndexedSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) + MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) + MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, + tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + } // skip because already read when mesh created. + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedLineSet: " + to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedLineSet) + + if ((pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleSet) || + (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleFanSet) || + (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleStripSet)) { + CX3DImporter_NodeElement_IndexedSet &tnemesh = *((CX3DImporter_NodeElement_IndexedSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) + MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) + MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, + tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) + MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) + MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedTriangleSet or IndexedTriangleFanSet, or \ + IndexedTriangleStripSet: " + + to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if((pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleFanSet) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleStripSet)) + + if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Extrusion) { + CX3DImporter_NodeElement_IndexedSet &tnemesh = *((CX3DImporter_NodeElement_IndexedSet *)&pNodeElement); // create alias for convenience + + *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, tnemesh.Vertices); + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Extrusion) + + // + // Primitives sets + // + if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_PointSet) { + CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + std::vector vec_copy; + + vec_copy.reserve(((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.size()); + for (std::list::const_iterator it = ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.begin(); + it != ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.end(); ++it) { + vec_copy.push_back(*it); + } + + *pMesh = StandardShapes::MakeMesh(vec_copy, 1); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) + MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, true); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) + MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, true); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + } // skip because already read when mesh created. + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of PointSet: " + to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_PointSet) + + if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_LineSet) { + CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) + MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, true); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) + MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, true); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + } // skip because already read when mesh created. + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of LineSet: " + to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_LineSet) + + if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleFanSet) { + CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + if (nullptr == *pMesh) { + break; + } + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) + MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) + MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) + MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) + MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeFanSet: " + to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleFanSet) + + if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet) { + CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + std::vector vec_copy; + + vec_copy.reserve(((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.size()); + for (std::list::const_iterator it = ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.begin(); + it != ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.end(); ++it) { + vec_copy.push_back(*it); + } + + *pMesh = StandardShapes::MakeMesh(vec_copy, 3); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) + MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) + MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) + MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) + MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeSet: " + to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet) + + if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleStripSet) { + CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) + MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) + MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) + MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) + MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TriangleStripSet: " + to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleStripSet) + + throw DeadlyImportError("Postprocess_BuildMesh. Unknown mesh type: " + to_string(pNodeElement.Type) + "."); +} + +void X3DImporter::Postprocess_BuildNode(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode, std::list &pSceneMeshList, + std::list &pSceneMaterialList, std::list &pSceneLightList) const { + X3DElementList::const_iterator chit_begin = pNodeElement.Children.begin(); + X3DElementList::const_iterator chit_end = pNodeElement.Children.end(); + std::list SceneNode_Child; + std::list SceneNode_Mesh; + + // At first read all metadata + Postprocess_CollectMetadata(pNodeElement, pSceneNode); + // check if we have deal with grouping node. Which can contain transformation or switch + if (pNodeElement.Type == X3DElemType::ENET_Group) { + const CX3DNodeElementGroup &tne_group = *((CX3DNodeElementGroup*)&pNodeElement); // create alias for convenience + + pSceneNode.mTransformation = tne_group.Transformation; + if (tne_group.UseChoice) { + // If Choice is less than zero or greater than the number of nodes in the children field, nothing is chosen. + if ((tne_group.Choice < 0) || ((size_t)tne_group.Choice >= pNodeElement.Children.size())) { + chit_begin = pNodeElement.Children.end(); + chit_end = pNodeElement.Children.end(); + } else { + for (size_t i = 0; i < (size_t)tne_group.Choice; i++) + ++chit_begin; // forward iterator to chosen node. + + chit_end = chit_begin; + ++chit_end; // point end iterator to next element after chosen node. + } + } // if(tne_group.UseChoice) + } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Group) + + // Reserve memory for fast access and check children. + for (std::list::const_iterator it = chit_begin; it != chit_end; ++it) { // in this loop we do not read metadata because it's already read at begin. + if ((*it)->Type == X3DElemType::ENET_Group) { + // if child is group then create new node and do recursive call. + aiNode *new_node = new aiNode; + + new_node->mName = (*it)->ID; + new_node->mParent = &pSceneNode; + SceneNode_Child.push_back(new_node); + Postprocess_BuildNode(**it, *new_node, pSceneMeshList, pSceneMaterialList, pSceneLightList); + } else if ((*it)->Type == X3DElemType::ENET_Shape) { + // shape can contain only one geometry and one appearance nodes. + Postprocess_BuildShape(*((CX3DImporter_NodeElement_Shape *)*it), SceneNode_Mesh, pSceneMeshList, pSceneMaterialList); + } else if (((*it)->Type == X3DElemType::ENET_DirectionalLight) || ((*it)->Type == X3DElemType::ENET_PointLight) || + ((*it)->Type == X3DElemType::ENET_SpotLight)) { + Postprocess_BuildLight(*((X3DElemType *)*it), pSceneLightList); + } else if (!PostprocessHelper_ElementIsMetadata((*it)->Type)) // skip metadata + { + throw DeadlyImportError("Postprocess_BuildNode. Unknown type: " + to_string((*it)->Type) + "."); + } + } // for(std::list::const_iterator it = chit_begin; it != chit_end; it++) + + // copy data about children and meshes to aiNode. + if (!SceneNode_Child.empty()) { + std::list::const_iterator it = SceneNode_Child.begin(); + + pSceneNode.mNumChildren = static_cast(SceneNode_Child.size()); + pSceneNode.mChildren = new aiNode *[pSceneNode.mNumChildren]; + for (size_t i = 0; i < pSceneNode.mNumChildren; i++) + pSceneNode.mChildren[i] = *it++; + } + + if (!SceneNode_Mesh.empty()) { + std::list::const_iterator it = SceneNode_Mesh.begin(); + + pSceneNode.mNumMeshes = static_cast(SceneNode_Mesh.size()); + pSceneNode.mMeshes = new unsigned int[pSceneNode.mNumMeshes]; + for (size_t i = 0; i < pSceneNode.mNumMeshes; i++) + pSceneNode.mMeshes[i] = *it++; + } + + // that's all. return to previous deals +} + +void X3DImporter::Postprocess_BuildShape(const CX3DImporter_NodeElement_Shape &pShapeNodeElement, std::list &pNodeMeshInd, + std::list &pSceneMeshList, std::list &pSceneMaterialList) const { + aiMaterial *tmat = nullptr; + aiMesh *tmesh = nullptr; + X3DElemType mesh_type = X3DElemType::ENET_Invalid; + unsigned int mat_ind = 0; + + for (X3DElementList::const_iterator it = pShapeNodeElement.Children.begin(); it != pShapeNodeElement.Children.end(); ++it) { + if (PostprocessHelper_ElementIsMesh((*it)->Type)) { + Postprocess_BuildMesh(**it, &tmesh); + if (tmesh != nullptr) { + // if mesh successfully built then add data about it to arrays + pNodeMeshInd.push_back(static_cast(pSceneMeshList.size())); + pSceneMeshList.push_back(tmesh); + // keep mesh type. Need above for texture coordinate generation. + mesh_type = (*it)->Type; + } + } else if ((*it)->Type == X3DElemType::ENET_Appearance) { + Postprocess_BuildMaterial(**it, &tmat); + if (tmat != nullptr) { + // if material successfully built then add data about it to array + mat_ind = static_cast(pSceneMaterialList.size()); + pSceneMaterialList.push_back(tmat); + } + } + } // for(std::list::const_iterator it = pShapeNodeElement.Child.begin(); it != pShapeNodeElement.Child.end(); it++) + + // associate read material with read mesh. + if ((tmesh != nullptr) && (tmat != nullptr)) { + tmesh->mMaterialIndex = mat_ind; + // Check texture mapping. If material has texture but mesh has no texture coordinate then try to ask Assimp to generate texture coordinates. + if ((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0)) { + int32_t tm; + aiVector3D tvec3; + + switch (mesh_type) { + case X3DElemType::ENET_Box: + tm = aiTextureMapping_BOX; + break; + case X3DElemType::ENET_Cone: + case X3DElemType::ENET_Cylinder: + tm = aiTextureMapping_CYLINDER; + break; + case X3DElemType::ENET_Sphere: + tm = aiTextureMapping_SPHERE; + break; + default: + tm = aiTextureMapping_PLANE; + break; + } // switch(mesh_type) + + tmat->AddProperty(&tm, 1, AI_MATKEY_MAPPING_DIFFUSE(0)); + } // if((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0)) + } // if((tmesh != nullptr) && (tmat != nullptr)) +} + +void X3DImporter::Postprocess_CollectMetadata(const CX3DImporter_NodeElement &pNodeElement, aiNode &pSceneNode) const { + X3DElementList meta_list; + size_t meta_idx; + + PostprocessHelper_CollectMetadata(pNodeElement, meta_list); // find metadata in current node element. + if (!meta_list.empty()) { + if (pSceneNode.mMetaData != nullptr) { + throw DeadlyImportError("Postprocess. MetaData member in node are not nullptr. Something went wrong."); + } + + // copy collected metadata to output node. + pSceneNode.mMetaData = aiMetadata::Alloc(static_cast(meta_list.size())); + meta_idx = 0; + for (X3DElementList::const_iterator it = meta_list.begin(); it != meta_list.end(); ++it, ++meta_idx) { + CX3DImporter_NodeElement_Meta *cur_meta = (CX3DImporter_NodeElement_Meta *)*it; + + // due to limitations we can add only first element of value list. + // Add an element according to its type. + if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaBoolean) { + if (((CX3DImporter_NodeElement_MetaBoolean *)cur_meta)->Value.size() > 0) + pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, *(((CX3DImporter_NodeElement_MetaBoolean *)cur_meta)->Value.begin())); + } else if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaDouble) { + if (((CX3DImporter_NodeElement_MetaDouble *)cur_meta)->Value.size() > 0) + pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, (float)*(((CX3DImporter_NodeElement_MetaDouble *)cur_meta)->Value.begin())); + } else if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaFloat) { + if (((CX3DImporter_NodeElement_MetaFloat *)cur_meta)->Value.size() > 0) + pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, *(((CX3DImporter_NodeElement_MetaFloat *)cur_meta)->Value.begin())); + } else if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaInteger) { + if (((CX3DImporter_NodeElement_MetaInteger *)cur_meta)->Value.size() > 0) + pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, *(((CX3DImporter_NodeElement_MetaInteger *)cur_meta)->Value.begin())); + } else if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaString) { + if (((CX3DImporter_NodeElement_MetaString *)cur_meta)->Value.size() > 0) { + aiString tstr(((CX3DImporter_NodeElement_MetaString *)cur_meta)->Value.begin()->data()); + + pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, tstr); + } + } else { + throw DeadlyImportError("Postprocess. Unknown metadata type."); + } // if((*it)->Type == CX3DImporter_NodeElement::ENET_Meta*) else + } // for(std::list::const_iterator it = meta_list.begin(); it != meta_list.end(); it++) + } // if( !meta_list.empty() ) +} + #endif // !ASSIMP_BUILD_NO_X3D_IMPORTER + +} // namespace Assimp diff --git a/code/AssetLib/X3D/X3DImporter.hpp b/code/AssetLib/X3D/X3DImporter.hpp index f65dda559..559e4932f 100644 --- a/code/AssetLib/X3D/X3DImporter.hpp +++ b/code/AssetLib/X3D/X3DImporter.hpp @@ -41,7 +41,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef INCLUDED_AI_X3D_IMPORTER_H #define INCLUDED_AI_X3D_IMPORTER_H - #include #include #include @@ -50,8 +49,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -#include #include +#include #include namespace Assimp { @@ -288,8 +287,248 @@ protected: } }; +/// This struct hold value. +struct CX3DImporter_NodeElement_Color : X3DNodeElementBase { + std::list Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + CX3DImporter_NodeElement_Color(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Color, pParent) {} + +}; // struct CX3DImporter_NodeElement_Color + +/// This struct hold value. +struct CX3DImporter_NodeElement_ColorRGBA : X3DNodeElementBase { + std::list Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + CX3DImporter_NodeElement_ColorRGBA(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_ColorRGBA, pParent) {} + +}; // struct CX3DImporter_NodeElement_ColorRGBA + +/// This struct hold value. +struct CX3DImporter_NodeElement_Coordinate : public X3DNodeElementBase { + std::list Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + CX3DImporter_NodeElement_Coordinate(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Coordinate, pParent) {} + +}; // struct CX3DImporter_NodeElement_Coordinate + +/// This struct hold value. +struct CX3DImporter_NodeElement_Normal : X3DNodeElementBase { + std::list Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + CX3DImporter_NodeElement_Normal(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Normal, pParent) {} + +}; // struct CX3DImporter_NodeElement_Normal + +/// This struct hold value. +struct CX3DImporter_NodeElement_TextureCoordinate : X3DNodeElementBase { + std::list Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + CX3DImporter_NodeElement_TextureCoordinate(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_TextureCoordinate, pParent) {} + +}; // struct CX3DImporter_NodeElement_TextureCoordinate + +/// Two-dimensional figure. +struct CX3DImporter_NodeElement_Geometry2D : X3DNodeElementBase { + std::list Vertices; ///< Vertices list. + size_t NumIndices; ///< Number of indices in one face. + bool Solid; ///< Flag: if true then render must use back-face culling, else render must draw both sides of object. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + CX3DImporter_NodeElement_Geometry2D(X3DElemType pType, X3DNodeElementBase *pParent) : + X3DNodeElementBase(pType, pParent), Solid(true) {} + +}; // class CX3DImporter_NodeElement_Geometry2D + +/// Three-dimensional body. +struct CX3DImporter_NodeElement_Geometry3D : X3DNodeElementBase { + std::list Vertices; ///< Vertices list. + size_t NumIndices; ///< Number of indices in one face. + bool Solid; ///< Flag: if true then render must use back-face culling, else render must draw both sides of object. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + CX3DImporter_NodeElement_Geometry3D(X3DElemType pType, X3DNodeElementBase *pParent) : + X3DNodeElementBase(pType, pParent), Vertices(), NumIndices(0), Solid(true) { + // empty + } +}; // class CX3DImporter_NodeElement_Geometry3D + +/// Uniform rectangular grid of varying height. +struct CX3DImporter_NodeElement_ElevationGrid : CX3DImporter_NodeElement_Geometry3D { + bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). + bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). + /// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are + /// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced. + float CreaseAngle; + std::vector CoordIdx; ///< Coordinates list by faces. In X3D format: "-1" - delimiter for faces. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + CX3DImporter_NodeElement_ElevationGrid(X3DElemType pType, X3DNodeElementBase *pParent) : + CX3DImporter_NodeElement_Geometry3D(pType, pParent) {} +}; // class CX3DImporter_NodeElement_IndexedSet + +/// Shape with indexed vertices. +struct CX3DImporter_NodeElement_IndexedSet : public CX3DImporter_NodeElement_Geometry3D { + /// The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors + /// used in the lighting model equations. If ccw is TRUE, the normals shall follow the right hand rule; the orientation of each normal with respect to + /// the vertices (taken in order) shall be such that the vertices appear to be oriented in a counterclockwise order when the vertices are viewed (in the + /// local coordinate system of the Shape) from the opposite direction as the normal. If ccw is FALSE, the normals shall be oriented in the opposite + /// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the + /// ccw field, results are undefined. + bool CCW; + std::vector ColorIndex; ///< Field to specify the polygonal faces by indexing into the or . + bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). + /// The convex field indicates whether all polygons in the shape are convex (TRUE). A polygon is convex if it is planar, does not intersect itself, + /// and all of the interior angles at its vertices are less than 180 degrees. Non planar and self intersecting polygons may produce undefined results + /// even if the convex field is FALSE. + bool Convex; + std::vector CoordIndex; ///< Field to specify the polygonal faces by indexing into the . + /// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are + /// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced. + float CreaseAngle; + std::vector NormalIndex; ///< Field to specify the polygonal faces by indexing into the . + bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). + std::vector TexCoordIndex; ///< Field to specify the polygonal faces by indexing into the . + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + CX3DImporter_NodeElement_IndexedSet(X3DElemType pType, X3DNodeElementBase *pParent) : + CX3DImporter_NodeElement_Geometry3D(pType, pParent) {} +}; // class CX3DImporter_NodeElement_IndexedSet + +/// Shape with set of vertices. +struct CX3DImporter_NodeElement_Set : CX3DImporter_NodeElement_Geometry3D { + /// The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors + /// used in the lighting model equations. If ccw is TRUE, the normals shall follow the right hand rule; the orientation of each normal with respect to + /// the vertices (taken in order) shall be such that the vertices appear to be oriented in a counterclockwise order when the vertices are viewed (in the + /// local coordinate system of the Shape) from the opposite direction as the normal. If ccw is FALSE, the normals shall be oriented in the opposite + /// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the + /// ccw field, results are undefined. + bool CCW; + bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). + bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). + std::vector CoordIndex; ///< Field to specify the polygonal faces by indexing into the . + std::vector NormalIndex; ///< Field to specify the polygonal faces by indexing into the . + std::vector TexCoordIndex; ///< Field to specify the polygonal faces by indexing into the . + std::vector VertexCount; ///< Field describes how many vertices are to be used in each polyline(polygon) from the field. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + CX3DImporter_NodeElement_Set(X3DElemType type, X3DNodeElementBase *pParent) : + CX3DImporter_NodeElement_Geometry3D(type, pParent) {} + +}; // class CX3DImporter_NodeElement_Set + +/// This struct hold value. +struct CX3DImporter_NodeElement_Shape : X3DNodeElementBase { + + /// Constructor + /// \param [in] pParent - pointer to parent node. + CX3DImporter_NodeElement_Shape(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Shape, pParent) {} +}; // struct CX3DImporter_NodeElement_Shape + +/// This struct hold value. +struct CX3DImporter_NodeElement_Appearance : public X3DNodeElementBase { + + /// Constructor + /// \param [in] pParent - pointer to parent node. + CX3DImporter_NodeElement_Appearance(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Appearance, pParent) {} + +}; // struct CX3DImporter_NodeElement_Appearance + +struct CX3DImporter_NodeElement_Material : public X3DNodeElementBase { + float AmbientIntensity; ///< Specifies how much ambient light from light sources this surface shall reflect. + aiColor3D DiffuseColor; ///< Reflects all X3D light sources depending on the angle of the surface with respect to the light source. + aiColor3D EmissiveColor; ///< Models "glowing" objects. This can be useful for displaying pre-lit models. + float Shininess; ///< Lower shininess values produce soft glows, while higher values result in sharper, smaller highlights. + aiColor3D SpecularColor; ///< The specularColor and shininess fields determine the specular highlights. + float Transparency; ///< Specifies how "clear" an object is, with 1.0 being completely transparent, and 0.0 completely opaque. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + CX3DImporter_NodeElement_Material(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Material, pParent), + AmbientIntensity(0.0f), + DiffuseColor(), + EmissiveColor(), + Shininess(0.0f), + SpecularColor(), + Transparency(1.0f) { + // empty + } +}; // class CX3DImporter_NodeElement_Material + +/// This struct hold value. +struct CX3DImporter_NodeElement_ImageTexture : X3DNodeElementBase { + /// RepeatS and RepeatT, that specify how the texture wraps in the S and T directions. If repeatS is TRUE (the default), the texture map is repeated + /// outside the [0.0, 1.0] texture coordinate range in the S direction so that it fills the shape. If repeatS is FALSE, the texture coordinates are + /// clamped in the S direction to lie within the [0.0, 1.0] range. The repeatT field is analogous to the repeatS field. + bool RepeatS; + bool RepeatT; ///< See \ref RepeatS. + std::string URL; ///< URL of the texture. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + CX3DImporter_NodeElement_ImageTexture(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_ImageTexture, pParent) {} + +}; // struct CX3DImporter_NodeElement_ImageTexture + +/// This struct hold value. +struct CX3DImporter_NodeElement_TextureTransform : X3DNodeElementBase { + aiVector2D Center; ///< Specifies a translation offset in texture coordinate space about which the rotation and scale fields are applied. + float Rotation; ///< Specifies a rotation in angle base units of the texture coordinates about the center point after the scale has been applied. + aiVector2D Scale; ///< Specifies a scaling factor in S and T of the texture coordinates about the center point. + aiVector2D Translation; ///< Specifies a translation of the texture coordinates. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + CX3DImporter_NodeElement_TextureTransform(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_TextureTransform, pParent) {} + +}; // struct CX3DImporter_NodeElement_TextureTransform + struct CX3DNodeElementGroup : X3DNodeElementBase { aiMatrix4x4 Transformation; ///< Transformation matrix. + + /// As you know node elements can use already defined node elements when attribute "USE" is defined. + /// Standard search when looking for an element in the whole scene graph, existing at this moment. + /// If a node is marked as static, the children(or lower) can not search for elements in the nodes upper then static. + bool Static; + + bool UseChoice; ///< Flag: if true then use number from \ref Choice to choose what the child will be kept. + int32_t Choice; ///< Number of the child which will be kept. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pStatic - static node flag. + CX3DNodeElementGroup(X3DNodeElementBase *pParent, const bool pStatic = false) : + X3DNodeElementBase(X3DElemType::ENET_Group, pParent), Static(pStatic), UseChoice(false) {} }; struct X3DNodeElementMeta : X3DNodeElementBase { @@ -352,7 +591,7 @@ struct X3DNodeElementMetaSet : public X3DNodeElementMeta { } }; -struct X3DNodeElementMetaString : public X3DNodeElementMeta { +struct X3DNodeElementMetaString : X3DNodeElementMeta { std::list Value; ///< Stored value. explicit X3DNodeElementMetaString(X3DNodeElementBase *pParent) : @@ -361,6 +600,36 @@ struct X3DNodeElementMetaString : public X3DNodeElementMeta { } }; +/// \struct CX3DImporter_NodeElement_Light +/// This struct hold value. +struct X3DNodeNodeElementLight : X3DNodeElementBase { + float AmbientIntensity; ///< Specifies the intensity of the ambient emission from the light. + aiColor3D Color; ///< specifies the spectral colour properties of both the direct and ambient light emission as an RGB value. + aiVector3D Direction; ///< Specifies the direction vector of the illumination emanating from the light source in the local coordinate system. + /// \var Global + /// Field that determines whether the light is global or scoped. Global lights illuminate all objects that fall within their volume of lighting influence. + /// Scoped lights only illuminate objects that are in the same transformation hierarchy as the light. + bool Global; + float Intensity; ///< Specifies the brightness of the direct emission from the light. + /// \var Attenuation + /// PointLight node's illumination falls off with distance as specified by three attenuation coefficients. The attenuation factor + /// is: "1 / max(attenuation[0] + attenuation[1] * r + attenuation[2] * r2, 1)", where r is the distance from the light to the surface being illuminated. + aiVector3D Attenuation; + aiVector3D Location; ///< Specifies a translation offset of the centre point of the light source from the light's local coordinate system origin. + float Radius; ///< Specifies the radial extent of the solid angle and the maximum distance from location that may be illuminated by the light source. + float BeamWidth; ///< Specifies an inner solid angle in which the light source emits light at uniform full intensity. + float CutOffAngle; ///< The light source's emission intensity drops off from the inner solid angle (beamWidth) to the outer solid angle (cutOffAngle). + + /// Constructor + /// \param [in] pParent - pointer to parent node. + /// \param [in] pLightType - type of the light source. + X3DNodeNodeElementLight(X3DElemType pLightType, X3DNodeElementBase *pParent) : + X3DNodeElementBase(pLightType, pParent) {} + +}; // struct CX3DImporter_NodeElement_Light + +using X3DElementList = std::list; + class X3DImporter : public BaseImporter { public: std::list NodeElement_List; ///< All elements of scene graph. @@ -389,6 +658,9 @@ public: void readScene(XmlNode &node); void readViewpoint(XmlNode &node); void readMetadataObject(XmlNode &node); + void ParseDirectionalLight(XmlNode &node); + void Postprocess_BuildNode(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode, std::list &pSceneMeshList, + std::list &pSceneMaterialList, std::list &pSceneLightList) const; private: static const aiImporterDesc Description; From 6dd9ab062c0404383fb6ebc034687b71a482c7a8 Mon Sep 17 00:00:00 2001 From: ihsinme Date: Fri, 11 Jun 2021 10:56:45 +0300 Subject: [PATCH 156/335] the expression does not throw an exception. maybe you just forgot this word. --- contrib/poly2tri/poly2tri/sweep/sweep.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contrib/poly2tri/poly2tri/sweep/sweep.cc b/contrib/poly2tri/poly2tri/sweep/sweep.cc index 23aeb6b57..8e3d794c0 100644 --- a/contrib/poly2tri/poly2tri/sweep/sweep.cc +++ b/contrib/poly2tri/poly2tri/sweep/sweep.cc @@ -129,7 +129,7 @@ void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangl EdgeEvent( tcx, ep, *p1, triangle, *p1 ); } else { // ASSIMP_CHANGE (aramis_acg) - std::runtime_error("EdgeEvent - collinear points not supported"); + throw std::runtime_error("EdgeEvent - collinear points not supported"); } return; } From 4b4d6b13268bda0c16bc5ba593b8b2abb81b1d3a Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Fri, 11 Jun 2021 13:44:52 +0100 Subject: [PATCH 157/335] Standardise Clearcoat, Sheen and Transmission Also cleanup glTFv2 defaults, don't import/export if disabled --- code/AssetLib/glTF2/glTF2Exporter.cpp | 236 +++++++++++++++----------- code/AssetLib/glTF2/glTF2Exporter.h | 26 ++- code/AssetLib/glTF2/glTF2Importer.cpp | 39 ++--- code/Common/material.cpp | 12 +- include/assimp/material.h | 77 ++++++++- include/assimp/pbrmaterial.h | 28 +-- test/unit/utglTF2ImportExport.cpp | 15 ++ 7 files changed, 281 insertions(+), 152 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index dfa62372a..3cb3891b0 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -436,11 +436,11 @@ inline void SetSamplerWrap(SamplerWrap& wrap, aiTextureMapMode map) }; } -void glTF2Exporter::GetTexSampler(const aiMaterial* mat, Ref texture, aiTextureType tt, unsigned int slot) +void glTF2Exporter::GetTexSampler(const aiMaterial& mat, Ref texture, aiTextureType tt, unsigned int slot) { aiString aId; std::string id; - if (aiGetMaterialString(mat, AI_MATKEY_GLTF_MAPPINGID(tt, slot), &aId) == AI_SUCCESS) { + if (aiGetMaterialString(&mat, AI_MATKEY_GLTF_MAPPINGID(tt, slot), &aId) == AI_SUCCESS) { id = aId.C_Str(); } @@ -455,49 +455,49 @@ void glTF2Exporter::GetTexSampler(const aiMaterial* mat, Ref texture, a SamplerMagFilter filterMag; SamplerMinFilter filterMin; - if (aiGetMaterialInteger(mat, AI_MATKEY_MAPPINGMODE_U(tt, slot), (int*)&mapU) == AI_SUCCESS) { + if (aiGetMaterialInteger(&mat, AI_MATKEY_MAPPINGMODE_U(tt, slot), (int*)&mapU) == AI_SUCCESS) { SetSamplerWrap(texture->sampler->wrapS, mapU); } - if (aiGetMaterialInteger(mat, AI_MATKEY_MAPPINGMODE_V(tt, slot), (int*)&mapV) == AI_SUCCESS) { + if (aiGetMaterialInteger(&mat, AI_MATKEY_MAPPINGMODE_V(tt, slot), (int*)&mapV) == AI_SUCCESS) { SetSamplerWrap(texture->sampler->wrapT, mapV); } - if (aiGetMaterialInteger(mat, AI_MATKEY_GLTF_MAPPINGFILTER_MAG(tt, slot), (int*)&filterMag) == AI_SUCCESS) { + if (aiGetMaterialInteger(&mat, AI_MATKEY_GLTF_MAPPINGFILTER_MAG(tt, slot), (int*)&filterMag) == AI_SUCCESS) { texture->sampler->magFilter = filterMag; } - if (aiGetMaterialInteger(mat, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(tt, slot), (int*)&filterMin) == AI_SUCCESS) { + if (aiGetMaterialInteger(&mat, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(tt, slot), (int*)&filterMin) == AI_SUCCESS) { texture->sampler->minFilter = filterMin; } aiString name; - if (aiGetMaterialString(mat, AI_MATKEY_GLTF_MAPPINGNAME(tt, slot), &name) == AI_SUCCESS) { + if (aiGetMaterialString(&mat, AI_MATKEY_GLTF_MAPPINGNAME(tt, slot), &name) == AI_SUCCESS) { texture->sampler->name = name.C_Str(); } } } -void glTF2Exporter::GetMatTexProp(const aiMaterial* mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int slot) +void glTF2Exporter::GetMatTexProp(const aiMaterial& mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int slot) { std::string textureKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName; - mat->Get(textureKey.c_str(), tt, slot, prop); + mat.Get(textureKey.c_str(), tt, slot, prop); } -void glTF2Exporter::GetMatTexProp(const aiMaterial* mat, float& prop, const char* propName, aiTextureType tt, unsigned int slot) +void glTF2Exporter::GetMatTexProp(const aiMaterial& mat, float& prop, const char* propName, aiTextureType tt, unsigned int slot) { std::string textureKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName; - mat->Get(textureKey.c_str(), tt, slot, prop); + mat.Get(textureKey.c_str(), tt, slot, prop); } -void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, Ref& texture, aiTextureType tt, unsigned int slot = 0) { - if (mat->GetTextureCount(tt) > 0) { + if (mat.GetTextureCount(tt) > 0) { aiString tex; - if (mat->Get(AI_MATKEY_TEXTURE(tt, slot), tex) == AI_SUCCESS) { + if (mat.Get(AI_MATKEY_TEXTURE(tt, slot), tex) == AI_SUCCESS) { std::string path = tex.C_Str(); if (path.size() > 0) { @@ -568,7 +568,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe } } -void glTF2Exporter::GetMatTex(const aiMaterial* mat, TextureInfo& prop, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, TextureInfo& prop, aiTextureType tt, unsigned int slot = 0) { Ref& texture = prop.texture; @@ -579,7 +579,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, TextureInfo& prop, aiTextur } } -void glTF2Exporter::GetMatTex(const aiMaterial* mat, NormalTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, NormalTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) { Ref& texture = prop.texture; @@ -591,7 +591,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, NormalTextureInfo& prop, ai } } -void glTF2Exporter::GetMatTex(const aiMaterial* mat, OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) { Ref& texture = prop.texture; @@ -603,10 +603,10 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, OcclusionTextureInfo& prop, } } -aiReturn glTF2Exporter::GetMatColor(const aiMaterial* mat, vec4& prop, const char* propName, int type, int idx) +aiReturn glTF2Exporter::GetMatColor(const aiMaterial& mat, vec4& prop, const char* propName, int type, int idx) const { aiColor4D col; - aiReturn result = mat->Get(propName, type, idx, col); + aiReturn result = mat.Get(propName, type, idx, col); if (result == AI_SUCCESS) { prop[0] = col.r; prop[1] = col.g; prop[2] = col.b; prop[3] = col.a; @@ -615,30 +615,109 @@ aiReturn glTF2Exporter::GetMatColor(const aiMaterial* mat, vec4& prop, const cha return result; } -aiReturn glTF2Exporter::GetMatColor(const aiMaterial* mat, vec3& prop, const char* propName, int type, int idx) +aiReturn glTF2Exporter::GetMatColor(const aiMaterial& mat, vec3& prop, const char* propName, int type, int idx) const { aiColor3D col; - aiReturn result = mat->Get(propName, type, idx, col); + aiReturn result = mat.Get(propName, type, idx, col); if (result == AI_SUCCESS) { - prop[0] = col.r; prop[1] = col.g; prop[2] = col.b; + prop[0] = col.r; + prop[1] = col.g; + prop[2] = col.b; } return result; } +bool glTF2Exporter::GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlossiness &pbrSG) { + bool result = false; + // If has Glossiness, a Specular Color or Specular Texture, use the KHR_materials_pbrSpecularGlossiness extension + // NOTE: This extension is being considered for deprecation (Dec 2020), may be replaced by KHR_material_specular + + if (mat.Get(AI_MATKEY_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) == AI_SUCCESS) { + result = true; + } else { + // Don't have explicit glossiness, convert from pbr roughness or legacy shininess + float shininess; + if (mat.Get(AI_MATKEY_ROUGHNESS_FACTOR, shininess) == AI_SUCCESS) { + pbrSG.glossinessFactor = 1.0f - shininess; // Extension defines this way + } else if (mat.Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) { + pbrSG.glossinessFactor = shininess / 1000; + } + } + + if (GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR) == AI_SUCCESS) { + result = true; + } + // Add any appropriate textures + GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); + + result == result || pbrSG.specularGlossinessTexture.texture; + + if (result) { + // Likely to always have diffuse + GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE); + GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE); + } + + return result; +} + +bool glTF2Exporter::GetMatSheen(const aiMaterial &mat, glTF2::MaterialSheen &sheen) { + // Return true if got any valid Sheen properties or textures + if (GetMatColor(mat, sheen.sheenColorFactor, AI_MATKEY_SHEEN_COLOR_FACTOR) != aiReturn_SUCCESS) + return false; + + // Default Sheen color factor {0,0,0} disables Sheen, so do not export + if (sheen.sheenColorFactor == defaultSheenFactor) + return false; + + mat.Get(AI_MATKEY_SHEEN_ROUGHNESS_FACTOR, sheen.sheenRoughnessFactor); + + GetMatTex(mat, sheen.sheenColorTexture, AI_MATKEY_SHEEN_COLOR_TEXTURE); + GetMatTex(mat, sheen.sheenRoughnessTexture, AI_MATKEY_SHEEN_ROUGHNESS_TEXTURE); + + return true; +} + +bool glTF2Exporter::GetMatClearcoat(const aiMaterial &mat, glTF2::MaterialClearcoat &clearcoat) { + if (mat.Get(AI_MATKEY_CLEARCOAT_FACTOR, clearcoat.clearcoatFactor) != aiReturn_SUCCESS) { + return false; + } + + // Clearcoat factor of zero disables Clearcoat, so do not export + if (clearcoat.clearcoatFactor == 0.0f) + return false; + + mat.Get(AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR, clearcoat.clearcoatRoughnessFactor); + + GetMatTex(mat, clearcoat.clearcoatTexture, AI_MATKEY_CLEARCOAT_TEXTURE); + GetMatTex(mat, clearcoat.clearcoatRoughnessTexture, AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE); + GetMatTex(mat, clearcoat.clearcoatNormalTexture, AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE); + + return true; +} + +bool glTF2Exporter::GetMatTransmission(const aiMaterial &mat, glTF2::MaterialTransmission &transmission) { + bool result = mat.Get(AI_MATKEY_TRANSMISSION_FACTOR, transmission.transmissionFactor) == aiReturn_SUCCESS; + GetMatTex(mat, transmission.transmissionTexture, AI_MATKEY_TRANSMISSION_TEXTURE); + return result || transmission.transmissionTexture.texture; +} + void glTF2Exporter::ExportMaterials() { aiString aiName; for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) { - const aiMaterial* mat = mScene->mMaterials[i]; + ai_assert(mScene->mMaterials[i] != nullptr); + + const aiMaterial & mat = *(mScene->mMaterials[i]); std::string id = "material_" + ai_to_string(i); Ref m = mAsset->materials.Create(id); std::string name; - if (mat->Get(AI_MATKEY_NAME, aiName) == AI_SUCCESS) { + if (mat.Get(AI_MATKEY_NAME, aiName) == AI_SUCCESS) { name = aiName.C_Str(); } name = mAsset->FindUniqueID(name, "material"); @@ -660,20 +739,20 @@ void glTF2Exporter::ExportMaterials() GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE); } - if (mat->Get(AI_MATKEY_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) { + if (mat.Get(AI_MATKEY_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) { //if metallicFactor wasn't defined, then the source is likely not a PBR file, and the metallicFactor should be 0 m->pbrMetallicRoughness.metallicFactor = 0; } // get roughness if source is gltf2 file - if (mat->Get(AI_MATKEY_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor) != AI_SUCCESS) { + if (mat.Get(AI_MATKEY_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor) != AI_SUCCESS) { // otherwise, try to derive and convert from specular + shininess values aiColor4D specularColor; ai_real shininess; if ( - mat->Get(AI_MATKEY_COLOR_SPECULAR, specularColor) == AI_SUCCESS && - mat->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS + mat.Get(AI_MATKEY_COLOR_SPECULAR, specularColor) == AI_SUCCESS && + mat.Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS ) { // convert specular color to luminance float specularIntensity = specularColor[0] * 0.2125f + specularColor[1] * 0.7154f + specularColor[2] * 0.0721f; @@ -694,17 +773,17 @@ void glTF2Exporter::ExportMaterials() GetMatTex(mat, m->emissiveTexture, aiTextureType_EMISSIVE); GetMatColor(mat, m->emissiveFactor, AI_MATKEY_COLOR_EMISSIVE); - mat->Get(AI_MATKEY_TWOSIDED, m->doubleSided); - mat->Get(AI_MATKEY_GLTF_ALPHACUTOFF, m->alphaCutoff); + mat.Get(AI_MATKEY_TWOSIDED, m->doubleSided); + mat.Get(AI_MATKEY_GLTF_ALPHACUTOFF, m->alphaCutoff); aiString alphaMode; - if (mat->Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode) == AI_SUCCESS) { + if (mat.Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode) == AI_SUCCESS) { m->alphaMode = alphaMode.C_Str(); } else { float opacity; - if (mat->Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) { + if (mat.Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) { if (opacity < 1) { m->alphaMode = "BLEND"; m->pbrMetallicRoughness.baseColorFactor[3] *= opacity; @@ -713,86 +792,43 @@ void glTF2Exporter::ExportMaterials() } { - // If has a Specular color, use the KHR_materials_pbrSpecularGlossiness extension + // KHR_materials_pbrSpecularGlossiness extension + // NOTE: This extension is being considered for deprecation (Dec 2020) PbrSpecularGlossiness pbrSG; - if (GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR) == AI_SUCCESS) { - if (!mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness) { - mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true; - } - - GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE); - - // If don't have explicit glossiness then convert from roughness or shininess - if (mat->Get(AI_MATKEY_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) != AI_SUCCESS) { - float shininess; - if (mat->Get(AI_MATKEY_ROUGHNESS_FACTOR, shininess) == AI_SUCCESS) { - pbrSG.glossinessFactor = 1.0f - shininess; // Extension defines this way - } else if (mat->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) { - pbrSG.glossinessFactor = shininess / 1000; - } - } - - // Add any appropriate textures - GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE); - GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); - + if (GetMatSpecGloss(mat, pbrSG)) { + mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true; m->pbrSpecularGlossiness = Nullable(pbrSG); } } // glTFv2 is either PBR or Unlit aiShadingMode shadingMode = aiShadingMode_PBR_BRDF; - mat->Get(AI_MATKEY_SHADING_MODEL, shadingMode); + mat.Get(AI_MATKEY_SHADING_MODEL, shadingMode); if (shadingMode == aiShadingMode_Unlit) { mAsset->extensionsUsed.KHR_materials_unlit = true; m->unlit = true; - } + } else { + // These extensions are not compatible with KHR_materials_unlit or KHR_materials_pbrSpecularGlossiness + if (!m->pbrSpecularGlossiness.isPresent) { + // Sheen + MaterialSheen sheen; + if (GetMatSheen(mat, sheen)) { + mAsset->extensionsUsed.KHR_materials_sheen = true; + m->materialSheen = Nullable(sheen); + } - bool hasMaterialSheen = false; - mat->Get(AI_MATKEY_GLTF_MATERIAL_SHEEN, hasMaterialSheen); + MaterialClearcoat clearcoat; + if (GetMatClearcoat(mat, clearcoat)) { + mAsset->extensionsUsed.KHR_materials_clearcoat = true; + m->materialClearcoat = Nullable(clearcoat); + } - if (hasMaterialSheen) { - mAsset->extensionsUsed.KHR_materials_sheen = true; - - MaterialSheen sheen; - - GetMatColor(mat, sheen.sheenColorFactor, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR); - mat->Get(AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR, sheen.sheenRoughnessFactor); - GetMatTex(mat, sheen.sheenColorTexture, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_TEXTURE); - GetMatTex(mat, sheen.sheenRoughnessTexture, AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_TEXTURE); - - m->materialSheen = Nullable(sheen); - } - - bool hasMaterialClearcoat = false; - mat->Get(AI_MATKEY_GLTF_MATERIAL_CLEARCOAT, hasMaterialClearcoat); - - if (hasMaterialClearcoat) { - mAsset->extensionsUsed.KHR_materials_clearcoat= true; - - MaterialClearcoat clearcoat; - - mat->Get(AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_FACTOR, clearcoat.clearcoatFactor); - mat->Get(AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_FACTOR, clearcoat.clearcoatRoughnessFactor); - GetMatTex(mat, clearcoat.clearcoatTexture, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_TEXTURE); - GetMatTex(mat, clearcoat.clearcoatRoughnessTexture, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_TEXTURE); - GetMatTex(mat, clearcoat.clearcoatNormalTexture, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_NORMAL_TEXTURE); - - m->materialClearcoat = Nullable(clearcoat); - } - - bool hasMaterialTransmission = false; - mat->Get(AI_MATKEY_GLTF_MATERIAL_TRANSMISSION, hasMaterialTransmission); - - if (hasMaterialTransmission) { - mAsset->extensionsUsed.KHR_materials_transmission = true; - - MaterialTransmission transmission; - - mat->Get(AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR, transmission.transmissionFactor); - GetMatTex(mat, transmission.transmissionTexture, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE); - - m->materialTransmission = Nullable(transmission); + MaterialTransmission transmission; + if (GetMatTransmission(mat, transmission)) { + mAsset->extensionsUsed.KHR_materials_transmission = true; + m->materialTransmission = Nullable(transmission); + } + } } } } diff --git a/code/AssetLib/glTF2/glTF2Exporter.h b/code/AssetLib/glTF2/glTF2Exporter.h index 86497516a..edc85d998 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.h +++ b/code/AssetLib/glTF2/glTF2Exporter.h @@ -72,6 +72,10 @@ namespace glTF2 struct OcclusionTextureInfo; struct Node; struct Texture; + struct PbrSpecularGlossiness; + struct MaterialSheen; + struct MaterialClearcoat; + struct MaterialTransmission; // Vec/matrix types, as raw float arrays typedef float (vec2)[2]; @@ -97,15 +101,19 @@ namespace Assimp protected: void WriteBinaryData(IOStream* outfile, std::size_t sceneLength); - void GetTexSampler(const aiMaterial* mat, glTF2::Ref texture, aiTextureType tt, unsigned int slot); - void GetMatTexProp(const aiMaterial* mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int idx); - void GetMatTexProp(const aiMaterial* mat, float& prop, const char* propName, aiTextureType tt, unsigned int idx); - void GetMatTex(const aiMaterial* mat, glTF2::Ref& texture, aiTextureType tt, unsigned int slot); - void GetMatTex(const aiMaterial* mat, glTF2::TextureInfo& prop, aiTextureType tt, unsigned int slot); - void GetMatTex(const aiMaterial* mat, glTF2::NormalTextureInfo& prop, aiTextureType tt, unsigned int slot); - void GetMatTex(const aiMaterial* mat, glTF2::OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot); - aiReturn GetMatColor(const aiMaterial* mat, glTF2::vec4& prop, const char* propName, int type, int idx); - aiReturn GetMatColor(const aiMaterial* mat, glTF2::vec3& prop, const char* propName, int type, int idx); + void GetTexSampler(const aiMaterial& mat, glTF2::Ref texture, aiTextureType tt, unsigned int slot); + void GetMatTexProp(const aiMaterial& mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int idx); + void GetMatTexProp(const aiMaterial& mat, float& prop, const char* propName, aiTextureType tt, unsigned int idx); + void GetMatTex(const aiMaterial& mat, glTF2::Ref& texture, aiTextureType tt, unsigned int slot); + void GetMatTex(const aiMaterial& mat, glTF2::TextureInfo& prop, aiTextureType tt, unsigned int slot); + void GetMatTex(const aiMaterial& mat, glTF2::NormalTextureInfo& prop, aiTextureType tt, unsigned int slot); + void GetMatTex(const aiMaterial& mat, glTF2::OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot); + aiReturn GetMatColor(const aiMaterial& mat, glTF2::vec4& prop, const char* propName, int type, int idx) const; + aiReturn GetMatColor(const aiMaterial& mat, glTF2::vec3& prop, const char* propName, int type, int idx) const; + bool GetMatSpecGloss(const aiMaterial& mat, glTF2::PbrSpecularGlossiness& pbrSG); + bool GetMatSheen(const aiMaterial& mat, glTF2::MaterialSheen& sheen); + bool GetMatClearcoat(const aiMaterial& mat, glTF2::MaterialClearcoat& clearcoat); + bool GetMatTransmission(const aiMaterial& mat, glTF2::MaterialTransmission& transmission); void ExportMetadata(); void ExportMaterials(); void ExportMeshes(); diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index b5ba65857..b0f0955f5 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -291,36 +291,37 @@ static aiMaterial *ImportMaterial(std::vector &embeddedTexIdxs, Asset &r, M aimat->AddProperty(&shadingMode, 1, AI_MATKEY_SHADING_MODEL); - //KHR_materials_sheen + // KHR_materials_sheen if (mat.materialSheen.isPresent) { MaterialSheen &sheen = mat.materialSheen.value; - - aimat->AddProperty(&mat.materialSheen.isPresent, 1, AI_MATKEY_GLTF_MATERIAL_SHEEN); - SetMaterialColorProperty(r, sheen.sheenColorFactor, aimat, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR); - aimat->AddProperty(&sheen.sheenRoughnessFactor, 1, AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR); - SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenColorTexture, aimat, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_TEXTURE); - SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenRoughnessTexture, aimat, AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_TEXTURE); + // Default value {0,0,0} disables Sheen + if (sheen.sheenColorFactor != defaultSheenFactor) { + SetMaterialColorProperty(r, sheen.sheenColorFactor, aimat, AI_MATKEY_SHEEN_COLOR_FACTOR); + aimat->AddProperty(&sheen.sheenRoughnessFactor, 1, AI_MATKEY_SHEEN_ROUGHNESS_FACTOR); + SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenColorTexture, aimat, AI_MATKEY_SHEEN_COLOR_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenRoughnessTexture, aimat, AI_MATKEY_SHEEN_ROUGHNESS_TEXTURE); + } } - //KHR_materials_clearcoat + // KHR_materials_clearcoat if (mat.materialClearcoat.isPresent) { MaterialClearcoat &clearcoat = mat.materialClearcoat.value; - - aimat->AddProperty(&mat.materialClearcoat.isPresent, 1, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT); - aimat->AddProperty(&clearcoat.clearcoatFactor, 1, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_FACTOR); - aimat->AddProperty(&clearcoat.clearcoatRoughnessFactor, 1, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_FACTOR); - SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatTexture, aimat, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_TEXTURE); - SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatRoughnessTexture, aimat, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_TEXTURE); - SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatNormalTexture, aimat, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_NORMAL_TEXTURE); + // Default value 0.0 disables clearcoat + if (clearcoat.clearcoatFactor != 0.0f) { + aimat->AddProperty(&clearcoat.clearcoatFactor, 1, AI_MATKEY_CLEARCOAT_FACTOR); + aimat->AddProperty(&clearcoat.clearcoatRoughnessFactor, 1, AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR); + SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatTexture, aimat, AI_MATKEY_CLEARCOAT_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatRoughnessTexture, aimat, AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatNormalTexture, aimat, AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE); + } } - //KHR_materials_transmission + // KHR_materials_transmission if (mat.materialTransmission.isPresent) { MaterialTransmission &transmission = mat.materialTransmission.value; - aimat->AddProperty(&mat.materialTransmission.isPresent, 1, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION); - aimat->AddProperty(&transmission.transmissionFactor, 1, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR); - SetMaterialTextureProperty(embeddedTexIdxs, r, transmission.transmissionTexture, aimat, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE); + aimat->AddProperty(&transmission.transmissionFactor, 1, AI_MATKEY_TRANSMISSION_FACTOR); + SetMaterialTextureProperty(embeddedTexIdxs, r, transmission.transmissionTexture, aimat, AI_MATKEY_TRANSMISSION_TEXTURE); } return aimat; diff --git a/code/Common/material.cpp b/code/Common/material.cpp index 5230c8b43..6c90e66f0 100644 --- a/code/Common/material.cpp +++ b/code/Common/material.cpp @@ -47,10 +47,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include // ------------------------------------------------------------------------------- -const char* TextureTypeToString(aiTextureType in) -{ - switch (in) - { +const char *TextureTypeToString(aiTextureType in) { + switch (in) { case aiTextureType_NONE: return "n/a"; case aiTextureType_DIFFUSE: @@ -87,6 +85,12 @@ const char* TextureTypeToString(aiTextureType in) return "DiffuseRoughness"; case aiTextureType_AMBIENT_OCCLUSION: return "AmbientOcclusion"; + case aiTextureType_SHEEN: + return "Sheen"; + case aiTextureType_CLEARCOAT: + return "Clearcoat"; + case aiTextureType_TRANSMISSION: + return "Transmission"; case aiTextureType_UNKNOWN: return "Unknown"; default: diff --git a/include/assimp/material.h b/include/assimp/material.h index 11cdef1f4..33e39529e 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -144,7 +144,9 @@ enum aiTextureMapMode { enum aiTextureMapping { /** The mapping coordinates are taken from an UV channel. * - * The #AI_MATKEY_UVWSRC key specifies from which UV channel + * #AI_MATKEY_UVWSRC property + * + * Specifies from which UV channel * the texture coordinates are to be taken from (remember, * meshes can have more than one UV channel). */ @@ -292,6 +294,32 @@ enum aiTextureType { aiTextureType_DIFFUSE_ROUGHNESS = 16, aiTextureType_AMBIENT_OCCLUSION = 17, + /** PBR Material Modifiers + * Some modern renderers have further PBR modifiers that may be overlaid + * on top of the 'base' PBR materials for additional realism. + * These use multiple texture maps, so only the base type is directly defined + */ + + /** Sheen + * Generally used to simulate textiles that are covered in a layer of microfibers + * eg velvet + * https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_sheen + */ + aiTextureType_SHEEN = 19, + + /** Clearcoat + * Simulates a layer of 'polish' or 'laquer' layered on top of a PBR substrate + * https://autodesk.github.io/standard-surface/#closures/coating + * https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat + */ + aiTextureType_CLEARCOAT = 20, + + /** Transmission + * Simulates transmission through the surface + * May include further information such as wall thickness + */ + aiTextureType_TRANSMISSION = 21, + /** Unknown texture * * A texture reference that does not match any of the definitions @@ -314,7 +342,7 @@ ASSIMP_API const char *TextureTypeToString(enum aiTextureType in); // --------------------------------------------------------------------------- /** @brief Defines all shading models supported by the library * - * #AI_MATKEY_SHADING_MODEL + * Property: #AI_MATKEY_SHADING_MODEL * * The list of shading modes has been taken from Blender. * See Blender documentation for more information. The API does @@ -324,6 +352,7 @@ ASSIMP_API const char *TextureTypeToString(enum aiTextureType in); * Again, this value is just a hint. Assimp tries to select the shader whose * most common implementation matches the original rendering results of the * 3D modeler which wrote a particular model as closely as possible. + * */ enum aiShadingMode { /** Flat shading. Shading is done on per-face base, @@ -384,10 +413,11 @@ enum aiShadingMode { * There are multiple methods under this banner, and model files may provide * data for more than one PBR-BRDF method. * Applications should use the set of provided properties to determine which - * of their preferred PBDR methods are available + * of their preferred PBR rendering methods are likely to be available * eg: * - If AI_MATKEY_METALLIC_FACTOR is set, then a Metallic/Roughness is available - * - If AI_MATKEY_COLOR_SPECULAR is set, then a Specular/Glossiness is available + * - If AI_MATKEY_GLOSSINESS_FACTOR is set, then a Specular/Glossiness is available + * Note that some PBR methods allow layering of techniques */ aiShadingMode_PBR_BRDF = 0xb, @@ -942,28 +972,63 @@ extern "C" { // --------------------------------------------------------------------------- // PBR material support +// -------------------- +// Properties defining PBR rendering techniques #define AI_MATKEY_USE_COLOR_MAP "$mat.useColorMap", 0, 0 // Metallic/Roughness Workflow // --------------------------- -// Base color factor. Will be multiplied by final base color texture values if extant +// Base RGBA color factor. Will be multiplied by final base color texture values if extant +// Note: Importers may choose to copy this into AI_MATKEY_COLOR_DIFFUSE for compatibility +// with renderers and formats that do not support Metallic/Roughness PBR #define AI_MATKEY_BASE_COLOR "$clr.base", 0, 0 +#define AI_MATKEY_BASE_COLOR_TEXTURE aiTextureType_BASE_COLOR, 0 #define AI_MATKEY_USE_METALLIC_MAP "$mat.useMetallicMap", 0, 0 // Metallic factor. 0.0 = Full Dielectric, 1.0 = Full Metal #define AI_MATKEY_METALLIC_FACTOR "$mat.metallicFactor", 0, 0 +#define AI_MATKEY_METALLIC_TEXTURE aiTextureType_METALNESS, 0 #define AI_MATKEY_USE_ROUGHNESS_MAP "$mat.useRoughnessMap", 0, 0 // Roughness factor. 0.0 = Perfectly Smooth, 1.0 = Completely Rough #define AI_MATKEY_ROUGHNESS_FACTOR "$mat.roughnessFactor", 0, 0 +#define AI_MATKEY_ROUGHNESS_TEXTURE aiTextureType_DIFFUSE_ROUGHNESS, 0 // Specular/Glossiness Workflow // --------------------------- // Diffuse/Albedo Color. Note: Pure Metals have a diffuse of {0,0,0} // AI_MATKEY_COLOR_DIFFUSE -// Specular Color +// Specular Color. +// Note: Metallic/Roughness may also have a Specular Color // AI_MATKEY_COLOR_SPECULAR +#define AI_MATKEY_SPECULAR_FACTOR "$mat.specularFactor", 0, 0 // Glossiness factor. 0.0 = Completely Rough, 1.0 = Perfectly Smooth #define AI_MATKEY_GLOSSINESS_FACTOR "$mat.glossinessFactor", 0, 0 +// Sheen +// ----- +// Sheen base RGB color. Default {0,0,0} +#define AI_MATKEY_SHEEN_COLOR_FACTOR "$clr.sheen.factor", 0, 0 +// Sheen Roughness Factor. +#define AI_MATKEY_SHEEN_ROUGHNESS_FACTOR "$mat.sheen.roughnessFactor", 0, 0 +#define AI_MATKEY_SHEEN_COLOR_TEXTURE aiTextureType_SHEEN, 0 +#define AI_MATKEY_SHEEN_ROUGHNESS_TEXTURE aiTextureType_SHEEN, 1 + +// Clearcoat +// --------- +#define AI_MATKEY_CLEARCOAT_FACTOR "$clr.clearcoat.factor", 0, 0 +#define AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR "$mat.clearcoat.roughnessFactor", 0, 0 +#define AI_MATKEY_CLEARCOAT_TEXTURE aiTextureType_CLEARCOAT, 0 +#define AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE aiTextureType_CLEARCOAT, 1 +#define AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE aiTextureType_CLEARCOAT, 2 + +// Transmission +// ------------ +// https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission +// Base percentage of light transmitted through the surface. 0.0 = Opaque, 1.0 = Fully transparent +#define AI_MATKEY_TRANSMISSION_FACTOR "$mat.transmission.factor", 0, 0 +// Texture defining percentage of light transmitted through the surface. +// Multiplied by AI_MATKEY_TRANSMISSION_FACTOR +#define AI_MATKEY_TRANSMISSION_TEXTURE aiTextureType_TRANSMISSION, 0 + // Emissive // -------- #define AI_MATKEY_USE_EMISSIVE_MAP "$mat.useEmissiveMap", 0, 0 diff --git a/include/assimp/pbrmaterial.h b/include/assimp/pbrmaterial.h index fd904e4fc..c67bcc3b8 100644 --- a/include/assimp/pbrmaterial.h +++ b/include/assimp/pbrmaterial.h @@ -60,20 +60,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0 //#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0 //#define AI_MATKEY_GLTF_UNLIT "$mat.gltf.unlit", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_SHEEN "$mat.gltf.materialSheen", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR "$mat.gltf.materialSheen.sheenColorFactor", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR "$mat.gltf.materialSheen.sheenRoughnessFactor", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_TEXTURE aiTextureType_UNKNOWN, 1 -#define AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 2 -#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT "$mat.gltf.materialClearcoat", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_FACTOR "$mat.gltf.materialClearcoat.clearcoatFactor", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_FACTOR "$mat.gltf.materialClearcoat.clearcoatRoughnessFactor", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_TEXTURE aiTextureType_UNKNOWN, 3 -#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 4 -#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_NORMAL_TEXTURE aiTextureType_NORMALS, 1 -#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION "$mat.gltf.materialTransmission", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR "$mat.gltf.materialTransmission.transmissionFactor", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE aiTextureType_UNKNOWN, 5 +//#define AI_MATKEY_GLTF_MATERIAL_SHEEN "$mat.gltf.materialSheen", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR "$mat.gltf.materialSheen.sheenColorFactor", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR "$mat.gltf.materialSheen.sheenRoughnessFactor", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_TEXTURE aiTextureType_UNKNOWN, 1 +//#define AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 2 +//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT "$mat.gltf.materialClearcoat", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_FACTOR "$mat.gltf.materialClearcoat.clearcoatFactor", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_FACTOR "$mat.gltf.materialClearcoat.clearcoatRoughnessFactor", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_TEXTURE aiTextureType_UNKNOWN, 3 +//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 4 +//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_NORMAL_TEXTURE aiTextureType_NORMALS, 1 +//#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION "$mat.gltf.materialTransmission", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR "$mat.gltf.materialTransmission.transmissionFactor", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE aiTextureType_UNKNOWN, 5 #define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE "$tex.file.texCoord" #define _AI_MATKEY_GLTF_MAPPINGNAME_BASE "$tex.mappingname" diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index e0ac10ad5..766372325 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -152,6 +152,20 @@ TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_pbrSpecularGlossiness) { } #ifndef ASSIMP_BUILD_NO_EXPORT + +TEST_F(utglTF2ImportExport, importglTF2AndExport_KHR_materials_pbrSpecularGlossiness) { + Assimp::Importer importer; + Assimp::Exporter exporter; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf", + aiProcess_ValidateDataStructure); + EXPECT_NE(nullptr, scene); + // Export + EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured_out.glb")); + + // And re-import + EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured_out.glb", true)); +} + TEST_F(utglTF2ImportExport, importglTF2AndExportToOBJ) { Assimp::Importer importer; Assimp::Exporter exporter; @@ -169,6 +183,7 @@ TEST_F(utglTF2ImportExport, importglTF2EmbeddedAndExportToOBJ) { EXPECT_NE(nullptr, scene); EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "obj", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-Embedded/BoxTextured_out.obj")); } + #endif // ASSIMP_BUILD_NO_EXPORT TEST_F(utglTF2ImportExport, importglTF2PrimitiveModePointsWithoutIndices) { From c86522e552dc3edf0d2a3cff228956f3ce87b5c0 Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Fri, 11 Jun 2021 14:34:42 +0100 Subject: [PATCH 158/335] Add glTFv2 Clearcoat import/export tests Uses Clearcoat model from Khronos --- code/AssetLib/glTF2/glTF2Importer.cpp | 5 + include/assimp/material.h | 3 +- .../glTF2/ClearCoat-glTF/ClearCoatLabels.png | Bin 0 -> 10270 bytes .../glTF2/ClearCoat-glTF/ClearCoatTest.bin | Bin 0 -> 50328 bytes .../glTF2/ClearCoat-glTF/ClearCoatTest.gltf | 1669 +++++++++++++++++ .../glTF2/ClearCoat-glTF/PartialCoating.png | Bin 0 -> 5077 bytes .../ClearCoat-glTF/PartialCoating_Alpha.png | Bin 0 -> 5065 bytes .../ClearCoat-glTF/PlasticWrap_normals.jpg | Bin 0 -> 144210 bytes .../glTF2/ClearCoat-glTF/RibsNormal.png | Bin 0 -> 1605 bytes .../glTF2/ClearCoat-glTF/RoughnessStripes.png | Bin 0 -> 5033 bytes test/unit/utglTF2ImportExport.cpp | 57 + 11 files changed, 1733 insertions(+), 1 deletion(-) create mode 100644 test/models/glTF2/ClearCoat-glTF/ClearCoatLabels.png create mode 100644 test/models/glTF2/ClearCoat-glTF/ClearCoatTest.bin create mode 100644 test/models/glTF2/ClearCoat-glTF/ClearCoatTest.gltf create mode 100644 test/models/glTF2/ClearCoat-glTF/PartialCoating.png create mode 100644 test/models/glTF2/ClearCoat-glTF/PartialCoating_Alpha.png create mode 100644 test/models/glTF2/ClearCoat-glTF/PlasticWrap_normals.jpg create mode 100644 test/models/glTF2/ClearCoat-glTF/RibsNormal.png create mode 100644 test/models/glTF2/ClearCoat-glTF/RoughnessStripes.png diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index b0f0955f5..b435f111d 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -208,6 +208,11 @@ inline void SetMaterialTextureProperty(std::vector &embeddedTexIdxs, Asset if (sampler->minFilter != SamplerMinFilter::UNSET) { mat->AddProperty(&sampler->minFilter, 1, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(texType, texSlot)); } + } else { + // Use glTFv2 default sampler + const aiTextureMapMode default_wrap = aiTextureMapMode_Wrap; + mat->AddProperty(&default_wrap, 1, AI_MATKEY_MAPPINGMODE_U(texType, texSlot)); + mat->AddProperty(&default_wrap, 1, AI_MATKEY_MAPPINGMODE_V(texType, texSlot)); } } } diff --git a/include/assimp/material.h b/include/assimp/material.h index 33e39529e..f348da369 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -1014,7 +1014,8 @@ extern "C" { // Clearcoat // --------- -#define AI_MATKEY_CLEARCOAT_FACTOR "$clr.clearcoat.factor", 0, 0 +// Clearcoat layer intensity. 0.0 = none (disabled) +#define AI_MATKEY_CLEARCOAT_FACTOR "$mat.clearcoat.factor", 0, 0 #define AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR "$mat.clearcoat.roughnessFactor", 0, 0 #define AI_MATKEY_CLEARCOAT_TEXTURE aiTextureType_CLEARCOAT, 0 #define AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE aiTextureType_CLEARCOAT, 1 diff --git a/test/models/glTF2/ClearCoat-glTF/ClearCoatLabels.png b/test/models/glTF2/ClearCoat-glTF/ClearCoatLabels.png new file mode 100644 index 0000000000000000000000000000000000000000..d47f2f5e1f06116fca5ddd9697001ffedcc5bd68 GIT binary patch literal 10270 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 literal 0 HcmV?d00001 diff --git a/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.bin b/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.bin new file mode 100644 index 0000000000000000000000000000000000000000..e7c6a93a4995917f931d580f7d25c94f33d1639a GIT binary patch literal 50328 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 z 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 MV6NHY-@g5S0BBn6`Tzg` literal 0 HcmV?d00001 diff --git a/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.gltf b/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.gltf new file mode 100644 index 000000000..ae12bfc11 --- /dev/null +++ b/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.gltf @@ -0,0 +1,1669 @@ +{ + "asset" : { + "generator" : "Khronos glTF Blender I/O v1.2.8 with hand-edits for clearcoat", + "version" : "2.0" + }, + "extensionsUsed": [ + "KHR_materials_clearcoat" + ], + "scene" : 0, + "scenes" : [ + { + "name" : "Scene", + "nodes" : [ + 3, + 7, + 11, + 15, + 19, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32 + ] + } + ], + "nodes" : [ + { + "mesh" : 0, + "name" : "BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 1, + "name" : "ClearCoatSample" + }, + { + "mesh" : 2, + "name" : "CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 0, + 1, + 2 + ], + "name" : "R0_SimpleCoatTest", + "translation" : [ + 0, + 5.25, + 0 + ] + }, + { + "mesh" : 3, + "name" : "R1_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 4, + "name" : "R1_ClearCoatSample" + }, + { + "mesh" : 5, + "name" : "R1_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 4, + 5, + 6 + ], + "name" : "R1_PartialCoatTest", + "translation" : [ + 0, + 3.1500000953674316, + 0 + ] + }, + { + "mesh" : 6, + "name" : "R2_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 7, + "name" : "R2_ClearCoatSample" + }, + { + "mesh" : 8, + "name" : "R2_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 8, + 9, + 10 + ], + "name" : "R2_RoughnessVariations", + "translation" : [ + 0, + 1.0499999523162842, + 0 + ] + }, + { + "mesh" : 9, + "name" : "R3_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 10, + "name" : "R3_ClearCoatSample" + }, + { + "mesh" : 11, + "name" : "R3_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 12, + 13, + 14 + ], + "name" : "R3_BaseNormals", + "translation" : [ + 0, + -1.0499999523162842, + 0 + ] + }, + { + "mesh" : 12, + "name" : "R4_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 13, + "name" : "R4_ClearCoatSample" + }, + { + "mesh" : 14, + "name" : "R4_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 16, + 17, + 18 + ], + "name" : "R4_CoatNormals", + "translation" : [ + 0, + -5.25, + 0 + ] + }, + { + "mesh" : 15, + "name" : "R5_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 16, + "name" : "R5_ClearCoatSample" + }, + { + "mesh" : 17, + "name" : "R5_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 20, + 21, + 22 + ], + "name" : "R5_SharedNormals", + "translation" : [ + 0, + -3.1500000953674316, + 0 + ] + }, + { + "mesh" : 18, + "name" : "X2_Label_CoatingOnly", + "translation" : [ + 2.0712804794311523, + 6.619500160217285, + 0 + ] + }, + { + "mesh" : 19, + "name" : "Y0_Label_SimpleCoating", + "translation" : [ + -5.3578033447265625, + 5.25, + 0 + ] + }, + { + "mesh" : 20, + "name" : "Y1_Label_PartialCoating", + "translation" : [ + -5.3578033447265625, + 3.1673200130462646, + 0 + ] + }, + { + "mesh" : 21, + "name" : "Y2_Label_Roughness", + "translation" : [ + -5.3578033447265625, + 1.1383899450302124, + 0 + ] + }, + { + "mesh" : 22, + "name" : "Y3_Label_BaseNormals", + "translation" : [ + -5.3578033447265625, + -1.099429965019226, + 0 + ] + }, + { + "mesh" : 23, + "name" : "Y4_Label_CoatNormals", + "translation" : [ + -5.3578033447265625, + -5.252500057220459, + 0 + ] + }, + { + "mesh" : 24, + "name" : "Y5_Label_SharedNormals", + "translation" : [ + -5.3578033447265625, + -3.1963000297546387, + 0 + ] + }, + { + "mesh" : 25, + "name" : "X0_Label_BaseLayer", + "translation" : [ + -2.087031602859497, + 6.616230010986328, + 0 + ] + }, + { + "mesh" : 26, + "name" : "X1_Label_Coated", + "translation" : [ + 0, + 6.614150047302246, + 0 + ] + } + ], + "materials" : [ + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Simple_Base", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.5, + 0.019999999552965164, + 0.009999999776482582, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Simple_Coated", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.5, + 0.019999999552965164, + 0.009999999776482582, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03 + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Simple_Coating", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Partial_Base", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Partial_Coated", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03, + "clearcoatTexture": { + "index": 0, + "texCoord": 0 + } + } + } + }, + { + "alphaMode" : "BLEND", + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Partial_Coating", + "pbrMetallicRoughness" : { + "baseColorTexture" : { + "index" : 1, + "texCoord" : 0 + }, + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "RoughVariations_Base", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.6000000238418579 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "RoughVariations_Coated", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.6000000238418579 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 1, + "clearcoatRoughnessTexture": { + "index": 2, + "texCoord": 0 + } + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "RoughVariations_Coating", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "metallicRoughnessTexture" : { + "index" : 2, + "texCoord" : 0 + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "BaseNorm_Base", + "normalTexture" : { + "index" : 3, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "BaseNorm_Coated", + "normalTexture" : { + "index" : 4, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012848637998104095, + 0.021861059591174126, + 0.11068868637084961, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03 + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "BaseNorm_Coating", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "CoatNorm_Base", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "CoatNorm_Coated", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03, + "clearcoatNormalTexture": { + "index": 5, + "texCoord": 0, + "scale": 1 + } + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "CoatNorm_Coating", + "normalTexture" : { + "index" : 5, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "SharedNorm_Base", + "normalTexture" : { + "index" : 6, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "SharedNorm_Coated", + "normalTexture" : { + "index" : 7, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03, + "clearcoatNormalTexture": { + "index": 7, + "texCoord": 0 + } + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "SharedNorm_Coating", + "normalTexture" : { + "index" : 8, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 1, + 1, + 1 + ], + "emissiveTexture" : { + "index" : 9, + "texCoord" : 0 + }, + "name" : "LabelMaterial", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.8999999761581421 + } + } + ], + "meshes" : [ + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 0 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 1 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 2 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 3 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 4 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 5 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 6 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 7 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 8 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 9 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 10 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 11 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 12 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 13 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 14 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 15 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 16 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 17 + } + ] + }, + { + "name" : "Labels_Mesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 4, + "NORMAL" : 5, + "TEXCOORD_0" : 6 + }, + "indices" : 7, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.001", + "primitives" : [ + { + "attributes" : { + "POSITION" : 8, + "NORMAL" : 9, + "TEXCOORD_0" : 10 + }, + "indices" : 7, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.002", + "primitives" : [ + { + "attributes" : { + "POSITION" : 11, + "NORMAL" : 12, + "TEXCOORD_0" : 13 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.003", + "primitives" : [ + { + "attributes" : { + "POSITION" : 15, + "NORMAL" : 16, + "TEXCOORD_0" : 17 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.004", + "primitives" : [ + { + "attributes" : { + "POSITION" : 18, + "NORMAL" : 19, + "TEXCOORD_0" : 20 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.005", + "primitives" : [ + { + "attributes" : { + "POSITION" : 21, + "NORMAL" : 22, + "TEXCOORD_0" : 23 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.006", + "primitives" : [ + { + "attributes" : { + "POSITION" : 24, + "NORMAL" : 25, + "TEXCOORD_0" : 26 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.007", + "primitives" : [ + { + "attributes" : { + "POSITION" : 27, + "NORMAL" : 28, + "TEXCOORD_0" : 29 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.008", + "primitives" : [ + { + "attributes" : { + "POSITION" : 30, + "NORMAL" : 31, + "TEXCOORD_0" : 32 + }, + "indices" : 7, + "material" : 18 + } + ] + } + ], + "textures" : [ + { + "source" : 0 + }, + { + "source" : 1 + }, + { + "source" : 2 + }, + { + "source" : 3 + }, + { + "source" : 3 + }, + { + "source" : 4 + }, + { + "source" : 3 + }, + { + "source" : 3 + }, + { + "source" : 3 + }, + { + "source" : 5 + } + ], + "images" : [ + { + "mimeType" : "image/png", + "name" : "PartialCoating", + "uri" : "PartialCoating.png" + }, + { + "mimeType" : "image/png", + "name" : "PartialCoating_Alpha", + "uri" : "PartialCoating_Alpha.png" + }, + { + "mimeType" : "image/png", + "name" : "RoughnessStripes", + "uri" : "RoughnessStripes.png" + }, + { + "mimeType" : "image/png", + "name" : "RibsNormal", + "uri" : "RibsNormal.png" + }, + { + "mimeType" : "image/jpeg", + "name" : "PlasticWrap_normals", + "uri" : "PlasticWrap_normals.jpg" + }, + { + "mimeType" : "image/png", + "name" : "ClearCoatLabels", + "uri" : "ClearCoatLabels.png" + } + ], + "accessors" : [ + { + "bufferView" : 0, + "componentType" : 5126, + "count" : 1113, + "max" : [ + 1, + 1, + 1.0499999523162842 + ], + "min" : [ + -1, + -1, + -0.06000000983476639 + ], + "type" : "VEC3" + }, + { + "bufferView" : 1, + "componentType" : 5126, + "count" : 1113, + "type" : "VEC3" + }, + { + "bufferView" : 2, + "componentType" : 5126, + "count" : 1113, + "type" : "VEC2" + }, + { + "bufferView" : 3, + "componentType" : 5123, + "count" : 6180, + "type" : "SCALAR" + }, + { + "bufferView" : 4, + "componentType" : 5126, + "count" : 8, + "max" : [ + 1.0280373096466064, + 0.23501670360565186, + 3.8289083903464416e-08 + ], + "min" : [ + -0.968224287033081, + -0.2350165843963623, + -0.010000125505030155 + ], + "type" : "VEC3" + }, + { + "bufferView" : 5, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 6, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 7, + "componentType" : 5123, + "count" : 12, + "type" : "SCALAR" + }, + { + "bufferView" : 8, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.23026323318481445, + 3.751463495405005e-08 + ], + "min" : [ + -2, + -0.23026317358016968, + -0.010000579059123993 + ], + "type" : "VEC3" + }, + { + "bufferView" : 9, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 10, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 11, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.2302631139755249, + 3.7514624295909016e-08 + ], + "min" : [ + -2, + -0.23026323318481445, + -0.010000428184866905 + ], + "type" : "VEC3" + }, + { + "bufferView" : 12, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 13, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 14, + "componentType" : 5123, + "count" : 12, + "type" : "SCALAR" + }, + { + "bufferView" : 15, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.22039484977722168, + 3.590687924770464e-08 + ], + "min" : [ + -2, + -0.22039473056793213, + -0.010000280104577541 + ], + "type" : "VEC3" + }, + { + "bufferView" : 16, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 17, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 18, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.21764808893203735, + 3.545937588000925e-08 + ], + "min" : [ + -2, + -0.21764802932739258, + -0.010000137612223625 + ], + "type" : "VEC3" + }, + { + "bufferView" : 19, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 20, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 21, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.20775499939918518, + 3.3847587843638394e-08 + ], + "min" : [ + -2, + -0.20775499939918518, + -0.009999996051192284 + ], + "type" : "VEC3" + }, + { + "bufferView" : 22, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 23, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 24, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.22341907024383545, + 3.6399587344249085e-08 + ], + "min" : [ + -2, + -0.22341907024383545, + -0.009999859146773815 + ], + "type" : "VEC3" + }, + { + "bufferView" : 25, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 26, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 27, + "componentType" : 5126, + "count" : 8, + "max" : [ + 0.9169960618019104, + 0.22670458257198334, + 3.69348676088066e-08 + ], + "min" : [ + -0.9199233651161194, + -0.22670456767082214, + -0.010000176727771759 + ], + "type" : "VEC3" + }, + { + "bufferView" : 28, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 29, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 30, + "componentType" : 5126, + "count" : 8, + "max" : [ + 0.8968609571456909, + 0.20853587985038757, + 3.397480696776256e-08 + ], + "min" : [ + -0.9147982001304626, + -0.2085357904434204, + -0.010000113397836685 + ], + "type" : "VEC3" + }, + { + "bufferView" : 31, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 32, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + } + ], + "bufferViews" : [ + { + "buffer" : 0, + "byteLength" : 13356, + "byteOffset" : 0 + }, + { + "buffer" : 0, + "byteLength" : 13356, + "byteOffset" : 13356 + }, + { + "buffer" : 0, + "byteLength" : 8904, + "byteOffset" : 26712 + }, + { + "buffer" : 0, + "byteLength" : 12360, + "byteOffset" : 35616 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 47976 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48072 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 48168 + }, + { + "buffer" : 0, + "byteLength" : 24, + "byteOffset" : 48232 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48256 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48352 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 48448 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48512 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48608 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 48704 + }, + { + "buffer" : 0, + "byteLength" : 24, + "byteOffset" : 48768 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48792 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48888 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 48984 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49048 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49144 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 49240 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49304 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49400 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 49496 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49560 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49656 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 49752 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49816 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49912 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 50008 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 50072 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 50168 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 50264 + } + ], + "buffers" : [ + { + "byteLength" : 50328, + "uri" : "ClearCoatTest.bin" + } + ] +} diff --git a/test/models/glTF2/ClearCoat-glTF/PartialCoating.png b/test/models/glTF2/ClearCoat-glTF/PartialCoating.png new file mode 100644 index 0000000000000000000000000000000000000000..d579bbebb8a423eedb16594a7ef715fe7521a752 GIT binary patch literal 5077 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 Q@JSA$qoJ>ky=fQyKcnMWr2qf` literal 0 HcmV?d00001 diff --git a/test/models/glTF2/ClearCoat-glTF/PartialCoating_Alpha.png b/test/models/glTF2/ClearCoat-glTF/PartialCoating_Alpha.png new file mode 100644 index 0000000000000000000000000000000000000000..b1911d9f0a303a57181d44856f3f31ea18c11063 GIT binary patch literal 5065 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 Q_!|ylXYGipvh+*(4_Os-!2kdN literal 0 HcmV?d00001 diff --git a/test/models/glTF2/ClearCoat-glTF/PlasticWrap_normals.jpg b/test/models/glTF2/ClearCoat-glTF/PlasticWrap_normals.jpg new file mode 100644 index 0000000000000000000000000000000000000000..73e6260ef7126ccb11113179c58cf6611d3f3e9d GIT binary patch literal 144210 zcmbTddpy&9{QtkM4mwFHL}gd0Tv3)p6n2#džOVOJDoNkR^>T_xnWONE@;Rbq*4 zIn0@rb2*>3a?BiOIgBl{!+XD%>+?JP@%`ib`wh3fUNvRhT{&|%F#j%c0KH#lWzblU9v1#^pwmR5Gx>>be89Wm}V zZ+Up$zT@TRAMh|RC^#hiX+&hyv*?)TFJ2}kr@Tr{dz!pljPw6`{4d9)2*&l> zx^-*U$*qoS&2NF=XRYG8_4{=<>^OTx?uPHq11BDD+;uMTO~L0)8hTe3l-wS4ZB{;b za`X^yb!h*M?0;`yPyT<6?EfCv|1++B*w(dcz~rq}gdt&#orEzV+B#P%5TW(|mBE@8 zS=JvGIDFd7SL`O?FBfPEX_wMR7@O`>;h^bu&`Yma)=B7aXdi*y(i>1J>Z1 zz1CTd`n94v@k@%hY{oRAvM!yOmqt`BfRxOCOs>p8Il6b?qs|=`>mHp9NH~CHPDbQ; z^oD7};Qa5Vbo$NjC2*@cyYwj$^JG%CMwAih&>(lyF~Q^4J92YNnA906$eL`kJ?7NQ}nL{3T+omFSO1Ea7EQ zTxSKZ_o0e>q!F98vd4Y}Z_XsjVCpFQd==<$452>{V<>~2^J#qB!-5X~DQ4yFd#eRL zee{YtiA-pEFG8mWeJ;wShuB0ZXK`FT*>#y$7;1}uAVP9Qo_nmJ9n&W9 z%!KlIG!>J9Ipm+Chwh$Od4VOo_sRCdsGROWTfQf}_nP$IDTD1KQ$tIoUujv&Au`yg z_2#Os@77RmP0}QEXm@!CKh-F{CTU7t6SNJ(xF_%PXyRhW>>q?OJe}z*9d#x>hDJp! zC*R&{li2yZPVQ*O%Fa&{l8X-unY5?d{?aG1!y~a2FX8dY4K#~gj8P*ITt6~X7};cb z$PY6$qSSpcnCjz<)DNArk@(FmO+d{zhKc+en0`%32(7!2T25EWd&|4auoC^mE|_}O zO;~+vk-_{siFsk#&v-s|0_ud^Fm+EuCW={JIL~gLW)oIaq!Zp7*?RnZAH@<+ipwsx z-u3<^4?ovFmsH^|PlOQlM0X_ne8@H8vX_B=BQZL9+Eot8Lr~{!!5Z~*R(XOPt7iq@ zXeg(h(Q};|Bv{^mA%oq@hIY1A-Pe1h$$URRlUR2b8{=d!cwv}Vc7+U9Uyd8j>gj^N zV8$FRT5q+iu@!XA;r8RJ;G4EC_w0f5Gvz61e`AsA6+!kBLfKtWTmx1~Y5dOTT4vz0vYu>_Lx!1B3foKu+rhynMZ%q} z1SUtDhCW@B!L}I3_y{@#l!3KS_`bhdAUZ^^{EN`}ye5WJ-!aj;NBXllz*qcOkiQ9~ z-|3Yl-OVzU6y%Z`Z^dA14)vs~z~e&>zc_r zt70#$&;vxQ!1umnQuh5ccv~KgSy_#WUCXB-R3#W(t5hyRMXY?-m8|&zE=-0~?vGD` zMaQ~^`-^Am6(+Jhg zP~0eXW#ZA@U(V`qK~l%`F%Y}jz4SuaR?`b6~~l7y`jt`!3FzW4P9%7`&Y-nERpCt&zbJ9|=GRiZ(OYB%peJ))q5GEEI3AM+2VT9(2^r%(Z>3wB0nS-s6MBo?zDb zFY-9mq6t?K#LfF#!gcP5PdV|1dUr>O#&{TZr)g-3b=%&C&kc!6af6AN0Uh{z6V~;pYpC zPcf_NZkKqzS*ZI?@E*1?SPBO63urDg7O z>xgW#FL()mE@@w;+~P8LGdP=V=!N{YK*+?#w@cW?|27eNawM^}zvPgTMLk*X7$&0N zcTpd5`_ch)fX`D2VISg`LsgeTWV?QlDV_UO2Gc?lg`s2iQC!!eJ>%xu?Rk3|5xpW? zC6dmL4@>5UKj^(Tx%rk*|8>orw)<7pUiBh(lrU|8_$wlyZq#tbEFA<-ABg;}UROk6)ZhT~SS>VDG9MhJlyrJh4{MWLdDWHNJb7i3Dpk

8#fXvU&U@TnHe66M)}` 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≫PJyOL#2noO42?ak;#zU>=*qI;o{}k?8jYM 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�-h@x9GW3t>NHF#%4~dx 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 literal 0 HcmV?d00001 diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index 766372325..ee8b1a742 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -151,8 +151,65 @@ TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_pbrSpecularGlossiness) { EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf", true)); } +void VerifyClearCoatScene(const aiScene *scene) { + ASSERT_NE(nullptr, scene); + + ASSERT_TRUE(scene->HasMaterials()); + + // Find a specific Clearcoat material and check the values + const aiString partial_coated("Partial_Coated"); + bool found_partial_coat = false; + for (size_t i = 0; i < scene->mNumMaterials; ++i) { + const aiMaterial *material = scene->mMaterials[i]; + ASSERT_NE(nullptr, material); + if (material->GetName() == partial_coated) { + found_partial_coat = true; + + ai_real clearcoat_factor(0.0f); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_CLEARCOAT_FACTOR, clearcoat_factor)); + EXPECT_EQ(ai_real(1.0f), clearcoat_factor); + + ai_real clearcoat_rough_factor(0.0f); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR, clearcoat_rough_factor)); + EXPECT_EQ(ai_real(0.03f), clearcoat_rough_factor); + + // Should import the texture as diffuse and as base color + aiString path; + std::array modes; + static const std::array exp_modes = { aiTextureMapMode_Wrap, aiTextureMapMode_Wrap }; + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_CLEARCOAT_TEXTURE, &path, nullptr, nullptr, + nullptr, nullptr, modes.data())); + EXPECT_STREQ(path.C_Str(), "PartialCoating.png"); + EXPECT_EQ(exp_modes, modes); + } + } + EXPECT_TRUE(found_partial_coat); +} + +TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_clearcoat) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest.gltf", aiProcess_ValidateDataStructure); + VerifyClearCoatScene(scene); +} + #ifndef ASSIMP_BUILD_NO_EXPORT +TEST_F(utglTF2ImportExport, importglTF2AndExport_KHR_materials_clearcoat) { + { + Assimp::Importer importer; + Assimp::Exporter exporter; + const aiScene* scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest.gltf", aiProcess_ValidateDataStructure); + ASSERT_NE(nullptr, scene); + // Export + EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest_out.glb")); + } + + // And re-import + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest_out.glb", aiProcess_ValidateDataStructure); + VerifyClearCoatScene(scene); +} + TEST_F(utglTF2ImportExport, importglTF2AndExport_KHR_materials_pbrSpecularGlossiness) { Assimp::Importer importer; Assimp::Exporter exporter; From 230f367ef92c9b9d187a64bf8308e6a56b12ade1 Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Fri, 11 Jun 2021 15:17:25 +0100 Subject: [PATCH 159/335] Fix glTFv2 texcoord/uv mapping Use the standard property to indicate the UV map index --- code/AssetLib/glTF2/glTF2Exporter.cpp | 21 ++++++----- code/AssetLib/glTF2/glTF2Exporter.h | 2 +- code/AssetLib/glTF2/glTF2Importer.cpp | 3 +- include/assimp/material.h | 4 +-- include/assimp/pbrmaterial.h | 4 +-- test/unit/utglTF2ImportExport.cpp | 50 ++++++++++++++++++++------- 6 files changed, 56 insertions(+), 28 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 3cb3891b0..7e0966aff 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -492,11 +492,14 @@ void glTF2Exporter::GetMatTexProp(const aiMaterial& mat, float& prop, const char mat.Get(textureKey.c_str(), tt, slot, prop); } -void glTF2Exporter::GetMatTex(const aiMaterial& mat, Ref& texture, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, Ref& texture, unsigned int &texCoord, aiTextureType tt, unsigned int slot = 0) { if (mat.GetTextureCount(tt) > 0) { aiString tex; + // Read texcoord (UV map index) + mat.Get(AI_MATKEY_UVWSRC(tt, slot), texCoord); + if (mat.Get(AI_MATKEY_TEXTURE(tt, slot), tex) == AI_SUCCESS) { std::string path = tex.C_Str(); @@ -572,21 +575,21 @@ void glTF2Exporter::GetMatTex(const aiMaterial& mat, TextureInfo& prop, aiTextur { Ref& texture = prop.texture; - GetMatTex(mat, texture, tt, slot); + GetMatTex(mat, texture, prop.texCoord, tt, slot); - if (texture) { - GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); - } + //if (texture) { + // GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); + //} } void glTF2Exporter::GetMatTex(const aiMaterial& mat, NormalTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) { Ref& texture = prop.texture; - GetMatTex(mat, texture, tt, slot); + GetMatTex(mat, texture, prop.texCoord, tt, slot); if (texture) { - GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); + //GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); GetMatTexProp(mat, prop.scale, "scale", tt, slot); } } @@ -595,10 +598,10 @@ void glTF2Exporter::GetMatTex(const aiMaterial& mat, OcclusionTextureInfo& prop, { Ref& texture = prop.texture; - GetMatTex(mat, texture, tt, slot); + GetMatTex(mat, texture, prop.texCoord, tt, slot); if (texture) { - GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); + //GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); GetMatTexProp(mat, prop.strength, "strength", tt, slot); } } diff --git a/code/AssetLib/glTF2/glTF2Exporter.h b/code/AssetLib/glTF2/glTF2Exporter.h index edc85d998..f5238297f 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.h +++ b/code/AssetLib/glTF2/glTF2Exporter.h @@ -104,7 +104,7 @@ namespace Assimp void GetTexSampler(const aiMaterial& mat, glTF2::Ref texture, aiTextureType tt, unsigned int slot); void GetMatTexProp(const aiMaterial& mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int idx); void GetMatTexProp(const aiMaterial& mat, float& prop, const char* propName, aiTextureType tt, unsigned int idx); - void GetMatTex(const aiMaterial& mat, glTF2::Ref& texture, aiTextureType tt, unsigned int slot); + void GetMatTex(const aiMaterial& mat, glTF2::Ref& texture, unsigned int &texCoord, aiTextureType tt, unsigned int slot); void GetMatTex(const aiMaterial& mat, glTF2::TextureInfo& prop, aiTextureType tt, unsigned int slot); void GetMatTex(const aiMaterial& mat, glTF2::NormalTextureInfo& prop, aiTextureType tt, unsigned int slot); void GetMatTex(const aiMaterial& mat, glTF2::OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot); diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index b435f111d..aadc9fb16 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -165,7 +165,8 @@ inline void SetMaterialTextureProperty(std::vector &embeddedTexIdxs, Asset } mat->AddProperty(&uri, AI_MATKEY_TEXTURE(texType, texSlot)); - mat->AddProperty(&prop.texCoord, 1, AI_MATKEY_GLTF_TEXTURE_TEXCOORD(texType, texSlot)); + const int uvIndex = static_cast(prop.texCoord); + mat->AddProperty(&uvIndex, 1, AI_MATKEY_UVWSRC(texType, texSlot)); if (prop.textureTransformSupported) { aiUVTransform transform; diff --git a/include/assimp/material.h b/include/assimp/material.h index f348da369..2024be07f 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -144,9 +144,7 @@ enum aiTextureMapMode { enum aiTextureMapping { /** The mapping coordinates are taken from an UV channel. * - * #AI_MATKEY_UVWSRC property - * - * Specifies from which UV channel + * #AI_MATKEY_UVWSRC property specifies from which UV channel * the texture coordinates are to be taken from (remember, * meshes can have more than one UV channel). */ diff --git a/include/assimp/pbrmaterial.h b/include/assimp/pbrmaterial.h index c67bcc3b8..93e7e3095 100644 --- a/include/assimp/pbrmaterial.h +++ b/include/assimp/pbrmaterial.h @@ -75,7 +75,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR "$mat.gltf.materialTransmission.transmissionFactor", 0, 0 //#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE aiTextureType_UNKNOWN, 5 -#define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE "$tex.file.texCoord" +//#define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE "$tex.file.texCoord" #define _AI_MATKEY_GLTF_MAPPINGNAME_BASE "$tex.mappingname" #define _AI_MATKEY_GLTF_MAPPINGID_BASE "$tex.mappingid" #define _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE "$tex.mappingfiltermag" @@ -83,7 +83,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define _AI_MATKEY_GLTF_SCALE_BASE "$tex.scale" #define _AI_MATKEY_GLTF_STRENGTH_BASE "$tex.strength" -#define AI_MATKEY_GLTF_TEXTURE_TEXCOORD(type, N) _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, type, N +//#define AI_MATKEY_GLTF_TEXTURE_TEXCOORD(type, N) _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, type, N #define AI_MATKEY_GLTF_MAPPINGNAME(type, N) _AI_MATKEY_GLTF_MAPPINGNAME_BASE, type, N #define AI_MATKEY_GLTF_MAPPINGID(type, N) _AI_MATKEY_GLTF_MAPPINGID_BASE, type, N #define AI_MATKEY_GLTF_MAPPINGFILTER_MAG(type, N) _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE, type, N diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index ee8b1a742..2c000bb37 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -603,32 +603,58 @@ TEST_F(utglTF2ImportExport, sceneMetadata) { } TEST_F(utglTF2ImportExport, texcoords) { + Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", - aiProcess_ValidateDataStructure); + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", aiProcess_ValidateDataStructure); + ASSERT_NE(scene, nullptr); + ASSERT_TRUE(scene->HasMaterials()); + const aiMaterial *material = scene->mMaterials[0]; + + aiString path; + unsigned int uvIndex = 255; + aiTextureMapMode modes[2]; + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_BASE_COLOR_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); + EXPECT_STREQ(path.C_Str(), "texture.png"); + EXPECT_EQ(uvIndex, 0); + + uvIndex = 255; + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); + EXPECT_STREQ(path.C_Str(), "texture.png"); + EXPECT_EQ(uvIndex, 1); +} + +#ifndef ASSIMP_BUILD_NO_EXPORT + +TEST_F(utglTF2ImportExport, texcoords_export) { + { + Assimp::Importer importer; + Assimp::Exporter exporter; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", aiProcess_ValidateDataStructure); + ASSERT_NE(scene, nullptr); + ASSERT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf_out.glb")); + } + + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", aiProcess_ValidateDataStructure); ASSERT_NE(scene, nullptr); ASSERT_TRUE(scene->HasMaterials()); const aiMaterial *material = scene->mMaterials[0]; aiString path; + unsigned int uvIndex = 255; aiTextureMapMode modes[2]; - EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_DIFFUSE, 0, &path, nullptr, nullptr, - nullptr, nullptr, modes)); + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_BASE_COLOR_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); EXPECT_STREQ(path.C_Str(), "texture.png"); - - int uvIndex = -1; - EXPECT_EQ(aiGetMaterialInteger(material, AI_MATKEY_GLTF_TEXTURE_TEXCOORD(aiTextureType_DIFFUSE, 0), &uvIndex), aiReturn_SUCCESS); EXPECT_EQ(uvIndex, 0); - // Using manual macro expansion of AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE here. - // The following works with some but not all compilers: - // #define APPLY(X, Y) X(Y) - // ..., APPLY(AI_MATKEY_GLTF_TEXTURE_TEXCOORD, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE), ... - EXPECT_EQ(aiGetMaterialInteger(material, AI_MATKEY_GLTF_TEXTURE_TEXCOORD(aiTextureType_UNKNOWN, 0), &uvIndex), aiReturn_SUCCESS); + uvIndex = 255; + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); + EXPECT_STREQ(path.C_Str(), "texture.png"); EXPECT_EQ(uvIndex, 1); } +#endif // ASSIMP_BUILD_NO_EXPORT TEST_F(utglTF2ImportExport, recursive_nodes) { Assimp::Importer importer; const aiScene* scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/RecursiveNodes/RecursiveNodes.gltf", aiProcess_ValidateDataStructure); From fbf75963dc73c4bb70001f358989ed8ebb0ce6ac Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Fri, 11 Jun 2021 15:25:02 +0100 Subject: [PATCH 160/335] Fix typo Thank you clang! --- code/AssetLib/glTF2/glTF2Exporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 7e0966aff..40ba41c20 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -655,7 +655,7 @@ bool glTF2Exporter::GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlo // Add any appropriate textures GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); - result == result || pbrSG.specularGlossinessTexture.texture; + result = result || pbrSG.specularGlossinessTexture.texture; if (result) { // Likely to always have diffuse From 36c8cdf3dee0f846eef2bd82e8564925ef2077bc Mon Sep 17 00:00:00 2001 From: Evangel Date: Sat, 12 Jun 2021 11:44:28 +1000 Subject: [PATCH 161/335] Add scene metadata for glTF2 files as allowed by the glTF2 specification. --- code/AssetLib/glTF2/glTF2Asset.h | 2 ++ code/AssetLib/glTF2/glTF2Asset.inl | 3 +++ code/AssetLib/glTF2/glTF2Importer.cpp | 6 +++++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index 6aa0f92ed..b1fba7bd5 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -922,6 +922,8 @@ struct Scene : public Object { std::string name; std::vector> nodes; + CustomExtension extensions; + Scene() {} void Read(Value &obj, Asset &r); }; diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index c4b1e72ee..14088353c 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -1803,6 +1803,9 @@ inline void Scene::Read(Value &obj, Asset &r) { this->nodes.push_back(node); } } + if (Value *extensions = FindObject(obj, "extensions")) { + this->extensions = ReadExtensions("extensions", *extensions); + } } inline void Skin::Read(Value &obj, Asset &r) { diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index c62989c3b..9ba6270ed 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1498,7 +1498,8 @@ void glTF2Importer::ImportCommonMetadata(glTF2::Asset& a) { const bool hasVersion = !a.asset.version.empty(); const bool hasGenerator = !a.asset.generator.empty(); const bool hasCopyright = !a.asset.copyright.empty(); - if (hasVersion || hasGenerator || hasCopyright) { + const bool hasSceneMetadata = a.scene->extensions; + if (hasVersion || hasGenerator || hasCopyright || hasSceneMetadata) { mScene->mMetaData = new aiMetadata; if (hasVersion) { mScene->mMetaData->Add(AI_METADATA_SOURCE_FORMAT_VERSION, aiString(a.asset.version)); @@ -1509,6 +1510,9 @@ void glTF2Importer::ImportCommonMetadata(glTF2::Asset& a) { if (hasCopyright) { mScene->mMetaData->Add(AI_METADATA_SOURCE_COPYRIGHT, aiString(a.asset.copyright)); } + if (hasSceneMetadata) { + ParseExtensions(mScene->mMetaData, a.scene->extensions); + } } } From 5be2330fbb969e8e8f669fc2f225cd6cb594674c Mon Sep 17 00:00:00 2001 From: Evangel Date: Sat, 12 Jun 2021 12:20:40 +1000 Subject: [PATCH 162/335] Added CustomExtension to glTF2::Object so that all subclasses have it instead of doing it piecemeal. --- code/AssetLib/glTF2/glTF2Asset.h | 95 ++++++++++++++++---------------- 1 file changed, 47 insertions(+), 48 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index b1fba7bd5..4aec48586 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -356,6 +356,51 @@ struct Nullable { isPresent(true) {} }; +struct CustomExtension { + // + // A struct containing custom extension data added to a glTF2 file + // Has to contain Object, Array, String, Double, Uint64, and Int64 at a minimum + // String, Double, Uint64, and Int64 are stored in the Nullables + // Object and Array are stored in the std::vector + // + std::string name; + + Nullable mStringValue; + Nullable mDoubleValue; + Nullable mUint64Value; + Nullable mInt64Value; + Nullable mBoolValue; + + // std::vector handles both Object and Array + Nullable> mValues; + + operator bool() const { + return Size() != 0; + } + + size_t Size() const { + if (mValues.isPresent) { + return mValues.value.size(); + } else if (mStringValue.isPresent || mDoubleValue.isPresent || mUint64Value.isPresent || mInt64Value.isPresent || mBoolValue.isPresent) { + return 1; + } + return 0; + } + + CustomExtension() = default; + + CustomExtension(const CustomExtension &other) + : name(other.name) + , mStringValue(other.mStringValue) + , mDoubleValue(other.mDoubleValue) + , mUint64Value(other.mUint64Value) + , mInt64Value(other.mInt64Value) + , mBoolValue(other.mBoolValue) + , mValues(other.mValues) + { + } +}; + //! Base class for all glTF top-level objects struct Object { int index; //!< The index of this object within its property container @@ -363,6 +408,8 @@ struct Object { std::string id; //!< The globally unique ID used to reference this object std::string name; //!< The user-defined name of this object + CustomExtension extensions; + //! Objects marked as special are not exported (used to emulate the binary body buffer) virtual bool IsSpecial() const { return false; } @@ -834,50 +881,6 @@ struct Mesh : public Object { void Read(Value &pJSON_Object, Asset &pAsset_Root); }; -struct CustomExtension : public Object { - // - // A struct containing custom extension data added to a glTF2 file - // Has to contain Object, Array, String, Double, Uint64, and Int64 at a minimum - // String, Double, Uint64, and Int64 are stored in the Nullables - // Object and Array are stored in the std::vector - // - - Nullable mStringValue; - Nullable mDoubleValue; - Nullable mUint64Value; - Nullable mInt64Value; - Nullable mBoolValue; - - // std::vector handles both Object and Array - Nullable> mValues; - - operator bool() const { - return Size() != 0; - } - - size_t Size() const { - if (mValues.isPresent) { - return mValues.value.size(); - } else if (mStringValue.isPresent || mDoubleValue.isPresent || mUint64Value.isPresent || mInt64Value.isPresent || mBoolValue.isPresent) { - return 1; - } - return 0; - } - - CustomExtension() = default; - - CustomExtension(const CustomExtension &other) - : Object(other) - , mStringValue(other.mStringValue) - , mDoubleValue(other.mDoubleValue) - , mUint64Value(other.mUint64Value) - , mInt64Value(other.mInt64Value) - , mBoolValue(other.mBoolValue) - , mValues(other.mValues) - { - } -}; - struct Node : public Object { std::vector> children; std::vector> meshes; @@ -896,8 +899,6 @@ struct Node : public Object { Ref parent; //!< This is not part of the glTF specification. Used as a helper. - CustomExtension extensions; - Node() {} void Read(Value &obj, Asset &r); }; @@ -922,8 +923,6 @@ struct Scene : public Object { std::string name; std::vector> nodes; - CustomExtension extensions; - Scene() {} void Read(Value &obj, Asset &r); }; From 7f0efa0866920ec15dd5e268befb67125ff7f6c0 Mon Sep 17 00:00:00 2001 From: Evangel Date: Sat, 12 Jun 2021 12:50:44 +1000 Subject: [PATCH 163/335] Added ReadExtensions to glTF2::Object, so all objects now have their extensions read. Importer is the only place that needs to be modified to make them available on the Assimp side now. --- code/AssetLib/glTF2/glTF2Asset.h | 2 + code/AssetLib/glTF2/glTF2Asset.inl | 85 ++++++++++++++++-------------- 2 files changed, 46 insertions(+), 41 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index 4aec48586..759411b13 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -424,6 +424,8 @@ struct Object { inline Value *FindArray(Value &val, const char *id); inline Value *FindObject(Value &val, const char *id); inline Value *FindExtension(Value &val, const char *extensionId); + + inline void ReadExtensions(Value &val); }; // diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 135def597..24fc48b3e 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -304,6 +304,43 @@ inline Value *FindObject(Document &doc, const char *memberId) { inline Value *FindExtension(Value &val, const char *extensionId) { return FindExtensionInContext(val, extensionId, "the document"); } + +inline CustomExtension ReadExtensions(const char *name, Value &obj) { + CustomExtension ret; + ret.name = name; + if (obj.IsObject()) { + ret.mValues.isPresent = true; + for (auto it = obj.MemberBegin(); it != obj.MemberEnd(); ++it) { + auto &val = it->value; + ret.mValues.value.push_back(ReadExtensions(it->name.GetString(), val)); + } + } else if (obj.IsArray()) { + ret.mValues.value.reserve(obj.Size()); + ret.mValues.isPresent = true; + for (unsigned int i = 0; i < obj.Size(); ++i) { + ret.mValues.value.push_back(ReadExtensions(name, obj[i])); + } + } else if (obj.IsNumber()) { + if (obj.IsUint64()) { + ret.mUint64Value.value = obj.GetUint64(); + ret.mUint64Value.isPresent = true; + } else if (obj.IsInt64()) { + ret.mInt64Value.value = obj.GetInt64(); + ret.mInt64Value.isPresent = true; + } else if (obj.IsDouble()) { + ret.mDoubleValue.value = obj.GetDouble(); + ret.mDoubleValue.isPresent = true; + } + } else if (obj.IsString()) { + ReadValue(obj, ret.mStringValue); + ret.mStringValue.isPresent = true; + } else if (obj.IsBool()) { + ret.mBoolValue.value = obj.GetBool(); + ret.mBoolValue.isPresent = true; + } + return ret; +} + } // namespace inline Value *Object::FindString(Value &val, const char *memberId) { @@ -330,6 +367,12 @@ inline Value *Object::FindExtension(Value &val, const char *extensionId) { return FindExtensionInContext(val, extensionId, id.c_str(), name.c_str()); } +inline void Object::ReadExtensions(Value &val) { + if (Value *extensions = FindObject(val, "extensions")) { + this->extensions = glTF2::ReadExtensions("extensions", *extensions); + } +} + #ifdef ASSIMP_ENABLE_DRACO template @@ -569,6 +612,7 @@ Ref LazyDict::Retrieve(unsigned int i) { inst->oIndex = i; ReadMember(obj, "name", inst->name); inst->Read(obj, mAsset); + inst->ReadExtensions(obj); Ref result = Add(inst.release()); mRecursiveReferenceCheck.erase(i); @@ -1683,42 +1727,6 @@ inline void Light::Read(Value &obj, Asset & /*r*/) { } } -inline CustomExtension ReadExtensions(const char *name, Value &obj) { - CustomExtension ret; - ret.name = name; - if (obj.IsObject()) { - ret.mValues.isPresent = true; - for (auto it = obj.MemberBegin(); it != obj.MemberEnd(); ++it) { - auto &val = it->value; - ret.mValues.value.push_back(ReadExtensions(it->name.GetString(), val)); - } - } else if (obj.IsArray()) { - ret.mValues.value.reserve(obj.Size()); - ret.mValues.isPresent = true; - for (unsigned int i = 0; i < obj.Size(); ++i) { - ret.mValues.value.push_back(ReadExtensions(name, obj[i])); - } - } else if (obj.IsNumber()) { - if (obj.IsUint64()) { - ret.mUint64Value.value = obj.GetUint64(); - ret.mUint64Value.isPresent = true; - } else if (obj.IsInt64()) { - ret.mInt64Value.value = obj.GetInt64(); - ret.mInt64Value.isPresent = true; - } else if (obj.IsDouble()) { - ret.mDoubleValue.value = obj.GetDouble(); - ret.mDoubleValue.isPresent = true; - } - } else if (obj.IsString()) { - ReadValue(obj, ret.mStringValue); - ret.mStringValue.isPresent = true; - } else if (obj.IsBool()) { - ret.mBoolValue.value = obj.GetBool(); - ret.mBoolValue.isPresent = true; - } - return ret; -} - inline void Node::Read(Value &obj, Asset &r) { if (name.empty()) { name = id; @@ -1775,8 +1783,6 @@ inline void Node::Read(Value &obj, Asset &r) { Value *curExtensions = FindObject(obj, "extensions"); if (nullptr != curExtensions) { - this->extensions = ReadExtensions("extensions", *curExtensions); - if (r.extensionsUsed.KHR_lights_punctual) { if (Value *ext = FindObject(*curExtensions, "KHR_lights_punctual")) { Value *curLight = FindUInt(*ext, "light"); @@ -1805,9 +1811,6 @@ inline void Scene::Read(Value &obj, Asset &r) { this->nodes.push_back(node); } } - if (Value *extensions = FindObject(obj, "extensions")) { - this->extensions = ReadExtensions("extensions", *extensions); - } } inline void Skin::Read(Value &obj, Asset &r) { From a7a30baf27fd0e9dac8e512aff1c4a37cefa93eb Mon Sep 17 00:00:00 2001 From: Evangel Date: Sat, 12 Jun 2021 13:08:14 +1000 Subject: [PATCH 164/335] Renamed local variable to avoid shadowing member variable. --- code/AssetLib/glTF2/glTF2Asset.inl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 24fc48b3e..8175ab428 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -368,8 +368,8 @@ inline Value *Object::FindExtension(Value &val, const char *extensionId) { } inline void Object::ReadExtensions(Value &val) { - if (Value *extensions = FindObject(val, "extensions")) { - this->extensions = glTF2::ReadExtensions("extensions", *extensions); + if (Value *curExtensions = FindObject(val, "extensions")) { + this->extensions = glTF2::ReadExtensions("extensions", *curExtensions); } } From 3de20af3cc214c2459bce5013be61518ef49d7fc Mon Sep 17 00:00:00 2001 From: Evangel Date: Sat, 12 Jun 2021 13:16:53 +1000 Subject: [PATCH 165/335] Renamed glTF2::Object::extensions to customExtensions to avoid shadowing in other subclasses. --- code/AssetLib/glTF2/glTF2Asset.h | 2 +- code/AssetLib/glTF2/glTF2Asset.inl | 2 +- code/AssetLib/glTF2/glTF2Importer.cpp | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index 759411b13..25e917712 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -408,7 +408,7 @@ struct Object { std::string id; //!< The globally unique ID used to reference this object std::string name; //!< The user-defined name of this object - CustomExtension extensions; + CustomExtension customExtensions; //! Objects marked as special are not exported (used to emulate the binary body buffer) virtual bool IsSpecial() const { return false; } diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 8175ab428..f88ea359d 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -369,7 +369,7 @@ inline Value *Object::FindExtension(Value &val, const char *extensionId) { inline void Object::ReadExtensions(Value &val) { if (Value *curExtensions = FindObject(val, "extensions")) { - this->extensions = glTF2::ReadExtensions("extensions", *curExtensions); + this->customExtensions = glTF2::ReadExtensions("extensions", *curExtensions); } } diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index 9ba6270ed..8ccc6b058 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -489,7 +489,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { "\" does not match the vertex count"); continue; } - + auto componentType = attr.color[c]->componentType; if (componentType == glTF2::ComponentType_FLOAT) { attr.color[c]->ExtractData(aim->mColors[c]); @@ -1002,9 +1002,9 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector & } } - if (node.extensions) { + if (node.customExtensions) { ainode->mMetaData = new aiMetadata; - ParseExtensions(ainode->mMetaData, node.extensions); + ParseExtensions(ainode->mMetaData, node.customExtensions); } GetNodeTransform(ainode->mTransformation, node); @@ -1498,7 +1498,7 @@ void glTF2Importer::ImportCommonMetadata(glTF2::Asset& a) { const bool hasVersion = !a.asset.version.empty(); const bool hasGenerator = !a.asset.generator.empty(); const bool hasCopyright = !a.asset.copyright.empty(); - const bool hasSceneMetadata = a.scene->extensions; + const bool hasSceneMetadata = a.scene->customExtensions; if (hasVersion || hasGenerator || hasCopyright || hasSceneMetadata) { mScene->mMetaData = new aiMetadata; if (hasVersion) { @@ -1511,7 +1511,7 @@ void glTF2Importer::ImportCommonMetadata(glTF2::Asset& a) { mScene->mMetaData->Add(AI_METADATA_SOURCE_COPYRIGHT, aiString(a.asset.copyright)); } if (hasSceneMetadata) { - ParseExtensions(mScene->mMetaData, a.scene->extensions); + ParseExtensions(mScene->mMetaData, a.scene->customExtensions); } } } From 064ffc625bef301a03a91a0e3ab7104466101605 Mon Sep 17 00:00:00 2001 From: Evangel Date: Mon, 14 Jun 2021 12:21:29 +1000 Subject: [PATCH 166/335] Const qualify aiMetadata::HasKey --- include/assimp/metadata.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/assimp/metadata.h b/include/assimp/metadata.h index 551a9aba4..57fedfbfc 100644 --- a/include/assimp/metadata.h +++ b/include/assimp/metadata.h @@ -432,7 +432,7 @@ struct aiMetadata { /// Check whether there is a metadata entry for the given key. /// \param [in] Key - the key value value to check for. - inline bool HasKey(const char *key) { + inline bool HasKey(const char *key) const { if (nullptr == key) { return false; } From 44763528829fec49c46b6462df0d42a6451191e1 Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Thu, 10 Jun 2021 18:13:46 +0100 Subject: [PATCH 167/335] First pass at simplifying glTFv2 PBR Removed 'core' set of GLTF-specific properties --- code/AssetLib/glTF2/glTF2Exporter.cpp | 60 ++++++++++++++------------- code/AssetLib/glTF2/glTF2Importer.cpp | 21 ++++++---- include/assimp/material.h | 40 +++++++++++++++++- include/assimp/pbrmaterial.h | 14 +++---- test/unit/utglTF2ImportExport.cpp | 55 ++++++++++++++++++++---- 5 files changed, 138 insertions(+), 52 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 751508225..dfa62372a 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -645,7 +645,7 @@ void glTF2Exporter::ExportMaterials() m->name = name; - GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE); + GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_BASE_COLOR); if (!m->pbrMetallicRoughness.baseColorTexture.texture) { //if there wasn't a baseColorTexture defined in the source, fallback to any diffuse texture @@ -654,19 +654,19 @@ void glTF2Exporter::ExportMaterials() GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE); - if (GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR) != AI_SUCCESS) { + if (GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_BASE_COLOR) != AI_SUCCESS) { // if baseColorFactor wasn't defined, then the source is likely not a metallic roughness material. //a fallback to any diffuse color should be used instead GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE); } - if (mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) { + if (mat->Get(AI_MATKEY_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) { //if metallicFactor wasn't defined, then the source is likely not a PBR file, and the metallicFactor should be 0 m->pbrMetallicRoughness.metallicFactor = 0; } // get roughness if source is gltf2 file - if (mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor) != AI_SUCCESS) { + if (mat->Get(AI_MATKEY_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor) != AI_SUCCESS) { // otherwise, try to derive and convert from specular + shininess values aiColor4D specularColor; ai_real shininess; @@ -712,36 +712,38 @@ void glTF2Exporter::ExportMaterials() } } - bool hasPbrSpecularGlossiness = false; - mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS, hasPbrSpecularGlossiness); - - if (hasPbrSpecularGlossiness) { - - if (!mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness) { - mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true; - } - + { + // If has a Specular color, use the KHR_materials_pbrSpecularGlossiness extension PbrSpecularGlossiness pbrSG; - - GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE); - GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR); - - if (mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) != AI_SUCCESS) { - float shininess; - - if (mat->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) { - pbrSG.glossinessFactor = shininess / 1000; + if (GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR) == AI_SUCCESS) { + if (!mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness) { + mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true; } + + GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE); + + // If don't have explicit glossiness then convert from roughness or shininess + if (mat->Get(AI_MATKEY_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) != AI_SUCCESS) { + float shininess; + if (mat->Get(AI_MATKEY_ROUGHNESS_FACTOR, shininess) == AI_SUCCESS) { + pbrSG.glossinessFactor = 1.0f - shininess; // Extension defines this way + } else if (mat->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) { + pbrSG.glossinessFactor = shininess / 1000; + } + } + + // Add any appropriate textures + GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE); + GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); + + m->pbrSpecularGlossiness = Nullable(pbrSG); } - - GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE); - GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); - - m->pbrSpecularGlossiness = Nullable(pbrSG); } - bool unlit; - if (mat->Get(AI_MATKEY_GLTF_UNLIT, unlit) == AI_SUCCESS && unlit) { + // glTFv2 is either PBR or Unlit + aiShadingMode shadingMode = aiShadingMode_PBR_BRDF; + mat->Get(AI_MATKEY_SHADING_MODEL, shadingMode); + if (shadingMode == aiShadingMode_Unlit) { mAsset->extensionsUsed.KHR_materials_unlit = true; m->unlit = true; } diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index c62989c3b..b5ba65857 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -238,16 +238,18 @@ static aiMaterial *ImportMaterial(std::vector &embeddedTexIdxs, Asset &r, M aimat->AddProperty(&str, AI_MATKEY_NAME); } + // Set Assimp DIFFUSE and BASE COLOR to the pbrMetallicRoughness base color and texture for backwards compatibility + // Technically should not load any pbrMetallicRoughness if extensionsRequired contains KHR_materials_pbrSpecularGlossiness SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); - SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR); + SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_BASE_COLOR); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_DIFFUSE); - SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_BASE_COLOR); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.metallicRoughnessTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE); - aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR); - aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR); + aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, AI_MATKEY_METALLIC_FACTOR); + aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, AI_MATKEY_ROUGHNESS_FACTOR); float roughnessAsShininess = 1 - mat.pbrMetallicRoughness.roughnessFactor; roughnessAsShininess *= roughnessAsShininess * 1000; @@ -268,22 +270,27 @@ static aiMaterial *ImportMaterial(std::vector &embeddedTexIdxs, Asset &r, M if (mat.pbrSpecularGlossiness.isPresent) { PbrSpecularGlossiness &pbrSG = mat.pbrSpecularGlossiness.value; - aimat->AddProperty(&mat.pbrSpecularGlossiness.isPresent, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS); SetMaterialColorProperty(r, pbrSG.diffuseFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); SetMaterialColorProperty(r, pbrSG.specularFactor, aimat, AI_MATKEY_COLOR_SPECULAR); float glossinessAsShininess = pbrSG.glossinessFactor * 1000.0f; aimat->AddProperty(&glossinessAsShininess, 1, AI_MATKEY_SHININESS); - aimat->AddProperty(&pbrSG.glossinessFactor, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR); + aimat->AddProperty(&pbrSG.glossinessFactor, 1, AI_MATKEY_GLOSSINESS_FACTOR); SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.diffuseTexture, aimat, aiTextureType_DIFFUSE); SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.specularGlossinessTexture, aimat, aiTextureType_SPECULAR); } + + // glTFv2 is either PBR or Unlit + aiShadingMode shadingMode = aiShadingMode_PBR_BRDF; if (mat.unlit) { - aimat->AddProperty(&mat.unlit, 1, AI_MATKEY_GLTF_UNLIT); + shadingMode = aiShadingMode_Unlit; } + aimat->AddProperty(&shadingMode, 1, AI_MATKEY_SHADING_MODEL); + + //KHR_materials_sheen if (mat.materialSheen.isPresent) { MaterialSheen &sheen = mat.materialSheen.value; diff --git a/include/assimp/material.h b/include/assimp/material.h index 08c0491c0..11cdef1f4 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -202,11 +202,15 @@ enum aiTextureType { /** The texture is combined with the result of the diffuse * lighting equation. + * OR + * PBR Specular/Glossiness */ aiTextureType_DIFFUSE = 1, /** The texture is combined with the result of the specular * lighting equation. + * OR + * PBR Specular/Glossiness */ aiTextureType_SPECULAR = 2, @@ -309,7 +313,9 @@ ASSIMP_API const char *TextureTypeToString(enum aiTextureType in); // --------------------------------------------------------------------------- /** @brief Defines all shading models supported by the library - * + * + * #AI_MATKEY_SHADING_MODEL + * * The list of shading modes has been taken from Blender. * See Blender documentation for more information. The API does * not distinguish between "specular" and "diffuse" shaders (thus the @@ -364,13 +370,27 @@ enum aiShadingMode { aiShadingMode_CookTorrance = 0x8, /** No shading at all. Constant light influence of 1.0. + * Also known as "Unlit" */ aiShadingMode_NoShading = 0x9, + aiShadingMode_Unlit = aiShadingMode_NoShading, // Alias /** Fresnel shading */ aiShadingMode_Fresnel = 0xa, + /** Physically-Based Rendering (PBR) shading using + * Bidirectional scattering/reflectance distribution function (BSDF/BRDF) + * There are multiple methods under this banner, and model files may provide + * data for more than one PBR-BRDF method. + * Applications should use the set of provided properties to determine which + * of their preferred PBDR methods are available + * eg: + * - If AI_MATKEY_METALLIC_FACTOR is set, then a Metallic/Roughness is available + * - If AI_MATKEY_COLOR_SPECULAR is set, then a Specular/Glossiness is available + */ + aiShadingMode_PBR_BRDF = 0xb, + #ifndef SWIG _aiShadingMode_Force32Bit = INT_MAX #endif @@ -923,11 +943,29 @@ extern "C" { // --------------------------------------------------------------------------- // PBR material support #define AI_MATKEY_USE_COLOR_MAP "$mat.useColorMap", 0, 0 + +// Metallic/Roughness Workflow +// --------------------------- +// Base color factor. Will be multiplied by final base color texture values if extant #define AI_MATKEY_BASE_COLOR "$clr.base", 0, 0 #define AI_MATKEY_USE_METALLIC_MAP "$mat.useMetallicMap", 0, 0 +// Metallic factor. 0.0 = Full Dielectric, 1.0 = Full Metal #define AI_MATKEY_METALLIC_FACTOR "$mat.metallicFactor", 0, 0 #define AI_MATKEY_USE_ROUGHNESS_MAP "$mat.useRoughnessMap", 0, 0 +// Roughness factor. 0.0 = Perfectly Smooth, 1.0 = Completely Rough #define AI_MATKEY_ROUGHNESS_FACTOR "$mat.roughnessFactor", 0, 0 + +// Specular/Glossiness Workflow +// --------------------------- +// Diffuse/Albedo Color. Note: Pure Metals have a diffuse of {0,0,0} +// AI_MATKEY_COLOR_DIFFUSE +// Specular Color +// AI_MATKEY_COLOR_SPECULAR +// Glossiness factor. 0.0 = Completely Rough, 1.0 = Perfectly Smooth +#define AI_MATKEY_GLOSSINESS_FACTOR "$mat.glossinessFactor", 0, 0 + +// Emissive +// -------- #define AI_MATKEY_USE_EMISSIVE_MAP "$mat.useEmissiveMap", 0, 0 #define AI_MATKEY_EMISSIVE_INTENSITY "$mat.emissiveIntensity", 0, 0 #define AI_MATKEY_USE_AO_MAP "$mat.useAOMap", 0, 0 diff --git a/include/assimp/pbrmaterial.h b/include/assimp/pbrmaterial.h index 2e41b8b6d..fd904e4fc 100644 --- a/include/assimp/pbrmaterial.h +++ b/include/assimp/pbrmaterial.h @@ -50,16 +50,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # pragma GCC system_header #endif -#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR "$mat.gltf.pbrMetallicRoughness.baseColorFactor", 0, 0 -#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0 -#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0 -#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE aiTextureType_DIFFUSE, 1 +//#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR "$mat.gltf.pbrMetallicRoughness.baseColorFactor", 0, 0 +//#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR "$mat.gltf.pbrMetallicRoughness.metallicFactor", 0, 0 +//#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR "$mat.gltf.pbrMetallicRoughness.roughnessFactor", 0, 0 +//#define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE aiTextureType_DIFFUSE, 1 #define AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 0 #define AI_MATKEY_GLTF_ALPHAMODE "$mat.gltf.alphaMode", 0, 0 #define AI_MATKEY_GLTF_ALPHACUTOFF "$mat.gltf.alphaCutoff", 0, 0 -#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0 -#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0 -#define AI_MATKEY_GLTF_UNLIT "$mat.gltf.unlit", 0, 0 +//#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0 +//#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0 +//#define AI_MATKEY_GLTF_UNLIT "$mat.gltf.unlit", 0, 0 #define AI_MATKEY_GLTF_MATERIAL_SHEEN "$mat.gltf.materialSheen", 0, 0 #define AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR "$mat.gltf.materialSheen.sheenColorFactor", 0, 0 #define AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR "$mat.gltf.materialSheen.sheenRoughnessFactor", 0, 0 diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index 4110edcfc..e0ac10ad5 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -57,10 +57,9 @@ using namespace Assimp; class utglTF2ImportExport : public AbstractImportExportBase { public: - virtual bool importerTest() { + virtual bool importerMatTest(const char *file, bool spec_gloss, std::array exp_modes = { aiTextureMapMode_Wrap, aiTextureMapMode_Wrap }) { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", - aiProcess_ValidateDataStructure); + const aiScene *scene = importer.ReadFile(file, aiProcess_ValidateDataStructure); EXPECT_NE(scene, nullptr); if (!scene) { return false; @@ -72,13 +71,49 @@ public: } const aiMaterial *material = scene->mMaterials[0]; + // This Material should be a PBR + aiShadingMode shadingMode; + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_SHADING_MODEL, shadingMode)); + EXPECT_EQ(aiShadingMode_PBR_BRDF, shadingMode); + + // Should import the texture as diffuse and as base color aiString path; - aiTextureMapMode modes[2]; + std::array modes; EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_DIFFUSE, 0, &path, nullptr, nullptr, - nullptr, nullptr, modes)); + nullptr, nullptr, modes.data())); EXPECT_STREQ(path.C_Str(), "CesiumLogoFlat.png"); - EXPECT_EQ(modes[0], aiTextureMapMode_Mirror); - EXPECT_EQ(modes[1], aiTextureMapMode_Clamp); + EXPECT_EQ(exp_modes, modes); + + // Also as Base Color + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_BASE_COLOR, 0, &path, nullptr, nullptr, + nullptr, nullptr, modes.data())); + EXPECT_STREQ(path.C_Str(), "CesiumLogoFlat.png"); + EXPECT_EQ(exp_modes, modes); + + // Should have a MetallicFactor (default is 1.0) + ai_real metal_factor = ai_real(0.5); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_METALLIC_FACTOR, metal_factor)); + EXPECT_EQ(ai_real(0.0), metal_factor); + + // And a roughness factor (default is 1.0) + ai_real roughness_factor = ai_real(0.5); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_ROUGHNESS_FACTOR, roughness_factor)); + EXPECT_EQ(ai_real(1.0), roughness_factor); + + aiColor3D spec_color = { 0, 0, 0 }; + ai_real glossiness = ai_real(0.5); + if (spec_gloss) { + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_COLOR_SPECULAR, spec_color)); + constexpr ai_real spec_val(0.20000000298023225); // From the file + EXPECT_EQ(spec_val, spec_color.r); + EXPECT_EQ(spec_val, spec_color.g); + EXPECT_EQ(spec_val, spec_color.b); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossiness)); + EXPECT_EQ(ai_real(1.0), glossiness); + } else { + EXPECT_EQ(aiReturn_FAILURE, material->Get(AI_MATKEY_COLOR_SPECULAR, spec_color)); + EXPECT_EQ(aiReturn_FAILURE, material->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossiness)); + } return true; } @@ -105,13 +140,17 @@ public: }; TEST_F(utglTF2ImportExport, importglTF2FromFileTest) { - EXPECT_TRUE(importerTest()); + EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", false, {aiTextureMapMode_Mirror, aiTextureMapMode_Clamp})); } TEST_F(utglTF2ImportExport, importBinaryglTF2FromFileTest) { EXPECT_TRUE(binaryImporterTest()); } +TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_pbrSpecularGlossiness) { + EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf", true)); +} + #ifndef ASSIMP_BUILD_NO_EXPORT TEST_F(utglTF2ImportExport, importglTF2AndExportToOBJ) { Assimp::Importer importer; From 4a66ec25d0371f694e3aa41f3a0996c24a606651 Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Fri, 11 Jun 2021 13:44:52 +0100 Subject: [PATCH 168/335] Standardise Clearcoat, Sheen and Transmission Also cleanup glTFv2 defaults, don't import/export if disabled --- code/AssetLib/glTF2/glTF2Exporter.cpp | 236 +++++++++++++++----------- code/AssetLib/glTF2/glTF2Exporter.h | 26 ++- code/AssetLib/glTF2/glTF2Importer.cpp | 39 ++--- code/Common/material.cpp | 12 +- include/assimp/material.h | 77 ++++++++- include/assimp/pbrmaterial.h | 28 +-- test/unit/utglTF2ImportExport.cpp | 15 ++ 7 files changed, 281 insertions(+), 152 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index dfa62372a..3cb3891b0 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -436,11 +436,11 @@ inline void SetSamplerWrap(SamplerWrap& wrap, aiTextureMapMode map) }; } -void glTF2Exporter::GetTexSampler(const aiMaterial* mat, Ref texture, aiTextureType tt, unsigned int slot) +void glTF2Exporter::GetTexSampler(const aiMaterial& mat, Ref texture, aiTextureType tt, unsigned int slot) { aiString aId; std::string id; - if (aiGetMaterialString(mat, AI_MATKEY_GLTF_MAPPINGID(tt, slot), &aId) == AI_SUCCESS) { + if (aiGetMaterialString(&mat, AI_MATKEY_GLTF_MAPPINGID(tt, slot), &aId) == AI_SUCCESS) { id = aId.C_Str(); } @@ -455,49 +455,49 @@ void glTF2Exporter::GetTexSampler(const aiMaterial* mat, Ref texture, a SamplerMagFilter filterMag; SamplerMinFilter filterMin; - if (aiGetMaterialInteger(mat, AI_MATKEY_MAPPINGMODE_U(tt, slot), (int*)&mapU) == AI_SUCCESS) { + if (aiGetMaterialInteger(&mat, AI_MATKEY_MAPPINGMODE_U(tt, slot), (int*)&mapU) == AI_SUCCESS) { SetSamplerWrap(texture->sampler->wrapS, mapU); } - if (aiGetMaterialInteger(mat, AI_MATKEY_MAPPINGMODE_V(tt, slot), (int*)&mapV) == AI_SUCCESS) { + if (aiGetMaterialInteger(&mat, AI_MATKEY_MAPPINGMODE_V(tt, slot), (int*)&mapV) == AI_SUCCESS) { SetSamplerWrap(texture->sampler->wrapT, mapV); } - if (aiGetMaterialInteger(mat, AI_MATKEY_GLTF_MAPPINGFILTER_MAG(tt, slot), (int*)&filterMag) == AI_SUCCESS) { + if (aiGetMaterialInteger(&mat, AI_MATKEY_GLTF_MAPPINGFILTER_MAG(tt, slot), (int*)&filterMag) == AI_SUCCESS) { texture->sampler->magFilter = filterMag; } - if (aiGetMaterialInteger(mat, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(tt, slot), (int*)&filterMin) == AI_SUCCESS) { + if (aiGetMaterialInteger(&mat, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(tt, slot), (int*)&filterMin) == AI_SUCCESS) { texture->sampler->minFilter = filterMin; } aiString name; - if (aiGetMaterialString(mat, AI_MATKEY_GLTF_MAPPINGNAME(tt, slot), &name) == AI_SUCCESS) { + if (aiGetMaterialString(&mat, AI_MATKEY_GLTF_MAPPINGNAME(tt, slot), &name) == AI_SUCCESS) { texture->sampler->name = name.C_Str(); } } } -void glTF2Exporter::GetMatTexProp(const aiMaterial* mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int slot) +void glTF2Exporter::GetMatTexProp(const aiMaterial& mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int slot) { std::string textureKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName; - mat->Get(textureKey.c_str(), tt, slot, prop); + mat.Get(textureKey.c_str(), tt, slot, prop); } -void glTF2Exporter::GetMatTexProp(const aiMaterial* mat, float& prop, const char* propName, aiTextureType tt, unsigned int slot) +void glTF2Exporter::GetMatTexProp(const aiMaterial& mat, float& prop, const char* propName, aiTextureType tt, unsigned int slot) { std::string textureKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName; - mat->Get(textureKey.c_str(), tt, slot, prop); + mat.Get(textureKey.c_str(), tt, slot, prop); } -void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, Ref& texture, aiTextureType tt, unsigned int slot = 0) { - if (mat->GetTextureCount(tt) > 0) { + if (mat.GetTextureCount(tt) > 0) { aiString tex; - if (mat->Get(AI_MATKEY_TEXTURE(tt, slot), tex) == AI_SUCCESS) { + if (mat.Get(AI_MATKEY_TEXTURE(tt, slot), tex) == AI_SUCCESS) { std::string path = tex.C_Str(); if (path.size() > 0) { @@ -568,7 +568,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe } } -void glTF2Exporter::GetMatTex(const aiMaterial* mat, TextureInfo& prop, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, TextureInfo& prop, aiTextureType tt, unsigned int slot = 0) { Ref& texture = prop.texture; @@ -579,7 +579,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, TextureInfo& prop, aiTextur } } -void glTF2Exporter::GetMatTex(const aiMaterial* mat, NormalTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, NormalTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) { Ref& texture = prop.texture; @@ -591,7 +591,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, NormalTextureInfo& prop, ai } } -void glTF2Exporter::GetMatTex(const aiMaterial* mat, OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) { Ref& texture = prop.texture; @@ -603,10 +603,10 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, OcclusionTextureInfo& prop, } } -aiReturn glTF2Exporter::GetMatColor(const aiMaterial* mat, vec4& prop, const char* propName, int type, int idx) +aiReturn glTF2Exporter::GetMatColor(const aiMaterial& mat, vec4& prop, const char* propName, int type, int idx) const { aiColor4D col; - aiReturn result = mat->Get(propName, type, idx, col); + aiReturn result = mat.Get(propName, type, idx, col); if (result == AI_SUCCESS) { prop[0] = col.r; prop[1] = col.g; prop[2] = col.b; prop[3] = col.a; @@ -615,30 +615,109 @@ aiReturn glTF2Exporter::GetMatColor(const aiMaterial* mat, vec4& prop, const cha return result; } -aiReturn glTF2Exporter::GetMatColor(const aiMaterial* mat, vec3& prop, const char* propName, int type, int idx) +aiReturn glTF2Exporter::GetMatColor(const aiMaterial& mat, vec3& prop, const char* propName, int type, int idx) const { aiColor3D col; - aiReturn result = mat->Get(propName, type, idx, col); + aiReturn result = mat.Get(propName, type, idx, col); if (result == AI_SUCCESS) { - prop[0] = col.r; prop[1] = col.g; prop[2] = col.b; + prop[0] = col.r; + prop[1] = col.g; + prop[2] = col.b; } return result; } +bool glTF2Exporter::GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlossiness &pbrSG) { + bool result = false; + // If has Glossiness, a Specular Color or Specular Texture, use the KHR_materials_pbrSpecularGlossiness extension + // NOTE: This extension is being considered for deprecation (Dec 2020), may be replaced by KHR_material_specular + + if (mat.Get(AI_MATKEY_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) == AI_SUCCESS) { + result = true; + } else { + // Don't have explicit glossiness, convert from pbr roughness or legacy shininess + float shininess; + if (mat.Get(AI_MATKEY_ROUGHNESS_FACTOR, shininess) == AI_SUCCESS) { + pbrSG.glossinessFactor = 1.0f - shininess; // Extension defines this way + } else if (mat.Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) { + pbrSG.glossinessFactor = shininess / 1000; + } + } + + if (GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR) == AI_SUCCESS) { + result = true; + } + // Add any appropriate textures + GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); + + result == result || pbrSG.specularGlossinessTexture.texture; + + if (result) { + // Likely to always have diffuse + GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE); + GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE); + } + + return result; +} + +bool glTF2Exporter::GetMatSheen(const aiMaterial &mat, glTF2::MaterialSheen &sheen) { + // Return true if got any valid Sheen properties or textures + if (GetMatColor(mat, sheen.sheenColorFactor, AI_MATKEY_SHEEN_COLOR_FACTOR) != aiReturn_SUCCESS) + return false; + + // Default Sheen color factor {0,0,0} disables Sheen, so do not export + if (sheen.sheenColorFactor == defaultSheenFactor) + return false; + + mat.Get(AI_MATKEY_SHEEN_ROUGHNESS_FACTOR, sheen.sheenRoughnessFactor); + + GetMatTex(mat, sheen.sheenColorTexture, AI_MATKEY_SHEEN_COLOR_TEXTURE); + GetMatTex(mat, sheen.sheenRoughnessTexture, AI_MATKEY_SHEEN_ROUGHNESS_TEXTURE); + + return true; +} + +bool glTF2Exporter::GetMatClearcoat(const aiMaterial &mat, glTF2::MaterialClearcoat &clearcoat) { + if (mat.Get(AI_MATKEY_CLEARCOAT_FACTOR, clearcoat.clearcoatFactor) != aiReturn_SUCCESS) { + return false; + } + + // Clearcoat factor of zero disables Clearcoat, so do not export + if (clearcoat.clearcoatFactor == 0.0f) + return false; + + mat.Get(AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR, clearcoat.clearcoatRoughnessFactor); + + GetMatTex(mat, clearcoat.clearcoatTexture, AI_MATKEY_CLEARCOAT_TEXTURE); + GetMatTex(mat, clearcoat.clearcoatRoughnessTexture, AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE); + GetMatTex(mat, clearcoat.clearcoatNormalTexture, AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE); + + return true; +} + +bool glTF2Exporter::GetMatTransmission(const aiMaterial &mat, glTF2::MaterialTransmission &transmission) { + bool result = mat.Get(AI_MATKEY_TRANSMISSION_FACTOR, transmission.transmissionFactor) == aiReturn_SUCCESS; + GetMatTex(mat, transmission.transmissionTexture, AI_MATKEY_TRANSMISSION_TEXTURE); + return result || transmission.transmissionTexture.texture; +} + void glTF2Exporter::ExportMaterials() { aiString aiName; for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) { - const aiMaterial* mat = mScene->mMaterials[i]; + ai_assert(mScene->mMaterials[i] != nullptr); + + const aiMaterial & mat = *(mScene->mMaterials[i]); std::string id = "material_" + ai_to_string(i); Ref m = mAsset->materials.Create(id); std::string name; - if (mat->Get(AI_MATKEY_NAME, aiName) == AI_SUCCESS) { + if (mat.Get(AI_MATKEY_NAME, aiName) == AI_SUCCESS) { name = aiName.C_Str(); } name = mAsset->FindUniqueID(name, "material"); @@ -660,20 +739,20 @@ void glTF2Exporter::ExportMaterials() GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE); } - if (mat->Get(AI_MATKEY_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) { + if (mat.Get(AI_MATKEY_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) { //if metallicFactor wasn't defined, then the source is likely not a PBR file, and the metallicFactor should be 0 m->pbrMetallicRoughness.metallicFactor = 0; } // get roughness if source is gltf2 file - if (mat->Get(AI_MATKEY_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor) != AI_SUCCESS) { + if (mat.Get(AI_MATKEY_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor) != AI_SUCCESS) { // otherwise, try to derive and convert from specular + shininess values aiColor4D specularColor; ai_real shininess; if ( - mat->Get(AI_MATKEY_COLOR_SPECULAR, specularColor) == AI_SUCCESS && - mat->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS + mat.Get(AI_MATKEY_COLOR_SPECULAR, specularColor) == AI_SUCCESS && + mat.Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS ) { // convert specular color to luminance float specularIntensity = specularColor[0] * 0.2125f + specularColor[1] * 0.7154f + specularColor[2] * 0.0721f; @@ -694,17 +773,17 @@ void glTF2Exporter::ExportMaterials() GetMatTex(mat, m->emissiveTexture, aiTextureType_EMISSIVE); GetMatColor(mat, m->emissiveFactor, AI_MATKEY_COLOR_EMISSIVE); - mat->Get(AI_MATKEY_TWOSIDED, m->doubleSided); - mat->Get(AI_MATKEY_GLTF_ALPHACUTOFF, m->alphaCutoff); + mat.Get(AI_MATKEY_TWOSIDED, m->doubleSided); + mat.Get(AI_MATKEY_GLTF_ALPHACUTOFF, m->alphaCutoff); aiString alphaMode; - if (mat->Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode) == AI_SUCCESS) { + if (mat.Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode) == AI_SUCCESS) { m->alphaMode = alphaMode.C_Str(); } else { float opacity; - if (mat->Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) { + if (mat.Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) { if (opacity < 1) { m->alphaMode = "BLEND"; m->pbrMetallicRoughness.baseColorFactor[3] *= opacity; @@ -713,86 +792,43 @@ void glTF2Exporter::ExportMaterials() } { - // If has a Specular color, use the KHR_materials_pbrSpecularGlossiness extension + // KHR_materials_pbrSpecularGlossiness extension + // NOTE: This extension is being considered for deprecation (Dec 2020) PbrSpecularGlossiness pbrSG; - if (GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR) == AI_SUCCESS) { - if (!mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness) { - mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true; - } - - GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE); - - // If don't have explicit glossiness then convert from roughness or shininess - if (mat->Get(AI_MATKEY_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) != AI_SUCCESS) { - float shininess; - if (mat->Get(AI_MATKEY_ROUGHNESS_FACTOR, shininess) == AI_SUCCESS) { - pbrSG.glossinessFactor = 1.0f - shininess; // Extension defines this way - } else if (mat->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) { - pbrSG.glossinessFactor = shininess / 1000; - } - } - - // Add any appropriate textures - GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE); - GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); - + if (GetMatSpecGloss(mat, pbrSG)) { + mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true; m->pbrSpecularGlossiness = Nullable(pbrSG); } } // glTFv2 is either PBR or Unlit aiShadingMode shadingMode = aiShadingMode_PBR_BRDF; - mat->Get(AI_MATKEY_SHADING_MODEL, shadingMode); + mat.Get(AI_MATKEY_SHADING_MODEL, shadingMode); if (shadingMode == aiShadingMode_Unlit) { mAsset->extensionsUsed.KHR_materials_unlit = true; m->unlit = true; - } + } else { + // These extensions are not compatible with KHR_materials_unlit or KHR_materials_pbrSpecularGlossiness + if (!m->pbrSpecularGlossiness.isPresent) { + // Sheen + MaterialSheen sheen; + if (GetMatSheen(mat, sheen)) { + mAsset->extensionsUsed.KHR_materials_sheen = true; + m->materialSheen = Nullable(sheen); + } - bool hasMaterialSheen = false; - mat->Get(AI_MATKEY_GLTF_MATERIAL_SHEEN, hasMaterialSheen); + MaterialClearcoat clearcoat; + if (GetMatClearcoat(mat, clearcoat)) { + mAsset->extensionsUsed.KHR_materials_clearcoat = true; + m->materialClearcoat = Nullable(clearcoat); + } - if (hasMaterialSheen) { - mAsset->extensionsUsed.KHR_materials_sheen = true; - - MaterialSheen sheen; - - GetMatColor(mat, sheen.sheenColorFactor, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR); - mat->Get(AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR, sheen.sheenRoughnessFactor); - GetMatTex(mat, sheen.sheenColorTexture, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_TEXTURE); - GetMatTex(mat, sheen.sheenRoughnessTexture, AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_TEXTURE); - - m->materialSheen = Nullable(sheen); - } - - bool hasMaterialClearcoat = false; - mat->Get(AI_MATKEY_GLTF_MATERIAL_CLEARCOAT, hasMaterialClearcoat); - - if (hasMaterialClearcoat) { - mAsset->extensionsUsed.KHR_materials_clearcoat= true; - - MaterialClearcoat clearcoat; - - mat->Get(AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_FACTOR, clearcoat.clearcoatFactor); - mat->Get(AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_FACTOR, clearcoat.clearcoatRoughnessFactor); - GetMatTex(mat, clearcoat.clearcoatTexture, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_TEXTURE); - GetMatTex(mat, clearcoat.clearcoatRoughnessTexture, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_TEXTURE); - GetMatTex(mat, clearcoat.clearcoatNormalTexture, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_NORMAL_TEXTURE); - - m->materialClearcoat = Nullable(clearcoat); - } - - bool hasMaterialTransmission = false; - mat->Get(AI_MATKEY_GLTF_MATERIAL_TRANSMISSION, hasMaterialTransmission); - - if (hasMaterialTransmission) { - mAsset->extensionsUsed.KHR_materials_transmission = true; - - MaterialTransmission transmission; - - mat->Get(AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR, transmission.transmissionFactor); - GetMatTex(mat, transmission.transmissionTexture, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE); - - m->materialTransmission = Nullable(transmission); + MaterialTransmission transmission; + if (GetMatTransmission(mat, transmission)) { + mAsset->extensionsUsed.KHR_materials_transmission = true; + m->materialTransmission = Nullable(transmission); + } + } } } } diff --git a/code/AssetLib/glTF2/glTF2Exporter.h b/code/AssetLib/glTF2/glTF2Exporter.h index 86497516a..edc85d998 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.h +++ b/code/AssetLib/glTF2/glTF2Exporter.h @@ -72,6 +72,10 @@ namespace glTF2 struct OcclusionTextureInfo; struct Node; struct Texture; + struct PbrSpecularGlossiness; + struct MaterialSheen; + struct MaterialClearcoat; + struct MaterialTransmission; // Vec/matrix types, as raw float arrays typedef float (vec2)[2]; @@ -97,15 +101,19 @@ namespace Assimp protected: void WriteBinaryData(IOStream* outfile, std::size_t sceneLength); - void GetTexSampler(const aiMaterial* mat, glTF2::Ref texture, aiTextureType tt, unsigned int slot); - void GetMatTexProp(const aiMaterial* mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int idx); - void GetMatTexProp(const aiMaterial* mat, float& prop, const char* propName, aiTextureType tt, unsigned int idx); - void GetMatTex(const aiMaterial* mat, glTF2::Ref& texture, aiTextureType tt, unsigned int slot); - void GetMatTex(const aiMaterial* mat, glTF2::TextureInfo& prop, aiTextureType tt, unsigned int slot); - void GetMatTex(const aiMaterial* mat, glTF2::NormalTextureInfo& prop, aiTextureType tt, unsigned int slot); - void GetMatTex(const aiMaterial* mat, glTF2::OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot); - aiReturn GetMatColor(const aiMaterial* mat, glTF2::vec4& prop, const char* propName, int type, int idx); - aiReturn GetMatColor(const aiMaterial* mat, glTF2::vec3& prop, const char* propName, int type, int idx); + void GetTexSampler(const aiMaterial& mat, glTF2::Ref texture, aiTextureType tt, unsigned int slot); + void GetMatTexProp(const aiMaterial& mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int idx); + void GetMatTexProp(const aiMaterial& mat, float& prop, const char* propName, aiTextureType tt, unsigned int idx); + void GetMatTex(const aiMaterial& mat, glTF2::Ref& texture, aiTextureType tt, unsigned int slot); + void GetMatTex(const aiMaterial& mat, glTF2::TextureInfo& prop, aiTextureType tt, unsigned int slot); + void GetMatTex(const aiMaterial& mat, glTF2::NormalTextureInfo& prop, aiTextureType tt, unsigned int slot); + void GetMatTex(const aiMaterial& mat, glTF2::OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot); + aiReturn GetMatColor(const aiMaterial& mat, glTF2::vec4& prop, const char* propName, int type, int idx) const; + aiReturn GetMatColor(const aiMaterial& mat, glTF2::vec3& prop, const char* propName, int type, int idx) const; + bool GetMatSpecGloss(const aiMaterial& mat, glTF2::PbrSpecularGlossiness& pbrSG); + bool GetMatSheen(const aiMaterial& mat, glTF2::MaterialSheen& sheen); + bool GetMatClearcoat(const aiMaterial& mat, glTF2::MaterialClearcoat& clearcoat); + bool GetMatTransmission(const aiMaterial& mat, glTF2::MaterialTransmission& transmission); void ExportMetadata(); void ExportMaterials(); void ExportMeshes(); diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index b5ba65857..b0f0955f5 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -291,36 +291,37 @@ static aiMaterial *ImportMaterial(std::vector &embeddedTexIdxs, Asset &r, M aimat->AddProperty(&shadingMode, 1, AI_MATKEY_SHADING_MODEL); - //KHR_materials_sheen + // KHR_materials_sheen if (mat.materialSheen.isPresent) { MaterialSheen &sheen = mat.materialSheen.value; - - aimat->AddProperty(&mat.materialSheen.isPresent, 1, AI_MATKEY_GLTF_MATERIAL_SHEEN); - SetMaterialColorProperty(r, sheen.sheenColorFactor, aimat, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR); - aimat->AddProperty(&sheen.sheenRoughnessFactor, 1, AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR); - SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenColorTexture, aimat, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_TEXTURE); - SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenRoughnessTexture, aimat, AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_TEXTURE); + // Default value {0,0,0} disables Sheen + if (sheen.sheenColorFactor != defaultSheenFactor) { + SetMaterialColorProperty(r, sheen.sheenColorFactor, aimat, AI_MATKEY_SHEEN_COLOR_FACTOR); + aimat->AddProperty(&sheen.sheenRoughnessFactor, 1, AI_MATKEY_SHEEN_ROUGHNESS_FACTOR); + SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenColorTexture, aimat, AI_MATKEY_SHEEN_COLOR_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenRoughnessTexture, aimat, AI_MATKEY_SHEEN_ROUGHNESS_TEXTURE); + } } - //KHR_materials_clearcoat + // KHR_materials_clearcoat if (mat.materialClearcoat.isPresent) { MaterialClearcoat &clearcoat = mat.materialClearcoat.value; - - aimat->AddProperty(&mat.materialClearcoat.isPresent, 1, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT); - aimat->AddProperty(&clearcoat.clearcoatFactor, 1, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_FACTOR); - aimat->AddProperty(&clearcoat.clearcoatRoughnessFactor, 1, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_FACTOR); - SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatTexture, aimat, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_TEXTURE); - SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatRoughnessTexture, aimat, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_TEXTURE); - SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatNormalTexture, aimat, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_NORMAL_TEXTURE); + // Default value 0.0 disables clearcoat + if (clearcoat.clearcoatFactor != 0.0f) { + aimat->AddProperty(&clearcoat.clearcoatFactor, 1, AI_MATKEY_CLEARCOAT_FACTOR); + aimat->AddProperty(&clearcoat.clearcoatRoughnessFactor, 1, AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR); + SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatTexture, aimat, AI_MATKEY_CLEARCOAT_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatRoughnessTexture, aimat, AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatNormalTexture, aimat, AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE); + } } - //KHR_materials_transmission + // KHR_materials_transmission if (mat.materialTransmission.isPresent) { MaterialTransmission &transmission = mat.materialTransmission.value; - aimat->AddProperty(&mat.materialTransmission.isPresent, 1, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION); - aimat->AddProperty(&transmission.transmissionFactor, 1, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR); - SetMaterialTextureProperty(embeddedTexIdxs, r, transmission.transmissionTexture, aimat, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE); + aimat->AddProperty(&transmission.transmissionFactor, 1, AI_MATKEY_TRANSMISSION_FACTOR); + SetMaterialTextureProperty(embeddedTexIdxs, r, transmission.transmissionTexture, aimat, AI_MATKEY_TRANSMISSION_TEXTURE); } return aimat; diff --git a/code/Common/material.cpp b/code/Common/material.cpp index 5230c8b43..6c90e66f0 100644 --- a/code/Common/material.cpp +++ b/code/Common/material.cpp @@ -47,10 +47,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include // ------------------------------------------------------------------------------- -const char* TextureTypeToString(aiTextureType in) -{ - switch (in) - { +const char *TextureTypeToString(aiTextureType in) { + switch (in) { case aiTextureType_NONE: return "n/a"; case aiTextureType_DIFFUSE: @@ -87,6 +85,12 @@ const char* TextureTypeToString(aiTextureType in) return "DiffuseRoughness"; case aiTextureType_AMBIENT_OCCLUSION: return "AmbientOcclusion"; + case aiTextureType_SHEEN: + return "Sheen"; + case aiTextureType_CLEARCOAT: + return "Clearcoat"; + case aiTextureType_TRANSMISSION: + return "Transmission"; case aiTextureType_UNKNOWN: return "Unknown"; default: diff --git a/include/assimp/material.h b/include/assimp/material.h index 11cdef1f4..33e39529e 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -144,7 +144,9 @@ enum aiTextureMapMode { enum aiTextureMapping { /** The mapping coordinates are taken from an UV channel. * - * The #AI_MATKEY_UVWSRC key specifies from which UV channel + * #AI_MATKEY_UVWSRC property + * + * Specifies from which UV channel * the texture coordinates are to be taken from (remember, * meshes can have more than one UV channel). */ @@ -292,6 +294,32 @@ enum aiTextureType { aiTextureType_DIFFUSE_ROUGHNESS = 16, aiTextureType_AMBIENT_OCCLUSION = 17, + /** PBR Material Modifiers + * Some modern renderers have further PBR modifiers that may be overlaid + * on top of the 'base' PBR materials for additional realism. + * These use multiple texture maps, so only the base type is directly defined + */ + + /** Sheen + * Generally used to simulate textiles that are covered in a layer of microfibers + * eg velvet + * https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_sheen + */ + aiTextureType_SHEEN = 19, + + /** Clearcoat + * Simulates a layer of 'polish' or 'laquer' layered on top of a PBR substrate + * https://autodesk.github.io/standard-surface/#closures/coating + * https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat + */ + aiTextureType_CLEARCOAT = 20, + + /** Transmission + * Simulates transmission through the surface + * May include further information such as wall thickness + */ + aiTextureType_TRANSMISSION = 21, + /** Unknown texture * * A texture reference that does not match any of the definitions @@ -314,7 +342,7 @@ ASSIMP_API const char *TextureTypeToString(enum aiTextureType in); // --------------------------------------------------------------------------- /** @brief Defines all shading models supported by the library * - * #AI_MATKEY_SHADING_MODEL + * Property: #AI_MATKEY_SHADING_MODEL * * The list of shading modes has been taken from Blender. * See Blender documentation for more information. The API does @@ -324,6 +352,7 @@ ASSIMP_API const char *TextureTypeToString(enum aiTextureType in); * Again, this value is just a hint. Assimp tries to select the shader whose * most common implementation matches the original rendering results of the * 3D modeler which wrote a particular model as closely as possible. + * */ enum aiShadingMode { /** Flat shading. Shading is done on per-face base, @@ -384,10 +413,11 @@ enum aiShadingMode { * There are multiple methods under this banner, and model files may provide * data for more than one PBR-BRDF method. * Applications should use the set of provided properties to determine which - * of their preferred PBDR methods are available + * of their preferred PBR rendering methods are likely to be available * eg: * - If AI_MATKEY_METALLIC_FACTOR is set, then a Metallic/Roughness is available - * - If AI_MATKEY_COLOR_SPECULAR is set, then a Specular/Glossiness is available + * - If AI_MATKEY_GLOSSINESS_FACTOR is set, then a Specular/Glossiness is available + * Note that some PBR methods allow layering of techniques */ aiShadingMode_PBR_BRDF = 0xb, @@ -942,28 +972,63 @@ extern "C" { // --------------------------------------------------------------------------- // PBR material support +// -------------------- +// Properties defining PBR rendering techniques #define AI_MATKEY_USE_COLOR_MAP "$mat.useColorMap", 0, 0 // Metallic/Roughness Workflow // --------------------------- -// Base color factor. Will be multiplied by final base color texture values if extant +// Base RGBA color factor. Will be multiplied by final base color texture values if extant +// Note: Importers may choose to copy this into AI_MATKEY_COLOR_DIFFUSE for compatibility +// with renderers and formats that do not support Metallic/Roughness PBR #define AI_MATKEY_BASE_COLOR "$clr.base", 0, 0 +#define AI_MATKEY_BASE_COLOR_TEXTURE aiTextureType_BASE_COLOR, 0 #define AI_MATKEY_USE_METALLIC_MAP "$mat.useMetallicMap", 0, 0 // Metallic factor. 0.0 = Full Dielectric, 1.0 = Full Metal #define AI_MATKEY_METALLIC_FACTOR "$mat.metallicFactor", 0, 0 +#define AI_MATKEY_METALLIC_TEXTURE aiTextureType_METALNESS, 0 #define AI_MATKEY_USE_ROUGHNESS_MAP "$mat.useRoughnessMap", 0, 0 // Roughness factor. 0.0 = Perfectly Smooth, 1.0 = Completely Rough #define AI_MATKEY_ROUGHNESS_FACTOR "$mat.roughnessFactor", 0, 0 +#define AI_MATKEY_ROUGHNESS_TEXTURE aiTextureType_DIFFUSE_ROUGHNESS, 0 // Specular/Glossiness Workflow // --------------------------- // Diffuse/Albedo Color. Note: Pure Metals have a diffuse of {0,0,0} // AI_MATKEY_COLOR_DIFFUSE -// Specular Color +// Specular Color. +// Note: Metallic/Roughness may also have a Specular Color // AI_MATKEY_COLOR_SPECULAR +#define AI_MATKEY_SPECULAR_FACTOR "$mat.specularFactor", 0, 0 // Glossiness factor. 0.0 = Completely Rough, 1.0 = Perfectly Smooth #define AI_MATKEY_GLOSSINESS_FACTOR "$mat.glossinessFactor", 0, 0 +// Sheen +// ----- +// Sheen base RGB color. Default {0,0,0} +#define AI_MATKEY_SHEEN_COLOR_FACTOR "$clr.sheen.factor", 0, 0 +// Sheen Roughness Factor. +#define AI_MATKEY_SHEEN_ROUGHNESS_FACTOR "$mat.sheen.roughnessFactor", 0, 0 +#define AI_MATKEY_SHEEN_COLOR_TEXTURE aiTextureType_SHEEN, 0 +#define AI_MATKEY_SHEEN_ROUGHNESS_TEXTURE aiTextureType_SHEEN, 1 + +// Clearcoat +// --------- +#define AI_MATKEY_CLEARCOAT_FACTOR "$clr.clearcoat.factor", 0, 0 +#define AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR "$mat.clearcoat.roughnessFactor", 0, 0 +#define AI_MATKEY_CLEARCOAT_TEXTURE aiTextureType_CLEARCOAT, 0 +#define AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE aiTextureType_CLEARCOAT, 1 +#define AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE aiTextureType_CLEARCOAT, 2 + +// Transmission +// ------------ +// https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission +// Base percentage of light transmitted through the surface. 0.0 = Opaque, 1.0 = Fully transparent +#define AI_MATKEY_TRANSMISSION_FACTOR "$mat.transmission.factor", 0, 0 +// Texture defining percentage of light transmitted through the surface. +// Multiplied by AI_MATKEY_TRANSMISSION_FACTOR +#define AI_MATKEY_TRANSMISSION_TEXTURE aiTextureType_TRANSMISSION, 0 + // Emissive // -------- #define AI_MATKEY_USE_EMISSIVE_MAP "$mat.useEmissiveMap", 0, 0 diff --git a/include/assimp/pbrmaterial.h b/include/assimp/pbrmaterial.h index fd904e4fc..c67bcc3b8 100644 --- a/include/assimp/pbrmaterial.h +++ b/include/assimp/pbrmaterial.h @@ -60,20 +60,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS "$mat.gltf.pbrSpecularGlossiness", 0, 0 //#define AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR "$mat.gltf.pbrMetallicRoughness.glossinessFactor", 0, 0 //#define AI_MATKEY_GLTF_UNLIT "$mat.gltf.unlit", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_SHEEN "$mat.gltf.materialSheen", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR "$mat.gltf.materialSheen.sheenColorFactor", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR "$mat.gltf.materialSheen.sheenRoughnessFactor", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_TEXTURE aiTextureType_UNKNOWN, 1 -#define AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 2 -#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT "$mat.gltf.materialClearcoat", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_FACTOR "$mat.gltf.materialClearcoat.clearcoatFactor", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_FACTOR "$mat.gltf.materialClearcoat.clearcoatRoughnessFactor", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_TEXTURE aiTextureType_UNKNOWN, 3 -#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 4 -#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_NORMAL_TEXTURE aiTextureType_NORMALS, 1 -#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION "$mat.gltf.materialTransmission", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR "$mat.gltf.materialTransmission.transmissionFactor", 0, 0 -#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE aiTextureType_UNKNOWN, 5 +//#define AI_MATKEY_GLTF_MATERIAL_SHEEN "$mat.gltf.materialSheen", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR "$mat.gltf.materialSheen.sheenColorFactor", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR "$mat.gltf.materialSheen.sheenRoughnessFactor", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_TEXTURE aiTextureType_UNKNOWN, 1 +//#define AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 2 +//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT "$mat.gltf.materialClearcoat", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_FACTOR "$mat.gltf.materialClearcoat.clearcoatFactor", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_FACTOR "$mat.gltf.materialClearcoat.clearcoatRoughnessFactor", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_TEXTURE aiTextureType_UNKNOWN, 3 +//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_TEXTURE aiTextureType_UNKNOWN, 4 +//#define AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_NORMAL_TEXTURE aiTextureType_NORMALS, 1 +//#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION "$mat.gltf.materialTransmission", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR "$mat.gltf.materialTransmission.transmissionFactor", 0, 0 +//#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE aiTextureType_UNKNOWN, 5 #define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE "$tex.file.texCoord" #define _AI_MATKEY_GLTF_MAPPINGNAME_BASE "$tex.mappingname" diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index e0ac10ad5..766372325 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -152,6 +152,20 @@ TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_pbrSpecularGlossiness) { } #ifndef ASSIMP_BUILD_NO_EXPORT + +TEST_F(utglTF2ImportExport, importglTF2AndExport_KHR_materials_pbrSpecularGlossiness) { + Assimp::Importer importer; + Assimp::Exporter exporter; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf", + aiProcess_ValidateDataStructure); + EXPECT_NE(nullptr, scene); + // Export + EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured_out.glb")); + + // And re-import + EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured_out.glb", true)); +} + TEST_F(utglTF2ImportExport, importglTF2AndExportToOBJ) { Assimp::Importer importer; Assimp::Exporter exporter; @@ -169,6 +183,7 @@ TEST_F(utglTF2ImportExport, importglTF2EmbeddedAndExportToOBJ) { EXPECT_NE(nullptr, scene); EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "obj", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-Embedded/BoxTextured_out.obj")); } + #endif // ASSIMP_BUILD_NO_EXPORT TEST_F(utglTF2ImportExport, importglTF2PrimitiveModePointsWithoutIndices) { From fb039bb9ebcba96602f7ba63bcee8d481f6eccc5 Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Fri, 11 Jun 2021 14:34:42 +0100 Subject: [PATCH 169/335] Add glTFv2 Clearcoat import/export tests Uses Clearcoat model from Khronos --- code/AssetLib/glTF2/glTF2Importer.cpp | 5 + include/assimp/material.h | 3 +- .../glTF2/ClearCoat-glTF/ClearCoatLabels.png | Bin 0 -> 10270 bytes .../glTF2/ClearCoat-glTF/ClearCoatTest.bin | Bin 0 -> 50328 bytes .../glTF2/ClearCoat-glTF/ClearCoatTest.gltf | 1669 +++++++++++++++++ .../glTF2/ClearCoat-glTF/PartialCoating.png | Bin 0 -> 5077 bytes .../ClearCoat-glTF/PartialCoating_Alpha.png | Bin 0 -> 5065 bytes .../ClearCoat-glTF/PlasticWrap_normals.jpg | Bin 0 -> 144210 bytes .../glTF2/ClearCoat-glTF/RibsNormal.png | Bin 0 -> 1605 bytes .../glTF2/ClearCoat-glTF/RoughnessStripes.png | Bin 0 -> 5033 bytes test/unit/utglTF2ImportExport.cpp | 57 + 11 files changed, 1733 insertions(+), 1 deletion(-) create mode 100644 test/models/glTF2/ClearCoat-glTF/ClearCoatLabels.png create mode 100644 test/models/glTF2/ClearCoat-glTF/ClearCoatTest.bin create mode 100644 test/models/glTF2/ClearCoat-glTF/ClearCoatTest.gltf create mode 100644 test/models/glTF2/ClearCoat-glTF/PartialCoating.png create mode 100644 test/models/glTF2/ClearCoat-glTF/PartialCoating_Alpha.png create mode 100644 test/models/glTF2/ClearCoat-glTF/PlasticWrap_normals.jpg create mode 100644 test/models/glTF2/ClearCoat-glTF/RibsNormal.png create mode 100644 test/models/glTF2/ClearCoat-glTF/RoughnessStripes.png diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index b0f0955f5..b435f111d 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -208,6 +208,11 @@ inline void SetMaterialTextureProperty(std::vector &embeddedTexIdxs, Asset if (sampler->minFilter != SamplerMinFilter::UNSET) { mat->AddProperty(&sampler->minFilter, 1, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(texType, texSlot)); } + } else { + // Use glTFv2 default sampler + const aiTextureMapMode default_wrap = aiTextureMapMode_Wrap; + mat->AddProperty(&default_wrap, 1, AI_MATKEY_MAPPINGMODE_U(texType, texSlot)); + mat->AddProperty(&default_wrap, 1, AI_MATKEY_MAPPINGMODE_V(texType, texSlot)); } } } diff --git a/include/assimp/material.h b/include/assimp/material.h index 33e39529e..f348da369 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -1014,7 +1014,8 @@ extern "C" { // Clearcoat // --------- -#define AI_MATKEY_CLEARCOAT_FACTOR "$clr.clearcoat.factor", 0, 0 +// Clearcoat layer intensity. 0.0 = none (disabled) +#define AI_MATKEY_CLEARCOAT_FACTOR "$mat.clearcoat.factor", 0, 0 #define AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR "$mat.clearcoat.roughnessFactor", 0, 0 #define AI_MATKEY_CLEARCOAT_TEXTURE aiTextureType_CLEARCOAT, 0 #define AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE aiTextureType_CLEARCOAT, 1 diff --git a/test/models/glTF2/ClearCoat-glTF/ClearCoatLabels.png b/test/models/glTF2/ClearCoat-glTF/ClearCoatLabels.png new file mode 100644 index 0000000000000000000000000000000000000000..d47f2f5e1f06116fca5ddd9697001ffedcc5bd68 GIT binary patch literal 10270 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 literal 0 HcmV?d00001 diff --git a/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.bin b/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.bin new file mode 100644 index 0000000000000000000000000000000000000000..e7c6a93a4995917f931d580f7d25c94f33d1639a GIT binary patch literal 50328 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 z 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 MV6NHY-@g5S0BBn6`Tzg` literal 0 HcmV?d00001 diff --git a/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.gltf b/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.gltf new file mode 100644 index 000000000..ae12bfc11 --- /dev/null +++ b/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.gltf @@ -0,0 +1,1669 @@ +{ + "asset" : { + "generator" : "Khronos glTF Blender I/O v1.2.8 with hand-edits for clearcoat", + "version" : "2.0" + }, + "extensionsUsed": [ + "KHR_materials_clearcoat" + ], + "scene" : 0, + "scenes" : [ + { + "name" : "Scene", + "nodes" : [ + 3, + 7, + 11, + 15, + 19, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32 + ] + } + ], + "nodes" : [ + { + "mesh" : 0, + "name" : "BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 1, + "name" : "ClearCoatSample" + }, + { + "mesh" : 2, + "name" : "CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 0, + 1, + 2 + ], + "name" : "R0_SimpleCoatTest", + "translation" : [ + 0, + 5.25, + 0 + ] + }, + { + "mesh" : 3, + "name" : "R1_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 4, + "name" : "R1_ClearCoatSample" + }, + { + "mesh" : 5, + "name" : "R1_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 4, + 5, + 6 + ], + "name" : "R1_PartialCoatTest", + "translation" : [ + 0, + 3.1500000953674316, + 0 + ] + }, + { + "mesh" : 6, + "name" : "R2_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 7, + "name" : "R2_ClearCoatSample" + }, + { + "mesh" : 8, + "name" : "R2_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 8, + 9, + 10 + ], + "name" : "R2_RoughnessVariations", + "translation" : [ + 0, + 1.0499999523162842, + 0 + ] + }, + { + "mesh" : 9, + "name" : "R3_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 10, + "name" : "R3_ClearCoatSample" + }, + { + "mesh" : 11, + "name" : "R3_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 12, + 13, + 14 + ], + "name" : "R3_BaseNormals", + "translation" : [ + 0, + -1.0499999523162842, + 0 + ] + }, + { + "mesh" : 12, + "name" : "R4_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 13, + "name" : "R4_ClearCoatSample" + }, + { + "mesh" : 14, + "name" : "R4_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 16, + 17, + 18 + ], + "name" : "R4_CoatNormals", + "translation" : [ + 0, + -5.25, + 0 + ] + }, + { + "mesh" : 15, + "name" : "R5_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 16, + "name" : "R5_ClearCoatSample" + }, + { + "mesh" : 17, + "name" : "R5_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 20, + 21, + 22 + ], + "name" : "R5_SharedNormals", + "translation" : [ + 0, + -3.1500000953674316, + 0 + ] + }, + { + "mesh" : 18, + "name" : "X2_Label_CoatingOnly", + "translation" : [ + 2.0712804794311523, + 6.619500160217285, + 0 + ] + }, + { + "mesh" : 19, + "name" : "Y0_Label_SimpleCoating", + "translation" : [ + -5.3578033447265625, + 5.25, + 0 + ] + }, + { + "mesh" : 20, + "name" : "Y1_Label_PartialCoating", + "translation" : [ + -5.3578033447265625, + 3.1673200130462646, + 0 + ] + }, + { + "mesh" : 21, + "name" : "Y2_Label_Roughness", + "translation" : [ + -5.3578033447265625, + 1.1383899450302124, + 0 + ] + }, + { + "mesh" : 22, + "name" : "Y3_Label_BaseNormals", + "translation" : [ + -5.3578033447265625, + -1.099429965019226, + 0 + ] + }, + { + "mesh" : 23, + "name" : "Y4_Label_CoatNormals", + "translation" : [ + -5.3578033447265625, + -5.252500057220459, + 0 + ] + }, + { + "mesh" : 24, + "name" : "Y5_Label_SharedNormals", + "translation" : [ + -5.3578033447265625, + -3.1963000297546387, + 0 + ] + }, + { + "mesh" : 25, + "name" : "X0_Label_BaseLayer", + "translation" : [ + -2.087031602859497, + 6.616230010986328, + 0 + ] + }, + { + "mesh" : 26, + "name" : "X1_Label_Coated", + "translation" : [ + 0, + 6.614150047302246, + 0 + ] + } + ], + "materials" : [ + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Simple_Base", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.5, + 0.019999999552965164, + 0.009999999776482582, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Simple_Coated", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.5, + 0.019999999552965164, + 0.009999999776482582, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03 + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Simple_Coating", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Partial_Base", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Partial_Coated", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03, + "clearcoatTexture": { + "index": 0, + "texCoord": 0 + } + } + } + }, + { + "alphaMode" : "BLEND", + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Partial_Coating", + "pbrMetallicRoughness" : { + "baseColorTexture" : { + "index" : 1, + "texCoord" : 0 + }, + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "RoughVariations_Base", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.6000000238418579 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "RoughVariations_Coated", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.6000000238418579 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 1, + "clearcoatRoughnessTexture": { + "index": 2, + "texCoord": 0 + } + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "RoughVariations_Coating", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "metallicRoughnessTexture" : { + "index" : 2, + "texCoord" : 0 + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "BaseNorm_Base", + "normalTexture" : { + "index" : 3, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "BaseNorm_Coated", + "normalTexture" : { + "index" : 4, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012848637998104095, + 0.021861059591174126, + 0.11068868637084961, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03 + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "BaseNorm_Coating", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "CoatNorm_Base", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "CoatNorm_Coated", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03, + "clearcoatNormalTexture": { + "index": 5, + "texCoord": 0, + "scale": 1 + } + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "CoatNorm_Coating", + "normalTexture" : { + "index" : 5, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "SharedNorm_Base", + "normalTexture" : { + "index" : 6, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "SharedNorm_Coated", + "normalTexture" : { + "index" : 7, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03, + "clearcoatNormalTexture": { + "index": 7, + "texCoord": 0 + } + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "SharedNorm_Coating", + "normalTexture" : { + "index" : 8, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 1, + 1, + 1 + ], + "emissiveTexture" : { + "index" : 9, + "texCoord" : 0 + }, + "name" : "LabelMaterial", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.8999999761581421 + } + } + ], + "meshes" : [ + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 0 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 1 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 2 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 3 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 4 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 5 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 6 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 7 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 8 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 9 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 10 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 11 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 12 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 13 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 14 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 15 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 16 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 17 + } + ] + }, + { + "name" : "Labels_Mesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 4, + "NORMAL" : 5, + "TEXCOORD_0" : 6 + }, + "indices" : 7, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.001", + "primitives" : [ + { + "attributes" : { + "POSITION" : 8, + "NORMAL" : 9, + "TEXCOORD_0" : 10 + }, + "indices" : 7, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.002", + "primitives" : [ + { + "attributes" : { + "POSITION" : 11, + "NORMAL" : 12, + "TEXCOORD_0" : 13 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.003", + "primitives" : [ + { + "attributes" : { + "POSITION" : 15, + "NORMAL" : 16, + "TEXCOORD_0" : 17 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.004", + "primitives" : [ + { + "attributes" : { + "POSITION" : 18, + "NORMAL" : 19, + "TEXCOORD_0" : 20 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.005", + "primitives" : [ + { + "attributes" : { + "POSITION" : 21, + "NORMAL" : 22, + "TEXCOORD_0" : 23 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.006", + "primitives" : [ + { + "attributes" : { + "POSITION" : 24, + "NORMAL" : 25, + "TEXCOORD_0" : 26 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.007", + "primitives" : [ + { + "attributes" : { + "POSITION" : 27, + "NORMAL" : 28, + "TEXCOORD_0" : 29 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.008", + "primitives" : [ + { + "attributes" : { + "POSITION" : 30, + "NORMAL" : 31, + "TEXCOORD_0" : 32 + }, + "indices" : 7, + "material" : 18 + } + ] + } + ], + "textures" : [ + { + "source" : 0 + }, + { + "source" : 1 + }, + { + "source" : 2 + }, + { + "source" : 3 + }, + { + "source" : 3 + }, + { + "source" : 4 + }, + { + "source" : 3 + }, + { + "source" : 3 + }, + { + "source" : 3 + }, + { + "source" : 5 + } + ], + "images" : [ + { + "mimeType" : "image/png", + "name" : "PartialCoating", + "uri" : "PartialCoating.png" + }, + { + "mimeType" : "image/png", + "name" : "PartialCoating_Alpha", + "uri" : "PartialCoating_Alpha.png" + }, + { + "mimeType" : "image/png", + "name" : "RoughnessStripes", + "uri" : "RoughnessStripes.png" + }, + { + "mimeType" : "image/png", + "name" : "RibsNormal", + "uri" : "RibsNormal.png" + }, + { + "mimeType" : "image/jpeg", + "name" : "PlasticWrap_normals", + "uri" : "PlasticWrap_normals.jpg" + }, + { + "mimeType" : "image/png", + "name" : "ClearCoatLabels", + "uri" : "ClearCoatLabels.png" + } + ], + "accessors" : [ + { + "bufferView" : 0, + "componentType" : 5126, + "count" : 1113, + "max" : [ + 1, + 1, + 1.0499999523162842 + ], + "min" : [ + -1, + -1, + -0.06000000983476639 + ], + "type" : "VEC3" + }, + { + "bufferView" : 1, + "componentType" : 5126, + "count" : 1113, + "type" : "VEC3" + }, + { + "bufferView" : 2, + "componentType" : 5126, + "count" : 1113, + "type" : "VEC2" + }, + { + "bufferView" : 3, + "componentType" : 5123, + "count" : 6180, + "type" : "SCALAR" + }, + { + "bufferView" : 4, + "componentType" : 5126, + "count" : 8, + "max" : [ + 1.0280373096466064, + 0.23501670360565186, + 3.8289083903464416e-08 + ], + "min" : [ + -0.968224287033081, + -0.2350165843963623, + -0.010000125505030155 + ], + "type" : "VEC3" + }, + { + "bufferView" : 5, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 6, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 7, + "componentType" : 5123, + "count" : 12, + "type" : "SCALAR" + }, + { + "bufferView" : 8, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.23026323318481445, + 3.751463495405005e-08 + ], + "min" : [ + -2, + -0.23026317358016968, + -0.010000579059123993 + ], + "type" : "VEC3" + }, + { + "bufferView" : 9, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 10, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 11, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.2302631139755249, + 3.7514624295909016e-08 + ], + "min" : [ + -2, + -0.23026323318481445, + -0.010000428184866905 + ], + "type" : "VEC3" + }, + { + "bufferView" : 12, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 13, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 14, + "componentType" : 5123, + "count" : 12, + "type" : "SCALAR" + }, + { + "bufferView" : 15, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.22039484977722168, + 3.590687924770464e-08 + ], + "min" : [ + -2, + -0.22039473056793213, + -0.010000280104577541 + ], + "type" : "VEC3" + }, + { + "bufferView" : 16, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 17, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 18, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.21764808893203735, + 3.545937588000925e-08 + ], + "min" : [ + -2, + -0.21764802932739258, + -0.010000137612223625 + ], + "type" : "VEC3" + }, + { + "bufferView" : 19, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 20, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 21, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.20775499939918518, + 3.3847587843638394e-08 + ], + "min" : [ + -2, + -0.20775499939918518, + -0.009999996051192284 + ], + "type" : "VEC3" + }, + { + "bufferView" : 22, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 23, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 24, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.22341907024383545, + 3.6399587344249085e-08 + ], + "min" : [ + -2, + -0.22341907024383545, + -0.009999859146773815 + ], + "type" : "VEC3" + }, + { + "bufferView" : 25, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 26, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 27, + "componentType" : 5126, + "count" : 8, + "max" : [ + 0.9169960618019104, + 0.22670458257198334, + 3.69348676088066e-08 + ], + "min" : [ + -0.9199233651161194, + -0.22670456767082214, + -0.010000176727771759 + ], + "type" : "VEC3" + }, + { + "bufferView" : 28, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 29, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 30, + "componentType" : 5126, + "count" : 8, + "max" : [ + 0.8968609571456909, + 0.20853587985038757, + 3.397480696776256e-08 + ], + "min" : [ + -0.9147982001304626, + -0.2085357904434204, + -0.010000113397836685 + ], + "type" : "VEC3" + }, + { + "bufferView" : 31, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 32, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + } + ], + "bufferViews" : [ + { + "buffer" : 0, + "byteLength" : 13356, + "byteOffset" : 0 + }, + { + "buffer" : 0, + "byteLength" : 13356, + "byteOffset" : 13356 + }, + { + "buffer" : 0, + "byteLength" : 8904, + "byteOffset" : 26712 + }, + { + "buffer" : 0, + "byteLength" : 12360, + "byteOffset" : 35616 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 47976 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48072 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 48168 + }, + { + "buffer" : 0, + "byteLength" : 24, + "byteOffset" : 48232 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48256 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48352 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 48448 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48512 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48608 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 48704 + }, + { + "buffer" : 0, + "byteLength" : 24, + "byteOffset" : 48768 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48792 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48888 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 48984 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49048 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49144 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 49240 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49304 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49400 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 49496 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49560 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49656 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 49752 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49816 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49912 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 50008 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 50072 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 50168 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 50264 + } + ], + "buffers" : [ + { + "byteLength" : 50328, + "uri" : "ClearCoatTest.bin" + } + ] +} diff --git a/test/models/glTF2/ClearCoat-glTF/PartialCoating.png b/test/models/glTF2/ClearCoat-glTF/PartialCoating.png new file mode 100644 index 0000000000000000000000000000000000000000..d579bbebb8a423eedb16594a7ef715fe7521a752 GIT binary patch literal 5077 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 Q@JSA$qoJ>ky=fQyKcnMWr2qf` literal 0 HcmV?d00001 diff --git a/test/models/glTF2/ClearCoat-glTF/PartialCoating_Alpha.png b/test/models/glTF2/ClearCoat-glTF/PartialCoating_Alpha.png new file mode 100644 index 0000000000000000000000000000000000000000..b1911d9f0a303a57181d44856f3f31ea18c11063 GIT binary patch literal 5065 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 Q_!|ylXYGipvh+*(4_Os-!2kdN literal 0 HcmV?d00001 diff --git a/test/models/glTF2/ClearCoat-glTF/PlasticWrap_normals.jpg b/test/models/glTF2/ClearCoat-glTF/PlasticWrap_normals.jpg new file mode 100644 index 0000000000000000000000000000000000000000..73e6260ef7126ccb11113179c58cf6611d3f3e9d GIT binary patch literal 144210 zcmbTddpy&9{QtkM4mwFHL}gd0Tv3)p6n2#džOVOJDoNkR^>T_xnWONE@;Rbq*4 zIn0@rb2*>3a?BiOIgBl{!+XD%>+?JP@%`ib`wh3fUNvRhT{&|%F#j%c0KH#lWzblU9v1#^pwmR5Gx>>be89Wm}V zZ+Up$zT@TRAMh|RC^#hiX+&hyv*?)TFJ2}kr@Tr{dz!pljPw6`{4d9)2*&l> zx^-*U$*qoS&2NF=XRYG8_4{=<>^OTx?uPHq11BDD+;uMTO~L0)8hTe3l-wS4ZB{;b za`X^yb!h*M?0;`yPyT<6?EfCv|1++B*w(dcz~rq}gdt&#orEzV+B#P%5TW(|mBE@8 zS=JvGIDFd7SL`O?FBfPEX_wMR7@O`>;h^bu&`Yma)=B7aXdi*y(i>1J>Z1 zz1CTd`n94v@k@%hY{oRAvM!yOmqt`BfRxOCOs>p8Il6b?qs|=`>mHp9NH~CHPDbQ; z^oD7};Qa5Vbo$NjC2*@cyYwj$^JG%CMwAih&>(lyF~Q^4J92YNnA906$eL`kJ?7NQ}nL{3T+omFSO1Ea7EQ zTxSKZ_o0e>q!F98vd4Y}Z_XsjVCpFQd==<$452>{V<>~2^J#qB!-5X~DQ4yFd#eRL zee{YtiA-pEFG8mWeJ;wShuB0ZXK`FT*>#y$7;1}uAVP9Qo_nmJ9n&W9 z%!KlIG!>J9Ipm+Chwh$Od4VOo_sRCdsGROWTfQf}_nP$IDTD1KQ$tIoUujv&Au`yg z_2#Os@77RmP0}QEXm@!CKh-F{CTU7t6SNJ(xF_%PXyRhW>>q?OJe}z*9d#x>hDJp! zC*R&{li2yZPVQ*O%Fa&{l8X-unY5?d{?aG1!y~a2FX8dY4K#~gj8P*ITt6~X7};cb z$PY6$qSSpcnCjz<)DNArk@(FmO+d{zhKc+en0`%32(7!2T25EWd&|4auoC^mE|_}O zO;~+vk-_{siFsk#&v-s|0_ud^Fm+EuCW={JIL~gLW)oIaq!Zp7*?RnZAH@<+ipwsx z-u3<^4?ovFmsH^|PlOQlM0X_ne8@H8vX_B=BQZL9+Eot8Lr~{!!5Z~*R(XOPt7iq@ zXeg(h(Q};|Bv{^mA%oq@hIY1A-Pe1h$$URRlUR2b8{=d!cwv}Vc7+U9Uyd8j>gj^N zV8$FRT5q+iu@!XA;r8RJ;G4EC_w0f5Gvz61e`AsA6+!kBLfKtWTmx1~Y5dOTT4vz0vYu>_Lx!1B3foKu+rhynMZ%q} z1SUtDhCW@B!L}I3_y{@#l!3KS_`bhdAUZ^^{EN`}ye5WJ-!aj;NBXllz*qcOkiQ9~ z-|3Yl-OVzU6y%Z`Z^dA14)vs~z~e&>zc_r zt70#$&;vxQ!1umnQuh5ccv~KgSy_#WUCXB-R3#W(t5hyRMXY?-m8|&zE=-0~?vGD` zMaQ~^`-^Am6(+Jhg zP~0eXW#ZA@U(V`qK~l%`F%Y}jz4SuaR?`b6~~l7y`jt`!3FzW4P9%7`&Y-nERpCt&zbJ9|=GRiZ(OYB%peJ))q5GEEI3AM+2VT9(2^r%(Z>3wB0nS-s6MBo?zDb zFY-9mq6t?K#LfF#!gcP5PdV|1dUr>O#&{TZr)g-3b=%&C&kc!6af6AN0Uh{z6V~;pYpC zPcf_NZkKqzS*ZI?@E*1?SPBO63urDg7O z>xgW#FL()mE@@w;+~P8LGdP=V=!N{YK*+?#w@cW?|27eNawM^}zvPgTMLk*X7$&0N zcTpd5`_ch)fX`D2VISg`LsgeTWV?QlDV_UO2Gc?lg`s2iQC!!eJ>%xu?Rk3|5xpW? zC6dmL4@>5UKj^(Tx%rk*|8>orw)<7pUiBh(lrU|8_$wlyZq#tbEFA<-ABg;}UROk6)ZhT~SS>VDG9MhJlyrJh4{MWLdDWHNJb7i3Dpk

8#fXvU&U@TnHe66M)}` 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≫PJyOL#2noO42?ak;#zU>=*qI;o{}k?8jYM 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�-h@x9GW3t>NHF#%4~dx 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 literal 0 HcmV?d00001 diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index 766372325..ee8b1a742 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -151,8 +151,65 @@ TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_pbrSpecularGlossiness) { EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf", true)); } +void VerifyClearCoatScene(const aiScene *scene) { + ASSERT_NE(nullptr, scene); + + ASSERT_TRUE(scene->HasMaterials()); + + // Find a specific Clearcoat material and check the values + const aiString partial_coated("Partial_Coated"); + bool found_partial_coat = false; + for (size_t i = 0; i < scene->mNumMaterials; ++i) { + const aiMaterial *material = scene->mMaterials[i]; + ASSERT_NE(nullptr, material); + if (material->GetName() == partial_coated) { + found_partial_coat = true; + + ai_real clearcoat_factor(0.0f); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_CLEARCOAT_FACTOR, clearcoat_factor)); + EXPECT_EQ(ai_real(1.0f), clearcoat_factor); + + ai_real clearcoat_rough_factor(0.0f); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR, clearcoat_rough_factor)); + EXPECT_EQ(ai_real(0.03f), clearcoat_rough_factor); + + // Should import the texture as diffuse and as base color + aiString path; + std::array modes; + static const std::array exp_modes = { aiTextureMapMode_Wrap, aiTextureMapMode_Wrap }; + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_CLEARCOAT_TEXTURE, &path, nullptr, nullptr, + nullptr, nullptr, modes.data())); + EXPECT_STREQ(path.C_Str(), "PartialCoating.png"); + EXPECT_EQ(exp_modes, modes); + } + } + EXPECT_TRUE(found_partial_coat); +} + +TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_clearcoat) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest.gltf", aiProcess_ValidateDataStructure); + VerifyClearCoatScene(scene); +} + #ifndef ASSIMP_BUILD_NO_EXPORT +TEST_F(utglTF2ImportExport, importglTF2AndExport_KHR_materials_clearcoat) { + { + Assimp::Importer importer; + Assimp::Exporter exporter; + const aiScene* scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest.gltf", aiProcess_ValidateDataStructure); + ASSERT_NE(nullptr, scene); + // Export + EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest_out.glb")); + } + + // And re-import + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest_out.glb", aiProcess_ValidateDataStructure); + VerifyClearCoatScene(scene); +} + TEST_F(utglTF2ImportExport, importglTF2AndExport_KHR_materials_pbrSpecularGlossiness) { Assimp::Importer importer; Assimp::Exporter exporter; From 985f3ee6650983582b8612217f236f8c46a274e3 Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Fri, 11 Jun 2021 15:17:25 +0100 Subject: [PATCH 170/335] Fix glTFv2 texcoord/uv mapping Use the standard property to indicate the UV map index --- code/AssetLib/glTF2/glTF2Exporter.cpp | 21 ++++++----- code/AssetLib/glTF2/glTF2Exporter.h | 2 +- code/AssetLib/glTF2/glTF2Importer.cpp | 3 +- include/assimp/material.h | 4 +-- include/assimp/pbrmaterial.h | 4 +-- test/unit/utglTF2ImportExport.cpp | 50 ++++++++++++++++++++------- 6 files changed, 56 insertions(+), 28 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 3cb3891b0..7e0966aff 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -492,11 +492,14 @@ void glTF2Exporter::GetMatTexProp(const aiMaterial& mat, float& prop, const char mat.Get(textureKey.c_str(), tt, slot, prop); } -void glTF2Exporter::GetMatTex(const aiMaterial& mat, Ref& texture, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, Ref& texture, unsigned int &texCoord, aiTextureType tt, unsigned int slot = 0) { if (mat.GetTextureCount(tt) > 0) { aiString tex; + // Read texcoord (UV map index) + mat.Get(AI_MATKEY_UVWSRC(tt, slot), texCoord); + if (mat.Get(AI_MATKEY_TEXTURE(tt, slot), tex) == AI_SUCCESS) { std::string path = tex.C_Str(); @@ -572,21 +575,21 @@ void glTF2Exporter::GetMatTex(const aiMaterial& mat, TextureInfo& prop, aiTextur { Ref& texture = prop.texture; - GetMatTex(mat, texture, tt, slot); + GetMatTex(mat, texture, prop.texCoord, tt, slot); - if (texture) { - GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); - } + //if (texture) { + // GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); + //} } void glTF2Exporter::GetMatTex(const aiMaterial& mat, NormalTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) { Ref& texture = prop.texture; - GetMatTex(mat, texture, tt, slot); + GetMatTex(mat, texture, prop.texCoord, tt, slot); if (texture) { - GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); + //GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); GetMatTexProp(mat, prop.scale, "scale", tt, slot); } } @@ -595,10 +598,10 @@ void glTF2Exporter::GetMatTex(const aiMaterial& mat, OcclusionTextureInfo& prop, { Ref& texture = prop.texture; - GetMatTex(mat, texture, tt, slot); + GetMatTex(mat, texture, prop.texCoord, tt, slot); if (texture) { - GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); + //GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); GetMatTexProp(mat, prop.strength, "strength", tt, slot); } } diff --git a/code/AssetLib/glTF2/glTF2Exporter.h b/code/AssetLib/glTF2/glTF2Exporter.h index edc85d998..f5238297f 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.h +++ b/code/AssetLib/glTF2/glTF2Exporter.h @@ -104,7 +104,7 @@ namespace Assimp void GetTexSampler(const aiMaterial& mat, glTF2::Ref texture, aiTextureType tt, unsigned int slot); void GetMatTexProp(const aiMaterial& mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int idx); void GetMatTexProp(const aiMaterial& mat, float& prop, const char* propName, aiTextureType tt, unsigned int idx); - void GetMatTex(const aiMaterial& mat, glTF2::Ref& texture, aiTextureType tt, unsigned int slot); + void GetMatTex(const aiMaterial& mat, glTF2::Ref& texture, unsigned int &texCoord, aiTextureType tt, unsigned int slot); void GetMatTex(const aiMaterial& mat, glTF2::TextureInfo& prop, aiTextureType tt, unsigned int slot); void GetMatTex(const aiMaterial& mat, glTF2::NormalTextureInfo& prop, aiTextureType tt, unsigned int slot); void GetMatTex(const aiMaterial& mat, glTF2::OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot); diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index b435f111d..aadc9fb16 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -165,7 +165,8 @@ inline void SetMaterialTextureProperty(std::vector &embeddedTexIdxs, Asset } mat->AddProperty(&uri, AI_MATKEY_TEXTURE(texType, texSlot)); - mat->AddProperty(&prop.texCoord, 1, AI_MATKEY_GLTF_TEXTURE_TEXCOORD(texType, texSlot)); + const int uvIndex = static_cast(prop.texCoord); + mat->AddProperty(&uvIndex, 1, AI_MATKEY_UVWSRC(texType, texSlot)); if (prop.textureTransformSupported) { aiUVTransform transform; diff --git a/include/assimp/material.h b/include/assimp/material.h index f348da369..2024be07f 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -144,9 +144,7 @@ enum aiTextureMapMode { enum aiTextureMapping { /** The mapping coordinates are taken from an UV channel. * - * #AI_MATKEY_UVWSRC property - * - * Specifies from which UV channel + * #AI_MATKEY_UVWSRC property specifies from which UV channel * the texture coordinates are to be taken from (remember, * meshes can have more than one UV channel). */ diff --git a/include/assimp/pbrmaterial.h b/include/assimp/pbrmaterial.h index c67bcc3b8..93e7e3095 100644 --- a/include/assimp/pbrmaterial.h +++ b/include/assimp/pbrmaterial.h @@ -75,7 +75,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR "$mat.gltf.materialTransmission.transmissionFactor", 0, 0 //#define AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE aiTextureType_UNKNOWN, 5 -#define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE "$tex.file.texCoord" +//#define _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE "$tex.file.texCoord" #define _AI_MATKEY_GLTF_MAPPINGNAME_BASE "$tex.mappingname" #define _AI_MATKEY_GLTF_MAPPINGID_BASE "$tex.mappingid" #define _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE "$tex.mappingfiltermag" @@ -83,7 +83,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define _AI_MATKEY_GLTF_SCALE_BASE "$tex.scale" #define _AI_MATKEY_GLTF_STRENGTH_BASE "$tex.strength" -#define AI_MATKEY_GLTF_TEXTURE_TEXCOORD(type, N) _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, type, N +//#define AI_MATKEY_GLTF_TEXTURE_TEXCOORD(type, N) _AI_MATKEY_GLTF_TEXTURE_TEXCOORD_BASE, type, N #define AI_MATKEY_GLTF_MAPPINGNAME(type, N) _AI_MATKEY_GLTF_MAPPINGNAME_BASE, type, N #define AI_MATKEY_GLTF_MAPPINGID(type, N) _AI_MATKEY_GLTF_MAPPINGID_BASE, type, N #define AI_MATKEY_GLTF_MAPPINGFILTER_MAG(type, N) _AI_MATKEY_GLTF_MAPPINGFILTER_MAG_BASE, type, N diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index ee8b1a742..2c000bb37 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -603,32 +603,58 @@ TEST_F(utglTF2ImportExport, sceneMetadata) { } TEST_F(utglTF2ImportExport, texcoords) { + Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", - aiProcess_ValidateDataStructure); + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", aiProcess_ValidateDataStructure); + ASSERT_NE(scene, nullptr); + ASSERT_TRUE(scene->HasMaterials()); + const aiMaterial *material = scene->mMaterials[0]; + + aiString path; + unsigned int uvIndex = 255; + aiTextureMapMode modes[2]; + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_BASE_COLOR_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); + EXPECT_STREQ(path.C_Str(), "texture.png"); + EXPECT_EQ(uvIndex, 0); + + uvIndex = 255; + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); + EXPECT_STREQ(path.C_Str(), "texture.png"); + EXPECT_EQ(uvIndex, 1); +} + +#ifndef ASSIMP_BUILD_NO_EXPORT + +TEST_F(utglTF2ImportExport, texcoords_export) { + { + Assimp::Importer importer; + Assimp::Exporter exporter; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", aiProcess_ValidateDataStructure); + ASSERT_NE(scene, nullptr); + ASSERT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf_out.glb")); + } + + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", aiProcess_ValidateDataStructure); ASSERT_NE(scene, nullptr); ASSERT_TRUE(scene->HasMaterials()); const aiMaterial *material = scene->mMaterials[0]; aiString path; + unsigned int uvIndex = 255; aiTextureMapMode modes[2]; - EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_DIFFUSE, 0, &path, nullptr, nullptr, - nullptr, nullptr, modes)); + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_BASE_COLOR_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); EXPECT_STREQ(path.C_Str(), "texture.png"); - - int uvIndex = -1; - EXPECT_EQ(aiGetMaterialInteger(material, AI_MATKEY_GLTF_TEXTURE_TEXCOORD(aiTextureType_DIFFUSE, 0), &uvIndex), aiReturn_SUCCESS); EXPECT_EQ(uvIndex, 0); - // Using manual macro expansion of AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE here. - // The following works with some but not all compilers: - // #define APPLY(X, Y) X(Y) - // ..., APPLY(AI_MATKEY_GLTF_TEXTURE_TEXCOORD, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE), ... - EXPECT_EQ(aiGetMaterialInteger(material, AI_MATKEY_GLTF_TEXTURE_TEXCOORD(aiTextureType_UNKNOWN, 0), &uvIndex), aiReturn_SUCCESS); + uvIndex = 255; + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); + EXPECT_STREQ(path.C_Str(), "texture.png"); EXPECT_EQ(uvIndex, 1); } +#endif // ASSIMP_BUILD_NO_EXPORT TEST_F(utglTF2ImportExport, recursive_nodes) { Assimp::Importer importer; const aiScene* scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/RecursiveNodes/RecursiveNodes.gltf", aiProcess_ValidateDataStructure); From f412595887647d77cd476576a3c970bfae01c4eb Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Fri, 11 Jun 2021 15:25:02 +0100 Subject: [PATCH 171/335] Fix typo Thank you clang! --- code/AssetLib/glTF2/glTF2Exporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 7e0966aff..40ba41c20 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -655,7 +655,7 @@ bool glTF2Exporter::GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlo // Add any appropriate textures GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); - result == result || pbrSG.specularGlossinessTexture.texture; + result = result || pbrSG.specularGlossinessTexture.texture; if (result) { // Likely to always have diffuse From af748755e186faf5a80e597a37122ed55b6d3fcb Mon Sep 17 00:00:00 2001 From: Hill Ma Date: Tue, 15 Jun 2021 13:20:12 -0700 Subject: [PATCH 172/335] Fix issue #2873 --- code/PostProcessing/EmbedTexturesProcess.cpp | 23 +++++++++++++------- code/PostProcessing/EmbedTexturesProcess.h | 3 +++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/code/PostProcessing/EmbedTexturesProcess.cpp b/code/PostProcessing/EmbedTexturesProcess.cpp index 7e435e556..ba978dbde 100644 --- a/code/PostProcessing/EmbedTexturesProcess.cpp +++ b/code/PostProcessing/EmbedTexturesProcess.cpp @@ -41,6 +41,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "EmbedTexturesProcess.h" +#include +#include #include #include "ProcessHelper.h" @@ -62,6 +64,7 @@ bool EmbedTexturesProcess::IsActive(unsigned int pFlags) const { void EmbedTexturesProcess::SetupProperties(const Importer* pImp) { mRootPath = pImp->GetPropertyString("sourceFilePath"); mRootPath = mRootPath.substr(0, mRootPath.find_last_of("\\/") + 1u); + mIOHandler = pImp->GetIOHandler(); } void EmbedTexturesProcess::Execute(aiScene* pScene) { @@ -101,27 +104,31 @@ bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const { std::string imagePath = path; // Test path directly - std::ifstream file(imagePath, std::ios::binary | std::ios::ate); - if ((imageSize = file.tellg()) == std::streampos(-1)) { + if (!mIOHandler->Exists(imagePath)) { ASSIMP_LOG_WARN("EmbedTexturesProcess: Cannot find image: ", imagePath, ". Will try to find it in root folder."); // Test path in root path imagePath = mRootPath + path; - file.open(imagePath, std::ios::binary | std::ios::ate); - if ((imageSize = file.tellg()) == std::streampos(-1)) { + if (!mIOHandler->Exists(imagePath)) { // Test path basename in root path imagePath = mRootPath + path.substr(path.find_last_of("\\/") + 1u); - file.open(imagePath, std::ios::binary | std::ios::ate); - if ((imageSize = file.tellg()) == std::streampos(-1)) { + if (!mIOHandler->Exists(imagePath)) { ASSIMP_LOG_ERROR("EmbedTexturesProcess: Unable to embed texture: ", path, "."); return false; } } } + IOStream* pFile = mIOHandler->Open(imagePath); + if (pFile == nullptr) { + ASSIMP_LOG_ERROR("EmbedTexturesProcess: Unable to embed texture: ", path, "."); + return false; + } + imageSize = pFile->FileSize(); aiTexel* imageContent = new aiTexel[ 1ul + static_cast( imageSize ) / sizeof(aiTexel)]; - file.seekg(0, std::ios::beg); - file.read(reinterpret_cast(imageContent), imageSize); + pFile->Seek(0, aiOrigin_SET); + pFile->Read(reinterpret_cast(imageContent), imageSize, 1); + mIOHandler->Close(pFile); // Enlarging the textures table unsigned int textureId = pScene->mNumTextures++; diff --git a/code/PostProcessing/EmbedTexturesProcess.h b/code/PostProcessing/EmbedTexturesProcess.h index 90970937a..5915e0d44 100644 --- a/code/PostProcessing/EmbedTexturesProcess.h +++ b/code/PostProcessing/EmbedTexturesProcess.h @@ -48,6 +48,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. struct aiNode; +class IOSystem; + namespace Assimp { /** @@ -80,6 +82,7 @@ private: private: std::string mRootPath; + IOSystem* mIOHandler = nullptr; }; } // namespace Assimp From 148b8c66a8ac040cc859444a27b5c7f3e061232a Mon Sep 17 00:00:00 2001 From: Hill Ma Date: Tue, 15 Jun 2021 15:18:20 -0700 Subject: [PATCH 173/335] glTF2: zero out extra space created by padding. This makes resulting GLB deterministic. --- code/AssetLib/glTF2/glTF2Asset.inl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index b51ac20c2..c58732a03 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -790,8 +790,10 @@ inline bool Buffer::ReplaceData_joint(const size_t pBufferData_Offset, const siz inline size_t Buffer::AppendData(uint8_t *data, size_t length) { size_t offset = this->byteLength; // Force alignment to 4 bits - Grow((length + 3) & ~3); + size_t paddedLength = (length + 3) & ~3; + Grow(paddedLength); memcpy(mData.get() + offset, data, length); + memset(mData.get() + offset + length, 0, paddedLength - length); return offset; } From a34b9d1c95fc60d6b5a91ef998271097b2d2413d Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 16 Jun 2021 11:21:31 +0200 Subject: [PATCH 174/335] Fix review findings --- code/PostProcessing/EmbedTexturesProcess.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/code/PostProcessing/EmbedTexturesProcess.cpp b/code/PostProcessing/EmbedTexturesProcess.cpp index ba978dbde..cb2853926 100644 --- a/code/PostProcessing/EmbedTexturesProcess.cpp +++ b/code/PostProcessing/EmbedTexturesProcess.cpp @@ -4,7 +4,6 @@ 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, @@ -50,11 +49,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace Assimp; -EmbedTexturesProcess::EmbedTexturesProcess() -: BaseProcess() { +EmbedTexturesProcess::EmbedTexturesProcess() : + BaseProcess() { + // empty } EmbedTexturesProcess::~EmbedTexturesProcess() { + // empty } bool EmbedTexturesProcess::IsActive(unsigned int pFlags) const { @@ -68,12 +69,12 @@ void EmbedTexturesProcess::SetupProperties(const Importer* pImp) { } void EmbedTexturesProcess::Execute(aiScene* pScene) { - if (pScene == nullptr || pScene->mRootNode == nullptr) return; + if (pScene == nullptr || pScene->mRootNode == nullptr || mIOHandler == nullptr){ + return; + } aiString path; - uint32_t embeddedTexturesCount = 0u; - for (auto matId = 0u; matId < pScene->mNumMaterials; ++matId) { auto material = pScene->mMaterials[matId]; From e27074594f7444104bbe796feee041b5affa4233 Mon Sep 17 00:00:00 2001 From: Andreas Buhr Date: Wed, 16 Jun 2021 18:58:41 +0200 Subject: [PATCH 175/335] Replace swear words in IFCBoolean.cpp Add a more gentle comment. --- code/AssetLib/IFC/IFCBoolean.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/IFC/IFCBoolean.cpp b/code/AssetLib/IFC/IFCBoolean.cpp index 86cac7f46..afd0ad6eb 100644 --- a/code/AssetLib/IFC/IFCBoolean.cpp +++ b/code/AssetLib/IFC/IFCBoolean.cpp @@ -513,7 +513,7 @@ void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const Schema_2x3::IfcPoly } // we got a list of in-out-combinations of intersections. That should be an even number of intersections, or - // we're fucked. + // we are facing a non-recoverable error. if ((intersections.size() & 1) != 0) { IFCImporter::LogWarn("Odd number of intersections, can't work with that. Omitting half space boundary check."); continue; From a8c75c34a1dd297dfed74e660d30b83d4acc9a56 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 17 Jun 2021 21:31:28 +0200 Subject: [PATCH 176/335] Update scene.h Add some more checks against nullptr dereferecnes. --- include/assimp/scene.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/include/assimp/scene.h b/include/assimp/scene.h index fb3b2ef6e..522ddc6dc 100644 --- a/include/assimp/scene.h +++ b/include/assimp/scene.h @@ -5,8 +5,6 @@ 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, @@ -402,15 +400,23 @@ struct aiScene //! Returns an embedded texture and its index std::pair GetEmbeddedTextureAndIndex(const char* filename) const { + if(nullptr==filename) { + return std::make_pair(nullptr, -1); + } // lookup using texture ID (if referenced like: "*1", "*2", etc.) if ('*' == *filename) { int index = std::atoi(filename + 1); - if (0 > index || mNumTextures <= static_cast(index)) + if (0 > index || mNumTextures <= static_cast(index)) { return std::make_pair(nullptr, -1); + } return std::make_pair(mTextures[index], index); } // lookup using filename const char* shortFilename = GetShortFilename(filename); + if (nullptr == shortFilename) { + return std::make_pair(nullptr, -1); + } + for (unsigned int i = 0; i < mNumTextures; i++) { const char* shortTextureFilename = GetShortFilename(mTextures[i]->mFilename.C_Str()); if (strcmp(shortTextureFilename, shortFilename) == 0) { From 94c3abd841dd08d8c98d1c7cbbf953ffb314bd94 Mon Sep 17 00:00:00 2001 From: Aaron Gokaslan Date: Tue, 22 Jun 2021 12:27:15 -0400 Subject: [PATCH 177/335] Apply various performance fixes from clang-tidy --- code/AssetLib/3DS/3DSHelper.h | 76 ++++++++++---------- code/AssetLib/ASE/ASEParser.h | 8 +-- code/AssetLib/B3D/B3DImporter.cpp | 2 +- code/AssetLib/B3D/B3DImporter.h | 2 +- code/AssetLib/Blender/BlenderLoader.cpp | 2 +- code/AssetLib/COB/COBLoader.cpp | 2 +- code/AssetLib/Collada/ColladaParser.cpp | 2 +- code/AssetLib/FBX/FBXConverter.cpp | 2 +- code/AssetLib/FBX/FBXDocument.cpp | 9 ++- code/AssetLib/FBX/FBXExportNode.cpp | 5 +- code/AssetLib/FBX/FBXExportNode.h | 5 +- code/AssetLib/FBX/FBXExporter.cpp | 17 ++--- code/AssetLib/FBX/FBXExporter.h | 15 ++-- code/AssetLib/FBX/FBXProperties.cpp | 9 ++- code/AssetLib/IFC/IFCGeometry.cpp | 2 +- code/AssetLib/IFC/IFCMaterial.cpp | 6 +- code/AssetLib/IFC/IFCUtil.h | 16 ++--- code/AssetLib/LWS/LWSLoader.cpp | 2 +- code/AssetLib/M3D/M3DImporter.cpp | 2 +- code/AssetLib/M3D/M3DImporter.h | 4 +- code/AssetLib/Ogre/OgreMaterial.cpp | 4 +- code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp | 4 +- code/AssetLib/STEPParser/STEPFileReader.cpp | 5 +- code/AssetLib/Step/STEPFile.h | 4 +- code/AssetLib/X/XFileExporter.cpp | 4 +- code/AssetLib/X3D/X3DExporter.hpp | 6 +- code/AssetLib/glTF/glTFAsset.h | 10 ++- code/AssetLib/glTF/glTFAsset.inl | 2 +- code/AssetLib/glTF/glTFExporter.cpp | 3 +- code/AssetLib/glTF2/glTF2Asset.h | 4 +- code/AssetLib/glTF2/glTF2Asset.inl | 2 +- code/AssetLib/glTF2/glTF2Exporter.cpp | 3 +- code/Pbrt/PbrtExporter.cpp | 23 +++--- code/Pbrt/PbrtExporter.h | 4 +- code/PostProcessing/EmbedTexturesProcess.cpp | 2 +- code/PostProcessing/EmbedTexturesProcess.h | 2 +- test/unit/utTypes.cpp | 4 +- 37 files changed, 131 insertions(+), 143 deletions(-) diff --git a/code/AssetLib/3DS/3DSHelper.h b/code/AssetLib/3DS/3DSHelper.h index 1930c0c40..e8efbf949 100644 --- a/code/AssetLib/3DS/3DSHelper.h +++ b/code/AssetLib/3DS/3DSHelper.h @@ -348,16 +348,16 @@ struct Texture { // empty } - Texture(Texture &&other) AI_NO_EXCEPT : mTextureBlend(std::move(other.mTextureBlend)), + Texture(Texture &&other) AI_NO_EXCEPT : mTextureBlend(other.mTextureBlend), mMapName(std::move(other.mMapName)), - mOffsetU(std::move(other.mOffsetU)), - mOffsetV(std::move(other.mOffsetV)), - mScaleU(std::move(other.mScaleU)), - mScaleV(std::move(other.mScaleV)), - mRotation(std::move(other.mRotation)), - mMapMode(std::move(other.mMapMode)), - bPrivate(std::move(other.bPrivate)), - iUVSrc(std::move(other.iUVSrc)) { + mOffsetU(other.mOffsetU), + mOffsetV(other.mOffsetV), + mScaleU(other.mScaleU), + mScaleV(other.mScaleV), + mRotation(other.mRotation), + mMapMode(other.mMapMode), + bPrivate(other.bPrivate), + iUVSrc(other.iUVSrc) { // empty } @@ -366,16 +366,16 @@ struct Texture { return *this; } - mTextureBlend = std::move(other.mTextureBlend); + mTextureBlend = other.mTextureBlend; mMapName = std::move(other.mMapName); - mOffsetU = std::move(other.mOffsetU); - mOffsetV = std::move(other.mOffsetV); - mScaleU = std::move(other.mScaleU); - mScaleV = std::move(other.mScaleV); - mRotation = std::move(other.mRotation); - mMapMode = std::move(other.mMapMode); - bPrivate = std::move(other.bPrivate); - iUVSrc = std::move(other.iUVSrc); + mOffsetU = other.mOffsetU; + mOffsetV = other.mOffsetV; + mScaleU = other.mScaleU; + mScaleV = other.mScaleV; + mRotation = other.mRotation; + mMapMode = other.mMapMode; + bPrivate = other.bPrivate; + iUVSrc = other.iUVSrc; return *this; } @@ -461,13 +461,13 @@ struct Material { //! Move constructor. This is explicitly written because MSVC doesn't support defaulting it Material(Material &&other) AI_NO_EXCEPT : mName(std::move(other.mName)), - mDiffuse(std::move(other.mDiffuse)), - mSpecularExponent(std::move(other.mSpecularExponent)), - mShininessStrength(std::move(other.mShininessStrength)), - mSpecular(std::move(other.mSpecular)), - mAmbient(std::move(other.mAmbient)), - mShading(std::move(other.mShading)), - mTransparency(std::move(other.mTransparency)), + mDiffuse(other.mDiffuse), + mSpecularExponent(other.mSpecularExponent), + mShininessStrength(other.mShininessStrength), + mSpecular(other.mSpecular), + mAmbient(other.mAmbient), + mShading(other.mShading), + mTransparency(other.mTransparency), sTexDiffuse(std::move(other.sTexDiffuse)), sTexOpacity(std::move(other.sTexOpacity)), sTexSpecular(std::move(other.sTexSpecular)), @@ -475,10 +475,10 @@ struct Material { sTexBump(std::move(other.sTexBump)), sTexEmissive(std::move(other.sTexEmissive)), sTexShininess(std::move(other.sTexShininess)), - mBumpHeight(std::move(other.mBumpHeight)), - mEmissive(std::move(other.mEmissive)), + mBumpHeight(other.mBumpHeight), + mEmissive(other.mEmissive), sTexAmbient(std::move(other.sTexAmbient)), - mTwoSided(std::move(other.mTwoSided)) { + mTwoSided(other.mTwoSided) { // empty } @@ -488,13 +488,13 @@ struct Material { } mName = std::move(other.mName); - mDiffuse = std::move(other.mDiffuse); - mSpecularExponent = std::move(other.mSpecularExponent); - mShininessStrength = std::move(other.mShininessStrength), - mSpecular = std::move(other.mSpecular); - mAmbient = std::move(other.mAmbient); - mShading = std::move(other.mShading); - mTransparency = std::move(other.mTransparency); + mDiffuse = other.mDiffuse; + mSpecularExponent = other.mSpecularExponent; + mShininessStrength = other.mShininessStrength, + mSpecular = other.mSpecular; + mAmbient = other.mAmbient; + mShading = other.mShading; + mTransparency = other.mTransparency; sTexDiffuse = std::move(other.sTexDiffuse); sTexOpacity = std::move(other.sTexOpacity); sTexSpecular = std::move(other.sTexSpecular); @@ -502,10 +502,10 @@ struct Material { sTexBump = std::move(other.sTexBump); sTexEmissive = std::move(other.sTexEmissive); sTexShininess = std::move(other.sTexShininess); - mBumpHeight = std::move(other.mBumpHeight); - mEmissive = std::move(other.mEmissive); + mBumpHeight = other.mBumpHeight; + mEmissive = other.mEmissive; sTexAmbient = std::move(other.sTexAmbient); - mTwoSided = std::move(other.mTwoSided); + mTwoSided = other.mTwoSided; return *this; } diff --git a/code/AssetLib/ASE/ASEParser.h b/code/AssetLib/ASE/ASEParser.h index d04fc0662..f49cfc36f 100644 --- a/code/AssetLib/ASE/ASEParser.h +++ b/code/AssetLib/ASE/ASEParser.h @@ -95,8 +95,8 @@ struct Material : public D3DS::Material { Material(Material &&other) AI_NO_EXCEPT : D3DS::Material(std::move(other)), avSubMaterials(std::move(other.avSubMaterials)), - pcInstance(std::move(other.pcInstance)), - bNeed(std::move(other.bNeed)) { + pcInstance(other.pcInstance), + bNeed(other.bNeed) { other.pcInstance = nullptr; } @@ -108,8 +108,8 @@ struct Material : public D3DS::Material { //D3DS::Material::operator=(std::move(other)); avSubMaterials = std::move(other.avSubMaterials); - pcInstance = std::move(other.pcInstance); - bNeed = std::move(other.bNeed); + pcInstance = other.pcInstance; + bNeed = other.bNeed; other.pcInstance = nullptr; diff --git a/code/AssetLib/B3D/B3DImporter.cpp b/code/AssetLib/B3D/B3DImporter.cpp index 3d9a5075a..11f7bcd14 100644 --- a/code/AssetLib/B3D/B3DImporter.cpp +++ b/code/AssetLib/B3D/B3DImporter.cpp @@ -143,7 +143,7 @@ AI_WONT_RETURN void B3DImporter::Oops() { } // ------------------------------------------------------------------------------------------------ -AI_WONT_RETURN void B3DImporter::Fail(string str) { +AI_WONT_RETURN void B3DImporter::Fail(const string &str) { #ifdef DEBUG_B3D ASSIMP_LOG_ERROR("Error in B3D file data: ", str); #endif diff --git a/code/AssetLib/B3D/B3DImporter.h b/code/AssetLib/B3D/B3DImporter.h index e2a75abdf..a7ed65c3b 100644 --- a/code/AssetLib/B3D/B3DImporter.h +++ b/code/AssetLib/B3D/B3DImporter.h @@ -96,7 +96,7 @@ private: }; AI_WONT_RETURN void Oops() AI_WONT_RETURN_SUFFIX; - AI_WONT_RETURN void Fail( std::string str ) AI_WONT_RETURN_SUFFIX; + AI_WONT_RETURN void Fail(const std::string &str) AI_WONT_RETURN_SUFFIX; void ReadTEXS(); void ReadBRUS(); diff --git a/code/AssetLib/Blender/BlenderLoader.cpp b/code/AssetLib/Blender/BlenderLoader.cpp index 7cf4e070e..42a7a1723 100644 --- a/code/AssetLib/Blender/BlenderLoader.cpp +++ b/code/AssetLib/Blender/BlenderLoader.cpp @@ -679,7 +679,7 @@ void BlenderImporter::BuildMaterials(ConversionData &conv_data) { BuildDefaultMaterial(conv_data); - for (std::shared_ptr mat : conv_data.materials_raw) { + for (const std::shared_ptr &mat : conv_data.materials_raw) { // reset per material global counters for (size_t i = 0; i < sizeof(conv_data.next_texture) / sizeof(conv_data.next_texture[0]); ++i) { diff --git a/code/AssetLib/COB/COBLoader.cpp b/code/AssetLib/COB/COBLoader.cpp index 94327c683..822bce16d 100644 --- a/code/AssetLib/COB/COBLoader.cpp +++ b/code/AssetLib/COB/COBLoader.cpp @@ -230,7 +230,7 @@ void COBImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy } // ------------------------------------------------------------------------------------------------ -void ConvertTexture(std::shared_ptr tex, aiMaterial *out, aiTextureType type) { +void ConvertTexture(const std::shared_ptr &tex, aiMaterial *out, aiTextureType type) { const aiString path(tex->path); out->AddProperty(&path, AI_MATKEY_TEXTURE(type, 0)); out->AddProperty(&tex->transform, 1, AI_MATKEY_UVTRANSFORM(type, 0)); diff --git a/code/AssetLib/Collada/ColladaParser.cpp b/code/AssetLib/Collada/ColladaParser.cpp index 5dbf0a567..3166136b2 100644 --- a/code/AssetLib/Collada/ColladaParser.cpp +++ b/code/AssetLib/Collada/ColladaParser.cpp @@ -453,7 +453,7 @@ void ColladaParser::PostProcessRootAnimations() { temp.mSubAnims.push_back(clip); - for (std::string animationID : it.second) { + for (const std::string &animationID : it.second) { AnimationLibrary::iterator animation = mAnimationLibrary.find(animationID); if (animation != mAnimationLibrary.end()) { diff --git a/code/AssetLib/FBX/FBXConverter.cpp b/code/AssetLib/FBX/FBXConverter.cpp index a564b3e9b..a6a3fc566 100644 --- a/code/AssetLib/FBX/FBXConverter.cpp +++ b/code/AssetLib/FBX/FBXConverter.cpp @@ -2599,7 +2599,7 @@ void FBXConverter::ConvertAnimationStack(const AnimationStack &st) { anim->mMorphMeshChannels = new aiMeshMorphAnim *[numMorphMeshChannels]; anim->mNumMorphMeshChannels = numMorphMeshChannels; unsigned int i = 0; - for (auto morphAnimIt : morphAnimDatas) { + for (const auto &morphAnimIt : morphAnimDatas) { morphAnimData *animData = morphAnimIt.second; unsigned int numKeys = static_cast(animData->size()); aiMeshMorphAnim *meshMorphAnim = new aiMeshMorphAnim(); diff --git a/code/AssetLib/FBX/FBXDocument.cpp b/code/AssetLib/FBX/FBXDocument.cpp index 7adaadf6c..0c4435348 100644 --- a/code/AssetLib/FBX/FBXDocument.cpp +++ b/code/AssetLib/FBX/FBXDocument.cpp @@ -57,9 +57,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include -#include #include #include +#include +#include namespace Assimp { namespace FBX { @@ -248,10 +249,8 @@ Object::~Object() } // ------------------------------------------------------------------------------------------------ -FileGlobalSettings::FileGlobalSettings(const Document& doc, std::shared_ptr props) -: props(props) -, doc(doc) -{ +FileGlobalSettings::FileGlobalSettings(const Document &doc, std::shared_ptr props) : + props(std::move(props)), doc(doc) { // empty } diff --git a/code/AssetLib/FBX/FBXExportNode.cpp b/code/AssetLib/FBX/FBXExportNode.cpp index 91e421420..817308ec8 100644 --- a/code/AssetLib/FBX/FBXExportNode.cpp +++ b/code/AssetLib/FBX/FBXExportNode.cpp @@ -144,9 +144,8 @@ void FBX::Node::AddP70time( // public member functions for writing nodes to stream void FBX::Node::Dump( - std::shared_ptr outfile, - bool binary, int indent -) { + const std::shared_ptr &outfile, + bool binary, int indent) { if (binary) { Assimp::StreamWriterLE outstream(outfile); DumpBinary(outstream); diff --git a/code/AssetLib/FBX/FBXExportNode.h b/code/AssetLib/FBX/FBXExportNode.h index c5f29ef0f..6ef27972d 100644 --- a/code/AssetLib/FBX/FBXExportNode.h +++ b/code/AssetLib/FBX/FBXExportNode.h @@ -157,9 +157,8 @@ public: // member functions for writing data to a file or stream // write the full node to the given file or stream void Dump( - std::shared_ptr outfile, - bool binary, int indent - ); + const std::shared_ptr &outfile, + bool binary, int indent); void Dump(Assimp::StreamWriterLE &s, bool binary, int indent); // these other functions are for writing data piece by piece. diff --git a/code/AssetLib/FBX/FBXExporter.cpp b/code/AssetLib/FBX/FBXExporter.cpp index 6bdd0b5be..27b65a27f 100644 --- a/code/AssetLib/FBX/FBXExporter.cpp +++ b/code/AssetLib/FBX/FBXExporter.cpp @@ -2718,16 +2718,14 @@ void FBXExporter::WriteModelNodes( } } - void FBXExporter::WriteAnimationCurveNode( - StreamWriterLE& outstream, - int64_t uid, - const std::string& name, // "T", "R", or "S" - aiVector3D default_value, - std::string property_name, // "Lcl Translation" etc - int64_t layer_uid, - int64_t node_uid -) { + StreamWriterLE &outstream, + int64_t uid, + const std::string &name, // "T", "R", or "S" + aiVector3D default_value, + const std::string &property_name, // "Lcl Translation" etc + int64_t layer_uid, + int64_t node_uid) { FBX::Node n("AnimationCurveNode"); n.AddProperties(uid, name + FBX::SEPARATOR + "AnimCurveNode", ""); FBX::Node p("Properties70"); @@ -2742,7 +2740,6 @@ void FBXExporter::WriteAnimationCurveNode( this->connections.emplace_back("C", "OP", uid, node_uid, property_name); } - void FBXExporter::WriteAnimationCurve( StreamWriterLE& outstream, double default_value, diff --git a/code/AssetLib/FBX/FBXExporter.h b/code/AssetLib/FBX/FBXExporter.h index 563183268..3f5e8029d 100644 --- a/code/AssetLib/FBX/FBXExporter.h +++ b/code/AssetLib/FBX/FBXExporter.h @@ -156,14 +156,13 @@ namespace Assimp FBX::TransformInheritance ti_type=FBX::TransformInheritance_RSrs ); void WriteAnimationCurveNode( - StreamWriterLE& outstream, - int64_t uid, - const std::string& name, // "T", "R", or "S" - aiVector3D default_value, - std::string property_name, // "Lcl Translation" etc - int64_t animation_layer_uid, - int64_t node_uid - ); + StreamWriterLE &outstream, + int64_t uid, + const std::string &name, // "T", "R", or "S" + aiVector3D default_value, + const std::string &property_name, // "Lcl Translation" etc + int64_t animation_layer_uid, + int64_t node_uid); void WriteAnimationCurve( StreamWriterLE& outstream, double default_value, diff --git a/code/AssetLib/FBX/FBXProperties.cpp b/code/AssetLib/FBX/FBXProperties.cpp index 1a5ebffd1..c3f4de260 100644 --- a/code/AssetLib/FBX/FBXProperties.cpp +++ b/code/AssetLib/FBX/FBXProperties.cpp @@ -52,6 +52,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "FBXDocumentUtil.h" #include "FBXProperties.h" +#include + namespace Assimp { namespace FBX { @@ -172,10 +174,8 @@ PropertyTable::PropertyTable() } // ------------------------------------------------------------------------------------------------ -PropertyTable::PropertyTable(const Element& element, std::shared_ptr templateProps) -: templateProps(templateProps) -, element(&element) -{ +PropertyTable::PropertyTable(const Element &element, std::shared_ptr templateProps) : + templateProps(std::move(templateProps)), element(&element) { const Scope& scope = GetRequiredScope(element); for(const ElementMap::value_type& v : scope.Elements()) { if(v.first != "P") { @@ -199,7 +199,6 @@ PropertyTable::PropertyTable(const Element& element, std::shared_ptr meshtmp = std::make_shared(); if(const Schema_2x3::IfcShellBasedSurfaceModel* shellmod = geo.ToPtr()) { - for(std::shared_ptr shell :shellmod->SbsmBoundary) { + for (const std::shared_ptr &shell : shellmod->SbsmBoundary) { try { const ::Assimp::STEP::EXPRESS::ENTITY& e = shell->To<::Assimp::STEP::EXPRESS::ENTITY>(); const Schema_2x3::IfcConnectedFaceSet& fs = conv.db.MustGetObject(e).To(); diff --git a/code/AssetLib/IFC/IFCMaterial.cpp b/code/AssetLib/IFC/IFCMaterial.cpp index 2a79f0754..c26a3aa0a 100644 --- a/code/AssetLib/IFC/IFCMaterial.cpp +++ b/code/AssetLib/IFC/IFCMaterial.cpp @@ -75,7 +75,7 @@ static void FillMaterial(aiMaterial* mat,const IFC::Schema_2x3::IfcSurfaceStyle* mat->AddProperty(&name,AI_MATKEY_NAME); // now see which kinds of surface information are present - for(std::shared_ptr< const IFC::Schema_2x3::IfcSurfaceStyleElementSelect > sel2 : surf->Styles) { + for (const std::shared_ptr &sel2 : surf->Styles) { if (const IFC::Schema_2x3::IfcSurfaceStyleShading* shade = sel2->ResolveSelectPtr(conv.db)) { aiColor4D col_base,col; @@ -124,7 +124,7 @@ static void FillMaterial(aiMaterial* mat,const IFC::Schema_2x3::IfcSurfaceStyle* } } } - } + } } } @@ -134,7 +134,7 @@ unsigned int ProcessMaterials(uint64_t id, unsigned int prevMatId, ConversionDat for(;range.first != range.second; ++range.first) { if(const IFC::Schema_2x3::IfcStyledItem* const styled = conv.db.GetObject((*range.first).second)->ToPtr()) { for(const IFC::Schema_2x3::IfcPresentationStyleAssignment& as : styled->Styles) { - for(std::shared_ptr sel : as.Styles) { + for (const std::shared_ptr &sel : as.Styles) { if( const IFC::Schema_2x3::IfcSurfaceStyle* const surf = sel->ResolveSelectPtr(conv.db) ) { // try to satisfy from cache diff --git a/code/AssetLib/IFC/IFCUtil.h b/code/AssetLib/IFC/IFCUtil.h index a1190746b..20b9a617c 100644 --- a/code/AssetLib/IFC/IFCUtil.h +++ b/code/AssetLib/IFC/IFCUtil.h @@ -54,6 +54,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include + +#include + struct aiNode; namespace Assimp { @@ -137,14 +141,10 @@ struct TempOpening } // ------------------------------------------------------------------------------ - TempOpening(const IFC::Schema_2x3::IfcSolidModel* solid,IfcVector3 extrusionDir, - std::shared_ptr profileMesh, - std::shared_ptr profileMesh2D) - : solid(solid) - , extrusionDir(extrusionDir) - , profileMesh(profileMesh) - , profileMesh2D(profileMesh2D) - { + TempOpening(const IFC::Schema_2x3::IfcSolidModel *solid, IfcVector3 extrusionDir, + std::shared_ptr profileMesh, + std::shared_ptr profileMesh2D) : + solid(solid), extrusionDir(extrusionDir), profileMesh(std::move(std::move(profileMesh))), profileMesh2D(std::move(std::move(profileMesh2D))) { } // ------------------------------------------------------------------------------ diff --git a/code/AssetLib/LWS/LWSLoader.cpp b/code/AssetLib/LWS/LWSLoader.cpp index d469a1064..01a50b6e4 100644 --- a/code/AssetLib/LWS/LWSLoader.cpp +++ b/code/AssetLib/LWS/LWSLoader.cpp @@ -318,7 +318,7 @@ void LWSImporter::SetupNodeName(aiNode *nd, LWS::NodeDesc &src) { } else { ++s; } - std::string::size_type t = src.path.substr(s).find_last_of("."); + std::string::size_type t = src.path.substr(s).find_last_of('.'); nd->mName.length = ::ai_snprintf(nd->mName.data, MAXLEN, "%s_(%08X)", src.path.substr(s).substr(0, t).c_str(), combined); return; diff --git a/code/AssetLib/M3D/M3DImporter.cpp b/code/AssetLib/M3D/M3DImporter.cpp index 38bbd1d4a..4b2f9bd77 100644 --- a/code/AssetLib/M3D/M3DImporter.cpp +++ b/code/AssetLib/M3D/M3DImporter.cpp @@ -655,7 +655,7 @@ void M3DImporter::convertPose(const M3DWrapper &m3d, aiMatrix4x4 *m, unsigned in // ------------------------------------------------------------------------------------------------ // find a node by name -aiNode *M3DImporter::findNode(aiNode *pNode, aiString name) { +aiNode *M3DImporter::findNode(aiNode *pNode, const aiString &name) { ai_assert(pNode != nullptr); ai_assert(mScene != nullptr); diff --git a/code/AssetLib/M3D/M3DImporter.h b/code/AssetLib/M3D/M3DImporter.h index 7a2a9fbd3..05e8ced7b 100644 --- a/code/AssetLib/M3D/M3DImporter.h +++ b/code/AssetLib/M3D/M3DImporter.h @@ -89,8 +89,8 @@ private: // helper functions aiColor4D mkColor(uint32_t c); void convertPose(const M3DWrapper &m3d, aiMatrix4x4 *m, unsigned int posid, unsigned int orientid); - aiNode *findNode(aiNode *pNode, aiString name); - void calculateOffsetMatrix(aiNode *pNode, aiMatrix4x4 *m); + aiNode *findNode(aiNode *pNode, const aiString &name); + void calculateOffsetMatrix(aiNode *pNode, aiMatrix4x4 *m); void populateMesh(const M3DWrapper &m3d, aiMesh *pMesh, std::vector *faces, std::vector *verteces, std::vector *normals, std::vector *texcoords, std::vector *colors, std::vector *vertexids); diff --git a/code/AssetLib/Ogre/OgreMaterial.cpp b/code/AssetLib/Ogre/OgreMaterial.cpp index 295dedde6..6da82f89c 100644 --- a/code/AssetLib/Ogre/OgreMaterial.cpp +++ b/code/AssetLib/Ogre/OgreMaterial.cpp @@ -415,8 +415,8 @@ bool OgreImporter::ReadTextureUnit(const std::string &textureUnitName, stringstr // User defined Assimp config property to detect texture type from filename. if (m_detectTextureTypeFromFilename) { - size_t posSuffix = textureRef.find_last_of("."); - size_t posUnderscore = textureRef.find_last_of("_"); + size_t posSuffix = textureRef.find_last_of('.'); + size_t posUnderscore = textureRef.find_last_of('_'); if (posSuffix != string::npos && posUnderscore != string::npos && posSuffix > posUnderscore) { string identifier = ai_tolower(textureRef.substr(posUnderscore, posSuffix - posUnderscore)); diff --git a/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp b/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp index becfa41fc..a1da8fd74 100644 --- a/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp +++ b/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp @@ -99,7 +99,7 @@ static void extractIds(const std::string &key, int &id1, int &id2) { return; } - const std::string::size_type pos = key.find("."); + const std::string::size_type pos = key.find('.'); if (std::string::npos == pos) { return; } @@ -208,7 +208,7 @@ void Q3BSPFileImporter::separateMapName(const std::string &importName, std::stri return; } - const std::string::size_type pos = importName.rfind(","); + const std::string::size_type pos = importName.rfind(','); if (std::string::npos == pos) { archiveName = importName; return; diff --git a/code/AssetLib/STEPParser/STEPFileReader.cpp b/code/AssetLib/STEPParser/STEPFileReader.cpp index e97ea1e28..ac6d83672 100644 --- a/code/AssetLib/STEPParser/STEPFileReader.cpp +++ b/code/AssetLib/STEPParser/STEPFileReader.cpp @@ -49,8 +49,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "STEPFileEncoding.h" #include #include -#include #include +#include +#include using namespace Assimp; @@ -87,7 +88,7 @@ static const char *ISO_Token = "ISO-10303-21;"; static const char *FILE_SCHEMA_Token = "FILE_SCHEMA"; // ------------------------------------------------------------------------------------------------ STEP::DB* STEP::ReadFileHeader(std::shared_ptr stream) { - std::shared_ptr reader = std::shared_ptr(new StreamReaderLE(stream)); + std::shared_ptr reader = std::shared_ptr(new StreamReaderLE(std::move(stream))); std::unique_ptr db = std::unique_ptr(new STEP::DB(reader)); LineSplitter &splitter = db->GetSplitter(); diff --git a/code/AssetLib/Step/STEPFile.h b/code/AssetLib/Step/STEPFile.h index 90eaef5f3..e09faad98 100644 --- a/code/AssetLib/Step/STEPFile.h +++ b/code/AssetLib/Step/STEPFile.h @@ -634,7 +634,7 @@ private: }; template -inline bool operator==(std::shared_ptr lo, T whatever) { +inline bool operator==(const std::shared_ptr &lo, T whatever) { return *lo == whatever; // XXX use std::forward if we have 0x } @@ -816,7 +816,7 @@ public: typedef std::pair RefMapRange; private: - DB(std::shared_ptr reader) : + DB(const std::shared_ptr &reader) : reader(reader), splitter(*reader, true, true), evaluated_count(), schema(nullptr) {} public: diff --git a/code/AssetLib/X/XFileExporter.cpp b/code/AssetLib/X/XFileExporter.cpp index da20b935a..bd997a3c5 100644 --- a/code/AssetLib/X/XFileExporter.cpp +++ b/code/AssetLib/X/XFileExporter.cpp @@ -530,8 +530,8 @@ void XFileExporter::writePath(const aiString &path) while( str.find( "\\\\") != std::string::npos) str.replace( str.find( "\\\\"), 2, "\\"); - while( str.find( "\\") != std::string::npos) - str.replace( str.find( "\\"), 1, "/"); + while (str.find('\\') != std::string::npos) + str.replace(str.find('\\'), 1, "/"); mOutput << str; diff --git a/code/AssetLib/X3D/X3DExporter.hpp b/code/AssetLib/X3D/X3DExporter.hpp index 00115e0b6..fc8f9a921 100644 --- a/code/AssetLib/X3D/X3DExporter.hpp +++ b/code/AssetLib/X3D/X3DExporter.hpp @@ -63,9 +63,9 @@ class X3DExporter { // empty } - SAttribute(SAttribute && rhs) : - Name(std::move(rhs.Name)), - Value(std::move(rhs.Value)) { + SAttribute(SAttribute &&rhs) noexcept : + Name(rhs.Name), + Value(rhs.Value) { // empty } }; diff --git a/code/AssetLib/glTF/glTFAsset.h b/code/AssetLib/glTF/glTFAsset.h index da49a1737..4cef646d2 100644 --- a/code/AssetLib/glTF/glTFAsset.h +++ b/code/AssetLib/glTF/glTFAsset.h @@ -456,11 +456,10 @@ namespace glTF /// \param [in] pDecodedData - pointer to decoded data array. /// \param [in] pDecodedData_Length - size of encoded region, in bytes. /// \param [in] pID - ID of the region. - SEncodedRegion(const size_t pOffset, const size_t pEncodedData_Length, uint8_t* pDecodedData, const size_t pDecodedData_Length, const std::string pID) - : Offset(pOffset), EncodedData_Length(pEncodedData_Length), DecodedData(pDecodedData), DecodedData_Length(pDecodedData_Length), ID(pID) - {} + SEncodedRegion(const size_t pOffset, const size_t pEncodedData_Length, uint8_t *pDecodedData, const size_t pDecodedData_Length, const std::string &pID) : + Offset(pOffset), EncodedData_Length(pEncodedData_Length), DecodedData(pDecodedData), DecodedData_Length(pDecodedData_Length), ID(pID) {} - /// \fn ~SEncodedRegion() + /// \fn ~SEncodedRegion() /// Destructor. ~SEncodedRegion() { delete [] DecodedData; } }; @@ -1149,8 +1148,7 @@ namespace glTF void ReadExtensionsUsed(Document& doc); - - IOStream* OpenFile(std::string path, const char* mode, bool absolute = false); + IOStream *OpenFile(const std::string &path, const char *mode, bool absolute = false); }; } diff --git a/code/AssetLib/glTF/glTFAsset.inl b/code/AssetLib/glTF/glTFAsset.inl index 6e1e60846..e915a3aee 100644 --- a/code/AssetLib/glTF/glTFAsset.inl +++ b/code/AssetLib/glTF/glTFAsset.inl @@ -1377,7 +1377,7 @@ inline void Asset::ReadExtensionsUsed(Document &doc) { #undef CHECK_EXT } -inline IOStream *Asset::OpenFile(std::string path, const char *mode, bool absolute) { +inline IOStream *Asset::OpenFile(const std::string& path, const char *mode, bool absolute) { #ifdef ASSIMP_API (void)absolute; return mIOSystem->Open(path, mode); diff --git a/code/AssetLib/glTF/glTFExporter.cpp b/code/AssetLib/glTF/glTFExporter.cpp index 5e1992319..810263f52 100644 --- a/code/AssetLib/glTF/glTFExporter.cpp +++ b/code/AssetLib/glTF/glTFExporter.cpp @@ -405,8 +405,7 @@ void glTFExporter::ExportMaterials() * Search through node hierarchy and find the node containing the given meshID. * Returns true on success, and false otherwise. */ -bool FindMeshNode(Ref& nodeIn, Ref& meshNode, std::string meshID) -{ +bool FindMeshNode(Ref &nodeIn, Ref &meshNode, const std::string &meshID) { for (unsigned int i = 0; i < nodeIn->meshes.size(); ++i) { if (meshID.compare(nodeIn->meshes[i]->id) == 0) { meshNode = nodeIn; diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index 6aa0f92ed..eaacf8564 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -408,7 +408,7 @@ public: /// \param [in] pDecodedData - pointer to decoded data array. /// \param [in] pDecodedData_Length - size of encoded region, in bytes. /// \param [in] pID - ID of the region. - SEncodedRegion(const size_t pOffset, const size_t pEncodedData_Length, uint8_t *pDecodedData, const size_t pDecodedData_Length, const std::string pID) : + SEncodedRegion(const size_t pOffset, const size_t pEncodedData_Length, uint8_t *pDecodedData, const size_t pDecodedData_Length, const std::string &pID) : Offset(pOffset), EncodedData_Length(pEncodedData_Length), DecodedData(pDecodedData), @@ -1188,7 +1188,7 @@ private: void ReadExtensionsUsed(Document &doc); void ReadExtensionsRequired(Document &doc); - IOStream *OpenFile(std::string path, const char *mode, bool absolute = false); + IOStream *OpenFile(const std::string &path, const char *mode, bool absolute = false); }; inline std::string getContextForErrorMessages(const std::string &id, const std::string &name) { diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index b51ac20c2..ab81e3519 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -2132,7 +2132,7 @@ inline void Asset::ReadExtensionsUsed(Document &doc) { #undef CHECK_EXT } -inline IOStream *Asset::OpenFile(std::string path, const char *mode, bool /*absolute*/) { +inline IOStream *Asset::OpenFile(const std::string& path, const char *mode, bool /*absolute*/) { #ifdef ASSIMP_API return mIOSystem->Open(path, mode); #else diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 83356f7c2..0e7251b1e 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -798,8 +798,7 @@ void glTF2Exporter::ExportMaterials() * Search through node hierarchy and find the node containing the given meshID. * Returns true on success, and false otherwise. */ -bool FindMeshNode(Ref& nodeIn, Ref& meshNode, std::string meshID) -{ +bool FindMeshNode(Ref &nodeIn, Ref &meshNode, const std::string &meshID) { for (unsigned int i = 0; i < nodeIn->meshes.size(); ++i) { if (meshID.compare(nodeIn->meshes[i]->id) == 0) { meshNode = nodeIn; diff --git a/code/Pbrt/PbrtExporter.cpp b/code/Pbrt/PbrtExporter.cpp index b793c37f9..0be244600 100644 --- a/code/Pbrt/PbrtExporter.cpp +++ b/code/Pbrt/PbrtExporter.cpp @@ -106,14 +106,13 @@ void ExportScenePbrt ( } // end of namespace Assimp // Constructor -PbrtExporter::PbrtExporter ( - const aiScene* pScene, IOSystem* pIOSystem, - const std::string path, const std::string file) -: mScene(pScene), - mIOSystem(pIOSystem), - mPath(path), - mFile(file) -{ +PbrtExporter::PbrtExporter( + const aiScene *pScene, IOSystem *pIOSystem, + const std::string &path, const std::string &file) : + mScene(pScene), + mIOSystem(pIOSystem), + mPath(path), + mFile(file) { // Export embedded textures. if (mScene->mNumTextures > 0) if (!mIOSystem->CreateDirectory("textures")) @@ -210,12 +209,12 @@ void PbrtExporter::WriteMetaData() { aiString* value = static_cast(pMetaData->mValues[i].mData); std::string svalue = value->C_Str(); - std::size_t found = svalue.find_first_of("\n"); + std::size_t found = svalue.find_first_of('\n'); mOutput << "\n"; while (found != std::string::npos) { mOutput << "# " << svalue.substr(0, found) << "\n"; svalue = svalue.substr(found + 1); - found = svalue.find_first_of("\n"); + found = svalue.find_first_of('\n'); } mOutput << "# " << svalue << "\n"; break; @@ -596,8 +595,8 @@ void PbrtExporter::WriteMaterial(int m) { } mOutput << "\n"; - auto White = [](aiColor3D c) { return c.r == 1 && c.g == 1 && c.b == 1; }; - auto Black = [](aiColor3D c) { return c.r == 0 && c.g == 0 && c.b == 0; }; + auto White = [](const aiColor3D &c) { return c.r == 1 && c.g == 1 && c.b == 1; }; + auto Black = [](const aiColor3D &c) { return c.r == 0 && c.g == 0 && c.b == 0; }; aiColor3D diffuse, specular, transparency; bool constantDiffuse = (material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse) == AI_SUCCESS && diff --git a/code/Pbrt/PbrtExporter.h b/code/Pbrt/PbrtExporter.h index 167f318fc..e8ff03ccb 100644 --- a/code/Pbrt/PbrtExporter.h +++ b/code/Pbrt/PbrtExporter.h @@ -74,8 +74,8 @@ class PbrtExporter { public: /// Constructor for a specific scene to export - PbrtExporter(const aiScene* pScene, IOSystem* pIOSystem, - const std::string path, const std::string file); + PbrtExporter(const aiScene *pScene, IOSystem *pIOSystem, + const std::string &path, const std::string &file); /// Destructor virtual ~PbrtExporter(); diff --git a/code/PostProcessing/EmbedTexturesProcess.cpp b/code/PostProcessing/EmbedTexturesProcess.cpp index cb2853926..500032c39 100644 --- a/code/PostProcessing/EmbedTexturesProcess.cpp +++ b/code/PostProcessing/EmbedTexturesProcess.cpp @@ -100,7 +100,7 @@ void EmbedTexturesProcess::Execute(aiScene* pScene) { ASSIMP_LOG_INFO("EmbedTexturesProcess finished. Embedded ", embeddedTexturesCount, " textures." ); } -bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const { +bool EmbedTexturesProcess::addTexture(aiScene *pScene, const std::string &path) const { std::streampos imageSize = 0; std::string imagePath = path; diff --git a/code/PostProcessing/EmbedTexturesProcess.h b/code/PostProcessing/EmbedTexturesProcess.h index 5915e0d44..b33968850 100644 --- a/code/PostProcessing/EmbedTexturesProcess.h +++ b/code/PostProcessing/EmbedTexturesProcess.h @@ -78,7 +78,7 @@ public: private: // Resolve the path and add the file content to the scene as a texture. - bool addTexture(aiScene* pScene, std::string path) const; + bool addTexture(aiScene *pScene, const std::string &path) const; private: std::string mRootPath; diff --git a/test/unit/utTypes.cpp b/test/unit/utTypes.cpp index cc354eb3d..1ac9a1d5e 100644 --- a/test/unit/utTypes.cpp +++ b/test/unit/utTypes.cpp @@ -53,8 +53,8 @@ class utTypes : public ::testing::Test { TEST_F( utTypes, Color3dCpmpareOpTest ) { aiColor3D col1( 1, 2, 3 ); aiColor3D col2( 4, 5, 6 ); - aiColor3D col3( col1 ); - + const aiColor3D &col3(col1); + EXPECT_FALSE( col1 == col2 ); EXPECT_FALSE( col2 == col3 ); EXPECT_TRUE( col1 == col3 ); From 16508687015f383b423d710a2a1b13523c2fd9f9 Mon Sep 17 00:00:00 2001 From: Aaron Gokaslan Date: Tue, 22 Jun 2021 12:32:58 -0400 Subject: [PATCH 178/335] Remove redundant include --- code/AssetLib/IFC/IFCUtil.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/code/AssetLib/IFC/IFCUtil.h b/code/AssetLib/IFC/IFCUtil.h index 20b9a617c..e1dd064e1 100644 --- a/code/AssetLib/IFC/IFCUtil.h +++ b/code/AssetLib/IFC/IFCUtil.h @@ -56,8 +56,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include -#include - struct aiNode; namespace Assimp { From b17c2f29e9472e0d345a22f831562fc23ba703fd Mon Sep 17 00:00:00 2001 From: Aaron Gokaslan Date: Tue, 22 Jun 2021 12:44:36 -0400 Subject: [PATCH 179/335] Replace noexcept with proper macro --- code/AssetLib/X3D/X3DExporter.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/X3D/X3DExporter.hpp b/code/AssetLib/X3D/X3DExporter.hpp index fc8f9a921..fefaba9f3 100644 --- a/code/AssetLib/X3D/X3DExporter.hpp +++ b/code/AssetLib/X3D/X3DExporter.hpp @@ -63,7 +63,7 @@ class X3DExporter { // empty } - SAttribute(SAttribute &&rhs) noexcept : + SAttribute(SAttribute &&rhs) AI_NO_EXCEPT : Name(rhs.Name), Value(rhs.Value) { // empty From 36815b014b7c55e4a5a9a6677a7565dc392aa4ca Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 22 Jun 2021 20:05:16 +0200 Subject: [PATCH 180/335] Update FBXExporter.h --- code/AssetLib/FBX/FBXExporter.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/code/AssetLib/FBX/FBXExporter.h b/code/AssetLib/FBX/FBXExporter.h index 563183268..2b751ea26 100644 --- a/code/AssetLib/FBX/FBXExporter.h +++ b/code/AssetLib/FBX/FBXExporter.h @@ -64,10 +64,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. struct aiScene; struct aiNode; struct aiLight; -//struct aiMaterial; -namespace Assimp -{ +namespace Assimp { class IOSystem; class IOStream; class ExportProperties; From 544148a6263d6141acf157fb23d603583bb6b2ea Mon Sep 17 00:00:00 2001 From: Aaron Gokaslan Date: Tue, 22 Jun 2021 14:28:46 -0400 Subject: [PATCH 181/335] Fix code insertion duplication --- code/AssetLib/IFC/IFCUtil.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/IFC/IFCUtil.h b/code/AssetLib/IFC/IFCUtil.h index e1dd064e1..b18f35052 100644 --- a/code/AssetLib/IFC/IFCUtil.h +++ b/code/AssetLib/IFC/IFCUtil.h @@ -142,7 +142,7 @@ struct TempOpening TempOpening(const IFC::Schema_2x3::IfcSolidModel *solid, IfcVector3 extrusionDir, std::shared_ptr profileMesh, std::shared_ptr profileMesh2D) : - solid(solid), extrusionDir(extrusionDir), profileMesh(std::move(std::move(profileMesh))), profileMesh2D(std::move(std::move(profileMesh2D))) { + solid(solid), extrusionDir(extrusionDir), profileMesh(std::move(profileMesh)), profileMesh2D(std::move(profileMesh2D)) { } // ------------------------------------------------------------------------------ From 6170c49155b0e9f7678ab1ceb3cb96c720b7536c Mon Sep 17 00:00:00 2001 From: Pankaj Tyagi Date: Wed, 23 Jun 2021 16:49:09 +0530 Subject: [PATCH 182/335] Fixed: 1. FBX import is unable to read the texture UV rotation angle. 2. FBX export is unable to write the texture UV rotation angle. --- code/AssetLib/FBX/FBXConverter.cpp | 3 +++ code/AssetLib/FBX/FBXDocument.h | 5 +++++ code/AssetLib/FBX/FBXExporter.cpp | 7 +++++++ code/AssetLib/FBX/FBXMaterial.cpp | 5 +++++ 4 files changed, 20 insertions(+) diff --git a/code/AssetLib/FBX/FBXConverter.cpp b/code/AssetLib/FBX/FBXConverter.cpp index a564b3e9b..066b79871 100644 --- a/code/AssetLib/FBX/FBXConverter.cpp +++ b/code/AssetLib/FBX/FBXConverter.cpp @@ -1766,6 +1766,7 @@ void FBXConverter::TrySetTextureProperties(aiMaterial *out_mat, const TextureMap // XXX handle all kinds of UV transformations uvTrafo.mScaling = tex->UVScaling(); uvTrafo.mTranslation = tex->UVTranslation(); + uvTrafo.mRotation = tex->UVRotation(); out_mat->AddProperty(&uvTrafo, 1, _AI_MATKEY_UVTRANSFORM_BASE, target, 0); const PropertyTable &props = tex->Props(); @@ -1885,6 +1886,7 @@ void FBXConverter::TrySetTextureProperties(aiMaterial *out_mat, const LayeredTex // XXX handle all kinds of UV transformations uvTrafo.mScaling = tex->UVScaling(); uvTrafo.mTranslation = tex->UVTranslation(); + uvTrafo.mRotation = tex->UVRotation(); out_mat->AddProperty(&uvTrafo, 1, _AI_MATKEY_UVTRANSFORM_BASE, target, texIndex); const PropertyTable &props = tex->Props(); @@ -2324,6 +2326,7 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial *out_mat, const PropertyTa // XXX handle all kinds of UV transformations uvTrafo.mScaling = tex->UVScaling(); uvTrafo.mTranslation = tex->UVTranslation(); + uvTrafo.mRotation = tex->UVRotation(); out_mat->AddProperty(&uvTrafo, 1, (name + "|uvtrafo").c_str(), aiTextureType_UNKNOWN, 0); int uvIndex = 0; diff --git a/code/AssetLib/FBX/FBXDocument.h b/code/AssetLib/FBX/FBXDocument.h index 69cda1c1a..1ee526368 100644 --- a/code/AssetLib/FBX/FBXDocument.h +++ b/code/AssetLib/FBX/FBXDocument.h @@ -500,6 +500,10 @@ public: return uvScaling; } + const ai_real &UVRotation() const { + return uvRotation; + } + const PropertyTable& Props() const { ai_assert(props.get()); return *props.get(); @@ -517,6 +521,7 @@ public: private: aiVector2D uvTrans; aiVector2D uvScaling; + ai_real uvRotation; std::string type; std::string relativeFileName; diff --git a/code/AssetLib/FBX/FBXExporter.cpp b/code/AssetLib/FBX/FBXExporter.cpp index 6bdd0b5be..486e08da9 100644 --- a/code/AssetLib/FBX/FBXExporter.cpp +++ b/code/AssetLib/FBX/FBXExporter.cpp @@ -1688,6 +1688,10 @@ void FBXExporter::WriteObjects () // link the image data to the texture connections.emplace_back("C", "OO", image_uid, texture_uid); + aiUVTransform trafo; + unsigned int max = sizeof(aiUVTransform); + aiGetMaterialFloatArray(mat, AI_MATKEY_UVTRANSFORM(aiTextureType_DIFFUSE, 0), (float *)&trafo, &max); + // now write the actual texture node FBX::Node tnode("Texture"); // TODO: some way to determine texture name? @@ -1698,6 +1702,9 @@ void FBXExporter::WriteObjects () tnode.AddChild("Version", int32_t(202)); tnode.AddChild("TextureName", texture_name); FBX::Node p("Properties70"); + p.AddP70vectorA("Translation", trafo.mTranslation[0], trafo.mTranslation[1], 0.0); + p.AddP70vectorA("Rotation", 0, 0, trafo.mRotation); + p.AddP70vectorA("Scaling", trafo.mScaling[0], trafo.mScaling[1], 0.0); p.AddP70enum("CurrentTextureBlendMode", 0); // TODO: verify //p.AddP70string("UVSet", ""); // TODO: how should this work? p.AddP70bool("UseMaterial", 1); diff --git a/code/AssetLib/FBX/FBXMaterial.cpp b/code/AssetLib/FBX/FBXMaterial.cpp index 6ada9630b..aaa043c12 100644 --- a/code/AssetLib/FBX/FBXMaterial.cpp +++ b/code/AssetLib/FBX/FBXMaterial.cpp @@ -210,6 +210,11 @@ Texture::Texture(uint64_t id, const Element& element, const Document& doc, const uvTrans.y = trans.y; } + const aiVector3D &rotation = PropertyGet(*props, "Rotation", ok); + if (ok) { + uvRotation = rotation.z; + } + // resolve video links if(doc.Settings().readTextures) { const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID()); From 170063643cfaf86514b2d28d0c2d1ceae7df4791 Mon Sep 17 00:00:00 2001 From: Jerome St-Louis Date: Wed, 23 Jun 2021 18:11:52 -0400 Subject: [PATCH 183/335] include/material.h: Fixed broken C support - The aiGetMaterialFloat() and aiGetMaterialInteger() C preprocessor definitions were broken because: - They had a space before the opening parenthesis - Using material key definitions expanding 1 to argument into 3 breaks the invocation of macros expecting 5 parameters --- include/assimp/material.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/assimp/material.h b/include/assimp/material.h index 2024be07f..9b0c92b0d 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -1499,7 +1499,7 @@ ASSIMP_API C_ENUM aiReturn aiGetMaterialFloatArray( ai_real *pOut, unsigned int *pMax); -#ifdef __cplusplus +#if 1 //def __cplusplus // --------------------------------------------------------------------------- /** @brief Retrieve a single float property with a specific key from the material. @@ -1520,7 +1520,7 @@ ASSIMP_API C_ENUM aiReturn aiGetMaterialFloatArray( * @return Specifies whether the key has been found. If not, the output * float remains unmodified.*/ // --------------------------------------------------------------------------- -inline aiReturn aiGetMaterialFloat(const aiMaterial *pMat, +inline aiReturn aiGetMaterialFloat(const C_STRUCT aiMaterial *pMat, const char *pKey, unsigned int type, unsigned int index, @@ -1530,8 +1530,8 @@ inline aiReturn aiGetMaterialFloat(const aiMaterial *pMat, #else -// Use our friend, the C preprocessor -#define aiGetMaterialFloat (pMat, type, index, pKey, pOut) \ +// Use our friend, the C preprocessor // The macro does not work with e.g. AI_MATKEY_OPACITY expanding into 3 args +#define aiGetMaterialFloat(pMat, type, index, pKey, pOut) \ aiGetMaterialFloatArray(pMat, type, index, pKey, pOut, NULL) #endif //!__cplusplus @@ -1548,7 +1548,7 @@ ASSIMP_API C_ENUM aiReturn aiGetMaterialIntegerArray(const C_STRUCT aiMaterial * int *pOut, unsigned int *pMax); -#ifdef __cplusplus +#if 1 //def __cplusplus // --------------------------------------------------------------------------- /** @brief Retrieve an integer property with a specific key from a material @@ -1566,7 +1566,7 @@ inline aiReturn aiGetMaterialInteger(const C_STRUCT aiMaterial *pMat, #else // use our friend, the C preprocessor -#define aiGetMaterialInteger (pMat, type, index, pKey, pOut) \ +#define aiGetMaterialInteger(pMat, type, index, pKey, pOut) \ aiGetMaterialIntegerArray(pMat, type, index, pKey, pOut, NULL) #endif //!__cplusplus From 00bf7576883d7cf3edfffe51390855d293848a4e Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 24 Jun 2021 13:28:49 +0200 Subject: [PATCH 184/335] Update material.h --- include/assimp/material.h | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/include/assimp/material.h b/include/assimp/material.h index 9b0c92b0d..f0207c6de 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -1499,8 +1499,6 @@ ASSIMP_API C_ENUM aiReturn aiGetMaterialFloatArray( ai_real *pOut, unsigned int *pMax); -#if 1 //def __cplusplus - // --------------------------------------------------------------------------- /** @brief Retrieve a single float property with a specific key from the material. * @@ -1528,14 +1526,6 @@ inline aiReturn aiGetMaterialFloat(const C_STRUCT aiMaterial *pMat, return aiGetMaterialFloatArray(pMat, pKey, type, index, pOut, (unsigned int *)0x0); } -#else - -// Use our friend, the C preprocessor // The macro does not work with e.g. AI_MATKEY_OPACITY expanding into 3 args -#define aiGetMaterialFloat(pMat, type, index, pKey, pOut) \ - aiGetMaterialFloatArray(pMat, type, index, pKey, pOut, NULL) - -#endif //!__cplusplus - // --------------------------------------------------------------------------- /** @brief Retrieve an array of integer values with a specific key * from a material @@ -1548,8 +1538,6 @@ ASSIMP_API C_ENUM aiReturn aiGetMaterialIntegerArray(const C_STRUCT aiMaterial * int *pOut, unsigned int *pMax); -#if 1 //def __cplusplus - // --------------------------------------------------------------------------- /** @brief Retrieve an integer property with a specific key from a material * @@ -1563,14 +1551,6 @@ inline aiReturn aiGetMaterialInteger(const C_STRUCT aiMaterial *pMat, return aiGetMaterialIntegerArray(pMat, pKey, type, index, pOut, (unsigned int *)0x0); } -#else - -// use our friend, the C preprocessor -#define aiGetMaterialInteger(pMat, type, index, pKey, pOut) \ - aiGetMaterialIntegerArray(pMat, type, index, pKey, pOut, NULL) - -#endif //!__cplusplus - // --------------------------------------------------------------------------- /** @brief Retrieve a color value from the material property table * From d18d83881265714f0ef3d859449e0e5a44450b43 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 24 Jun 2021 16:18:11 +0200 Subject: [PATCH 185/335] Fix formatting --- code/AssetLib/glTF2/glTF2Asset.h | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index 25e917712..605cda332 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -388,16 +388,18 @@ struct CustomExtension { } CustomExtension() = default; - - CustomExtension(const CustomExtension &other) - : name(other.name) - , mStringValue(other.mStringValue) - , mDoubleValue(other.mDoubleValue) - , mUint64Value(other.mUint64Value) - , mInt64Value(other.mInt64Value) - , mBoolValue(other.mBoolValue) - , mValues(other.mValues) - { + + ~CustomExtension() = default; + + CustomExtension(const CustomExtension &other) : + name(other.name), + mStringValue(other.mStringValue), + mDoubleValue(other.mDoubleValue), + mUint64Value(other.mUint64Value), + mInt64Value(other.mInt64Value), + mBoolValue(other.mBoolValue), + mValues(other.mValues) { + // empty } }; From b38b65ff49bf9bb061b2dcec79f18266bc680f4d Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 24 Jun 2021 17:03:00 +0200 Subject: [PATCH 186/335] Add const --- code/AssetLib/glTF2/glTF2Asset.inl | 49 ++++++++++++++++-------------- 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 06a8b5c58..4bcfea5f7 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -4,7 +4,6 @@ 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, @@ -777,12 +776,13 @@ inline void Buffer::EncodedRegion_Mark(const size_t pOffset, const size_t pEncod } inline void Buffer::EncodedRegion_SetCurrent(const std::string &pID) { - if ((EncodedRegion_Current != nullptr) && (EncodedRegion_Current->ID == pID)) return; + if ((EncodedRegion_Current != nullptr) && (EncodedRegion_Current->ID == pID)) { + return; + } for (SEncodedRegion *reg : EncodedRegion_List) { if (reg->ID == pID) { EncodedRegion_Current = reg; - return; } } @@ -832,9 +832,10 @@ inline bool Buffer::ReplaceData_joint(const size_t pBufferData_Offset, const siz } inline size_t Buffer::AppendData(uint8_t *data, size_t length) { - size_t offset = this->byteLength; + const size_t offset = this->byteLength; + // Force alignment to 4 bits - size_t paddedLength = (length + 3) & ~3; + const size_t paddedLength = (length + 3) & ~3; Grow(paddedLength); memcpy(mData.get() + offset, data, length); memset(mData.get() + offset + length, 0, paddedLength - length); @@ -866,9 +867,7 @@ inline void Buffer::Grow(size_t amount) { // // struct BufferView // - inline void BufferView::Read(Value &obj, Asset &r) { - if (Value *bufferVal = FindUInt(obj, "buffer")) { buffer = r.buffers.Retrieve(bufferVal->GetUint()); } @@ -888,16 +887,21 @@ inline void BufferView::Read(Value &obj, Asset &r) { } inline uint8_t *BufferView::GetPointer(size_t accOffset) { - if (!buffer) return nullptr; + if (!buffer) { + return nullptr; + } uint8_t *basePtr = buffer->GetPointer(); - if (!basePtr) return nullptr; + if (!basePtr) { + return nullptr; + } size_t offset = accOffset + byteOffset; if (buffer->EncodedRegion_Current != nullptr) { const size_t begin = buffer->EncodedRegion_Current->Offset; const size_t end = begin + buffer->EncodedRegion_Current->DecodedData_Length; - if ((offset >= begin) && (offset < end)) + if ((offset >= begin) && (offset < end)) { return &buffer->EncodedRegion_Current->DecodedData[offset - begin]; + } } return basePtr + offset; @@ -923,18 +927,18 @@ inline void Accessor::Sparse::PatchData(unsigned int elementSize) { while (pIndices != indicesEnd) { size_t offset; switch (indicesType) { - case ComponentType_UNSIGNED_BYTE: - offset = *pIndices; - break; - case ComponentType_UNSIGNED_SHORT: - offset = *reinterpret_cast(pIndices); - break; - case ComponentType_UNSIGNED_INT: - offset = *reinterpret_cast(pIndices); - break; - default: - // have fun with float and negative values from signed types as indices. - throw DeadlyImportError("Unsupported component type in index."); + case ComponentType_UNSIGNED_BYTE: + offset = *pIndices; + break; + case ComponentType_UNSIGNED_SHORT: + offset = *reinterpret_cast(pIndices); + break; + case ComponentType_UNSIGNED_INT: + offset = *reinterpret_cast(pIndices); + break; + default: + // have fun with float and negative values from signed types as indices. + throw DeadlyImportError("Unsupported component type in index."); } offset *= elementSize; @@ -946,7 +950,6 @@ inline void Accessor::Sparse::PatchData(unsigned int elementSize) { } inline void Accessor::Read(Value &obj, Asset &r) { - if (Value *bufferViewVal = FindUInt(obj, "bufferView")) { bufferView = r.bufferViews.Retrieve(bufferViewVal->GetUint()); } From 0e41efb05008c94ed256c887e73b9f15a0abb872 Mon Sep 17 00:00:00 2001 From: Jerome St-Louis Date: Thu, 24 Jun 2021 13:25:44 -0400 Subject: [PATCH 187/335] glTF2: Improved support for AI_MATKEY_OPACITY - Exporter: Writing opacity to pbrMetallicRoughness.baseColorFactor[3] even when alphaMode is set - Importer: Setting AI_MATKEY_OPACITY from pbrMetallicRoughness.baseColorFactor[3] --- code/AssetLib/glTF2/glTF2Exporter.cpp | 16 +++++++--------- code/AssetLib/glTF2/glTF2Importer.cpp | 1 + 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 3ed27a035..d45e04a5d 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -778,19 +778,17 @@ void glTF2Exporter::ExportMaterials() mat.Get(AI_MATKEY_TWOSIDED, m->doubleSided); mat.Get(AI_MATKEY_GLTF_ALPHACUTOFF, m->alphaCutoff); + float opacity; aiString alphaMode; + if (mat.Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) { + if (opacity < 1) { + m->alphaMode = "BLEND"; + m->pbrMetallicRoughness.baseColorFactor[3] *= opacity; + } + } if (mat.Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode) == AI_SUCCESS) { m->alphaMode = alphaMode.C_Str(); - } else { - float opacity; - - if (mat.Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) { - if (opacity < 1) { - m->alphaMode = "BLEND"; - m->pbrMetallicRoughness.baseColorFactor[3] *= opacity; - } - } } { diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index d2fb1e9b8..1a04ed8b0 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -267,6 +267,7 @@ static aiMaterial *ImportMaterial(std::vector &embeddedTexIdxs, Asset &r, M SetMaterialColorProperty(r, mat.emissiveFactor, aimat, AI_MATKEY_COLOR_EMISSIVE); aimat->AddProperty(&mat.doubleSided, 1, AI_MATKEY_TWOSIDED); + aimat->AddProperty(&mat.pbrMetallicRoughness.baseColorFactor[3], 1, AI_MATKEY_OPACITY); aiString alphaMode(mat.alphaMode); aimat->AddProperty(&alphaMode, AI_MATKEY_GLTF_ALPHAMODE); From 7c822f23bd075d3621d2e2f33188e1659f657b31 Mon Sep 17 00:00:00 2001 From: Promit Roy Date: Sun, 27 Jun 2021 00:53:40 -0400 Subject: [PATCH 188/335] Added support for custom properties ("extras") in glTF2 --- code/AssetLib/glTF2/glTF2Asset.h | 2 ++ code/AssetLib/glTF2/glTF2Asset.inl | 13 ++++++++++--- code/AssetLib/glTF2/glTF2Importer.cpp | 17 +++++++++++++++-- 3 files changed, 27 insertions(+), 5 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index 605cda332..9824be149 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -411,6 +411,7 @@ struct Object { std::string name; //!< The user-defined name of this object CustomExtension customExtensions; + CustomExtension extras; //! Objects marked as special are not exported (used to emulate the binary body buffer) virtual bool IsSpecial() const { return false; } @@ -428,6 +429,7 @@ struct Object { inline Value *FindExtension(Value &val, const char *extensionId); inline void ReadExtensions(Value &val); + inline void ReadExtras(Value &val); }; // diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 4bcfea5f7..7bfaa81f5 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -372,6 +372,12 @@ inline void Object::ReadExtensions(Value &val) { } } +inline void Object::ReadExtras(Value &val) { + if (Value *curExtras = FindObject(val, "extras")) { + this->extras = glTF2::ReadExtensions("extras", *curExtras); + } +} + #ifdef ASSIMP_ENABLE_DRACO template @@ -612,6 +618,7 @@ Ref LazyDict::Retrieve(unsigned int i) { ReadMember(obj, "name", inst->name); inst->Read(obj, mAsset); inst->ReadExtensions(obj); + inst->ReadExtras(obj); Ref result = Add(inst.release()); mRecursiveReferenceCheck.erase(i); @@ -1661,9 +1668,9 @@ inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) { } } - Value *extras = FindObject(pJSON_Object, "extras"); - if (nullptr != extras) { - if (Value *curTargetNames = FindArray(*extras, "targetNames")) { + Value *curExtras = FindObject(pJSON_Object, "extras"); + if (nullptr != curExtras) { + if (Value *curTargetNames = FindArray(*curExtras, "targetNames")) { this->targetNames.resize(curTargetNames->Size()); for (unsigned int i = 0; i < curTargetNames->Size(); ++i) { Value &targetNameValue = (*curTargetNames)[i]; diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index d2fb1e9b8..4827a9060 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -998,6 +998,14 @@ void ParseExtensions(aiMetadata *metadata, const CustomExtension &extension) { } } +void ParseExtras(aiMetadata *metadata, const CustomExtension &extension) { + if (extension.mValues.isPresent) { + for (size_t i = 0; i < extension.mValues.value.size(); ++i) { + ParseExtensions(metadata, extension.mValues.value[i]); + } + } +} + aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector &meshOffsets, glTF2::Ref &ptr) { Node &node = *ptr; @@ -1016,9 +1024,14 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector & } } - if (node.customExtensions) { + if (node.customExtensions || node.extras) { ainode->mMetaData = new aiMetadata; - ParseExtensions(ainode->mMetaData, node.customExtensions); + if (node.customExtensions) { + ParseExtensions(ainode->mMetaData, node.customExtensions); + } + if (node.extras) { + ParseExtras(ainode->mMetaData, node.extras); + } } GetNodeTransform(ainode->mTransformation, node); From 0a48a35382236dd46f7a1decd8c57480edbd92e5 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 29 Jun 2021 21:22:22 +0200 Subject: [PATCH 189/335] closes https://github.com/assimp/assimp/issues/3971: fix wrong dependency --- tools/assimp_view/AnimEvaluator.cpp | 5 +++-- tools/assimp_view/AnimEvaluator.h | 24 +++++++++++++++--------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/tools/assimp_view/AnimEvaluator.cpp b/tools/assimp_view/AnimEvaluator.cpp index df5167923..5a2ddc182 100644 --- a/tools/assimp_view/AnimEvaluator.cpp +++ b/tools/assimp_view/AnimEvaluator.cpp @@ -39,9 +39,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -#include "assimp_view.h" +#include "AnimEvaluator.h" -#include +#include +#include using namespace AssimpView; diff --git a/tools/assimp_view/AnimEvaluator.h b/tools/assimp_view/AnimEvaluator.h index 950763081..566247604 100644 --- a/tools/assimp_view/AnimEvaluator.h +++ b/tools/assimp_view/AnimEvaluator.h @@ -1,4 +1,3 @@ -/** Calculates a pose for a given time of an animation */ /* --------------------------------------------------------------------------- Open Asset Import Library (assimp) @@ -40,11 +39,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ +#pragma once #ifndef AV_ANIMEVALUATOR_H_INCLUDED #define AV_ANIMEVALUATOR_H_INCLUDED +/** Calculates a pose for a given time of an animation */ + #include #include +#include + +struct aiAnimation; namespace AssimpView { @@ -63,18 +68,19 @@ public: /// @brief The class destructor. ~AnimEvaluator(); - /** Evaluates the animation tracks for a given time stamp. The calculated pose can be retrieved as a - * array of transformation matrices afterwards by calling GetTransformations(). - * @param pTime The time for which you want to evaluate the animation, in seconds. Will be mapped into the animation cycle, so - * it can be an arbitrary value. Best use with ever-increasing time stamps. - */ + /// @brief Evaluates the animation tracks for a given time stamp. + /// The calculated pose can be retrieved as an array of transformation + /// matrices afterwards by calling GetTransformations(). + /// @param pTime The time for which you want to evaluate the animation, in seconds. + /// Will be mapped into the animation cycle, so it can get an arbitrary + /// value. Best use with ever-increasing time stamps. void Evaluate(double pTime); - /** Returns the transform matrices calculated at the last Evaluate() call. The array matches the mChannels array of - * the aiAnimation. */ + /// @brief Returns the transform matrices calculated at the last Evaluate() call. + /// The array matches the mChannels array of the aiAnimation. const std::vector &GetTransformations() const { return mTransforms; } -protected: +private: const aiAnimation *mAnim; double mLastTime; std::vector> mLastPositions; From 391080d2e61025910b879967876b6340ff443aec Mon Sep 17 00:00:00 2001 From: "DESKTOP-HEFITKI\\Iraj" <1mohtashamiraj@gmail.com> Date: Thu, 1 Jul 2021 15:01:39 +0430 Subject: [PATCH 190/335] Update Gitignore exclude x64 folder generate by build --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 0a999d3aa..7849cab65 100644 --- a/.gitignore +++ b/.gitignore @@ -25,7 +25,7 @@ CMakeSettings.json # Output bin/ lib/ - +x64/ # QtCreator CMakeLists.txt.user From 3cbd31900c9ce9d893747e25112d212cab938a70 Mon Sep 17 00:00:00 2001 From: arkeon Date: Thu, 1 Jul 2021 17:23:21 +0200 Subject: [PATCH 191/335] Manage /R/N lines ends correctly on binary files, tested with solidworks PLY export --- code/AssetLib/Ply/PlyParser.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/code/AssetLib/Ply/PlyParser.cpp b/code/AssetLib/Ply/PlyParser.cpp index 59cb6b976..8af0edfdc 100644 --- a/code/AssetLib/Ply/PlyParser.cpp +++ b/code/AssetLib/Ply/PlyParser.cpp @@ -419,8 +419,7 @@ bool PLY::DOM::ParseHeader(IOStreamBuffer &streamBuffer, std::vector if (PLY::Element::ParseElement(streamBuffer, buffer, &out)) { // add the element to the list of elements alElements.push_back(out); - } else if ( TokenMatch(buffer, "end_header\r", 11) || //checks for header end with /r/n ending - TokenMatch(buffer, "end_header", 10)) { //checks for /n ending, if it doesn't end with /r/n + } else if (TokenMatch(buffer, "end_header", 10)) { //checks for /n ending, if it doesn't end with /r/n // we have reached the end of the header break; } else { @@ -501,6 +500,11 @@ bool PLY::DOM::ParseInstanceBinary(IOStreamBuffer &streamBuffer, DOM *p_pc } streamBuffer.getNextBlock(buffer); + + // remove first char if it's /n in case of file with /r/n + if (((char *)&buffer[0])[0] == '\n') + buffer.erase(buffer.begin(), buffer.begin() + 1); + unsigned int bufferSize = static_cast(buffer.size()); const char *pCur = (char *)&buffer[0]; if (!p_pcOut->ParseElementInstanceListsBinary(streamBuffer, buffer, pCur, bufferSize, loader, p_bBE)) { From 1b37b74f9e95844a4603623335378c5e87bb23e7 Mon Sep 17 00:00:00 2001 From: Rahul Sheth Date: Wed, 7 Jul 2021 17:01:19 -0400 Subject: [PATCH 192/335] Hunter fixes for stb_image --- CMakeLists.txt | 5 ++--- code/AssetLib/M3D/M3DWrapper.h | 2 +- code/CMakeLists.txt | 19 ++++++++++++++++--- code/Common/Assimp.cpp | 2 +- code/Pbrt/PbrtExporter.cpp | 2 +- contrib/{stb_image => stb}/stb_image.h | 0 6 files changed, 21 insertions(+), 9 deletions(-) rename contrib/{stb_image => stb}/stb_image.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index fc1366a1a..2dfe592bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,8 +46,8 @@ option(ASSIMP_HUNTER_ENABLED "Enable Hunter package manager support" OFF) IF(ASSIMP_HUNTER_ENABLED) include("cmake/HunterGate.cmake") HunterGate( - URL "https://github.com/cpp-pm/hunter/archive/v0.23.293.tar.gz" - SHA1 "e8e5470652db77149d9b38656db2a6c0b7642693" + URL "https://github.com/cpp-pm/hunter/archive/v0.23.311.tar.gz" + SHA1 "1a82b9b73055879181cb1466b2ab5d48ee8ae410" ) add_definitions(-DASSIMP_USE_HUNTER) @@ -227,7 +227,6 @@ INCLUDE_DIRECTORIES( BEFORE include ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_BINARY_DIR}/include - contrib/ ) LIST(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake-modules" ) diff --git a/code/AssetLib/M3D/M3DWrapper.h b/code/AssetLib/M3D/M3DWrapper.h index b5d1785b2..54d7a2eec 100644 --- a/code/AssetLib/M3D/M3DWrapper.h +++ b/code/AssetLib/M3D/M3DWrapper.h @@ -57,7 +57,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Share stb_image's PNG loader with other importers/exporters instead of bringing our own copy. #define STBI_ONLY_PNG -#include +#include #include "m3d.h" diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index fe4abc06f..fd92d41e3 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -873,6 +873,7 @@ ELSE() ../contrib/pugixml/src/pugiconfig.hpp ../contrib/pugixml/src/pugixml.hpp ) + INCLUDE_DIRECTORIES("../contrib/pugixml/src") SOURCE_GROUP( Contrib\\Pugixml FILES ${Pugixml_SRCS}) ENDIF() @@ -1034,8 +1035,6 @@ IF(ASSIMP_HUNTER_ENABLED) find_package(RapidJSON CONFIG REQUIRED) ELSE() INCLUDE_DIRECTORIES("../contrib/rapidjson/include") - INCLUDE_DIRECTORIES( "../contrib" ) - INCLUDE_DIRECTORIES("../contrib/pugixml/src") ADD_DEFINITIONS( -DRAPIDJSON_HAS_STDSTRING=1) option( ASSIMP_RAPIDJSON_NO_MEMBER_ITERATOR "Suppress rapidjson warning on MSVC (NOTE: breaks android build)" ON ) if(ASSIMP_RAPIDJSON_NO_MEMBER_ITERATOR) @@ -1043,6 +1042,18 @@ ELSE() endif() ENDIF() +# stb +IF(ASSIMP_HUNTER_ENABLED) + hunter_add_package(stb) + find_package(stb CONFIG REQUIRED) +ELSE() + SET( stb_SRCS + ../contrib/stb/stb_image.h + ) + INCLUDE_DIRECTORIES("../contrib") + SOURCE_GROUP( Contrib\\stb FILES ${stb_SRCS}) +ENDIF() + # VC2010 fixes if(MSVC10) option( VC10_STDINT_FIX "Fix for VC10 Compiler regarding pstdint.h redefinition errors" OFF ) @@ -1101,6 +1112,7 @@ SET( assimp_src ${open3dgc_SRCS} ${ziplib_SRCS} ${Pugixml_SRCS} + ${stb_SRCS} # Necessary to show the headers in the project when using the VC++ generator: ${PUBLIC_HEADERS} @@ -1158,8 +1170,9 @@ IF(ASSIMP_HUNTER_ENABLED) utf8cpp zip::zip pugixml + stb::stb ) - + if (ASSIMP_BUILD_DRACO) target_link_libraries(assimp PUBLIC ${draco_LIBRARIES}) endif() diff --git a/code/Common/Assimp.cpp b/code/Common/Assimp.cpp index a6c539bca..ca0912979 100644 --- a/code/Common/Assimp.cpp +++ b/code/Common/Assimp.cpp @@ -1277,7 +1277,7 @@ ASSIMP_API void aiQuaternionInterpolate( # endif # define STB_IMAGE_IMPLEMENTATION -# include "stb_image/stb_image.h" +# include "stb/stb_image.h" # if _MSC_VER # pragma warning(pop) diff --git a/code/Pbrt/PbrtExporter.cpp b/code/Pbrt/PbrtExporter.cpp index 260d90322..1c7024c28 100644 --- a/code/Pbrt/PbrtExporter.cpp +++ b/code/Pbrt/PbrtExporter.cpp @@ -83,7 +83,7 @@ Other: #include #include -#include "../contrib/stb_image/stb_image.h" +#include "stb/stb_image.h" using namespace Assimp; diff --git a/contrib/stb_image/stb_image.h b/contrib/stb/stb_image.h similarity index 100% rename from contrib/stb_image/stb_image.h rename to contrib/stb/stb_image.h From e49ee6cfb25ac0bd02f51e3a9f587647d1b9a30e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20L=C3=B3pez=20Antequera?= Date: Thu, 8 Jul 2021 09:51:05 +0200 Subject: [PATCH 193/335] PyAssimp fix: don't always search anaconda paths The helper was erroneously adding the Anaconda paths to the search. --- port/PyAssimp/pyassimp/helper.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/port/PyAssimp/pyassimp/helper.py b/port/PyAssimp/pyassimp/helper.py index 7a4b2bdcb..7c14f6097 100644 --- a/port/PyAssimp/pyassimp/helper.py +++ b/port/PyAssimp/pyassimp/helper.py @@ -34,7 +34,8 @@ if os.name=='posix': additional_dirs.extend([item for item in os.environ['LD_LIBRARY_PATH'].split(':') if item]) # check if running from anaconda. - if "conda" or "continuum" in sys.version.lower(): + anaconda_keywords = ("conda", "continuum") + if any(k in sys.version.lower() for k in anaconda_keywords): cur_path = get_python_lib() pattern = re.compile('.*\/lib\/') conda_lib = pattern.match(cur_path).group() From 206b2436d4d76c974695f8e05c618c183f728a24 Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Tue, 13 Jul 2021 16:47:31 +0100 Subject: [PATCH 194/335] Apply clangformat --- code/AssetLib/Collada/ColladaParser.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/code/AssetLib/Collada/ColladaParser.cpp b/code/AssetLib/Collada/ColladaParser.cpp index 3166136b2..2e77035ca 100644 --- a/code/AssetLib/Collada/ColladaParser.cpp +++ b/code/AssetLib/Collada/ColladaParser.cpp @@ -170,10 +170,10 @@ ColladaParser::ColladaParser(IOSystem *pIOHandler, const std::string &pFile) : // ------------------------------------------------------------------------------------------------ // Destructor, private as well ColladaParser::~ColladaParser() { - for (auto & it : mNodeLibrary) { + for (auto &it : mNodeLibrary) { delete it.second; } - for (auto & it : mMeshLibrary) { + for (auto &it : mMeshLibrary) { delete it.second; } } @@ -396,7 +396,7 @@ void ColladaParser::ReadAnimationClipLibrary(XmlNode &node) { std::string animName; if (!XmlParser::getStdStrAttribute(node, "name", animName)) { - if (!XmlParser::getStdStrAttribute( node, "id", animName )) { + if (!XmlParser::getStdStrAttribute(node, "id", animName)) { animName = std::string("animation_") + ai_to_string(mAnimationClipLibrary.size()); } } @@ -420,7 +420,7 @@ void ColladaParser::ReadAnimationClipLibrary(XmlNode &node) { void ColladaParser::PostProcessControllers() { std::string meshId; - for (auto & it : mControllerLibrary) { + for (auto &it : mControllerLibrary) { meshId = it.second.mMeshId; if (meshId.empty()) { continue; @@ -445,7 +445,7 @@ void ColladaParser::PostProcessRootAnimations() { } Animation temp; - for (auto & it : mAnimationClipLibrary) { + for (auto &it : mAnimationClipLibrary) { std::string clipName = it.first; Animation *clip = new Animation(); @@ -552,7 +552,7 @@ void ColladaParser::ReadAnimation(XmlNode &node, Collada::Animation *pParent) { pParent->mSubAnims.push_back(anim); } - for (const auto & channel : channels) { + for (const auto &channel : channels) { anim->mChannels.push_back(channel.second); } @@ -644,7 +644,7 @@ void ColladaParser::ReadController(XmlNode &node, Collada::Controller &controlle } else if (currentName == "skin") { std::string id; if (XmlParser::getStdStrAttribute(currentNode, "source", id)) { - controller.mMeshId = id.substr(1, id.size()-1); + controller.mMeshId = id.substr(1, id.size() - 1); } } else if (currentName == "bind_shape_matrix") { std::string v; @@ -698,7 +698,7 @@ void ColladaParser::ReadControllerJoints(XmlNode &node, Collada::Controller &pCo } else if (strcmp(attrSemantic, "INV_BIND_MATRIX") == 0) { pController.mJointOffsetMatrixSource = attrSource; } else { - throw DeadlyImportError("Unknown semantic \"" , attrSemantic , "\" in data element"); + throw DeadlyImportError("Unknown semantic \"", attrSemantic, "\" in data element"); } } } @@ -708,7 +708,7 @@ void ColladaParser::ReadControllerJoints(XmlNode &node, Collada::Controller &pCo // Reads the joint weights for the given controller void ColladaParser::ReadControllerWeights(XmlNode &node, Collada::Controller &pController) { // Read vertex count from attributes and resize the array accordingly - int vertexCount=0; + int vertexCount = 0; XmlParser::getIntAttribute(node, "count", vertexCount); pController.mWeightCounts.resize(vertexCount); @@ -723,7 +723,7 @@ void ColladaParser::ReadControllerWeights(XmlNode &node, Collada::Controller &pC // local URLS always start with a '#'. We don't support global URLs if (attrSource[0] != '#') { - throw DeadlyImportError( "Unsupported URL format in \"", attrSource, "\" in source attribute of data element"); + throw DeadlyImportError("Unsupported URL format in \"", attrSource, "\" in source attribute of data element"); } channel.mAccessor = attrSource + 1; @@ -777,7 +777,7 @@ void ColladaParser::ReadImageLibrary(XmlNode &node) { const std::string ¤tName = currentNode.name(); if (currentName == "image") { std::string id; - if (XmlParser::getStdStrAttribute( currentNode, "id", id )) { + if (XmlParser::getStdStrAttribute(currentNode, "id", id)) { mImageLibrary[id] = Image(); // read on from there ReadImage(currentNode, mImageLibrary[id]); @@ -1727,7 +1727,7 @@ size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vector 0) { + if (pNumPrimitives > 0) { std::string v; XmlParser::getValueAsString(node, v); const char *content = v.c_str(); From bff1d012bd3791453bb513320ec88388f5acc0e4 Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Tue, 13 Jul 2021 16:50:09 +0100 Subject: [PATCH 195/335] Read matrix and input (shared) correctly may have leading and trailing whitespace set attribute is unsigned. It is also optional, default 0 --- code/AssetLib/Collada/ColladaParser.cpp | 199 ++++++++++++------------ test/models/Collada/human.zae | Bin 0 -> 1093924 bytes test/unit/utColladaImportExport.cpp | 22 +++ 3 files changed, 120 insertions(+), 101 deletions(-) create mode 100644 test/models/Collada/human.zae diff --git a/code/AssetLib/Collada/ColladaParser.cpp b/code/AssetLib/Collada/ColladaParser.cpp index 2e77035ca..a58cc6003 100644 --- a/code/AssetLib/Collada/ColladaParser.cpp +++ b/code/AssetLib/Collada/ColladaParser.cpp @@ -529,7 +529,7 @@ void ColladaParser::ReadAnimation(XmlNode &node, Collada::Animation *pParent) { // have it read into a channel ChannelMap::iterator newChannel = channels.insert(std::make_pair(id, AnimationChannel())).first; ReadAnimationSampler(currentNode, newChannel->second); - } + } } else if (currentName == "channel") { std::string source_name, target; XmlParser::getStdStrAttribute(currentNode, "source", source_name); @@ -627,7 +627,7 @@ void ColladaParser::ReadController(XmlNode &node, Collada::Controller &controlle XmlNode currentNode; while (xmlIt.getNext(currentNode)) { - //for (XmlNode ¤tNode : node.children()) { + //for (XmlNode ¤tNode : node.children()) { const std::string ¤tName = currentNode.name(); if (currentName == "morph") { controller.mType = Morph; @@ -907,7 +907,7 @@ void ColladaParser::ReadCameraLibrary(XmlNode &node) { if (!name.empty()) { cam.mName = name; } - ReadCamera(currentNode, cam); + ReadCamera(currentNode, cam); } } } @@ -1361,8 +1361,8 @@ void ColladaParser::ReadMesh(XmlNode &node, Mesh &pMesh) { } else if (currentName == "vertices") { ReadVertexData(currentNode, pMesh); } else if (currentName == "triangles" || currentName == "lines" || currentName == "linestrips" || - currentName == "polygons" || currentName == "polylist" || currentName == "trifans" || - currentName == "tristrips") { + currentName == "polygons" || currentName == "polylist" || currentName == "trifans" || + currentName == "tristrips") { ReadIndexData(currentNode, pMesh); } } @@ -1674,12 +1674,9 @@ void ColladaParser::ReadInputChannel(XmlNode &node, std::vector &p // read set if texture coordinates if (channel.mType == IT_Texcoord || channel.mType == IT_Color) { - int attrSet = -1; - if (XmlParser::hasAttribute(node, "set")) { - XmlParser::getIntAttribute(node, "set", attrSet); - } - - channel.mIndex = attrSet; + unsigned int attrSet = 0; + if (XmlParser::getUIntAttribute(node, "set", attrSet)) + channel.mIndex = attrSet; } // store, if valid type @@ -1704,20 +1701,20 @@ size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vectormSize; ++i) { + result[static_cast(i)] = obj[pInput.mResolved->mSubOffset[i]]; } + pMesh.mColors[pInput.mIndex].push_back(result); + } else { + ASSIMP_LOG_ERROR("Collada: too many vertex color sets. Skipping."); + } - // ignore all bitangent streams except 0 - there can be only one bitangent - if (pInput.mIndex == 0) { - pMesh.mBitangents.push_back(aiVector3D(obj[0], obj[1], obj[2])); - } else { - ASSIMP_LOG_ERROR("Collada: just one vertex bitangent stream supported"); - } - break; - case IT_Texcoord: - // up to 4 texture coord sets are fine, ignore the others - if (pInput.mIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS) { - // pad to current vertex count if necessary - if (pMesh.mTexCoords[pInput.mIndex].size() < pMesh.mPositions.size() - 1) - pMesh.mTexCoords[pInput.mIndex].insert(pMesh.mTexCoords[pInput.mIndex].end(), - pMesh.mPositions.size() - pMesh.mTexCoords[pInput.mIndex].size() - 1, aiVector3D(0, 0, 0)); - - pMesh.mTexCoords[pInput.mIndex].push_back(aiVector3D(obj[0], obj[1], obj[2])); - if (0 != acc.mSubOffset[2] || 0 != acc.mSubOffset[3]) { - pMesh.mNumUVComponents[pInput.mIndex] = 3; - } - } else { - ASSIMP_LOG_ERROR("Collada: too many texture coordinate sets. Skipping."); - } - break; - case IT_Color: - // up to 4 color sets are fine, ignore the others - if (pInput.mIndex < AI_MAX_NUMBER_OF_COLOR_SETS) { - // pad to current vertex count if necessary - if (pMesh.mColors[pInput.mIndex].size() < pMesh.mPositions.size() - 1) - pMesh.mColors[pInput.mIndex].insert(pMesh.mColors[pInput.mIndex].end(), - pMesh.mPositions.size() - pMesh.mColors[pInput.mIndex].size() - 1, aiColor4D(0, 0, 0, 1)); - - aiColor4D result(0, 0, 0, 1); - for (size_t i = 0; i < pInput.mResolved->mSize; ++i) { - result[static_cast(i)] = obj[pInput.mResolved->mSubOffset[i]]; - } - pMesh.mColors[pInput.mIndex].push_back(result); - } else { - ASSIMP_LOG_ERROR("Collada: too many vertex color sets. Skipping."); - } - - break; - default: - // IT_Invalid and IT_Vertex - ai_assert(false && "shouldn't ever get here"); + break; + default: + // IT_Invalid and IT_Vertex + ai_assert(false && "shouldn't ever get here"); } } @@ -2170,10 +2167,10 @@ void ColladaParser::ReadNodeTransformation(XmlNode &node, Node *pNode, Transform // read as many parameters and store in the transformation for (unsigned int a = 0; a < sNumParameters[pType]; a++) { + // skip whitespace before the number + SkipSpacesAndLineEnd(&content); // read a number content = fast_atoreal_move(content, tf.f[a]); - // skip whitespace after it - SkipSpacesAndLineEnd(&content); } // place the transformation at the queue of the node diff --git a/test/models/Collada/human.zae b/test/models/Collada/human.zae new file mode 100644 index 0000000000000000000000000000000000000000..691b09f83f6ac2baf2d6bb55026da9b95470c536 GIT binary patch literal 1093924 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

uuGR&r@9o{lX?*e|=%DZ$(yMTN$V&+tmT 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

Ao$|V)IS!XrjDofR+cp% zBTuc*(3BUXT>BS4WPR>_!aB=J=uc=7^yJQ0FL^&TZFGqGg?EU}iH>xSc-zFxo(G>= zi;m3eXRSZ>L9dJR9-sDa)Ve$mo*cjV_p|R)ll%SOmFK||_urEp2DZ#0VxIFp?38zp z=(hl#zRb6p=_W;UXoi`%?uR0|8S7*Iy2);3evnL}SZ=GnHJ>n{y>^c z(k3)bnr^#s?tR<9rGzn@zArjpEsMSnvO1c=LCpN&hMRgIU zo*H7JcR#~U3spLFQqmaiq&kkVA?phN>lA%nSB!A5)JMzlc7C^6V>J)Z2iKK+kS5SkMSnb$^ANhqc{F&cl-A z1Pg8zaV}x)xHxdUSh$_rk}|EK?gI|pkW8kEeI@M*Ti-x492ixC5abxSj(Jrdu(chlTzQNhibyLvq5rADz+P_QkQ0f zd}NpY$F_UmRvT+LC^&5_>=5J^;ui|qTfo!iIXO9?7cIn1x&`tqP$r2SoBKWPwtV4p zlB66A%GzXj26;Zw8)I8wh6n=Vwkc&wy119HZ>xdQ@LK#r8IzyiKeDQKC0QzH!i_o{ zc;XvE@FBiHIv3Dddr6amL{90xkk%o=0>x74W+8-2X`c+pR)c{j+VIng6SuVQj&0gF z2Y$~6W;ce=M5@3n!Ddk15EMFWj%NStK*04A@?hdxY@x-{tw|PlNB}1ieS_{(vNjer z$bKejk1278&F1K=l|mzy1jp0n6co}-OCoD#!=MExo8K&ZFaRL5oN6}fp9RK)~P8 zk})M-5+Dw$`h?KoofR0m4kl#m5J9}pY>F3?f71ZXZ}QC}RVz?(oqVq(8Vxm&7RV;j z(4!un8e4_WKx15pQ7i)%v6GgD0po*CndB~{p^#Mh-#XsURs93?Y!;ElPDf8P~f*zM#xc`*Wm!1tlA6N6;p|j*t z*mfx~2oXosB47>4#_VfH2dF@1(hW-A0{JGf{gr0XE5WO{F=9DVpj;Mi({`xFWOWJn zZ37gLAT>I1z|=BxfP^E36h>%lgUJ1xwtt)xGG6gY<=Y^+!?owrUlTje=00n%TwT2{juOr+}cV4^3_N64)*Z z%!^EZph|S{PUcn%4_YXz#ojJ5HS2yUtFLL^u7vP@K26%Z%pzsbR*q0s>(dV)$g5BI=vqNn9KBH@YGIeAZ+w0gPW1uO0 zq0M%ZZeVYxfM!myga_9(BaTZcOwZ9$3J_5vcKr|z^IZBp#%e^Z%)JtxAoa>zA)q_F z-}@CMnXVg&I4uP1Uor7LOHX~%(ZmrVYD(QP7bfkY{Fo`Sa(B~< z$jdUR=5yj}NZh)GAP7ju`U*Q8CX0!I9IJsa?0_7UPaCTw^Th%Q%9d_j0!Z6mjA3ad zp7b{g*q)4>I%Gq$O3J6y?b?T%_p2eC}xx>ko7fDuy#X zjhq~uAq!*2bUQw6fY6fXZ_}=!<6+HgN;3I}1}jQ)1Mso!6E>&VOeidKW5B9%Hh60GkL1e2xyiFHgjQOJ zRn^cHTTHG*JKgLxVOQ{sC0}0zBhr!UzZ|w>QLVODc0p6LF>XuXE;>0^OW%gI)|dK? z?O{EoWx>_Nx7m7fghKo3Ss4-0GUUJ=rs3+NY|g{72Zt7(!1i&a7k>GvxgT=vm4H|p zMIBNkdjg)$x<*7cKD@shqy`oW_jg4`P~V1-bb!uWlKpl40RHc?gO5Nc1d} zheMm~g&G8DTQWnCzKhkv(i!wEEn0X6CV5>mZFK8^ zAsGGJi8XMEH$(!1B*~Iy(GFj!6ydE{#{KD=pZ+FSK;HFD@ty{)=Sw=)u6GaNo&B)r z>p{T3I4gqyb(vEI@4ex=8l;3ac(*|uu*^Cz9c2^#n=xD0Z<0-e5_qTp-3KIjVG0H< zH9uL>bEG=~9Q8tpo5x(#MF&L}$=(M(dsdg=V4G|9InBruEiDOcN#d>RH_>M#lE-k( z6Bkrf0yQDlOu?1p4usPe9?Cj@X{Kt}U9$#vOi!OV=&lmg!Yt6khN_MHZ|K^oND zp(Dm6!5?&2ZsO^|n2TM_w8^F5$+3BgbpfYpC!JET@Km#c&9h1NT)q;ry^A`+Amwbt zXOHwo1r1!=E%`IWuLZVZkdKVksGd#EJ2q}(K1a#w@=Z@addapKDtA^DwgjG-Wc$O% zk4NIIV6{(Qz8O^dHB&=lTun9xpNP}XJ8VMkfupB z%m!@P6MZ7B@PBT`!3~oAMs6dSn#Broj;I_A66_9)h=h=K?Ld`wH_w;O()zBnInQ$0 zJHA5ZD+>u3tYTXwX%Q!C+oI%@Sq*~|XiXG2Lz`BdMVC+Eh%R~nie|UV5s5M7D>4o) z5TAv->2k6wMtEVJk6c3@PyG-X0P#P`#>j!WoUluHyfUep zHbCb(GdgMw`6D?Nq}1gNUDWXkHH`%~MG*u}k)UwfnhZK;Y$!+cctYzUi7hi7d_cd#B0~)%sZ_g(|>FmQ~q~|6TQW)l?VzU2)W0 zGjIvNT`7q0yL*-EgA-S&Y;9JzrUMqx4^(ddVv2V)?eGPF1d+$@ir*xSU1_FY*p5Pz z(Bi1cvbl!OiF1NoDYi~_cLiq%zsFJbD=d_}Ep@#79ll0k>XsxXMHjC5EPCQFz3Pg| zV#vm==Kks7PGxs<#r^d4w^yE2<(l7w(G*nc zbH|%ct(^H8vZS#gv+iK~#f~{D+=!v*j)3&_Hx2G0XUubRB=6?i>JoL*kt z2d{9fnV#h&ZsV~6#A1`>@cp)<$r-A%!N$~O_y|zS&S3XQu)25P;Ugiy-lDxN`B2uW zq!%~-L{6%s$#VsCkMzxAk$)?wmLd+PZLKu-XtMG?Cp?!~YvtujrQv6re0im2YO1V~ z+0y~3)aEzFrV{OLIzj3*r3ds`LIApo9679^tz@ty+^Q5vHX>YyE+YV3p9RskO-F!0 zWrp^q;9Jd=w55b7^KV_KJ1vB^&`e=1L>_B%0?v`%kWN(-U=DPigyU3qs*WHpU4K~) zPr&%z0f-Fdvo2u`N2x=bg93`(3PAVT&c!sEF_;sCSh% z#EN-8^lqiDwuq7o|G9|j%Ft5EgAC0Z(R|^@HZosfAlu{;Ds_uB)XC~yTE&L)sSR&h zO7$*M9)4kMDo;j^1^=@U{KP)wv*T~RmFJ{ZLnt(Kof2oU)}}Y8vK51D-@UKz9!V^P zd6BhyA?2mHEj|*8Q|Wm!IHJqF!h^sK zMZDz&|LI*QV&wE~o)z^q)B(`kR6{7%<>#XSZ(iyNpsacq-IGH~!SU)cqUg;){^v=gql?Vs+$v6p#V2Xs}SD(EC~sQ_`8JF{AU&JNY%0~ z`B?T>u5plmd#i^;5Xx)Tz}|p9<0s1%kt^KL-&1QTh3?V3K@N8hEuPj8p8-_Mwuw}( zK%~5)APiosDIxgXDjd5T(y%&170G9^duaRD_k>gZ*yuqUnK%96b-EJ92>Obg0KEh~ z$4OXC)LN$+g(n-@aD5`8kXSA~crNhf}+)Oci<&-mu--`|`*8K(oA`g`j7=UMm_qY3minT6=A`1)EBzey{& zxDq|Us)faJQFM`ZrkKX2C|@{SYely{y+pCQV5HVIuHsj=zd{TI3OM-*D$CC3SSvXQ z{*L(X2Xf#J`1;F<2&^Z;NC7dpn1ETVFp?Q6GjwKeT?kv2(^yCbEF6$TZa8N7O{big z$)J$qB!xwj`oG^SykFyfm3QDX(!AMhUuB{gS(#_f+EYP%qos7@d^+wU`M%OEOsdx8fCaS6n-s$h2 zX@5l~BYZxb#0~1ocKzLKfc6SqKp|&08-&s9oSbSNY-<|@?+^o_m$GA!f-YLti`6Tq z#%MQ>GU(p&n^WpB=VG8=cHW)cT)sQ?7xZyAqq{#-L7;e|h;pM|Swre;k&4^9B_ zW-@zIj|I!mqs3k6Zum>a8_KV@qP9XSKd< zT@n(P>Is-y5@ZRvHml-fpQ=SL$Kp4Avg0{oCA9B0jzMRp-_tA^*le!1ofhQxnYjd9 z#@*BGiZ5k@FsXlh%xb}~DTDCIHJeEFp72A^ySv%E{{;xxsJ*8I3*=Y;h$qi*^dLBm zG=|5Y$mwXVAXk(Rr>%?V9J$fQu3FlS{0E;!Yc=~>xci~oej(YyI(`NTHFl>FJk1!q031CSPZ+(h80qS@&5sURP7jcrvm8aOnSbj9EqIiME-V zt-LgTg)8Cn?TJSXQ!YbZcmYajh-wjON*K(07G!iZhzq1Co_bKCmRc+Fi} zBx(gs(l8{sc{*%x(*j}R;^|1Pt0zFlCAOdZ($rREVgM^KJgHx8Gi@)Ys(_OiJBC9p z!Pg$P`IEoKd6p1oH`~O?Sa&@LWUy(A%`=3?z+b@zP(vBg3N+uHJ1DK}t+vCwqK$aw z2fZ@)B(dr6$$&M?kfL!VVbhnjG*;Vp*E%h&lUaVOy~LkT#FQ#122BgiRN@C3j}{2w ze3LHaLTAQeX7|qvLJwAL0KRvAB_4zt#k=z>sZjZ3d~+$STn$egcc41%`K#&|^ApC6 zc#bt^#W(VPdJy09ZdQeXY(M{n44wH2CaITd%L(UwdIg0ahSsqRg?!ec>(m*4!dc1^ z%!Vhhgwl1d-rqTiZDG}*nkb?y0fso8Z_$QZmS8>BdI`|+@7CACikWq%ceMVyTG?Bk zMXQEISZb7YK2N5OociVDg6uqjf}`v-%DgucMe5$N{mc1(L%1B3ktrn3e28sG#D5dr zO6Ojot6?+UtuQ`9h)kiMS@2`AmgPiIt8?IjinPW64$J%kBb{BjQ6SO zw9ySkVX+Pbp52_>E#hbQ0YTz{fy6R=LuLje(=Y)^R7lE* z|9yJZ^Q?WUk4Pia0Ehe3=G))yuCB|v-^wGr@D&uM=}|xyFTF6e6wfk1sIkau%0zk8 zn7YB47)3q=ZslvgY`$=mVE>4(#&MY3Gp>$Us+{`zi zd3#<6Rf7-M%hiMlsOgcud7xaq*eD_Ce7<4L7IbnYJij>M8WA-yC%Fus$)po-`=kX% z3_@v913lUGA|kn1utFX0Fph;$!`#lP%%Fw>PMbYL80d%Y`kVVsREtM^7^_l}3Pbti z+IlKXl66K&h2fi8T&h>xiKd8>_K`}kI#96bFxNzm;K&k|fI*CEMNbnEt;aB(L0JkN zJ>@L6=u^3<_80d78*Z+(mMC#rxlZUY5lH_L+#=;qgVnJ; zjpVnk8Fa!GMB_X3&Y5JQR}W8`P-o20E#*U|)n=S@0-Nz+LMyuAVamcK4`5_k{ot?= z7UP;fG^;DyI9n+gSMqm!FNrzIMB1zhTt>ct`g4Z; zdJf`m5p@gc9Hzy>c0U{e4}b++AacdSe~i13vg5`rCW>OhPmX5fED*`GXmWIXh*@-?W^dcTD+%qgL;b$0^~; z<5&qYyOUcKr4rXniPIETHB2oodC&xNm>aE2sl*B_@~YcpSGedcB%|l#9)YP;=ggJA zwR}nWe8X_=Kr$5NRx@>7EHtArGJ!nvk zmU?-XG)a6Q+duKaC|RM$T!lOR%+e5`_1=F2mRv#8MqR<2gZtpyujGGizhXD@&f#yz z{+oBA*<5Pu!z)(l9Q)hFRB=p9RZmS!*H7TKPrc@#~ zT<o9hE3Lh(b*4j0>x99%&#!M3#gZnU-{8zMWsQAC9H)7B{-&^$INil}p&Z(p zcXE~XWcnn2De0ivptypC;8rS8KSI8CZ;`Xg@zRg({-T)p-zge)T&9;xv(&?}5PYuK zX>~Q><$OsHSR^Rukn>BZOFJ#lY8r0c*P22CN-SwZno6;bEagF^1g^>^>3nGUDP^}$ z_^ncjnwxkex9D<8IUdiUO-S8RM}UG^oJXp7kDV&QveG)@5v$7Oml!_fo4XVw6-J6J zhjtsOhV)a*f*P3GgljFGNIM@iM4N}!S8TY-Qfa+&w$N6Dnxx6v)@JUeR8go~zp1^b z?cyLwY5P)odhs!@lJ_g6yYf=+ykcG42dss+l)d@BouW_&BIDuM-E12?&BZGovF$-2+8@xKKHV^D)hwG{33vJhYfDn2n>C{M2tZ&yo(vfuK4?d5XA`^9h7*p+nT#h#I>6ByuO-C)Gf%%K`*l z8Y#3&ucaFDMtTu?gu2u)r9E<|d%sf&$sJ1`r+g-dJ$q;;>dAh6%mg9;tPZQ8h9O!%B?SZnz!wnmbla>p&i7))8dx zR@MU_POCH)*9B-L((u-j0jInz(t>?ZISWfoK|=E2j)MZ$10#a|2alvUtRo2G9o86} zdTr_xKOvE<7YfKYGH_(2j!rQ@X44bZhg# zF07pv(XV(afL5GgRXWz%j9cW^lGu+mJ1Gj*<+K72M+U$30GwQxjBcQgAY| zj0G54uuds7QC=PgfZ9^7w7Bq_oSEp%ORLdvEKo(_0B6n*I2(?|6=aW$Hc+0x8#uE( z6}Xai4Sy@!`;dY>sO|%;Vk1$3U~1vDsgEI>4#bheD$Lq0hLk}p&O>L!0HDY9RF1?8 ztoC^naat%i_{}*mYWp-6N4k{WmTQfgy`5eWlQ`2dv|wpUU$9Gg;)6cS)4fINBB{lr zat;DwJ)lsk=rMr$_;O^{ebt9)k$<6lrWJ>=NEMO!2o1Il)z?Y|XUR(*&57VSX{bGvf|x#@aF#V{Ms3^@{gp^f0n1!_Amr zUR!D&MY5o-SbKx_SxLo6L(6$mk-;pc3x}EqP-RYbH7AA{5$1ET$Qcz#34k0mwI#F9 z(`cWp1Hv1KYsUKe@nM2TpK@PZ8Lb6Y9cD=)d#kbIpZ@r7)qOMHo;$TwvP#+jtG)?% zzj-J|znOiXF<`9qScjV zl#tma>G5Na%v4G{*y2@2KqGRT9`|}!7}NEXKLEQ3QCVvMYLj&chL#0n2CGq!DqW02 zs#Xxt%MESK_@2}v8Ck||t=~M>Tkp6s(V=Y(N_*^HsPsWApB1g(9@Usy-Rh;JodJ9> zH=dOrt|!>v(yqToZAG`!TxVjl$1cBU`N~{;z5h}TOnRkgk2G3DT1V!|$f!9eLrSCI zQCj)IRN`q*Ba*FyrToGkS?^+)(QkCEr8kXiRP&zFPey%8ch_vq3pw$b_RJQSZ?5^7 zd2*On2NzXpwt%h@wHY1qTaS5iCCuw_H;a-0^yM(B3=BB*fr(Yd{^%(bvfeRt*Z`Y; zJg~!v!9&_(oUzeN+JeZN=wn}b<7u@r4lPP(Ss6b_yapvuQ-=P;u&%e8AE#_njNX?j zf^m7sw{-#1!b2~&IK~y+P#AQ685cx8ti>Ufy)AZ2Do)0jSxe~5N?SmWl@ZmJ%xX|$ z$EP@r`O-ryOqeZTc5Fr)2xsgq9LSn79@lFfx+^WZtCv&XF1=VmK>hYYw$bm4aH1{Y z97dai4@3I|?;~XTxU3)`4RNj0Mz&T0h2y#N*!sjGc%IsR&0f<_Vzwi@Qq1F%`;vJw z_Nckjyd_ZPd9E|RwbrLrKOjU|!>X5zMW4or+;dP);zY38hk-^6( z!EttFS}fKhb1~cp?gR$A7FKwwGM}f$0PNanPdPJ;D02}l6l)ISW$neZQh^=HyX2@` z?jLcnR`(!!;{@1`%iO^9wJtg#r!*!Qa$mX>Y)WY-cS2G0m7@)2>1nuc(I#h}uoXXz zQPP`hZl_joBgeEP?it#j2o1F*DA-|MmU#@BxxoCa+=&?m`NdkR*Jb^sHNQ#u<d8L=&E7hWXgMPl9h0D{7s`lp}U?8L+FsF9iAK$yjS{E$zt`4KoV ztr3DiKD)amG+#X|iXJ;zLola3!>RUsFwE3QPCar}M*`G;xa? z(UYeWA^OeF0UThX`WS1Lo-{aDW;EXC*NoI}r{n@{aHa;f5O1cxf3eSbXe;TCWsxUz z%FE~tbUb0unAzDff`Q-k+vB>qjsAwOPy*m{1zLC-Qc24K)a}}OnHj;(`}(y%cVwj> zAQE8L5P4@t0BdcQ5B$_!Q+Fmn5=|zyGqH_{?TKyMwr$(CZG5q9JDJ!vH~X+ZVjsI7 zs=De_pYE>H_uf(YL@s(uk>UFGWn|=m#v>5ai}l=hBO)er159t3boBj!!5rh^c!(+A zn(3~Y$xJ=AcW|d)=Y}C@##Ycj%K;zhX(dK z^`aO6-p0oY&Xh5n6BHYLIJOxV62GB!%7uYZ??4nm*ujl-G+n<@;*qg-SXJ|>SR>U+SLw>Zay~?olzR}5 z=>;+5rgMGNkjH>9g_{Gmb60a?igSEHxP=U06?Wzr zCgTtm^p84q82NY1zQ4JhP&A$@1KrKVhG2Oo-7XLX2mMRq6;|&|`4fWPJVUc0@-F4BZVEaN~`wTB~2?yjKl7U^JB+!w_+jyP9C4@YWsH)An$zx7QhuSmom>U9o^O z6?vc;ZJz`=uOWX2qA^|s+6FKojPJ=buGHOUdzL8NHVM+sn3QT>|7t#DcF+)GHNkZz zH=OJpjuHhtRQ`h+D)r5PbL%Eb37fdbs4T$6>T84~B(?O0_0V^=T_zJ@?>@tPOoe05+e2i~rxH96 zT^KHy;A^iwPz9xLYL6rT^pvFp^^}(Z9{FJsGNbAm)>pLAqZkjTQu?&dKSRd@zy7?C zRC7qqdb^^^A|hSWIwyLYBx`1fNs_@gb%BD%d*w}BjkF}a^a~NK?3>W>drGfmcs?DC zATRgjw#h5M=l8F~nT1K%<#oq=8$cq!e-|TV!U1E+U^>i&$i2|g1ef&Dj)`;k1hFBZ zIkbMplMHgrUA=YNP1sTjmH5H`7ZXj`C};HjO1p7W13Jx)DXp`bz^xN%6vxn8p!(P{ z*DYV(xCUj18Umhs0nT0FWDfB3BnSCOAC>akCFQajWb|EU+~3iMbTrVqHo$FMaR9}{Y5&Gd~oo3OQFH!7f~gA9k)v86r6GhmhPq2G+vKXDF>L3=11 z4-T&_q5eb9CX}8L2DP&3XXN@v!-k$J(>o-+Gyxym!SQP#5z3aTbu*0y0?`3`J;eBF zc#&~d(G~%1gxRMt03`l%agW zOm3sK8W@&}-R$AF&NgSeUnl!()NX)J)zaN&r(31K6SC#8Y%KcuM(2EJx@sX>p)acH z`2lefEH`8EGdf^RF&qnjpvj?BptXM32h_^|QfQViHl8W7B-|AGs;g0Z(u z%W|hq&KquQ&w=(KC7=5P21eXq-_ZVcv`}FADNEhpc@{QDsRGw>)PxkMbeyH+ejS7= ztCTNeS8BHv%t_%n5=n=bJVUq|_)PoOZx=d}qI;eagT(-~jd`;;1|d4q*Z?Q*q z;BBVmFO!!iqc$_kAcFo+u9}S9t{0nJy)^I_K_Xx!Ugp zm?T1!n`)5YBa@rLVMdN=JAR|2;-Ns>7-8UtqBfg;AQQ9FtP}ib8EU}be{#c>9r+o^ zB_`YImWi!nC6SGc7+}KncE}I{Z`8rv1D<4Cuo}#6K)EkC;W!!SZIkqnVMl86?Nk#4@I3)YY zGEP1YO-pU=;N(M&%4W(u$z0A?OC!+ORPIk$UT^?Ln_iVOcZmXNl8eb8=4>FW$g8nq zS(Q-Lv{r=%x>NWdU-OK@Ik3tZZ48xcC3{n$&KkkW za=d>(p@)tHbD_1C2P=t#Hs}EMu_9r6gau+oAbB3%v22!Y4gR(am0^UPUnQW;lh))& z98JO;qe~aQkuVFSP5fj`n(-6p(pDAG_a(as;0U%bg6byt2Yhg!bpiW^HW0atP04MvsA?5NHhY=;7kPO9vzCocoasaX36~K=>-jtH%i?cW=e^r*qD-d<{#1J z5Hqlml??Jd0lx4vr2w~GM|$)pZRlkquXBJPYZ)GS;;$oy&?!MWUaRL>jxJ-BtQhOZq{oosu#}yt! zRJSkozC zjB$OA#2OPC9928DNAoz#-G6jB#Y4BMxwr!~4hCBds~rG<`MB_^WFbYCP<^W0X#RM# z4Fh)_Eb-w>QFZm_y%|l?q5U~>U%N6atUm<%>7?c!dH{orQR{*kNTA(hEtR^GL-e{J zMU?Wb-(d|ZD4^F-Z@VTI4nCT7i{Xn&MiYTy$qzy(R)CM(qCL~AR=w<2Jx+!vPW{$fed z&tI(>3@}GKzPOonYLaz~3FEgrreq+) za6@!2N@K0-&KY!$mR&t&iq7 zw)M#0dIO4roxy~_0AaQy^-sbKrA`c{rq13%!`PN7JB3=2L$4!{VnQ+bhZ|l@h4xy@ zHm!ll8Y+daP3`=Tzgk-W%uU!+CJQ7744GN{`du zn%|F79lcd7dZsVE?IXT%FiR{IxR%=Mv@z8~7V0Mk!1Cl~6GBH<0w^yp9(Y5|oaa2n zK`b(~2UenVSG0IQl2L7j5fiH>>)GWH+4WWOC)bK$Z9><6HF>XsR!Ix_5-Ilau$j?E=YfCyP7^i zUTIEXNH(_@eg$jx8*pdp_3GDXu2Rsi1DRC<`(JR|3}xB#CLZRi#LBzD>s{s|nv&b$ z;aEb#PEq9*)1L{aOs-EZa_5DTnq%cBgX)LLV4_udf?+3igtxZvcRHMejDbM6MHKzq zgx&j#Rj{I9dO0vKwvS_c9@?mjiz_n17Kp=*c*{xy}KdvRBkBs?yxO+ zP|*qM@02Xf;({n1U>z_kLJ;x7NiiyJ2eFCBy4qZ@h;jq7Pc^!f>w{rlc(Q_WH3wU4iMhpH0lDF4$ z=ad&07b#8I9kx=2_X?B%GupTioUV%-#pIf&OWz0rD!J&YZ%h=v8lDyS^HzKaiXL8g zTeKbQT7mHjo?q(Oo-`i&*aumCDnvRYJ zyZEfcuxzWHu!6G|@^5QY33WJz<84r2o(CIGQVH5KZ8d?xEX}q6>+Yv%H5(>lGrwyl zR@esfM*76 zuAj^QcT~4b6%xeiSghqii<6o_Xp4){Q;c&_Vo$-hEg}$m1_R~|kd@yXJp}sb48cQW z0Eb&5r%)*h&fIOXD~;#pGMld-$IbHwTdxizq(nwAW|M5|K4|P>jq({`;p+T*@+gf^ zgW+TfhD!D_uDBP*9|D1X@s-0mWi5fPq9k+joA&)~L~uCm6Ho~_ukCC{j2_V6PuV;~ zZu|=K|DurryfIy}$n>F9Q=?)*ERdFkYvimWGGy+@S=%!3GNBVcdh6M7e5AKJu%~ir z*HKZged)<%Ts5CEYEHFh>$|jB0PHU(N@sx?`qM|Ut>D1~LMC`)VF6!;XpzZ#g8mMa zcowgY8}SI|C!%_yP2KICNN{}?A~ljjmX1mA;6lv?SeqR*&s$rUf@g>T$q@@K2e9I| zRwJi<`z;;`D7-WvmnBp#mxb*?O=fVK@GDPp=8ApwE1dP>znf6kXK8bb)12bdKOMxs zVVuU~qU}gvKM)Pqq%;}IkQk-4dM8&l{$H`h)hL2_)0+&J%tA3)&}5H)%*pc9jqesP zVZt?S%1{gDVQH&M2a(_$D}w%fSW+E_-u$u;!eEn9{gHoW-xPsr6Co)ccNY}FT+#-H zE50}p?SiqnA)Y4?`K^jrw+W;g=M`DQR^BEtEX!{Vd1{6MjfLkYqh};2WjT5g?I&*t z%C$Te^IT7@fPNo9mj$cVP1ulw>1kNd{4w&32oG zm{^ogytG%99I^4=`-PacG|ywwQ&cuz(d|t0@`zvOBFh*u#OlKY|{r-pepkTYAoY~*y<(PY22C;n|v>_&!Adn z>iGhr=c9RAJc7mSVT1xP%X~|j;B1>RT7-Sg$|7OJN%M7>FcNbGUaQ}5U22k=#23{l zjLQjj23mwv-H9VBNn5j0nS0bG{mVj^>T>&+Sacy~P3J(nG+*il1l zK>s+q@mKyG)u<9NZUI)cl+fN4T==T5}E1CuKv37pr2D+X@_tnW>t_VFaS9dj3S&-mS zyP4o1bCE3qcSI@fI9p<|*^V@rNb>3sS9v2!?<|Hvg4?hVYpkfjt_f{eW0?ULgv|0# zaIac;B(}|OGkJqJ)vb}!``>1JArFLLKfV@K1FlE)s;PzGq;PSSQ>~=I=ATY5O z8AP0NrIYYSW`X=`7Q+hpnoR||nXRc&jRskquxlFyG@YE4iq59~noASQ+4vsFISWij2$MpBmlwg*obTa+`r9J5QR%Hmhs4`rSj3QXba1u7= zzhZ;Q70|+2qS^g}9`Z}h5;DS9f=D{EOKLe4+R%KC#2{jl=3EWG9yOqdD`)Ysf`o}x zgLkkXwM)t76M5H&p|_PA`}yBJ&cSrjG+2`LxhpdK7;xG8b78Y;6*rBv;f?Fge%jU} zC56+?%O{akO3rF}3Dre-u*{hmD(owhZso#)6*i)Gww3Z8gEOi#2LhFqZCcF=-X7^X z)yIgw6*6L=aTPSdlX3#OxCqMG3EwYK{@FSKLPPM&w^vHl$NVZ?MHl>GDtw&)<()q% zEzp4}p{kDA>MFIIisgg%=0w${F8?xl*UWz)d<&8AN>D0sj7pzc6hKQAuR-`Ke-|I@ z7AMr;U4s!p6AI6(o1fXcht1i&x?l@+>on*o${@Ca6&|Xp5I#3aLy~TaH2S>WlQ`%+|C(TQLLVk30n`u?X;agwuvr3 zr8mktutZ>#tc)>FxT!?`Q{c4pB4Z@F6f4siZFlsFuq36;TxXGmCe)k|$V9;*_b5lW zendq$wWv5IXePWsfKz^U;6JfwlLa&@L32j%(v$HnrlZ2L?gyDGV8nt&DIo!tn5b}N8tcHDIVw?6wyI&Tcx9z>DJK3o3kyOd6lGtW zGsC4rs^DqdN=-Tug=Ld0->b_22c_C|r`qarB|)&U%vzSf!DSm0FWioB9P^`aRf$4n zbJRj_Qa#T&d9CE-q6i8I5Ze#a(%)|?aQD%eW0CQ?73yYb0~NzQI19~2MLd>pLAy0o zp=w~kHRG^>Y*Mch+^Pq_6bwUFxT6|NV-ei)guliqhE{3gibf+jpu6xC=x->xKo06X z4Io}mmQH?-rd%`eb?d{;q9a18NHnUga&m6lVpgTeWI6&&N+(rPt9 z9s(Gj2O!FY^i5OUUgNp<4Qz16a}WAfNO+`ajdNariV;;o6kMvJ_Z=c?l!*x(Y$4)H z+Dw+AZe#(bYTv*IhqAp;lTS!Ibn=SH(ArhjO*Yi!yQEv}Mxf{E zluBGzEj$Y3fK7HbPsb5cf^*S^zhW^~=b^?U#lJI`zSgvk*@WE~}+FUpEryO=c4-6D#Gm&ac_x2`mKGU^ZrI?7Q0N@LU>RkQUk zmimF=^5U|GjY)_;?=tI*&Zto^Eeot(4vx0C>>tXf24t&H=uqX3=H#RrddXy*V7i%% zevNN{0>2Kw6Y~-Ym9c8D0%Nr}Ud?D)Xb{%8YhI|T6(YoD`L7GnhzZ_Sa5Z}!yc@qW za~kExTxg~SZM*xjm<)~YHFC`Bv}UTSQV^ZTun9+PfsF7#zPb&HBs^#}AwztlQ3@0M z)c_@Ngsku-4LK5TrHJ5TTM_}>2x<2r=z=wzRl7>G+H&bK3kM}-iV=6g+OeRZ9hJ=r zi0)Sj^e6e^i<iD zb7+JSd4+t9^KgNB4cE#t9TwLN@6tCE4))}ONkM_%4c`3R70E3h(Pj~X_ru2))ivb9 z0A!VFd!B2p)TK!BWTXr=4z&kAf8`hm-WzG9bEvkvphw)7JZc7Oe0GrU-GUi3*MR7{ zoIrvY&d;f}ZO8K=#Sk2pgJ+P##Q1P(h?{^ofhfru%q7W~*!e+63e6m2erSisBLAQ` zzzl9iR-6>oZGHw!A&-)(yes^s7@GY-8P)DI@McgM9{ViyP)rOF?;;xw;QH#}YZKL0 zo0KvFygQ3a^;1@xHfEN44d=xwXE@wriF>KbY?^!hJmPq{D3hE61#5|W3^TY#-8=L&$0K+?$vId^E>!&D!iLb2sj z{j0Kdjl(;V&0fg;t~%fq-`?z)D|qcm$G%qud8GWb<~7h_IF)FG!uyYrTZczD0IqE7 zF~~wr_+b`EL?iWmyQ*3k{mHbHB?N}h4CTaEe68^}bn*#stJPTX+)6*#u$O#me*s*m z@e@m+!oPkZkD-E93Wy@#3<`^;gTbpUD9*Grv#70%GC5q7uLA8WLcHNFZz?548vE#{uGI=1!@FGL|RBi2Srnp?Kvb`zi+48C>e~h!b$eijoMy zu|ZdYphZXJ38sm*KchreXJkLG* z&Rqh64ajIXGa7Ok&({N}DgRETIgOG>>*{0w8ddh?y7~)>h2ETTn7Jmq`0)YHlaE!+n)84-V#!6 z;@sm3(^-1soM@JJD3$G8Z*BFbt;Nv7qg~e&L9Ywm!@XzhsK@1)ai5e5&f4E72=XaY zBRX0I;M?wPlI6;#tox9hBC$c@+syr2`YdhF+Avt1EmxZ(D= zGSl?crvJu#^GRQ{CE^urq#zvDq`71Jn-sEDG=@=QyR}$q3C_}d!Y27AH)nJuyW$RS zWUf5QOqzSEV&UG{YdS+_oJTJD6bZVGgbtyrE-#|v6hJ04;8rA-Vio{mTyN(Uk*X9C zcT@QmBFx>8CxQ1iG45Hn-lRf6Ce*L68Hyr!K8Te#Qf_2VDxnIcZiF)>m1UCQHZ5J4 zbL1fCnyd?cOW68zaR5ePFi`{jX62i%Px~}77TVAM`B8ETOQI=4U2Xs#$Kl$B)%_b` zx_(zc;`hl3WwZ*kO?-o?+~i_<8U%Y$Af^V}0re>1Y-0gbZmaA>>8ZguqsvDKr)n{) zc}Xa=<81mr+0+pC=x(fcxYJgWZgs^`SRUia<^^yyecyDFiG8_B_3l#C{)#gF&pdUl6wt=7@LKe26ds7{-oz4`#&0d#E@vc4ZvtT%7&S1I1PhIs zw<@x@zF}o4lvp^j!~pu$gN8C-QmQ|(973#JH}5%ShYLi#p^rdgX(A7qM+pwQZYpRb z?PeO6A!WVhHq9AuZ@MX^4ah(&MC4?IC`6_VakY|bE2AXtswu}1m81797L+Pej%v#h zM5;U!`n2nY#zM>|3rsYteCO_3csg{xqZigDMt132_qpw_1%2q{6~sI$2~>XvMn6@XhsVd?M-TV9LF1GQ(V< zzo+x5u}-%?o(C*ysyZQpe_UhSrH5OTCTx0u{9UX{Cu+MQITB)fydX;>LRh9*ICiX3 z$NO9VmK2hvp9_Wz)n0kk!Gh#RYV_G?A*qIPFScS%X(}>E4#>5d-lOFKVg2 z&n;Gui3|}dIM2azHo9|jdMuBA$AF-3A@OTZ!Z~YlYX_&4#RZN*Xt-&zH8e#HbDa9Q z1<+bVV^_96iTcZAVo*lTQ{$_Xf=O*4WW0!sN26_NT?$|l(nw$mh4StP0pu=zYA|>w^Q3o2!CUM0bBnp6fQ`~Q4?Vx*zCt)Ds zUm&%}<(wMS;Ve`llj68x%g`%SwVcaD5aDm5&z#e2JJeqVB@0-@ z^Hv1c9RqWfH47j`Y=DCp6hB;jFpQ~xOgPR!1sAoKb^@PQw=qht~p!=gi-!oA}% zG*}TPibZ)yN;KvbI_TeP@a!axn{ioBkLzPKD7CxGS$DLVo=&{}krChbHfYH@!CV~T zV{jj?+sRi(0RpCW2KO8@<)5Gy3E#4n-De5lt*7L!D;|>r~xAy9u`#Jt%p_7co1{ye+pmn%LG(>83C z5~Ws?E0xQ;J&SPHEqJH3)AFK>eE(I4lPI%0`c#!Jid>L$+zW;eGSCVh1A4SM=DiX3 zg9LWihIxruhO%;J#~t^e8J!G#OGp^e^;hIgZzb)Aa1D(*9x|2Y9ICdwYwsX#wB7A4 zaQcWflB&3qipgN1$DV$9wYSeU5?zcBDDDMKh-rEUroft#(7KXdbj@h1q;r)?a6dVRU@S* zgnCKQ@MhDV8l3C}9+RZ~R$P~vrm-FTOP{2M!}8EhMcs~U z;0LjbYL5HEh@sHUJ1G4k;W3EO{P9nzMPHjHm`LUtZ6+ZF1*Aat(rRo@Tia#*lk0M5 z>5#*eBuRI#!}j+8W+`P32eBxb)=DJbvYlfe)KH7mkPJ8~P+mOhClgEm*@ZSab9JNn8z+_#0k+ z@(Ho4UGCwoCowMehdCtb?r&PPSXStmw;33O{x}-8sR%2);fa_6{oA5EO30-<4_fPY ze7IH>D9IC(tHF@N;vjIpck`r5mXj#cR}^M*6kD;Z0{SS+7;iKKR2==mG)gNvoQTe*!xk0W*0cTAak&0HZH8oZnJ4 z3RFLmFf2RhuZ5?OBjlA@H<gX&53Aywh?b%v7A?gaha|UBL&* zGDxT7A@pi*xqnSt8^~6i?}R6ZwX+MWDx> zNOsPGP`?*LcUF>Dz*P|sChb}j2r9JJZzA4ky%Bg90@$TL-VEZ`M{EiULB9+eJCIhM z=6k;gJNaLw&XHAZ^8K30Qo$Qv`=H{Z6HjAP+T4AkCm#+9o1+cTY3|UpJL;;OAp-?B zQ8oNY3*Iw~3Bx)spVZ%1YylGVtr24l%qT9u>#0OU4I8PIpQ5}|1YQ^0Lnz%d2m+<#{L*BNy0FWrga<$<5Pfu`pTB&#a1yH)bk-c;dS5S4pQ_u(UL>+4N&x-{U1Rsb zL%mJ*@h}R@iItQIBo-jftxsB%bx_F^PcRgz0P~xh6DRS>C(BUXf?Ku%7zKy{Bi; z+uLJh`H~c9l)x&+Z2J8;Kom$s^&hn?#Oah73QQAv@(Z0rzuMDLK#XloB;wjP4^DPH zlSkwcAriwo+l=XwKFrdc4Rc3Wd&pK&RF${&U>h9mREwHIRjat?vHa3;faPf#oh$BY z#Czq~iZN%2jBTIMH65>Vn|B7=$R676gf;9gh#O^HH6{T==Cy--D+~2vuI{<&im7Uo zR;8;a<1j0;4&%4XkD;ES0W?HGokcWJ_HVl>>scM*OQ9bwp#fSQj!#QarBT80y#-6mq;(!J#bQAzgk$78ISes0Ad3^)Ukz+D|mX3uuD5Ps%6S4zXB} z;9)PU%OV4OLp#hBn$@Jz!SC_C-t)naGBCXZA2L<>8U53q<`?OCGzpF&F8jV-0mv@* z<6P>b^#jk3a2gx**oY?#t`JTYq&p++enT9thfKR3VTRoTn6Ta9+PC-JL^6$;<6cSL z3dG~b6~u-j;n&As;K$@Lg85s8UNv1Rs(_D8eK!4HQ>gZp=%OYHAAl~p=TDH8eoVPz zUM;SNgR}PK8wh6!mAVQ=i+e=gW<1FFGP$MBDw;Ret-?fVJFMxdlnXrPQ*KPgA^VIx zI&$Cv@eund3kG8NcFS#;KWWQDU(LX9-*fuqG|qyXG|ISQ7W*8>X9%R$0+XNUTrbhd zGtT<(80nVcC(UHnZ{GC!3vbL^|}LybV-B)Y+rICRqa zc?6|hi8jBKj3CR0!V=PfU&xDNd~*Al=mDL7E6-Du;*k457GnvL0e5jbCMmcfuQS=b z>7^kq#@&XQ@}yeFOb`s6?KzWX&^(jc<9^)!ePrg43m?5K^RWKv>y20wj!wtBRQ?xDit<9<)E40$Wd{Z01__c#!Iool1A#ahprKUew#~IX z8kozEV;_EmxGF#^@@zJ%v}24Ro%t*c0u2WY`wh@Dn5xYOd6VPN1J)_Kz#SnkZQMK% zU{=DT(Uk-UUW@<@$Z^eN*P}QhmtONmr=V{gT+7W^>Eu@EWuBJPMO|h38EmyQQYHox z&lu9v2Cpgo9 z9t3ren5*<0jdn@J0i}&*JQ(iHb+{Y{bA6(w#sX9rT}pKgR8Nv-ZERq?;*!TdWGWFF zBra0Fkg?8WPA!Cj@(fq4%VLG8f<-ie%0=9)2+7nk?FrvBEj9EbvEkDVPKiO!3zllJ zL}`O)Fle&2*Ygw(N4-`bLlO=NW}JU2M?;sEJ)EUl18Dx{y_sV;~S4>+BGYl1; z{G6H(7j8jM5YF7ttIxw+Mv$yDFBn6E8HVGaIU4^-t@5SkBdrSw{B@9*p{QGCq*T7- zFxyFQ@>MqAtb$UHw^-BnW5B1n!yBCS^F^0!ocJ$rgAH{>iZmRO# z>6yW)Ut(|t?vdngId8n;X{B|p;&iTo1`>Sb!#_CZ6^{D3cD_Z|%AyWjoG$th90YSX zhWGfF_&<3z$D1!s9La*a{H18&Vh0Tk0ZY{$7Go)~VL`DtA0JIKc~!!v6{S%6NVreIeCY z;4%e%u=lnY@v8>*pzLY>Q3>!P07X(%Z_-Y;XIzy4s@c>aPwF|IBIjc!`)<^%L`Eh zy8~21P;DHIx7@B#e)>0Iz);AcNi>!9{M>WgYHy%F*&mQ^=6xwK>`{6A=QjqI!y$$!jkE5QaBJb*b`^*O}W|Orx=b>6hZd-y9d) zn)wRA)rGk_d)podZB@4be#aZ_JxIg+uXOajrjP(!K)@b8amUl=zVMr>g#3Dl$31>D z3)T9(e#BmfxIby^E12pXhCRvtf}Uv;lLe(xl>L?9&=SFe+WIql;{$CEVeVKn!l98F zdU<|xDTz$(WM3IVRO=5VYrsx~JjSScD01Eh#U%R64s&rKoi`;{t@qGF67%e#v}!6f8uHj5!dKH0HiIp9 zY)n5^`*Y>iyTUJax1~kLX_-WyY~^Rfb_5u5M=}AC=E*9}Re(ijEE_-CR%p2_#w4I> zjAaOIJ2dry`PYy{%P8EJDF;OQq@TW$!IJx36}-U>^V40 zzWU*U&>gdC3nSvrw1+rZuk3oX!Jd;o%5}89Q|=@%7JcBM;j=iLD~7`aG1t<6l~i5<}azani{U^#tDLH)H%(FC35s!~I3R+(yw zZZ_bZw=h(Q@ed|@|70Z1dE-4hGB^L)zspNgj!k=#^_;7zr91HYEa@2+Goc^x2p!vr zRuF?(#(KPwxmXe8P>=mV3Z%5u1Z!<78N?$aB{LTZ!F2LXB-HbIpR!dm3mbmTN+s_`rrZ@ zt>B#Wq2Hua0FrEP3EP5GS^g^HsbN>XDfHIaLJ(7LlPipXo$)#fmM&ZL5LV#Jl8o#s z3ow@Z%7UG20|yTg{Z1SNJR(mqL`>phZ2T?kEDd%!&1S(#(W=reBr1(j|d261RK5-?FsyI|CXr@T`8;F#3?yLI{PKc`q z%gdB^&&dMyRZ_};YQyhG91^X#2A6$<6(A;`?HzcYwJGYOm?!rWu;JeY2mTJ*GAme3 ze9cBD^7R_Dcb)2N;kcpnGia4Tnje65eLU~`yawZemt9-wXaPJQtUjO}<{fd_r*c-` zj^!H6_v4xu&F9N*+rYRP+tG;9FV};r0Ub5%Ebi0Hm4t7O2%`&PN9{O?I|&L39fp+s zul5B&(}oO(ZKz>VsjON6m&Lz@QIVzqA}!)6Ki^B{0zF7)-VtScnws6N9$@uAf*uLd zje)=vcvZycryK89Rm*SgwE6W1jM7Z!yJ8^#^{_Tc4P=MGzDDtYydVSugH5|(6c+nE z+oiMMcxm!kijE_TYZ|?WAeFNiONLzU#t;rgu2O_U<90ea)5=0xls-3^3Q+9HA`#?; ziB?Vm4!MxewhdkP7DmoqHuhzCsMGJQ75Usf+WCK!+3alVi3|aESs=dkBU}1Tj3p)Z z6ZTj;AbgyUT$lra%L6VrsIbyn979wLT4CHc=|38_9O-)$8_FOU@Nt#yHT|c&ZKC7% zt%#@FZ#Z(~&HjoO`pJy$&2)TW@UtQ>JkCtVW!kUCw2t;gez#A;i^y9jDx zOuh?-!Hz8&o^&f2${uTIw{nN#NMZ93@b3sqH<$BxZWabwd$~@CVj%^+Y?*6?xs~0z z{>rEto=+*2XV^8hugcCQPyay1QthTjgK|0*X^t)xo|3AHoDG&%kB`&IRs2Sd!mckU zuAJhL0n`pZ>?+e)N)lNiRM4iW3Gkc0Pw8bZ5Wgds?}Qm>f-o<;-Hq zTaRDMe_I5Rw+c#dnwl_MWIkg%Fr>%JI1n-C)T|)8snH+PvqqIi7fNEgwEBpF%CXl~ zFek*k|GTUDJ|DVyFYa`a*)|heb*jqGQQbDpQS0>S0IL%oZ5M2~`4CH#h;y@^s-moA)sPb( zeftP`D4J@+S_pyo<5Na{jDdmtbKV^=?2b=4v(xh-+vD~1oa%e4`*r2o z^R@5$_IU04eg6Hm@{Zv9F}3rx;`?&>{VCh?D(m~+(ewSj^EvRfe}7;5{bcKVAIkS} z_1yDudHr>Y|MjBU6CoFMuG;hQ&-MG^z2|dE_w&s6tM>chx#ug?_ty9G4WZ}t_xC@( zugIm(`*VEX?~U(|4-MbPtIN}CeBaM^-S1C2-;e9>mygbnhf3YAe=EMx?dZ%KHnodyj>r6KPFXs)=OUTbHBeb zzmHiOf0oDhvCsE;o67fb1UPj+s`Yua{W{wDtn~e^{W{Y9J`H|j>vvGKj{`?~Y(r2GBm`jaxcdFUhP~Rb7n5tzJq;^?kPpT(5^?nHI{WdeQ~^!pFEIS zeRF(#qIOm_-jOH?OC^h1gs_mJHwN z)~4?9_icH$UMe29Uiq$%0K{MGPr416$9%)Ov8A6C`~RDrzU(n#yT#A#Sv_WWskq;I z%{?)x>GAxrApJ4&p4y`FeNdU~di>p)X|MHLx7sG#RFznUL`%2&lW|Y!>T&7qoz%Cz z!=<_hsdZ!r@Wr$2oAt%Rv+33R_`OhT=t`7WOMGV}WxAKAt!h6##?X9^ZzmcxZdF@i8vh&d z4%X|?ZwTuz1Q!p6hp~&(kuT5sS*u%DZImW0;njGmGQN9#7}@s`0na*A5Bp zuaD~K>C9ToXooxJ$0hUkL#*5F<7CiULEWURP`y$iSH zF4S`L=pNAOw$amdw_Q5h-#IfizD{Txe9Y`iUp+MQZuP-@D&9=UzwBDSQq|EV~S+)jkg$r$k3<`)uv};cc~|d1!xWFPwPf-*mI0 z(M12+biPANYnV>c)&;&01ES>F^hqxC{trvv9mwYQJ#5ylYHdnOTYFXQ-J(@mREt`% zSL{s$wYRpks9BWuV-u@LP#C0o16AO>C7x4 zm+gR!ZKTb6?~UUKM29k9gA;IXHKuLCFs4>99ZUQuMUj=!2_o=|4^m15uj1H3WYz(w zfqpZkAXP&uaKvu-r>0oK>{dDRxthis=%156_*sth2Xi&+iEy*G(X-a4qTj$g@J6rt z0R1oSkcUv+c4JuD9N2X>MYL0R^(E?~1+lRYsoMl2+C<*(Eq3@ljDeZu%|wPW`u@Xi9UOY)aZbUpbrN z`?S)3%(*GSBI|xmd{$T$*t(j{)IRA876HS9M62aYfkTZ%Vhx-V7wqYfsH6>CM_4sS z*b_^dj)|l{4yiSta7Bk*5#;cmHnVe^1Y~WSw|}ZyB5n`}RG&IOEF1xjHuIr^ro)^H zr^5V~4v{b{OoOoNUsPJ1;eEA&S=e$#ZM8&V@OuvP{p^_Zxms_3WIq>*t$G5_n^!mO zJtu78s#kl7cfq%LAJgZguyapcV&F?Hp?!rq3~4S`c;G&P6WWinn+ATI(sj_B;?p|# z1%;>Uf`re1skkhK?Q2A;9S?Fue5%(@A2#E4BVxB#))qgyGMBb~-hR!;h572Cwe+$@ z(SkOqUT%s{G?DBz->woIK-a@PofcH+p#w>X9mksS(VssPmW_Zd#Jg(cwQNDdK)o4T zwN`OX89}pfe?TWV&6$7wa>>jakm~8F2eFkZo#s2g>Z2+`C*4kNN?cB27h^@SODMXg zz;0SDtkA5f;Xd!6~sIn zeH-9?8TRXPg`;%weE#hM2jSdeq*e}LcGT3Pa%+L^NTj!_lNS<|O)M-RyJnJ(qaCVs z!GnS*{9Z10ASd?md%Z%%vWrCY!u*&kkhuUbs#Gj!vG;1v71G4dAu%UWtY}epL|iZ{ z%48Q;y`#1$5wFeg;PxaQ!YyE+xyZ);q32w^nh%R;1Qikfz2=)`FrfO{V_-UBX9th#`qIwar3pse0dln?e#gRh!55jJ~xkBR3xiIneu z+HQkMZnBi{aYha>_1jCBT7HJqM#5Ho5J+>>?C0~oR(AD@CX4(Oaa;8%k0O8D&0kRO01sGToA z{u0WeiY7qNND%DmQ4O@8Sn>$~@jzB^L_l3PI3z4lEsMkmoQ@c5dv)>3)sAMq$U11I zqRCtO=NPbBA0i#S|4+d;TWww5IwJ^3f*L|yV*c%1EEMP5Bo9sV=fBbc3K469h(|qX zokfOuI4mS6l4nakeiL+lxbHvk4L*+xeq+Jm?C*&WOhLJJM(`li@!<4=IRs$=4&C!7 z;Vz7{z`#1@+#|i4)uBWX%!U7#4(J|~g+b1|I>oqJ{_VS2zI&-_Rkh0L-ev@Fs^TSx zbKwgB`@v&{)CFdj#4*W#wg=B{2;?*$G7TtjpFDdC@dxTvWM21r-Nn2^J;3EzV^wQ^N?wP9F{U}rM=!@?69i>rv43L|P)@5x+T2&O8qB3y|cds+pbaX z_@SbyNz${4U4CCM>Jik!=WotgDxe?uw-y|V#>?-Kj#4*GE&fuo zRFo)H%lZ+Adbs!`cTtdTvQRFhRie7ux61UK8|m&;g|9XpZ*s1r4xI0w%t!Cyw;_J8 z{(kV@IJiDWZjdmzj5WktmrxvPKPG; z-#U{YMGW>P;v+$FTl^BLfy|vY)yt#)p-UMYyv>c%VRnU6KMCB`3Ugu>n9;iy{G&=1 z8ZfnXr2p@8o)wY>=x?68Z&4%_64JdJVYv4cQYigJy0QB=sr?q>+kp>=41x*a9$E|< zB5XPem-p>73gvf!88#Ep{j?bbQe)6d8u*-}E_TYrJh|ZV8b1`&oLm zruG_7Zg`W}s&}bYz0Fh&#dV$b-8>sAN!|8E1&c;xqgwkfqg5|RR~x4#1jTcmQwDw;5llfn2eY;j88mRNNkKzCKUlK+wl`#I)Rv<~!akwX%ekWCY$4wM7Ir$+I5 zqmo)1GYQce^MA5dNe#7WR>}oJLZ#+y(CX~z)$GuQ$Q3 zW*5K6uH=XKTULUZK(TL9Pw}zxIt-y6n%)FK3^ws31vh9b*z60&mPQ~B@#E>7N9ViU zLct;F#DbP}Ra49L1v{xtwy&}SXYp&d`Qc!h8esRe0~w?b6yF8|R_IK5Q$6cd-vjSs z7JY*nXw)1#qmmLi>Cb=_bOchMLa`~hBpkAvZbIP1)uaD$=zyQV;X^2q;7P5DSTDRp zTO&9rFap}5r@iTLlKX1MwPAn_8twQt43^RBps0u#uAPz$G!*HU=|6gW~lBKn}3F#&|!*t2Lq9mcz*6!uGTYO#bDp30fxdN zgB3}pZmYi;30oUvFvOpE{d2&CON$Bg0*8l(&#A>RsZr&HYMyQ`He-o+BX%NcF<2;d zSvL)<1x1%vvtRhF-&L7d2sPg8b`Pw=T9E9W?w!5!U91Y4pv#d_c|3~>$Flfaq1T>} zQ&#F|+EE4Tq)6|;)e7czEhc6yKpH$K&T4E^bEQtIUz;nl2C6=LN?`jaaH32OXWz6c zLue$ZVT-X2-Tm$?-#SQ7vwOxVAxvi)J*!vsK(0NsjaGYas63&C%NUVLY@)nZjR{q_ zsik&=sb#F{K$uR7&!%~{D~sMw4U$)$6ew#`%2-X1Nta|skIR*ewF7Tl_*2_yNC;|Fri0J->+d~!R1>wA<>$-Kj zOd%#YDM5waobyG?7`Nv8$4iyiI0vUDJ~&P5v|zNNy<(}vqyH+g_d_7FVZCzupnA*{ zw3+aQW>kkk)gxU-fJNbK=e|%U?gUtyS1t9{p=wU>k?bYn9CIrt1$QvSV&DhH(ylTk zTFV?gUQT{}og8>N`)iIfr2%?QuUCRydslM~eG(`g1a$f7-BGo{o#%|t06))c6SCzaBS3JoA_jra3NA{eEjVmV za1rDwnnr)Tj)ET(>pg zoc@9_pDrdS6w}4?c!f}pp1ur-J-#+a&5i-qYmn@3Qmz&n;Ex~)wykT0g>_x-3cWxq z?3&y7AcDN(lQ4F>61s+9mr&=QrU#cgn@8PR|HrK81d+PaM6194AchS zfeVj`D;EFI_8R72lBV2uT65qUD7-kW>yun?bDtFx9hUaqept)Wm>ZBqA_vT<61N1| zCuI`#)n#$pk&+h8+i!g;iI-*?h;|$Z=VBJELxwx#zCta))#B!cQAn^;Efx2yM*D+W z?9zUy8V@8~%Z3s;Qc_AHS+1~nFh#U>w4V4ct_pubZylgAh=ZF=UdSH-DF7+!+C>gc zQyJbMpBnk-JWnDNj<@hU!7r`&7gnaNes6zWug@bT;(u}U!Bj!SGaF~~YFV-q{c5oC z>MtcRFQB3OydN99vv&VYM+hcG%sU@HmI(-VTDMb?3O}qaFzh&88h;wB4w)tvc%#F8 zx5NM)YhwvP>iE7)o4Sr_IM6ysHKhBrERN!}mj%DK)xw%XOrC<|e(TZf7SATsR_1it zoj7*Lu8|s_n@`$J^2Y=~GAVRlU>1X~c}P_wcs(yQO0237L0DOzpL1GsER$wGl=n;C z1$73!u&bCwD3b_Ek&=wy^!yu`%4Cy!aU@sdSx9KasDF#pFWHBMyXldn6wh9i$gcE? z!1;J7m0h)Afug^qp87V)cO_Z_=OHo0#+3z_K%2T-Xx5;T>g5IeQ8~2~*v$rj>NwSc z_#;Z+tJr&&P$SdMv{MfMt`vBi({Z<3oZ8~rU~`T!?>NT8yAPv@t_<!SR*WGm=RZ<$VO6d67LDX6UPIYm1RA)?b<|BZZ#TBm>u++AG@aT zwA!R(FGjz-&p6IB7Y(Cfe992PgJP9{)LKnXcHYDZc0IsJE-oWU4)@xN)S!%7BCYxn zH$r|B48{jC&!@1f0uzW}^vg9VNHSQGz#D9}K%kp8hp5b5JRJTn?LrYO@SbTDH7-k$G6@w0T9yhq zwC3g#+p7^1IFC{Prd2~N8fVAjY`pTG4=xPew8#YsNr2rRm+X(2!>tgyK2;RFo7NI? zUXoooXsLa&ca^%dkqQ1;3hl-+H8=lD#W7)Nc9y@up23PKow>#bwh;*p&E~H&e4k!< zHu)cxcuK|SYM`ycJz`-0+Ny&mujW1NZWu&`P1R)aa=s?cwu0ckI$zc zgDMmbo^+L<7n+Zb?AUA=^F?NWi=x#v(2SQS+!OMf#S_7QU-Dx}W+3KrAcUf*xh7w;9eu z_`ENc@ZJbGT-nrevcZ%FXRittjw@9o+WWd#%^xDs*q!#OH11EpG!b8T0OqM*J z^XH+51qJ*Pq193r_%9A3kYW~e-&@wK97T->!nX*1|J9 ztKA@PZ5%*pUsoVHhVt`iof*vs*z&QYeuyv1aaIowst z+g^Q&qUCXaN)Ym&!f@1fMz&F4@PPvC z*Rro6X#E)Ifyjw5?SoQPb^3V!lgH)1SmL51WRrsr>Om$Y&g)4hUat%8fa9C?A!4?+ z0~7zu63oqPb@g&D$gpeA{T*_kSi)T0?VTV&z4PENP`$lc(8sH(3syS)*!244ZAf@{ z4E(mF!x)K)FA)Dk)+ThIdf<@&C-FZBa|LeKIB;&#(T8nbTC{jVYXfPyc|v$j!V*Y8 zl$fHRxSEdc=aabEm)m3RE}_3r*RtBIK(6V)ny{#3^rGzD`*hpR~L`y zQx)RR=tAC4LD03pD-40xMzLl2suNzj9Ev^y;$g+-O);T{woe{U9bLq6?U`s4^A`2ExuvrutfitQc}Y%(YsX=3dz;f&Qc49HL?lqkx`C4lIO6q$-mbR9A+7QcO z7Ei7He__VxK9@ba`9LweaY1}KhHh%CMb?>S9rmIjgXdY9kq*q?6X6}2uc zpy-?V#+PU^V39mW^WE9v1#}_G-1FKuX5NrR@eSf0itoTdQq`?;?0pX21%N-9b>5LOU$hf zwzTJVyw;DWRQ7*ba)zidFxVg)%Ds2vXCjnqIwJtLar}twPx6niY-WdBZ4#86Rqa+h;ZoX z0RX;9-_oe6rq0c8v|D6zFB!`np34)ivg`b{ zKHu(pw{7Z9cP&+efSgy%@L_wt-;tg>9I_8QW_f(`aU%blhwzEUwap3;^SyfPv>bJ) z=@{}Q=GmeKjG;h&V(|>IbK8w~gXaUpAYa}0@b<^oaGE>sq`-)t^3aIqh+6ILi!j4Q z+qs+m@t^WReX=yx7g^34z+^p!dm;*YgJmKQNCz`4op}Cc&18S}(dS&=jr7szE3eD{ zHAv5Z^B(n;X*ugDMyj|y(Nm5fYX3fbf*9pKr~u#FImPmb^n1IO)tY{R>3g-I)bj;O$bC7*sYhBYS4%o+Nt zVZrA?k|w`RHHMN%wuc{vv3m9l55o8lPf3>Oue{P)*yYqDe(Z}JmhUX3x}5QtcYHBqVX z3ri|;oD!5hT_#QYGDz|`9q9=+1cxKrrqa%r8(zMGk-p6PN8qAW^V1x%7LUA@)8&S1 zWpYKxP7$DY8|D5}A+>ltrdB84llR*#Rq=ek4_6$aS>*Vjw{-psu79_Jy~Eb*oK_oRv`~MNd(of@_q8VF1?8hOA&!#P}(ynUwv_@YTw%*;3vV5g)v3a)q27@`Q zG&_%w4BlT}LY*Pn49b+Rtl{@gPUGxmDEIqU;4D7+4ZL?ny!GA-mYr5F_nerT2|Qi> z9Ppw1X6HS+at!7v#o;t)>r~Dumj_mko#KKe{#K*d?=%-Pv6T1hW7v0*T!H8zAv-OG zuqg_neUZOAXuL4n@9$XW$0}1)FxEZ;8S!l?RH585@WiY{Mz%p_ZPCMo(yujx)*0~#!5CHLopdqY;jFj_?ymv--EuBcMZ$1G(HY)^ z{ltHG|7BiQr_J!*X?U$&=*cSPXD0f%6Iyl!=Fl9URCn@>suaF2H=wAk^rho){ekEuo}`Shnze~XAPTe%2vz{!sPwraiwt0-#taViof zHgr{!RZnK&a{l1#VJ4095zETj?beCC+*~!|vw}0y4vMKSU}vWjokj1XfNyH=+-f!D zIW-m-oGwl=po)ykNEx{64BR zmiQ1>TOd#La{Jb{@`Ja-?A$x`S08hc-|~7ev*jy|4i3T=)+J|HA8&Tx8zLg1Sa68iQL?a zi_9uxd8+SB$wU@*c54_mc%1O8u+IM8dY?RjFPbgAw@dSOf=xaNW(pmfT+T@}TwigO>`Ped-bTM|ouE9K~7IG7%sSg`U ze0lr1^{3n)$YYL$6&~2uT0@+pjibGGQFvg&7CNe;*-BgVqMnnc&VE9yDij?DuWOvE zm6{#+qbQZH!lgki{k_Um+^ink!CAoZBUDdKs@dg8=+lD=JJ!kwzKg;H%FN63_=a-+ zsCm?|DUE7|-68V5Q$pk}Llx)d?1(oh^9gG3$W#P9Is2GS=5|M}m`^s>sls!RTJw-h zdxgGmKyW2;E!CI14%KTE{&nj00aXQ38RPaDL*i00hvV!Cw-Q@OF79*GR{b}fsSfkq z;dLg}TC~kG3)2_+4K_nDzV=~3Qum}*A{bybsySW9i)(Rz@Bi_R;v6Fn z*4Dn+7c6yAsIg>ja<0~l`22apAeY?0d-G25M*fl&L|4|J;lq#wU(xa{?uK!S?#dVA zQ8CEl&p+o+)tFtRzo!0qA?weq(Q{lpXNRE_Vq6AH_K45sUEI$^-o>iEQBn9Vw$6LN zI##7*^FB#5*m_`T)}#HJYKC8|8@Xr*pJzk)&b;27NQlkfOtIC_4qJI*VFJNHR{hQ7 zCoFYH>g8AdYgP_!8Izkpc=0eR^1}ME9jv03iI~5C2pzFWqSMjCVxN}OYytMq{-Z;s#mPzKy z+>kwy2bl^HCwWe^pLN;&7+ZLAUb$!ycg}O?*KRWM_zu6x8ea9zN7$XrmweY--|GHH z+GL2T3hr4K*pUNWZr*kZl@dFCGHjA^t6#f3@zhF8cUf!X=F6ChcDKNkr)ddz`}eK9 zjw`>%0S1vdK*PX0pDF3v$wtq|zeHY*!a-%}WA=CS_;RILx-wu3&;Ae3U0nyQp&eNz-p^gax=GBcEJ2b^yTM=p)rKbs2{AHa@0hL&5XdM zrhO0I4LP{^I-A$imkjNpH)v>*hMC@;YdlH6Kg#Zpe55!3vHqCm80r_T3*T<&pTjB8KzyRC2ECSvv z`h^~|_>4T6bnpm^bE6c82ppacM6)^k888>5TWxX57Aj=2 zF(=J_M*h=Y_{Rhvoldd6!(k`$>fCJFTM6N3T-Bmfan>KrCGTeZs*)mCP$T(6JC({N z?FR;n1x9plZ!Uf6Si8LG!{Rr0q4xXXVDU8ix7tJL0O<09#7ylf-%9gc_vcA(-f-P< z32opuzo1!tjg(&8`Sz&Qer7R|GqQXDBi}8*{4+Rw1#y}hds>zw^Q!kF%YW<%0T1^7 z(;g=e`EyL)f7+8;>=y312d`-+Zyix%TvI8`=|gt!&m-iD>y&Ks@C&H-RnMngOe~Mp zTr_md)usGi{VX4z@+WsQb6i}=2{!vJIov^zY3rE&k$Q$u+6Bw+I|ql# zw`?XXh~~v?`b!fJ&!`8kqBk=U5q?`+#b-*rA@uj5f6O4ivP+K!1|GhRkQ&kzy+{r^ znWp4<{TumHFj(HkzisDRCP2hx1@RnUNU$P#UI$OGYmb`?L?1@^%{IoBNlLEUPpm=+xb2i?-`vD>7`ndJzq@(-z*Y?fV1;pwFDmg;H z^$Baw;)R2ecYQE)k`t{-=GID1yJFkNG44l}G8su+fj_21mK1B9!uOnUIm?M7!xO1{ zkG2a`W1~xWw$Z+8?;Q!bP9Z*ZW=8an?9aLYA0mz0pq-&-w8F{y%8IYR4Xe{R0hfkL zOMO>tsR6wU9_&euwv#N|na#saug)}BG+`6ft1%7>9*n3z$b@ev84n{p`uKQ#`oEQu zRaCH@KP7!YTltF5A} zP%2jI19V?_irHnSBt6+wH-hAwZ+Cs!EWK(yj67n1 zM7uFC=w~Dt-PX{(# zUw;FhpIiw91_(c+68@=s`M!XWwJ!sQ<~0f!+<(jMo+Mn`|8;}jC}8&7x90_8(aRB3 zUB{(JQR^i@^h|49rC9hB@c?pZ7ICXfC_Xi`cvV{DJkqWcUpZo7cPs46?(~-Ffl)zp zvQBIspVyPvM)dRA50&4kwRFNusHoY}^m{2%Q`QNJG_mb8D*8so_dNEkFFs8SM{z%I zxc=Ss7b-4!saLm@fhaW;a@`i=^yIH>&)t=gsAT7!%Xb6s$F>+g9W{GuqP-_7_IZ4L zdLLG}b`u|?BLljX6EoRf_W$tfH1bXRD;Yu~Vq+A!YhvUes5tbVo-(dHPU#koH(DiA5J=hro)PCby87wBh z9VXj{-CJW;F|Fa?j3lxwz~l6W9()pNVD_IjAybg3syR54g}wS74zxsDxHPT zkWz7d_<6cJt(=qg>N|i${EPKKWV*EkGl4#|42-MyVOa_(pdMW z_IwRKtfPt;Q#9zgCl(&(&hTZQlKb@tiK>fO_jTFDSKq%&{p*yyb#QfXoQZQKsugQx zlV&7GH+$!$fHfRKlPvkZP}>Vwt4huNC@w#mz2fF8*{Tm3ZsoxLir~9Z@$0&#cP4#U zz~7!LwpKIDb&Jy*7+pA8J7k1{zOn#5=Gmn|b~+4jkL)~^Ep=8H)3LPN3_G=ho>ke^ z1l|10$G;k#BIW)RQ!3xD#8&-L!R|BcqMQ z$`Y>iUkmhHV&(2rFj1v_knd48d_ZSqziBM>8c1nz^`wt;C$(-4ESN6B5^p?df2A7S zt?c^z-i02wpw+!OYE-cZ3#ay^&*vPQVhIW~9qaWE}D^FH$Y%K+(F7)l7iE zz)N%BY%XY>j6BA{c+;tQg`O2EkL&r@Q zjmMh3;sf;L%}n9$^V>i>8g0c7fusqSGMg|YwgpBwyHJ2)p!JM5Z@8dLe%atcv-GlQ z5&s)?G2C5z)KH2#)+dCts`@W{GViD!&VjQTb5X7-wtF`|kxR(?9L@Or87)J7e4$1) zGYcjGFeCUnE}oL4-hXk!K+_78^gboTkk!?7=R zX^&oTP=0eRPXE6ZRmCM^bk6SN`5-os#Ej{M2koTyU#S{Dvg=i8V(IKkx~Oz$`NCqq zN8UA{mA!dGKRO8ZI!`Z~4rt#So67Qe?8RP?$0t-=kgRUf-l)(STZ|-`>;^*HBqu@Y7&k(t$$wXFq(#M21@X zlFU{rG631+Z>o9l<$uk@b^2|-y2i4}K{6E-edda8zV=T^OL}3@se&{1wH0|^?2T%3 zh7IQs!52q@Wz;Go(c<)06kC$!G}f#+RPvp+f5a4i@wiPZ83v)*uI&ZhrpDMRUEAY4 zF|GY1KFu}%Hub#a?dD&RAD&%Noi#d2hR!vWGp0K%->fKuo_|2oZYS^Bm#oCKy_Tw5 zS9~76kzYDC6xDfX!yv4J63(1>OJVk1Y~JCxo^igOtieWl%FK?x`z3OIqY}cEoJ|fQ zJh95t)V;2gSKCw~t^-U>4VzhEHy9hdutG9Tm5{b)-DjHYYwU9Z@qszup(o&Q>)Tsz zbz{jt8p*%nL+adlqVets-1vsOu95UZT#Bl7g}x~5q}mEs>nt;8n-_eQo|3L)9nt;9@+7k#c7YMLA~DjHI| zibKkov8Gv-NvZ{&J06 z`yhAR;&eAecTXKGa+{;`qt$}6@Z>6L3U6Khe2Jh9lJ9iza!Z{* zY1VTYtJCFPm@dw72VKrm7J3sFsm%rk$VSO1G^5lgG#};>{hv!3u0wiNz?bgyxVem!?nhtdU>DgXq!f;$Ls>dTi2-Ww^PujX8+(d ztCn&V1vLSi;3%z|{Omd#+qZnmpq}*nF^@lcltJv;rde+sZ_)S_hF9W@J8Uahd9>E^ zx4%6vZ_`y4tdhA|vQaG1X|NGZ3{&#>EE@VpTWXfE!{DF!eZl*MMC-*?oLbNJ5#(V_ z3RkTu%b^{~L*>M$*}TFa&VMk>X_=8u;4dg<6B`z7xZ6nn%`KW3;j;edzUOc1nD1Qk zB_wkux)o(4P*-i6QP-eu3j?4Z{@~2xmiMh9Bq=)QpFGZw?tM0ZYhJr-VU=^HUGx1e z3|+n!yJP>)+_pQ%f75E!J&`mlVhul&;s|eYKJ{r?L|v8OEt01?nmI^vc>I7f zGYtP}??Lntq(Sc~71gASdM53|C;Mmc+Xo%tRm%!=9{=q0evg1o<~@k6a%E2QaLELf z;5$xD7nbbNj+aJj-=Do{$PnJ9GO3%D*rs~a*D*75XDMmH@XxZJDL2%< zzf*|oG}IaZys9C0d~nbF+V<2EkZggzx@0Le(T6-NyqW0^bCj9mSmLtPrz$~}KYM(8 zV5|TOp!$3BVR7TF2H4~R-8%rc&eSGI(*-Cd^Sj4+j;Ng< zx94VL&%?j%@nzOiQ{$gOtkeC?t~GEq@cLE&E|7~$YfC&_gnUA!DIkN`kC&J)oi=RCZl%(K@_4;%FFi z6i`m8KkmAd&is7J7B_h_;IBXEYw57@bt6FmcMN&KB;_rzp0woc(Pe-(Qk*lJ6j;1q z%_)A?Gp1cca=DEG(t*ej2ji0=Mrq?!?m4~e#P!XD7xR7N3mTbee>*Y}{ zNzhK5&Q#uQRIki$r}f^X%+6dI&usNa0As&Zg2yPZ7yNu0Fj=nt&EF!{m zhX!8F+`aW6%A7I3G5U$~FTVQgs_JUWtK+s)o?xzo(p6hax5zcyMlp^Jse~)zYI~ZG zYK4z=2o_nLyb*>kzRsV8uXS1Gr!=euId^%69zN`tqT$`h$WG%D2>Y0gX%Yr4LkS@Yr8iMPUR94g?Wr>ma+L1*ZCQf(Ddvlm=33mw|>{t4+A%`nwpNk+Ys|<>T-*Tk+JgCsn>11 zfW|n#?*V*VNKn_iWoMm@>;+kx>m{mxU~Wc6vTfg=%mK&{uC!#`-Jf-6$v%nyIxB7? z`{f|jsBurxUKK}96FI1JHllb*MN?F-U=`9xT_ zOJaV{()Qh{^_VuEY!dK+{XlK97VE^*&G}}xGrMGOWBs&Sf1FHys9KoE%i)@RFcnSS zcWvV=5Ub&VpwFOw41itn-gDnS6a(bhGvqW8LOfnP-HEpBaG41i6E8jt36+-B10V6o zx{^I7hih|UG)U+-0}>C?xNx|h?&lI5O^m^O-HFw&hlIk`E_yBpe70B|Z(?Q;*(Dx> zcg)Gc{9Ul|o4HtLYZQ_9GNYgtLHCfSD^SCl1%nNW?q-!SeHxoN$zA-~#s8M2^DZYDMzb?vROx?`v&i@vncY2{qq!j9JW`4^@eVv-_rFt1&u2BHiy|O^vsiG?C(ml38c{$9k$sd6Y&u!TPFWNVj_Y;h@z{5 zt?m2dUi2-mp|0Riuwgp=@h3X!7j5S5n?*PQ&>%uffzhe~KkQvA$M7alO_pdaZ`$nX1>A z`GcU{=)lS3qvQ^*xj_!;okS27>95ojBdLUd9+qd8cv;^>N~-Zqj^#_q`m`HQS9{M- zy%e|Q;b+g6+Ni{q==HMyenxJz)yq<^GPy7?DjKssc+$X-%JgtGb|=Z}N%r2)tg|jA}s3_s1~x^;^T$);7=n)8g|cdO6ZGnBBlvZGL{+p43h;t|sIm=}|)Ff_#=f zj+uXzS*;M~$+H?BV)i!bOfA490zj<61YP$Ls5bUK2=ML}Ja=QZ^Lph~&Gs-`qh(In zo>D2}(5hBWB>KU-MvOk8Gdb~o4DAJqPm|kTN-5YqV_0r4(J};6@^@sY{k@FB$1cUy zJ?|oy>&@3Ub#;5QEdo2j6=fYHheeYoANsByJ{%am+xFdxMJtmm)QcnNMP}52_G+qT zmv#^jD=F;u`!V?C15ICoI4M2L-4tZ`+{N{0m(xdgwUo_Cs)%xsXfSg})7_<_jEUav zbF{3$_2beETjy&%Z>hKNfa%i4;BvM;hIwm*pri3^?Hwna4QDH(q9CUNGd7#|qbazTaa@F^50y@(OL%LuAxGsKIY8n?NRsx;H~NNi-rkf+M9nRwK~N_bCn3%(s3TXQ`*uiHSx_ z01`ial7bwK93-yZ`_pR{gL9RFlMAr~hVE>qyh7+UlCtDR;gKiu8H5f^i>E@&kWV|u z0&3^?j_UQ<&PJN`S1Aiph8+*pN;-iZcUw!;Drczx~yZj_KZ6`u@wsp?iA(}o$hm5idP>L@sLzZ&KP zyW+;1uZQ9NjZHti;Qjn5S}gtWZ*movy6;2Zj_+0<>XT(|9v`h*63OcWCaEv$m|6w0 zuK0ZVd{aPOaedcDiaJL5apY#a$*z<&id6P&1(cHFH|Nfy8Qx@lnW{;+q1h&IK;-uM z3ZhkKv-*@h5w&%AvA(fH?fuv4b^a1rjpCp&jg>F1KP-zbWslWro_8J_%~07xs>Y46 z-Cd>TP@d3JP$(VpAuG&2^WgVsiVI+{Lp!0OGbL}ao4oe<`r_>^T1iF*fra)9j?kxw-_TX>O$Zb0+mc41=(pA3(}y%b~pgu{mhr=W5z$Y~KmH&t0d1)>~Ev z*8hMv8&~ZY9Tey!%Q8NU)8&cvBy+5`0-lk^{*kTO4M5gosp{EZ&Y05yr7StBKIqPq z$iLBRbIWR*>>v2g$6YUSZ+qTpHS&A;b4j$4mdC^gtMM%R%hYS7gAN1Zz^=Ge5;W~U z==^}(y6?BUQfosN!6T0!bC&jM$*v}aXvq&L ze}(+wTjpN47Ax~PcQP1xtP9OcT!=ccjy!(qz(vjsK-!))wEn7)qrQ# zbz7CjD%P%E+PPeN=H=}!xh{`MO0(WxIJsFYcB`aAKyX}URbX^n)P5+f_|9O%9;M_T z-)b(?EVC>+9`du2dv`00I2c493+Ku`k!ROK_eW!W&mNo$F zd`7wXUDW*Mvj4kX?0cX^O8V6nN3_@ITMctcA9S%@o?dKAc~iD_lX_9jIoWshDY<3X zqP&c}9x6xq>2R7xEN~_@ic;*~8`sgv5%0PW9~YI69$*OWMf`obm9I*_#SpA5vhkFk zs7ik@qQ`;5PCb>*c&z-a0I*uaE3}o6Zm>V1y~M_lr_Op@ zaenSW8mYnF>2s3*95HI~Vcic#O}!ELjKV8Vz8#FH`!b>}_+G>rdHj@|WbUAi2t=1j zkLvMYp@LYa6ETw;?ZUFQ(`JiipkjAuemGvg1|-EGBp>eKw9r;=ud1*@=ZMJ zx`|&?1WKX699B=nOG9E*em?Of(EEywAXc0IDNKwmDHDMhNc^2QENkZRsZmf`qN!hS zuC#}UF83PV8Y=lFLo*0Jxtw?tiwyE!`(MGO@W;^_;#G;?AFRdPM_S@74mzga-cQLOX@86a7(l}z%yqH*2*m6&k$Sx7uNl<7=4mb|rtx9++5&f$2^ zn9y8f(jSqyMkHUJ7W29Tg-y`PkmQP^Rom>JnmGE6hL2>woy4y!rH{sIrXFln>n4zc zrlS-mX04rZ9t1oAZ?*|)gQB3#;|$-NR8@W6B+jZzJsY?(2^`!so4jw7u&cp6#|wQQ z`HSySRe?;wg7H7zQIjZ2j>nU?npWLA3o}ZJVQ)TIV|BBJ zXAHo=if*cA;{5oYIaT>AaTV^A_^n|k!rNfY_U?x!j&^hC@q4>P2 zKl@{}AIp~8u6yxh_IsJ1ir7hlJjJH690_llg519XAPb0b8fVX+%bZ}1)us$h-^w3y z>@sv<2R%K<@k*JRX^bsE75@-%%rK~(ZC292Z|D!9n9S&(WebP+v3RCjV%=r`tVHDZ zyRuM*e2tShf)xsSuBwGjT*5w0TN{?p$oUraLRN{xTA$LjzNiO#rFoT__-0#0Y2ojg z#F`Z&9efhYJp+{yNp#0s8!d89j=1)%$gsk#E6treT> z&jX1UYWD(lUqsGNeEeJHey_*RV2Jd4s4Rm5@86;1x5QV!mjNUz14;^GA=RczJJRt& z1noL-z)zXVHYc}zMa8nTp7q!P`TD$bj4 z1u}sI1=GX8)#VdXO!U7%AVa5MU88pJnZ|^Fm?iy-RgmpaM;FM|M)?(VU-eVFaTD-N zx;kX5`XFh>0`nnOTLPEJThRO)?jhEbpOhpUmJIUed%B&Y<7#}}NtkAfUVh+Fl z`0y4Nx4HvujubQeTlCS!X*zJebLWApiR}Z|gJ$XHJiu7QZ33h5NTe?XRrdSte#Hu5 zSmxdztPCUG^ltcjRiSU&YjK4&IU3txB%Fy0imK{A{^UP!6;en3p(QzQ8C`k0awq=X z3ZBSXB~wfSRq)G#!Tpx7S|j5RqT9c7Gl7x#23$65uOqGk)NEf*UxgKIU)z+FAwu~@ zDUag2Oo=;W!Im;Tdl8`i#`f?kX!RL&QW6@p!0xP4ze(8aB+f>j7B#oG;;yK_JDF|2 zJEPN`blbLSP#qej6!_Q&)a1#m^4sECGE46+cX`u0e)CI+M!2u2L%-FQ5&2VTzPDB_ z1*9&77%+LYxT>b7aM8sHuhbS-C(`k*ZGod5v9#7s+iP>m8qQZ|$T>O3PR@GHtDNRw z{wba&0F4wnjl}|pywFh)+MpJ`UcN3b$?*)L#EVYo`^yA7gducYRUTU$u8KZCea~Uf zjXv*S!!7Kp>QlE3P(=khyST$U#qAewU2@k}Dv2?t^94AF=Lq*wH~-&>&k>rg|(bZi)Bp zTioRR=Ol?>H@K!|zy|`*U|D}8T(-dozS2<#f zEvmp6&(xqi%5^$)wkWHrxB#&aUUle5?a{$YfxjuxGiSbO z=1-k9+((Jw^rVM;_hkJ9Po_iJjT+8_P$OS~QF; zGkA5`8eq3mv;^GR;353zhc&J1455KZ?+h2+<22;In)pywCb5v!-JqVcgzgR8p0f+| zMcZ-J-SiH}DSUpRrf2PrkNfN08(g{RORdIG$5|gfP3{p5-LzZ``*Wq6Z`88es6z6AJ&YTgGuCceATi$5P@@` z`SvAWG@0~B&-mr(4!acJI;?1uWtKT|v+5e=F?@%VW<}1$vG&_fcnN&>RRilOg<7qs zA6tN{{8b1^UFnZ&Y*7`~6>i6Y*I0vqY45g8jeXJ7hm(~BL8f}oKSpf4QoLofnFW3b z!$m8uU_#0O{Cn%U1!iOCJ{_mJtUK!rn?;MD$({$Rz+-?7oHN!O3>P>n`EB~*vIm?$B z=MfvL|JfWrk?7Ru#GLN7wRGNUC$OiSer|ePHGe)6c#NEzUj2mrY72QP5y33(S_#y~ zu<4)w&m)x>qm=c&T>%T2xpNs`2`Nq+FFTnY+)m~~F0pjrz3k~#OH-o5?01HPqy~jn zRPK~<$?Z^&D6$-$p%));Di-8CUd1F)p^%c93|y@`)UI}W%T7UlQhkpIubXWM^USsD znGUu1)Y>-1reES@s*3sLvpXc>-M(2osLseOny|sMb%B2nlYLfr+&+E!pXG63@L;)F ziBpY-PM@bcrDef^S-f^Fxy@_E74%n5o%bt)4g*(ftE!)Phm&N0hqZfqk#}C$Bb4tM zZe%k*kT!uNBekwwaPXg0Vf()+g9pwah!-es*=)4U1HXsRe)T#id9}{nz*dWFHLh<3 zw7a-Av(6Q~>Q&=PXk{1T70=^8RIT9IxDrs|dbS&GIJb#y(F)qu{M!6FZRO*wfKOFz z*ly@gxk>ATmJ-)yQPqjtr#ZWIyFBt+BiGHVyjFi!zm>m3d>aqgvDvT|_k3`|>N~k2 zdv`b(5VaB=9#2vNJl30QV%m8kIxJ-Lw`~}s{0JbIaJzN-+W*taQ|GPXb4@+SOW?4}%;l%to;ne-1`h-mDo>nrnKD`dsbZLJ$8EtU)B)zWYcD_bg z@Y}&Ru6(Y1+fU3~ri3f#-j3KI)?QIEpQcwPXaF?+-z^}W%LV>=pxv$hVukvQQXlh? zy@?{MXsjqkGyD0%{_jCa*3EA#t3`=a?hu;Lh@ehz^wE#kAYoL_0wRz2V#S;Hegsz7 zZi(7|rkKMdy5Qvdz3>oP{|b0!y)ujx>_=lWlAs@nIZ6DBPRV7` zU&_&bm#NC$1J=CPM)bO@4w7$~Dh?23^Ugp7V%4UBRKnuF$(91&qXp7LX7_PO7vlje z1U+eAb|Xnav8I{5FEo2oWmOeYKJWpQBJiF9tA9biYtyh3cw5k?KW3oe%H-sfC?&F= z)WgrlvdeS=fI!4+^*=CpH|4Ewc8&4Q^46~#>v8ea7e>6!3U4Eoi>tD6kXRJiQg!xP z^qANcw0q$I#OVIp3Y^i6AK~1@`Bk-W9+cx_NOe`pX{52=F42u)Q3Ptmcv%c6ab)#i zw?FO-=(KMn*f{}tzwn+On4CGuF%#bqz3)9UGTe{Z%eHyukWmr2IMT@B|cmuDpsN~s@S6J>D$;9B^@er}p zl?Zmh6O#@K4db+1G}Jhm${jS7z})s0g;?^(Gx5JEx#2?WXz84teA`IQfL34_W{}d`p|M(g+o4PS zRF0ucRcE`KDQ^*v#DGow!6*ee{gfkId~Pq@_&_Zjxjj)l{;}H@&?Ik0W7RR@EBy?9 zLY4BP?Y^#Mw^kY&bezF4e8^=G>F^^2r~;HOxOd>7;KWoITUfcY>^Sv~#x8FHAD+2J z?WAe%eFiR3>s4z&-F#uk@C}%Bis!)kf|LM6-Pqs@5P3L@kRS*ZM1B>c!JBS5`bYiD zlk|O)JHZmI@uWv?{sMbVqBd#Dbby$mVuld>$Ds{y;C?f&s+Ph7_mHaaPVPt^_Sn>l zobCnFDs(5ee+vs;b${$Uj9zyqmFT4#`D@HGa%>uzpUX6SI=u9c*PpM!($enOoi6hx z_A+zNzf`hA;Uh?TC}^Bd&33a8sY$G7Ev3MpJ|h-MQ7|AiBlgJ0fVc5Yg&|@!DhjxR z;|iC{evD}z$(2s-&m4}I6(dLB@e_~x9L9tx|bbO(4Rtga!`dZ5uIFY(QD?LVOIJYtsnR>08f`4KQ;e(N{Fn-GX$8yk#*-S^3W zN%7Q}nNwEWTJVOe z5(Pv?!4p2B?%m>c15&cyquQ`!k0 zwp6uEZ)Uj73+&hpgexc>4{zbUR&fvd*0m>IZD`VA2Kp`bGMq$9_U-tA6J;bjl{0zs zT$2oD)>T`RH%<%bNo2%3KJW(F+Cpki1QY26h_8)yZob8-!2_pffs?x0Y=H{>%64Bn zIyKc!F~&h?0G-b~=h;ozDTm~w0c9@1A+%hzK>AbBa-hSW%|hAz5iEX0%PV&jdss*Q zjQ($`ltMdnLrkqvJEO9U7NLjP;)8zxR6b;dc2d0pN_!{D^{saP{)3{~IbZKjB{zg9 ztiF*gZ?_NO)MI$) z?b(l0Aq9t17OA9S8?bolUG)SLx~OODo0{gzOzO3D1ZRTZchK@l7P|#q@V;yjFyW(;~ZqTz5z*ly)8ePsa)HaMg5v)nx2XQ_|$eItL4*Awu?_&`i_aio%#IpSqQMIvwSH-J6^inQ%zQKao@{?JUK6gvqr6ZrL+&s=ar8%*L%pI-OLp2<-6rEGL5#w- zdA(yDlxJ;wwKQ?go<*yYh%H13>+Qw6-ILnW@Xw^J?zrdAn!|qP<0x~}sTTh1lbhP{ zn>SPoYkxN6`{{*HJ-|UfDaguuif6#9j&tz?##h{wzh5prl zHr_ghhqCKz5t-{<{!oLQeXuSWU)|8^5pH17Ei!g>qZ+?!xAy)DPwlJsy=*HEw~Q># zT$PyIGrtTv2Nibtn|*3!5rz>+8)F*G=*~xic5;9&Cm*^KS4! zPQFH+h1_+Wt)>*vP^`-fK~qsSl0jH!$2LD` z8miyMxj+YXEWM)aoCV@*Q3lgK#jm1C>kW(y;oST(p>H5Pp7K@us&d`b*Ax2tF7_4q zTH>^9JFIJuA!{Esh&eA&o0zkwE0G1(MVc25yy3qwjgx&lHtj}Eu$x*&5V-=ReLjXa zDB6>x<~g9QkfPxNP}jenM9=81Jx)>{a2K?}Pl~Rt4th70GXD&WUcSqm@N9(2PdQ5{Y5pth;oX!wuq%H&)3Ej*iC$$762|n?(7k$+ zLlfEtOrSiYdkv3Lv|;$OSYPPRA74>r|EvrAq$+Vx;NUkw155dKjwsvo%&6x9AkA}Z zmg9Kjz*-Z8qZnc_}><$K3Zf0V|9)pb|L-em8z5gz}dzv5h@BA3a51K0eqw@A}_ zhI{1Lw@ap@Fit-~jjcEySm2DDSES)T?(pabe{yOkl_vynzwG{(cPqxaK{J zTuOfuwU6h33D%e&;c*dFE*JJsiIi@_W6r)iCFhrPKNydFt-sNfP7n72h7O)bUMd|# zPd`P>7_uj`Quarve`fIS*j{H4Q{fY@B1g7Nd_04LcgM4_KH%Y{MlN@Q%6C3{HA?Ss zXy-qr4~~xfETORPrQMdACIw>Rl+|*oq=08!T3cTmU{D zSIAElDOFgipvFY?nnqJ&p%Jc?k`KExicNJK;!74FO>txZNxj6)B72o6N9m>E-f_uXhb|%L`wn{c z#Kg1CPCLR0)atS(ygh&=B8nhaWcz96Gh)a1F~E!j`r55(ldP^%We@*71hNg2uut}Q z(Gir32PWVR(U904P@ny@j)!+BS^6w`s2`UuzcrQ-mjE>)qd1!voEk{xGf*s2`=bdf z`3Jv5J}XhT0r6!htq;+L!@Cf%r6~4YMN57L)IpF%Lo>6)0{?m)1oAmGVt-+_ePYsc zfSAMVOW3f;Z2T!Xr1ZpT8RYNTl2pe4Ca`8VbbHTy_*-s!;H5`$@+Q=;5(P79fLpl9 zdB_~11pv&W4nCiTK#dkD0}@Tu5V~~ERsVtLPpb1+1W?Yd-Q9X0Q+l6)SW`GWhJuk^ z`g&-g7z>&Kh?$qz%=^sD28SR7>zSetn#GzA2b8fD$H$hWd7Fq%%>t;% z5Kfi?J2#J~mYv3d9i)R7LcuI|+XsCaKq>OO0$RU*&_VNk99S6PzPDR{p+4^8a8%*9 z&B!k<@X_+0e&@D?4fZ7|i(EIvH=?mnxt+=NyrnbbxmF^lT0%fXHn_GkRAAy5m?#A#%5 z^ZQeLR)>GsY(9rwS0YT8s3p4NXZ`~*3p?>=E06yNVY&o4cQ*4Bf}pJV*mfB4opf7F zCp2wQ&|&s&DXWOLZ*v7kw3fP=e}%~r2Kir5re&y`p*evL*kh?xkj96cSAg$o({TzT zr;uoUpuwq<&%D`vmA>EXZP$<}y5k1jp@!*_!3&`9|0mM6M?Ndb3~E*qvmW~FMpj|< z&s}bGC=>Te7ceGyTeMUXZM10szT7TE&A)-iWForZF?xc8s-MX!5F|=((oeT(ox+u? z{U-aU8ws&ELeJsB3k3*Tl_Zd92+!X87KhwObG-lX%-K zLR|4(lGj=zkdU1`<3)uF^SmMs0lP-C<}A+blUb~x7d%q;Wj*TJSafG^p%kse`()BS zM0wLn#y$2S|8g?T7H)_-7;wKw#`$O*gB@;%X( zM{`~ZCy*ds)%b`sXq@Cm|C%_#=o_=WUzA#_?+*Ycx;+e?35N0VP{Z5VxDwfneRq%! zyK~Vve^^g$UN3bgv%r+vcPb(RDKP{ZM;0V_P__;!K^KatbH-#bRG2)L;`uAvln06~ z!!PmUCSMfZt?o0{9yy>f*~S}~XoykdyU77C#+QIHHdc17*M9E2OO^chrHKrrA@GaI z!8_&45vKP9KDj17&lfjDbzZEi1E!>aH1rA%Kbb;{^-?#fM(m}mj?F8A3J%}S$!`}W z%mkDmVDtBfV9=xGOdE3m7N)ugH?vqrZSZYm828uN^?NG`vMO9}3yFg!@UyF2LD4u= zqOWGFg^;`JYhF@jn2%@F{aZ%vVJ9uBAuUe-4_tMA>n%RVh;h|=$(e$}w_qVamN z&=czBSnnm=sV4%l+pwdI)?gcTl z5@kqx@ZTZ4xnu9If0c@4fZ9x!-`k3lob{9~fnM|xe??3Ep0h_wqnJb&)Khj0rTKFVO!}RBfpVTv z(mJQ`q<<~V$B;-2?g>|r#jeiT8bR{&udrYW4HO5GNolSq{h-l(zU${C;ZyapTCTGK zW&1|wYeG^2es4N#ioVcM6{Ev2OBw~`e6ofrp7Ki)|1GYimqy7kkLDqe;i(?{Srmqt2u4v6^(vkqb%(J;Oc)-U7>=B(VH=t#h4MG9DnHG$r{`NcVf-hBmRRmYx6Zp zM1*v1{}9D*Sn13iRuevwWOfgbzwG%qZA|960OpP`lD{}feng!tJYg^jWk*~TtU7UX z;MeIWl#K-@X>^j|qFfZLR$Z?Yl(IVYJ91>d2vXt($M)2hlVV_r&7Gjs=lx3!H-+AW z>8)GXTH!%9WFYrh@~5ACHyFd<@8~g@XD!=wKIm^Qzzao>HcY_fIH64oibantee2l! zzLiL!h#1q&;tb=&#>e+ef1qX+r7iDa;|{e@oz)q2ZI=0kI$2-a_d*5uAMs7=Mgx)U(h8Pckqw2u3+xfQbIlJx9wprqc!6%IYkl z_y{=%pezxL7nKa$>bAmv!NZ1~TwxF<3NTBxgUO27+k&A3k0&TGKEk|B<-TNiztz@^ zizFg;lCtbHIyyB2v$*seI1Ip2B1_Is2DAmrJ{+j`svJpX600mSb^C zN}^=`6e!?-HpFYFol`g@+Un9{7ZA@1&VP4{E3(=GF~DDi+#k^eaG0Ua6twz_2{Kp6 zrwtFX`JU-s3!~i2ZA1HB$8k~@2v5M(Um4pZC>zsV4@af<;WfjPGUfvZQgJ7>emGV< zXWvbw#qwIj`m9CrjtlW?V%G!UGA~r+b~9#!_D_Oq6$^sIwVQX6Fu=@qVg%r!G+hTg z;g?tas0@sI1UUE!MPJ%FCOlPXQ4p@5xu*aR=`}RJL(J|o^K^{)Xmt7my3V&OVVvy* z`I9Him}t$!%}NSEXqT>_;D%$lZ31>)uzHlb+s1@<^5;9%x{P6lR&WM6RUAeJ1MKNM z%88qKh1oS_4op;y1tIqhFF_{4kpfLUtT!rsH$yk+>Adfy*-w@WVuc~_<#&)HCo+kd zUpyr^ayq0`@&uK2%ytI_dv0R|yvr)3!3C4pkL(rn)3*jsNKG=mfbClx(g`j)?eb3R z^9uh$-n@pQ-->UHi)jWtch1rFw&EAnaHkH+s&6LdzoNH9PawN+EAww^n`&ot%uMctqIG7OGMGzINzL(u~tNjX}rM|Jfl6e@&rIS=z0uG|y$v zKohYy;saxRhPi(wBK#4O{0Mepvrs|I)Cf07hm1dNC zx!|vkvLc>|$|QeZ=80q{&PLBh5_yq1nIi!zvkBTUnT^ja&eRlbH@(>7BDz{9`NG|} zvd%{vv@Z5Cl!W3s8>pzJj5HI3lJ7Ooh$~trYkosk{OJ1^10Hx;a_b|;HCwBk@TtSck z(0m8wO`YKu<=Ko9VZpkuP3YLH>FGdFb`0mM^L}MXC?WP?y-P~0g`)9rPy`w)LYoy} zu0vk>m@y5mKU1+gA5f??PSZh%G3mS>!6MwrkbdANwCiDtEM%rFcS7fcJCE$_W@^L3hMxcafJ)1KWrN(1=xCO;SGenBf@Ztyh zrt|^&yH^??TBv{Y_(driK@DVRH#|4`iCV2}OtrDZNw6L^`fbjx(YePh@e9d?>A^nL z&XZrn{yg1bE%QEdzWfh^`DnQXT3ZygT?-7PtsDKPHG*Q}`Kr^AJwwww-W{;*Z;{Q@ zbA5UrV)4uguNZyGH!jejwnld{3$*~~;s4N>R4mjd6hm&6j0xyJ+tnJC|GHIwC*l<1 zCQ8=ZAZF=4BDhf6m57r#lmFscY)A_pC}G-qkN#g+`Fg=CO@$fV50a~T4&u`<*$9gI ze6@>rFn1&K`BRUOn*K7qrf}SXUSva$=f-RVddzq0o(KV;V608(rBUD>Wx`1wEwXDt z42yUmR>G#Vzw;i~_cG%E#J0To`K3Z-=;@}^n%Jk+am(8B0@8uRl}PvIcJ8R|Y!3nF z-80{!R@EQRAi&yKG`6z-V5p?c*9XNH3T$Nbg zbEEa|`(Gl{>Hj4%L$8-E-+4}N@bpF7TXePMiimeeLh`=lD0lr^`|&eyy`EZBG}G1T z+MF(%`YW;lD(DR9b5ilIA1qL&C_ zoniNOIhr6~9jh=@m{hf_GbiN4yty)?S8#JP{d*`uX}(9ZAhzpwA*_HrEG!cQPx#^E zZsMWLjvfpzW_DOIhY}+~p4vI((@?Uz-)oaMmdnn=RK1{c)rjh}%^44`&3s%zK)^yo zB9;54`N`Lp_FGOr;OYH|qj*2rTF$89&iW5)%uug@>mja zx?R8YOE^0LhfG+uR<*eT3s-i!H_Zd1m_i$$J_6gERbpf}7vF?xY%fm9@;nls6T$hL zMt67rfqDVwrvv3dsbi}dMN+AX+$Z1$DDoos;HpUknV4ASjz-v@PMv0TX#o*k_IUML zSSFN!bL0RzHyJ^`Zi&W6qT%=5!`Zm$*huM|Y2UT*ABNMx zTWvudGFhoOhfsYtqs5a2Rep|To~Sh8wP`#3lR%7_sTS?1Gh4^uklBstR%~{2ub}V~u=(S-6Rw z!-Z~1)d82r%t-3DA|w4(DmCMl_6sf&P5K37*3+URN4HXnc8Z0G38ixdMHYHujRq+h zuB_OUZTMJY)r0o>M~T3tN#fGZsT~gYcyVFMR>N!?ehY;?8PeYb(mm1JKnnx_k($CY z)wNEQ#N;<6CfSw61hsMs%fw0Tg#??Vf}Dzts<w zst8x{{xAR8hN8%FPj>QBQvLUI7@G@X8P5#9+r6N63nStG8IFS8GV%QB+)s6?_>hIU zh<%28p#~9p$yl`10DDJY(iTL@*Xhr&$u9T~gf0OaU9+%Z=vhqaE< z4lvDnzWPf67uGj^LiU+ECd9_N6khCoT7o1-94?4XZd~#E+yE?A7DiD@NfR`_rh`@x zpKxqt4BU9nrhbJnhnU(Foll?I>Hnlc^Dl9(WIO_5hn7+tyjZJdMFFLq7f{+afQuzs zYO|{7rdL=pvyl0(1h8W_>xvqA;jAu|{fPf)R!kuxOpE-z^9e~^Pi|9w!(^49<-3TA z44T7G-Iw2Hjbg=D!kxrQX;Gl!wa3s8$gu8Gn+zj_YeZ&tSM-0EVRpEMsg_VRUE!Fg z@=k;+R0pnEW^t9^Ur#F6Na(F2w)5G*FwseIo?wA85RD%97`lRZHFdynNxlJGhNP~O?= zwDYr8&AuD5LPK^!4d;dE@*BQCxhd#LCFh}xLASy!eBCH{FhrN_$VRM@+Sdvc1GF83 zYO6XOONnLDnT1AHVryrT%z-M?95`2bWS-{yhqd>2$sesqNGsM*k8sI(@|I`?<;N@2 zfqEuk+Gh&YriCOe^iEW=7eH~Jw#rT-c6!X$Q4y6_S+CgTssRNb{}X0Le{4!T{D_!> zeu3El%MM$#5QlV+Bf3Hq~xG|(w(BFdRNA7?0|rXi8I9Z6PTvv7)$ z6X3#|;{A$uR)Pje0#ex9=se--@yfY%u^hvt#kU#_2gP#H8*ijrr16Gajk-$gzWq($OQwj#GR(zcOqtQ3J5 zb9JxJB>aBl#97RGJq_l*%Ro9dFhMSy0ETpU{r>}9Np+!XjwbID>wA_ip0~ z51=c9gRzj@bOh->9zahYfxT)<<=gOPa0d_g%Hhm(Iee|Ojfq7w78re816?=om^CHA z%*}9b>T>O9%`P>?B0u&RLyj!8KvRGr^+m_~FGE_0paB@t^BVSOr}2ZQIc_0G4XrqV z;?IJHepYDHk3`2DE=}?DsD&1?c*AGN6$OJNY(Lx*8URq^Gsw=m;0F?@;1|g$AU~Hc zc>Q|z>tqfk$3HY;W!FdOT3M|fdYRy3uD38DY|s0(23}_yJ)r&)TNU%ff+o^=NLTuf zN7*sS-AwZ@iNHEi9^&)J*+P>?A?WhM5D~^VrQF;rNPIf?LXl-FPGDzN#bGRn50tvV zQ%AQmg;^F&;a-m3LSfYlrXkkq3-e19DJ3og;7N5vTp^DzMPPbYpgifg=}-BdU1W6a zeIK{@^A{x&o9|g&l#}=tHy#13dLH()A1lmM5YoO`igUo-XP*OOByC}weE8k@Fl#WP zqppB^5BRy-^}XUnNuJ4_?`vY zKRi4fV$fPlm5RYMBD3tVwAgk(z6+-dh?Lb{P@0R};$W>t=KkSB6++_+!nv5=kDCA% zt4C_|*SqKJ1j)@Xil^ZivDK`A^zRyDzhz@+K_h%Ng5uPl|FV!`|J#R0X0%SXE{#_2BSyOAFsOX{E$YP-*+kYDO2n_#i@;qTaS*V*}4kA!Zjo z^&+0B*@jffpVBok9m&xO%?yow-wj_gIlwX;i_RM?~M*M_Duh+0C3p$ z&4qhWkLMACiN*WHT>`mSL4)K~Yhl@BDpnc6$)v*8TjW>=V;dcG2#=IeiyQ|fD-Dm_bJZ#T-e zA0XejL(m7T(zsogpjwrA05mQ^LXSJcVcYIrB#aJniVK!!{-yeF&?`Bs6V5(ykR*-> zGEXKZ?LZ_m33*jpOL$kyj}CR|SLrjX<@F`@jm-)*JR*oHS>}-3i&%cXXq1o)nh}j# z_jn3+%Xwy(q7n{^o>D|M+;M{=%c^Fmmzslk#559E;=W?}dE?S>Nde!xWrp+1oq|%! z(aT*qazgh#h}yHFiX7mM*K)+VHNWzIU^%9a>l@ao``s!BXT4G4jUvJ2$y)2$rk??{ zWWQb#N=3v_bh2(oOAIfWWze7z8f*NH+dD8WPCFA%RdTbmQyYjx#e+L5W@{~HJ<#e$UwHT< zD$PrFjWkXdx>gSSm{Q+@qU1xgRPc|@QC63tP1&-KFBzE5=ocoQ9RcAxIs0AJT+dqX z1=6UL*O4I4e-fC4jtEyHK#NL5|45TU7QBM~*c(U-4GCj*tt&+krqc$ZrDh#lW)G6H z(`B;(fYJAG1tV^=7e4L?%XC)QV{9Qr3W2RP_&^fr-^!TP-ODG}V)PS%jWUsmeu9 z`%qGGuSLkrA1&3Of76Z4g^q}$Qj+>g5={&Gki~^ftVi$(ZO5k*XrMR2n7kobH9gA# zA3L$|LLn8=F++HLRVEYswgI4yn{!Yq>7r#Vy9-@gNz&J9reNmb{knZJ=3r)DLW=ip zTQiWy_BafKmk9`+)2ob!4V)?PR1OP#Oi|M-XduKD)05vMr9pqvF=GnX zduB|v-bWoNl2rH6U8U_@c=oTIS_mi9ZEZxz>sIxUSUGxHep1rpyl8)nW(bvHZn6e` z!a+$VWW5>A;(`_{nQX3SthbIzqo-}%1c?F|5BPl3qcw@E-b1MaVOzyV+v`>3!Hw9nVX6Ruw1Xt>%e8ic zh(Ikav@3rxfGsnX^wsOo+|H9s%^;8y1MRCVGvN-AX@TzCnbbepse+@;hQ5#s@sp*^ zi#r#>yitY)EPH>iTch9Aw~_&0t5x!1>vh)aWlpFbzGOS1U1g3tug}NYe)rIDH)j!F zly;eINb-ogw~!grgdpZj#t#=7fKF6Ep+ESpxz|i?pZMnwQI;oqRcw6k8Q&RiSrlwZ zP-L(*ZQl6se0$|;G*?UuA2ai6^6-0!>HGF@Fkg948yA>m*^_4~mDFwAM>m4!|F*M} z0rwttJoq|EWi~Z;b&JFQ+3C4#5y_VyqYU=UQ_A}b1&{W=$F5A|tszWxFdu>rgKK%9AU`Lq?uh=-$Eky68Y1!Rs;*GZ|qQ2&oqgG_LZwi`TD-szO9s!M+Vr|gK zsj;(?F0^v41K+;bCn$i5fAg3F`SX}jZr*>8zhd6PgR*SN_wpyp{&pYBrNJid{S4ZX z@>xWl?3QF{(HRuHW8d7!!pJ7eL5y4>EmSyEH8+QpxLm-Xjq`GI%z*u7&gZncffdc1 zJ4CvPL5w_t1FpfsIN&(Gk+s(m+>4FyGSWzey$E4s3P|kWR0y`ADal8y#!g0~mmQCh zaH^p}NJz9{AgS{PsRpB#qycr(CHwtG$zj2ECOQKt6*8YT!v@5F?_o&sSY?;uRK)rh z?SQ(jFMlUyAHm>+$$oaZ>Ir5{BXY1SHNY{3!^(-C@Mp(b5yp~%oD;)2cqfBz{qflQ z4wVrj({aW(()LZi6AX@$+~f}S-FkS}iQe!<+xkfToR>B~+!jUUjPhU(vxBX56R#=T z(n*u~!rR(7ePqBlbiYole<0^jMf!`vroPU9j=)V%xlnj}e{xWkaY}NF$>@iufzwzD zQ#z&cCV8Ojs+uhhgReL4Y$%2QV;|xOXRkrbCG0tJEaQK#xB)u-z{cWIeMk4k9OY5D z)pX|W(T8v~b{UYH)z)$thV-BKrU(p}U1`R|#)@G?ikE}nq%&@DmNz9lB(0o!W{JFu zc=NsJOC3L5!(V^<`R8wsI5_q9O<{m>cCUv)6Xz_eZs2ysrj|W6_04)GOvH-`KeQEp zpnZ}{s+7s=a%o~l-s=g8%cyfXWT=2sP|*%`>d`B2h!ef@gjFUqdo{w)bs*c7${p1BSrcinq9 zHWy5i)69N$g50QXK1sU83ty8$SIUf%Xx-uiIbx@!^Oqk;g}qIEe1%w8Al5ExloGjy zVu|S+T#eG-4ru($w^Xohhu>n$g%(EH?|M^C(I|d6p}M_Z4n;S#vK&{v`kN9Dbr4fcP!10u#UHu?_(^d(~M>4Zj)+;5en8ouu!x+)D zJ57dNYOI{ENi7W0M1Fc0&RXpW<__Y0e~S{K=u3(fir?Tbku^zGsYX|vcm>%FN`sGv zHY~^B)lik3xP>`5k(O^U5ZGt@+|{cC~zO<2@!jo#+@ge&%k5`x!F`IhMNVhp^RoIykD0!a>z}dYSa*=uYcjl8kf%braWIY(j&%9SYs~ zfv^HBX7EPXRg8IcY<7ATMTf~;Z$+JCFe1gCk zJhHIivjH2sE=L=9jby!M#zrl}p&_Tu2LafFE9v*4McI4OYwZAogkhNiy+bFOjN7zwH*voEV zG{gbUIyIFik#SaDa8I$X|$7*c8gju4@ zsVdr>wi^2;5ikn70zv0qJ48-AS$l32frR;)3nAAAx;KP@?vLwK>wFG$`4|m_yEI2@ zri=X|5AQ76m0y41i!M4v_i3}Xv3! zlj=xkvuEXlM(6bI-_dqANTWMyH5AgN0x=c@F5WtX2DIt9>>4oEu`PD}2zEt$`CG$% zWRZ9e>o7Alxc&Dfex|6;8sIBs8SCDUbG{62^w-^fJ!&-Y8~uc{d$6-n?n~dUYLV<0 z?ialSUm^V-d6661n8w+nnWU}*zs^EH(j38q-Z4{v2ajew6)@tlq#?GNyN`C-K&>7( zGdPU7jzZyIB|P@Tvou-$o@7P z#WUqiF=yb{d3{MYP@Ml_csme(8>&D5`PKV)p?;^KTlaSmyT_)2f*>cW?-pUrzb?C?KHGg|)>AuqW6DOTAt@-|NEo)^~(Ydxl~E$buk#zW{Y?KCpR z?OkyHG*CP4?WcTX)l;K5i8{qU+K|kr1x#%duJRxIWZu3reY|dAhfY-X)+zoy(?0F% zyH<8Ab4AO~d82vQobQ+QXxb)oACsxoX(bCXot5) zdH6LC=}rEFnN})c*M@FbuHY9lQTF}Ui8X!+k1){D=Gd`CZrZyxN)B>7JVIs#Dd~ zr>FgmOvK}8F~S9z5nquQE$(dWjfJb72@Jim;jlLm^FKd+HjBXy@Jg8MeJ`E5hU%i; z&Wkkm^w3D_;&jKh z;bQvQ^sNg%l``FN=Ai2g7bX?&MIdFDPzc_Wuw63$X_^&NThObrjBDGE3uNR=Z}?Qu z5n!R=^z&`sB-XvI(WLDy67L_<4F@x9uVeyr0(egKW*KsVbOP2gs|WH-QTC4zo=zHj z)}&=awp>?^sR!tJhrO5H6pS(^HsUxnz z+WLr`W79r$E&8ifE6iP)@Mgqi&g*7TT*sp5*FU`c>oEoDT8>Qy6m2{F(?n0A^|(Kfr$C!{ zO+KZnp>kf@>Vvq?jXa56!q-# z484;-@6ApTc_ibNp4PweL7%iy@wn-VVp${m_lm_hwF(!qK@XL~($*3mBhuf3%2@sh zF2$9gCrticYMmOyw+g3{<@MhZdR8jOVC!$yD8#goz;E&mei1A6EP*jXaT!&Y-?h?y zJzkqDx^3)pmf4)A3^bPY1r{MLO7dZhd#`L?Xk}a(lES6T8%_S=-(1V+Sr;Q{rC~a` zzDe++Ht2#X2|AyUgR8|e*Px9uD~lJ!QyAl=ZmQ4X#>Klp_>(o$H6qK$o=z%QXnEepUoIWY=_u+w&vhRYE zuEY_w??rOU;=a^M){CS1l7nad35thQ z_evf`osDlrd^d|s-@+K*2$eMj33Xc`e-(B?XFKF@`?e>JoAis58vCdz^-P|CKB{UU zZ&0I=X2j1u7!2OQa@Mue=eJKFqqQbc`Vnl+74JCx65T#xUTa0OP$VYlkT2WAS?5TT z*}~Q$^4c}R)T=Pb(#s_X%J;}+KalAiSZJUe(?(lIu|NV%=-Qd4btN@TfDSwDUIXtS z1pX_%ItY0lG|2)l^0Vy(HN(z4uqE>qU|*5UhsOz&Lq)u4=D0ssX}IaXw0P0$_U96< zMu|rf<)!d#9odz##k*Z6?3Q`s2&h(Yiil5JOgBPgj*+3KG6%drD&({meIYgn)R* z9Yx-DFlQ~lYb=5Eu}Zi1Eq&PqTZ^lY>?kIUV^X{I{=32#o3}`K5~u&-NT1Qr9uR^> z=RDm#eO9j84fJjBKgopVR;Aguy!aK#0YAD)p#ULEG`RX9>p4}W{o>P^d}+MF%mp9t zZ0Fy`8coh`FRDL|NI<8Qy7`xJizQ{%xrySyMhdS=vOT>&Kgyn5fogni0SyX$1$1BD zErxsM@Tw07a;NbJ0(2`pIQN%}XhCY2h`CwKfL}`W?|5xcy zvT=9R(ZY3KB&OlCdF-!hAH(1y=z{ikEjmwmLMKsC&@$PDe*5qma`o{^R|y5zjV+V|NqO@W7HOTlFom>1 zX#zqKr`~ZyPyQ!SfE%H1#0xsvjF=v}L(O6DYgSt$qp_Dd+L(4ygi&~pyYt}h#tsUw z+Kqf?n2YOOz!DqoeScas1s3X$jiiG&E4=V550%U2+B%bv_N+nygW9L&rFmDi@ctlzP{{9)JLb7U6ugl-yDwGn*# zT}j&y$kpnPXdDTy&*X7zZRfg|P*)JUM6o@p#C|A3A z;#=obR8kSUS2DjtGww;~BgcN%3_f4P-#T>V$6RtP;pz&_BjJ`FX8t(*gkxjP?cjsR zA*KWotdA%!2@RED3Pq8hqX>>ycN#DDbGxfK92KDI`=FrK%nt}Em2c0ePTJXFt zFk9$uKL|011$>sF@2zqQ7bS;K>Svv#AE8#+x_P`DjynOo2!b*ld{6egHs|JF2LrZb zr~5yl^VOMZvH7)_}%~j5~hZn_>X~she{xd`*X*RSg^8CgEA`l>;i? z?9m>F0{WkW<5u>3`(#XCMNFDBZBp}e?!ym6_H(m#$keVajdb=^;F|(vfR?_EwU(R>7 z+UstWof4R`J&^MadhyL~?RKIQvrqGi8r4&WF@jF5bKu;Uo}KY>7-R~O^ZBm(*&I>{ z+)qXlaB;o>)%*0^N}Xw-RD|Yut*H4 zCJT0BhtQ_GQoXx>E%p@!O^6uJHb03s8yU)i!Y8TcFPLYG?!98NHNHB{`c^y*Y`M~U-iL;!W7L~r5eS<;SkIMIC|jV z&t4?o`G`DwCf4JFXaW*Kr>doZJyU^5Woyuyp*DjmciIk}U!}n{4FH$oMQ`#O!qO+!80D;v@Q-?1MZ_UJyP_pAO=BrD8!N zps(*u;V-|sRQ|L-WO#ZXnkyo?`M62Qxs1{_$nXlLPYAQ<8;Z~-nam!-=Vd}JFg>GT zkjS&A)Q%~DR4Z(If*9EsvMtcEr@vXRI{;0^@Ux?Ha_xbj+W>xd* zs-?JV>W8UpY1L0JW7$$@5f&0;9O%j(+b3MCCUxkYi+PYZL1n&N3iY*VL87ElWCqNzVQ-aC|Nh_PqXIKTODIen z=AYw#RE}3#rtu?j38uFo8TB0_HMP%Vf+nd?C?6MZE-GpJoJ^v4zpf@R9N{p)1=$ptTWaO78_=biPH zX5TorrL})n3y(;!3I51im=`^F#s7Tw-%UHiU!P|d>&*v+UDoO`PRb3io} z;bxL?E|9_Xg{zUPmn*~CubOHpYUTvh7jyQxf7j+^*(-I=a09BEa}pWn{3i9qqS#8 z>tPFdM#SBA5{wxq_h{RnL>zt?UJC`4SCWsh1oSl(9&i3F8e5$d+G+AY`P@D{v+jNy zl%Mk4^_x)U7ti|HW+TqO#%hiI`wCS_Y+t9!jXVpW*f2?tsrPoN13D&_ECZbo{2fznBx;b*oj(s+>H$6W)szw=AKps(BQ2&j?_B*%3fFE zEs9c_mFTr$8{Pnos{}vJv6zNBv+zPDmu8CwcjDP7OCNz1{jMCljddL!Rujt8WMo`e zb4QwHyom0(Wwp!deK%7Uv{rkk{Ltwu;kNLLe-4x%obFa0ef20U_as6@YbuvQDl<~J zNH!j?%eD2i%YAvTnQg1VwDK6_wwcx$j*blzn=;p>!U|FM4uV3tnWIznv4+G4ym+8@ zo7p{7H>pG?AtD6uFB<|odG~oaAt;@7pU-bpu7!5yBfl5^40#x)%=VSV;kytbCiphJ zd_5oOBu4(?)r&R9t7H)`q(1A$bDu0(*H|<(Jv#FLSvbU!&(waK{BA<7uDwZ(%;i%)KtmY+E>$F*9r(ELfXVEnU z);r~p6lu(Nm@*^L#o0<^G6MAVQY1#w7XL23$+%{G>zsTR^_e04?P33bw-#k(c??JV z89k6|{c2CV(F{2ZfQn(29yzm>$_hYRjQh3HZ%ahSHG`8}WNh)?k$F~Hrjs(5>nXRy z*$^(gVIOlP779^s0kz3B#6=K>`UX$`Kr!DL_CJ3^?PdDv`xIrEbFJf_&S!_hc3s~K zC_#Ol6GEWNdV}KlNCCHGWRXv%#u~^bZx%iMkWU#9+l1y2ERA-b75;zrIlZgVCeNw` z-`pa5<7GDA=fXS5@J2yxsSR#CyoVyfqsPU$g7gHZ4%~{V z??^4Wqi})@-XsByckDgS=_7Guq-BtE#oh4G>UJjV)08l8B?;jdl|=NBUw&D{3U!fO zI@l(Vy+4{wy(MjF%K$L-z2Xq6$a!KpUC}N^ulCcZR~Xro6aH*N3vl93BfO_5^eNi( zM6G$QWM9~C2xMGj6+-b)eH+J=^eVrQdE;KUB|FI5t7fhMk7a8rkKGV5W4PAfbXqgD=B`Xh^8my*%Gx_O@4ZTH4(mbD*) zwr}ZsCdeg*1u&qw;~Aoi=p=HTxk*E}DlRnJGV-UP(vfbKyQda4tfh}k`hOx455*}% z37ux*t)|~ekMSEj-)5F}m+NY})yWFbzr1`U4jFmdGY`oCJQS~M66-*@w0x0l8f|vy zKuu}1ZY4d8kd_$lt@>Y;7Un337f}?5H0u4oZg{vgJwH+~Q6nT0$h_#YExtfRa1Q_? zT54v2J|zNjH9+lNiD8J&0S#EEKQFo!OaF5Qgv3D(lvpa!cxuloJPNzoquZtX(d#%< zSNB8GC`>gqm6+M6vt?5vVx~`+bV4T~3m!l|0Emio;x(fZfdGp!+dD!{+;%<_XnH(G z*HiW_ihIqclp+SDrrc~f@s4|KTLxB#0;6^5Q;)EizSq`mOXlSg5$z zxqjd~?e4HNP_g_GBhANW3H(|42+KIyDlv%QHhoLhLkt=1m&fHYcx0ujMr<0pg%i0( zT1+NH%wo>-r@ljO9y>^e<2A<%u!nMo+O<^v&W$5{m%qW&n(@*!vwPS}y9C zJo+lOP}ltMw-j2fJcpxX`Dh2w43_+_D^el3tRwmVz1ou?MKg4SR+(XaIR^aD*|>*E z_qqH&Vc>{^QNrTij3Lqv9fb`CFe=ebdQM*{57V!g0B=^C6PM^FLF$d~ z(KfGNmKZ_|rWzM$aNPs5HEm|gNk(l&T)Fq$HFxzGB{cb=KMnF~aPFof8Xb@QPP6DvoQp-w(P5};{=Da6>{ zFgs*3U{&C5R_>Qy1C_PYG++zI{TcMCs|XAbM_HOq`ul(Xn)BfEV4YXm)isa3vkw;k z>|-MkR7nMdi1fFW*cSpsD0Q63RlV>CuB44;vUsr;k4Cu*nvy_^OVUU^lbU^n-2pow%9JDqO9j&#DQJZmqjAi#R#6>cZLZh$*rU{yrj-2rkEkt9@O z>aUH*%k*tTiAEk0W?g+5kRRxwLsb!Kd(6SId0sOY1t8EEoF5AO{HMcq0cM3*82~gK z^e^N5G;tnEG_Syh4L3*vsDPF3GAxaN)IYgN;+VjeBt$KQbrE@<`q?V>CWR{jfLPLe zd~Yw6VM^Q$-t!RR3=RMqN(|*n8+$)ao~3AEIGJ;*9PoP_J-7@fqQVTL1}ryJY9~T~ z?LO2C4_;QMaPFe^Th?p3O>(6BsS2H@qYhszYmow2&4V&u{sh^9Qft6R3oMYM&m9Ir zW?9)YALWBY-j=JT-nGLxfLv499GpPsF+6vJorD5FN{f0D<8{-IHvAcmVU+_*sIN9J zG7t>%7>nI6)g6>!SWjZcM<_8zRo*{k&9o^gV8v~>U;w(72BRxv0WHw3EcdkdXKm=p zEs|M{luN1_z<8GK$x>|yXr8Q2Qh_wdaJuOMNfJ-nr+->R(E-)q_g>vELwu6xeM488 zt1pd{)}!A_JK<7lY7Qg&r5RN{HA&w-WoK8Xrdxq4_xXtKH$$oc=9Vm#D8RH;P1n~V zD|LX+Z~%CfryMle!S>%FHxkmrF%PuLX7;-8Kg3Tg=#mbt?O;3Riq;4qQj!HseReqh z{4-*Fw9JPU8ls(K=w@#S(&sLLe3{kU4$pbR6leA-Qqp2VhvMV19C`}Bt8CPb@kiv( z6}qIB*0|NWSJ>B82jkByHNrKqX7?wP3ab&#TWoPdk(5~gesL|k+^14a8 z9gfKH@!vt=*e%5$?=}DbQ8ZeF0~BKa1U0zhy;!c60@p6cD?K(W+LaqPEbxIazTli{ z6}d(@(p4l#f>x7XI%Qxz{uHWg3N1&P)I49zv>#ebXm@xh0 zXQ1ok182}*hr79Wz1gQw>3exGM7xqASyBr&dws@-x$p@)@+C6mkJ4G44VrS*43*5n7~cs8tWsN?03+1Pkqt{N(n zq8&q-zH8bF0z_l-vIL0ws2K9)%t{qGaLz{CU=(`xPD3QDyS@W}Waz+eN}^NtxBD?5 zfx}5_u!PfhHH1|X>kFvvGds$c&5j9YR>t8uBDhU%LCbNah9+bZAFvM9F{3ayTUd_# z2uiR%0|Px4d14-{QWA0*wNQ#oP5z(PPA`8BN**nvwvHPewUQXHPh#Y1PAOp)l24dE z=~oE=lbG+m>weP(R7ES1^_AM|u$+T9m_q~`D$>g^P_w0Wd?09Ja!mZA6tS zQ&-CIoLpJvqQ0Rz~= zb9x7#Y9WE=K>@+ln>POe%?{}%oAVRS;1VM8UsmcoxhEhNBLz%dvl>!rWr#n_xu}Ju zLs3Nox;oJZ#=|Y;y0ey_wLpT?T?g2Ie%UqzBBD*g!(XiAE+Gn0P;k@;>mUP#pW1Wu zxi=`cP>P)q-XE?amw%IRHyF;wHwGF3E%od0nRBmOZ>R-(>lX@?*l)U(b^4j8q00tN zge^`TGf`TAkGWxwW=ZX1CR{^l?z_Njeq^<@hCxY2MX6hEDzlYzDc)?HB zN-O4@P;3)%fd|Q<05vUkw3rNNYTVZl~`&n1N(Z(a|3)7`l)U<(V$lss>Ma#+>uF`}h{(WG_+ zw#sbPCMCLp+f<+5K(I6Q?B!7Jt*A>K!pUSK%sl6}zep)_wvXSEHxaIl_Ri_IQvR5y z+r9o9SH8dcs5(jp=ium6WHzPW9@c#IaZ#PbOea8U2i=}o05grwH3^bSD6E#iN^H9F zc3p7V#I=@m%TXdEQcsz^(#!ST0sZdESrvM&CYp$@&AD<{SJff^9e)J75OIVuqy&j< zq<2manAXu9yrZ|BGCDut5X3fI?U+v8`3ve_>dBgNHuoX#^&ovn)eqBjNyTI zAkEvowXf(NsL?tq(jU8&WHn5kK=q)27Ce;bN9s#Ywt_}~W*2%)XYx}^?ff}(kyvO2 z-2tO^{0JY$_#CBFDbFNUI^(RUJx#EC__ zSu=xp`HrlYZfFz998sig>3xx3YsoV=yT6LZbmqJZo{<8tB1-sDTe0%t@qg{nY4E}| z^-ur#*M{*zM<)k}EXQ9lzi`z`e5SU*w-6f6ZFq{h#)F;K^E8J_OONd`TXZHdz=L=V zXu7u()-)D{vnwg?p^~hSPJPj(kDK7kUk9AO3IsZD?YW~ZSYApBvk5%e(D&W;`k7G6th`2CNe-(D!P z%0NFdk;i}8BSO`9@(hD)5dWySFl2067x2WHa2T}?eco=Q6WZ^Op0bA(z$9*! z5Cbi9JsQ=76^p#8qG*fckQ1r4M|kp8CDDciVSITeC2>L&P@h{3acu&ObosfI(BxS# zaTC&FQ{hG=Hi?JS5U);_pp}@^(7~ML8pB;N45FaCQJz@aK&y~*Y;1wVR|33vH7XfzDB z6>a1)K?jYz9-(0VyNX=MzYxs40v%x08b-yo#-!Uhl0r%0Mh<;r;idW{ZHi%oM%0(~ zn!n_C`=#tRfo|-ag0l_HbPhww5E!Vc%~Ah zZ`2b?D_1W32|UVcSOoy*K=Vg*IF+dH3Jo`SgfjBBr?zG{w^2zA4J=x&!H9O>W}DWw%kwKqU)d4!07H;y zKfRn8vUihbMi~Nc?G`TlPk;!I73kdF(8rG+gnKr-E$~|!UDCl+Xk~m-IYio&LpaTE zxAw=Rn-z>qOY?5f5R9Uj#7+ktNL9kjmiZ)bDL)ZYwDB2h0y8}M>nD?-^1{Y4;#z_% zer2g4_OXYkuA9d0c~cZZWnNF?t3x%X>4O`5T22{YuDJjw+*$hXM<{yP)$}VW>NmO= zs8nC+$aPT5LMUE*nCi=#JcAKy9zk?asg#5{0x*S%0yXlng*j|mx)^t_-3?v{2X+Zg-e78q!OV6a2W0b&i_`c-_<1QP6ytYQD%Vw-?jXdnj zUFLpDpWmmHaAS<>(J!yV_BT^tcD4H2i3~$8zgJmLy=5?ANlpj7F^%}s{L`6<#pn5K zSqjBmpQuI~4>U6vqJ&6f&D+b}K@G5N?UHOKVMn>*L|g3&nGx*DO^I~)0M-$X)s%U? zd#Q7|9QezK?XKZx+$#m2NwjASY>k1TTj{si!LV>D;TN}C9jji8T%^2K6^$RP8P~l^ zCsc(O&7b2QFP=(~%q*s9xTcp)Fv>k1+_IM_t-oae^nSc(-`}w<#e#D*UFD$-ceSg1 z|BvHsc3k;Gz%M~y;`8I|r#mLtE@!tPdCPEa^0#0qk8!k8KepLCFvgQ%WDIH2ZNIfDvAQjrpI^v89r z`_Kf}=Aw5XT6zB?3t{BKT!EtYZwtAvxH1(_kSFW8-h*c35uiXj$H5vuVyiRR>8`>w zzm^Wn9jVXGIzUVuTki{w_v{XJDEb@dvl{KJVC^}^1bjvmcDz%5a_fg%Zs}+X#d#fb zxs{J-mFhR1#*MKVJ#C03z;vsYS?)>azq)71gx9%+eF%1&ey zq}WU;+9;XW;lZ;>MbYzWBTBEZq4DlLKRWLdV)E?3`>n1&NuDZi?p^ER_<_)cl2!EP zLccg3Ze0ER?~frp@#wCb)VFc0$R4ldCIxA7ccZ(4to-DN!`^Ig?pA>jlKPW^2}Z@g z4QG?`-w=~1pdM%%k)5dhjG9-fz`V6(Z2IFsJzX8+Yw=7}QzX%TWa#aUyL)MVZi+I) zCA)W;6nGEQgjn-g|Apgoy92DUu<9MKr$1jwRFM3U@Kmkcv_bR>Ra8gs?K1W^tsj3b z`iSKuFMU*`E3B+3bbx%x!S)xRde59DD@l_qoJAnUKEN9!#wc{7$44+jUl&pTy`)QZ zxsp&^L7pg#!udO@$%f17)MTR+t111T9&#z4iPPIZ{|G!*-tJE@t1_WpI8rVfbmdIW zK96nEPH~=a5iS+9c%<^PpH4L8k#H0h-u+4#;_o~}V`dUvqo(&M=dYviY#CvesCv54 z=0DJ`cqm~(UN8qTZeNpIX4zdUpO<4WwH%I94yYJGxpV`{ezcFSVR6O1O$N59%zxw% z%QxG_ktcu5*WPFH1IT*i#cmsj`VED+5-}5kNjFWhv<~U)8MViwe`kBW={n+!6=44Y zwiQ48bOmFn;w(g1bo$$t;5_l;wX_bI__%Ppmp>!NjlanM!K+Ya_HUa&8Ptm@nl7;C zE8f{VnC;#D)yucjiW<~RH=<`4FjErUIPnauHwT`%Jl5?O`QW2SKz}N!b_wj3_WC!~ zcabm2CFfilAwdjs<)zO(-waG+Ld2HrXRA@gRJhz&8Fp0=e=7S@s;=J3;Cn2bx7W?? z!5vHwe3w@WH=6PKU#v8xE4 zy%#l!f3?wy=}mTf%+^Wo7c;Y~J2B=L=ml<5Lxyw|E39lAM$K8>arA(dHNrV|>i^(F z6^wtJkkRQ|%_>Z8)oLVtqOKq9vN|IBErRh}F?`cWdE&7Ilcx>~h?U z20kTxGuyNu_N5K|JUg?{VWhk$pL(jd+lni$tC08Ec&%`X{s9bLGh~})<|Q-Vmh7$W zn~$!meCxkL{oa>t*A&0p40f|3LNbN|+YctY?cxO59FnFy@hfmZ?`zTM>*8d@{?vYw zn%l00+&6RwjXUhNWUiF2os%V<$38f?B#-GNW4*Jr-qcPe`y-$ff6Ik?w4mmy6U8-J zFN>HMH6XKKT^j75)?=-AQ17q(sX)=szJA$xcMN`B**9So@LTG47z%OU{PMB(`crqG z$LzeUcHV7I_fr7+JHT61;c-Z>Z_?)lWpG>DDn5RdvhUjSzpLM8YSkoSUYrCc-_w}o zMUOXODp=<4$chE$i*N6JaFJQh8?)IR!{m3>$@|lWfq!+)tKW`mqL4Gocy5n%8ZRaO z;WYhA*S5^3@_RP+;C{tTu{Gb2(leBao}sTr4m$L7d?!Hr?Ns$$lR;hD%52P6UabF) zlAPqfTj~E{r_tlwrtKKYPHu-0K5i1fwahIQgWcqvgjr>>(I7e5O08KE56{7I)fmiQ$^8WeDmpMCbGx)>KlJ@%_E1^AL%7oh)q>|kSi7m`@_Q|EY@O5gAp{u#wAS^I|tc~&}`fJ?}ydKmQz{l2Y)X>vU3uHK>-er-s()$AWJtik`4v{U0VUPiOrRBfEyn;=^=ZY0 zfCo^aF#(WKOL+Sm|CkwpRBGqJEiK8oO)4$aX@BPl)dT7OPK^8f5#Nk6k)AAn|91+t zkiaK~P#?hjidp78MAmGt)gGR-B>3xj^Vi7}{_xCET$`T{&EfE~+y?o{eI-c)248hx zQ%IVEPP?O%*@%nED`9v*96~I|#jBXcY|*32QHwqH_PsocV?u6l3o)1PX0x09Blhke zb*em@BL#kVE>qCl$(n?KxX#-;H7*<~g|!CtANXoXl`%9EDeI#s?A!O^54Tor1>JzD zvQc}=@BU~s!JCEDsPLR@h2>tIobT~RH&tcyqNQ9cE>P3Mf%}I8Fvuz7u!djh99Fcf>MAb7X4?&$?furdV{_omZ>RcDf z;M&76cQTwht%|5%(fI%a4UvhvRy#yd5z%P}lxz~R1G59n*lz=lzaBsT1W6>M?h&cz2|BgNcGQ#v5 z$S6C3mnlvyBF>e12(QP1$uiZQQ{0xAp13cCcgK=(tS^>4)A~dEaF}&bSBDgow9_U9 zfUI#h)tXi-u;DW=RMCFg_&bPzTAU9}-8`-bL+&4TTS@(UshO9oj{xcWvW~^qbg!e! zbxJ359>Y;0D||=C>FffHV138|)%m)INVFDmQ_H#HDYC_nyc|#hD9$ivEb6qF0I2(w z8ZTOBy``o$DKIppD~gs->fsI3qB+GDOn3n97Bq%n`CE0A9pgQJZ{8tq-rfXMw%_4j z0AF$j2$|kPEW1f~TC?hP9%I#Q1rufm&6nLQyK5!8W_6=SFLclc8Gy~(sD4{5GA_uu z0r{VeE#1=&YKer(DL^g7t6yXk8VV)(;9e>l0`Bixn8@?T3&WO%gmOBrH5@b(`ul+( zy{A%4nv?wO_InNG^2A|oiN5{=7ip{}9W%gf9I|K=m7D1dUCbANha`K*X zKNSns2F&ebu?uA5Aqy0YXZ1CmrH7Dj%DpoJ9@oM_47|%V84JeP`~RJLs))ZAd#Xnd zqq(v6<%j(y?JFLf;p7JovV3y1jUw)_oI-AR>@S7GQ?ynit_Cz2@|`|lMb&^_fkOf4mqG)#y!0|D zb4p8jG_Nkg6}*g!z9m(c1<;X<4eiwNG57hKTAs&-436?)Yq=S_SH^D*3W9cMu6n}G zmZLjoOb0L^-5s=<#b()@!oVRy~33~@}Liy(L}RQbLH(V zIeFEhS&Ps_!FeG$Xis=g|VLnbKso2J>80m8V1{kft=;J7xcQPjgW&KtDC?sywc@ zMV2Jnu9^U=N>jz=Vzp- zkZ2~`Emf44G(l;mmMz36tRgnk(gyE#o;UB&-HmqkLwB;B`w#CLRr+jH-3ZQ>m^^K@ ziz>e~-~OXbTM? z9$uTyafZ2V97XI0IE=l|{``uRE^cOgKCl0A-lf>;OMP;IF(H0TAdp^qxa4lsZqcHD}@C zGpg(qS&L`qZIl#1SDVGy=_FBf>_l>&X#hiFe7sh-%#f<6UaX#hwZ}8R94rTFgWJCx zOGOuo-6NEr!qOjDoUEkne1!WI>8rHl5Zw;vl(0MAlR>$imoW{U_KC8BW!%H-CO#`# zW}Bbl^t;r2O{WImUu)@RL_ToLdW>*I8vil+mNqiQb@RI{lJ9}ZL*$sh2&aR>zeN3J zbgH#l(+1(}YkKwJzL8y1t`wV_Ct{W?)AOKtkw@01wibrRVZe+jhsm+Ej9Vs+t<)e# z!BAAr@~HYTbA67D4R(0zXEr>~wSUqUT&MX;Zy-o5wEcD4*WJ+Yd3w|I;XVh^5t2NI zS43g2i_o7{fwC~^psO=TO7j%qz~tZ79I!hN<}v2>P+U0#fiQi`lLvSt7eYqjgepw9 zH;|d^SVi~v!N#CJB3MpoNmkd9$=xSSphbu^mne*|i@SilT&}sHU-} z*}q0BNNZ_it}IpSG-e}7@Onngqou|l?_Pi!+Y=!LTz=C^h^WO=u0TKhQHN6)1Deq* z?Va&AA`f|n(&Ks9o>c}|E#C>*+__JZ4Q?9B{8dh&?}0TegXZlu=$k~fcmq~_S9G!C zU9fbLb3W19$ODOx<(}4Gqw+Nf%no_jAzOh&E$%utF84p72?nT*onN?MASdH%Xq_Xf zoc8GWA5@p?1{5GOzpkv^+Gs2o*!r1nHz_F%taHpZkGq~gp5b5Vown)ZYs%01gKF&$ zIE-^JYo9Mi(B7xdC#D&2B$pFt6R$D(|L+W;ES6j-=+fZ*ztc*y=$(wn=wI#10pN~b z0e;BrvnD&mZ8l7ufG;(1M6Hm6tp8ZOT>HgdOK@#DHo&~w-nwim(s99$Y-ajGgC$VV zXF1@%$BeTW8y8+5Z#Q+bubP8uz4~PFdyZ)_|C%m#fq4vLf-%z`A zH~+Ew^uLNor#YzzArFTFzA=J8y`=BRZ_&WBZGAa80|Z%b(#Pk$LYm0CL^>FtIAaX2 z$t|wz+Mh}SW~g%aO>F7jfERe^lZ|#7P<=jlsG{h-A)G%Hs(l{yDDvUIu$;$eZHWsZ}M77)S3{Qg0MHj(cl)<3BMAfX~fw-tT$nP^B0bAIDN_3-3h?S^!E&h-mN}% ztMEI~XkKuzCwTMUWj=gKcmo5Uaky~8FObNW4Zs*iVieCX(mrypp>YMkV>c>dx>8MJ zP3xCVznllWd!KbE%3_iCFdyh{uxfw`4Lm>vB<(ZTbzw2wZ0niGNxze&h15LF(s8JUC(<)2@`^TJ{w^|=rn7-|GRr|(&@Fq4>> zm}su}z=6e*&=i$c6amlWnLad#g#Hk$hT{5Ko`f!`?xaTE8bkWa}AO`f33%DO)y1( zWPP4&z{}mB?#%Mc&aao}OM|@wVTB%syv2r>9Rd-}6S9REf5&G-!LtqX0zJ##H%BLD zz2hHdPLO&RCsv}>J`o2Cm$Z~fPAAwEOp1(d&66IE<)EP$5|86^zobGfL&cXlNj#|G zL@*0e6^uHz3NFLoM5q-~o!Q9~#A2=3^!Bc!|elATBvGN;q)>@z|8eEkZ=sbm0Mp!btY^{ zBDQ2x`wh_a#CNg@re&O0v;kJ8($J1$IRw1+c|e<+gz0Q&;%-i*^X7%+mvmowWn>Z6 zo!>;$Kf5@eO1j82PkVYjgx9?GA)o!d+qpE@yyu`%Evvn^+o=msQrwhxo6($`V{A$d zx2TGtZ5ocKYT+9vm^)0gUF=@2pQ#=N%-A+(=eMjcs=)bd-K@!1AH5^gr~1irwgl#y ziMLX{FNFWqu;gX>^ZtDsEY>5sQ=vWh)>`YyX^^O zGxou6$xEtG>xWckyA5jiVOmHh0J!QDdpH$^}(Bm&h z=f}oQn?v<*AJ|LQ2uQZxFJx}p&})1%xp($Xo~X1W1eWehq;#217Ia6}hPhbjPIrgB zr?0u{4%w|WJX>u3&Gk?5bLH8PB!^RjZc@2t+Wo7vT7dE!K)Gq;*uaOT$*h_8iNK1b zL3IC}stq|U>G?Pne1T>RbL#S3&}3=w0NQV`t)I$d*e6w;A0W5sBipx`GUTn7 z!?=rx2fv)c^nflgeBX~!6JO3L@@UB-2IRH5aDo|E@(y;Lkd4&^J!11*jGky|0Q>ui zEd?wF`j)dMxs%pk)v!m_F7<8`SZT}E@SJ?bGkgIq-oNTC6GM3^Pr{Dvz+y=lmLg@O z0u)=+=X(D4_@F%~Tp*SHX>Fq?KLVWfIQPBw+n|17`npJoAr?{Jj6AQbgHJnd=k#3E zY?Ql3(48naTSk=gtO;0lcDNk56I)yiL#EZ~sW6w5r7b3L$O(FbPY1I~c`-E6;A1 zlsr&SDuIl}njhe)V~mBAsB5~!EBECLq#n?aQ0T@y>^}SaUDFu{r6$Cl9+=1+jNHk< z(RGSEE8!YcRI!#xtSXw_XJ*Fz>UQZf9mb1ej!dOPpGYHOgVFN^DPZcff%lbKLba|! zEBAO&4hDBaGyo#BBe|MLeM{dKAYL{70ENgtHjV!qKy!=&=(}>jQphKo36T#OO2n?z zW&NEYzclNgo@11Bgq8GF=i0Y6$eZ2#AmQx~3rlY=A2Uh-ikzp)uIWTFWfYQ?gCPdL zn__GltjZ8?WR5GC3vFN%W-CsH8{-#)^lq3bEX|zk)?6E`OPVReFgNDUC5OqaH4Ib{ zndETtp6eFB5__~q@|!0Y8T1Ky0X-_MkX6=C_^;UvNd`=k!XEy;yd!erKS{-k+N&Bh z&vsMYjd(AwZ=#?4>EkvW%BGNl|al;$tGOAgp%#>wKDKl{={`5z0+y88QTBgbH^ZLy0X)|5b04~8Zfppc zMb8tF4(N$qV5w*E6Au?S1-dni(fF<|!a^y_CoNuNGP z$GT{Wi%iKj83G9A;|0Tyjz}x9gkQ7r$2JM}+XL*4%#G5w_ao+K?L5mruAXn3;AAb& zMo*;{s-E(A{P%ut8EEJ&wDNNeK7gWaBB8qkMb1V&iS_I#A z0KI513ITf4_iNbUyC|sNC%CbSpnZ4o=xnwHR~d_@T=K#wPh#@l`N%2K1d~LXJ|Z04 zb#c=KZNvkoldSehBptQ`dgYnR5UP_pyQS3$eV6B&m zxxI1KmT5%OjK40h@cLe6gVr3kHohK%e2@my$&4%luF1cqJ1=3HrK`Qx7MSpVf0R~e z%die~lF1NQ5jUWhjrshPJK90@Zsk?5_!p1^@7<3PWa(gP z;8yl=p|MHZku*Sn1VGV~`6to@5Y&<7K9TPRyFL%MsBnkcoTm@E$01PR6fF_3Zt|I!|J+Lzb4H2U``#%|R z)Gj$DWz>b6x1&9#sC5#-%<@Ql>1p(DkoOt(SGh#dKJMatMg+eHK@XM>HO4@!6gb5fXaj zi1BN=bkb5YT+BYoSFiXrB~Ulyy#~g9rb;XBmr5R{RDRAQM7Qf(dLop8~p1)-_Lr;${kdVQ&anrMkE$5-dIS(`O5f!AuLcdH} zE%jDU_4uetQHow)<3wu2f`14B{a%i}xp7bl#d9$D|ET)Ps5YW?ZGua13+`^gU5dMV zaV_rdTHK1eyGxN4rxYk!pircxI23n^-Qk>j*ZRI6$(o7mO!l7F-{;v$Vj<7E+tjYo z9jw1I7Lys)IRBWD>Xvf-tTNNHBr?#SXVY*JdyY9=fPLua?LFi+L>nCJy8JivC)OLh z)juF_6YH1f+U#Z=(!hC}JY#V2x8Qf9Rx*Ge^28W=RW5}+sbJmm2WkLb&kJb7f6 zNpK4H#>>jCOA-2HF96xJ>Y=NZ-hoxKZpg z4*rFUA;j!I{UI{*h(j|&lCz@GY_Il&+9O6Is+=2-)N{4{Havp8=;UNm=T9RCX~K8v zhZ54-t_?FVVSlBzsemIGh+^KS>=7#Sf9d8Kd^3}7aHmF9roR81J$R=>O<<+7JviRc zV1>=*Bc38p&{Z%$XGleebqQ*>+op;u6vpC_S^zR%PG9G~?+uSc10+W~uq)JYQFmrn z(Kb5oppxCO_jMhni$G(cR{ssq4&~JI4(VujuD-TTaD_byVV1%RLpndYK-OV=?yX=R zMhWc0C}WxQ_3w%lltEOa0h$Z*bgJw?7*iw8zn<8Xq<8uMexdJ$!IPYNP$=+;x1 zD}4n)^%{$jLxpWs-;yYa#~1%eUp1jf zoURpJ7H)R}KoIj9hPYUzrxlWB5qx8y+8}5_6XklRpGvx~=QZ2a#Wqx2kXOnR09kBV&mds%))wN0bJ+(! zT|!J&v+>wYSGyLIAOu;gmBsfu8wN_JyM2`;8KT~sV-*{;q+b{X?26sLdOUvi8G$q- ztNwl@!BzF9IQ;SUMC3=LvJ$g^LBzy*JCKIqUrI(&)m^Y8Y;u-%hQz?!PNqItceVpQ zmdp*|&ceWQ*M?PNSQxTuWnalSBOX@}Hhv2!?li#XLsy%N-8jCE}2dXCE;ys+@g8tzeZ;41wxT6Y3kv%`4; zP>Nw#&gx>tZVosDPV8}yFiD5;mf!ly>fo;YgMYXhjYGIuAp<60HQS7T{ekevPZ;n6 zB}o7|F6^}a654C~enpC;mI64C0|dOt>}7dbNypMJUK+FQBV@^F?GU&Cx^o=O(g zfcIw~a6|;)V0abi2$pfZWbrFZ(V~bsn*SceFyq#SwalxU?Z|f^K>_L3Q3{J&o~5;? zz%5q$wuzzgkwXGK4bWU25=_Gbu9y>DQr?N3glI1QK`@;m_(@|V~Fi1D5_=E#} z`7#8#K#TZ*Oo2uDj<$+sC%mhME%3hp*y%^v3>2N3W-XA&kB2+htM4^E>eJ^4zMp(* zYhVojY8!9I^w5RI3w;^GsTUj?MbcPz&^AET5#(uy?W*RkRjww6X6VC&dCF!~U;%xR z70EaQ3b-)SP^RO=6pe5l z5*`@Xv25;I*1=l+1Vb1i4`7k(Cy-zsE>r_3L)HDn`^}Q94q%|iEf=5xtIt}%#q%C( z6N2(tj-SH=cdP*)T=gwARtW3%JYskMxBrrb7&C*|5l!0Z7@8i^p0rirM^zCBEtwpR zTscKhRVpnU%qDeRIWd%+A}vl)1PYezl-SEP2lYBQfLmfOGQLE|o(teo!Ur1b&#?wh zx-GY0*A$O{-vk=4uMxVOGM9+6|51hNcvcPKZm>F6jiMD(uGe7kYbY?+cEMDs3F{bz zDpdoE(8I8?V^X5V7ZRLH2XzKq4{6K+RZM z8MRKCAl`C3@>B`TSIYQ9p!BG2$s`$SnIc01RR4gqO^S?Q;;y>cds=%85UDk5E!s}{ zKq9@7nK)HWnfkQoVA7j+LkKt58ahC_6r}+7sEMWny9v81E>%)@r#cMjNLnTc!OWDR zCh;F}Sf|)vpBS~=D(B!Yk&m39CSo#G&X<*tjb{F37ui<V1ZBXo+a-Wp@OC~%?l_JAwOy;;9I$e^27{5oYsV)`tNrgCl7STuL$O(ex&A!k3)esGhJ5nXRju={0UB zt3|0nhhrz$OrD91@3`ipH&Tw#XCgFW^{awVZ43Uf=+38#-LSwHPM4~p?Rv*)*Cu?B z^pZo@;Ob;kFH?pWFKGbkxzm4}iEco&5*R1UT58T0fK zODhm!WXf=3cRU~+pAA!EZzDeSDbq|}RlA4vB)4nU~o9vmriWUr_q^ z@guDAXri+m%;4F@F)zq|RaT2R^xiAB8?lnwgqAkPZkQd`)Is|zVeyx#LU!WqBjiJe z_Q#VJ>kn5T-jc+1FVIzcizx9Y-_<9CF$ex>Ph{uQ%wOWC#@~8C_C8hNs~S$Y&Mt1j z!M{Fb^47|aCGo?by<`8B3D_}pBPkOOrr)>=)J{l*eh8j{H)m9#a@^i*uVSX1CHxKe z{JWfJQ}L!88vr~AH&Id2^V(CeqC*%Xwlb}EuDHOR)y_$Q~4e8?%Tl^)P~k8!gxp-86D zF_D!AjDWV}{Q2g^ih>QLA?)SD0cYvNz{_|oA&^!7D-M~=tmZlkfrFAIzdk3#py^cJ5Yvmr-2($KH|27e!5m6D9l zK~Sr&iW+*Gp?2T6U1OOE3jW#VnFU_~d1`y0cC}(-)2uAoGl~tPyq!8Y7JRE)*k@g9 znk6JPw$|Q2w=DhuuivXRmK$Xh+F*?bmuFy)z8Egr_jy-aT`MVO>FG4;^YsuD4#N62;}KBa{9Dd&CY-Z084vrG+SdQEM$ zhYaTb&MCvXK%+hsYad1TJhy#+mhvDz`T_Jsd=r~~()y^kdHv{jEM^t;^nz_!b5y|;qeT!T&LEN1N4g{! zY+?(gcGdGhUK1Pp&M-;CNLP!zW@mulnzD_Zj;yduC9 zJKUz_~a7O+p2I9(7 zb)bR<`8pC>z``PzXNk2l1H3$Na@y@liD2gb4vflJoB3PkwL(|ZuH=wxMc#7gTt!vg zE1HZ^!%C;+o0wyRuEs&>W|!pt(g#CM`hmlO#W-Y$Wd2L*II>ZhB#$h@UcX}*YODsD zS^R0W7EI1R&AvrQK3l9CwK^M~zwu}bgX9l@OA70l5)DtW-R28K2*Im3KLlcK6&A-B z>Mx=s4sf2tdPgu%r?t{27nEW_!%lwgYJYOKj~Tq%;PG^Gk<;o2NEvb2(o{mL$6W9f z*;=8|Q2cP@;`3=6TA%@jjmW5#0+Do=%pe9FZ6O6<>ou(AKtBU%7iH2GrQ<;qLXH@o z*fSKlOR!`nH>#BgvlTuUgPB0XjF@K}Xum$oQ+o)Ky;}#+Fi{AxgFLY#ZMEHf%%C(~ zhV~LHobPuxB?v>jf#B6k7iXWkciQD|6DuLnh<4Dv5h8TNJ!n514Z1;mnmq#tS9qaX zBL)x19aAS${|72d3{#WwEDUJacGeBu)=~*VT63<(S%F4U_z z$cGlkoGcuCsMZdz4GuSVw$X3nN3gL|k+K~;>+s^83OAQUs*#3H{wy}{H1n-ciLe}m zww>;Afj?ZkpB7Mibge^`1C;HB`w;DMf0%)nu)@uE!HbN8(2^)^ zYKRGIJ4QNtfx7{`7mBT^{+~dH7N>NfmgK%=+U8bUqvFIF4_mJvniU^p|!dn+KXT z$FNExN&$c_#&kxm1P8r)j7ZXG^M9GpIp_xPFymTJFAIzF89qaG2C4@|fHr+y>{k{# z4aO1va{X%!XekQfQf}Lrqz&ycj#MxE!dCbnhc!E|4s~R%a-l{oVc7A&6j^|j051^% ze)x8)MLM@>ivr+P3mV7vov;5WBO29$dpIX*^2Ar}Tt?#U={-ZML^wF>A| zh*HQei(oj|!it32$#mweL|m#mD(t7Q6l5MS9Q8O4w@NcSobKF$6$A%6e`4<1AS!{i zxI`N$;p#4Lj8u%4jqVl0gSoa03o4$ zRYGm-vyT!4QF)wnVoo&g`q_S!CgLZiEWiiBcvA?SDHeSL?3DjX_ly1sk12gBi_76f)Z^m0euuVM7(gT8RKCho&NfP<}O1^ z@6~bu=(SY4sXNNgriYSFMD_iZ0~4pB<^xq7?SdT`77m!;7+UU!l6-)EaIX-$u{EAa zMJ((2G7u`u15AlF_gi2CRPlgBd$2jPxxBf}VrZQog&_zjS^%x=r%`LsM9nVQCkr^DK%ww zGngKn?L0iW@f{a6vLsVT&s14A?GfXK;NtlPz8KtIeo)p7gfk-nDI{AZI%k6zFL`* z8ud!@G!}d#@c&84N*_Y>G@tB;Pp^s}Se&9L&3JH>T`$?km5OIwr+19(5ZBwG7G}uo zIlF)3!9O%B**<&lw$xk1JeqYd+|W7?{!}&muySWfA{0@@7h(AE6P1+}XpU!#8{07( zpaykNZ2+-YvYv&v=IitT7o~!}=*|fY0(jdh6Zau?m8MNl%jh*YIS2U}G!K4&zn6>VMk?HN&gK*SnmPeIO;8 z>|+I{X&Pl%yD8^)t?d*&yp(6ddki^PsmOYBn|La_6KY6hY@+5yzg@1)uAa&W{4i}R zz>F)uy+w(pSx-+GZvMOu%oc4GJ?AtVcbAp;Rl-pFA*_t`YZeNSO<^|_KOFYMU|w1k z5d^r8O3N__5nJ0YWppC+U;(-FB`QdBK&6v3VjH;(AftGSKb}@_aHN%xplO?ZQcy~u zUc77%wqxe~uOKnJ63Gg|qAKDBj;Uw?NY88oV)H3L+a*vb%W(S-hNgJ6j2klQaVJa_ zIYooE90?5My;LA)LqjQSkmgbL`-Z4Z4%$%}whyQsxyv5_$ED!4KlF_R|mk6S^9SptY=NT`c z9fS&>5+bhk+A%Le%$x|pxXd0w^7Io^B|I7S;HrK~Ia?c7PT=rN2rb%$b&<8aM*BarkCl06lXIM@=ZleK1CayBqbdDF5I?yh78u%p#ZRsW&e&G1u+r+Q{ zpIou*M2I1s3VUED$F)%c$k}T!G?D4m7X#RIc`~rUrttN^=rB}0X4z%}kqNAU=GpiH zhUH_yKho{N(#7573gP|G$p;cARj0p{(u816REnN#crdo$|N3i*_hROEiq>rDtpD-X z7HUxXm|E?%`T02fS zE?Yv(6Kia(H@Dt3ZqaeXocR@-oi<4KU=C(ft`n_fPhu_a&;ypL4Dd}#Irng~=tC<- zD<{3M#*{~2e&)Ji89^sg*BHRR`I2q~xw5@V5R+at+_Nr#zsJ~`@opuSdQ~IoxUX6w zHX0;nsT!F3E{}}^RmRe9@QlphmTk|D!a?EIUu|Kl<`c z@~h3{RQ`1}0qQLYuPO8|_fiFq_=U=qW0QRemNYJ|Y3wU+_^U*0@qvF2#sSpTb6`zq z;3pPfF|dkka{DHT*rcq)**^aGy#)nd+EP@RXX6MT2Dc05il9dCw-?>C6Gfk>$^BQ0 zvf=bBIlB3D8NND9fFYdDYz@);*Duff}9FTWSr zB0QcORnPQ)7qO3=-D7uwjL$EI3J=#)^D>8%_6>K3^2r)dF*bmGOE|LqUtMMoIDzJm zKSy;fk+ov~GjTG4?-?&csnSlV`ZOc%hA=tvVWsMSy@z}aZ5b9V2~bPiMe|2Mw;^Gp zsz)L|79cra~1oJ2LVW&NGj3^#O8vL|SOFmO6>p zog-LU*Ho}?%(B%C=Q#%7<=fW+B34QL=}{>7$W{WXyz>xuSdSuFff>+aCeaS_C(JB6 zg9=D5S#J=(s+>8IR7kYBJ0Z@?8Qb+fANbDDe)8T4VE;JdriAnbGz=D)Nt~ws&NRhz z6N4QNd_T+R@Ew)Im-`y{r=TYy=q*&p_u`t~q$yf@WQ`7XoaWiM}-R^yp$q zb3pR^R7W%fucqU_g7h?Rzj~@i7aIE+K2M(dzDlKL={;S{&wTj27~cCTr@W;qx|kdx zcr`mw1QsC$6=c(*Ov+t(Y?09?#_Rh|JHP#FJCyGwk2oSxSK6(6zbmclfp5saFO(m6 zl43`Q$j~NQ0 z?|rz{_x>pd?vSFsh-?*dfsn3w7)N2B)E6<1K|TysltQeCkgq(SI;nC2CM%S^>!;hN z;z!5uz_2@FzUSi*ii{%R$GdPjax#t|mycHu1}J9HhGD<%tNMAJ9|Sc9tY>2gdjbc! zRRihY{PTGV(hmNJ1~+)HalA(q^AfBb%-7T=kwEpk+_aRCDqdbKepVZq2eVyJ>ZpO= z``VU)~ZJeB<^Dl+TO?u**6mP=8-=!>W9 zGiMUo#VUoC4;oKmn271w=c2~|#0NEp;Gx0?%M(A)OVc{@7g(Y4I~o zr#HRO(0eTO&1qVi;M<*@7R&s)e{ctpisOydwLa>M7`~}93b}9x zftLl!#%i{aNOw#VAM2_^Ds)lX^uK=_D89x9k3Ici!vWv@quTE$iW%=K&gds%t^3p4 z56!g{M2CCN>H6+hLTI@>Va#OA!z-{BHVlHsz|%9PKC%9r@r?%QQG^wvYsJt~97UR&W2EaI0f+OBg-= z#G6Ez5Pfwh2ta%+sKjKu2hTJrLj1A84%{RpvwAmy1tH4j69!7b`=G*0sS2K6~m7)80wDQCpieUW=0R? zgP#mPv{B&vK-;|_-EBExrQ{K$g*7Bod996$mt^j1a=k=YxMVpliK@U-!CB2DJ^pp{ zZwCOEvJ=KE?|Grx{t8uw`l~`dgtXbbxx^ZT9{eJVzMn4rnSc{1q0EA1lk_kIEqTim zGzXP5s=V*HM=i6C8BM%vk9Qswx+u9%9^WXvk0XfP=N;#hG5&1hpbM0+HL?jLCz&A~ zti(ibOZzJDi$SyB>tioVQQ}q-&Fw3`CV1hsMr+u7D&umJ{?gfj5{JlKOREz9qgR;* zqN1*S^GcNmSlH70aAg-t?nFr^V87R8N`U5ezArM?X2yghXJ4yyDJHXI;YsRiR2WtG zkd!5dSvDio{vDBvM6u<5icdR-r!S`%z#b6FNMy+UPJJ?x#;Ka^9{RI#$&505gP)V7 z&hwqn_vOXX_{xddKX1*++4kG9Az9A1-dB=)#ccJB8X3M{!NxQQsoq3FD)?maN%h{> z;}j?KXg!z0^qz@h_BUDJT5R|p@1b77b1IwTyrVqxD=j6T)yO>^@j&+B%eby06l3}` zPV5v^Ja0JkhS2HjU@uFF_eU_Q;%~%L@6iet2^JZPxuKcx61HSzj0D6qQvLGo%KHm( zr8jiOjEejFIgCz0l2ycOxP^KNf6#V?SSwp0aDQ1zGz)5aWLj1usSbyw9Fhhu|8+!| zS^~+-*_=Oikw~70!P$yXOS4&=Y1??SX)p*&9L+URq?9RY3m1JY3ux7g-%>Kb}aylb5a99n#5GTU{?J{}h4uathi zZaYM&wS^XOD3LeB%z}P*9yLg(mBRt`e&jqC;ZS?bE83Hp;N?aDbuiERMk^fdzsKm) zvLJ}#aI^iwJvl4Ki8?Y{6$|$P$*nnKM621ypH9|^YJU#n`8nw7d6|7iYk4JOd(g3k zrQ?&ZduHpL=FjMDPxP;Zld*cOMAW@~Ed8IYzuzFw1hFA##Mdy=r{7HHcn>dQ;|>?y zg!y7vU+2zT23dYfu?Y{Vogs{#fic0Qyj<;QEdDsY8ZbVli0)-?ABblH;~V}RR} zzIjS6{%}QePRg7cdcHD?$zK`dl#8aE0?V^afqg|5tNYbAkoz$k=2MQd56MW-4SSDa zWzp)47E*lucTRuZRi4LpXb1C8jy{18sF&Jssydb&%*FhWd%tx)#HBWkZ-{GqcEwlTLV zgm1dAOL5;B=5KY^(ki}}n>w6-0yxH8O$s;PbnOYALq z_G%j_HIgs5AiG7*40Jz>Sc)zY)?S`j&WpAO)Zu;)!7wbR$hb_6k13el|a`AdSsMWSr zG&b1{y}VjTef@sS95W@(%@2Yn*Lhh6+VMXh2q(>nq{>%re>`ivow$pBI=h%~(?Q%e z9H``lO|3iwkN4mI5tBSnFZ=W)J~|Em)>~VzRBN27tE6R5yybLxD2Gwt^oMC3saGIw zJb9#0E_)T+O+;Fd$&>$7?k#N+-v#N~J&#*)vPDm7O zTWuYkdsc$$Wb_YJ`e}*wQzwc!iLu=AeELoebk3&5X*Ll| zL?xM0ioR^^{84F@pt!-2d6X!BGjA@IH>$GAY)Pz}I|1gnNf-PrCdOr;QK5CdAk^23 zCE8lMq8!m%q5Y;n5v^RK)k0r(Bofx!PT%`r2bNZ#131@-Q#Q-mz{AalLAh*zWMZf0 zh4x1JsSr{A`G`NvMNLc4O!$v6$e|yn6K$F9X_%g>80wg8e$lFw@1WBRG?M_Y3alqn z&R#P!UxTZq*?;|>H4*+gzO(8S8hQ8t0)ZqGf+(74FwdFGM?O7b!KT<9%^@=5dr+me zN}kA3sK<)c$CwQyIa28j;+!&UVQNHVwmqSvee_uVR$ia-5OxqzEKBY)V3J&c`e|AX zgj(A3&7D7VD>;b>#7l2MQM^(3FurW<6p3o7R}6O;%!Ljw^zLNS9?lHnty=TGZ?V5+ z5EdV<{sDoOO*lhL-xsrKjy+Q(bgPn;3~uDzDFedq;`;vVx+t`g4Bu5mZC$2BRf!TD zA0jctn4I6%6GX+3)*o_8^9I9(doUJxyBNmu6$v;YRC=Ckb6zedKes5g#nzsVKYyJm z9OFwK=@e3{6TMW3#$q6eoyxCs>%g5i3$)&(;FjX#OhJPa=B&->PT!)Oj);5T@OJT9 zTc#rOwx-bdBgqBU&YVI78-((%i4)s9;bTxx{8^r-iW2pE0ks2cxSxJj8+KJBoVETV z&DX-@6@_Y|^Gk-0ex8mC3g`819Jv{hW`m(T~7pD@0VO1p_A6}H=-IzuMpQHN_b0KDbb-$+u&4}CI~_=Pf=?Bx*{%BY^2DvN$mfUD z@7A$JGfg^a2fq#VroW$D_wEX8G1C*#`Fj}HmpkKNaKnYNqOAH3?p_;;R?JN4b2!vp zrHY^p6Q@rXkP*+Twm0hoR$p-b78;Wdf+wGewH1m(9~_=cug0JZ^r7df5)m#HKlovd z3FG<=NimH@z0Y#al%^S>OE5=D09p5(iiI{)>72kCfHrM8e=ohX%TGx+OO!eYG zgBtT1*~l&KK!&>p|3bT%NlOMoSy?{+n*szDrSUKF1IlLsA`HUkMCuqTw8ztSdzqia z*5pL}ZW5GHr}RS2wEe8yagOBBpQh8YQsE za-SD&$+0v%eoI7m$dso$!K5Z)t~^tk*7jq@n0@(m#AI6LsIrId(D3E~B}UAtqBXFW z7ucxzPi11db)+jgB{4S_2PG`o>gP@!swN`21YY@bJ;NNQspbS#a54gR{Hk87Hdra) zt00zJgVXqAdD2qWlft9w$f={U7Q0>TywZP-+a^-X>l{>cck2^Oe6$U$DFbHrQh zEnk7OVp<1`q&9{0dt64C#_YEcqp0b{1B^L#P=JRdkPTK1jraMev$*K7rIp3xRzvqcTuttoQcY9>%cMK-6N2PTSE+4H&8XR$`vX*)0T-=;mB-0 zfoj9bEH5g@R8{q}8HnmPSZRId4O;O9JQF$tix08qmJVkk`I?$UC6+O&o-0v_h2!Pr zs5mmkjHO5<{RQLu)9~cF@*jHWx(zie1ceU_jNqv8pikcpSD%-OE`y!G6)g%|z96Q` zKq86ly{On8#F1mjbrb>h=s$%?^C0&T>famuw9CwT?cqKIUp z(p!}S`hL1uYl*P2fg*ePB%M2Lb%P8qLSuBMKGI83Z@1-~g#^YmDA4NKHp38Nn4%d| z{JiESGxgRr-;ajhi$dmi6k#ub_3$H8iWgQpO}Imo7Uk0n#^BUltyFitASTc=VERg> z^H2iN?~AtR83jJTB85g_s}bF&VVhRdHGL$2E6lkjk4W%THu;vXupraE?DRb7d(KV1ucyjeka( zsNC$J!6vBtu{?5Ab(RrxLp$@`n2-F-w0)#;joDXuxV9NM^91v6q0tmu#CN-#G z1@j&z(zXHR_I!Oke~_0A7(Wmd_8NT8cHiN3&6#>6h~H)X#5?Y+TRHu`!NA@bP_%dM zIl%rQ1y5T7Lnq9b59LT>LgChX7eP+Ha6Yh8)}c5On&6S<)Sg!CXfdcHKG|DH>mgRS z5I$S6>oB|NW=(PMW_sol+ts7-X%d)-e4wT`Uexqe%Fpi6LCch zbh!ooKtI!FN?ZZn@Q;e(F*)gngxWYSk4iv>6 zZ}~)X+Ny&Gl;YwYE7B4OT5GvDOe+<> z0#kt0)f<|((=xH5f1iLSccE(0;k3M6;4ey`nF$G^8)!h;uK}^sLV>eEg4zC$0Wh6f z5GJKSSK3o``gQ!~ugtj$2AgJQ@NRG693yVR$Ydk1sbT`KA=t1d+WLJ{4d&<$qGR_8 zh!!M&)2{o_X%RcT=|3X|Ir=8nyETR~(8#0vnygajjFpR=uL)R*6*rc#6S`+lhGj_y6Vo+x52jHwp z;vYvRAUA6rq4GOy#AE@OdEzcT7%lw#Xb{*Nii6k;Ss6yqP%`!fErHxp-=pL!m!~6< z7?IBoBQsd1i#mA=$lzmRJF!RZ*n757vW!z1McT^f>>7VA6s z@_YZZuZ_BC>CddqiQu}Y91X^*o_FBI%=FcEBKoYi>$FS~ofud4C)UMS1PIsn#>>Vy zm<%dY5Ow@DVn~O!#~w96?!Nw&`8da=kR~9j zdp@YRG~J;*OAHpAS5Ae7C{&&nQYcC6P?tUWUzlb%H3$B?+fXE@Q(-!)4t=nw(&_lfx$gPUs5L7eyqSG{3c+DlJ;v!|Ab_wYnzEGJK@>s z{3As^hpO1Y#a5EZt~JE-$%)>wGVMdJwMv{xSsEL+?Xdb~vB9ImO_0Qwta!IEiTb-; zT5-bj2@t|wt5~u3nszH;0z;-axHv}QA?)7Rd^RI$!3|t(h@3Sdt9T+I5yq0i7=DvJ z=|{B_`v8Nm|JH>X)<@9l;A4V_o8KFaVILwKIGxTr)^b+P3$-x5D6;nyMj=9# z!?Mf_nS|~zeMatg2(^<21q1Fht#swPDQhYtpD$^91U2;`%rYnkuTOFw=e? zoyXSnzS$^%n&uBJKTdy8@SNJE5>xmWYL37~Hx<3u)~0QIq;12ztHtT=nzJRg>z6>#v6? z44?S~i)DK(C9L}c88vU0OkkwCLm)^d($NIfboWTR< z!_>FO&S`=yNh-C6AZmMn5x!Xb6aEHGJk73MF46qTXDfS?-s_y=h=I&|O z<>3Zq#FzzHoP~eO#TD?rkOF8Eb7#+_{GT6W5;qJ%>=N`JX$zT78FFdfd_h@MFyNdH zE$DU~GXEypRftx@dyd`gfb+gh5KD4d+Fqfa6JtSfs~wa^U0-fEz#cT6CXiZt$Nc5w z*NnJHXQ(mihbOCYloVeMqF(h?(^zgMCpnnWONQxMfz6MtX{{rWeKdQ4esZ|kMS+_1 zE*yvpls~KgBq0sL99Y?lmBJc4K8ADZ1fyvbe)wfm_l5FH`1>-i8i!43d)u|I8((AB z2xKjT4*4VoK|w#$(`kyE;|(onb`muFy>=1Bq}7p;Qn|m!a&Acf`+&<4sA&_Fk2^7m zUWaG004X(;&0M~tO~j$xR?JCXgN2i{u#PQIQ#4B7tb->#|C_a7$GY?&LjEJtnt#A78Cvx%UaY2@Vf+Xj?2UF^QN>BKG-2g)Z zfdkJ&oNs-RQWDWXcF$lsHvCu6X6bT>;@RWUBis2A%!!NFoA#&Nx($5g3Hy0+y7%UO z4<6{7$S7l455P{Vl_9~Yf0)(iB1l@l+f6lbt8EJ;_AL>M{!C%+qGh8N<2w`@VFz_`=Q&B9xFl|MSP=^qP*) z|NdAo?;2|?(Nz0=DuRqa&(=M$z%V<2H_Qan_66!C_gtL?Ly7vLX#EBbkhIqU3aew}0j`ux#au z{ctQpxz<54xPB*)M!(rIuOQ(q4V}s)BQ6!0O+`dexi88INWVYk+p(v4jq-R_&E*Q) zou20t_xOmE{@ScDI(byz>W;yunE846Rf`zq`6^*-@pnJUR-+urMqBAwe$|}F7PcI; zDyLuPNJVt1aBua|NwMvV)JUZE!~K>iHO09NCN~=auiT&ZcN8_^j+@I_s$TSMh>XkP z;#%aUAT~D)uZ_c9Wu~gB=*K=HD@mKHB7B=~n?;O%7W0xTCT8W1K9^2(sazG`FIMPf z(p=I7Y1RveJ?0zaB+Lm}Y(sY+UySyvLS{_2=|nkZFE32GkFx9dTnw#DKC@k^U`%dV zRUUmWZ~8M+Pe}J>EPNwgJPD*o$+@DNGsxRGv+Y~lv|-Lddh(tH^J-sTZdNE1tvFUv zw9&)R{zGkT0Ny4$0$n{EGH&5zp<)IwbJq1}iGn2}t>+WF^gfM8yB8;8ex<266XIwTZ0xz>W$rWL z9X33glHCDY7gNT24DLfn>eURyOeLPN`?%ezPYJs&6i2x@iRtRHypSLJU_F!u+JQ={ zIe8Ro#S#XgZuFQa7kbXB53z)p)>Qn0CnIL6co(}?BxdA~ekbh0W@PFHOd+89J*>s& z4BJzKNgk);)qh0LO~<|R*pvjQU!gh8mTaN^tn4>!)o+++#d0At<5pr{FB5d!?3b-? zemp(g@_Xe@!uyq1ub=o@Ej%RXlG~->tm7opqHV1*-~LHASL|>(m+zxoH8-#x^P&r4 z=O+`@L>hxuT%V2RASsbM9ndBI!b#@Qigi>>w}}m2^eDRjo6a~jS|us+G-g^QZRa;D z5%pZyH#%#=#mTngk$dH4SomGZ@XKh#?Y=TUL$00@Cz5{*BVSO#(l0-qQKmD$^47e5 zRfosV|2)id$d&O&m!8HRAM@#h^7$!}^EJe9ajjU>WT^D1ZD}<#mtiSbQ(1AClG9Sk z*j|rn=rpD7mEA~LkG|l%p`rV@j4c>l@}boi@0hTb`lUA!$=4+zKm+vrDSKvvM{HTGGJkv%jAs+W!kPf3vZp2`A zz24|MvF_K{k$en}Y3@zV1>A$VX=6;=*R`Ud#rz@cOU?HMb{m=@KgmQ_y=QLv$FX!A zKfF=eN|lLUzEGgi5riPsr5x!?hO_UaEyMcRY!5aUcPB40?Be|}vdZQg!Jh}RUr9bV zB&lU{TlA|C-yprzbodjWRA&kC@MkBZ z75cIp+XRAq)Vy)6zc=+KYD;14E-tT))E0BOn1+-U&;l`oxhH>fIH}3`a^$6gOlMrh zXlpc&WM-l^Ts6|iE&9l~!o7;7sIMET>(N@?|9ExY`kcDIuz$3@|71+aiBMdb)_WSd zr?+lNoXo!Be7^rwF9LyMs{dSAFlLz(nkwf9I+E@fB)1a(USe;pf|ydFl6`;6Jf^J<1;D3h+AwEck%yMXTk^~joy4Xrp=rSt zb8DUw3c-mj=Kg&PCp65xxXNu9XC5VjbcI%G9ZG%i(5X8Xw_BA8Vg4FAoieSHLSz^! z648sAnTkIw!W~nH;VE7Jm{;7k-{L{3q?Kmo(%uvKw>ne0*ufwrPy~3+{#x%D=EkBcK9=4sDfe2@VXe=YP zr81FAo_14QKZ>XOZ{3qj@(~SO9=JxBAq{dtLY+1I`PQLb#@=0Kf$6Jz=R03_0kX>amkVU@s&9R%! za=P}Hj0M83cL+o>AVRpXd)lt-KX~_)B#W&X38LyM4<0E2b(Kqwv=DT)8y{dqg(_)U#h$^jC-J4R!Yrxg z{0()mbzi~BA$TK=G0^e**LJsX;6}3r8vBvN$P?_+n;xN!xTG4m#uS!QxW?**BP7ccQdcs-^WkFtACj&zstu*vCIokPDDLhQDN-n2+#P~6xJ!Wmh2ri~ zf);mocc;anNO6Zk@xI*e{miUnWoFi#nIn7eBP|us3r+AKZ6#H)x7)YKCTSL_LeN9~ ztwmcIq3ModtkK}!OSJR&cB? zScS3jFXC5Kmm%8`XhrvcJ-JrfWY};~DtisMSBLDL+xRxOu_tpvhqYJdUq<<}Q5a#( z0YC<2<^5fsaxqf7yk!|o?;oX07|ZbEXn6v=-9SWaF0kCQJ%`(zrKz%`&7un4{0)OG zk6GwXA|pUAREfexkgxvsh8$X^p8w*7zYsA3@Cwy5omDc!!vwApRs5C*uJS-u%w~0T zjnAlHCO*DRgTCU0w}8Puj)^aVY5_pgA!0mduM@%wn9eWcP%I*fXCXlcT3cxC(w}$X zcGSH>D`-amV@lde*UF=560Qu1v#8#mzrKBIU;E#;6(lVj-yWbr7FOO4wNH|NT>(`L zT!jMhf7&vAFKHN-&P9TwF$FTVcFe6*Kdd!~x~|v5hb=m5o6UkI`D_602_^|4-Yg4T zZAXXbeV#8(i+jWQiijkg1W4aQNU##q{ftgBKIQUhb@Oetzv>@5it`U6k=Sx9n)G@8 ztNY=PRQ1>vqWkjN?&dU} zpfiRAnWFA0H^cjN@N<&beUcww`S+gZMmpnbDVDj>Nw*YXHa_AH5n(HO?%QU+h8a}U zj;)%sWv1MZW1SgByUSaDJ?VuzJfn1G|JI9>I+QMN&1i+XX!my2ty6-Wu=PLFwq7yl z1Vbs_==_ih$p#@8D!R~rfNde{`0kt|ZJ~bgB5zHs=RrjL5<8#^YHji(FrjKivniKH zbZSN<#_5~tmeWHed{2|p2sQFYV&mJg5eC;0uAlCxq-QTZ5yV;as+uElp7h3|B`N{d z9ilzzw0L$tXKGqkdBDIawSR0ypIjf~;)#eVudnaJ1<#C=Crc=myADYwSP9>P-Rdcf zpU2I>y;BBu{@e0?N$|WzY=yeFvvm=h%O&x8opQHJ1L+={K2qcyBCd!9nV$WtWf>-J zSCWWw?-Y}Fk4uB}zN=g-@s|8z@0bVul^91!UE%?Ei9;_|e32Tm6EUGDpVTNd!ZN~p z=>#Lq-l@Q%D?X1Y!~HlCRFaQ&Hc)n7_L0QoL2?@o!;owO43rfaOQl= z_9C5pIsnPKO3!0I);|;i&Q^o0rZj+XMf_Xz)+P)KQ$$?$MoV?p)}ee{GSb#m3Ja}Z zip&mVRH!<^$Dl2ySLiT?%dSnxW_k>2#Gn#=D{tQTlv8eqCQ4bzj6r&T(%ff&Q_3 z%EmB+=v0(ddD&C{-Ww(Gd}BRcG;IkipY>9@bptq4^W22g7^^!W+qiT2Dz(NE9Gi24 z=@VF-snmNjuD*isO;27Q$H&xuu+eF2ozo}K5yD%Ua4Cth_#W30IK!?p1vfP{VW$kf z#1Dp#ofDO^RUx_oHRjclj2pKnS6spiSyls+$oV?67s6Aq0EUk{*W)D$A!If?6uKjA_zzGzaD-=TKqA5Ma->QFA9+^qfb!|^0 z_T60(_FW&pMz&Q=J2nK}Mf98uafABzhQ=`t*aldTbTl(axct%`P5kPIw1lo5xIX^*NiLCo1<4USKqsLmx1fz(ZW=;_d+5h}GPM&ii%r~m8$vgDR2Tje(r8n)z zsBDHzb-^4{;tl-!<~FRdyjIbvBp0ZMlWcaN>}JTPd~BQ^Wseyot(|+QK7ouAVq3m@ z4eYS6Ta0Gr%P5r%IDS%|UwVy{F(=g-T`^NxYM_MVAGNrLklGTaQB)sG8%iT5qMhKD zFDC};M1)I3hq*JrgN|_fyRa2J+Q*wGxbZQmUxo{;Io0 zZ)7%Z_;dGE1rBy1K2tNV3Z83Hz!^z(U2q9M6}hm@ee=Pjz1z(AyN63B1Pd*Jd!fX? zgiChTVBq<;dx+DhvU|Gk*L|D6*i-0A7qgXAc8S>~?h%d?fE1^aR~HHUX*-R;JvQs% zVVnyl&Czm1$lG6)XFlI_nIe#P>qOGlZY}OjY@0WknyS)S29gs8` zo)hYQrYLiiI-GU4)GIe6n*7F)Y#6Te2`hQXbtMnxs@KWRszR8V$`}3FM@j(|t&H1l z9b#3(rPe4noDMtT=j|#{aw(1}bLb?E4H6MYS_(bU!Dg2S;Z5hAjmI z6xZz?b`4BE;~(6AROBQRGe*C zq<)P#8oCDtmdr!co~1_$Ik(|z3GUdI3%#8GlIR)wtx( z(?22XF#fX1b{|W6q;!qZi18d?AH686@}x$|Cpk;%I)DI6!#*Vt>w}Ulo@(@`NJot( z#wCTm;LSfF5%`pky0p8U2~SJN_+~Ep@*+}wqB#8^BVIRJ67cLprjF2qPlR)z$dT4B zh}7C^c}V=7xZt5MBF1N%A`aJYwWc<^!Kza!koTv!AwdoTbxlMk(%)BZLGI*{SnH@& z(dM++rMA5Tla4{3jS#+>83f<7)+bugFAg&VY!Vei8>)5s&=o@^E*VNb8};58>T0HC z#AzziAs>7r4beEY0D`j>HmgpJzB_QjU#Z-=vG*xWH`)y7(b&S%`+)nh6XVW3&7D*CH)@ zgH&Nxu1eOCy(Lz1(SgYAjsJ7!oY5NtqyTp3Jl{g$aUs&qZXr@PxB}*6GS?zL7BY5} zuJoEffQlh4KkLM3GxJ)fc-<`wD{xJ~y?Z!nEeR59<=#hZO~EDvf5(Ju)>{33Ro+xE zHpAUi{LET;Z#_lN3!r{W*4ENsKhvQQFWga#k*|G&Ra6svdy1G=Hz`neP~%hCH#q36?4cU&;C$C zUt7`;-Xu7(S7(5I>Q3vuvXhw`4@>99J$f{(=>j@TGSjP#WrQ>?Z;DjRJ~pZ zILmBmvR7w}wQ%T9OOQGs8MqM~QJQ89zEiXLAN*ridD!oO8j>9OS;7i46X>5{fA> z#XVnEx^^!;sMnERvodf7a$3@q%`oHmF+Mz$8y%u?-P679qegjPw$j~=Q_r@VPVAc1 z>TPvQspVK1>r;X-6fLDW(=PEw06Bx4KG9+S^o>WKZ0DN<7dW#p&?o$i`x8r3y&KkBeeKL5F!8Eo_QK7hMr zBca8=@6k+=Dqvwnb)#~QQUlnZ>y9V?bK(ZA5Q&G=)({r&Qzi)()9{R}9bWx_TRLImOA$~Rrfqe@a;1*3#w>6h@Ua_Y(nBR=lY zoQ&Y?nRAl!ck;P{m4f~Ov@%`~r_;&PB}JZ5o|&BL&7XY_tHe92v??dAzuX=<=+vUG z7JO2!{A95_9clrAQa{ad=P zR~0MY`_6W7#Stu%CzS;^4$KuxioK#dV$Yvpf_h(!51~w zA^0%u+qc#rCS@rctKH$4wAR4=gm-u2eTqDm$eVdbl{RE#-?!l9t%KW`Dwh&^=4Kt* zo_$6JB#K)HhbB?8<)#uyYNzXvm?2YxvY`O%LNp(*tX&^-i)(C8a8Z0med#i|Vm;DO zf+cLS;BUnA(fO81~~Asbe;;t>hAST|Xad(L@NlPtRkf&om>G zk($GIek=9ZfZIV|X0;mo1vMa~0;OTY$0e|FwAo#1>zn1nrvQ@C2oVj1BM$1?e%+(K zS@A@D1eAYt*z8%U)M$0K2=3&Z?czMfRVWHc5B{F0*j-(CqdcJ8%aD-5s zXwaAmNVw%?^r+cC4!KA4h>(qD@pS}96!$9&02u9!XYK%O5%2qx&iOEKmN7Qmkxo&o z@M)FV`Mu%r@AKr7hYFNa%>|{*Lfa+H9Hwt@Tq1ex1XQUVU7`b13?ENfe^+uKJK~=q z5ju_@88h9JuIX~VFrxsSj<@(y>tQD4%Ps1%uphJzwQ-=KZt!E<1=M6RT-)Ezi|iA5 zeu;S-NbQx4yLPD@f~0yrCD)-7$q8BV1UDhN9JZL9tzBB5vFxqzWJK!E)&4d1mfA&b z-O?=7E)oN+PB()-vdnSGTIJ{5gei_3L*>T#>Mggd*&`pPGpUgA*)i0-mxyO0a;Y6e z(m*-))j&sEVcFAF->z4M9L4RyI19`J1R;69-~r-(nHb-#W(!qr)3Q0}L8{GLcqFH> zUCJhh-13p*EQj4^2^!Adok~`1GJDw_9{{L($L>}AnzUS=$L^C3J-9VxAB`kCPXL_; zAPQ73T^<|3b1pdqyu3RZ_5+xbD9M;=l|qvaG?9+Dja1D1LOn8NjbX>)?C?mj9XL}# zj)Giud3KA&TDj#*q*Z$hZQga{2!uOE*9JIt>ueusdZTYhsO{IN;H`wT*T`mDrw`G8 z*#tVaPnBaY3$}VB8Ug;2Y`+T_VyCpi@k#so-SGR5OaKWnZ3Dbh($}C4b@@LrD)Ll- z4qEeVG>t2w6#FsiSrIL|FT?^nEV$qALfCbjX`Ar``q^9%Ng7`^Uwx|JvnqG_0biLu zzs#X;xvX=&13Z2OI{{Vy?uKW`UUoL$5)Fm^5ivfD#(k90EV?=%qm|qg6mnAPK@tD( zHsT!q?I082nfdHJ4F^1KK6o>9@15z64?*0zFPKY_64!~`fo%@c|S@azwNbY@oeD?uwz9ZA_X~7d+M6Kufq|Kd}u}#uW@w5 zI&m_3l=-2yak%RHL$Is^p|r8U2ZfRx(w^W44evZh#}>&jk5Jj;^{&+pb9snr#vx1D z@-5c>(0fykPov}TTz+kzhbLZB$p#ORz+-3dMc!`vn51w=Bgkm*I-(ft2%6|%zQ4Bf zuy*vJ5osBeN0)v!I#Lp`mKH0Jl7La+?iCr@GmU#AH`0^upxU+XR2~(q05(i*j3(&yvr&xu_3McN0N z&6wU^Cl_t?`%c2r1C5;I-t}|00kaOmdNfjO1tMM!DtQYQ{+Y^b>kr)B?rfq~jS&7V zUme<^VGMG-PTT|3RlL^)2^iiihW97w-Rbu^u1II*r}f~-AW3x^`ud-i9$%RK%DeS& zArxu1YSe--JONaCG3i|{W*Kr+auOywH~6x}2mAln3>KXBxu&Ehl=rhn5FM1uil-vY zoYVq&5 z5Hj2qzR22I^{djxf@B5sZ7JZ052BCQBg~G^)RrXv}WMBrWn zJP5B+=0%;MhueGvCk?T9cG70(T%h5{ZEEuwXO@3+p}zBY-_$QF$1z*syY^Cuax++G z5uWmAXR9&ZfL9fZqE}LoCtwpfbbnwC<)0T~Z=&zQ!>2Tq9^p9hQ|xnfvX#p_B35DS zuJ%6CSKiL5BYpd~9eHn|`V>f-Y{1v$Q}Y;nT~;h}C$gc9yKq|b-h3!qC{D$oAN(lO zDPauVv9fV+rescA)v(k$r}#?krkT)fZPuPPia>1X>G2rPF)=Y5qh=hpb@H|axYTZo z@YbMS^IWU-PH}C6s&nB?KBlyO{~1^S5roFaR7XK3N87YUuuuqK3fqBj+1WO(Qq8vJ zQ5re)-S}136#u8NGz&6+yBKo^uX?Q(BMNYdd_eUXc$7{FUQtllaNzj^Jy?2*w|wrH zYzq;p=AeIrsnyjUt6dU%v0_LA*{@1@r=mItZF<`g*IkF;;$%LvQ$p(u2)eP?-f``LY9=R3n|lu?qq(Y7I8uf(Xn3dSlK$jRwWu&3Ob+GsvF|f%_%ADU%qAdLOeU&G88lw*CEwQ_KNG+8_D{X3sC{e zUD3u%hLHf;Ql-F>K5Dnc>H@#c(%{h-=y4OhPeWMoneS0>PR5 zE{tF0^$AK4k5aWOg?5o(C1ruRZAQz|sJwwp=r>XBOf*P#{oCle6ol?MkRe0E&Kx3x zL>R>`gv9crvs>Lovup$^qZ2O)Glzv-RKV29Ae5H8s7fOKjv%EJN#WeR2>>3U_}4)k zAmh6FIWR0P9;U1wu5|8nqFxs&wes(fure4CsfXadf47CLgBNSJQ3KfO<}LJD6Ie&* zPqmfR522TGw7e1Q7{N&XSaUgFYloZd4A_B{bx2cI( z&Sr^jyR#J`ny;jrZAZWHsMSe3R-1$S*`!cKrKOci>M#y%csiFs=peNY5p_Gu^*JQ; zcQNt)QhoLKRLJ0xo8=V5FxsD{3B@0^08pq*bc5HXbzdkuPI14vwz}Qm zQw~-LTHZrl!c5V&%+s&jh+Dy1p?3(zmf=n9n@MAoPHv zsn-kmTb(tMYHKY!gse$DBe`0Z2Akc9VUo_5+B{ukEqfGV{F_n2#_xFCm2kbG(O)_39=P>p!IGJSwx@=qd1g6f&U-&TKh4Iup)ev%yI%i4;oP z)p$-DG9e~``b8a{Ic{*hmU`_3DoDk&tX~4wn^T*--f0TiQTY_j{X=*815B7`i=ZE6OtR$B9<9yZzMKflvmA>u@P=t&7 zVLx1=8h9VzXJmoN*xT`O5x%`2HD#EGVD?S33@v z+QTBkiAJRRK{|F^|JEI;i2@+VmdvhiR*F<`05yCqeNDL^FM1_Swm0S^$|;xHag^Gt zF@C8uG$E{^%{tBVb2* zA&xx7gB5qjy6uC{+tVYypdvNOPy7Rn5GjP!we6KMbV7qwL-X%&D$F zbCCTUEFe;Mu9>4lRlUB`e+7}dLxN}bQ|t!$3hQ~ni!%{ph`=P|HjPeiQ@sN zwW|m$MH_K-lW9@a4MMTa?5#Ebr8@4C)9~1QQ5*paIf16 zjD<>c_n_DeJma_j*Kj()U{%rSls3xwlz67w?K{=cFowois3Vomkl=)o$R^@Eaz9V@ zWN{K+#nychq<9uoSzw=AnZ$yGixN@02r%YSVN_r1NJ0K`FH$j+gwH^{3MgTiVDELA z+mKZ=5-?vEqLGT@5$L8iFKhA*8%Mgs{^`wtNd6%tXgoRIVAFixKp zf}l!K4UlO3sl{ZLth@zE{y2Pc(=g+DLY4MapI+VXt6Yj7kN~<&d!IV^7pgS(!&eHc z&5c5x_o^2^dgB4k!NgPyK1i)U1>O$cVS$BqkNs{k_^vHaK@pDb=0?cLF0zeSrNXl2 zZ`5Tw0x?TRUp25b%oz3-J<2Si?*K5Re0Y%!Mf<2SdQlMe?P-mxwBlnLW8=~(aQTB> zEkY|Iwf-s^n=VD^fkC&(Ar0>jug~3#KGAi5`d6pZG`QrQ;7%z4I1(17n~!bhk*RU# ztYw*|33D7V{H5|~TJHrft4OtGuWW6A8mJSsL}&CChD;DTeE!=g6>u95vp^^>N5Gnb z%9G9}C>DAh3vjTXV9czj8aM%U#s}#ZykOr*&23JUO2}uu44)}yc^dkuwPyACH04Ww zAbbnc^KFEd4-G^GOXx|h{rZVuXQAMUvxOZ60*OcuoDbuRl?6yyi7$~54hB%!+*tp$ zu_Z$$k0o$~nPrYyv{z`>VGeMil7Cfsj}S7fNo3Q+hMdfhTt6uG;f7+I4LN$NuO(*L zT?(TiMFeJ6h_E~3RP%{FaZ{P=RV8v>x1uyx>8xy z?3WahOVk=dGc1DUap7~4+G7vyq`QZQj6qt`t!v`n769S(_Ea}M4xC`16lw5d zDh4j0?-htG&$>SV)6OEI4u|H|NrV8E{QH-6)eaj3c1AoKi6qcg#vQi_CyCRiZ zCdlj$lb|$Oym0t=eHE;~Ynwq+VPOGjcuLJ9Uhu7@svV@k zN_m3F@G4MwwVBC3Q*E@{OMCbSkD>^sMZX99bGg{|?k4eo1xTdU_tl&FS>F`3{8TRb za|M+Ay%zTuMGPssV*+|KzXbUoX*eG8V;U$P@@T`|A=GgWFZ>Tp2Deoqq(><*4mL09 z#^bfq;Z>d4Bjq4CMy5W95pD3xxA2v#xE|6k`b}SPC~VuiC+RUi141guSU<)vra{#i zG4dLMnAC>B>P-3QI)I}4?R$oj!>~TSTj2X|M%`0AmdBijl^J_sYC%3b7wV#8hhn56 zT@B*O<~+Bq~(}QkIGk{YXmz?(o;5@)2lxSHY+?*g>B%CL2&TLlNxQy09~>iPXa+-DzA(tnpnjGe+me zH5J3t5iB7&1ot6?FcM{Ul$I1F7JAE6yzJ@ioT$I5OK_ThHFrLq~9{J0&p!ggQs z*V2oP=BB;Ac=0EhVrAJPo0f1v?3J6Qs%KA^YK8bp?c&Z^vYu3>71^-1tQA0B zZ8PC>J4a)PfA1`}HCF$}9i`s`2A2~pXA3r1GE~sOZRWUhl??_4;gVA0QpBbu@izpl1hvx@c_i}bdp1pR+@pOVXEj0ILi{{7ldbDROcnrL zl<8Zrrt-kKAnemWzx?9!La^`PnbAVsZwdK7x&dFP=bp?ne|QQzc7pPw8m{Eaf_wi1V*`{g--}K1K&TqrD3CSA(=)5 zY!AkCm=-k!0C;Ck_21@_@FcWgEr~J0t6s!->q5{{8>U2~X^cI7DhZW8wo#(*u77LA zl$~8R0wO-`Po&%9G;rC7ayf$hN(-uivMVXqP4Gk7-y$zDNl`u1c+Cwy&s!m;bLSKJ ziTBJS5LV5t9QH>mJ2uB`z;YnnKrk=K_cup-vo_`ooOBo(_Cd-)l;Ui{*eBS>$2T$a z;|NcJqRgIhf?|})sLf<#9aq}2A~@4%G4+YZJ0mBS^izKB zZB!Vm6~15_*`STTMvy0c9Uo21lcv1#;P=A7KHY2XG)nme*6QA zw(mgBe{bGC+PWohYzG2ZyO&YS6Ar5*_QKTX>M$`PeuVAuLCLU%wPH`oYROJOJU2$C zRufQm-B*Fmx+M@@P6iqN@}RUoeFWU05Z(G^GcJUxI_Gew=aj31{ADTwUO+1mztw5{ zoFsF3&){icXax^fPDz7GBK1WBP4FYO55UcunKC?UW}G%eZMjt>Jj-3A6ff~h0aAls z-Qm#mLbrj-4ZaMAXro{;Y;zZMT#4z7f-taBw92N|v2ch1W5_sn0m;>+@jT>D^FOkCy^&8uM5 z$-J9!Er@E*^Yo=%@0vPWg%|hVU>-nSy7}XmoOdp5g}u$a1A8QsFKQpI4CGtMfXjmX zQczSnh$K{A6ld$~#8~l5Xr@s3S0cxkTcwYpsc}h z_O1#DqKa#|XxV;e7Z)X3I23<@Bt*kLY+1eVjzBDyT}Z0PsDa#&fteF#Dd&?$5^;J` zfDeCqJbQ^g-`!@)G2VTOd=Lf(G8dAK#+#g-gBQ(+o9L8$Rl5Qrp_+!u)7%aDv?2=#V2NHSeNuaN zhHMvT(ac69PVNKVFYh{Jz$N_ko6*(N4)t*wQTgV-Qw4sX=^=e|dz&2J zHZOjEe(agM&BgrfM_ty`?jb`&R7mk%rb_u1J(S(p{|-vtt;4kJ%#BNxcM0evD(*qS zb@ZzPzkQcntQ8qIk50+uU6!DWq}-WM&sKzCwjDT=%h-6Y@ft6j+6Tv<+!!bGe zvt^i;7**=jv>*oXo{&i%sW&qcN(@Mwup0wd5`AT^fcgy7^7j%Ab)n#DWbjY`5@Q@2 zpCWEQ#{=E6j1L08)qcA$w#iE}jWJfBNM>eMFh)>3&~@=0Bq#Yj^^N_0#?#9Ft}O&| zdjjYe_ir%j-rQ)Db(Ro=bOY0&(G`lEm6GAz1yQo}qVXu`b139ieDG9vq-M@6H zP8Y?>o1u6^yMDpyCl7{odpkUtP<-PY+-T!o1Y8F(csL`s-3H_;HtUXc(zX=+viUpw zXrgLq7jtWI*o|J)>8+$dB^t7}0bY0&@Gqvqff*q)d1*Zf3;Hy+fLuOmA#x=h;hTW` zobJ8zEP&-0!E>k`xn)7RWkQ>W9I4E?2jE5dmrix9OW4a0JL@*fMCbgvkQ< z+-6@}?mRQ4I21+%@Fu)(Av7M4=@X5X*+Vdvrd(X?CHjWB1{eZ!qxWIDpn0@Cv?$jg zhSWMchA&FMh)aznUy zrKCUB`RWG=V>3W0AMd_@!Fgx(I8}7)1~hwmb~<4f#?rNm1zY@{t^hbWEW4Il{jyC| z{KfSbaCehj%_siu2FruIV`0j>D`-FZ-}!4McYnj6{ITS_o{IBv)jVY`L;XdquSpD0 ze`HINkc}wSBWf3wVYPzm$u^_(ltyvB?TVLqL?{u|wCDup>r7-vv;*|#6clMTw>R{hobj+=?O^_qDa z)@3}O(oftr;nj?3!1a$$^Wa4>JuWxzVZYK!Dw>_p_03n+ZH|Yqa7ziYQVwsXsD($< zGm^Ig&()3-rTj59#Bw}Hug4W4-dlVunzHCNV!VMMbLY&&vLZFq z-=PymkHca&CL(Q0DHC_QX8owyrb$lY1;)Pm;RYye$5WP4hZ6#DujZ#o&TD%!;q3YL zIhl}%g%a9-{9mWx@-JkDHNVnG*n)S*0fM%vFVawlFt(+4p!sgyqh=6#wxIavREsC0 z=O17yu69Kyjm|2oR=nCnlS$ET=8F8LV zJ5mxsk<2hCb~j>5y#q;;i4TZ=a{JV*%XFIVw$5!3_pv=--t;eYShd5q-%x%EfoI=5 zd;*+ePiC+Dd%zt~+4XBTQ^8=T8PQ_x-3?WZJR9fIM2N#;xIMl8Bu0RvbDr6KA~>3L zjoE}HGw9a21uD;w!H8htpPzVt|4jE|g(`%&+9h%^1Aoo)OS3^bg&J;9yLo2J$;7&z zl7mX%1V=$)dEkVXrSY0umI7u&WPcoPyeAgX3v{~~7O)3#k4ecPg>(>4A3qm;qV#}|J(xw z)cqJ6*PmG*O!!I4Q+#N_(*|QrQN@DDS*4-y5|*zum9HntR~?)z9~P z%I27|P zS!{c7xH8>h%nfh&=ORIoiiOP3Ki40GHggg(z7by%v^AAiF>i{vFh&%PZlF^W?Y&JU zD$&ICRxC%q0W03#bSRDCe^7U);nMs`Ct+d-F4A5=HGuMItag z|GvQRU|P6{3c;f3YZ@I0Jjf!Qy4bLd#oYpsy;7rRUD|D{w=+;N9Tb ztJCqMx2xffiz^_(;IZjjWC~;8^0`0Y7^6c{FximZCt#n#`7aR9f<^e$&;K-MS&`G5 z(K+v-d_`saV*BTgkV!{nv#sNf=KL?!4p(OOs%W^+1PhdK$|9d|Y?B{n-XNRZsqep_ zjG>U}iER6~RP(WE>FjaRyQ;izp9xq_kfHJry^V;ak##K8n< z`+kdVx9j#3$ax;o7;~s&WAJPqHmPHNWe#WvS!UlQtd`mZx5e>*Q!eVw2xf{(P|rri zUUr_~YJ$j1zS>Y#+{LC`AI*U7URu^pX)V)LW!2J6oRZ=UzyK>Fh+&d^)* zccQ82eIZ2iflUL~qJUSA2VD&6Zxmww(B$zUspZ<6aveD>zDZxkDR=mM;aRX>Oy_V? zf(V+X3Rk;=WO7R`8vhB?CQ5?#yva>W%48g%=yamOql*+%G0)gD(Fp2PV8Ex;pI0#Z zEVau~T|o>&x6~xq{PqMO6EN5bBWxn{CmiwA#${8NBl;vW#Nbxhe$6ALtEW?)1SFES z!xzm`uUkJ1b5bhy_zc8}S;Ptsevi4FH2R%gDjh{ga97qOv(;8y=a zO9hOB+sXdOs3;=go2%LW zfaZ4!P59~)-J}rC5@Y?(3j4&`xCan_Zv6zfqDssrw5lrY_NPkr?MxZa(OAk%@}^19(hHYoY`d^OVH5Sb zsO@senOBW%0j05ZViS&WI)q;Z;D3P=TlMIEmQ}>wgOzh;hV!WWgdhHcY@7As)o#-I z?yS92FtAJ#Fx6o*Y)QIz(cBI~Tc;&nL9PYdlZ~iJ&1t+%3vWHcwsT#O_&IN>rq4b3 zeN;AoAZ^K+_Tzh-tQ_xq`b~&DWj;^VcPYx6M02S<^4W0QOGcuv)hVrO2$A`7^?c3;vo=AEw#I|j-KB@6J3JytG ztg4X3is=(UKxDevEtl%j1)WcSCcwMr-+ePCjb9Fj9zw)UpM(S8nn&gdFgpLd=p(pu zaqXvU+%p%n%>EqppfXPX`@Urj5VH-A<3U0j+xEywVQkKzHFiX&@fOgeHh(XM)~j>z z%IQecnu&~B=Q8(Rrg#dO<*A9mv;gfa?;-0R?lu^i7sUGDAjD9|YDbzQl2a%s)=Cr5 ze7&JK$i!`R-yRR70?~1O33@_6J`<|q_z}AC>;zg4wfP;{AeWq=VZiMFO;;$MgPuK{ zn@{u-$=ev@kV=8UIpQf0Cdv`cxc{oNWmA04Js7Z;37?!}f4U$2P|k39b^SqC^g~4G zmCR|r?^hF{^+sPuJt`=34!F?)F-;_~-h{7%zUpZU!_}h2+PcTTc-Kp}k4io)GQ~_L z77Mo_4OoB&q|sx#M{)UDeTa&>B-}W)?B(8;eZ-XJYy{Yh!k}>O^e$~yz`WRa*MRUN z!%<7;j~EP(Pn4baUlooCT)icFUWnDquv@BuE{v#=AXQ#-N`Gg#X&6vfCi1kA5rf$A z4(`nk4|B(~C)N`L=aR&92)(Z0knHiZkL=FE^jiPD|D;p71l40T1JMO4F23SSi1fvh zW#nmH&w)%BUvo(P5`DqA#BliZS-ZYFE87aAOC7^U><{~zztiwn;`)2exV6I^*`sXH zwC%;C9a5ld%E@&0gzAixEK%7A0y5dGd}Omce@{QpJ*0}CL&K$vc{hT?1HQw<_D@*S z=}~34m!|4Cuk|v9M8EGCOHs5LaF4%;B`bEpr|rf^@FR4FK@?A0?wb+ZI??nHb@2E5 zoA5)b3)eIQDqyEIh$i9Ha7QOuH5lc6?k_0MUeU zHJlfdrX_|$VH0uwzGtBJ%8BThBq-O3+l&ITs?^2pM*8pQPNXSnQ?Ojrt0{ zJNW8*xqqI2BD5DQa)vds{Q#N>GjP}N)f0HsEr7?^1c?mkSPdyQh4~!l`5n$+d!m|cfw>`hPf|mGNMM^L)icZ5AlIz~stYT@^67l0lSh=s34Q@TT6UGMARUU5Xr{GdN_n<*YEH~1 z9r0(qFR-LKO^c~?WoReypaLreElj6iU)N2h^=m`oMRBQ<3DVZMjxg8oq3OoeUM6Ut>N5ImjF!yI>_glJMtgoJ}5 zoz7xOTYL;+;Us)V2l%I>2t^nqCH`zZprK0_sLcf>G{az3uC&34hXl^ff~>jf66yz^ zB%m%J%P%08XH$jZK^8KZ1)2pfm2hBARBj3*v8{lI(8mf_XMSiw@?RuRJB-->xSG%}Hgy$s6c2?l(L1aFqetqE3z|0hzG`eUc<4yjT~(~{qb7ew?oV{OueFi-*aC@H&Kw7_s+@pF|If4t3KjuP9; z%iiYi*mih>8=bYuvMogQ$F=ylNgpX~01Uxp=tLiQDQB=xQ|pY=jN@DLSDx2m2H2aO zL%ik}z*2|=bRBzh4Y7`4ly)j)jziV zU2+Z)BI-|nf~6JRDD75c`*JucWn-`Dv?n*Zz=CO58cQmSjOm%{m0>Cvg%266J48p_ zGzEn=v5LCg854>_vWB?%QXA0>VnrGKqlB;t@%DGg{dQlgV2U!Ko&hC1km|P4any>f zMKc=Q0()OKc#mRQcKXla*?j$ce7h(uZxx1W!{LN1VjYN=k@gf%NgGm}J#gMyIFyng zlsn0>rMKe&Cp%o`}Vn2~CJC z;pTVDGEi#0)^10np~JUcKwYqRTE;*%zgD$}u$gEPbL$HHwbh*D~CWc1Wr0w}!s;+eeUmw|eo`2y|QP!{toZDxvsRi=a`CEgT zj3WgO7HwP7^w~W%C{mVSx+x|(A5ykua<H@Y3ObFs2`ybIAL0FIsXF(nqa~009O}iu#f8ytu>dP^U(L}Yb$pznZ`(P8T zTGtUzaZl=SBIQzgTI$9BkrUciZeALWg`&ad5}tkGwk4bGNw$aENg>0V{5TDWByyI`bWwxmja{qTn z2LK!0P#*w}ZDJucHAAo&WUCAEWI!coaKbqzf*#%e8i~r4`Jl=T|R8R*m9n~81>&Tb7@q5#ScswL|3iiT$5n%~& zLo+peE(QFxx!7*)(sZNfsh!CE3r)r1qJ?!uxId55l3gKcRX*&`43jauGqeBIG?WwD zY=FD8DcT;AgyexF2-U&$k#vk6!EYxPMJ+=y%Ytr-5vDky!s5{2Q0>lDCs1H|T-~oP zyfX-Of^ur2HSOq3BRM(lM@YsjA39*hThu@NQ0M(&c;41Wl2pHfAvH1} zx?4;ELJQ;R>*kW0=Lw2^s_c&6CY zWR}Am6SroWcgV_(Qp21H;-0{{)I*3Ec|(naj33nl#y6&(t>DLmP$=Fc4xu45uczO- zkrjqtXo}!LN?;WQkKke9{tKa$FsfmcukeXO1F+h|K&B1O4Ojnpc|26e;R?g;Q4$~d z5@+5tJN%$m!*Gk`PPsubSP8?;Vb|_j3_rYkh4GxB;0FU$Rk53W{aP#uF;-%iK*voN zi+V;?1_r!aPpJFp;&ukcxC5X?QQX8f!K9f8l0ZPQ_)BxDE4OVmJFnmcQ!3HV{7v`O z^`ny%{qv8sE8X$-47vG(+6}#Ty@B?|0x+rTrXwL0^Y5`HTbiDT)ZNfbPl%$hxnfVqY@r5hpfJ@Lo2DqoWObq# zIj0EKg>!ULZPVh_!ELLg${6Yd*s_A+ z-lKhvn0@6P7s#vv)06W$@qYeaQtICaG`wnTB}tf6{5cfyi)D^wGTvR>^9oG$=Cx1} zBd;DFx}kWnhGQqxy|!0lw1&(k!)3c0X!FlRN^Epc0R+EnOsh4(;bl?3A0VDaS=3`4 zymu0(=tnm?XdR=j9yK;<%egR zDQ3z|e%tc;=ixa_so#r@5({@tGJX(G%BEI1swHs(^Tn<;u3#Kvx4y-Qw{kKRNORzA z!Jk;O`LDw|ZANGhX4FhI3R=RVHz=5vhngZ!plA>`G}w3-TN-(6$OfudW4Ru(XTIpo z(s+-tJhD0p&)V~*`}zgI742)fXJjUD5U3#1eizOFJOnvYMSxLsU4#m>3pOJDbl1tQ z1LD$G`+GFe!!Af?u@s6&kb@xbo!cyA{Nvk4JJv6coaxn<-6xtC#hiBWd%l|eNgufj zwtM~a{Sa-ix^Iv9UOTsScv`~Wz%b5HKorxLk#6&4wAOjm8_bMvGnKO#$xX?3UcBj( zW`R;izVgc`4OTZBfw+gpSM?Q$4+_|&B&(XZl0Z#Uc_k=C#n{?eQrkMWA}IX2&A8tZy)W>Q^}YTo=gLTC5gxN5sWxZhSMSBD{P>u@N!)dV<9$BMw%feH4-XZ? zj3TW0Abu*f{dSV$*b?r9Pn3>}xgzKSQZcW*kqp4g9}bCZPq z_R47sODzlqV>L|6jZ8|BL{zB(b`Lz`;4EQS@Z4;{r zFho_&f-}4F7?g1Mx^FQhZNSA(1}a%AvXO`4i^Ph8ru>ECJR5L0O#cI~AMS?@pC>+z zoZV)Ty}jgQ2h;&>uP#0-3@w9@;PU$iURQ19r)>BJIbLIo-vJnXQ_p~@kc4MzGMeYW z=A5$gu-xmOs=ZTn#25J%%(3e8Oq+#mrWsN~`&?;)lw#jb+24?u3|9zt@9s|fcdW3) z%F2@z%S-p2f>UyqhYSCbVK*Iy04A%uao)o;D=kE)4o;y+7+1byUyKnt{3aJ~Tf56(oi_y!a&GRDWbuYYc~O(f~1 zRqR+q0QezGQ%xB`fYR4KHj4$o_Ex=+GmcjygVhlPfRo8BKd)%NDbxtfqk^ubUmq%F}MokQ1;J}fm5mEMsigDD2J zp-Av`73XZ)MJTW0r9+H0y~ISG4jx~2|M9Wee7O=p=50B_rJ0kCRd9<74P@iv8pT5* z6u-g-D!D!23k#crH~3yc;b$WEh5gNz5l{Ep1j$F&fEDg6t_D>4E1-Q z7|p?3d-k_@fazu}W`9}P?AY!GkMPRUrhhL#LQppU$O7$lzK+~JzP(j{5oo|&%KBN8 zICGuL0{fY<}~bMl@sH(2v5R$Q*y+20&^@{yfq48KT-EY6`zs+mQ(T^K&51R^N z)(DiUoR=d3E@~;YxBX$3u6}ne;@m@C;&d z>U^oqR3+Xaoxa!&yJI<>t58V>-Q&^!!0Ce2b5-x{4<}!vg4$A6VFy4wwiP{~?otwk z*h6dT()jj%Ql#L<329Kd6o;k=nguQ*;vD)-1m;ghC&wA}XmNi5Jev zxRJ_3^$7y27__4anXbnG#`ZaaMzC=q`t2Eze&%T5lh1Rwlxa>%mEPo`k11Hm2c0(^ z$0#c49qvP@b675j>YB7*nyJc}XlKD!((1b{VP5-fVyg}(W2uMJ+II#6ybNSCp|f&= zqew%b3DUyqvqC1nRC#$I=5aiHyg})E=o7z1+pQl#YYoprX#6FX;A)3(C;u(vx+T|v zISt9_YI3S^R0Ym$BgUE-<%As)HB6~#OjEK?h&F&cs|VQ;L~9=nK3mn?WVb`QFm9_M zHO@XLJW4eN(CJnZ3S?(@Z>Z9lGiVyHPz$2UA5T*xC&>hkhZQSPE70>8>R1b~)N`lO zs1wr?NAEDy7Rcl+R1wc9Dw100>&6I`%@G^L^_szG%0`{BpMu6O%)W?dMQ~K=2w=q` zitn%l>hB6^MK$>mu%%3~DAWf_Q{c36mlTx)|kVX8U z^|~{#Oe-*oAIfdpM9oPQ-R*Cwey;#@Cu0MHODJ5P=Er|t$nU>qA-ROCR-kUR3{8+X z+CH+_5O2|eccJEHD#tn~T2xh4=w+HdHJcEBzus!nyhx?6zq=p8yQd}EfmvMcJm4HB zZIHu;cR}uwlju`L^tF++Pq(xkNufEH|S`O(5o~p1)44B08ik z5@BwE+V%o@V6ru|DrqplfwvTleT@%YOvrvQ%hKBUz1OX>70A{z!+->zr&v4T?#WvF z!2=U@%@BeYkoBPheKuRhNQeVRH^IPPY^Y^1*m3e-|Egfr=dAaGNNNNuhA)yh5o!H&WW0q4;D28z;2(!~ZCh z==8?-9^;=dY-EqQPStrV`Tt^w7B)p_jJb&F`Oo%>ef==!=HKz7WJ;r6?$>3WTOYzU z`V-!KyQzS~n;`X}K=LB#o!nOB8df41>gZ@scK}ntmDHPiKmc}xmLy@)EV`xYCH`6c z`J}t=`-sGa9#RI`fw!ddPS;@~h)N@3ulMg9L$Nl!I zo1He6<5#TgZ58u2W$oqRBi8DBn>Iu3d(k=RbH;v$&M@BTlI9txa>UwVaK@zGU9jBv zfJzi`mxcI;_JX2H5IMu-oaiA?B>bzvgRw4Yb9S%Kr(dj+2^VdJ&ZF}+9&F}wX;AlR z$u8;jR2L}u%Rv_T0(jqQrD#a5^oMv%{^)Fjv`fIZC#JcPd83><&3-18lNp6z`LHi` zhhZ);R2^vc-sgw+kFZcZ?%ktwwcyXm)t*)|a}Jn`bhScq4QxIA(l>0W75Wqu@crD) z=N)(Hd-oEu8bgFQ=aG(HE9htbF9&JkyYo3Jl$ToVl#Us&=iDce=}8E=OeNhy%^~_e zlF28&@1B&I=D1Aut6kYv6eCm^P&5emHvp_fck#~5R#SY|b{yF%e=>&ewd`TOPvxXH zPSv!vh6%i2rH*ZI-0h?DkmT;YlV*3Q=?JIlEbq@7BoY2Ah_b4&@;2$`gtI#*);uGB z;S>y1!l#$ewy>8mkAaYPR%%7y@9*y3Ho|O~+fhT2v!FZs3o6;FjW&neBY)J2Zk*mo z07%w4(|ar7(3iOs7VDS2KdUUH2?LHOnlRl1jSnq-p;R=|is;#F#Rm$6NYNcQ$$O}$ z>~z`la+sf7%{Y)Ey6V*dpCR*1Ct2%l-+QMPUls>^Q*CV5a{6KPse}SCA3%Hy*xgTI z!N@m`Rwe$1;E!HrC7{|I9%p`QKe#WdMZ(1f?u@bNs&u56F@`dk^K@gFXWAF)r#1KP z_N;&1MzD1G)5fqj%Rn=whrPkIna{T%7JyL-jWIBTm1EgjH2M2D%P#*(vVxNxz=bML zkGYK??xMrGT7+?eaLTI36n+Wu*UzRL%RJK@^}Xk%k+N>?JQ!z0s*hEg*LU1tA=Rnr z!7?+w&EjuZfJLvs-iyb7BPj6?*5PqF{V>zXQF}7cX{yiRr}P=g0awG3KDep8{xF2B zr+*WjPHMdvCRq)~+tcKz-i`4uTBzr$lYt^ad+nE_zA7Ad9#r?BiO0j#Yx2a2tK+%H zngjg>uDjGcO2c+|cumhtJE93zLqyr^{=&Q*njpS|1s#<%Z$3&VsM9&-)FQ^f)QSH( z5`ueb8FyDCSxr6FO-(J=%XZ>yJNAPRd!VtyQr%fS`=o^*pxmcF?&SWyGH0uj=u|3| zH^*&`-Z1_C6=8M!=jnURXUo(w=GIq}m5yn%+le-+t>I?hz}M8i^kFRPz*_T=r&LD6%gz*~iOu6C6u9gZR03I3PAVcF>wwklN}?*1PaRS%nK zPL4vqj&*;w9;LFpuCQP0X14vS&cJNRYI!=Di_`BTK;Y-~V7OcGy|ww6%m~yiA7QoL zU@^a2oq6-Cr>Zy5>hmWuF_`N94nmw04BuC-g~>@9%4)$bEg&MRoDK{bXiOy`JirPfRpx`9+A*=w_dWZ0+W( zMnYR+&XR>h`zD+K^;9QCquKV30@ARW5sqnKw)FPDKO9lp(AVs)o;^4yZUpP-7e^IB zYEdrm!;s?ckvl;i`)O`|okVNy`u^!ewjr~4&2$5^VK{f;xfv8a3TqvDMPJtE*m9~N z=z9M|5*TS7#_JQdA#`6?R|e%m58W?gMiIsa3}(E-#qap}97iS_CPubdW$kV_ zH4a$Nvwhdm)RsDfc#ovFNqGn& zBc6jNGH(Un-K`5H9aB&e5;umZo@o1i-Nt>$%@>h-3WdG2pXYgk^uh_2Ep zw-D3+>Kq%+%NqgL>8UCuI!425h62w^xQs+|)u=Iz&F3&}u$HtfooY;c%B}ok1vp?l z-1AO+q~99VLFth**i%p8OX9xcjv&x5^`P7L7nC2!9hh*(7`W4tAa~(o?MdVGVnp(s zG4HJ!|6~DBN{YilP*Sa#GgU(nQcFNVMdwp0_BNJ#F#53nCPsL?YhF|x%$-Rk(c+!fh#Ejgx-M>SfjAZQ`PG9TB70QYfRet{RVkp?R zjgY%(p(snDKC%VMzj*s2d+MUht{RX)nFxJVl5`fHbDuACKy5?tYwCnu$#QC|zq8%!axPuOI^3!$+Sf88yI&ItRHD`yI zZe$8o_SZjOTnd_g9hYE;YRJo9Ty(qql$~GKttojUT0(xs#vN|`g}c+`X40!I4C{zF7&#vw&~uTl)%=PrF6rK_V8tv-qWBX=yxBb z3+`zKpGvHDvpHKs8{m@3ZzjH!tqCuMi%wC}jk-wvfcAfzf(@=sWwQ=b7j1q8B+Qf^ zglqRN>xj)4|5aX#&|$9NUn@XU%gbu0q;cqw);uH>S0)P z>S7NOh_7zG%Ytj$e*NJbo9{y<+N1X0YGV}*cuxj*ml;=~{8BO6Pu9<~4wCox{d)H6o-6s&)z5GjP40K(nL@0tOatDPIS zsZ43!gh6#B*yfpIVYBBWfOzzKBz^5@yv;Bw z8rBR|l^kT2GE*-Zt{4QRBtNn}nK-3hb0#lJA^1*E9yvfCK|hZY+qSa<^YZ(!?3uCg5Gv6cm)ELjwVqc($?mI?)1q7C4k^gg(0 z7RNTzS2W!+y=fM=3cJV`ljjNfug?o-o+MW}jy_n&%c7OJr{BouB9J;CKk<~ZkUCor zZ~wT8oSgNV8z3*z6&ku~3s8C(oVO0HT=<(%jY25ihEB9ERq_?uz8+>CNs9JqL4(>v z3Y)xy;T`=%=3XIK6#=>FBascHr&~-BoP{T3M^Z|s{Q1*e2YUYFRKYEtOs6lZOs_B- zGE1sUba;&mfZWwQvP{Vddy*mAMoOGS$?qD&pkqD5_KfxEi=SrH5$yBcBys7dFuBxd zTSR*X@Jte#$$ut1DewIRwVTQd4CVWnb*Ucpn)X6s3hutQX{3k{wBYQsR2U5vsJZBl z&u`)iD@nb{f~&R16B)zbi8xGk73mvoX8vs^QnWX zL$q!tRFBm>lLXmhrUU;k_bz@Wg~K|tL#XKCDEGx~<)_S4&e66kYp1*60Y37sP>PBC@MybHr2=q1^S%CT%fG#QSSWJyAJr8tA)lo?)_(#?!Q4%l*EL#R5Z_h=t_w% zxNF`tvawPwoy*X5he-y;LnjxD96l$$$exD1WKWiTSW>F<69LQKLU3Q+cPRt1Bc(8&uLsw4+T>7D9g1r$8$R z5r?8wB$@U&W}1pp;hZhLwoo5KS|4Z6vawc^f<&vRDfwl>cF4<<2AY|2l^`@NyKGCI zjCP)8J&26yA|20Tsm*8)u=!XvbCf~wAZ*+32t^!2ElJkRm9p~0@_K%=6*5!PG1VhS z{_hz^^xCXb2j7Y87rQOezyD|~0I}nsnv3LXQqd9FVQ3#Opz{{4rS;ZVf#-frrlfZD@g(ez{`>3>#p64!l=0-;c z<>6C)Q-*Rb##Zd*8^No%-g@H?Od3NJvUK&SMznpN{N#evPY*`NU~ii$=G^POB0_Ha z>t*>ZCLn+?pqHji^iki^C34O)*3k67bF1osumC4U94Om<)nE}AFaiqdS=5~>v;Txz z0NFUpdAxTjU9i2)35sAJrtdfBKw)UX$8Q51b{Kp-4ZJxjk!*N+vA5tP#H>f@XA|}6?wnm1(y_T zuq%WVBL8s%iDU@hm5l+&ZTo(;{r-wW6vCm`;T78kxT$bsl$;kv^-OiZ1 z4cs^Qn;JHeNq4afoEGQ0nHhP|Mq5N!d1KiPtdqBvaWv?@Ih2&-x>&~6)^PVH`CoHr zALYH3GEAp=(q$&B2H5^l@u5Z8>@IxXrRGe4{#qs!!hm~A!f&G_=EZ8Q_Ww0a|@z*2%J`2 zW4|oDFL8pe_=kR8Zz%()uHRmgSG8$uI(3lmM<<#RjmUlS9WRl5Fqi*r2{E{JZ?3@l z`sbcmov{=Sykk8L&sG_P+H+pcU3W{^4-VcgZdWnqfV{4)P0O&azb=oyk?dB!jk2#i zR|OOYLE=u1WdNNt3}%lI?u?&Vg8L!yoAAWG90mE9RhXx2`a{iC*~wEv89=zO=8Urh zm(y{jvdTkpz($?l?8_b#{IFiL z-b3vehgFLs2zwaeEW<%uAL}Wb_TI4arVuyQS`!ZC2J`Nmql^5qEi!9aY(uL zP~E9j30nyKt2M$p2;F$SBiLTek|g6GuQ2NaN({CyHU`Pa{mJ=q;Qd1sv4GB9I}qlTjg7#42)Vv5ZpvJbyWpCIAH?%#yi82Mv8`dxG}!ru81wHjgs7^>S17*-vLqf(fX?K@ zyH0gEQsD)}vJwi-(EddCecvy(18uywh!}jE`)sy#NtW85jArhvO6)I|*nEe{gKI1? zUm-{gd-=YA?X@p8r%!RcvASs8q8r6+MJY&SLVdPsOIo(S6n5WXzZ?ho>k)jFe^c#} z(Er=03*lp(P6E{|wKr9olOMpyW6PsK)|G)p@;j1++=`%g8>F+Mnw<4FcvRg~Y^tT6 zCYsgi={;wjH=GcJSG>-yqoLaMZA18+-B{i#ck~{iCcZRy3a8d*rP)}+$!C_aa3nnh z8`s^Sfs#8>Dtq39Z$s6LTotwPP zjepb1;AXAepCWS`)Wf?bd+u3-w5#Es(d)M!(50cm|9Cftm9f-F$0I1b7d^3qvV8Q= z640~oDDr=Vr6INPs;uRxT^4_G8tB2S=a?>~3KLepWGG2LfpRgOytGZ z^tml*2va?|QyG2cWO}+Y9u6-Ijj*xX<|=B5hyfj(*OX+vTA77I9n$+=F)rm#P+41H zDL@_mOU7b`ds=#qa^|youYxT?LwlL)vkspz#%hBWv zbx>COe|)+s%jhVL61+fqi*)BPJd!2n{j?U0(vp()1(BI^&Mhxb0)#U~#5O`yqt=o!vg{LfH{)GB$y`7{Oi(+N9A$kd_k?<~87v!U|Rr#i(ayLaDw7|6c(D6C7JmoPTJDKyFM_#0S3^)`#%2?xro8fGrSDRBUL+~uR7!sYjG>H*;b z`z$qKv&0WYHWCquK~pua&rofYo-tURIeYOGG`p?KaqLLELy%$;^SCJAQZ(5qfs-f1 zTQjgTHZkhR^VTtvg2KoI;Z!ojD6?_Z2%XDCq{ifC*LXC71#Dpr4WWIklxM_FksKuS z;5f;aRMS}~f$N%-VPRz3mGB~7#KR*|Q!8Y2pd2uC?5S7v4 z9yf^Tc1cJ5CLaRjdQI;sT5h{g4<*`haqwY6B{<5clrdZz#Gub&iH^ZsOm{2~#lrPC zU~j#(7D3dA`dH(1q3RT0d(DM_XfimYg)mI9e)O5XhVZo!6$ROu=EC)glJYUG_wUyj z*6+OK456_E-NC%kOU{7}u5(_NmXh;iaza#(FFLCNB1{efTTuwZ1tu1a!EHhZ(~2u-u+%iowYAz0T#KyUo^xkq1Z>5+Lkd0 zdVKm(KAQK(G-!97zYR76F>Y&3a2caj>qQ)gCc<@ktq~gP{#oKOmQ`0O`&!UVvg)d= z0z617-MXLXf8YMn@2mmhibIgFVdU`z(~Z=Y+W$=;fdz!yq(MXB_bFmI;9E7qK*g!E zc9Uigo|)}o$=QP=c+fDbK`{|C2G-1smGS*~dLF3C=#PD*aAla>@Qp_Z_PGHs%(QiF z%{9i=R={8qz5g<$(`CSfUUU*BG{8sZI~6d7v-rOayDC6ZKait&Rm2`^58~UCM~V3( zFM7>E(k{mD1doUDL#M+3A{CdWaT;J4iDbe*#ZT;aZrS^?JDK4;K3SVy!4voif7J+0fC3KxA}~A^v=A zSf!J$$|cgkb)&P1cou{4vB)MBAiu>_G)IUv+Z#d-z`e3kgoR{HQlefOGd3J2w6}OX zsb{7Yr1lrhu%BXeW$?rPk~9BHG{+sMTLfJlktB4eauDC12~Q7_HY@%CGc~V08l)Bx zl*t)AKKv@66}yu9v{K37NyY0XnTV58UtEjKM7%Kgm50P-Y9FcND;V0xMgtXmz?cwF z-HkWbFqGl2& zfWgalE8|Th_@e`{h42+gO^P8}mlciUjQ(tC?3H&s`IaTwHtGYx2C2W@DU59jouU3g zib{S*rwsC+S;0}Sw&c^o@ks4S{`Hh-PVpOTpYF(Z7Akb`YZnr~7>Nvbf1g%up1zUF zr$V|d5`|h3Ofib=valshaD74)nu;=eWu5{O$<7UxK9WDb0^UTjO@({wN3*1WGDCfZ z6XYLb=YlJThGS;0e-RP~)xesSPMh`^1}tz=Fc|)8lu*8~UtB4boz=JFHA8zX{PQA( z2s`6+Su zYv4!@(B;wwXo30-yIC+Qq8QNJ!Pi8y63JSb`VcBY*GsUYeSVHNwS$hB-YTObOio-o z-z6`n4F~mKKe#Dl1UymNBK^su$HE|^Yo@Bz>~@8JqZHmRV8a{iiwzyEkE);r^eiO` zlY{CaCSSh2oj|%0lX2#}6u)-NF&~`R(V12^)$k#0{*!s`55;>}y+aZwx%tkgORt^n z5n%jPh&HJa0$y7c|HC1-+wi3$0L?HPmJJYQs8Vnk7OcJj(YD8ggig%=gLIcN#%@#~ zIEd`yGLYs6*ZZyRp_FJPaV$dEZ>R6d@kVCpUqKQ4`?5povbf0-ET=k1nz|8RNIz=< zq*&b*OWRNZNeruieRS?UMnetgd3&fLEHCVi&Wg#Bgf!M$Znt2=Z6mJ*nDDP%x zQ+@3C5<(7n?pk_?^XHk6T6}8hqo!KCAn)%QUoO3;fQL}We0;c7Y;F?%ru<({XPggK zlc3_BftOy+x4!Ie<6deNzSsW-pckioU~rXikdeN94RMZ7^|IB$)s&zBN2XG8Hq%!L z0ZINv?w0XVE2rBVejb9vjyw0NmX`>!xCv@xb9o-piJD#YN*AS+@9Y7efGK10|_dhm&@+(3Gm zj`iGG8~ZgB7@MC*sPuH=4&hP6!>f^S0csz*7@CK*PF!VO&M;d5V&E9=?$_V_KFs+A zbDTc9HQ}Qr{FZ92yi8&WUhXTHt0(qC8$q-^|7@kzBaOFdasI%%jSVYmg@d2~0K1#G zR!H-qSbC`awjA~vHP-xJkVnP+l7nD|5X;EHKoVgW5kvB`bOs7_nnlK#|O{Ynv z|6A|XpHV>TPSaW{`tZM6UlSa{;cU>e8=181p`2;pOcm{^)t{mh#B63W;e@qbX zU1WYUgbBZdH8E8qd%aXf*Dy%6*BJ3}gN$G>XGRaXR{m{1Yk9u2NaK?W8S%dvG6lyK zBi(=!k;m@wg#bXNW29oilhSB2ac}mh>4@5`FXsV=9l%n5bVWr+J)Gb79;Cat0?Ip# zA6-ACq`hKHvt+Yf(#KSnP@&U_s%+HrsOh}2#+3Oig}2M;?h=pt3?VQ0Ptt>3q+oI8T_kUB)_Xh%&lUlu*Y-Xdn>keMq8{%SBC<8+FQZ zr*=(qAqU*F+X=ro(cS96#R?ws{cMh|Xrn5<&HWQfd!i$cXfnlUiy#tF_5bTdTZK}P zQcpv;^7#!1y==tlghX_fzvL1WYNh*dHMKgLxOhwy-jVR_kJEa?&685m-mIa@38}0c zM8}C{w@G-SNS*qV;pq1#7@oNG zP>vk1N5eR2`=()U%RtgP&4nIfg^8$%3P`NRh{J_1QlS0@101$erAH!$XBw!A!*-cb zZH^7wb>O;EFp&QJ1;$hK^v^ewL1>XTN4*ZEzNfzcM5N0~sjH~bLzI>Io2cLinHeO0MUjhz5U(Jk)*^)L z%698gMZ}hkhEYur)rL{Y(0C3e^9N#>W444*v6bG35qB@Brf6V*A$V2ELGWaQZ9m2Y zEF}FL7s%Chf?yPtE-BWv=_@9Q)90Wn6HaDlE|_bL@XWjB^Fs5mTNfT zF`U>=K`@bnS~h>88y(=x^LwHqbL@v!yD+dU6nhRJLLNn7op}{TtOzr=u`@k*@Rry- zod^UBw=W%|C0Yf)%fj!QUNg_dKEPzNf7zB7%VO$~a5kl_u)k#iggo=`kCVfTZg?HP zE5eg*af2`InZV%$?cSCW^SCf#lrhmmX?TZI-@Dx74brOB4i#_nFK(S)KQUtBezKq04y|Di3Kf}WyIGjq|EnCnhM5@$aplZz#bF}~bc5NK;=1NHct zY1SyUNi`8ego!!&t#)pAjHa%*`z;ZSI^~B1y_aA5sh+d$CyHm`1g};~lnC7guf5wS z{ckJog6F!xu(!S7HA3%y&uY;M*A@fn#o{9U!E3I5QldaM%Ldrd6LUJCvMz9|ivp*O zxc#nAi=>o#s``3g7)Wl}5Fo3PmZ{tmLM%ON;+|SWe;3H!_p5L5Hy=ran(&2%h?A7J zbs~!;Ik@NIqCj2apQl8L+E#KA0!SRJHqkje0wj;*PjwZBnKcNh(oB@%lNZ|LS&=B;2uP?=^ z=*k(- z$#!JeN6IAj{q~}{Tz9+*#{FBUTV-KHykLv4-R%U|iHqQ}ngt5KFL2Me=w~&U%1oXS zbH@|Aap-t*KQA!Y=&S8fg-)j+vL4Hi38%dmaL%L5$X`v$N!kdai>A}M`-cmOVfp$5%Y_f9ey#PEo9}T>g#x7%<3o;RrVJ-U7+vsa9&T(h1THM zb^}i8aGU!SkR8(L3TS{^L{!7M#PI5(0B#W=aX2)E$X8v5Yy;3izlds2E{+~aRjfAt z6k0fOyM#oyst7PW2)-v|ca*-jJ4E>~v4y7P=Ogt%ycE)!VBt@C1`3zJc=TmUI3L*a z)Mg;4kaaLxhgB0xx2k$33j=0A!LUdUaewVD@&^k`gi*a+_~YD;LqexTHaO}i^Xm@1 zU!EZlu!n0`wQS!V5w@#y^Sb#=Kiag;A2VKnCIps#)38QxGUsNsUeCK)`9n6`r&Y3ZY75*gS{6fFvGBEIkObz&jhK z^Zd*){GV-s?`yXqbB~m$6AM+! z!jq;L=9ne+aer7{Z`-$jDR50!VS+@PztMT)qhIC9G+$FzNnTIhJ;-yX3y%wao}9dI zT&xQn-3)J-w}`#lyPH;X6mrgk!G@NQ^uA>HHC+~R2-c+;B;jj=xm2+h0^=N>k|9wI#?CiLQVEUFfh8Uh|=#DOAH)k`>&s$6cD4xF8gDC<; z4<4fd1fx>aEy6>6>UXcg1n1X+xn#pWB0&A`KjHx*DcFSVXaE+Yv?$q$kD688iq5rZ zJF}JD{WoDcAk?qxjcDQsyFp#%5N?+z@DWoq^z1DsK;B@Q5TFQtp#Q;ENCiJAONAaTf!pVjw%aw!0 zXv}vh4;^%#G)6K#;~^`nP1+(2cuRU6%#qvqzIQ2DwTb-0dRU8zV3nyxw<`pZ7$iPl z=O;K6We3(aC0K(qb`mb;zhT70uqad59yY~2pb#fmkJDi>+%F;~60VByjGs*9y5M6I zVTB1kgq~i?_`ly{Oc7*CqogG9yIEnKECD>!&$dQ{&yENp@UZ)R2ze!)KVl*5d{DSP z2xQS3_Z@k}3jjr>dqioDu?;z;q!^z>R0ld>O}fS)JJ>G(4-`^$y8^J?-4Ve{M0?Zy zP_T>%Q>n+&_O%ZlyJHU7^$Fbe({9uBg1=KVu;Fj~S!7BBg`{OHcg#TIHegs4ZEEth zW59)iU&fk!)Ad>)vXi0oo$N8e4em@ZUj%Y^psm&XNSDuj$YwjKv4-eei*aw~0_jk3 zCKWgw!#ot)MuIn_12tzV6f&Rdoam(#<0`i@HcSi*%Cmt8VTN3J==EVjx|NzbR7@2Q zMY`Df4h&=Lo9g|gbZYX(;0-65KJg@G`)7FG-1Xf@5ll5ID#n!t>SdfaGQbqdy-#{6 zA;U;%AvUAlDtr;i8ILSVAcx(9PZJ0_xNED{9L>O?vRyZ7Qo*s*@p4}(z-Uu12NRnH z^lf+QsYe)oMV`M7bGsHFZF7Cw($P=f#{^*CcCC=+&Q5OZ)I2f7n}1$@yj@Bh-LZ!3 zF0!^Q!3@;=t=)$yd|hZfc(@~W+W z%j}S7$ZjpS;|YAoR4e~A89d`A`cA$8wnGr3*>cnxPbw2en^Z0f*ix^)_K3W2Gs3+t ziu+R*N5n2<{LuOuNhUpWG$5w-g4Q%2$D7FX@Gm#I8)M}lMkH$hi()rxb-%`4a~SuX zBK$2+!kp#ucBeCB7^A;!VRinlD0dxpJ&&8gj%06EC(tw%moBaBY?XzjopgfCDrC3p z;#17l02G?t>Vd3`ilDy0$udj#V4sbn_tBgj?4Cw1>qBBCkXrv@kvCc4|D;rNMMZn{ zM=s?GB-SSCs2>L<>M1$3{CvP(&hAcPkxNHMr;HBAq2?q9zrCaJmM>jNsAy%VFSOOV zV(TF6WVzP%M>m`K*I_Pji>uZ_=0!A~xE+7|{YKK)EY9h{D72`=j~t*Wvb`shJbT__ z&L9ipYyp?zSYuE;DNFfmZP_TNT@Rc0DXdm_cBy@(F5}p9D21de>cr-(l9$#zeI>KP zUc<>97g;Em(6%z$&sZ+V(2c?=TC&*jqmcyO&HfUGkP98qrrNPa4DV37BDk@g8Avir zS2g>=N;^6{k7@e8K{>j@9;Ed>i?{U?d})OIb@W(_#P1M zyTozr`A%|?wA-A3>3-$fEF^BWj>d@rV14B^SuP$6>z_c7k6i!Pw3*y>T(j?H4Z>KD zzhK&#=In1i9Mgl&i+iu19LG*tyxIgvU90rIg}8HQ>i7BEE~dq{`VAGTxNc`wt&)-S z@cDPxR9W-|IQL->%JqO>sA#4r=Ni!5deFVIAEV1%&lRpxb$T z(fvB%*Gnn5uJrjDf26{(;BD2&GMi|~#<1-b6UJSxa5FM4g*zk64qbmoT}?d8zV#3L zAZJCLMfICdBm>Fn?`RvZ_(wlOIiTeR*ooAsVWqdF@AfR6xV_y5u?oci<`*vYpm8)D z(TZe2CY;_VHefZ3?nSldWO)94mTyRWf0@`-^=S&7TmBe@I31z`e`Ca7#K}ADM>ucQ z##V!X;0W}|+Z)d<#_OnEe)rz5%mZ?^#`5}~lB#}6$PPodt*ONAYVt;0BG(WHBz2X; zyJ-$WQ~*!D*I(%Sle3XwyCx;NO0*FDsd4zp5Q=(wui(`5m7`(&8gWoCqowZm;qbvZU4ogmi7xqRPr*i9A| zHv|`!=9vtSF`9Ji?4a$#8aGp(i{%)=-+Upqu8hU5T7SyJCbyGB;MVqKM7N%6JG+kl zb|>H9I%6oyT=!1YU9LK~7Dltdw3Jr6>FB7Cd4}En0?pPUpY5`k$VwuOHC=FF4J59+0gq3GCR zVMsf1@l%sm;DWccV492vs}3GBvvzV_JZ)d4{n=dg9(FYEm(MFSRLU#G(JW&h0+cx# zIWAlvWdD9np0>PM;J(`!w;WK^WRNTpQ0%RSoFlM)rZ6hoJmn?)m`OTeQ-Y#5WjK6k zdz%VpG)=zmrGoXG@lMkQVrRF9<{kuNnQMCeA6s7;7uEOtPs753z)FK4Ex9xTqNLP< zbi>l!(jY0_-QC?G;S$o_4T^+xhY0?zpYN0Zga4C#-PgT)&z&=8&N(x4X5RDjWK0^5 z|Ce)zOx2#~7iyMNx;=)DsBgduRB&Q!@LxP#8mZ>ajSJ(|P_ z4GRS{1-s33*dY`#y9fl3 z{={yFVR=3ituUbvW-~Igg-E1asO0%hT_us=H-seDD}nDzIM7!xCmk%rhqjalI71ZH zi{CwL-P_5B9ZuZDhQHh#{_F>;Edt>?EnR`=q`5IHOQ;B)Ic+l`o#;{a?9e(jogA$l*1m-}Aod0II zq_o(73i~G$=3sxEuu|eE(>uKZMnlxRMnezl8@Z@M>Q|hBRlOxURQQWuBMj!t%Y2RZ zD|~VEJ{G$*$J+d4TXrXrg}i1}Gz3he45@60#Nu^kju&bm4x!gef-sv;uNgyX(koxt z8Sk(7&R&|m(>rRm-W1`gdG*H5Sg*0p+gOiR`}?=RYYY;>78xw<$_Gv=4UA^ol@gMq zfEp?l^*N>xl$UI1+Mj{QadEwKb+?Dy5nVukQDE3T9Ao1TFGaXC1S0H@NXfxW7hIu* z9Ne3LCMCJA?ayM2lP*glhS)OGd{zT)LdakR($q~Hk>+F>ZbDFOT1WwTTiB-3;Z@q6Uq0{s$@oip)LR_hCG65LE#XJh&Mklz zP}tTuK|VyJu)NMjD>Razo1a#_;A2DcaIHsY=GE02jPPE_G*mk&h|8Cf=Uq$i6jQ;! zBjg+&5i(JnKlZjbpE%)E?50Yef+BFbO=5YiyN8yTFzW5I#Ev>g@cOIU0UG^`EpoZS z`r;Ia&uo;iC{we2p?VOsiG>Lo6Y4{qSCbDi7K3S|9%7AjbXb(v!fz0tH|SQ^7%^(r z6fJY{1;gDJS~6D7CvME4Pzg*d)|L?a=-Uhtn;4m7_z)vu4_nV00|+}`1x>sJyCvIhh5Xy1Yu3C7SV_t2+!{JYApY7W#T z87@j4_#?WPf$;Z7#q*ZFb{GyAI4BMrHqs1DUwKt&W~7YzTNu!A?o%fs)w4GmCejxLPVNtev>gwZrh*OMD zXq7vQIm$*c`D$Z0y|kDPYrfUrn~#W!+E&CmxjD1s)O>0`J{@--E{{rP*^8kTfOOq2 zyvqzrQ&v1E9%273TcaASBspAHDivKqYoo*4YB#}MTRy-Rxh@KDVp+j$+-e_HIh~z_JBcNLqYLx7G4&L{yb`fF?I`(^}>yHK1<4iKkPbln@+IyEI|p7KahF z6W5NvYcpGLVH+|hc>!5t)uS)Nqw!d}*N8T?PbC-w?G@-(Jf~6Tc6qJ%^Eyn(*P%s=TQpf0I31PSipK`RRs@*VtuJ!V?#ZvLx6Tt*r~&crFfoVYW9!l zq8ebAF7@dQWyhNMbBKS>6&xkDq`ZQ*LtF5Szyz3=FDsKdnZh_c2Sm{n_>lxevc(3v zx&0-d__*4(hYz;{EoHxZhy5WHmuBYgCO%`&)C~>eLoKXSz3k%ed+(L)v>J?9k6HcQ zmIfIg+H=lyik2xsW#0s$HjVi#pk=OFOG$^|dfDdV)k=2pZL(KbJ1*t5?&_k#81itM zFLp2jD$yve%pKuVHXS&F9xvR8(JY4SRnmHlqWD~Y@YtJm9FC4Gy#V!4acwjcqiN22 ztdP7V;4nN(;k2h-E7dBhal;-`gWS=$E`+}E?0~_O9!+4E7^WwTUZ^9l ze^NqlFNwdV=hC8E3U{%F_2;uI)ou%{tCXh^*Ji6|GjVFt=-}DT{%BUk=U@l5(e769 z?4~jD??PKJU}-#a-Wl8r+Nkv#&9XBMBrC6<&xe#;WNI+XGhjsL{grV7W|dqG^+9O8R3sR`1|RpohOku9F0l)iKX$ zBS!1_{p7uM5N|^h<}nmG8ptHuP21eRMjnNQODryd8(e%_jCmMbH! z`d4=96NAb4dhj3U=0AQlAH86$P&3_JNMMjOF?OuCKB9Z6<9uEHMRWdSR=PN9Zw7t3 zfilxyeui83?PF{Hb{8}5o3szkVmeJ3!#l<*b7KZ-@j8iCD2celT5*So%0T~T!y+eG zB~~yxajgU<))+r2N&S`3X7hHw-RnX%`kx1=0k`vu!^vgPIkx`CmqjC`BbYX`Tsx=i z!TcU4pq^r(oEOBBlizK`|7C7ww4+bAKJIC;SVxprqI03@m3+_RLrWbaX3%s!1_Ea2 z-phypzIl5xB=uo^NT4^J$0q#b{c&IHe#xwrLcfVhaadb%3>T0> zKc~8%qPEOf;Ly``bmB4!w959nXD2N1Lobz40+KgPax9Mn2b-TgI}CSYLVGbQBFTZ( znM3lc9H5rQLC5*W+UXDD-u}A;GKsJtCnc<`bzcvUOB+6K@Rh#~TuB`g=IZfF#Y}_=qeeB7 zzd8i_#=6k{Q{@liih(5yFiV7`QQWg<6iCUwB}Gx!1A3}P{AUWE(-SU?tmuxbEj@+> zk#A)UTK5(}8v!+P3K!51%_@Tou_QI^_W`Y|+%pvg;h=z0N>0>gD4?HhD4v^=pZO1_gHKexs*XP~Rly_Z^Q+UN7 zX?i6Rrmf^7y6Wf6rUQ~wPhv`9t?7j<AC^KVj+f&v~6X^<83eU zu!Uq7ZLa_Og2}0kKP&I9TKHMK>`mHf>+v}CE23`u_m4waxTN}MjX&HbCAGGtv2fjx z1=X!LE&EH9@(hK9;`;Y?OC>*F;2bl1LogRfqoJN=c}|!e~Xs&H>?7EOSN>d z0^+DSyLt)|Y<%S#G1b3X_fv_sPC8 zon8VJ?r{4bBPZXQ8rcE41FwaC_wUaTdO=0<<>mJQ3>Z=TSgw4VO|{8+rYomMwxhHu z%%mGotU}dKj&ITb-a{PNh*Q@mSg$vILtCrie_l?5Pjo1GoQibE_KMK}h2u3vVikO# z?{DBAI5Fc}Y6FNP@#+L;|BczFUxD$rPt94Ww{f4=8KuXP7t%=M9|-lDEu6zlS6(A) z3iM7O=YQTBct^6uSBmrz6ra@n+0sbL>WH>LoTx} zCHsJJwH4J)z_&x|Ssm3dpoOV@OO`2z8D8OQEcs%<0BFRv#nmX+pJdMM1mbw{l7j*N zewpiKf`oc}iu4m)T)8QdMPKGs^5ci55(Nd^j0Cm{bEDarsos#!;gS7p2XHRpu_P!@ z5$UVQCc#YDc6YHESI&CVr^zYvxySu@vkTT}aIXB?u4g0MFuGg(fcA!;*>>nTF}sEV zSAt3U+fA^Zw&}obrj+?j=Fk&7&#V0(&kTrF=Hdg_?Cpf+q+vHO}5SrnQMI-q;VU47u+%mw`HEEoujY&!RQF~u{Wl8Z&6={ z4l^nvf2sLgo4zS^EKIW8gEcVr?K4wO!rRr_9~;S!UsMfG`&Zx0od`6((dJoA*7%C@ z1T&`}b_R{cW*my~#;P-I33b)u<8Re|$zK&3KI}W31Kp=t!*q6G-jH|QZ!3DRp($)u z=>l9Rh#iedIMbH1jqGUWBdX6JQ3-FrOB>lqwt2iQj25Qv2?SPkm(*KQrSR7v!BV?4 zH83J`asuS>S$l>@z8U#W{ABp0+zXo-Zh3U1Hqgnk^2|#+!^9K-D+Up3G-;5mV3TXb zV+5~e#bSdtc`)4<-U1{y-cHN3H2=Hm`E5GZHQbtJz5Oz3^XwDrqnL;=&$T${dB2@!3)C{662C`FWQ98U0D zd_qNNRQzMRI2&V~Z)Y@Toke3<1cUsp{vnz_l_p8@O8&xwmjG+PF;y3LG9o0~wG7uy z6}`D570>K5b&`UEKE&~(&X?5$K9>iS7t-?Xx+l!T`(d&Kbq(Z6(Kgs?vvuH`pu~`+=(#~#l$Jk}LIjUm zi%#Kp9Q`C`6m>rvf(RhH9Fn>IkVpn!%tCv)BX5PYSgZjwJt^B!{WW(9IQz4UmHnFt-=>?1thm#p!Qt$!^_;vLDTN_ zJB5jqE#92cIV>}DNgz5uVWzE*lK43fIfVtAhG|0L)_A0^Sz17pAMY=Dg{tmI(uA)( z%u(J4Cxa`?AYb<#1um3O z_&c^kicEVi8AQ-|rE!?>xln0r@RA2yY;~7JSiOwtw;F;=hhe5K7c#al?Yki_^)D!9 z2&xSIm!0|CBNq|yJ7(ly>Q7{1vP=k6gGw&(Wt94$kDagSm{ntG5UMLvU%Rp}dhrzk z@pfYP%%E&c?BNYm(E7F@E75f*pMsi{vd2qVRP?a!7X*W;LD+gPpvTCRiYZb=x0IAj zRMaN*eyT1&wd3e(*cuT(Dd`38RnHL&p=b$Bd0of^{X$*=0q&7%{~FgTXD;I>iKJe15%Cn zX#VKFO{pviuU{AXrfy$7G+bA|#Wc8msZ+Gvy6{eby3(n=uFEY#TQj*{-dnyaa@hNm z0meD4t5h7@Wdjm3kZ79hIzVZX&003|-aI;u?^g@s28w|2OnL78_ea&wBZ~XWi|)OM z>^esFOGO$xB+fq6%{UnOZeCdqv}J6Oack9X54pwHmX%m{lG0=O=nS@b0>R*b%kLgv z8odAhu~A;z=+YZNg!!A)ycrL51UD3Zmsl8Al*eCs~AIK&Xos-{_f5J(P>@BiB{Jn@tU!WK0? zS0$yrE8!9Y-@0#0%K+lbY@_tSy()77yi{@qNh)8wxkyHf9BBwjS1QNHwzHs)b$U8# z=_cuu>_%G?2kZ?6B9}6xci0_z+R9f4vflG^F1?6Kw|JoHGClY>@+6`e7uZS!h=!z4> zY7KlwE#4S8ig*=al^I0;+|Gas`n;w&rsa`32BH3~MAhz4t91*y+Gm0;(cczS6+EXt z*Iq5c{NYj_o1<;Hd4;d=V+!>3(;FtdnBZrU`w4Bl2UegU)KVa>qMB@BC?2{*;e4ZA zxi43w5%l0+T=o`>&e_h5C7LHxFH^;Q8ZHo&;{yrFs_9Sj{t;I?-i!E5*R-Rtcx?g` zda&lHiXX-n5G+d>^}mg z-EG~X?AS+Gy?B^oo`2vo`gs-^E>Y@$gGv%ZQ>3piFv0h0t!kV(dX0#Yj8rg-aqZhOBB3}@}@2kam>bTQNr%8aVAWZ%zX?w?*(3QQwAKUTPVGW2oBf>4Ig0gqs*t7WfZ1YGx`h04F3eL%lz%a2~Wl z7>TCFT~($NZy3Y9+z?SUTCK`T_Q|f$>pmB#jlLQqvjIXK%-#8-sUBA&I(p{Fe$qNo z1M-~j|LBuVeN?+ZTexhlD^R~4CJMoLde0BLKl+{`%jL}0IEiGRuBANh(~v&6WRTuC z=HYtdSXrW@n$oT)X+N(8z?OiCOuX5x+c(_&p0>Rdx^SEI${&00T={A?3iQ_227_}K ztH;N?e)s>awfVz^9DBc0VPW;;yk5!M(}y`nvF*mRmI1$w{<}ytYl6}mpUSFWxHS;D zZgtM8@Tm)6j9%=kbvqn!YI5_}3-t~p^gf5h_BD6kqP)b6mj{@oJf-*=H=D~68mrtR z%Gh*@O$|8AlK}V&{Vqp}PR7R^=`Js$n{@peErqU(%+lOZri~>{Ci$ zcDIANo^=$~i+a7odvViMuURbm4Q<^9pwvyacZ?WQ3uHE5EQU+K1L;T zz+J(*r5;kgy@*vO?e2dtGfp#iwo>Y}`eO=lRUqQQ0_k?aM$O!avni3rmi(WwX7Qi8 znDDaXd_CrR8iVKdAzI8?Y)v0qf+Z>ve;Dh0Zu``cq*qQ3!H6kg@?{z&V0uT*UMKYh zl?jo!Qp9$4s7v2{6C7R+a#{nSx+7FG^Cb?=Z*;DR7u5F zwY4wOqFD4ePgKd5PiWNz_f^ZN18V~@RCppYJ|1^b z0)!Yf4jXng=@rhd`nsW^&-`_isgU$WAYjUtnny)aLEH2>0~RHyBV7mY8XBrYi%l_~sc?z^&g56c&3rA+%QU(N*0 zVJJZ@>efn4a1X(s=4?>V?4L+BEJETi+*p<$B$NpRqv6#AR(_AvL4)thuf=~At^`Z{ zxwgp_6b$X|C+>mIoApTqkOl=rb&m^e#H0`zP16T>$uW}MdP_>`(1Y_FzR}rh; zy$FIc+EK9!5`hCmW5mU#+68)U<(v?itH;vTYeujSO09`JX&b-6UOM#5zCM2^2(#Dh z;(Pm^j#b8daR zi%VpW;WtQ953bVp!Ku9o4iK37?7wCzokM4|mFVb7e;U=Uly2dCL{zGFbTL8GT^wpz{?aG{QDwSg;jD@qo&o15e;J;6v>kl!x_t~ zx;rBbrRFV9*QP`~6lS6^ww7sg>f>7`o7}pmns&Q#C31L2a}mkxSCKtMpLtFMA{nc? zVtK770_-MmPOM!gbIK}3Sw~m`8CIC#!Yh3FuLM|^4|Z_aA&^SYQ@JS2KX;`i;&bb|EziP|F63>&XxEXXS2gPQBEaJ zJqdYIS7t%Aw==PIx4iX~qlM!{4k zIhs45q-IMMqU#GQnrvR_j~(%z0*FcU^TAPq9k7r|)BD!2qlu{K9Vj?|IMYGhuKh9Y zKK=m^#=+o#(P;VXet!W2A}y<=aq{NUpSMlHc_b{Ddj zAiAvug!;!83XN1TR}O`!Q+;e5=!S1CGkk@@w;YSNpTW0KiUk<_145^&!K+f7(xZjJEzbZUPpf#KT6+KhjT1QBViNAG!%KUc8qMfs}Kx zlim@nxyzG!;Jzl4E_(j#(@-0TfL8%{8xYK)V>wd6hBY+z#Pm?QQQz3$0E@mZz+QNT_dLPz%PDWYeyBov`_j{bZf#p3{}zU;^uL6Ngu$4zh?NQ~C&hzGkmy zd_6OA)b8ZvcM8lRE=$5gU@2>6#{=uJq-@mwn^Rwd9Dez;)bl$=$-MID*%F_BC8!GyNZd@g+e8mY&I4z{rc-1o!kT(AEPMww6txKwW~7WJ%VHfsR3F^1tDo zYswvcUG|I>ke>@DwfE^nW<<+CinS(dL1B}^_Ew@`W?)ZGX`&HPzHl?)_p|u0RLR~g z(xu@#WE%$EE`{k8hF$tqU?k$sBgc~T-nY)%Fciu+HIKe(i~Rt!X&m$A?k1GHK0Xjd z@H>xQ2L$6iLR35&nCw&~j~0JO^;UrEgmb5J_D37*B7nAn(54^T(2e(H(cEBSpj42; z9$ zBW8B#R!@V1T{H7&RQKX*JVl1l=a(GTv8AN}5>FQ$99SrrBm~428@NqllMe5Ddp_rU zu5bqolT-+^qod1I=7)#|O_3_l2F}yKdze@1ydswo6xqObn!EUEZ#=>G@PQ#WYdc<3Rv_)M-4?c=q3V-=!YDcAu$nlNhkR;4M*?0$?Z1gKpiv4 z4$zQpQM&?@{GatkJC&^)h%gJ5$WM(}TACuYwScv)ktV1F=wyV`+7uiWQtSFE4{>Hr zVUqk|rWs;PyxpH+$2bQvO|F4+_Nr3R5mQA=iYw~_Rw1YeGH&Omw>&YQf(e6Q!__{*@G4$y0g zr0p;_4Jt0O5cvzyE%6Nf%knl53`|xDa;z=evhZ6ZOjUOUQp(#XI!4EeJ{JMpWyIA0 z_UB`;7_l&ZIr55S6C+{FXvUSB$vfy_-hyeMp60D+!wVngeTd-C_WG<}g*I%?W-5yl z@lZj{uhaYicZeUiq*nyL54fMN8HSc%+kk>DkKJyAd#tV_UDGK1qP`s%Wt^0OFR~$m zpY0C@q^XVUlg135J2(koP@%@hV6L2VMVtOUFmeKv6J&Sr0ynO_tGxzQi5Qqh z>BOf+$H+*XTQ~U|H7k+YF0k~ILzhrsR)lr>i{C7IhH%^e&?Xg)2piR2e(`6(6LmPk&WFr*_ES3Y^@Qk|7-TZGV1 zD-7;EBRPlGrtH5fa`U{y7QY8xIuQ~Qu1!Qgr-CAE?kcn5&)57*uDK!*Dm6{xQ`jXl zoC9?hsH~t-{ZR7zX$U^LsFv%et42Mmh$6p)*H|2%=Q9sPAZS`^dS9k}TKBCTcSWR3CDn!k|k;HI0oFO!~W-@H)4n+Z?Et zcE-ICk9X=g1F3D+mA5>ptsQ^zT7zklI6e4=dzIg)?sgVB$-sN0!T<9)EUm-}_y&%id<$Js<@H$(IvnZrzop_ z-J`l4=)^=f5!`c8GVDPD)<}jGFp-Xh>7&q+Dwt5|G`;pH#Rn8vq77b7-rdhJ@EZ^t zkq+RV@Ui06!Lcg>Z8G?SI?H2Hh{93Al`KM}+GPnTBqaD9N7ZYAoosLf4mKX_=ALZ< zSW(Bfi_Mk}k066+MNg;MDxWPUL+!zu9`8;ueVdrTCBdHn-$U~pzBLujdb>#@LsVA7 z!Nn#?&SzH~6Efr#H@NiGNe$J?320l9=mjLLrHgseM-f5!5xK@Z_LM%&V~>W|#O1R$ z6WDf;g@WVtfELgE8mUW*Uk6(%_Fp01;~;*cqWKvn@KG2m440cO#=TcYwPEaQA;%Ae z;ixZ^23neAHi=;{$SBM8n{QQJ4ne*Toh_$h+>7?~&^m43g)$d86T%dtq#KWAY?s3? z5zDl}_UVIrUCU+%m2w?+TWx9TVhsS&Ks(wm{Y4h3z0M?XWbRw^Zz~A1zK@F2G9|x> zzjJ23HCgVgM$u~xIoOR2Z+#`O$`9Tot*V$5ZgNYn%&SnhZ5$o<7n)vm`^X2i9pySG zDafRb#IC@uur&`!f761`qi&eo&N=(!CVKg-BrqZ&yhABXx8)mx)RoniD`M@B2MLiq zH&NkGI4^2nO0nyAtKCq$KPyQGiRSCX1|@0S$WrUFq@S>i(_z=kEkSm2OK57X7QUzp z3aNMEu|UZ0($l6xc{V8*Ku=$YNf^P1s?)XaH!H}+Z_Wj+h3Ki9LD!J z=i!6sNLpPf)?!$)SZwm*jfj@RW!J0hkElw%qoZ5&T6#*(h}Kryt_J^V9}ajdO&JW1 z=P_O`sF#TAp^=rb{}b;-LQAGBU6h{s&HoArSmG~Uke$tdORik~gnY2;ZJQ~{A%Bct z2hqW9W`YpBP}$+(rh|V>_8x)u4t>Uf@I}S=$t({E*)@%Pp-c3 zj;>Ft3ENS-g$=;xbt}sxbY*3;EgWlc_qi;!WxX#+bP%2!r*3sV3^7f;UP<3hZ(Zs& zMf=I7-Rz!SEhM}^aZ%c${6a$J_i_uaUGcIR2A1@wM3=8Pi{Yx&8m&>Dop7=ZDpU2$ zh|nthl$;|l!1`pVbVc>sTI;&}W^`?ObkyJJpk$dG6OyPb+k=~I&b~w1E)%ATQD`;G zp$}^5CIAAg{{Ao5_|wvclTA^LztC*Q7mCWJQ}^B>0yFYq{ut{nD%CJZ8s%OwEkqe2 zPS@uStq9Y2`ElXC#>1|gZ>oRQ{o;^*W;rM|LqY&hLCH!RCel%ujSH@4Yzg`5X}}eL-CW zl(#UcHyyzX0R{2->4$epp9vA&FcUDgM}tK@XZx-f4#Z2I)$xYjD)_buO?Ms)1^XHz~ zDQ>)!YEI5QBB&<9v;AMgW2t~+&V=c7t^LO}-Gkh(g#t{48vXflD2JCdpw8L%M}yoh zy|O9T*KK}I7)x(!3k8JJ4i&a!^q~dPy>Q&~1lA@ycI)A_uW|A}Z#=2G$l{wpS!9fL z=5nfpVcu4s?6&S)#1Rd8iom*oHz;f&%#1z6ylw}p!w}8A4vu#_zkXI{hckYo`QY~J z!|7&5`f^T!>Kzblz(6tH>?g1Mt3N)6av3P-qj1Mso8qwfrj1bh@S1Ppu5;m)>)`ye z>l8jkZLg*JZ0dvM_!_FaFY}sB>9w&3*h4Yq2R-9wCd`)iTbpNUDIIHioN4iF>OPA1+q&0MO8UiF5?cvDq zNaK&tVTMd&agL)Kt5ZD1afx8eCjuQ8H_M)up~>H9F<&Gku`%ASzeFa8(vvp+YMG-5 z(P#{T!?B*>dgpDYR(8qIEYgqBXD58)n`a~)6PMUVg@gEbIRbuqWJ2o}+PAUbcdR1r zD5HV-Z^UR)$z+@uzt5-nvroDTA@vDKn!cl#CXm+H*7a~z+HYU!W`rvQvta+*B8C$3 z!_|{>j8VXOz^O)y^HmCTMyHo1nJZpZ^sz*=H%w9fgTHJ-$X{4jMM5FF6+IdU?+6-d zA-kIMhl}>ByjzDMGzj=e^2SG(kwFyvRxHEJQUi8hwix$|iFAoTf*fsVH~`r#QJc4L zi|@LiC^Sjbf96@eBvgf@aJqRiSCz(%R!vqZ1fvQL*ZB-Ceup(39CfESy#6rcb@tLd zGfu9qH8`H7VI)~rA7M+@R{lHPz~-?O;unFIQq?ZS#IJR z(2(yCEd=bvTEGPU$-vHyAv~yRyK_V@MLi->@!}V~eLMULju!^{=r1sp@gYnv@eHQMit{-wLTM}l=8{|E{<6y=pc zh`EKY(c>xI7*p?7xN4{HAaTGD)8NdlEe+WC!O3uBI& z4PsQoZ?&I&oK$gsN%#T_BhX$d-N~4-pZnYM*=H^+v>R=N=Ct1YO)^D=yFk2d#UTLEs& z#bEVzE{Z%CMaFJ+qu`W7e%KwoG10QAzPdewNxDk(q!hpH%+D*vm!=uct(s@_<=bzB zJDj*p92NVNL2;aL8z-vc`xt;K@dQi2YwKM=CW0}dcG)Ck-%#2{ll~nk7k1zK6`pwc*UjyA~B&b0ju?7uC0GK64#0O&a)iKNJYf1bpBE;#NA2?v#2AstP%ERx2ltd>0#|v$ruGDCieQcUPhi( zRkoTh(PxhCF~ih6tEYh(4t`|5JnPqYh7EI!mg=?|XfT)j88p}>bBr=238{9_Zvqy( zt;)VkQ(QN(?arH-FXS#E>T#}l)n2O z!n^UK)S}6Be+o5B;?^F5iYo$Z^GwRfINdAtdDv8-pUc@4I$~7n_ z8^Hbr?j0ZQ1^@o6Re=MU-d2a^${(Wcz)KdqM0RI~xi4etQ&s9?%RA-> zk*Q2!FMjCQj+R9!prO_TDu}@*#;na_Vf_m8lOt=TR)0lulbAKd5);b1rnf2yF+$pV zdzqatw=>mU6Ro%-Iup#{OHChhJOBeJ#*jj#5;vn!S1=^a3aO%z=_Rq)k(sJ*i}QEI za8IO+^~Mu_zS$j;58^F(Jt&q%D@UK}(9vt+a0GdhTv4y*o7O%&iQ-upeu%3_eN^a^ z-vxWe@wNRGmB;ALqoKmkAAFIuzSW{e(40YGIe(TM>2SHO&HfoPsMad{Ct*_wL&EP{ zC$mgQhyv`TmGeX{N)Ejd2e~eq75JW(SN+0}uckb~l)MtqQWKGX!Rq8z;)I@METB~u z;VOwDjl(QOh~wW?SaB%skzHTT1q~pg1IAMEAo)Kw(AMxB#_+*o& zTAv6rO^C?ufrH#gj~~)Mo*;TgERSu9{u4 zL-HNfDo8MQk575tWWE3LVtACuQY*7%4UU7oMk8q%HTo!KqQ{YnBG~Kfc4Uxxc$%BP zf%+CXIui4W-6^T_0r7__n+dhh>XdTv*dc|>>U8<_=JVX~jXl%P!Y)WK#`9KkqhW;? zU=s|RPZN^eRWm`@uX~+;3#s?%Gc?kD8#<9#UGt&CGucEt-nCHA+nH|fh$gF2jT*1; zep$kCh#a20qaxMvwU)x?hwQeN2|b;@D`#RoSu!u?>o2mmc-3!K-J8Vw2SdrR+$*}9 zglbb}c4yA5>ZC2PU$x~N>~137)6HXNRK7GDHp;wAvidb%$we~ffihlPwKNAiV^n@y z;dtg-*!L~v;SK2){#9|Sr;#Etc9(Us+4G)EHi)59y4}QxVaX`*R1>euqi@PaRe}qI z^b4!ykDyA4plrMGT#7r8xTW>!Tz-onN96&0BG$c?uZLXAjF~I={nL4=L?Wrrq3z#h zheio@D^7Py*x1|aPo1`|El-c;#iV}X+Y8crU&8OBHJ*;=g0zR*^>VirE|ZqG48Xdf6R>E1B@h7 zcuXl*8)?-P>b%BZV)f!V@(d-)Gvt}Oq}`e7Kgt9#;jq(L!ef74ShA`=FP3!U`}^5q zPhQ|(ag|8Fj3klcFm_JxWD-BB`}0~3&TGv-qilTHbVQC_A8!@Duo_O(ft9#JMtn-9;7(NU+%~=dDQC8W)JPQxCN+jAy-Zgfxkm%V3h}q zzWG1+ryL-UdC09cBsAHNhmU`|SI|8E-kFT?AzMZH@nX$?rL40gT>t%cKT;NqWTp5% z1IcRPFFsB5@7Wa3w2~2VU*R`-R8z8OMZ?xGf8N7QG?}38V$>P>$5-#j)r;|bwES4G z58oadj`#llTTTh|r1j_9?MX()rF%aGA~U}?MX?b%MeZrbkuDb-)Q*;y;w7HVR>sE!}u;^{S$Va^yw6>koMb*0bUfzT)A-#beXs3zTeb> z{drg9U7KFLy%}Wmm{l|8MWbGb4Z=H~4ZXKfT3~qhJMD0`N~HQd3Gxj5b0fe$eFB>C z5?hoaE)_%*YEtUHgSz}Lf8K-lv6x?sj{P4`-yP28_rFiIRa#Uj zN(rTQsoGm9T8f&r6}4;cm1wn9HA1c0d+$|Ss%meFnn47yM@W-M@_pW)-*x@|%atqF zlk=SO8uz(h_jw&`di3V!Q9I5|zvzW~9s{k}cLKgFP$|XZW!M9ry+0vW*$cR=x>B@o z4A`XH^+-g(4oTQ``qNzJQ%8f{0Eks;(}-fMPNd|zfX94WqOk0{?orXKCfz5 zf#KNz_Fsp9^_1Ta&%)A5iLtyu@RLHtzAvtA3=gGL(|uMl)kRLd6uDBgSD*mecXaqZ z{EfRTnc2Or{pKoqVIY6vO>(61t(v6zwPA%_hDhv7(i(`Zs_j}@1Wwg&N`S0r!N1Xa zt^ZCm^-ug7-Q%b0O(lb)Quc$fH}8g2gn>hV1rLR4Mo1XXh^S-$txlf%vUX6dN;t9n zNi?XHD0InQ#gRB>Zh2_)tdXj46R(g+6(ySd^LB5UpVxp-LoT21<<=cg{mbd!e)~Ks z|1w?buKU5t)#rvva-3sT9zB^RGos|vJ2=23j2`Mlpy|&sE=HS9$Y97b$U@MRb0XuA zkki|s8!8!UQwlk6YSe#{^d-K(<%Te`Htt{D9Z<#gTi%P102F|ToMPWg00`=Tnl&fznRee8jHoNM@Xoz{97q0@$mcv zE|~t->0%AUkuw`{l8j>3s)z39HWtFk-h`D0%m#9Ff48hjmymk|2Lc5|jwZkvi6 zZkxA!A&zB+Vp|h2--ovIVGu#D^MGcTEr+1uuuow=hRDWp*q*9#IuW;1nOy>O1!(c* z*uO;JM$nmiGYM%6dQ2QXh>L}|xn-TL>9xh_?7H>0+0EscAD9wMs;` zW9fk9s68cf@y`b7Z4Kg}`g`_D3$Z=Fuiwm#1&0sI54gN`Y;36bdO!bUctmvNueY)t zi<|AOG3+NXX*M@~8|i?sCx=Py;-8lXI3n9<=GG&jJ)_s<&#L}SC&LaZ{=80J*0bkc z*(VDeqEY)AZ;hJ>92YBF5uldu7`lAY-Q7!(C(2qQzwm92Ho@U5x24qP7a7ja-!D_T zy6OFyr&I;)R4K+`)NRs+HRsHe?I8vURgi3WaK)wiN}XF7;68=?h_tmpkzZ3@i^ga! zm5f|gWqF8qMBaJ>C_9A@8~{GY1&4MiaoO}ANzundXUN-)>LcQx1xmDY3*UEWJ;I=U0wF=!KSHhW(6$R8e7SqqH1HV5+8De0)*1kbNV!6yMybZlX;a zZK;FmDP83udL!XyG(iM+_yGe&=c3HqQxJrv8^=lzASwCY^<*)i)A6Vot*fP?x*s-XU$(_Lr_`OW~I+ik)4J3PqjiV9|_~dS&PM zR)s~pu3l-{_CS{SI>OoGgxB>ad)~QvCgV}$U#O`=Wo3ay3 zmy&I!Iat9GpO3JOa$S4TGEsxe>z1OwI7x{*Ti(_C`?0$Mg-fU0K%D~j6OOJ;SX>U# zv?3WhUF5F+5<9;P-)8OHJz|2mGVeZ8$v{ivf9|vAF#MZLtb@N>R8ab-N`-+dZej?I zA!;M6o3)&9s#|?OQU9Dw%L{5!NH9c znPh~Ci;2s%%MAkZk|qA$o?gzof6lvfhKfuNVj=TraKy7ue@vav zPeneB?}VbIKWSZR3`qAU{W^KOJ$b0lN&nF9X8D<_8w0~(GISVs|4PxwD8v<)Y}2jx z@Xwh%F^o%+$-G)I?)59H22!x=pH*^{(9Ze+L8E-%6{c=Hs(+`N;XO{}JRRG!gg8jK zQ;2ZiMrm6=SFIM^#H&L7mNtT8vAJVMVO(R(i*{VA zOuOM+qs+rTM9Zt{@pgeccvbb$z_^^YIz>zr<&%G^QE5l<9;Gc4(qT$#g!PM$m{Lp{ zG_buu<3e&jkhNSsJU`SP+d}4&kfCKmBop~Kl`RrVQF4R6sjUGoztm@ZeY68z zunqCK18eN2d}%L76%fBoIRL4-@Lk_n`%@L5g8OG!*SXNWtnV$4s7!X4%97V2xk3FO!!!#CT%R`aa_ff@P(N(rJR zfQ(D&UqTjof_yznc}Sq!kL1n+7CR`JIb52257szQve5?N3r3IEyHTM5vyW6*(MX;T z#8ow&T>J#S#d~SJ5SL$`g)Sq>z$J*j#3BZ-(r}37r5`h=4lx}$kHjzKX4QgmnFVSE zUsm6``z`8*hfb!T^ZB-8Kq0yZ#+B&7+0NBca#Vw>mX=~$UBW5^50 z)T&0|*B!%hxD@%c9(ckzqTuB7g`@nn=Z(7^YdSO54ty?QZ)Rnpr-ORKa}CTda}d?@ z1H*`FI_SSBgcz&0cehX39xeIws4FvP9MP^R7cKNJQS1CR;YG+3@`0$N8bMa~eTZe- zvdj*pP z3FRGVG|O%6TEB>067qnycC8`Wpz|Jjk7pu@=?qwQ3Sf@~*us~b>)wRii{2`_uJBbb z=%|>{1(L(b9_{6&poj`7xNR~^R}mY@yEn1tvNS{P@?#s=AQ3CrSJC772zpNU%hB?k zD7b72kD+AO1P<7tydE|@MT~l;CErHI7$PC3VGR)C5dH-z5`~zYFVQoAOOT%;CNz&r zWMSUnTe$Ia6bzT8X|`iXT15+ZmUj(Kl;U6j=m7&Tsqs=!1$PDI3G5gb!3c($$@mlA zes0|-Qb-t!UU--{VG}nupav2xJ)QJ_`~Wn*J?=Yl8rG>zkehd!&wCy+&2i0ee;V!t z`dV@_*k5_y#wnBw-8ReD?^_5vgF@#!hfh}M>7&nvKuMXOEvs_x1d7&)PZJ5KQ*Re@;I=plb6c2Ab zZ}XnA+Yaj0-Y5l7vfq|^m<8XY0v&Q3BSQR zq%atDLjNR9xIgnOa|N5mkT<_hxD1q}WaA%>Bu3)VmPP`zF zp15yOVwgWG+Fg9c_b#t`Lu&ADD;!$kj=1s5J`X51Y-WG~BpRlKI}trgAWk}!)HW?0 zFp?havt#%^cWqrHAek?+7Tm1ib%F1c4$s_MqL$%@|GD%b6nZk+FORAH9g+e&!u>ff z#=C`eibH~M9ZuM8Nh-PLdSSy)BTv1m=X}GuqA>FCKl7cf5l_YN)t4&DXPAKyy7Uic zRs7qSZ&hLtEQZ!Jw^6oC3PedW3^@wR$tk(c79;w?EI01=$pxz@$)3@J$7KJ zj7da{m7W(QC0TGgDs328W&s!mKA(%+Gwx)8@H`w z9U9u%t;B%~+CFIGG1}LQXMT4TAr=&tgQ-2kU*qZo^oWVUw?4pSbJwFo#zlLM1e4E; zyLpp*h`qh=Z@F=>zb~?Ced(-@8_z1|CzkfksjyGLEm+gh`B4yp$co`>hfNV|ffayc z;VpiFwuny}Cwk%9+*|aDlS+c;6%tYX!C^Nb)lBaPA;QokncYy1t}Wu4@7v_d~> zcCE+8l|)LlZY%B>%de6CP?nOR6j7)>_FEqoQMsGx59H%CaDn%Bk$YT+qB*Ou1|?Dl zq`Eun*$jM8Tyz=4if%XwE}a@CZl=@e>R|$>oaJa|bFKd0);C!x_c5M-E6^xplk#wC zS<=M`D=}sX3np4CQM#jq7DKIbCZw}%x|DQ9TLNQkvUA#$Y|+OT844c()SQ;cO&C6z zLQcU0L*%-Im8}IyUY$<-v*Khfsme={9-^m4uI!gT6^Ec7_)S5#2!}Rj=d2L;9s(H* zZq4};fl0g@`BSoc!)Y|iJq-B?LAU*S+4peoX-zKv(Ss^cL?0B`FIW290GKt<(|g2n zsWIuG#xXT~ibY3I+l8;dSjG6o`z86-2U!sq>@I+nL!u911&V{>_I~Hz_TP6nhIcAp zj$~jmiaub;GlrwaaEY8>Mkl!6xFT$3r*bC@aNt_amzv=}&&e$4Fz1J&FqQLAh^+Ew zMJGbJ1-oa8-fZZl&K`X~Y#cuBsXK8TC8z8|Z*&Iw&U=em?(1R;3kbN3oH(|JnuiC5 zXFc_%+}^$ww_dcJryP?nrL?LMYy#2^p~Y(NK%38vYN2U?g$5YjkTViMrd$Nx?L^i4 zVXa`X|8CjnHJ_X4`p}Mp+Kh(tV3B9qBJA>{VDO1j%E$es`edAy-Z?HL)Z;dCHQCW5 z3ZQpT>jz^&DQz%C>kqnpk!Ri_1X<;v(9SNuVNl1erhXg1?^MA^aM%R#M-yz~smfVo z`xUVKsNvMr_qKOhH2Fmyu(_FRsu`#LNl6T$lhearrwa#96`E z1mGq{U{{}u|HqOn_P(5j+&@xOhJX-+o|Bv!bJl#mb2h6`30K`+hW1?+qp->D;=TW_ zX2)gJ+`fKrKUw*6!F@Y9WJ?9CAo72WnaqM2G~H$F!Sb7>MDDx@ z_ldM|{+O*!(wy4&yV%w-*5WFJAU+E!oYOB+8f$Q$3JV_fOwc|r?8X30XpwrM?rtxe znlj%4A9Ak*Wrq}VM!r!P>uTIli$EuypIn557IbUSUB3rvQueH1(v@@0T`^L8B}jQb zqBE606Sc=KZOk3pj+iE;&kCrtS9im&d1e&6KU72J0Fznu^5HM+adpY)xoyJIi|~LL34ouD>Aw0LK`5k{b{<|KcbS9Z zdQss4Kk=A??wnj0=-{LY20XbD22iQ~=iO%mkA9Mt9w^VJ)l9s!X;UhmIo|x|Rk-K! zEawlV6Z!4w8RdcD>hIya>cU-8C9#gUQ84>7bpN`W_~6NM3dqKvC;|9)tMdSsR&Wu3dX1`-87L=cd6{MoX`)tw3SBY9=&9^pOsTMcHfG*GHjSba%w!xa465 zfb0U!c~w}g2vfCzuTyNoQMlx%UnvPEohJ(ek&{p^o5pUh@IxF}5Nc(0?K#{mXKydT zsgjOQfbYP`nv{je+-os|Zv{CbV)nQz_2Qu#kZsTiRAW|vKyEPg!b=O{OFTkk#$j%K zknfuaO3|G+#39Y%YL%2!zv!Wu>!{;?8R9Z?eWndll#u|Q>n}~f7@iWB7jo+K{+3z5 z5ypadhX#5t0L-*Nr1fl{2JZlv=im-!*Ao8Se{8S1JK%LG;X4#+39J-RrHU|#T+H0lq?C-gD)+n0-D{I#wu_=xo z!;AKXBoEKeif$KLqp-16H1;AD9)5b?KGPEyCbP{v50`+QV9UF5iE$)# zo66dS4%>;$uDcfrP$!a`0$u3X{==lB?;xTmVT~vTf$t_E8j+Yp>2c}q)8_^mT`sr- zaS}pbS1DHq`;9_kQv>u@=q0FSs6MEQU*!Y$kD0q9y0u&mhX&4+{ zppXeL`<-2~u&h0NI-T-vh9Ljxb`j)FN#NuNPW-^Ia2tlDfQrOmzbrM5gPbVCCMvA2 z6q(KDf`g3Ewc*gM?T)-#5M2gFEI?EHPFt@{N%~FaMs=4ulh9+_klYlfbKBbsFCY|Z zo-@i~^L)dIqSu#PCau?=i|+w&rcsIHNb2R~@3q11L9lAe$_z+F0Mp-9+`ilh->W@_ znuT{M-fEXedD@mm&dmIm5{Mai`Ac!@oHMoK+!hP~wJQiDEEnkrZUNCGE#Zwme0y#3 zXb1H_mV{ebn-V~k?FzDrU68JB-(VMLI$`_F4Wsvw5JU_=Xv=~J@~^<};0TboqeieV zerAb*$c1V77_NV<7{4@{yK$lZxAsu99_xima}0?h?4f-~saS4%!oWxiHe_;%5aXfO z1{vu`9M^t^yko5u3hW31HC)e92MW~?gXX1?z>?k8Cx&G zx_JdW2R=5rG1Dsux)ofJ^`T#e!lRp^%xs(Neh}4bmD#yLn%PG!dEQVKX)u+{wNh)c zH&ND~mMUByy=V(dy6~}&eY9>@`YtNZR{Vo;uiygJb(JEmPxyq%y852yP9K|jO)291 zG)%d(>3=*(O~@;}r`LS{(Z+KL{}wSd^=EAF_YK9?t1|ozLUPDs(TD)UD_3p`6}+#| zt!#f)S$M-QQ*)b{r>){FGUhT2bY+EYFfL_rMR;pbp7U4DY8C5WJfA-4_wdK@hK3J< z!tH{M@ve!MqgJg(ruX5kppacD+~eL9p!-P z9=K{FFLC^a8*Z_w&A$xz=E>ZOh8%ZQoS4)V$KYZBK{Hfl_3cIUD(%8vbv=JXU ze)>3vv@e-0>VDZ~{EYuLtN(Y*6wf!_y5?1ObB6jB+Lqnd@;|jL>3_a(runR^D7VvU z>hb%5^U$Z4G!aqF4_KN6g_A$iCSQ4rjMPw_8L=%gPhEY@`R?cOmw@$k7)dtWpiukdSaoYkKAsc#a;KjV|1l~&CV&FP&QFFzs^?_tbF?Ly0KbNnK|Zv-;-aL2zdRl-WG9N#9zop zLvrw41ZBp?`(IZ&MGMQ1LO))3fu2;+{+ju>xBtq8lb^!x-X|}u#gQlNH|}PiCR9E> zp=Nkf^Vzt;yHFhEm?KyMv?e0bB3z-|1B!8&=x(Ik1ddsC%+S4=>;sb05S-{ zTme6(SY1}5+4Lq?_Q4;^Q95PGUpsm;pH=@njgTJlX`xMV?Eev1w&@i6D?+1WD_%$S zlWN{HuMs~c#Qg&XxohX3>SSj2v{}{D#6z-2{Lt^=pZxnET` zt5q_#hpPyw8It}q3!GsZ4UfBD(znYu-Pv7C##-1hNI2$M{rMydx2CCl`>Bk66(#i| zEWkO`s8`TMaFEf+zE0`s>v=W8h`=i~z!<+Zt4+FJtNTH&lO}fGc#_pJ7rxa#v!JW7*AHf!)ua z-tku4o>RO0op)z#&T}4uNDh+^BCH)EA@wB_^ohtA5Fr zB{)XvDeTddr^TpS0S1Q3u((A<@>m_sRN~ucX?0+cSZWRGO4Nz#6hF6EW}sg9x2!lo zQ=L|($L35n%};fG7~>(wpA+YX?35A}d9lT!v3udRuS-s(Q~h~unuYBZ|4Nolb_mEU zn>|5eB!@N##7^1kfLL78@ZISI5?7^K1ZK+n4jY;_q)upkEwqvT7Pw+DnU6s^d%zLdkS%@O(ct4BO}Vc_{P4HV&ur7%Z;0%BV0a?UD1u zeg4!jfA7dwT;ExICg71LQwXsnGaEPqC8S3vDH0&@!#zcYGk5vJ&9DfbSXb~ zlr*zCDP5wY3IDh4@vr)OMwd8H5EmHx{!^(522%zzrX zooIt1;#VyZ(j))I#K4Vlxxz@kIOGc`9**I8q%1J1$2yW^FabjqX?jp@IZyP}$)Lt} za{1huo=&|8*R?5yWF=ERM9T&b>3tnf>IQ!JmiH1M=$mpGGx)6F5E3k@fl#!kAV9!o zdaybJPc8CLc2UsH(B$ss>#|scPbA%0g!?_|oE_N9e(aT5J$(P&#Y z_|Avimx+81$ zF3YCv;l|Y5^tbNdo(S~EJGZ@T>R)L(FOx=6oE|CrK822}^*ujD(#B;zy(d;uSHEo} zf0d6Jf>Q7M^HKZ!Mj*Gubva*5>5qsX_7WM|=W&mAC+kLje!FM@K!eL{otBhG1E;0y z8-}LLnBNTEf)Dm@#MLh>ztYM(P)e#%vefA`b-IikIWT8+geb6!T<7iy9wXbc==7#y zP0MYb(*nz37iQnf5tt%hUBw=7zi~-zOsPDF^e|n!7sJY6E*UG+Uoier92NV%8t6%H z@5zugjHDE~CvKgaIeJal=Z;Y5!gHtJ^;Lqvohuw03Ma!6Q5K2mv5)d(=tWFwXnv|_ zDhDr}OBH0Wm}s~Ub9dd_=WFF}3Z&e}owRg`SpUs3Q-8xp?kP^~#K1Y$e?&3ZZS>&k zzd+kN#tZ5S3Td{sANKf`uWFnn(+07vKYgQbR!kT3sciYHjid2`#xEVh()Gu!H)46& zwrm-h3$+-XU(hS;Mr5*EQ9k|^Woxjg9hfMFA(uuXD5;-yUJYVXKB9dN*Jl>0a=URw zIjgM~^@c`rt6W;%OdoM8J!8-A6U`@zH;gOGlymAD*Gmn$?pYPH`_Jpe-0`S5_Qc2s z2GPkL-Sai0G;z$INxv8OAdz>uYB)_l8=Bpc^;Lt{zH4~xwupn!-#wc~p`;12eqK&D zs>89{d4`iL5+Y;TW%s%JCj$*_No*{+BaICo+Esas7=PPFhy8NSD7VDdhPyR4U>>ka!tBIR=DQ}Ep=V$bqX(vXqNlOUiC@&OcwL;M^0%f8Zi>0 z5t(ei>&%6Kk~m!}cxdg)sCmZKqy4-|-pROQulK1p<-Qk4RG58LnAj+K_?t>{ZJm|w zv&PR%@{M{{%WZ=he4R=^h-AEj$dV zRx5lc5uMWY4v>wSnlwLosWv_Fm{p_71xk7Eo80Y5Q`-w%Z-Lf!UkSVBA8eMqa}lHa z58AlYN$jmWqzvr9|K!)!sH;QWXAM4$j#s~%HNga`|D&niC>{)o{{}5JA1|Gd;aymaIM#zA6vxAXVK2Rvd>9V zfs#2`0cQ7-zUyPj9y`h7_6W>SV>U`FA41STk zuGR`??xIJ^#s|hNbk(_&>a87Q7%5sZz9Z+~oeTkj^#1#bS8Do<;)gT36qNCllmGUl z(app4htiLqG}#UhQ@h5l@JgT!m*l-@hK8F-i!CNXY`AxC-klvgL}reBjsE6uf!^)+ zcf~EEtXC!%s|DkhS=Mzy+n@W7Z$v~Xb<QUnTuK)X9rqT|L`i$hsWa-?CW~yUij>~_X@cB6|DEyu-$xFoO z&nkc+phfd@0P2{iS-f^bR_0sItzLoBUA?^x`Zr@OM1cz8IxCqd#}dmN7YP3%1Q2mn z!GWsdx$@P2IVix{okQr?2&vdmY?g=uy8N_2S#$!sfSuh#pnU*oi+_k5Odb$_h|r06 zUb{3H6zw#PweE{ja%hKIaRBM3bY|pi>9N3a`-RGk7}(?1)1H6B;S5(d_2$BW!WuR( z2<{Yn)wr(TJpQ)j4JJkBmp}m+#SO>cf`W5Ey+5Vmyl;sV-kI_6-FfFR5X30nLq$i? zcu&7cJRpJ9_y<{3(HuF+X$sik??e*eQYiyRKp}NUYt%-%re0wa<3qiAM z;8x(Md3wC@5RsM_)hzul`sFPZtmYrSVS8ti8t(ML-96|IAQG9Xbp4lN(^y|wL3X6v zL)Rm^YBLV)_ngW_?nIm}bPN6@VSTr3wmr9{|C!N_{Q-Gf^ZRKolBd6G}t7HIW?QVEO7zqYBIzK!4GhhLt3-9y` z_6a^CqKWZK(AHxjzm9>#(|av&cF4>@zNP7AP~H-hCFo~5ePP+z#Q?2(OCZkO3XD&N zgLU1y#O%^2SFi_WP$j$Y=Hgm_f_pU!VBQ0>0XQar9@h1tW>-@TblG(Hp(}y zq<$BghhAdS;Xqu77CNnh6}DT%AIIP#DUI<5o!+4gaV-5jPc9q0$49@*bG$TWaqd0& zm!M$$bn3tgtuIl7c>H$(FreQD_Ng|@TZ0<<@F%Tuo@$HN)LTUF3VQdw@#$2KhXKzC zym0AY8?iU$s{M32967rYV1K=4vRkHzn)WHGMKn<$IAgk44+=vCxHVyXJ6xAJ{Qdab z!$&-0VwWoa-soTRYE)g;c=7NVc#GGmR&QBJgFBYc=ViOiT&lSWmO3+Bu5mk$UK;pL zb>`GskI`K2c0_xHVBNLAOW)vI$jdt|>#WLK2fn_Les^)dB)DL1?)W8J%K{5bRuOR_ zfzX6i3oX{_I)&yz5K@mh1fJs))(5%Td za>;QWSx={n5|8C3&qx(m*!R$F*5Af|QHx-rlCi-NGyH(3Hpir96)*n}}xBc@@8v5`RN? zN+z>Bi~pXaA}k0VQkj?VYk?Dku%ExZuHy?_KV9PId{fqny6J}?U;iq|=-!|B<)(vy z4`W>`9OmDRYa6$D9sM0^7EM@e z&bHrwuijH`xS8NF^fIH?cW0f~=}7qK>_u1y{fYDC%o&Ce1B$CO~SDLV0zc; zl}l3W_}B5}9XJYWqf0BF^-O5f+_CC@Z{%DD8L2cr^Vuv<+k>i1o};oGkKEyJTnpFzkl)l70RF>mL=z_|HD_9P_8m`h6*Np`2S+i_F64ISInuxqC(SoVzurC+L4V3MW^qBNmxx1Y4es zE|DyH`KNZ2su@ofvZ#Fd%-1CSs8teO)6<-&A70xheIJiO2t6};H;fXa3lpQ&p;gi) zUv(k^MUTsmt+R$Cz7s&;aJd`+$YtOUgJ#}T?1+hq@(hP0zf%o)($5F={f_>+mp!Nc z8M$uLPhLGo`%f7TzxMoW21pSvfI@BUBI4luG>hBnYp2pG$U0}9($~m^qu;Q6hR56d zSn1kxgki<)c!uWsazMD8X&m|0)ly`GORE|<0Z|5$E7t0Gni&1U6B+0LR!Gqd`ASJq zPCqT^t`Fy@ODTI|YOBO=7?)Z7Xjkolfzmy7<|9pT6xosQ9lqD(^@$Ur?ytB6JKvE_ zOT;zz?M^b>*`((TuXUA7sqh_PE&kzMF7DIxBvbWj+q(s4k-ZO+wuorWRdGg2t*=+8 zDLJzhZKSNoxs6u*T(S~0Z%BT3aDUBFHcM9I5_6wa=fa(CIrNOFd|0xJoc7IMc1cH} z5DYPYc-o;>HAkV;OWr`2Ix_{q(|$nOy{z5(*F{K}@;4Z0>25P|_YGOO(y2Grb~8c4 z-?ZM!|D!F%1)jZlKBTr`;Ylm6?`+jV(b5O%?ED^@xYWLvW}O{`y7OA6ML8sEdtN!n zJh0sfIv%!RIa%~w9Uu8&^6GQnR)H%;waNPPRJy_SI{F>ukw8}G3n}rkg>XWF_Ck^_ zLk%tr1qoBVB02^{zeaw!C%ZDf&^_B3GMz|)M@<^TK{SGaB(JoQJ}cHQ={WXOw(ZyA z@zi-s;K##Y+C_E7S}8U7+r>tTK&t+pg#q|>p4Gce?w#de$yB!Z!55YfD6$s&eRU6G zf@?G4tH0G{J={tn{g{+EJj5RiOhqGohfG9sq2V!h!fe6pUe>Y};#cmWvRlQmVI{Mi z8dnZcMMZb&)Frp}l|(abCbHjeiEN0xM%5+ttwKl_JZ8prx-z*d?_MI_tutmVcssH> z#iW}R-u30qcA38EO+C3Kn#t&~@EDL?m7l73zG&m;e6lR#$8!dvL&` z13zjeFg-_~%Mm;7X*C|c{YF#${tSLNF@tHTLZ11bjf7wL*NS)a7e6;O( zb*djqQs;5MWIapOfJ*7!skr6nk+!N#c@JfabbKGYq+Zz0vqSdg%iPReHo2#i%?8!c z!%J|!?ic#Yao3CDJ+UAT<$hf^Rg=MQ^dK`nzHgYdK}{wUH=THI zk#9`)^*&|MSt%9zL-U%lh?%x07!s2-&F{YbnU&&R%)h6wfF~yCiu3k@>ZRgnv4t}t zK5(^Mth+J5KDO!Cy~nu}PP(NbI!1ni0#Y!Pk=S7{Qny?)@{#u5zI7|8$6@Z7Tq?_! z(hsFCB0jbJ6hoO(*qP0kh=hYZNy+S29l0!piuW`gL~`)KUb5OV<#3sNd-Qea+19S6 zBHLb+bC7eL?rlDC->4x=<838GZ5x+y{S3P25-#HHKZHtE0D^ojmTr!PT_ZQ9>wj9UR5g3O%$PS` z^MiFX!<%4O{!~D+)z?V%iUaJk4NVPh)BDnK#Upx|W8-LJrM0}sTdK*MVJ?kh{-vnP zj@qjrVJD;rV>vX6{;bh17H1kZUNH*hViYY~|3Ri_r~J3P z)9geiqVu)Gn148g1CZfmnO%(wCEF^IfpjTXlG!wxw?uer4uRnnCf48jVw?kpbm%#2 zjkUKj+LzPcn5lJ^hy?PQ&fTtlN1N>Nx9sUB1GApS{z5K#hTOY3k#eta?Vuwen>vlF zpSXuG&*SwYnqTo`hRR@@-z_q7GA=AVQVA)49UJrDApHDB0=%AIY|pB7SNzrQXpWXt z_r)+CsE={&WpB-DCpzfnW+6JQ%>=<9Wkxmp_x1A(SphtM=$ox(VFs8J%3?5e-j&tee8(%TO1hC(C4KV^ z$eS0{-_m%=Y(7uXS3FOon}xIWa(^la8}3oXTQS^F-Hl9}0PSK6 zHs6seda1q`PJNO%-3|QJiu81}sRpUYQU;L6dIdkq98cy=6`@}ke%C|nokpderc{qI z)iFmq9QzNOHu>m>u$yYoikZKqX+(#!nQF8Nxa+GSel+ZM?cduT6b@CUOg#yYZ>)Lm z_@a*#mi&kJg&j&ZHS`VY8{2ey!VZDeRF3jUIjl5kshtLEyw&OK)MPi++jDKfpr)T* zd;3Oz*llgk^AB*f3Rw!i)K${gS(!f61tf?Hk zfk#8}0(IY2_REUPwB={ojc5d`>F3p0;xMZtYCya$>}@{}pX*?=jU9?K#$k!peIAes zPJQn%$rY^R+M3Y1HjB7#8nO}(O4ZlWvK*XmqZ1~;wiWAuv)m?|uWtrJQ$_r=(LO$^ zO=w(Hv9F7k$8+Sb0-3}2#lEk7R4-0{6%6umwF#u?5*FT86eG_1yY0>{sjJ~rVGrbr!W?KC{T|6% zRW&v}-EFV3^|88GD+v4`-iXefVAK2kD8Gzv(le} z^}bd5Psjn7S-a2Rf3Hf&dNfwg86qpoWg)1I;_CYo*igbTn*d{008Nh#YT2QDih?hC ze*}i`PVjkt{9SD1su3&>YToI-p1QjG?$g$1y0gS?t%adfN1@Hgpnt~O)5+P+r~406mYb@bz32EwQ;4~ z+}l{Vo=QhInpHQsF4ir&AD6KF$w&i5h!Aeph8?{x)(#og)oR(=(2;EDKlzQYy~gU68hiaxjfx6;w5eFsK&*>3C_ukc*E+NtrD0E5&ivNF;+xc4 zc-Q6bpteQ2o>fcJB$0=y%jO3twWnq3DF5N`MelA5OOUagLGpdkl^<^HPeDB*$ z;eknYEtYO5E-&m%OJZmMV#zJxZ;1xOKcBq)?ZDUkY+?MpgIAjGFI5WRre{i2s8rF) z#Low2sX5jB4j@yF6;+C7+DV}?@sF3EiOupz{QS^ssM+S1=FVn{4xO5IR(Fl~M05Mm zm`Txf(-$o*P04O+_%9OMrYgdba07~uscTgUrgHFa1794_o`d76+O3-+MIBA=i??6x z{+vc7e9N4!?w#b9&(3M$i7sxDvq-dY7>`LUiKj-D3`CfQ&`wQPh93MiNa>h=kK~qI zdL6)IZYN4Hvi4&8-v{eN7Ru2P`X*Ku%hHo)%L>A zH=}Q2H^Q<^+n-IgbEjHE#@(ZVkN{4RE%ht)c!9e=@pIZj2XwPO`H;=?zrZj)8SUUz zes-zaj`HV9wO+CZnTEtL$tfU(kcvU)|7=nIBh!s{fyfiq8oq^1_6XtoQhZiqmxr?7 zx2#{oCL99Rf%K*ojtrTae%FV+C5#{d{^jyRwSEdMg~q zhD`^F1!&SF?KMy`cQIt87ng2|^e>mK+x?~+qAA~hIIUStvOYD8wkxQ!oA7Mb?p#-Z zC3pbCEN(fuv_*9#NLW4(RHCv*W+j`-!5o0FHNF?^fVMgfdoCSL|9d)gw}N71vVQ2S zhwbLd9fRPM_r>!(z?9{^es8J~qZ;sETn>5s#bNVyL(-$M=~~}6D1?!g$J}G>`J5ZL zV|md>Z~5Fa_PdovuSTGL<37aL8mP$jTdtJ zUObblCQJZkxGKLh0L>I20_2Y89?t_)iGY>xKMVrv_a?MntM#B5**O6&d#rg5Si`r0 zXz*fAGsTikX`U#lgd8pl@sjZFM3!;crl|BnDvl`!Rh< z3CqdD!dAW(ICXhZm+m1m$o55YcCrUnF~i4kWwXnr;4frI_c^8jEeHJ5m;Yt9)AmV> zWRSprv2;PCE39h(sZ$WxGQ;&y8ZV)p2KwARY)!fb1S`W01HnDIk%X$yH}@WC{WnO> zPu7|T3BspblR^c1Bh1xF_<&5bp9=0HRd@F8+(U`!Z^sE48OsAhPCRfRzf5sEy&ukM|B})yc;}+}dyyhBI|4 zP(WV4_tU1_#L<#$-Ipi$fLp?#bjcxq*%cJ?gH&BgoZc68O~d~}Y|82vAT=jrfYkg0 zy|EwtFSRD)K1B==y_3xCfWWOGFiv%orf%{MmM6$Md6RX(**=-0`tOKr-40>)@4+LT z!`YM@zaNNL#jOmjmls;L%M+x%Rjpbi%oADP*a88q@oqV}iBv=jx}aKJc=)V9=eaOw z_a05Qw8&!W?+5qVoT2n135f~WlXsX8jP2;@EEkhsj&vGA#b4Q>{vcw%oANZK^-C^B z$t%8}_oofZO)x=!ybb5O)NxsSsDrO18VWICh0&i|{!8napHIx2=!6k1E$DBOljr`J?ExhB6I^O<2G|n9jO%(r8u6!0O#{x_!?6vSdY#z zAJlD8s#J;ve^4u8g%Z-_D^Xc4T!*&O-LZDSS*tL7etCz2w9m& zQ4#@*WUJG{;XP5ZhtGf<_=oH!+fUc;r8FDs7{))@)|7VZ&9MO-wj;iFEwCf(XE5#| zT}17-tJzaQ1fA{c2$s(i>2cmA-0=nfai zXqnNH6QNA*I>0F=)@#?EJ6}*wppV7hAl@#vT^iGPv#O`8{u&9stHHqOclFZ&a1Ja9 z_FT_y9qHa#uQ5Nzc;DC`QVVhpkbjEk5xpX|i|sAwFvJR!TJOjyBf~|;8Y*QhKOgf> zD2%#-mrjREs$HEneSA){@&qL#XRP)e6YpsHY+%Ieb}nYInS(R&EkD%kggmq7X0IS) z)ly7^#Txw)#T>@eG?X>1EKB)K*6hl0OLWyV)ns>w@{bg$8h96#|F~4G(u>2Ttl_WP zbO(g&4$yNB@x(p?Mm8m6%6oG<#y6iL`Pl7kr#)dg2oKi(kSl!j&^$gR>Hy8qFr3bJ z|K993>KFJon${nC*qEqAVY8pEHc)$S=w_wT3SP>0U9v}+LhKeqW)tr3UjVB6~A4|>Sus`kpp?O8u#uGwHaOJv<+j&_l$oKv$vSwq9P(7Afc3WEe*nwQqo;Zmq>TF zlt{RAcXxL;OE*h*cXz$l=XrkrFYlMTd*|MnIdkUp%su1kxAaphzTTNdz6i{I^cWMbLqih!DAmRy`bdngI(!7B!Q{;1C*{XowkaW`{!OA*l!O5xlzs(5W1m8D8;DMHOUK@uVX+D<-PM6YN3s3~e(!%C{}@I|{=gfT z?bIN@R^?e6)}E*58Qt8pVl<-GBCiWdHesJ<%<{c{W?dP8+j&09E_i0Y5}Xa&r6$?7dJ`@`zJFgtP}ifIvm+dey$ zp}^xvx5pnDGwYMou!;+LI*IlYf}~6pp5uzh=16eq50|6%9y;#*e3eJl<-w%6f~3(b zX6V|KgIyBYMPdd{GkAM%^Q#uPckK7|_X&BC5$#=$sA%FCPjsbQJ@8IMQmaYR&vL$k z*gI?D8$K7D`r+lYVr$T}h6M$C7qjN=K8Kr!9q&NK&;2J7vqB?X zu*{|=Qj2WKvoofSIOFKG%c$2}wxz1%?l|vjF)~&f~t$xU~DIldS@A(m0Z; z+D0S8J>HFHw_{Lfo$p4Nll>`qYXi{A#S`m^hoJhXfj9`}z}U()kG@O=S@9-r4eVs> zAEO7pdKl0Eel*>VkF~z(7iQ4B7?7Crtvw|5ucOv;yk#t*;(E%V(`|{qg=6oM zEnPpX4O2B9l8<{PDleflJlaCE_2Dby~SuCq^YER8m?O!)P@(4 zL4R15ttb{>XD?Um9S_V7eQtMv%_lo$6< zm`*h;9!SX=4>#QqQHCS_;A5NflECpFzT>p&by$J|@pT@66gV@egr4jExsQ)cbB_=A z3zeEGPC#m@fF?0^OW|4`1CK18mlw>j*NzBhLf3XC37JWjc9)kQM;MnFU!IUSJXS45 z)b_c0Y&{H|Egf8S_Mdjm>$>nk-nw(*7vOs>`DAD;_#THa9`+dSEL{nq9w;pykZ48L z0pDe3r`Hl+xq(K!dgo;iJtuPp^w<>^wn<<5ytWY@ZLU*79G8Dj-)7r!{arO!b#$+! zh}rZb!|s4%c278|feVB?^q7}OfB)C9>aE*@uP9mBScQ)ji}Zcs(L$2yH#>iO#+Rc> zDnMW;0f;}{O5*;mw&WVGHr@wG4`WjG=8&64gRQDx*D4Q7qkoPmYBD!LdwIY!oP-X= z4o=iD+{NI=XG1Wq6#b+YywSG8sAXAH-;sHCL=4q1CkxF#D%OEW-SKAhTnULQE%fd? z6`#F?Qx06nqt2njcq^vST`cmanrE9UoNFKCFc@4yL4F&{^es!Wvnau?kj2NCWe46Z zcs&D6y2ZCQWC1R?&uFgu>Lk?Bkn725xQNE@6H8Z zi?ookz+2euiI($g!GqP8?GX;0?_4%7)a{7hWbwwnSbx>s(Su5ioA*#>YR}a6H^h^! zl~2BUc8{-YMA*p`W9=SQLR;AP;w^4<^zrpC>az@eFEu(q?l7cos5a_HY>1{W?MNZHsbb&8kJiBL?&4>;$FnSXs!>#@5WPv zkaEatw~xEF!%+lbYh~mS8s%3o6?J5_^6r`@u{~vm>bO)(uKM?+=nE)G?t{!#7oUq4 zw!qZqk#^=_$Vcs6*@MXIY05lx#RDSr@AOG$YO`4P8!p<(qATpi$BCK)R56MTL;q<; zL$2C!$!MVJR_3N$07|1ngnxFQQ$0qt{1A2X=4hB7X&5q+`P^s zeT)2h)02IIMEsFrHL*;0i}{dZ6l0JmHp8--4EpS<(XdH8(T|+Tm4+IhWWh;3wWF~h zjy#lqb%`J{uG1Uam?=Du!VSc|h09ALiD|T3Bj>-aQ@xHM9;++LFPn8Yb84|~78vLG zIR%ug*&lNsKB%Vga^fgkTMb<2KFIz3TDEdki`01i3VjZ0#*D0tL`LjH2SWc%oV8R0F$KVR++;us_ph3nszb?K7RM*>0>ip?=|`Ml+_M0MA%8(!4nZ)v*ov!@sFyQi*1y1h7Jchk*ym+*0bG{6o}n}=*{^1txw zK%F3)_|6H0S~6ZHS!CsDLwIek>^8GQS)$z?`dXGWj`xr1bZ@W< z80hyRkqQnKQNAXn06xl2ORi%Zy*9d(hc^TCmVeXQsclFF{ryYtl6sywzJC?R+cbah zrzjI>=NU&eO}Nk}hzxYXV*g|z(I}2$?V1mOW9+qu zth2VD+HbWrmH;?HunT$(4GNKTU=3iGw4@{frevAV-s~!ZuO4^x3d!?Bs_hK7F8Hk7 zo75+__U`jd3nRDb^Qy~wq(@t4Xgq4nR|gwk%HS?rzCr-oLWwU=Xn8NY0Yy#xn{<+O z_06V{2FqXRq$PPTXMMj^yI$Or+uKg8LFl^DYxVCFulylpJZerdq$|oURB?P}Am(Dt zjbB>5e$!H3t(_Q6FSWLFkx@@QQe}6R$Z$XtlZP%`y4e+L-!uiVkr-^L0cf4?iGEef z0wmdW>;aHS^4w~ahd?vg|EOM45}OYC^2X;M-lGwBWPX)at#PsmOjRraLHyXuD**o+ z9vDww5#Y(RP&=&N@;k3Sg#C(FcwyM0&+n?pMTbAO93KOOBu*dKgtc?E%tl8N(_-B+ z?fqIZ_EOruP2IU$!1bkC)^jdF3EGUs-SF2^G-ZZ^W>nDPkSQRlf<9rV@2a(^5)x-{#~0{4QJ4_@qG@;^LPxGz>I^CeX=n#z$`R^) zd~b2u#dmt|ce)2IOFCG&sL$51G*>lbp1XW}DC^Zp03@_xIc5URcvO9gg)Vs41UTWv z-rw4^%G!OE%}THv?StgjJq4joU2K@#>Ol367M<}^2R#=1F%Dbal)-CBu9lJt6m5aL z|NSrSyca+J?@JcBY5LpI6z7i>^>hdOyA2(|${j<7VBOC^I_CC`I9`)|f?;ow%GACI zL+v6=%MwF09jio_aX5GZakqzsf7(|vpJ?$cfV~XqiLCvvCxiW01WW4?VBep`5An$A zOqHp+?4+B8YnoAhjEj0Yy5^;isDKU1@f7+lhTFTpw}~aB>J?4_qUp&%SyC>x=`g6tYs^Vqri zZ0Zvf_YIh)n@6F$%E8skk@(9e4MlgmIQq(&*PP}KiJGSBOU$94JZrr>5mjD0_=b)2 zxj#Qz5YgMY#B-xq#du#h(c9fLEDmPZ4C@h#g~rr3rTBWh-#MS~f2Otl_aNDyV+451 zFhwP=ei9DWL<~SGax}&~v|?F?9lL$uRo^;ubp+pAeg&M^kgmZ9TTSZ3HLv>(M|)p$YYGuBa3H8g-W@mC=p{*{rtj&GzMDhsTxCB4O2Q zj-mpGmOU&d+YLzoI_a9meddz=?%PuLZHED>kfmJWc@snQml$m(U+z4o#EuhbJlPc3`l(an&43Y#3g}n@LX17V@1Qin#Se zwlX&3QGhm$e>AdWUJIs{O|Huq2xo4>VYRZ^9(XG|;Glf;FhrUFY|YeC%lvwR`wZqk zwPGn)GT^sEZznBaZ7tViJ!8paWz`?gr1kLXoU`Jcwv_j}@3Fho;+E9mG;=gLS?)%K zwPoOEw^F^o<~ABS*TUKEFUio^Z_8)|ogd8dgu03>8?7(~ITH7>7SchN$=JFEl}k*l z$wDphV-0*E_8O@K*j=_`-fq|e>vF6{aR45e%QQm}nj27>ZCtE}nE-mW)2)XRS&JLj zwT-b1UZ387hA{MWEJ=@AqZS(etH4`!lC_EQ-f2wEWQuk08r__S;>)7Pw%>2EUOGyc zU(S8%$6-PDWmSt5@8igM{(MOPm#Xp11h1QxXV6)dENu+STj^`jenvqM7s>ef{|dHO z2Izr;xSAOq|2r?Y;YXh~PeR+HR7e|K*3}Yt`xsSz0#c z^xX@wdJ#ZN6!IKKzN?HM&2rk9{L*rq8VuaKq8WS0=JZH1IBTt04OFs+5K0>aP!=Uf z*#+)nifS;(5B8{qmGx;VOd`fImDojF;55lQe9$R|SvGk=?B6?YB^p{4v6WLh%4lQg zkEQR#+r_kuVF`lzD@Bpy%lGka2f>ody%Aj%mjqC6kpOd}VKpw?2)}GPUKjPcrR?<` z)@@L;^{`l%*zk3!w}=J<3ve$tgn2Jg%WL|l(1=Tk-)?vuWmguYg%O0ou8K1tI;`fZ zJ?UWjF0;N7kKcB9{G2rXC?H(#i{76k+-bit$i24Qcr){vxEK-XHi)+r1WqN!Av6Qx zUc134AH}95m7;}(ht=-7YjjK-Z*)#c&K|>HSc3|p~W$wHHQPiblZsX zqG}s(&j`i&I2Y14pYOm+^8+Dd$J@pC^~|lZ4?srO1|EIAjQyaqI2i9nWsr0nI>pw`9g;a|Qjexilr<9oQvyy-KK@~^w4c%2xI)>Mt_)YfAW1kVG{ z&Ax+%xnH7)VblbuX8ao@;ME5`e%oxsvmd!YnzL+r!JLDekXo$77rga*9^0YUo1|^H zUsz;b-#Cf9tm*~J;bIEU`+l8XwtpBL*6w=v}F z9^4&1rYGt1zezs;yECsmi2}<}ma3wbDZlANjupIk-rmWTu1WmPW3$6orfE8nl=~O! zshhAqgAhjft${MO@#D?dXW98zJ8Gs=_s4D@1AgPGI}#|Jd5lne>DQq@X1eVCT=)TuGcS$X=@XsJ>18A08TsqOZ+@z-JKwST8=}bkASFhI zCJponqR7InuYfd0hP*hU)SCPtk9jY+l4Wa-)3 z9zuG}03D9Av+$frBZQr{6iQ!cN{wUS_{v`TwNgEqVj*bgQgtWG#{X@Xgu$R4d<` z7)cv-M^FKA(jKf5G#^Rup{bf$4I_U%;xJ^+GkaU1)Z|(mOZw>9c!af#IIJK|AcN;w zgD9Yii>0qsop8n#^&p@@`>ZGriPQIysLGz^^T1rQDG=hkjNX2*m@fY0l{-E}waE{t z+vhn?y3d8R*=K#H#Ig^CjO_Mm!`YoS#$(G|<}}%S^(JFi=Kj-0>HZl-gH)~kNw~#g z($$P^eKkUYMstT*|AO7DY8UcetL&)AILP3p3%N*5M0?K zFJ0Hq5eAG6k=2J@Jb7-2I)xrM?S16`GCcm|xrwZr5YG;+4t){x7RVK(i#mSB`ReAm z+EA;)MkI?kNxCc~TR%fy7<6 zYAD-zuC$ox@|#-KG~$S^&3X^fNm>lsT@EFgRhk`~9HF}yJ5>6rpSH4`IPo~TV*&A2 z=aM0)lmDw}?`IpaI58^#@BR!!BUR$wD-{Ox{cVqM?%!u8O zc{91FbhPr_e#mPcIv;o5v)T-bU`c&spc{FQ{GH1*VzuzaR(&Amma@+U6a7!6uSPnq z={V_lhPvp-b$5_g+Vu{c8pH;&e0ns&zuu34TNUg}NZpQ!!mQCA{0*IlZ>Ofrvd5H* z>)t&1u<~p_?Cn)&_-9}nqVGaBl6171RS5c)!$%RhRc?DY@Z8^u6}}#6(+TU~D?|E4 ztpLI3a*}Vc0GuyP&uG0bv2*Qzwi=7PZ$GJf9FG8?zLF9S zqE-sO@l*ynA=QO?^D1POP8_e5)c`Oc*r6SSlk$QTjPk`gMTZOD#+m7ceyrz`?$sMV z2|uTOxkWTXCUTc2n%Y5+O8l=r0Hi`zsB6fFhU|5+ZRJ-|g#UeEeuZz?@LvO8%S3}N z6Ym9G3Sxq!*EOXJnR@#U^TupmVgyEaW;+6LASmd)ArE=O;AQ!9mkc$Ht!J5ktV_W{BmF)%T6J9s}QQH4OGiQ;ojp_njts zBI%D={k`eBraKF_&7^R%Yf^l{RBoOnmVWIA`M7ZJHL#6$gNgmT*t^(ut-NXd-o2I7 zgI!@Fq8WQP_w6@zzC>FUKXOAsz4=0S&l|qg`vWS{m#<;~fiw+7hSGAY@`E7un7Ug@ z**ecB9xu1d3AH04a^kIGl0A~#!U7I+ z<^CXq4`=8^}zP1Cd$)sdg z-Kr^8m4&q9_}C_HsU*}@T@K1a6X_w6@97VZD;1J3PR@f_A z=GEqwQ^^$ElNl#4+A*0Kh%CmYKe+OXTkZj2q?To{(>%M=gu6Ep>l(mqe0`;k!;6kX zi$AufiE)ytQUL$PV;Lz%xYk(mzKLl}MRa`sP;7OKc<*{fn+o_$4nKuUf&h!I zL8b4{CcF=$UV`|aUZMQbB0JgWu=3~gpQOr*LD404{dzaHOCZYB@$9)aW@S-jFo|7$ zuy=jD#Wi&7lNqu9k)m|~W>dJF>V1r50^=$>IP^5 z6D;$aoe|Aq{l^UlD)_9s^~&zgGQ^-$kB)>q`i)er6p(lT{LRka{y5KQF<_C5a=R@9 zRl45mi^l095luqr*(tEw&e7t2fvurHKHvdpNQgS^41E+0wWrJ37Y?sd<=o>OTs`z;fQXJ@3K z{N!hBVp}S)uqnUFSx89Om0=s%S&7}UTUGS6{X4KdV(zA4z~Cg$2{5G_C4k*NK{6aJ zNk6olv;_D*rvBTBS*kL9{F?atf3_6REUI-bkSE~>;*LLlT5re_J>IBU6&W7$UDo2I zH5~8JT+{)MjI#o@B#>WIg;wHH+iDBT^(fA|~C_fA$5hQWkR1)0i412ujxJW)23n(eOvBRTGq(uV;?zfMT~uJ=6@ z_w!>a@e?$4@y*rYMg1lpV9euYy4*ZdHm9dV43DV=pH+&}(W*eW9_SX8lg`5}#}v8Z z36>b!f?_DySsbklCtT4PT){;-{)X7_X|{gqr&Dh2Jjw``WO88bmO4gBI;fc8vt1B{ z@6dB>dnK^M|YciFj zEYTfR9y*D01p=~7(Xo))jOn>8e-&dq!TCwqEWT5ZR*L?rGGOC2M#=4;tPGna$zYTr;P#iDoYuMzfsO!?%mCmy!o z%_0dk{RlWUj5QkBB62ie^Ed7C=bMl!xV4!Q+(qEi6cPo@4@bVr0^%}Y;3J+| zzsf39ZP3reT9qz%1f;}MfwD@L4d+tQC(ivU&ov#IxZjYJ;c<%<;2bUW33?;7O{FE0 zF3F5xFMZJh^2Kz>_C9fA3aB=NlyuD^Ad2`Q$hK{$uq`==gz+ehlBd5P_uR6O|JD5> z7N23$hKkCHjKlhUH{sbs?qcm9f}>}BQpAOPIX=9RHo^`r<60=ipsklpKoOhMAq{%Y zi;?=gd;0lYzxaY+3Sf6UJ4$bGTjYYWn{_azb#8$PGAsBmP_9hNI)pVzat%S`h*u@x z7mUyQhH*q)=ORKuF7Cj6#A*C(h7)MMN#974i~MI;Tk<<;BsxUMO^t3uqoY`kzo=2$ z0H$Q#B0Y^xuiFSOgU{nnb8M&GFc5oDhCEHzgiU~4$baj81_nmqM&R_;C2fs?LdsSL z%AK`cZx--FY9@2NswRpF!-*z+ONK$OhYtc1Cj6sR&}D)gN%Bo1uf9-;5=fpWcIDf# z&?giwL2ox6nPc$j!IJUlgSD(%V9ck=FS?(Yjv*D02JcH0DmHI7Rv$;_`qdTzC${>L;wnuURGB88Y<-&6>}Lq%lb z$$oxYj}n%MyTJSh)KZ60cVbSZCrBY5GnYcjzBWqEPBkC+y~yH++^C3*=lzlxlh-;VdDiY?K%tF`kKI(ddoKR=JZL^PZ=?x*u{> z1V^WI%{kRz(>THi&#prECjihsVbLMF$H(qVwspihvVx;AO^-I{uB1cmRrztiyoTST zk7Ht)!W9A7Jk{UVFY}IQWh+p~NXG400!1YX@jI5g;sKxSj0i<&s|Uqp_n`i@?RFlR zkeeR;amlK|lYSloX{PL~?X9LwyHcL(tif7?Rl3OX|Do)@I>76fydjN}Og2?GF@*gS zdObSNZXmV6f^TtZDHQXXz;F;%hOt zLC1CfeP;?k#Mw+Kq7lxP?;ph!uDmN9AZg5aBU1l_3Tk2o?b6T|g1}8vO`-?4zWz=f zh845`Wvg))d97?H#WeQs<)fH#Nosdw<%%J*Ur*fidcb*{qN$^C=+h;FW8~~)$t8ry zv6~*+I`CiCXS@J4VKGHA?@F7i6{Y?as^r93vWvR>7_NMVv(Maw2u0&uLDO5z&NY^< z#>x5}jkrV|y-~tGrS6C6IPbYoj}*_LL$QhCa&4AK{z6iyZ+wZ8_pAy}Jrp`Mp`XXG zXgN2pw6q<}joshR(k9*SHp}7?3(Ri7L>_oCF_7aG6@c8Vt$#!4!lZnZ z2Bgl>^w-iCSBWM{OWC3dM`h;ByM~#{AI)!x-WS;GZ(7v^*_VpFb5c z#G3U$B=mLJNzZ(z=mqA|dhZev}WNfV>&jPHk`MZEF{H6Gj1%F zZj;VfX;hm3Eju{rDS}Ydn!W8e+%L5FaOH(jiV62uBct#rV_%8YOMQ9tp=NIrVjbKr zqQk5cvB~>p^1!#+igAe1P}*PnHK^$?NjOV(Q9h=)y{6-W^PJ49jybzJlaVvbgXE#W z{d3fq#Xp=j#I5aXs|XDm-i9?PVb7Ri@FGE&MHeXQYw6UzJXW+kuP!jOk=f4=(@M3mAjB#0z9XgfT;;cu)ApnV;DPXbFgpRk9 z<3XG)Dr3~B^6e!MQA&rD`^D*v#pJqKmoN2KMFQ5TjTOFYi&_X=UJzH0p0x$Cp;=sG)6^B zy^gjM=d_ObSq>fCH<}cO`M=j)72)j9&N3wNZeyW|7nS*P#bL%qFu-$GMljH&#GRjS z>uXvO&f+F&18Ul%HJOzw4a^mJk2At-B>h}0SZnn0E&4&0g(6mpB1XQhTlq=Ky`Oyl zt354eU7M7hi~#G=9@iPEiSiSf1HV;q?PaCLi)jSA^MH3bY_rrSQ>RZOS>_2=9Y`h> zS!%>EAG@V#&i)Sso-F?6)rzPDB3u}51sKi@F6cD8;on*)ogbL@Co`_@nG(gD^#v1;T?A zcX~UIL(*5(eEzGOTOA8XB``c@JcM3wy4yRg#CWIT?QVAdSrq+OK&I%F7K;b=!m3y7Zp~5;i%rRS2<=_kTI0zs|U&Cc& zw77w$c}w+|k9V4g2r(}9>(-zq1D0_q#d3Pi;OvWv+)mk%dqk}MgrN1eaHE4a?OO2t z7g{&SYSOmEX`%ddi80UJ9o*RUa0F0fQCS;e&@8v>WanP&gDhx zhsn^=XC)d*+AEzR;xmV0KYR%_MH(39BI&rK1`3^=doOr3E=S_K)Tett+X^^kq1NnY zxhjf^sm0Bmm;&{}imWABzx)xZ-eRL$yc=xa8z=(LO>$M0C-tc)?2F zG<%+(r4-pd;;j1ky0c0?CVI=wT4i;X7+3>;2MWV2B~b1UMAE5GshjF`@w5)K$ng~T6cX7Kw&o-2aD;iO*+Y79 zADhzo&JaIkkEbV@HG_?B1v8`wZEs2~t;8^;IC@e^Y?;qq;D2je?EB6njqmll_w@x+ z#X-;F=^et&War3-`d>tv2PoL~aiF7Zqd%Guldu9DDi(XO9)73 zB-Bf{s1RnHCl6_|w_1Q_i61gN1?~30o)K~skEMKYf8h2Gg;fETAYXtC}nLdif zXZxEufC?A+C8+DE=-s(vrj{x^B=hp443IfmqN*QRJUv{>Gi7}qe|t8d&tw(O)3dy) zH1UZDq6Goy>fb>cG@Qm)E`Kyv^aSR?H{aWWbnh>jV0mxYysK_oDGF}2cXgofQ^cHt zdt1Z86U;$Z?|C6M>c=EH1b4QtAK@xTY%g)F2|doB7&j#J4W)-5?GHcY*S-kLyx%Pl zBMnKgSrX843LJTmCMtnP8x`Y7QY8+=5NkM+0!e0RQg(BMM|!!}W3u<7ws=TX&=tcK z&Y;k3MFEtOYmGc}gN8!zE;Q_id9-}u+^a&b!dAokD5eSh4ADH_GH7$dEPjv0!fLCR z1~NlPMOU{5G)~7zW<=-e?<3KlMal?>;d}q_Ak)iVW`Ax_Q@VZ-Uyej<$Mz2*Gim zX3ni*Q>T*n;8;+!jls(Gc{ZD{%>TxcV-nN+I{4vzxFoM`u+E}oK6zZM1TYQR1ZDK! z7cI#tX3!9BKas}n|Ek|^Sag*vQwHCDA}d8iY!gO-+N{jz=Q;_v@jiG0BV3caUN{gPeKkops$o_}EDa-6um$wd z+#kCv1kXjJGLu%QgKh=|pFg^CWmC+04*X2PpWyHwFgUvTuNIM!4-%Y&{R8uS; z8NY7u6#GU>F=3Odk7@-Y6g)E1kN|q>$MUGVh^Dfj0ZtQlc-uUv897=b-=DDORjczy zet4j{P5(H|-t7QKx7DjkXuPA-2K_XIpO8)m+IN#nUz8tX(7Zz>t1hc?W3!MSHtlW` zzyz)Fjghm>1>wPipKh}V{VH-YVpxlCo73jL0*cK4N^+n`Gy9eFSwNIdkvo`nJv>B`UxlGo}{qZP2eg^w=%EUF?w8XsfJ94|9!t~L{VVhj|gfr?PR4=Ye`qW zy-Fldc8N_9F4lwG5-Gx60e6jh4CPQ<_RD?0;Qp9KTvbYqwZno9WCYi#cc8w_!Q|7` zqCe)iQCmxe9LgQ`OC-g9@q*PlA=4hGY&sKj)Xp|f4Rm1%{caSC*EE6LU8bGUJb)`z zAG413>CZDT)q%(JHUS{H9Vm8g)yRHH8=*qQ0?J$%ZO?J{&zxaPD*q0YB1Dskiq)r_B$koDWHA`dxmQJRK6HO2WR! z3!{C~{IKv{q8Bypwexy>$iSmN1Y)z=pfjJMdB1X{KL-ghbp1LlhQNe9_FV zs7X87%(ad1j4nDV{&SI!!N{d=MHF~-&wjxk&kLA`gzXgTOUH$Lb?pxF|Khu&JnLw{ zCRflM`VqC-`G`0_Ymfkj&w0fI-pm^8n1T}m%x-sJ-1~SN-Uv@#mqdgyp$|&r5kvx> zEwbTeob9D!(X{RZtXw)&2 zN@oL}Z*+yV)V_>a78&CpVtoP90tuOHw&51uSUWRR5qT3JIu`th7+MOfM(j|3@nKno zIzlJ0$9Cp?Zdf%M)W$xT0tAyVvg$aRH|SGLDfc3Zp|uG}t2*o^WMM(Ml`vndqr8-* zvx>)l#!q{_6J}_2T5OavqgDKucQf%BVTXoMpd4IfZ$fe5&Jp<{Lr%%_F zqKWtP&oMKhr?O)3fKF828bFY1u?EfXUd3`H(7C=z>^hnn*ic%;#IH83EYx^0nx$CB zS>T|c-iC@xcte?t1`-7lkzSU+1ZC&a%TNZ7H-bIC=_4wtG6!o=?|O;GLgRHT*0&00VO() z`3Cwr=ISRO|BJ^A&EQu@H|wIe(WyXWaj*&6Pe)I0aooNj;C*%bt#OjLTIOb!(22Ik zivXa7@WwMmMR8uXT98HyAGq}&iy#N6>VNkm$D-{eZZ!i=5E9I(%Nn?3JneZ32)Z2_B#f& zyZh5SxC4rZw6`FU5!ca&9%3MT`ur9-|D6sC!qJCG3?mPK`yc%?dEh!I7PQIUl$Dd4 zJC$H*Y-*TdxL(0otJCrT(a%}$l&jn?>kX|ui!>#V+;TEbCC>uOGz@o?3d4>i0m*B& z$Y*$dh7HzpP{p>2pXjRO8roB1PWLbcap;wn_Qm6VIxK7ZM*!K#JdPH!tc-FO=Fj^5 zSs&-Q+8_J35pD_{z;Hw#kX%aYdu0$%$CP4I;#ANBQg&tBi4thR!H;WTp=4(#KU>E) zt=uUSv++(;3Np6S*ttL6FcB>3M_)$%c(%1IG2m=*44dI>aQbqJ*?e<{$RG-w+Tf#eUq!j^aA4S*)-=-@MZvhum<=W%tiCE!VG?f z`%R}te+YCckC(S$m9WxaGNLgw@s|RBQ^%y1jzyMl_y*}o65c(YpR3UHpLp;>1lh2y zG(<|zojx(buCtX;!KH>as6__SiRwbbjR@B3}bumLB>OMws1}JVgkvoTb*zMa$ z@KZ_HZ)4i3-RqpuJ?AC9z9U=@f)QTD*lbZz##yxyvSr z|H+XUM*ax9E(-E9rd>oT-6m!eR@_}OUEEJT$Z8?85^<4)S0EZnE0?UJ2rl!V z62qJV`T*s}cA^JvKvD+BOjdzy`NhGFu=0Vzi`Z11k{-;M;1ucu7rKL8TKxXMS<*s{ z``Vv3zw0j#5PX?Oj68@$)|^tx*Wt3|;~x4@7j9bwKI;<7w&edzr2fY$P3s~gsS%-o z>`^s3v*#1{j@_72(qABnhP|^*)->ID$e6+JNA*q0q4}|E3<`G~wZCquEdAoNyRV}@ z9K_K!t-i=awhowV$_dAht{i2>-9$w~Hk76)?rizBq_sn$A9|GKzD*J0qC5-^+B+1fK6Uh3!l>Pr%8K<~>Xa2IPU1a?$^Rlh!I_%-F9t$Ss2z)5>xBN6YOdA)tX zxy!ANl3i4sNgp>uU$EV1(9Ji~aX-p%G^=}@qmsoRUW0RqsiKZjOlgtaMWkpZC*-q4 zjq4)VI`4e`xK+};i+;ozr7-qT4$Hsz$t&XKEl~#-)KH@!0Ya17Y`-p!vZ5ndf*<5c z>W&6^#}5)0tiH{ry`NAN?@yo|!zXP%U|bMrFaLF!o!rMPV^*-ByFYQ2X(1$IyVdcO zvV$6}Z6dG<>G(TPj&r35H;lkf6Jn6I4f!a{qU;)p8Dvg?_U*Z7ABn@S(40*DW`D|0 z$0IMv$fI1wv^HnlWi?k6{SECTnP!lyhq!DLywwainN$93J`jDmX zVN$q`bGh#a2;K+GAv6J^1(GyfbtvQzt$-XlP6HVtJ^G-q1Lf{g&(*DZc@&1f4A>4X zo*SOrM;{6qkIXnBddcq$hqQJNj$!}I={#t)K85Vr+-bl%RFRah$PC9B#L} zIFo_}HTnRdNfrT{1rpisqU%w?>KK*l>Tv^rLB@(QrnJd*7vnYk zk1JN;w#8(kDyo?sO6OW-IN+sfb|f|MwYnxvDk;(!ZWh#}U41-!O-n!LGuL0n@T-wW z?6upQI-!&%ZQ!2c7xr~w)ESb~TQd^+ykZ_pSn|7tU%YP?f^h1MQ7DVuxsQ~J3>+3n zq_XL$54$5e;RU|8z(V2pd4g$yu%}<)##LuR1o}|9%9#L#K(>E1ei6+;4S^Yu0JJvoLi>|o0Vl)=Us7~_`7i&2_(2` z`lW-h3`~9dJX3oPTQiQ8eFcq#*uLp)bDG{`LY?9w7)Hu5uAOEO zR8=AIa@P*6!c#0t;@A|*jXkfGrK%xX7j7-3AqPV#&WG!_)Ct8&T!hwCNcdFQvanEQ z8Xde&npPcN#+?^|z5IkEn_AJpafc8=M8!U()K>@6#2z-=w{}Eci27s%xJ!4LJmn5M z$}f(T4!CRA(e&uvZf84iULzeTrO5$N89nWe+boBKbs58?)xpm|bO&YAeZ!6y%=ebW zs9-&%U(-aIT^W89UU-=$BF!vY>SZ0!q<&PmpOvZ-^RDvgyJrOA$!m635hW|~%n)PG zRY(u&Di05B8)1OO-5Qz;40`-FB7~*jR=Fedf^y*lFg=E@WVU?agIOH;eD48#aJ(+B ziLjHZ3;)X!I2=-36p_ZuQK_-EBZs~`bHty8+d{U2hG3-Yi(qEpHCZFJ1A+k?3PRV> z@?DxEw7*2;kGt(;^5i~Dm4Ce%HNHhOD||SDR+pQTuBtJPVg_C4tDqDWLzD3Pm2W71 zRfh{^sQRCu;Jq+2V2BI+ccon|$7MTsO$vug443!*Ey5-L&!lv$Vy;yYV`h)L^#?A@ z)79$iO4|op3xRX!<3ey16pLRQq8E-AKaTT{|A%>4Sbk>aUioZ7@aH)`q5Qb`v2W0d z^YCmE8VQi~4^5mB$~#f@Ppras{N+$N&d@h-_)^V%OL-grI&I`?~%=B(&d zyB8`q8z|dzZF6^}p0vdw^-N`76@HZhVK>jJw3;&-#N98$yo64@r8rSn`p^^Qn$Gxv zf1X250&O_)YC(;z4m5E%G+ZouGwL0Cc2XGP$#c~`Oe`uyr9&A|52%~&PC)dOJ#if^ zUCCaQI8f=DlHwz>>aKJhiKcSCF<|5sH zCgIPi;mqt7L8JUE`v>d-iBjw=;wnYG45hl=vC7 z^g9xxYYw8m3CK9g=03<5pzpSbPN%WnPtnwbmnoZ&`CFXbss9NzEZ$NNQB#3>AFBGl zI<7+hQ$=k5R1qGuktd{-OS7kxtTT_>O?rGi3&7PUS^vT~LPRIp2i$XEfHyg^nn}Gr zTfX~p^#5r3%CNY(E?T6x6(~MHarffIiaW)nxVuAfhhl?M+}+)s;_fy$6o=vtH}7}v z{lhbvCnRT*?45PiUV9zY0wrMfuL6fOa zmo4j_6Qq9_V?7U|1U7U-g}7*9*|LUl+>!>?p^>!sY;xCd zy#+_fmNJlv9c9Mv4KaP(ue#A^FJ~dBlJ46k&V^#K$>l{s4LT<``X_+!GxsPiHE{-*c_JwAI`bcd>Z1-<>ipDRFT0~FN zyM9SvpdJrvan!hZqN`!4YRBV7RgM#|IjArTwE(g@dnkuuBiV3D!~2LePd1xAgb{N@KPHe$esSrlDc}hOo1KGc{rv{J%Bjw zP~vFg_&izt`EjVW0Sv)ZT?!!fQ8inU2WS6ubR885i6q>?iV8ZqU%-2Y6^u8!5DZio z60c{|kDVZnQe@z$k;Bv|3-WFHP9y+q(YuBVCeEl((w9F2Ao!pDPg&ryF)@EteY6DP z_TRG`B;ng%PJrndQK7nGoJEO&c3*dY7~*+`KmVAl^b?3L0tWXe_bX%$i}T=%voI4Y zFFK7R;Eci7|6riqLCFrlX6og{AK03dch zB5WWToZYhshIuxgNKrg^*S`StMnQb4Om#zu46)f|cX&m024hGP{f-*>dA#9aYb zXIQ}U|DIKeN(R6uEdciM@F1a-`G4WuU^3rM?0)_WHU_~V?1gZ~e`F%D=lm>per zRFR0bIO*G+$!>1Q9iP?97{CA)1TmmS0j}LtB)`dCDb^#VB|-BpIOriI8%Sf%AyR=d zC;}WQ-S@rPa=`PIR`&MY60?ea2+I!ffNcr>9HNyEU_Ms&xL`4Dmk*f4;xQm^x=bnn zduf=vLmc%cuz?ioy8(=3^nbsjMqaHNUZ9D_{P(T`r!!dGt{^|@_GziG(Tl=O zOceXgM$g>Bh;!2azbi^!5sRwUxt+?AA8mmEvNZK1;S*quWqvXQl5$uuO{XxG%JC!t zt=S6k*8@hJ7E!*gNZ?H`o%SEFo@cm&@igNzZ=_|a{G&jSkHKYG-IL8UGT>ND9x@EX z@FGe)7tT>3H&j-D?YIzxZ@YYA;UblI;=!21x4B@ zo+MjiXTZ`Or2j9VP2?m18vQ&c>QUC*YbGHf3XUC}o!YzgwLuraejGqh$qbrqmo?#*It5D0W5my>7c$O4 z`Ibkkrv)T#eVF%nWP%l{UjOHgMPE$2dV7k}*cnZ{?b>qr=GC;|p>r#6fsq7IEt*e> z{I|>|Du`lVL-GFVyo{SD6c={p>1or3(PI0!%l7_Z%Z^q}D|ih8$G;-l!k}%9(`Os` zd0kHc3i7kVM_8Ep&DxtT#DUw_2Pn{n7zKZ`%S&)rqYk!n$A`5ORDXXZAjkN!`-P3@ zPUfqLcmG7#W;aljg6_@u2ycDvwAJ*e(L7cL-OL2#>Lm*AkghLI?kBZkH1Az|9ZcLI z_lKx0kR==I;$)!;!9I$!gDmzJ|0ctQB-O_BY=>9u&;G$JhR#1k`SkW5s5Uy%;j9n; z5N#lJwTpFF3N($?ZtRaLHv$%N<8O9RM?Yn^wxT=FzqMYiGw($E?8eK`;dyES@xA+r zQF9BuHROt-loh%EHyng)db?&hfZxP4v0k+R!|!6^htY78W*P8!F#&~-QjhanM~I`g z0?mjIz`SRL^~?f^%-eVp9YSW`<#&*!oCW6pfEBXBfO&x{rB{m`!pv2HM+kBI0j#fn z>YqN9?F*E^jY(Q;i59?1NZcB@SVZ`~;R-8ekv$ABkg~+mKMLyUxm`fTBctvwb1Azu z<5Ci6Qip0G3VG%z@I`>& zKg!Y*I_Yyvjq-xrEmz!TCKdm}tWqRmSRI`F@YhB^!E`Uir3W}^Z6hEj|7CuadQMXo zO+etE$nKHSiD-Z3bv8jz8P}`amNaG2XpwFhFyko z?l|}L!XgsL`X@>ZFmexhW2QR6^e7*Z_hRGVD5dbGPr83_10?UeMi}C7_se7+N?ANF z<27X{dk5!gXUDPHT{ zH|A+sB}5EGCFO{NzHV`l6yRwYB>Hcg=n?ul#s5=XbP$6DaJ+wP6dzb)cqMuX76m~l z-5;p>5x9C_L;G$ym2CTa)$)YdO&(KDKClXuJ32Tl>^!W7NlzO)PKX$>M*lTdG=}+$ z$=T*u%$D(FRT0#W%S_^Bj>uZ6M(V|d#2QnRfH8-!u=0$Rh)Sv{7@DB#AdH2IFop9W zo@{K01QQ0ejXB24k6hrnrx-6(?@4YL4j+um6CW%%*ye{6ikgkodMJ^}{wI7)Ojsq5 ztl#`w>_2_N1czWkbkBR(-q>vpGMr||Fgiu@a?%4yN))C>r7davzvUa!{D#w73hwRO zIa2~3Q3=kgMexnz2k1gQR>6fYRQT+u193aZqA>j=>76h|(mp_o&Hkp~MbMmHyeD;A zz-={n$N$$Y>C^bOD>OR#Ge{UUY{h&kSS~H}jCRu50E7~r{91Pis!=Fp4E5lm@mw%| z#4tXXLk!}8|5@fA6iMzN3oRMOB~rb?%J=6IFwBeSd;S2*5WGSX@1Mz?h_%Y;DS4O2 zpS<^Vm_E#_A9iq|B`qO4$uz4wtCqoUsI*(m(|jbHTMFuD{1>~G`qA;k##|~_i7|f@ zE70ppN^tMlr@nuh240)!tfznvVeX(!FgsOaH?<*XqIs~_5!L+;YwY9s>Iw!bvl^)Q zPhd~%_Q_U_^o;Qxj=>l2+y&d0ksvuaEb^9QDnIzUnY9jBFo{=G0@y{fC z20UjTW5&F&fvYNAGl^dyMIEoba-Y&2%CTk8a}I*z(1wGF(3qU zXgx%jNPXmU2`5%Z426$_mqmtPpz0W|ddT-3PRA%+!IrKVh+GjDXPMU;QMlrxthDG}6P$b~9FKLo`-YEM@D+c;C zFI*go?l&<(%n>48jFw7wvD_QyEPh%^09tKz*P!!0x3E2I9y<3B!)$!FbljMD?^EsK z-Q{@%z{ZDJ^hzLg8{laTX;D7o1V8>A|NV)!TC=ii<}M=~D9DbEi~Mp{jH{ z{s$PP5T2tk`342`c#eirZ=Ig%F^&KX8tpYH0QPU0jx4Byd zg$KJd!QoLwKrh^a`Rj;GTMj10jal0L^kWU_-1)ik<+-mV<<*jrOn{fS;Md0?VL( zTj&l0pv7#U7Ozz81wi|Gl)wBnSDE@(xGkcrqyEi3Pc=9OiO?r1kLv(@S-Sy7&RH4a z#JC;0sOlAQM?G$QDDJ`IUqho1defl?damWVsksn}G2Sm9N<;u=gF|)zv{UZ_{+BM2 z`n&C1vhlvoh$M?)*q~@;Xe*Nd(-d1JK+%z99s?;o68L0D>J8{asJwx^e^n3gZZGa% zhDMvy6=Md$abW_HApiR--p(Rmq>Cj$vHLH;q_y_8eJL{PEZRkHbskM?Pgb%PVGXhH3Jy?#~gKANE)*BK1QPqaKDtoB8zZrbnMmepwid5#b0mHspX)Ahd-Ld@Di=svzO%|{SFky+s z$Rlekwy>Xd)VfVMZsT!V3W$$VAt|B<+mTAF*X>T>k$QG2m4y*v?T!(!m3rlpqHod~ zZ^1r4^lyC7%fvPG3*+>X@Tj;G4EhS_>_61WKkhA~p(OCd7W(A$@)!Xa+o|^#67Zzi z`hgfWON&v3Y~267G;rEhN~Zzrm5Ala1&{I^izh(D2HGF~3*!T&@osDGS?NL^`@3MD zy>_4cdsR=Rt7G-Q$HnJ3SG?gt7j$y@x(G~v zl~``h!#dM9X8!9&xq2D%u+3ZqouuZj57cI9bti`{sv4$_Tpw#9JPUe}GdA>g#cUu$ z$fERtdrlW=B5@?IbASP!OS>L$+zG=Sr<5r~&*yNM6Oo?I2N2Y!2DrwgTrvPWw>|;) zb@bh(B1oVd19-yRfLn)n?dcfip!#1a+ouT_TVi)NP)c`h2lyEqV1n^&cM5F4eU{1~v*XPR1dxi+B3o=GEi(ajqA*B!hvA#0x7FdF*c5(XA?D($(`><=TODVVS|ZF0Sm7(s@iw{LAo{T2P1qfAUEJ>LUXZFVvG zwEQ~#*@jl_-q+3K2Ky|M?7fbXp!lyY=I?4?-fY$>)J2ho++sSSGL%OU8%gh;XnJj` z1=uTK7sbOm&G{Zmj{I9A0@^aR2}L0l0A6I63@!sW|K06Af7ptTXDH$$HfuWdf{3~XeNZ?jrn>AUd8D^Cixd2cJ%Y{Rt)+6m zoVD|3iAQoFCJ(0YgDi~W3`31U`)nBKHaK+Yb05lVn@}87GVZ#adR#jGVB9cPk2zv% z&K`)9=00PS>Qq9fzzLkz8V06z_HPhSEw^COe?8?i4UArXCVllsTZ&U1MHNPB@|ElcFW$Pu>{+TB`|s3`s<1x0luO;1i`i$HRdXAY|IC}00# z;^+2v{+zy#wC$Wm`?Io3_!|c!9jfPFylZHA&O{!##Te zpvBPW()fQ1tFZ1uEkdu)0jhs8J2(&Gt*B7vFa5nEGFw3cIA72e)`U21bTL^yIPecr zLKCJq@Rxc=7*2K<15I^j3{vJCX^4Y+1@z*xRDSeZR)14^ zB^XEMKc+Az4~yR@V+V078XEobh+i4i_wrnhXEV4`7FSq8qp|Nm%|OlAaP2}2BBES) z3+I|W--u+bDXwqCqiKTSKGlO&ICn?mKiN_546SeN39qC^TU+-}Amlk!K=r~vP*5S1 zvt!L!>v7l!Bir2yrZP^$XRYP!RWOC3Za$pGG~{XJ5>)tS44n)-C_zZqV-g>F_LNC= zkwO!!-Td%|`KI;ZngZtT3}L9bVWxQvd5(zR+JJ}tWiuP%Q`aiN=YHQ-qp5e@hj zfbjI0FnG>OAsmh7l|ENEw+Gfs4E3)wdmDy=C(ZwM;EE=Y<7+%TWX-sF41o!Ov6tZL z^G1IsrZM0AZ-3*oa3faU#=@XK4zn~Q$e)lwvoWUZhOE3nxkLR9b+QOg{h$ti=>$s< zv&~~p2qyg}`mfVOXw>`>Ma4dL>AGwkj70Z%y2jMP<)D-dD68;oZMFqo)*iNagdJl%rygg{+ zKdlH7)6_Ra=fGp9eQ<+|jttB`@h2wAcIy`C{Yy1hMNF-^acza}ChX+LD@WaqUQ;`6 zz@G9Y|L_YNP)@y=>O~SaKKKbeCvp4gicO6`Qn@6b!nJ(iG!E_+=E_yc9jH+ zU3iM_=H{Dn^acECa@Nnfdq|>Fl=+F;V9Ca0D@?q$)$Px7PsKNe!No-kvp7P9J6&R* zxh-=1?{XjBHvTcxhvd~m6AZ0>9(Sgu8OM#V`}Vu{i%mUhvch)`0sO0(QCyCWLlcn$ zY;0YV*?i>pbdH;&v$r$^o9QVt|gVc{{3HW&KE4U&t|oH6(^yNcpXlhxh8KW8&Z|g zn4^56^o;|qJ>NqweQ#^ z$o&wnclOg`C7nwSiJOhbztgJ@&B&eM!V*n>Chw7C#+B|`RX*NU&m!Z@2Fxv1=tb#k zROXKcjk5j9W8~VTa7k2)Bk;&cd{=EL_j0o)E-?_6WcyddBe1LTOui8GNkM|L)08sK z5Fc;4F3X}cLb|d#GfinQyLyYi>+J0}cY&KplM@+hDi5Sgam<|&sW~b<=1IyOWaz3q zLb)#4CJT_!`;n#Z_(?z#xzqT-2>rP2mOZf?>(a0-OAS`dQh(7cU}f6f(!gjvrPIsU z$2w&6Pkjov?(J97HCowNZ##4j1$Bpg58uoLs}3Mo7^65B zL?k%*8>ppU;U}NfrBc%K{B%Lt|FO_L18ylZpD+T%Q?&tF7M)XtPSd~ z-9P{PY{}8}>qN{&WmdyBQLNTmg;xHV8Bj1^no>k3GZO9ZJhW=NR$xD=iGE1}FC6QB zMhI~2>o#&Wms?{%-!ksK&y#N8}3nlUEI$JTbl|)l!QG{mg^GN$K1@H9i!<`RM`boe^=YzKU z$cX-R_;8Vt;ZN+brlZ1RA1+A{(Q|5z^pL+K{Q5U|loEy#;)(f|?Uk1TM5Cb-LN|F=7D1UP+_BYu4Q(7Mk?kl8=96S;HI+ zJ)gAy1vYZy6&Tn(DWgH{9&URB19?N<`aG3pGA# z>OsUOPPgnjc?fUT6}TFi50X`K01*#4X@OnA$%*4PeV0W!GtM+u6GdCwJ{FHtJ}tZ zwc1%AioW`VdpC!yRZLnZ5#C>+*i|pz`HL-ngsQRLuk7(qa#Q(%6v!aYR0jF%439bb zFmzN=oSSPVhYWo-cP zb|HPyM;L}zFkF)*s0fM+z#^FgN{lw%Sf|w@DL+~TMRmgrzb9?g_`n7eooL|8VP-}| zIMXvmeTQ}WHgA}n|DwM}2fmN*stnop>hi?|xA@%#RG?)uxTf}j|AlCwJ#r0-!)#ODd7g9e&n z*Obg0)F@eP^J$D*Wm=akMUr(Nzk*I2k4l?`mQCCCNXrY7-hfvORXzF2uCTikuQc(a zwy&<5Vuc-2cxfFPlvTOk6wUAkLaD2AzOc$w(ggAfs>q=k#0FfQ-oSURI_2lsEhlX* zyV;R2JHSDtwcSM^bXSJrpE!EiKm9OR^)iFB`*&p&UkK|Z3oA*qfYd2XpX6$S;^6R1 zbBa;HuBynfd!xk?x3A90G&MA|pr4%Z#6Mh~yDMRPTFNST%n?V=xn1xGbPzWf2%oc&i+;Ft3K}4zwNWMJ00HzY6FV ze0R)XK9qpmko~vqLz>}zeJ)p3`=h1B9}B5C^*$&8Pukq%zrxJTU!das1uZPx61ylq zQrxCPuB{8^*TD(Y^8p2U=2d+CscDt3Lc|eFYHTdcuG?tPFg4ZlgvWB1YEQ$#`e4rr zC4cgqrpA%BGz+Fk=2Y*>u=}>ttYm{{9PBr)z43Z~_lCJ>ylr~^g2|(F zy)qG7_Nz1w-O1uNK*-ND>BgF_|AS(ho#xgP^cbo)+K(@JM49#YM_>`8e{bmOvU%|x zhYRi?1w@mT+TFtQg!`+XB7JF&j(Egujhb0mhn|aMa)k zmq#a}y>ZH|IrEs5SX2BFWzz0xD{FcDFi?bEmJZuZ9d=UPKWzFLj!`|GNAfod{~;NE z$TZ^Xy}3c+57(EHzBA3%YAQ@aOo2j>vmY+*8p&JJ>`5U{?U(RpS=@z&j?~Tff!zg- zH%qiSSuYYv{mIx4b`liHLR>8rze3>xMIYq=jd00VQq`Db_5JSxvO6^K#H}Mmy1C^( zWJG#K#Hv@jMaV0zxqac4o7iCk>{<&SNF&%pauB+q3>4vOdvC~3m(OH;{qlrP^n&Hb zyRt-TmNln*5%c>ozr**w=_VWoXh*#j;*!??MD8^rqUqno*@twwkObK3#(#)N6Sfd+ zN=9Dp{CK~|9XK%5jbivtB*fqQmMQU`u029>2tfA?uG|OrP#wnjv-ySEC-};>%k`6K zVxR1o=@B=;iAYA#t;v-?)S}FULaT25#c@yX(@H~Df%GA>q@Cmp2lThw^y-;*fWb3; z$P$lrhFh6TyWqkT_=OoG_eXA@$Eh8Cxhg+%u=i9-pyJ0n-xUKwU^L5yWU-H6a5!wp z6~`Yg_^Ws}XMXf+dccZ5;3>W4J^FpipoVyfGn1|smQ7PI#b51Z?3AX*TPaSMaSz7` zg>CIv{Pz<3o$*p)ulH;=S(9282Fsf0Tt6yS&mS_?M*^d@KMA^hB!wigsE|U(VxjI@ zNA$SAJm_crYi6t$7m8mKZo+M|i{5?$?f`BA>A#_=^_DDKx_-4#$ayyhVTU1f(~*m) z8RZ@HiJHdP=6JM3n%z>Zi#{pF5xZYfCY+a`giX|cB8?YsIvObV+k0s<(RR_%>!4!R zqeM;O?x%0*;A8g{adE~hHp4ZhsEyEpO}>C|qJWtD^huVnu=^g<4C#*&3K2&4&Ad`s zu*jyXW`uXa-Es;UC6|>EnB*fi?z33OIRoAlpx}iEe_A z=csA>E8Nnu!z&BY1LcZq3Bn=eb_YE7frBaMhu{y`UP(~c&6QiYn6PCj9JTet5D23O z#Gpio>x&=O2PZ6DM1}JRcy~n+pjw4`5k9dCMI^#~(B*go4nh21srHW0=d+547;cRuFAa|R4R zd2y4mA=&qN8wg9Jvq)gf|H*k#4idNa98mYihGlk~=2;|bIJWo7SM zVtD<-T!QG-a7YrVo;hm&izWNJe-k3Wm59vndkq^_TK~8@$JU~;?!rPs41bn-dqEB6B zp9!ideYSSk@z-sjT4`=c-d{a1)Q!WQ$fuAP=FqB2Y~OsDT?US>wDhVMXi6ZGD?ln;7rD2#(G{m2{3uCi{h zigpi+ORg?U@`LOCm4L#Jd7h|+z-9YRU=5zU`Cwa}a}C~zn^NXfm_ZEOumjrW2pPsz zc9F~>852PF$SBpkQ8=b$&NYD0sLz6N0h)+&}2l)DsF#)xVF>m z6MvUhO&P-CSRNm1A}lGA0$l|v=i-OW zX~)%(fZbv)GjgVdN*=9!`QGXT{)z{U_s=%(1S(RCII1xJbA(4XGfC!OXnoXL&ap?{ z9$OWcrVPwBZMt*V*c~G)Xou4gE@Ez#tf;Kr9%f;~oo0E)V?auTm!GEgfyX)goTuf$ zv6eM+-Ll` z#N9h2fU)@zl$+}zb3dN}!bsRUISi|Ia(Sr)RF#f@eX+7XQ|7I>|3nOVWs1tL7b>+u zpPnk4b=AMn>6@p14X`aM4XcE>J#J7|dl;o9MAHkX4mz5jaoCY7k3Lgv>_(zUsmdQS?xgD~ndrP~$AF)zi(nmiYt21r-!Aw!QO{a{C@raAs5%1q~7_|T5Z)Fr}0D*Zwy88 z-^O{^mb7!75=Is9#xH2yhuTjPJ^?zHC1N>NEeFquI}&dcba|^J@!!h+xo71{C>zlw zM9FUEQrm_rsthGF)gGPIgFq?0i;g|oRl%E_lsR{~dOb!3(a<>TqOdQPR{*zIyz6gh z%RdaAoHu4KZIHbr@-&zh?Gdeq#39sYFI^+_7@IJGY`DVd{DZTpv(fy42`P#S4BI(J zLcdL9h11m!@ROR3i@m+d>6MEudpp^FugM?ocrn1HgY~v{aGDWR-BbZS;GiD{_7)#) zyfwMD!Zzc3=)Yj}T|}1WLjOY!%f?z491>wPK;7Wirdj_(MX-_D>^m0S_QPA!S<44GP*M}IvA@U6hKGmMg!(v!8&o;g zwmI(RSBy-GTKWFAm8MJvs@-b8iSgpSBrd>sAly+ z{rj>R6f4%O{y}aWp*u*AowjR6{vMYCx@RQVLSbOV1T=4agVDH-(rWZe+@agkjwg%1 zE)VAiyP~esEoA?&*%irjLZJQXY~bFiR*JVB`C{OHv>C5l-x-(A`g{cEdfTZXs_rid zOEu{x|0;VOo3(Tv-cB|VtbY>}TMia9_ou~!;=$&NlYuopI2T4J#lPNuRxjiTPwAz4zt`)U!2Ep2!@R*vNMDRsdt{WL zU*EDVzdf%Bt!8mvX!?&FAmQB;FNX%0y?ALtGsv_Xf2U;lZ)%HQVooz2YD!*?p(k(c z;^y$i!Q_93jWJ*cK^mvGLNFVOdQriltMXY4syEJ_3R?-t5c@s2RmlX89aEny{u^rY zgBQd;{7i(ur-Gw2F1}}<(;o?ip$3&X0zoo>Ii)s;m7ywtLuJ{(NYJvn4T{SzW&$PU zUzC_KSzTo&!KwE|1(QQF!6bZ=*?adtzsdK-NE~ut+J+bNxdQLR@WZsN_VDQC-y|8qxlmn7G_Xddrh$7k7Z6WDmg1hLkfP!-U?dH zRT^eUmY1PBbsTrH)VU=R1$Vr2@8zjOXVv45c0rBakI1uIP)5@-3183vF^kct&e%0V zZRItW=-XiUV)(hs`~8yPZ04Ou+?A{DeWZ;5lZITOx#}+2qCMM}-+-^u!q@W239DSFx1T2$or{pHr4Dt7;oNv*_vG^IPqrezz| zjL?7^E*P=hCHz#i*74iPwuC{?9wWUnqSkZ@=dw^GF3*yv&1nOg4z~Zq6`JVZ9=Lw^ zXLFJ9^X26%)}UX}MXQnqtzjDmZsYt8eBsL_b_U#-L=y}4oU-1x4O~B|{iAhj7x31O z$d-#yY$Uf6hiYqOxgmj2HhV@vUI>8NI|lY=0OyUGY&*!^JMpXwMC5H*nipTdT@cY@Xr}ZZ zH?_r+0k1es2d}R_o%T|ok)hoeM&+5IM8wWhozr>C^Ee9+mPx z1a60~(hbz=D&>|5V^Q_5eh(0~&1!=TzmiQA;xPcSd5iqW*F&HU$avS-RT>cO%%0OH zhaWZ_EmwtA&H+LdQ#qb$Pk44`$HU&Xpr$%E?z8yf$`!|&vr2zLhp-xXps6l#1Rx)z zv=FA(YITFIUI1&7b{0UokyfV)=Ycl52Pg~$6bCjODegrGe!Vi}R{T&~7|I%k_Ed5T zz77w>0$SEou~hRKG_0o7j*@BtYs6KQk{6224T_8Gg;>B8ky1HXo+Mrw{RG?$i5YO z9?&UZOrHw!S>9Y41|;rEulW>d$9#*wLj`=$62IKRC^#ftmwEAm@teNA#{=D7d8NUT zH_7HacB7{;v+0`fO3W@gl%9H?pw{3c=Jtpw)U}z;DhFvIhw{p!o{UDh8=^H$|#=Ik>?fs(d;OI7V7t44~0+y#S8RU&T4 zbiBr#y;y32xdjf=R{Dm!&v{VwIgE>^rZ28 z`Mg=89G~e}WVAJD>Rb2~Sd?VyStkF+T-=!^aw89l~3td8yO->DeAPMstg=2KkfKAPVNh zSiUB?q0l9M1`ilOCFSf_ixbtY-4~Gzcg{n*MrspsO}#JS<2VFOdK_8l09FS=utLMx z2Ch;I^9wR1^Na!r|NN^IwnAn23@_pSfhiy zgk6|ax)#6>v78%*(~xp%mdC9VrCL^{-SSnnuq(52>n}PUs>2<{LpNbz;JVU&u^X-^5Pb5u1$s zDjbOd7~Sfr0QQxvMNsZi8@us(NN+&b(4WU3{@~7P9oWAPxUyD@`wwg^=l<2;?$gVSG8xQb|Gexn?(1o#C1}923sJ-Ih?_n7d7=9d;=PS z)D5ePJoGL53`r^qBOs@_=fS`vhPN-Up62_`}sYAB9{4RT!GEIhEP{<;cK z?S*B=`CT#%(N>fTzVjq_M>R#{fJ0nfwChHJpulYvWj#`+EtO#E{?8Y#5s=u6@#${T zZ5e|iRpP>K+;~oiQ+#`Yj5pnmy|kGhpN^t;RLt&W2bHtLrO^V%bLhNCK6NSie!Rf9 z8REw`;pl2BRfQ;2cc{{@;0p%$fGWze?WBkR{!movN!G0qf}j;@*-KEy8Gh->7JBT> z)jK&qOut^IeJJddEYazE-OKKlfvGqqt)sHV_zkK%oh|=9PYrg?Y)}nKNaq}$p&Pg$#gITj zFAd)c6YIlqeEY{gwcb5@Vqbshc~=c6Jgsd`g1owz`)nFCZ(id+J1Gj)_!&{GkdGTH zLG}l(9i%@KTtUKV46{UHB9pmMIR7^B%bn z8?`rf`EzJNs9;0=_G9_IhCtnj+_%c3KQZhepT)d-(oAA%-^`su4$92vQqD?T_not! zmzlpYB(ob2$0aJZOa??X;Q!1@SCC03RetFDxv1kZL3XL~t(JSzm8W=n9IG55sZwsE z57u)p7%2K?(FGhA7aYJZA9m-)ATNUv&~df)qoUL2TWk|JjPKl^zHTK|+(BcI6Gt0dV%CASM4h~?&T)w`<#)Uy7nXm!$IZ^SoY+QZ{(=*-{KNQYAqagce_+z6T3iM zk9;Z!+$jyb?xsnKvwq$#(sb&mXS)>Y?ou)&lu5q@)KziOl-IQp7Fo~Nfio-iN6{lq z#Mz_)2QRfIiX#53nXg*q^PCJ?KC?I)T}}u#w|if4 zTI|n?fw4)pPc13|MNg?%qw~yDQ zZ_^=L>2&4K@)4j%YV)Hn4Jn!>7Ok6ND{q&|+{=0#3)`*jFRy5+i_x$us3%ti`B0@k zYtfGdbrlKvYH`l0mWiv)Q70z2rl0>blWrr=tPGU#pk26G%;vm4Uk1a=jzWkxIU7nA z)UKEOv}(f|ksz(VJA$=Qq?44AA-XJvE?cX0fXzzf((R5y$m}uC^6y>noL>Rf=xSg? zFX0h{7qD0W+WUd!wYLqC2>f`uoplc`tgg<3UWFohM*>^wu;9pR3Nb$=CPp|UbYFBw)SRET}tk?x_?lO6WRM$XfZ z4xiOmBP*Q-$qmC=v`Dt&Cub3 zv{e}-r>e&$l4OR14`WbEh*tJI(RrNw2v+W}1TBZ6@vpdakVdlJXgWNL?-O14UKVX>EG#;E;d5FpGWk9YCEdlL&(;g9%Jo8V=V-ZidSs89$BNk?uzSz1q0*h;w*huNg z@NUOQ70IBi5&EvWEe?w%l!yTu|KKz)qYQ0?83XtsIpx^mZ zZ}GnL@ukYe6lvQ0quD07TEsqP#ib2JBOoqiLF-=0;|BraspI%6;iFU{T`iT-G#QP{ z$qmg0%X~jMby(K{rKuK&Ni|xbn}%ryxFg#RR*5OduqBCnVfoLt+2Ta(aviI*4C(l} zuWe41h<8_vLGCFjeIdZ95hJ_q(+f&(YnVcV>s$1&a*4{IqW#mA|VVkEMA8 zn2N1e+Mcp2yhbfsO>A`^5>5l+IDWOcRake8XbEUFD?iR)nVBPm?P~oVd9vKoY_e+v zO|>~t3w^ggM4H)?GpQTe9jJfGKErGyo@z6n-87Z|p+lp!`Mr~#spLeS9%+OG-kmx9Go9^i+0me_7H7|7z$IznIPZ~V z6+g#$&_e0?d68-q)?q1cyB$D>hkWO|f#cchB2LZ8#f1$9FL}CX@@ta{r(>6$7iBzL zS*1o`U_}OsoQ>Hb-MD|vU%*)q(;)#f(1gb|`Ms|*WhF}&9vZV`w|jIuagXQ*!Q7ri z&tN;52Mz9h!lC?kr*zc>Ydj+NVUC3rx_ch3?kP*YEN8P3Vv;(%yqT-+l_FXu_gbfS zcwX=GvS6EOqk_z-bYC94xf5AS=swYN&SSu#N&kfE`QPn^U?+u&BDM)Piu#ZvTpy=s_YAnpl5^j_6bDRayjk>{cFS_|IEd55d>^+ZUVzK~? z_joRuM`OY`8+nwZG9pF$qzI=kn-Uc~%NxHSv>@DeRcf8m#$3t&!^J$I0=U2r!|QC{ z*Gvi7F8N9%$y$>noR)y#@FDS>R8-G!K_k0#K-@ot7#3t?o~3}zlPGuva`>MT8ZmPj z53p`P!cLO8dsz_SGVV@l8dOSm`NG77p&miFlZziu)ovN9e}N8Kn&iw$)Lk8L$84!$VvoE(GC9*n;vDJlE~ zv0o?xZ<7&5u}<{k@6-Q>t*?%X@(uo#E)fCgRJxU1Vi6HZ1!-6$B$qDfazzx71!=)0 zWC>}IZbVob7Ni@NuBDguzTe-y_niC3{fEQZefGRh%*-?Mna?{jCuNw;YOx$YnYh2f z`*N0=dzfD1gi5MPr#!ssfcM*Zf?hUma6gpy?M&auzfeDcRo|yy9uHEt=(B%Y?}v}I z2Dpl^lC%b|y;ZZX2ZJpdzvkM}`FIlb&OnuHKUy225Xc&#QJjX3s zlJJf}Wp!d+aZP<9{z!OLOk*!Ub0P~`Ciwy^|JLav+27iOGul5)K?R*k@* zpoT<3<%92>_vcFFA3rG-`2uz^y$Kw7|J7>#`g=aJTT>+2L!1vl2eElBT6VoEHxlapkBykVO?RF2u68WQF$?DBANNa>)vzrVDm;3D1a zYj1!s_v~igh}BT{bWy{`txOHQ zYHG^&1`-SZH3onpdLuiSDgIvnf^Sn(lXCX8?I^|3owX_cL=C~bH^nx{=H(f%HS(pe zKNPn2Pd%t9!#r?S^FYD)t$(ads*0fo5vU=YePDS2A$hm1z=rcIwKgMs?&jyjqhf=a z`b0PrLD0p;dCJeQ=p6oNZ}{7d_n(MDZ)=q>jL>oX!61Eofei>g95#8tEirO#FJxqr zb5S3meOHph-HuS3ynC6nno&!oicqk5TmV4?RU#r}mN12oPg(1_>Cg)sU?HL_|( zSo#%w9h0*6>G(AV>zdm=9^3%jj{H*J&zrYP=+%;>AyCtUfd@F4F6 z(PjrwJhvf%9E@FvBG-Px=#eY4{qm4dhkl7gPoyk~Q|VNH#Y9lU2x!JneJf*!K~1}D zq&cD?_TBpUA+R%QN3HWv3&<|fd-@hM0UuF9vkMIM#_Ftjz? z>tx7ilec!yRC~KJmrT}mRU4Wo*oe&$&%UKXB*mwGzWOyJUX`zwU6Y8hC1jp`TMkC& zs0L*R^!G%L>K1^%ortdzpr?%FCq862|JG1kOwkD>kt0K!bhAl#7Tk5%c_QM%fEKmi zNp9D8+g7bKTd1Y)Wj)!ZkKG2ItJ*O^Ac9(Df|elTbapp!aG!G6`kELQ8c~q++Bx3y zcD*$fILUJGUa!Hb0{&pP?P!6e!0!=K$&?ffIaKjV1)tj{979Xb*e5x}kR8z0Gw=#U zyn(gaXU$tQ&6arKkf$X$butww*F>u*_x*A7dsVXa8~cOQe8rsRd&PO{HphsE`#jAn{P8GvQJ(w8=N*m_u=s;2Ab(eIE)#@@9JdiBiK#w*~i1diNlj@1Q% z!P&pFgdv(~!{ZKLKVZAz>ql>#w?(2xI&RmtW7yPAlBxxLl$}&|3y)R5B&bt4iRmY< zUJeB}94Uvz)+VsO>_hIAw0ILT2e6}M?rpQ2TL{QYx$bJ_fb&Qa=knHS{C>75sU4;K zsC}Q&uh%{7pRyn;g3jSyGy-$R-_zti8Pj!>Oj&)u;Qu=7Y1+1`jXdkuW0JM&Ug_qM zsSc8Nr{RrqPVbaN+1{i+@w(SEVlopxLAC3a!w|TXn_c}iRO$H589hj#CvOh=`}nS; zn26DH;ERE;$4bY+m2!9Zi1Nni{~7_^qXSyM)+xzne_=PddcUsl&>fbjd=Jnbcs4C? z1r#lS1kTxaNImpS#2lZv8kU&0r$z5okN}-r`v3UOn%GaAkX9qT5HU)}zEeTjFW_fN z$0;M!c}VS`*Fj7|TjrVW zEeNo>Ic5K?4ZG$3e5&;2NdR&BWITp;qTr9of5LeyjXxORL{>vj=Fr;$NZxVd0`wNF zM(w+!$eXB0_n*!QrKAE%7X;1<5yX4v;t_1!y)6zuCZd0cWyoldlLm!RcyI70|Bu%3 z^plw(>N1Di){s?E$Q~2+p%n>qbGHx5vELaB{o+`>?edFd(79A3zqF_-${VkdUrVb_ zExVFCq-mrX8~Z;x7l+T>t?~I}Cn*%V9NYNrjI-bOKedy0lRQ&aYmwWD@ZJ0-kRj3K zYzs^L!2IkAKYarqj0a5m9l=tr6*s!+?#vY)+Y+M`x_JUk`+hwyLX-sY;KaR7={VuF z%zIJl|5Q)Hhi}t{L{ccQ8h=oW0!#?a6njUP&6aU0hd(I$nuH~nHK6FDH$^qH1vu>z z;Fj@WkMgC-_g;@u@7q~nE3QYwXXF+8 z)!&*q7KdKtxXUnaAT__|DkK;$Lb5&Io$lS0%u>_%pRaY!GEV8Z&3ca@UCX^>UFlfb zSkn=P%Czo<(Yuy4;FN5FRKFX%Hc4QHwu&aPzyZx;7I8fPRO$eu2lJGY!%3kF`lo=} z1qVIevMa#?BE7>79Wh#DANW67mwx=Q6|is?0_bWO{ocjiZ(*E!KVOSJAnyHR>`*PX zrx??~0!a1?ja#p~}93X*B%MH{~_9_OMEpVDd@GNw zJA_QKye5?uIdp6eA-D|gZltPaly%7Na(ArEHLS@WWge@ZmcMNx($=IVm+kS_Mm5(K zjY&Gffu+f-IHSE)Tb$AODw&HJCw?o1gkaZJb`a53>`D{WW=hOQjos#_#4SlvRWO0jLL{F}ONCMHsK82hC17zOi3_4Sf+8Or&FWmGIoIUXk+KRYaYOQ_v_ zFN5h&olPDbKW4nTV}t5>8q0QR@jLqY?#UkYuxd+Kd_exV63%&4o;h8xY5FZJltU$W zfxJYt%T;WOY3nDZKdv*j_+v%&gSy80&kOYG1*@!dW5|B^XnKH&0IrUJs7k( z_QPYN%0y<9u&9$NO|MCs33|}Q8wHZfdmw@99a6-DBzk5}554wor=?s@u^!5$+&8QV zDXB)_JyaTbDNHKEk8<-#O*Nwqieevhe@n*j@l4ddnv4@wHJ%3OaV=oUYCoXvAs*B& z!xKN4w)^8D{PI?B@*}bJZzAwIzD+!@3i_a>)jy4?N()SB-m9dDMGp&i*g7s`29Cl;RIOX2*U_x9 zOJ*hJ$$eu$-8rGpzCgTyzfbH~RLi~q^Pb?N-+bUn0;7Xtcey%0mX}ty4Y3T)Mwb%8XY5+NZu13WjN6;s|L~^)*2l38QDyK( zs^U3-=v-{dyb19jUe$|mrVRReq}g_ON*yvX^+_guSa=P&$e-m&tW9p_P6eD9;Uyh# zW_VZfOS$g}-bpVRxW2|&0M8C{g{^}#JthpyfE%T{e{-B#o9r&4uq;@XR$dMNl;;Oj zu*WsW^QD)YZ!D$y-8`tEren75r`O2MUAFy0CQ6!3y%$$2vz9TaU64Ok>_;_A_wP4- za7=};Uk%^SQqG#^we3Jr!MW?T;8OK=U&m6Y_|8l#AW@}1U+!& zmMvfFT|5WDTSWr6p0@jV1MeO4NMur@-NwKk&(4}H_a>>Dc_gag>&axDj+7zFbc10V z*tZ2u4g0(S9Fl~21BCA*r)P~A`&)rMYY2;N*VsC5odd~f{UVv!bVxCVu$z8=cLSO1 ztGwY9TjsjipW)Re_}mR=0z{Gt@Ua7#JwpHZ1OqWa%8|xVfQftV!6;b&&J6_YQ?s;k)M_P8{EkQ8Mc;Rp~-5TQaY+4zO2wR zS|MKaH*6z|9o$I%ed@mWDWqxEQh+3lISq(qUSsWTWv$>!Y^UrD#1#daB5DX@AV$ zX#223ix6! z!s)djhHKwUvtRT>zi1RY6?J`DrxA{K=?KUmnxohW-dISfi>^p-vnManyA@R7i2(b| z^#mtZt!Hi)ThnUqqN22e2-E8bh7Dx{rzx1_m8IUU5o`g@QHn2>BsP^>EDu^_XZuTL za^ws6cFR}d+rPGySoXu|LrD?!d;zU)K5pW5ir?*6t>#=+MX`Y8fTz2bHMW?{P5 zWNOMGBSlX#IpuM27tS_!bBk<<{7ZL&9MD1wKY*j*VC?%~lZNllIQSRm}Itd&^%Q%CZ5p_0Y3 zPCG~zbL`5I%-u-d#`DzJcD#gU&$%hSl2j$gc=lIjhiwg-{x9HJWRISYhp#rD-BVjq zvrbj*-SW#i9Q5i~xezVjE69o(Q)e~%;QOdjH$4^3A(6P(+<%pEms-! zp2Cqm2nwc>%-rR6sPT#};0xI)WD4t-btxhXThHEiBM)(z@ouxwIXubLQS%q4sLr2Z z-f?6IvK$_i=oJ&v_0n7*z;NHN3~S<4(mrMRuZ8fZ3x0WpJ6-FCWHu&tD}l1Jl9U$u z#A2u?Gp}xmS`E8wG#W&!NhUH{9A(6Ll^lmCkzIb2Sv2|b**B8jmETwt2oqIFaM0J`&j86kmeaElQ#P_&+ z8)C*zW|<{YLy>W|+r-!T1;BlmwObq9X65pS3scgtqv$q-7D2xW4V87?XIbESG$Gz? z*!01Orc70_O!wvHs;rwN^j^oqi~P$z(uU9To1aJsBk2^)HAf7Ru{Y<%Ptnnbk4Q%> zks2iyi#H{;&qv#$)PARW&xnanPJ}Ye)?qiv3Z-dxE9xEmG%`(SG&x-#G$w32eIF!c z+naD(4TROPMX?iz9Im~Fzc7NSbnwaVzxZ-6e?od<65rdK3SkHk{OJC(|02N7rudgm zoDzg{&@!6PYkW4CA&MSJRlC2`+SEKYbiDQ3fI-fE^=`skv9l+iGc4s|W;xMYv}U2d zL8&%tJW=d4jZ zsjcSFS7)&+^$1zVv+#a<3wQdwN0A+1d*1Nv$Gj~*IEr<)Wk8u4akhm`(Kas0%QA#vQG$se?|S2&J8FP>$B zZL8w^E+7YKU0{<-P4ree+>oq{F>KROx%i`RK9Dr|ipj~%Bx=QN1dif+RCOiQF^xUX zP7P2NRNPpR|8YxKxS>jE8xtbb{Avm5mhGZuHP`UGNd&jFj-xu7eienVB5Jwg!lI+G zzs)-wtMShOcbp3xk8ARL9N;5^anaWyeH3XLt=ihr96VD%| zDB;L@-Sq0(YcB@>(lgdctggRUKeQnD6ArnSOlT}7;i`!kI=mZwR(O`0uNlncY~|0V z*S*QgvLVG=Zo$vqO|YZ>n@P07HFTrjZMRv24`(Prc~SuriU z#lD$IrX`6rwO7B>(`y{V6;o+^{RqDQ!6>j>|5tnDT25u@f7dBK*986AXhhY3`l*V3 z+JjI_0xtaI`KkIN-pW$RWG~bVp-V6v!bNH~VxVd@>;F&Pnp60YerDZo5MqU)NHT3E z%2O}mJ5t3IZ<>yg{`Y^5`YZ_Oo%K-p@cd2^_fj@Bqa=!SV$Dyo0Q`8xGGsDec%3G2 zm5#4ZjX9`c0|B%a34Mlr-1piUMqO8w+#bn#s z5Uy}d3L<-<=UAP?_u=)de~?_7fcW=JQg+v3T*<{ih6oySZL6#3rpQjl9YvJnal4*b za-KsMx#+U~Y;R+X1Y~nV^Ut^DhBxJ92}65rgG|JHPg7{x%`vLNR(XhER&_SkYr?>( z_nkX^iGc5Muy?3l&EW7@pcQ7evif~S($oO1>!L@`E|bg>OSD#Usp=%eD03;`?G=jC zNH%?#0KQH2_WGj@_A-2Gz^13q0n_J~YUM@NTD4Jp;8AtDz0)l4Pri3%0 z-tj=Se#@|On9F|8HSueTX7_p*n5#D&qEF`VDtWHF;js0KF8-|QmlVC$Lg9wB-|^Cq z+f4jUL56W_otBUppdGG5OCjzwH$%^juohnRtGc~*B<@jzaSag)UWyIjf0gW2+g>Ym zg_lQMJLoQb+I~ep&WAHavQMo{3@}Y8cVJ~!q~Qy{1>l*LL^rnwZ)6>0N)u*HG%v=K zozvILYR*fHwxW5ZZZZ$I)O!Lig8YVS0VB-{b)B8=H<|AoHeLMg-DWqL{~7{O^Q-=} z;ROJbz%XCge~l!yOI9#fjDYV7e2!q={i(pROtRN)+QehAi&(|ALU>?Yn3-^CVfCl8 z?9A0r9dLD5!i2iEDs*vO^VPi*tslhwzdU!@cAv#qAXI>`sewZcfGt8|!4(&{YcI7H z{~h?8@4&O+Jd)ZxZtM}aw_0xJ6MVC=L;cKmnW4Ex5Ha9^ei_*5W^_EK#i2q27klV! zo}My1(skgCXo`E{5QAH8 z8w;nR1n!2a$8IMwTh*!KO_-p@qzt94DfG06-lKT#z>Z>{e|1c}pW!lgE@NK};=+ z)R}XXBeRmGB~dizfBYZxg2fudQXD%HQd89SEqDS#q=M&(zRew!wqlSiUD#E2nCBl{ zeWj}c-bKD9NNeo1&}Mwc!rK_Bn5>S&KbuWc{lb-RDSVk!>R$OQgzs5Z%g!M9RWsH2SFaMa)ne9V>xe44Qf!(6O?!3PH z_uta#M(3~0{swSp!M2N_nkuF?F&O1pdtZ;gQWa{V7#6c^>07H|#}d1PGbOmOBlmV= z3swvmJgS!Ah~rzl;T69+X})!m5w8t?X8X`O&ovDf^}fmiT8Tk@sv1(x>YX2!azdGU z>s%x$p&W!xmgnaC;J&Cgo1v;68n+pax-1hgEnH-thgHVjpg!@F5MeFo!U&w_F3K8=Q7qizdhZBkDRuq9L_z2d|E4}*? zxDourCsn4r?ZGQU#;4I(!ovq-@e`4*81POgCf5p~&_jFH(L@*gCvt;N0ysgr%hpDAV;# ziI&cBwiEU@D>$c+kJxj81wXOPkPn7gXE4NMWajD()$2cBb>@t~`W;gWAUKjUy!*H` zK6Eg>YWthGCX0X3r$^TNKE7fvKyYnfntXa+WY_geplgc0q@fsWJdgZQ)nTbd?R#~f zzQl&x|NC56w``1YnsjjM_BLAICaxXh)A|UeSa4^$!6AOVQZ!_*TF}k2usF zCP8-;Aw>r=ew%AuXeA{+P{=)=>b-2PurR)|BN&rCTj(S4h)@UjGb6JEL8N*XM z*l!PGcNqUfG&J|nSRk>%28>I}DGbf=>(eItV@x2OFNg2o{8jHiXyiBbhw&^do4Vi{ zBDT?8OyZtLfl+`pyDm~m6jc7wYw|Fhg``vCH>)iq(|<)V)?;E$h>P8Ce*~oCsog1a zpX%SuGQe{UHmKc%j!>U+at)>sF};u$c}sc1;A?wAzu>g|J%(h%F=1Oj^JO8wJONz;BY*rkR!5rw)& z2^m_90U-nP%52qaab?LWTE)(w>|$yIW$Mg?P_hL9h?t_4iA zAE2%SU1m_UdeHR=uP9Qn5)t3g zBC=S7v{V~ua=e0{te_2uu#t3OukJE4``F_WKG=6;Nn0jBkC?2Xq7k zO+}os;(m6{JoH?bGO9%tPo_SQ%&a{sjH$pwa7iSy2%3>AOhii1b?;GcD#ByN@FXFj zDa|pusSJv@ZjpfS3&yYGWSDEjLnM@QE$s~MXG>uZ_f(;FTg8+>0kJ$Vw|6*EeR~!y zXx3qvKfOqtSjt21n-lQrkI%2|t-kY?@ULai#tsOl@mn}w+Weaec98xLDJLq)xL-$W ziKXWVzZ0sBjF3%pC7VvNDDCq2jnkWmKJ^hL8-y(&TE(hK+bF4ni3>H(5S8SF?QQxV zbER6Fh~&E@3d!Tt0gHS8flbpAGs+B?B0aIE?q z9wqX4rD7`pHmB9+roRuvx#rU_;)D_nI?`~QkNp(r!C1{ZesRhH3yc=exzONx$;J;V zO?@D(eVSKgn$_O$$jmNVH0X|rj%6i2;Mko7Imzq!Trc2r8MW&YFn#9!qHzni(Bsi; z$nSfE5H7rC-|v`qFFr((*s75zay{`~Qx210+5mj_M$1ZwJ#6XjNqxB%>#0#KM=s6Kr^Y)Qm)Z@1gpGCrRn#GQ zS(?(xp;2#cAXeg9N0T!M$OsU*w7Ft#_dAGP9|pCt*O)BS*X=b|>Kao==N~~`Vsfkh zI!EG=VlHT1rt&CgwY#?n0rT*ulX?ENHx;q8(;W(q_z;hJyGZIacv0_LLg~#v0JcKpNzR=QHYHLd?L}XVFmrq-3ESGfn4Cbl#u$NT=RxT+Sl*ez5 z9|@#H1PAuX_GZ)ypW8nedNvfeJIT29<;mQX_yWZZDlOVBaax3iQ|okXw_vIw;UZu$ z9F<}UGDC)3#j~LPws0qV<w4xnyOAAcIeQDpY>)jK+B)Vjct! z`*eQ0isMfp7XqOHjSwTX5-JH&jyLFy_O%gm+4%UV@Lj2but$CeqZOg_?vsF(VF{`1 z0Jf~=7{2cm92fy70LJ405|z_$7KtN@xotbN)7o1xWWo;7;~ZnkPDMcKY*hm7FIwCD zVu~DhG*=aLm-V)`-Dt`Z_@RUDE8e`;1(D{G?_#f}G1Ynh3RSFvd*?f@%)r-5PYE-Mr zK{Lav`?bQavrPj+Wb);_hth+*Af7fRyvfHheqbF99ZAjXZsK@})D`R>FTP)uE?nqM zpRc)^pfEjw4dr3lg%2CEJD}o6Lk1Y*!Y;$oU!axo~XK$ye)Wv<3hi&cw2YR#qQr01S+`pvXp&> z$Wr~y!n@2u(R;zuDGv8PVFRM^T{r!pBkr{vFrNYj))ptQIF;5Yfjf4E>v6M(6yT&d<;mhZny1OHae0J>`y$s8{bWPh zfV6}WejSyPD%(@dJcI~Z0YyE-Iz@i!Tg_KN2f=rw=$dZHvUUNqzJ;;GwLL9JN1q3w ztBrEbb8o=zY&my^4Eho>o9QfU4e1FuR4)%nukN1~MjX~E>rs?${V7Psum`CeXE*{{ zGdF1dU;39+atEP_CGr12jw9Y)l&`s&6fgF6+eL$W0gtwMjMcA&_hK@GSky%pU+vpPgh)|bx4cH zLIK3xAe8;L!5YZ}jWxa02f<+r(383SIEIPq^-e51bSy|J*=t`xsJmrEnL{fu#rk8= zJI-aqYFXjOs_x7j^A4;lkl=^DFk50Xcc3^SRUpBVwg*vGE^T`*K=*&2E)5jtRLej* z#|vR7e>E1Qr3FaeRCUBaVv!j-f1~FHBsg>p{4Jih#Ns$n@G5#2I2b)Qtm)o3V#!>< z$o{t1jO;<2_{nnO`hVP&3MKhgFf=Bwc0`O?oyuf`YC7n(#Dyx0g#QCt2WQsvmU;Dm zv*E2ZB>HSj4ukU*1)SPLjEPg&1JNS2h)XYRug{+!j?6K{);t#_v3dEd7%%l~hFcaq+2>x*j7WX3Dj}t0!UbEG%9+2zb-fhD4k<@} zij$&9d9*Ue_~Ro#olTaW);lfyvkrxALQv#49-=Q0jHh(%8k>}CJ^aUihxv()rWx0u6Vh}RSWpF{QI5JbiLkz1jM zwl&H=I57Lg$&3V=@k#jsu%T_JOyE8^HByE%y6V|1lfTfvU?Zu0Y;&kVXgtErw?xg`AR{dNu(d$!cZ398D302{`|s6|TUJ&$CYykHN`b6Rzq$IcwL+Pu2T(#A4;bZ>*c=f=dksQ|^$-^(FkyEE2WYftq2H;J`&#MJ-Q8}`kef*6d z0eug2&O9^A@yyVy&M$9$TfN7y@3y&zk)in7=e`pL;`2L`anhfguV?s851k|PA|aRD zSMtPt2I=d@nl6Rh3nGN<0uat-*TdjnA#Z+hIm4C@KhWVp2X(xHtPl&ld`nVju;Cp3 z``f$_rE4=iDug=##btsP84G4jC}AkD_76YN3h%KHf%KxwJ@XRFrZ9I{RfY%>=x_Wm zzp01C23vR~HjfO!6)^EkPbVuKSIuV~H1`Gnag)S0mGgc3t*VCD3!h_(Vh${vebU+l znHMvA`}9}H=7y3MslDISpdw(LTK39hrmLF#LCcUF*G*Skk`7g~`!6gklz{E*Y{kfL z>crz|@wvIk+{?}kqerf4segkxU9Iy*!c(Q6s2=}Gv%=hsu%jzr?3`v^^&r;nJN?hs z<0D!`?zd~m9vk#ADb~eYJ+ri&8jY&%<*QM5BEKrVn7;47a(CqV-R=6tdP&0{BS(cV zVz~%dvREB2FeL;rzS;bF69B!0Is?3zj&w>y;cI(<-ypnE(tu2b^YvfD&0e66g+q}5 zL$ke?7f4sV^2FW&2?SXvmqz5%4AB{RF6FYG&*Ss06}{++#5d{;ZTLV-baWPIr0)oi zPAMO5K-E-}XluTbsi4UJ0;e-LHbg`VVKx=4ue<;4Q7xH~ zDymC))ycYR=n3X}Fyz>ntMc=Q`4SfwvyzU3*d9=r_u|3Aqsk7Brgl-464u@hQ{aOl zipq}0n&mZtmFs)}U%*l7GDRigXKuhH40}NZ4O&H18@`mZ9d!39%`YF|9ZFN=u+gv> z)1z{1$I#K#Z{V?!M;rUAL6cykkLRdZYK*Vl{B({)uKvsMPuaVwEFEB+@*4_Uq1|-0kxB;*Q z8~}`G(x9Wk+)eEL#iG3G;IsPMD<^`6(qz=bSpp{cQguY_uSu4jeVIn#gV;L-!GWBm zrHRiePG}9Ho)!Qr`f%)OeL8!Xg5sd0U;$H==Nq3+qZfFg+WM93Ywf z8|#TYENA{F{>Ap97cB6W=J1KYFnl@hbnn^TT6LQ+8Y8cHO#qM24n>>pGDRW3Ug%G@ z%OAmsm|HeF0f>KO?2!rnANlp5a&>p9E%PTWsnT7Sa8=8;oOt#GQc8#HCxS( z&_eT@YHKA6SI#hLNpJ}N{<8VvF>&q!B2nI>CPRj~<4;XZnQvk!4f z-o+l)mhQ0X-+aEFEiBvE@Mkg`OpuYpmtfe5KDv0oGd+ZmSqHN=XEt1Ydfj~FBcHvu z-{3)b#O@u)b)kwBX&9kbrzq2klK6@le$Y6#9+X`OMYnOQpS=K;CRW zi&y(y+wjrdg$+OMO{m5&hoYU)BHk%QkM?<6mB67c-O~vdqWJXawwO$b0zQ;l_Mt`W zoUZol1?kE6&d_2yOc-47#uAamciciaI^4B5WLHu%q>BTqz0!f=0Lwn@{wk67beH>@ zR5COAUuVdKOK8ZjO|rMIcow3P1GZnz2W&Y~D89_7@dG#C*J;xgYQ|8cWmK-7*6^H@ zZ`jG7|G-FA*nUkPtz-6+93>iA&XMjwPAsn2R)5QZCrUw=U=YdsHw(X&@pG1M3d>di zUnWIVTeyGvH-98|tN+1YzVslGhDF-dPvx2P>QA#fWz??y-q~(%)^w5_@5ub{dLH7P zkX|H~*(Ngtl4;F%ft}dY3~<#DreA#u4oMwr6FA+xP`6ntX)s&Q(!bmFHH{E|=lKxzm5ZvfJp?DAHdpz$}9m_^9yxDm{DV4>p{)+dMpA~kprQguS%2yCIHK%N)~SGs&LG%-uCx7zwELnbqZm26g)`mA6kpy>e( z<3#@Sx=d{zb^(08mvIq9SgRS8e%^*_pXF;IZVan~&iCG&?hb`ep7<+Mp1Ef~`IWi= z>)&5ZR*L(~XB2PREm5(&%kxF?7XI3eI1T84uG}xaiD)<4YI_MLtXV2P%{Y@O841=v z)o0Cv^xZOr90ptZns|<{Sj@mZ0CD`m34`mqZ(Dq}$TrEy%IMUv(Ac=C_-aE+2_lgG zcua!H&)l}V8>^jZ(XLYXd_+0 zrM^;cg+>p*=T-Qb_jtm*Ene(o2&=X9a$G|5tjxVFz8pJ*bp~iyDa%Y!z8cAkHWO_a zPY5U1zd`2tDM68Og&I_5C;%b$so|1)7ykPHqBN(lxI>Jk0q(8qZ~En_ zp&4%c%RT1?QHEL5612meEb~sbF;{xi)~xP42cf^u^_5GzuEF?x5lBCZ7+hi-a2u^5 zPc>Z{#@H$R znn2XWU)>LOxugTTmqHB>n~X964{R&g17 zJ7=U|Vm}s%%i@$7=$@sX8o&+)0}ujzYt34aXy*D9e6X22$O;3&2Fa+CmCdv^*;-t) zuMtX@^@!&r=ynGwx2uv-{d;AN(M(VJJdqpj<8Z|aR6j94ttNO73uB++H@qIe&t!hX z@NRv3cLLyB50#o7^hsjZBr0^*odGgBL+xm9;BwH!y<%BA;Zq?iWK6`OXytE$$)dR6 zq|ffmXmNt?1&7xZ7A|gszGMv~L+(z}kasFf*OQTh{uZc(Znv=*5pUq8oed_`?u*|j z;|X4QdU!U#JX^>#$&g8HrRH93?jj?NaN`3g(tyA9jHJp_<~{>_k)b3 zG~*GRR`Jozb?~Zf3%`WB%fY@5w{6aB&(FW?FDKr_hla+I38m}>0Mu^o(YQ-GYXK%# z%WgPjCWcsuq>`!ht*F-Tv>s6&zmf%ymDzL!tuV1M zL9=~<0UD}S5-BMuCfj5rVZdzZfAmQ^t`3Of+Si+O5t?_tS6(OGg+EyRvOpqzd2G;B zIpR$m`m0lQ@vi?dH49fNAAn1B6KL~Y@LJ>{B=ug4#e(O=euPn6Q%0R+OPuS{uq z8QI8DjSuk`toe{=gZVpD=m({AIov-a%@u>jGsIM#esR?TBMMj$6k|NW1!(+`?jJKj zc5xmrFl40N9uD%zwHMF-vVGp?O{kHdp6}5Vkv|k3|Lonnuv^p4j3o$zNI3_n{y932?JR7XHx zy9OSg2rjUK!Lhd6qVm(mqO~8^<^K7dY?BV_g~;+HJRND|7((e5HU5MM(omi??f+C9 z)=%Ev`+MY`aJqv+G0sj8Bu{5R@5K?1*2k&yCkT%GwB4Eql^-`@UmJJ$z({#3%) zUdB(T;`lF#{P%t{D`-Org&XD3i)jW9C$1tgps`1l)BIS{Qb3o?WpEy5)9(9@DCeQc z6v3b3XMVJnZAmdY38}f7X97Cd^L^9>vJIq9!&XT6$*AxOzn12#f6(DCHRFtN&KGir z!3;;8l=}(*DH=d5bAG$QaH3bFcjaw=72M|c`L4(`-j-b9RCLfjrju^tJX5#fdw(Ef zN_B+1wfaLU(1Jk$o(ict%37tT=9)tkt-+&pJ3&^?DP`h~G>%0l=7$nrGtY>0O(Nmt z;B(mrkZ`OkZ;DxEK%*g|<+%obQm5W!&4=_^zPEAKCHpUp7YW=4n@Lasu%mc$B`>+` zg1hNg^_7Pa(Va#42p_71gDrq+)XBtH3*vaOyT%1@0XumSyQQs31NoB#jyw}=riVR$ z55|{^@!2P*uW}raqAY3YnzBzl3z=}9aM^kPjV}ck?I;=l@kbd`f>&^O`IUWOzwsr{ zhG(BzO_IvrE6v4spQwoJz&g#n|l?)w^Kw3K@&^vamtv+q>g+?HhO{86Qj zRCj|OY<=?_RI3NPZzrOtlu?4`4mRbwqh9nS5Sa68dd9$vd`+NM2&0iHuK7jrWe+Rt zhwN+G_mm&8#oWLVLT)9dABjI34sPT7lrMxV%bb>lP`La!4KdDfd9?*wuFU3Z7IZYU zcXIy8i{w&ZDpt!jdlTk_;@RU$51;Xp!r2aRz}{jFqw_WGe^WCuw?f_+GK^OialPUB z-j}5!QwDQuF~E{0Ukk!%qe_7gT|KxoPH7R#eNz@#KOs4y9oLSowBzZsEswDPcWJFh zY^w)xipWkE-@B9}bQKQ$OR~r*H*~c|0&s~JUgCnDqiI`5$i&IMZsu6JzLrD@%l)4G zRodW87S^{CAM<=%>D}deQFil z4faYH>^j0@B(i0oAH7qebyr8YA_Z-UDM_ZzS1dpOQ$U}01bjA@1_J$-%i?3B$V#c6 zw-l1C`J#Y(lK7oBkmUuy=i`+LuWn0F98U&n>>y4jz6~Tz>+_&N1^y&iJx>sD{$(yO ze+!~ob#%yb*!xs_l7(ctl<-`iGWGMD(xLWxc}~9wVpV33=mQ`xliXX%(ZZBl7y8nT zm?wBOKVd;x6Nm>wjemNtbp`sl{qG@Jt3VT?%O}9V;1m0_Iba9^)_jVT2~RM4>LtOy zjKpD9>Ge<>K|5;h#9O5tiQk{~sksYoiR3255uwZo{s5l?HO_)ro7Xx#3~!RwFPiSFgN z)drRxn){_%7s#*c6!m>078?@&M-NpQH?s}3^WG@_QAjTnHmQWnOxg6Y90EFhiuRXC zj!ch=hY6$$I#N1TQrg^^lCIt(W^=&=u=0GL2h~-)^dTN{ujo*IPF%ZW3eX1%D~j+P z1Xqd32wWoRoLFq4+L_zf0*Xo$7RC7*w9;r57n=oZjjEFSC zLlpK=0mZq5t(fOMUt=B>J`RMT)IIrZ`!%NV$$-Ni5104;tw1ybLIFej+-w!< zXsdwS)FF-@#KRrN0rO6q5B;hsJK6rJzVsN!4!RiQ@JN|^D60b*|3=4Qn^` zved6Mv_$CA#7dgHk=Z55c^EK zekB%5%pugV=jIve=DUK3N16CH>>4V1(wyA#;ekvq$v+5z5ctJgZ(gGrpSPzxmqE`o zio`bX-G^5^8I}*fcyn0i;WG3R4C)Eis0H3yb~%#9yEi=W4}1`2%I0g6d5=H{*=1j- zh#17~S`B|V|7;f`ODJa>&}N43I3S;l7P?*0qfx1Pi6!T{{M zqqKO^nZ1R0LRGeTzNc=m|8hm`xwHDK8mX8dJQQAg;cHXN>m||$L#F$SH1;)1sU|`g zA8Lx#S**Ydy=6ttgHXC$BBLom{pegxi+c@~zruE;u#q?l7B$XAT^+zFdX?;fx{=Rh z=+t5Zf5kSY?$$(8n=oNy0J8!hQmB}qHmE1D(dC-`0}Vq}t7a(dDzM$j#R1sAkdY4< zJd*$da8O$gu@ThMRB-vfJXLDG!Gf5yiB-g)ATVe^o^#QIze1a~cozNhe{pn`VQn={ zv<9?LT#L526n8IPptw85DaBnYlv1FZHhg6W{ML-rWtuqwn9$wBB6jx{Q2+p*%MPNr2oLKXK?8oQQZ@x%(=&0 zw+bXOXVeNGg}kgUDaD2SHxGb}@5aA7=BDxi(qx}-g_Phi{v_sNaV4p;!uUupK$1uI zfRwuorDX<;>o(Y~?1$hgKo_rK1B~o3U7~#9IB9{A7~`B|zW3ZP7M;+>yoKkFawa61 zp%Co_PDevAznlh>7#hGKsUyR#T5~9)p?Tk>Y}~HaI3kMwNdrP72I^(SWd5yomF}op z&|v{}^8<#kc)XHf0UO*vVY)EJwGZ?spl&oZ{u>ox`*_1I#6M3CnJI?HNC7|o3m?C} zBv7=~$ZI7cb~eBOD$t+9sbBJA9&=yDJMog%|E53}C13q6R!}eRzU`&B(G^-itP>37 zhZ7u)G~W@W#2e#vmzejyGAekhV^VZh2{GO*(j}@&GLG>k!VK8}VY$`qazVX)I%}ZE zfR_mxQGf((r8XLA>aPn{fbZZQr&9o8GYu0V1eImMv}KO|*!88FVYqb0F_&PzI7n(7%}8lGhF%?==5SJ-)qe%k;@mL@ z8x6=i0>6=i)9Sj6!PZZ0?bN!OHKuZz4f?K=)Wj(>#fdUhC?KZRMRDocLIx&Q<>#XI zo|=7B&K;hN{}IHZe!w(;kATuYNdcWen+h}f5w2U{%3i;{Zz;`P7>xCKz@$VHh}*zF zXjt9+;2R|YOxWjg;F`ITuNo?+1f?)BIHi}YO}00Ft>_0?yd4|;Grl(;BA8xhgh}6! zmHNV%gOSmfP{=gBkita66Zi3FEJtd9q0hmRxA$6DC2q=3aVDtNhZCZ{KEOFey#XLmz_NZEl4n_Y3&dnE)_47K*nq^}=yA)3Jf2a&`tXKKqBp}rzQ4u6-f!_Jx zBV{&Iv5aAyATRgdv^kh>r*q)=7gbWaS3{+^Z2-+Z;M-;_0dxzd%qA0OyfC#MHyZd>XQeh&He=2X?nP5U(Kl}5_IbfW&UbO}koFdl}9iwat&ss*MR>xIz0 z*JJ5ICUQnuu_rWxm#D;M>X=P`DyF<=)OxQ7csum7aQ5bJ5RTGu9W#olyDU+xASjKc zRhF~aRu*_w=P%WKT_VFZ?#hnLHTRn!6$FKWYO8%e3&eGwQH-bBJ6% zI=8y~q(!DV-OwB3ooiq?1kFz5`-uT!F7?G+}Ify!As+kq( zpc4fP{AYfkZ=V~tk`73U4467~>w^*{^XW*rtb#N@=V0hpX_(oiV|b|L$W~=@8cKn8a`;v2p}# zc3Sn$+W6i+rE#Tf@I(soZP29Zzi@1%F0)q^S3ins#BucOQOkH&OT%@flQIEjCew)6 zz%lIF=A-vt^_}b6wwDrZ|xOam7*dJFsLaQ!T%O$20T*#;# z2InB>ow^;F@v@&kV%)8W(amsIN4)>Ko@z>_YHCkF@O^KScd|^fsR;_$Y2@0M(%kZ^ zg&{{*rVJPPZ(vNc98XJ+YiBcCPbekpTZ*Gwi}bt76qj%7#(|oa0cz$;<2QsDU{A=8 zvarB-<{_%xMIDo6r-F=94W?T33LdafE+*w9BbVw%A8il# zp+O0l>6f{MwI^k4RJKV}W+|e4U4cdQ`+~cWkUhQ6(ly|-@Z~!U3F9|0<<)(C@VgL9 z*y7L;Hze1z46U>%=Z`a^iyp3u(8Oow+wz~*O&E@u?PaCZXVo6L-tNLHAz#zoPJLsW@*2^7W$Qg z#ZK&=+(9nq?m17PLMnWV3~0Gpv&lm#S;`L}uHw@zLBC z29!MkhgzxUi?3gz@aCvHqJ_Gvet6RHULvvrdrlgY?jMF1!YxsoV$@xSDSSMq(?FEL z<8VWTq4jnTA;(MS-U6Z_-|Y*ISUdlP5N}$5uZ7^jqAI)fbloV`JSBm@MF8bq(m%o1 ztZ{$m)YV1v)d8j)z4l+ z12$W3snD0(c-H2eGq=7(*j6~Z*XW4w$g5rR9-_Hs(joG;Ey1F3|FKlXpTd1DvGOv5 z2?y0V>z?LpK!!ZjN4A_apth9qK1QHTkQG7rJ+%?QXrgkP`E2&1mR&D9su@>pBKx*cZuk>S(4j<>ePY`=Hq z&$*&D>U^pXr|C80$^pjezh!gwa8*9%AS2P|t1Qcb$y;Shk!Bi+Nj@$3Z

*MMruzAc9`aeV4mOVXe!g)p!=rmE|XzS3l&x zKJmy3>uzxD@eK|!r;+Uml-`u`dw%zV&CThzI!AY|CE=gu@mnZn$v8o zvZf-!f-{0D`Y!{#+dmgP>wtAy#AHHmyQ~!4*~m85-`chl^RpqxRsVWOr}+SY2uK*d zztqh{bIy3k|`U6{? z0SGBl56ca!ySCI1E!iK}_}>Ay)4Ye@zPZ2jv{VT4A|;&cxmc5mhw9?#pLD+mThm<=Md&pPq4TFz0(8QP=)m zR0pU7B)U4*3{t7-*k>;tHg%aO_Dd}b(2VphfLL!;dSQ4I#fFQ|fjZN0sco=jm!>+P zG9K16|D3g4wZXQGxH2GVBn(B}J1-V>Qw!d+JF_)&@evSL`L(#GxdEoWRumgDEvT(p z-xNN><^ak_h){%CxiqXM>df!0&|B@;0jP&VplMljMkX4uZQ-pqrR`CbyPH;Z(g{yB zMR~TCm+3wSroGgp-B5WuyWhWS4fw6-mzhH5NF<Ik@|!O`k`v2Bx42R3QGRJt_PclW!gzL`HKS;EKL zo@^OJ)>W!(&?TSO(%0^^FJ>WTz@u{rkQV_72f{{LV)`|Ji5bj9fNdTd1}->!o$%^f z3a;6Zvh==E(@xlSy2}^aZ}=YkN~{N)l4J3e2!*pPLo0}HiEg2LdwUDLVNU9i_3f3g zm8gZ|(r-m~yVy3({6Ho>N2lxr($2UL3c#!1u8>AOi=1jF_V{B)lSkyMb1w_5gn;~KY#dBk~` zW5Q3;PGvF|9J5gz*NX&qGk8VZ`@)OZK7Td=|LWtSDl1EMx=#aZ7O2?30ctu+jbkQG z2eNXMRDRozAP6=ydgNgOJKytI&lpHo`2b=O*d@LsTI{r?2T;zybV`qsU&_Sg?{IV~ zfcgC)46T&~e%}h_DxliTT-8(Ec3Cd>P! z#w?NYW%80`L}kIf2}kA8sk^m4?T!FJzfQ9qQ40zn*MupD}5y(vx{ zLP&!kX8V5p(N>yT)6N~Oy_U6ymxI`-7VMaRddvjTpylBalaEEC69vOG%*`9sau}cm@b}BpYpRe4m|geoz^_yy(?-`q51E zHxEpK`=afJC$WD>3~v5Rsr!8V{SnhZAR3!3(cL9Ms(_s>P&cZsX>6)9i)%)bCtzi4 zPLk)>wrYyluY&Hb5d5Ep6#1=(j9$$tREMMFsVjq7IwqLVu(L*}aR$$?yOfS9sRB8; z>D9=cLE*@+s`-r|0YNWGy=g`sydjx4eww%R_%}!LN`hVq*w($|rY& z^}xf{Lv`VTopEE+(JKCS&-qqr(k3-vG0;<{!}Ypu;Qem723KUvh4fn;b5Mvh?oElh zsffjrtj-l*bk|`{b=!^QPgCvFBaa)$C2&)OSc$mr=$nHzT`EEs9ZJ zY|$ByV1tkZxEtv|l6?)BUj}@~hXyY#s^$wl*GyGJSIo9UVatY zuAhP6LC#y)G8khghIc~;iaOdgtgT$;*O!dJ)SmNn=EQC{Fykl{i@%^G6~Irk?9NPP zw6>IC#@CD+r1v3HfSPm@9=Oy{^v#NH0o)oWXE8#3n=VWuN8PnOnKLj60j@?wGxP?_WVj0xj1rZ7q*Wia(ikKC?h80ep8Y$ zQySi?B-(XB&0k;)HJpT}Hps+*v3A&jZIO3#umoU+tFum^wW zoZwYildJWpVN;FZGUXYS(&AQQJeN1!m6r=Dw~05C?Qtz9e^yIPu52x|k7Vjns6rdM ze^p<%S1*D4jy2c7GG|I-c-`IO*8+o)8a?>o%wD`uk#E8DwMR0Iq4b`-&I$lpoCQzQ6_KY!fWo*KQ+ykxRj6qE+Vg2{}yJdR? zwIQ!Uw^#o1Ch*6NnO>!oO)-f|wAS+PCeSFE z1Xzg8nw!_ORN~#h)WNNH_1caO~ z{p&*caX`}QRn?~tBU3f+nkcoJ`v55=Y(N*Tb=Cpoma~g;uXp2Wh?#RRaqv)mXWNy* zmC+p5)Kwi&ipjs)d}hSuNbdJ1blMG9L6^w;zd8-KF;|-OY?r(VM-dO(mWjWSX|E;; z=Lvubq@jOKqD~mwmWL-3d6FW{)KHh>Vk&#;%DWCZSBZ*7c;O{3l*3&9pC}Ejc6<)E zs)YE7-A+t>bx5loW(*zZ=a!;<;}(I}+}uRc?_J6JY8_;BU&Q6GIwpubZrVMfsl7AM zB*0#+2N+kHheEObJw$2coc?Y50dvXDSYpnFi!5_Umk#{ zb8|a%7&b+ruP<6z_dIFZ{GTBj6<8ZmVyc z`dGaJVyl}OAVJXsJC|(-Dtg~>AdG3xS zMf&0DK5@)c3e>%AMoEx{;tifN5&)WA#2-6{gr0X1;aHzt`dJ4?>J4I1>7PDk_03ve z{43At9o+F%wJ~KSlQXPm{K|s%n>c7AC@N0I6U$*eB(gUV|M-n3w!`SH*|8|Fw&iIO zyb8p>jA_u`iOI4JcYavtRC_0!Ri^hqbP*AHP6qvKa8f>n=Zohsx(44zc1)I4pUlP9 zrZiH-IG)V9$GH5rapxQ30`*pAq&0V88nyR8gL5G63t#Eh>j^)PiWyAfwmeUQJItWV zGsIpb@#5RXrSTz;nFODmyBy7Jis)}#WLfY&4{b)pw@~p)H(XKG z^L7lm0qQWdp2qKOX0PwdQ4%(k8(qL(hA^dS!>k>Y1g|JNek#u!==^%deam%1rW)v; z#d$w;n~DZOJ$|nZKruxV$HqAow=9dBJ006Rm9X>8-cZNX@u0M?dp=P!Rv|Ny-sTf;<(yc!q;1zDQE))>Evr9vi zg}3oDbL!5rt?Z7A)vcTfKX#zI!lw&LNX?U=SIEX8c)QBFZHku zXjA0Kwd}PA4PNP=8tgWQ{Z{Ebe$G`f^`=^(nanx1HE1bM-#Ba=s=Z8X|yb0x~rYj0IOWK3{)to+d{Q*(n-|ITvs_ z@B`c#hY#ql+};Wm`^^mPcd zVk2+1=&#BvM9ZjmX33HJJ8BO?{ws%=)PLm|7A%vY?{MSg8rwRbXlwlmv#qPFst=G( z3Rky>+(h1`x%5C$nBFUcX^RldXLD42Gk92Rmc9&L|7p#_i(FDDzYz=`xwL|C2TjF1 z6}qnTr6+VsozPn8fUJeF%1q}*lM0oEZS4hrDT$^LDnlfM9o#Jt|Mk70@2QUr$@0H( zUwUt`coK$3Q|2PVYnBnDkR*YTKR6k!aBj9pD~q(UDq6n}?m!=UMp@n*p8mJ-jQ(#g z+F^Z7pzrKx2Hpj+1GpG>giay+Zd|Jdw_bH(w2QhExeBELBu04)m2T1Dm$#_Yi-j|N}Lb=tKXb} zPWPGAxh=sMFJ709hz%IhiXrJ^%3k5@*?iwD;;<(H4gO5Q_DL5}0qG0h3b{1XZZ_(U zka2eDUJrs`OjvVli*8ceO0WA3P)v^(!m}~2&Q0MDZ={Hm6&Ak=jF}QGiB=IV-O|M` zztT^F*H6RDj3Z29hAaN+?;=|alE%P+x8-u=!=IYa#wwqjcDGD5x-#A4+%J_3V4t)H z+yBlRXb$~pM;j%f6O%qZkQ2Iq0T>junQUvl@R{na;b>WVg=mJ`cVgaV63!V0#$cRq znd>#O_My%9Cn95<{j#yE6#f}t`m0%ZH4g~EHn^?2u39C&L01KtWAKUQ@K`{*A|SI^ zwd&zTB-*lQ@Gyvb8S0N;E@qk3UyD4=S}o^aKQjseV)PJdR&p0$g)(xBtGF<&LmuII z_E?(1PQO_OpN}_p2Hji4_^X#wBF7dj&y4PBjNr{fH(bq2-L4O+ZgIRwwL1?Y_k%_X zh(PjNQO^-<1F1UI%jL7;Xf2V|Mc2U_gxTqs!{`uV^<;*cw=1uo7AP>_>>HYbYE-c@ zuUEaK57HYH59aPiCjNqFV=AEQ-tyIeT!t&yGNfOA-pc+tyuD)s6E=lk9^hObCk~<) zbv^T4OODscy`Ah7ahH z2mbCxt8Npm-3R1N_x(NQ^v##s)myvf@VW`zo zR7F+T4Ri>-?!L?_OJ{;{r|$qU?)mQf!v`yCSI5MGg={7p_%~W5^F$TckJ>plX8&&` zX5y=_9J~0EW5cm4p)W7VB(p5{<^-B5>g+6mfTKEt-0n;r;ZGB@yBd@VGrlq z_7ebqUq82PP?A3%?t>umJM5o5slWH1zYH;S0{GQfwkIPK1S>`&Euxtz#oc}$z`=N> z0)OzdZ#L;%LwgGxr_iQ|2~?|7MI-5_*4WRt5~3bValpU(-Ctv_%^RJwW}9v`D(R#s z-O4Kn$w}SP=;fN-1sOfx{jyM?(4RA=sdDJ#@mHcl+Fo+KRIFQCCwgpU#;HaSWMWG- zbo8lTr|?p@@HOAu0XDMHnZOSMC^@4jK(C>usFQx36U~u8EQr35G%-Zhx^PcMpZYG9 z@GqA?14r=T=O@3DcQa;tzz z#OiK;h2Md$mNHr8q*;Kqzu(cze$VM=mRgn0j?n2C>4UYGRuN)=dV5eiLRR$Ya2BNp zPP4sUzO}w96`x~o5U%Xf^$s@Iu9#G&0MaZp+;l+{`@;I5ytnjEFl@wTIdG%kBSGKv%u{R8VM-~VF?asj7S+Of!y={vv$-_8y(B@i= ze+JD>FYgRo65Zkj3!S#_bT7TibS||;B}7Yrnv@CFah?{9z2IR3@)%;TS1133(aWj4 zA(SwMeqNiP9P3ewe3%nEKfOiUfvOJQ$~A>JH{ZqgqaXVteAvJix@o&V8EBiE z=>xAwE(53#TvEN|;G7eg_&P>&Z>!zc`bc_aIvlkTi(euIXq1&ugwEu>p90IO}=7oUAHT`ysBIMFf7oUsKR#d}2FOf297X7_i3u1wqyM_fdYbg5EosnCU| z|D0%u6m6;SV1_iGLD=C!KFWg}Y{f&po+4C=6#~`Mpb>o7kaPbkiG#Md78(jpIx=g} z&J_A;996(Rah#-+GeMYD?&&F!5J_H6>|OHf9DIE-dxnR<(XoNqoehq z-$zf~VF7Gk(Vr7y02~Ix=GCL`XNlCB*mPf{@s4xm>xc7$;sr0GH#?RJs&ovgV$T5x2urWcF=P8`jhmyf7O@PFBm48`D!v;S?weenMFqoj=Z%gB^ z^9=&8@MJ%D6r0+R2S32e3%S0AKjtg;D64#KKP-1#oaBfT79f2-h&x~E;p0M^sl1*B zUX}YJ&`zPdNDgk5vP==d_U(JrRoZ;c?*pIn$J(%<0fsMV24cK{mPJ$E(xoR_t-A~7 zJ+d;tG@pMJ|HoN2|4566H4-PBX*e|3a;e>X9xmOt0e$=|bE_Ke%X3oV@@AcDOFp*O zBBU}?KLJ+*wW=Hn_o)hU347U~qbDiZe7ey0)x1DF#H;Pr>2q;bc<|?=4F080>7I*; zH4)_+lZ~y*SxGPCeOsxlgt#04HJCvRWAn0S;O$ak~q*#=q;oEn*Esr>y3m#-*>KXk2~ z=*+ac@X#XIHpQpOUq8!N#kf=%+SwI!K4H1_bnv5RdePO-K;m}s&PAqFoTeAyi!H(S3ZgvZc`sQBsZa2Gaw}C44w;8j~ z@6IoSJ|ty6)7+sm^y1hi&=pbnQkoBHxt>q)pN@zfK{o`auymaEGb0?Pi#re7jO*YZ z3)%kO`T43R5I%N-FD(+xFW*;Ca?bSLo{qWB4@nh}nlH)MSCC~I1*1;s8ylCnN?t#A zOd^}(P|MEK1zLpb$=hA0r?7Dpt;sbi8_A7{1xCMA;}y7jNxwGBvP)dzwq8*_>?yf4 zT;wnqCAd^4|8-4#&ny#XP~nI?;#W;0iz4LbJF&JDDf#L@;c6@vsV?d<-`1qcEH16( z&UclYl~1&<`Q0sATFsRCzlZG>{se%s>o((Ik;AQC9C2i8)l%9Ia zv}wKZUwJ@{oiw;tpAR0RoWYGa#|C?PhjR$2YGiHOu$$&PCgB*hAE_e#eZEK&3DgOk z3}I8%QUi%VpcXp^_)2daj#S52b&ja9L&)Gw@u* z*_=sD_SJh2e4Z4+6-2Bxu&5!jXY^rRzkxIEp=fHjT5X|R`(eL>4ssMqBf)vlVK9wr z7BX8s@BFl7=eUh3wc&hsgC)?Iaw*7l-dKF7%Izt+D2v+@*BZPh=E=(YdHhH@;4sB< zpoE7rDu!)lf%&aAY~R9h#DeAfv+yctEPOUxrvOAMTR*vBCWDaQtMY(fJf{ z!z$t5P|WR>jE^Ez`gcTT08>6a%_22QkZv3&>zo`U$)xx8JgE1dv0^$-9JKR=QfJN& zTX}mHmP?q${1z8(h(6DKr#m*?M((IKSqxD5ZiparC0y>0QT%F*VIwf2a`%0vKjqiM z6O9}xOzoUtQq9X5MyH(k7Anvg3E)y|`RnI2%3`fjFo*gn`=+%gMZf;{UE6Ebu0^GziLmX`k9Jdm96tu%#1>lw&MJ7&*QWEDhNfLHP>9mQT%Wn{(Epb8 z8s6IuS?7~-e6AXfy)(vdtUz>_kAG`NWGw!x>06Gcv(}0@Y_PNJ@k9BoUv1=7!je>lQQcp*m_kEB*+uS^zyb?TF3g{(!HF$BNu7;0-zU29+2WS2M`(usN3l2F}W+=!tg(t2T zmDZ4d82i1i${EkWf?Dn(#t*DnQ*?5g!m+4KHCfPGr=1;(DvO+dqhL>ql~r@4(j#V< zSWV_%NH7l6FH{^_?VsT{#mmP35q)0>gLV1kW>SeOv!w{>I^5P;2q_4*9jK=5ht`~g zr@jU`r@VjvZhJ*S+jz6_d*G;kr5T0!O8tPQSwxjgMP+;g@0gtlA#M^3DWtBwA=t=k zJtHPwhPAeGZ$&CF9I^_uy=&CFKM8eaB(YeNUc^o14`;QDdM02(r_`+Aenk8vYh1Ep z;2)KpWrjF`+c!2H`6T5V7I;nYUxaq&3_d!n(D>jTVyzwao1A=nCoXPy&789;Blx)5 zZpykoFIWMVO!I^1cyzN&NZ#n@Fr>OO2)^}VAzvLl3;3GGrDN2tsp`iTT4lST+rr#^ zZ2O0IUS?`3yO^Lg{DYTAnU-k?kTvH>eCh2^c@yK)j- zyof%rH=3_t%5+j31V|M_<(0{_CxfbXrh(QM$cRQ3J*O_LAV*nEaZrhkOy&=tth&yr z@b&WOt;z5UW@{V2YOT$}eK94@Cb!WGubDtMptCd8aJK+{hqilc|f~ zZy!z7YmG$E!p>k`*~5m&%fX9%MK*mC3HLXOV8-8@bVB zw04L*{DzS(4TTRy{1{7hh7}u5WiG9&4HgER2fzN|sPWi?!JZ*fUi{ zTK3o8nXX#D?ExtPyv7>ImR{XjMecgn$Eka}X6cy1URY}3E+pqnHSt$`{FPP5L#S~d zZz)Iq-Igt`=EEk=!f%0NoABn08szWyQZ1amUWQ#yoV;u;Ra9#r9C)@14xZ{o`N#|N zlVo$EBms_xEJ`B;HC1`Mg`<+s*6W56cfPOh)KWU*KR2d*7un`9>#4%eRx&q7%`52N ze%9SlCv*Jgg6p$@J?(~6TlM8((99c?%_L&iE^r=m%4aa=byX2s1u^~@u7dYBMl3G zw{|8>DyZ2~%}>f9{Q?rDY`yDjmMw2QT4BKBe2QBke}Qs5!PC7h)Ov0jk80l0i=S$G6`L4P%8JE#dqlGPs?2SHi|D6&Ew^>3C5=BAyBE?<5>F;0 z14y!nX4#_)in2Lpna7R_kF=dCGhX85dgr~5TyPVN;4CJOxH(IeBQQ;7)f(m=+2eLe z+Uq$_q*uH!q57Kt_!g1vk#8jO&&BK0E6aoUMV~B1-!vM9*S(JM4DR@sL?xcwAmzvH zQb-zCNel2-fKm}%boI8&L67l;0?N8&rIGkKr6%u2qc2QN8W?E~+4ZjC@X;=R0$n<( zkhlLyA+NfQBRm&#>|`om>G$k!o>NHrsyU@H3IADU?|Eb<7O|pe`gg{QP9yW=FueDI zOZ8&(aHSBA+Fwb>G-)&j$K9`l_!`y42A5EhalM+yU{w_zNoJGL2CWWmz)aFoauTQ` z$)i^_E`)o{*xtQeKB80?&xTYv?9aOv@~AQ{4W|@vzud3OW#{YGV6NXjy18~lmVNok)i>HUq{mM#QbGq~ zlx2~}M>kmb<~R{+fd_M6gXxwJ))srkeywu7=i8KPSE$=y@F5sVedCzB=3vf7xMt>( zu=|UXuq_$Es9?%uV^BUF_`*=>%Td#nP6t9?=$J=g_w}<>WndeB+WCBxnz#cO7uh{K z5B=;os2bm1?cd+_Hm9=RWF>q>DW2<`(tJ>v=B{w7#g*tg!!zR|TJG0qBAdf}sk~e< z*O4~mbX%yZzdBdT3-3dr#6=z}$}|_Ylj^cb$_Qb6-SZ{3$2HIX`j$8(3R{A zNZX>KAs*h`#^yb~EUnWf3)KA8dN|LC9oK&TTR4#GEdIlB-y9IC+f*)39HnNq8ulsQ zV~hY>ZS?+e#JHR3vBlZoJpc1cBKhiZIt7(~+VhbW>R)E;Da2Qgnx1YarI7c#RQ)_O z_(iIm9yvqy>wWgBcwxn{So`N0@2byCq)zoIv;458R#%@Eq2O$XVg8~ovtLQhzDzWd z5mZl@C5TS-6|}$9DEJ|=Rn+8_$ z%n=2<5!=p@Q%CEwFU2|a^s5xyezIgI$`Kt)p3X4a<0*l0x>ahmVp8^cQRl(7*9OJL z>Fr*NfN!%6q%(oiZDN75(Ohvy+CTp4r6M%P4cni|?9C^lT;oj!^fO7nF;(=Xanq9l zIq4@wgkCsQr398LICj@RHesD!e8D$(CctNL!}HkP6<=KZ$6ZIuaNKY$d%EzXvTeTv zqHFIx?4W*eM0ex)qr|}rq6>|htF{8c{j&qw4rQLnGqDR;nMa@zt(!_7B515SNY_N% zhs$xSNRQCwx$n%$QAdr+x4n237$eAa1m9D8u%n26cA^_#ods-jJ8NN=zxx+4%fdP& z=T$*0v}Ty?=?Gg{Uf~Yns(k-1LctM;~L;XLiM`M6xTflIr_9_<|sE9VnLa5%;7=w__)*M zbc8x!w%B8RbwIs?Oh@AqY_P{u$fxD~2PFSvxNbY5%gn*da%bP`MwF8rMkMmJ87soh z;=9LZ836})bj!DpDh=>$^^2d4Zk#W(ct zz)gkmMB^p@k~;9j{0G?ATU+CnEU}q>f~6^%-P90%zH|7jPjDk&W2J5Ee8|o^XtM50 zn^=!X^KSGtpZ^od>IvcC*&fZ~?&fQIM~=)m{#(`TvE@T-M18dDCvp6WJkDF%aLARe zt;{TAI;LTEmfiGcd^rvqz}=dfMCNLxfJ0rObLSaY0z!Y+HM5^!OpyVSj(ummS%CLu zR0TpW;ul51_y>FVr^@*HWFh)+_P{j`8_R3+k;r`M@DlARob7qknhp&!X#9^}uDLE zX`jG9$0lXbo8?5^St?7udYf&}qu@e0%ItFb>Nrzk4Qp`Z^bEb3{d0&r?$!cK6}m%G z&`>*b(2q$ydte+oe;OzqM0PsR%5TxjH%uDAt+~r6mOi1{;F#%^s+&XJb52A7sJl+B zi@xRhZF~AR!TrTWC{Xw4u+7)!v-?YYwa|=224Y%WGPy~qbCFMR&Gq4ue2E`z+V(@J z+NOJBwvCEJ<0D@5`-~B)2-m}l5FPLNc_RpBOw9J)Fz?l?k#c!aWiQGEnZ(RzGK!ZF zU30ztkg}P9aN0YPmA;_Fm(F@$H{`N}dUF;0VW!J_F+YtG(!3hjxy{;!+Fs!J#z+G# zrM)=i(TGh=zz^jW`R>|Yiv^X~sS^(@vo&<)H%OJSY2SGeyN>ND|A(*OFX;0^I-O-A zIiA=ynb)|{&4_mgcixYguTwCzRXM46; zyU`liY%U`7DX6j%vaCmOJSwV}i-G-;5TcD>XeVo*kjdAUl-dx`yTe|xi0 zB1bc`Wu@W3kAGGPj;G0%=_PdbU2&**&F}ywpZYX%+HAqjtWE;e+~BB`c3yMchQyI3 zUX8(EEP_4=zkG;)Kwg}Ke(JnEVQyT*|ET)zc(|VMT|`(!ln|ZOTlC&75fM>Bh~9he zy%Q4A61@{ah~CSp5xo<=vsPcMUhk3b@BTjb{=@8P<;~Od1&5)MK_xAcm4>@8#ac;-o7%f9+>lRy)TXk zk#)QkysK&JX>QshUu=dbnej3_~W7HEGfnHQ*(A6vZznU~!o~2^XQU zPkLTfrf43C23EKE1?6RCzbGZa^`uMWf}kOodYSBHPiO9gS1a-U6m1sy;s`f0m}MpD z+o;kTy5;JPbVC}6Rr3*A+%-v5AzN>DEyasM2{5ofMit#QgVYL8cCObBy>U1P&M^gB z@;COxl{|hjtb$Y;{}2_~V!DZ#uPzIld6Nt)m=3=ZqCRg`?r=5Q5#WXU_eZ^@vin8v z{u@q2C3o?+&d|JL;G{WSEMctm?N2em5wVUgJ!iEVx~^EX*x0gPl)R6Ui|(xG83HNA zHrBf3cl9MP{msh>E;l`LrFLaDwcR6>4`_=DPvcSz%ZK*96}a1|YH|{BFmoA^tkyW% zEQb0l$NU`I?zKDS-f=N+5UDgOy6P>gfq%_iQ6YOe>#=_`Oh$2mFhlZ|MB~ias#qv`lojUVV5mwL7Y9=x%(oA=%GY{0!f9 zV!}&m)zVAwn5l2EFUEgo^YJPH@*9D+s+>E7@>f^cdSzmK{BI7alA-V2#R1!Q1 zZR$C@TfIEq4MdTS*9L!H&1T6i{&nNo2_ zUEh90o%K+`-YAse&V=UG;8vKw9-EeE57;~U+S#=|L+g@$#$cS(Jlu5K&A`*X<55ZX~+h<(Pj)!Y5Lthzk^| zJc=lN78yhxzR#Xh*~V0!q`N3IHVN$8Et~OUuW3E~7_sn5{zzTp;&bRK9ekEivHwbL z`f^gi=ggndDQ{WEW!CO>dKiCn6~`vDS$sEuCb90d_Bkvdaj0$FfcIQi#sTY)LU-6y zrRV8LT%T_Zdd8k|i9+sh-4A~}nGvf*bQgtmw9jPf5efur1ws6U zQCB*Km|W@^O%nNk^|$23f7KD?f*MjYKe`6m$0<~(HGW*5%vawj()F^;>8##*I?ot% zthD%TjmP*#=8E$y?z`(O&GsL5`)q>_XrW$07ZE~{;6!16{)N8Rr21Dw*4$d$Jd4Un z7K;g`*jqvF#j@o4$Ztt#W+U+7j&yRGuw(^R0nfYCqVix^S6FRl%G=1a?D*d6K_mkmqLz<8LQqu5e@Kf8_d#PNQ zYXeg1Wzzj3Wc40tc<&k7|2yJ34+4Am5aV^6;#CyUm8*i$uFdRoQy}dkc7lZUH*;1a zHG9p>3i1Z5hyLVIMOAc#emKI!tbF^R!FOh6$utb zcb|K*{N83GcXT@n`!fB3mT`Zzspf~ONqjy&R;6tN|C=ZFImsSJ{qI2fW1du>DEUO^ zFZ%OL-{Al1?}?6_p^cv9peJ}7VQW~pVWA-Vh<-q6E*EDNE1u*;=zMRtbJAwJcUIh4 zDF*C2nZK3=buBFg@lH}C!{fn!2x zSof0Q$IdOfULb!B)f4PiMdOr2_*&jml>@~Sl{cS6)4pqgPNieaGSc&$l{`Np<2E{} zv38t`-eTu9y;XSB?J65epZkvPg)J}Ly0bmap_#wv2D7fxYG(7$AGNNp^_Zq*)}5=Q2yQ zH>`g|Jg0m;Tlpw#;>t;}9j{Laz3O%9F;+PE=ey}%#m7dmU0fS4zR#WoD2ey|AX#gu znfNAkF7e@DG-^ZuGo_CDS(QB`W*pa23+u4XpEG_2E-^>(u?zd6K)%SeEkNAdT zcBFPFqC>`fJAp|d^+ov0`uEmlUHgrGTD3zkqMxPXlM6*Tb32x-g9@Ft5PrBP!{b+E zQAFmTHrTA-f zPa*R8r$sN&1=($ORaG?(MrE(^uAGYB9{BOQQSmPDR_u1*daCA%f(R%>ZQm9~9f zbC&C(p^w(;l3h5vYP3*Sn#-S+Fz0)*d1h%jTIOtfcR^(-Cl1-Zvzk+44eU?psklg& z$}d+v&vI8J849GAKc+)+s+$&!b|lu?#<=zKf3(}MJD$-e#y~nHI3sMqJviP)k&6wslTGD+ z4Zf;Syqr}1V`O$#O>4Ie@Z^t7j*AnOSrJ5fvo&f^%pmH(i@Uc&j!`%G z)rn46^>LY(hQFVRT<(4hv%%l=T<_aTH4_mQGQ(q-`pGXnjY64Vb@9=}>851@%JB&~ zly}CU5=CQc8z;@hT#XAR+$H10eh=KCg0*|>X-Z0$qRbbt?-?a4ZX}o@OSmMGM;9?- z%N+4wjkNTB?=gL^KS6oHtCAvr^uE{b^6 z=WUFB1NKh|lMQwyY?4f=4lFF$Ms9Tp?UPn9qnm}ZU(%yTt^f103ihC4OQ{`g|K z3*TNMrC(KNm^)_;O9CK6b0hVKQY*;?+ACj~9}%IkF7* zuMOD-Mr#-X-i+gAdb!qIXT!DkXg0e4Qt49X=~Vjh!5?7)>}s7~Wj1^$0kCG5pEMe8 z)_f{`^KF~dSV}vIRlh#hL~6$NI`}9W=KHRTo}@2r6kL6Bg!{+&5C#uC)-4j+$)AgS zoacAUsORHK$(UrB>zd$Izxv~7OEb(+huy&@k{OoVGWwAY>M^6^aJZn=8>#X26YFTH zG0Zc{rej^M(jW0XIzChmdNE5pkd(zVXW`%@iT&t!OCZ5T`@Kt}5l?*xK5t*Xq^ez0 z0wU*2Rxmel-EDz(W^c}{T?G$xM1loM3GJ|Pm~ddZ$`Wlw3h#-msO>-(QGtx-VC%l8r5n zcVe-;PkZ_bjccB85QY1F0MYf%hDKC;ujb+h*7=zc3J>v39K;0CT!%8-LCBY^(vw@y8qmw-{ZrD^Lu{tvA{W@kPPg!ANp>Gq%LMYs;pPK0r|`>FPK z(gE?$L-=|tKcaow0>5K<_|LFu67_IEoFdr?iY(zEw%$ZHbUCK)1%ZSR&vCFH+aA7Y zhn>N9v8x~Jh)Es@@%=5m*bsZ8tp2;62H%NLSLNC?NAGc}424wx;-h+uRI&omQU*$D zd(q+~64+MzuaA`xZg)qe({pc_a37ZIIGjr$p}(kRO~4OS zr$13{du2H!?)4`gV@~GXrwz@0!X#o#M0Sl65o2Muqmj9^uB=ezy1U~>#2yo8 zkTY?b#gLXLCUnm^pWE5@CATnShTx*eifEFxIqtnrDzjoLQL-1HdGC0b zWb+<3PGpQXk$RUlJ_zgZMC*5FT)!5QQ2NpdN2iBjJ7k1HP0dOSw7- zA=_npmKtmigZi@;$HGgGZQ`!xc)Viu7S!39oeK2hwG;KeW1ocj1ME5Au}JCKrd@1`eZQs?Ujcnv zi!EIH?Ty~=VaG``r_r6zYvOPHUjqgBzV!eLj54_-}$BOOqo^le`vS-{!Yu) zRwGDLpw(q5af$Qf0=L~>?fRk$f7iZOMvjgs6Vz@y@L9sX3y%F7VgDlf-SmO%RCDN1^!_1r)Af+`##4`HmmGWjey2Tz*vu8xZD|t?nyYgW-^o zlGk?pVx>!injF)8_eO_#+hZ>byb$?Pef!S}8}&j9*JsZ(;e*oakI&}Dw?S=Qdru!1 z7_xJ8(i8qHcE$-Q#a33{Vm3U#j2j$&$2Q5xbuywsLnvOZy(J&%c2 zv}mYz>rgA8sfm>OC8lrpa#_eSjAP>~<5*Ho3Kg#U^o;k}?qW&36QfDG?EVIBJ6$%N zIYM?!&iYk))B*TfJhkpwGvnsOF8e%wPP%>WlJL{hq;x5X_t??gA|lwTVh*R89R*%7 zRC7+ImRmL7Z6sa>W^nWFn-)2Z*!<~G0+)T~tHG;`>^d;AkZ{a4We{TMhK_n@!~!nyz{>6>6qshLCM zJ*wah*S+Bi^nNFhVfZ?`?Q;KoxlAqKNzv`}?zQ6`V_r+!?Wxq=#JSGt_H1Ih7 z{sh@};~9902)LsOyl=lPT=qK$uDaWtmbxqwvYK`bl#L0zIBP~2j^26SlwsU#BJK{F z9jDHFR7yAkZ{dMQ9|G?T0|FH%3$yRCc)uiH(gfa-+^;U*T;J=S1G{497LqjSTsj8c zA=@sh@2Ttk6D~%0SOR_gZ&#OT?*6|&_b=|h&b1|>-~F9xJJukVginvx zOce&8#-iwioLqSZa?rMcIs9^S?gZ9o24)(Qj$0&M6J1|Dz44WV-%Tc(1aM>~%I&@V znC+XWK1=$%if?-&&yzjkn?#_&<2Akywuth4S=L!3T;ura#c8yJW-+{c1SG!g*zUtZn+pifBQk?${4~S4w$mUA235&%$=R=kEJA^~32o~bc&0@=w zr*5CFW#RDu^!rPH1= zNc#!*+C<68;@oMJioES1nd5pETK3*g7D>l-k6@LvzduhKpQd+YUR>JLdiZl@cLF8s zq1HCe?5>BBl8$0OvlVkds}JUCU+pg~(OSuRNyjpvYRBdZ`uL!01mKFE0$6JCUq`>G zPu(je@-(yhTk2hJ8YIt+>6;6&l?-=FUtSNFU;8m{vL5!e+GuAt&ut-qF>J}+HgQqh zKD0$uqQ9d4M`Ep7Fo-PMqS1r+%FV3HQY%>O2uYm9MRXc-r+nl0ap$1iV`d8h^!bZ% zcOo)+=iwmJX}QN$jkx;9;Zh|N9?}%|cg=QYp1CRnBhpZy|Hy2Qd1>)R4|SdS?6O#m zYku=n9Gb&1vdMy>cFq%46Gpx40IfgGtUs&#P6@Y1$^%-TT@YQE8P=AllnW|0w=;|B zd}wPL83(%hyEfsU^x&}P*$34%&!h}nLQikZEe&f|Cf2?EyAiK}(ZEkr1x+4C9jSwP zvMPJ`_BVO@DOt)-a(-osS^yXHV!cAQ+IRC4?IR6`J*Q;nVp}S~Djxi{0nrxp%{YEl z!uHTj47FWX%{m4D!2n=ctw1BgDr z@^zPwi{kHt<4(`Yy{`dSYF$c3^qBv+KOS-b+V_w2!dRo4mC#@*@f$LJbhI(2qj=fzHb>ZN_;ZLY|k9n=8K8Aolr+_ zA|V=IP|`V`LG*4I<|RWEH><`LQg3YCm_b#kN&{lrz2xt?^Ep6TwiI)P*j&;3hBYrW zC{`@zv;?~bs0A<}F@8)tr5yt%mLwdj69pSZKygV<;aGPfr|ReLW}cMI}2&L}-66!Lo;(xCbkKC)BAJ26ur!Y}z*@{QGxxN8hGl=C7`EEQZSmnKhG>$=bM@ zBrm&w!CX*XZ8K+U8K^w_-$VQ)1~Ij$>!2o^LCC1Pe}bvE1nwr5+kTU$e$w=W(Oby$ zW}-ywLvdKTL#&&ko3BG9=OO^3Ln8w|28`8oSX!tb>CK;2w3gTW)BlUZ4}qn_Cg@-> z0+H6UuzSoqq30zswW)MY?Ekb05$jSil)UATfj7G7IuGUKa*c&aNX}Wfd#5J1w!N!E zeuka$C$~Co<^69d`~Q|KsbxoYt?lfdagoLTxe3ncEImS_P14}(nG148uT6JFvDs%I z$pfw@RmVT6dp|uNo>F~ry>{P>PV3^4CI4%nKg#>DdjcG3CGPqq)@Ri(^y)Iv>o+h$ zo}QdL5d;7fmI3*OEs>DJ)hl}#mYHx2o@sU^9(A1U#VBI5Z+rP6K72L@j1 zLh@id=Kx0kuqRu8N}g$oO}?H-Y3uqys@u#T+`G6W2%OkO83a}-tkc#vVCqIg7ZEvn zK!+3u-wf8p3^*j{UOKMo**|Zda)hA8Z!RADkW&CZTj@6C=>h+2^~CLzsMG_TYl5b`(sUO9C{)ta9zsJ( zi5JvQ=;)it&x6r#1xW=^&Bn;)B?ta8m@Wm;VSa>q$Fy{iQrLV~}Uq3EAtc)}f z54y}c^miNX30uc7pV#vRx<9eZ52pUMIkxi;Zz;y#Z;npSFD4h<4CR*BPje}qq{|u(Fc-JBL4P}r`o?sVExjAO zI7=2~&e1x?)RRkei`11~{Y(Di@U1(+I5k5C z`z-;>&d=iNiWm5lHXo7C-7l&OkD8#kOPiC)3L&pzGM~RktaRs)_R#HA?u4?5h=Z1j z*Xi_W_Rj;Tn6V77CaDaX`3lcnIyrpC(Y~Ak@51T_z{;ot&&-Oq+%L`}ZjVpyJO?N0 zdX4-bqpB^qtmTD`3Zt4689vJr(Ia_MzG<&Qy1!lYk9$#YCxfksvV>|Uu0RIpkhK$N z5v7VLbmJy_=h#I&y0mTvrErt6oNmCh)eC}I9x z(_hXPkv^$eLW!5h_M#nnop1cE%W$M%-_SQ~JEqd`=>r6HtZxKMm2|7|20PBv*S~Ow zoGTh`T=60_5y~efcy`z{vD?@7bI_&Fem5!QQsVY`h&V>b7;%2h$CeR=QFl2pdsl?3 zTGxW=%AWa%!dFjOO_Stv5i+_3r=1t(BYmoy;cGR2+D7`gca=Z6J%Ns7b^9rKR6reg zq=-3!&8vx2>ZXZz3M(X}und^%O3$a~4{(+Vp^bhY6x?SIjGOzDa;fo?R)f~kD+qR1 zZ-;Y*w%)h;Yrog_}yvBp;jwIz77XXm#^ESsz%&JIimDrlCOw zRB$b_UtS2Dhx(^d8kc7uc^@OH*?&OGuzkD;|uWptT(G;+nS#NF)mf z*pVMyG32zZFn@}0a9G&Sf)~1EvT|zG^MP7DbY|iy68B=q=D?Xk>93aG;xKQ~Pm_>7 z@gQae>Z(xV+(<$K8V z;%@$j-2;4-d2;J_GIk#LbW(8dnx>M$oKwuPEVEJ8jFf8DK>Tx-r5*t**qn3vx)ta<9w)rgjf%;Pdh zPP`m{?XTb;C3+~XeNOk1djtv>x3*@&Ix6Bd)HYDzQvhY9T=oOBrO^3!dGI0S{ir;3 zSrW`y{Gr$udJyZf1kDO>3!RhKcQjD>y$rbx6*9=`fQC5PK6kWiSo02mdHVtBeHQbz zRL{f6pS7ugUd5T|;G5hsE>$!^-=29BOc}4?uBZ0mrR?P|e@LTi_4E4ey{Qxr2X^+Z z&DHzfEe^TOE33yP?Ey>g8f!jEOx@|Xui$@2lhc&`;S8!PCZWXo!xh<|A1N9FUB1Trj88nq>SpasCw_YUUai$( zt!Ec!a_w1#jRD@^%_l@D=Vb4>217@_06la=F;ZIn2Tuncbg})u& zgWFs&I*CJ&pyK^%N?X27tVzz{XP~QhHuLiq!#r6ec;$TPVfez{OQoFbe4C``i~aK! z_A!z{PJzay*7%)Gj0VR*?5<#`-mg4^^F)#S%qvsaVVUsm^Bx=zGK5gUgdeba6!7>| zEVdDT3Yc<(&}7;~nWTVYOkT$VM}Yk@Xbim#UI0Y45R_z>3BQ7&o_UX{Yhm}#>M{y^?gBp{c7Go7DZk0fycd!Zbb!hIX?io7%tf|8m&5-7B>c&q8DGJ3q*y zU*&j$z;24;9BTd-PR75aB^}diQ(Y^}CLSesm4TtAJb;EYH^<=W9Cr3Q{EXBZoc-nw z=IXxav=6-(;}VJ0CLg4+!;5?TPkw|p(p1Zy$^6=xbU;QSC{_Jtlnc zyl>Zc_ThOkP_Du=K!#{q9J24BX?^%C6lIBl<=8Obx(b8Zzq$`qo+A0-(E`{;xr^IG zb|Oa5c~bCCHeJ!6YfsE?+IN$Omf&5wd^=?OGpM*!E`+-&_A}tPFCCZ;cOh)v8?bnx z@4wCt~^)C*|Zcx$KUJL;;7>Ygy z7X{pm5g2rE!~^Iq^SFaco(IuSW{WZTv~{UIuz+&#s@#Ss5yEcVMbg<|iOdg+Z^gmu%(N{&#K#z{! zmMDyhwjJ^E;x83{1C)6cZ)h7NE}zhmbi_z01T1AN5elYYTyn~)~Vp64Xn$eF)QSQz`ybv4*~A6%xFJV zbpTS&`C_w*RPE4Czi{w$VfLJby>ct?h9OG>AEd?d?QZgj-|?o>zax^~NQ<`n%Ub^{ z@1iU`Q&Z!M$p5Wn!25{>@a860kS97X#?=CNr>_EN*N>8%k>wEtALMD(<|{h?rC z>jI=w)0x`#g=u+Lqdvc7C#Wl6Zc8i{d78C~8QnqwSG%-oJIApUkttr;>k$o$#)Z4R zafBI_KUwJCfGW>vsRJPHp#5EG^vbOry{Bxhl`oEu*-(0w?56R$eJ_8-(E1BNleUXYv!B}?7S;tk(;Pa>4jVb*&zg3AV~y}R{~5MldFg)d`)2(*eW>=j z`PmZvQB-tkY~#Rq>jyxpJL`{}%Ldl*_i4sm9QvQ}JM6yp10*Q5NnJyi?^(v4yT!E< z)PAH-(ys2n&0xm0Dk9zS&j|RuBcE?_!plH0{>Jm5?W}q}E52>~2(Rt~gI)xJBC8u& z;NQ1%dfRixn7sRq@|%-?cyl$N0iB)0b^rZW4Og#v_{_z{`6pXKsXO(A1k(i2jqCVV zU!GL}HMhgJr#SP~g?#}_cUQ)Xv3Ax~G!<B^7;vJ9<^x=0Jpu+9i4wkPQAjebr#*nG?RJkMVK#j*g{Aw8>pxTW)=xwKnex}T z_4uJRsZFojs0S&$Yy%Bod=Sto{DbEYT6m9y)Udo94zxV$Aa2lH2DEHfNM&*T&}ZF1 z^t8i+xSqG~w*BxRuJ;*!+i@WPkj}w&L!C6$R*8N?Ij2I06r4kTtNwC1$A9NRO;aNZ zI@ZHE0>#pjA+3j^f}{PIPYw86HtGdSQ1wh@kJ&-Z9da_Js0n&u5?ge}pO+Xx?DPGB zJ6*$3zodUDq_j84r?6GnRMI(D;$_-Hv>cO$uzyK_aGnH7EJ(AlrW2ndjFu{&Q^$OUx3aeEVC4iGU7h-Oz2HxIRA=q`Uyv2l-TYHbd2eY37viab zdf6G7ihRlkVqH=-YGFbk{9020_)NAsk=D zpg#J+QA4i}j3C#|7B9sht5EtM`F2ZR>P_r2ja}Z%E_Fs>U5U~`jDR;NdcAB)7O)F9 zs>8O@EAI5Y8ODD}gWq4m;I?IsYl>!fU3m&;ixOCx4ckU>%V*2DURte&)Y@sLXgnFi zsi|6dkGa?=;)un)j$S$uL11DUOB;WrAUK}W{vF3w`uoyCkU>LNqKMPop8gbF~vp!*{5s~Rl@qXY;498X;PbX?T z<~cewGD+g&YH41Mq=c@`EZ@$43S|8duvY1X0XbG@7&XI-InO5hNnKjwZ%b^1@)CJV zN#wZXM$zP9B3^~_%ykJlV=lykiz82J*Pljq_!YmVJbjXlTWpT(ELp2Ye_Txs;mMuK zs9$rzRN80_+5GBzqp7EZk1xPrDam`wl#y{HOLb{Yo1Ot(@jLmQejw`DX)#*(Q(ftpf= zwWBQg*jh@2;1h^ytjnI%oj^nImm4z$K&aTj=D$LUCTO%xu4O&p+vdT@r!pm*?_=>M z2hc5XWD?*B6WT}Sq1BkQ-5&khgBQFZxRsVSz<0QQs50D-p$DX4+;oi}9Y4FH(}ub9 zr=?jvduNF+!%_%!{3^NL5(^FsusVGr6ZEYMOOiy97nL^6rI@WUW2l>OUHTL0q7AN0 z@8>RDNyG;%nVN0AWMt?Na6=rwf$?vm@W`tv`2|1=EQJHN{Lq|rO&)`cIb7yTG8h+w z@KKxe8D8=ZuN&Dn=-|^y7eb2+1@}MT$)Il~2%QcT_$$bPU%DxKKO>nFH{b&iq^TRQ zPk0yf90<`9y$(HY1fe<;a$RcW^VBpE&d+IGk{bLVyfJ}pz!rd$6XpS+{AaJ%?7Mw? zARwjj13nOH5I}T8ifE{`bS}m0(do6`3S;1xQ4XHCGOrURN#K>5A^7&9)39|5KeRDt z0yO&krtt={Y-!-JCrL(_lAo<#)elL0I{ZTDXUAw2zd;E{wu$~=t0|qAUtxW#3zwqko#l~(+MBicg7{83z$Ee zfZz+o4_NS--`T(%iijP-cdA0*-1=NDp66kzZ*WCbZ~QmvF2!YD=!Uhs6qWHm6^xCu zBcWY$4DF3-3NegI&aedc4iORry6O)r3L@}Jd#DHk)%8C++ArnqypX9`Nf0{9eXtOI zTggl7pdw!GhhM!<%dQHtajOh@(y65dF&oxlgSCvL16#X>|QC($B2riQD2q zAO42ov;9(1`+Ui>RN=jii8ba2G>&I)0CrSKh;5zDAzY?#B*k;H>cy0?uZ?ZW5VEuL zA8g^Y4Naa%hCY_*T5EmjjQ^b;?(93Tt+@1<_M4fM4Zcjcx1n0PB9_cnxnA-)%-&V% z031en)^yy^;y0d4O5>z6nIIJVdw`j$|A;P(_6(ZpXeha@8b?A4u7nTyUB@KG<^p1o zJk~vmb@OT3i<8v#sG!7Q-y&Sa0(9DNSHmiL5Ko3{TM4c?2JI(PKt=gba9c{Z=Wu*< z>-KB4S41{!_~M6{6f}(sla3^?gCIO&Q0Bv>o^viP#w-zqPvF#D7X#j`;J0|O65YRd zf$$Mm*TD}7Du4pE0|rVUViUdVz%LW`6sOQ>Q7jz_6R|Gu!YoTSphcO2u})$fG7HlQ zz>UgHSO$cEZyuPX!;ey=a#BZ^kWWNS^`eFR407UM$ly=C|F9;To{YqnK_3QQ6v{W} z09_SVRm7(o*!*8C9YYh>vsPGDT&b(;qc*Su{Dz81rgY9BQi&U!P$i|T^}-}^EiPAv z<)e5W&s%fcbDB0XzP#2b~+8E z>p_s1>u*<(PxcGRVNYfF_gjDP;nOJ<@*5;4Z+o}cA6=6fdtR@R;me#ANKR3Qt)x+AG# z$cU}=6Jqm=pX%JHH7+eRHAtv6rID*-9FcsXczYPsSld~$D2d6KoZ8bP&0`={d?}Vs z#E~4q6enj);kV3U_h_&<4R)$DPRjc0%5ka7uA6lB{n6&oAd*)@Fu%o{Z@uMhx>7w* zGvgs`rhb|(L`CP(U^e{;<{mMd&XAqzk#tp^+1ICWk&vRY?}_<3W|P@8gj5`Be0a4a z(E~jHZd@5Tl^qm}CqB4hU2e9vRf%a2`&3TSzhzZ|`6<3#TBR&((p#Dwi{;o%ktGHb z{8gG!K;MqL1LlOwnMjNg}%s^V&?YZ0zcyw`$~|FJ;l{)cLa+ z$xymLv(nCb$ZJjlPb?-OIsdoAedq_AIc7;^zzv&pZeR)chsZ1`cOc5A9H%n z5_I;38B>y(j2F^^@2$Va&W|(yrd2W8*)2f{{51xdKD@wr9RDokA;56!2?! z%SYxbNs()K85z5)n|OQJR873?T*CXNah5r+#4xbiGjL$%;2iT7Nk{pJ%tuy*?krJx z=%`DgG4!C6Nl68i3Xd5Err3SwxQbIR1FmY*RMW;~(jId3(E|7>uYZ*Po`fUk#GL9d z7)1gzPGH6iaW}?gD_aCXaVh}lb}C#3jgB9tbj!QM>WSZ84kXl65ladFJCyGpl=Ps^ zXyp5~%<+(l{KBges#Mo0*>yY=cKIN43l!$eLP7));b?MEYu9hVZpkz0-S)Sms!Idv zjfK0pNX(#;yf_reQ=ZrE;7ii!mm zy5>3}E6#Q$k3N;~fqu>}aIs_Bf=vDhyDm*~**-c$nMru{F#iTcb|F$IO_XpD+E3o2 zgb~e6?ygd5^}XJ;aew;7*rvHh}B6d1Pl&QkSL)3Cb1^Wo$mu}6PjV}lD%S?i&c8d4~0l)~$|!X}RAlHT)`5$Gr0 z8-RZrVX#m8lrw01c_g1%ly>8hlxG4L;guua{?<>T9S;c6-5pANr16o@xI~ww1%r=u zB#)US1zS5r@Kz~&0j~11^IJDXcn6le&3J}CwNkMJ@7ub}hYYrw_{wDx>YnUQi&7 zZ$0u85^Y%iqNMWN$Nw|4y(BF?DszxO5uD@@WyUj8-tS+WvQ5kdwGc~cmw4|r9>PuR z3o0vIoGc*Povkwp?G^=f-W1>nH*yGY8-pu7FoQkwja7g8l{qrD8dHb=Ol0H%TtYZH zH{MLO_hj zdj!=w`uB4f;w4HMwUfumuITpooIL;xf5aT(-?3Zmv5{0B(NEKk{j^uMJ;Z1PDJe1n zX+@z%^DUfC?jF6sfHS-`)cP&_C^)-a$JM~Cm5jlNUVW*nr&}l#9cm7^f99gQX9;ak?qQmNW>_kpW)#{%{U{$2)Mm!1 z6B4t+^U<-1UR2XFmHfJ%#b~h#w`cW|MxPK8K=ArKq?RmaY6c^`7L}s?h9lIytF`Kf zfcdYA)0vtN6hnYeUlY+I3V!ENdimavTz0)tEhI=G9R@-RAM)loqFewuF-OHhKgUMH zjY7g%eNHljjw+8bVq5+tgk8$VsItP-m)K}aT;~J*+J!8jl$M7D#azX(I=)lTzsU*v zmMHDykq_0h00upJ`3PhE^DpU!a_;gjb!*O%=jeL+#s7th+Z{@s!&(6G!H>Z#FSeuM z+0dXw#ZyM=I-avisaUYl5uu8=$ggv-5uRmBXpqLq-G*j)LNRhgH_q`hJ-N z$g3JKL(2C28-6imT=2t1kr_Y(G@e+YRC4*!+7jI-mn*1!bKy4qtnZ5q!P2*w-o)1(B8~O!p6# zYT?C81yQBDqocYe&%u$6W@z|M9z~M2@|#(w+?S8Enzb$Qot!_HX9Si}quc2ga{b*%@3@74ULVg|8bAaxOZHqstj zj8jt2bs%h;F;S6UcNG717Dxj@k!7Gha&gKM{I13{>~O)k zsTK%b6kZSJmZ5;Q@&`;g^fpF=KVgYr3xsk(rH1+9TvzK+xxAM6pLg?>VxTt0A2U%4 zW(6-i!sG(p!plTmcX{p%EOab3>mJ&C{RJzPNKD}{_$W8I_JckGl^VmcGs`&%jLDz(pHo`2T=~IQ8ZQ^e&*_){J=Qq{bz#TsT?| z0~!#CrHvJHuL@^ylMo&% z-}V+}qGWW@597qe2lKR3`aUFFAivVuqU{kZQ^abC%*NiYVL+uC|1#2IQwqghneKat z2Tv#WAnZNQZrd7yCu!DG_pSqxo0VSP-TwE$ zry=>81wqsn=p7N*KT62#RsnzX$`l?ObWYh&7D|J1W*3Gya5WB6<^;ewg>+;!-@f4q zfgoA@6H-6e=mgT_Z%F^R~0VvO;h_c;gPuGmIQd6-#ybzLZ)txD>QL!|IHlYt)fg{Mpm)LOjUbrH{l ze?I$o7{<|;GId$&{2BI)YrE=0C)aR!^fr68mMlB1>HTKtGFx^7Z;v@s#v9Fb4YTvK*u^!>Ga`1{c_D+$Gt8#C{{wnJ zg}>W{lI}Yn9r9C0N_HDd=j*(DVCuaD*CjKK_ygN)`mLvP3~u?v1zQOTt7~}6@`(Yp z5)v-cFp=dG?`I_>?4;q?$_KVCVND(HW*DdPfxAk$LdQZG4yk-%Y^;QYS2FBK`NXqW z2?=9jxPtPDb+8f={=j_y<qkQ^dtc2tnF?)FVu+b+!V6vMZkNx@6CZ7B~Zl^Xo;}2|~Rzk8znhoj)b}=g< z+p1oh&!-(K*;p-|uk-S8|HVuAqp#<`pZ)Y7|MZXSm*4!uzy8Cw-+jOT_OqY<>YMNW zp1=LHR2=9k}n^Yg#?-@f_V?^DM3`%nJVJNK(!u6F&)*LTSK{cnHy>+k;VH^2Q=O7itp z{?`NleI391|0(VM?nm#zRiH>>79`{wU{`A`4w{a^m?-~Re1U*G@oxBu;%?^kpDuRknq zx9Fe0`R(`L{L3%C{q;Zo)AwKBeY1M-{kMPnCqMbmfBjeA{L4?i;?KW$|NK{f{$Kv> z&wu_GfA)T}YC3-M)8EnDpZ=h|Kj`#Ne}Auk_R}97Gb`O!$^H@?{Chb1@4x+Te~+d2 z&A)#0-S6UT|Najg{sRv8;raiV)BpTmzxl=A{Ps8hr}O#yxBn@*oX@_~=kwwR=fcmP zL_XB%sNih#WcVqIQ>LH&^xyvc-~ao6^Jo9&Pyguu0#Hi>1QY-O00;nzbL~{yIbS#AviP^ z+}+)S1VV7=MuG<|al$GRl003mb|9j9;|MLo^;SB%J3)MzdQ6BIO&q&le0&C1Y;@ z0E_7VJwQNCF8O~xqPC*-@;+Xa=>OwHCJ@fz4-An zsW-oeh-1194^#3@X`1u*A2T?I9yL6R*(xz5Y4HGj*z@)CV<;QV|BqF2CKL#s{9u<2 z_j99d@Rn%sqNF+jCd#0+8*FP}aGNCyFktrvuI4SFh|gkyvYzX!F*%5ejHlK|Iy` zy+?~(R>V6#@Z1N(iRs9m)6#2nXx++>rbJh@d>SgaoR*}a%B!H|@PqFYupnV655mr3 z3~0iC(MA05d|kc8?s*D-3A@1^%=bs2v+6P#8)B3sE!bxkTKI3B4-O)R`7LQ&SCSYk z(b0R?@@Voe)k*d1BivJHAtC(46HV+a`{s%#M z2Ac=AtZ<~}0urPg>8@YmdU?5~@32>3&YgS)p0=c`aoT#GBz3*!AR1p8V*#olf#1aR z89_1dCard}^)41rHRE~Q-2T*$G>q7Zb7YhV(XC>2K=TN7^o~i#F+2IQ%VdbJ1&uo3 zp9sEfM~C=<-a-koI-d7%7fx5 z6Y&!%fYocR`={CP1QvkUfANHbcm@!)8B(+$A-H4$aDq7d#wLJIu|ihBU8xb*cA#vY z^0cJ1rbsg6<`f5VLp0X~R`FBR<0Axm$F@8{)`!_zU2AsAIaWjU>rZ`!`t;=Cl+l)8 z3Zxf!MF^~$Se}vN{|U5k_}x9uO)+STd9tQk#KW?egYF2r+VHwb6$#HO%8$_REzmVh z1I<7(r*EUs=gas-aee4qUkulvAgjy@O7JlCeyA)F4(DN23}y-7p<%MEz3&t)CtecE z5;orn8ywlZP6D2$Rx@PI2wFcR+^l=wR_sh42nNrsO#VcMDE=P?yFeHBtH4`_2mkG7 z^Rn~~>2SG&*H^mt4WqccAQu*^3t+a@&qJ`hEKE4=+)v7^SqrmYE9u9&RVERX;C^igC7d9`FZexuz8I z@w~r78^+88p3vs<(u%<-Z%pKViW89cv?q-?lQ5i0!SS%+^z^XYM0&}UUt zcb8PAjAJTf?S;FiGxN5?&($p-O&p40D*V0(cdHfy67Z$T4FLAn);fE6v7H&QJ%Cj*R#rq|8QbU~V} z&vz8%7QM@LiL(*Mq=&9D@PD_YSO$1Arpywh?F6I=`##}6Rru#8zduZArAV4b6Pl>W zzk&V7?A9efW*+@)OPkNTw!O261o45{X1tZQAgZ8MBd_yT zG$}L_eG>X)e&2cGKdlpIrZJ-c3c5Z&6sIN^`=7-)f;{?|jRGXB)jRVSdr9DVBubrQ zMN-3uT_b=3$ZUJyj4asFF|pz6fUVXexhkt)ie}0{WuhW)j-U7y+jdKywo;LBw2O0| zKAG}Pkch%I#e)})110L$%NW*!{h2^sdQcCw6w$F6>3lnI*U~>R^j-WlC0W!eYv|4X z?YkGi$)Ep+Q^T-l$NK{@Rmqownn2-vXqA#k$z;s%{ppEFP1&L2$q~g9MgeCP>n*i#m$pq; z$e{SVy#jKYRQOlTt;LZ8PG#t8!pA^Z|Ij1wBgCBhryb;f_6YOM&PR_Ap&SuAMo2xs zza*B@-V?b(wDUGcUccyaXFJ6mu<=i}6zjiE9h^DSdPZQ;4Wgh|qeUS$E=lJA`5zej z8-z)P4F};=^6=$WuqisPngH1ylF{f<+)ewh4G3^W8fiSg>v{roA}s@X`#)jWuf8PU zDNo{}YyTXQ9)4=~NV&gS0PwAZ;e3LcY4b?Qq>Q*Dp`7SbxQx!BLL1+H>K%b3m#>FP zux!Gp%5vbr3S zXBd7>g8CLPnGefo<%L&B(@70wc=JmI`KRSQzpSEk1d1?@+Mrr=S(!`TN%|y)uu#G6 zaCN^%wH@~GNG-NOG5StDdsJ31;()xVcX279HI*VtQ5B=e3bM~1zJc0VQEzcEJa-*T z1WGxoER)ft*^yRf0RO7UgT%>%$Bzfqgb9WmE3Q@pK5pl`PGCm*ug)G#vL!~wY)=pY zDK|#;R~nUI;No}OaCbrk4gPk;_JcPAba3Q!9q2AabbZX8x`>)pgM4j=FTA(AyN^qX zJa|?ciwQ*eix!HVkgl8hz48K7kyz81;+ zM;_&m{MbTBH-|S=9pFV=rfdWV`DW}U>#*Lf>eYr8HyMgJ2TSU`I)e+LuKW2;Az03700k*_tbH(l)2&i zms?J+dxIQ5DxA?9)d(N!<4KRuhTvspBL%exyf$!(68bH$gsfT$rGAs|^9LzryP8dOOL@Jj}m zC7>a-ShFv01J~Ul=#h~hOt*|{65-jcTa-)EDXw<>4(~imUZs0_9|ZhDGj8Ytc&&@x z*fm)e6Ah6?sVa~)F&B6sDZPHEZq?c1rdDda0aKVIJsvjNqx{g&=`*1UKtoqoYQeJC z&hCji0Q8fyRdD5SdIG$HdFmw5Zy$bQLN~oa0*>4xf8uh|JS||a9O=<95m%xZ z4blnT^)wI}4=H*)mmYeS@!K9Bdx1#*Q}Oy&o`y+q z=F*4eE4Mma2Fk$z5VbKJ9qvp88%x=-|{i_o`Qs-dM}JPdRaI}=_grC zZoA)|oy>Y7#5~*ci6rx0)?ux@LPCQ9SiAY21z3F$L*ctBP*8m@T(Z-!6VS~3ue?E@ z@$%JhPK>O@FHLwVpZwyID)PKY;gMTUc?0`ANb!_X5_0B1T+zTDN*hrxFeBB#_jmpb zahafPzKolkrlfsbDbof&=U`5QQk88U^r(wi13PSsQp*gmf^K{t##iF8 zJi1@LPq+tc8g}*eatV^v=&B=EYi*YIzv5@FP)F=aw@61XwCS1X2$9YZq`O2i zoO_IV3kpfGmHF4ldU)&`fKQgJkX8XtWE{`5AdwRnxRMu+8Su=0b_L_2&RsAoeOF-- z$b6B8CiJdv!se=V|Hf>bQD^iB_R?ps`3G(Bi!|Sj=pc8OWYLvkIW6~wrXV+CWx#t; z8Nrk%Kc6EV4iE9Eo~}0PGYUuO$A-eeE^Lo}5ILH5$R(D+%dnV7v4^<(-K)R>&d9;OiXB zf0M!oKlroBdvAA*F7|WF*y|nW9Gk0P`gv+#BfB!ovrJcL*|j=AD!D*v1!_Ie=%(O| z%-yT9q-9AyKz`VCeBLPeaju^(+nHzeU7((y?NGGAF@9#p z^!CnPj3|>TlJqRDSH}AouEaY!S1tH(;*L71jQ=Dv2l!?y^LJ4~F(=W6uXrV%=4fkP zJvQ^~daV*Y50qytAdx|~t7JP+9`puBtINTFU$!y&qn5xi8|q!3yQaxpBdy3DEj$dP zQGlR9DYc&pQArqzx~qFLH_nRcyM=EIr{bj&=zX<{pek zoLcqkyGj)sbn9Q=o?Yp?NvuzQjb)nZ>CErjU#neRjLhNDl=g46_Ktn;KSM`kvV2VR zwCvH>gNRD1Ndebd<5b)xi6U_ZKK>(bH_fmIR>U?H^sd=#l5*_qgY7Ar*3R~3Ou0{W@^Mw&8%%VQB{REJ|l%Z-m2Xl zfQi2qORpG4_o1hjiVSp`5n}W=A?6Cq@lmsFI02&)L_%~$0{2n)Oz~4lgfhU2%iGOX ziD-bd$*K~z{A1YvWc+{NYS}SeNRymZO-`RO9Pac#lf_w_77%y_e&DouuOWx%HsWW* zX!%371Nvg$i|j$8eD=fC(*-x{Xa}|!fh;{${Y941Hz{$~O&EU3;;^*8LOy@9 z|Ioe>f_-}NZT-%3ev7GnPK8Mzt@WOY;C#j86UUNC#XVmvW`gUO3iFTFkocuX{*6_H zI|oRG=%VqSlf$Bt6fOgH#sLuTh#aCkHXu;w2abcQg^?!z$=e6X29P{YGA(dj381w| zn%~P|D1ngT+=)rY<0XKZrWB4=aksuv68;OY6Np)NOmp_*j*ieD<&W}x(+If-K`BO4 z53c!0kF_`TMgH^!pA8+$5scdBz>K+l>_-4@^v zw)~&R7dsKz5XP(q?7So`!=IDidg+UBKZqy^J6qxwmn_D6<+aP zQHD&uh9mXkp3t72@6`MP=%4w!`&N{p^#gA;@W6<$HUYTlBY&L}ry?tnTya=}ri+g% z_}MeS19=?#hsJQ?U}~35`hSAj(hi_3TF0mi65}BVs=ohej2#(FQwyf#;CWAiu|`FF z_MN3mFDsD0W2(gj$$i~OP+DG+!S1aPB7EmZfMi_XJ5u40ji(}#Y2=jI$M`WX-zT4A z8wH2ke%+ycagNGS~S;PewW$($t<;-}L>?XY37r5-4rP@BzDq z`nY}|9uSf@xGt7jY45`3G@$Cpmg;>vh$|0e+IrlXe8 zPOWD)zk=qlI*{KCoMG^RP5&YF_7&*e*r4?r<4RbUbg?Sun*6}#{*e1v*2uT{{R{Fo zlXc}cat1{yt>$oo7cTAfFTvG(S4D0E@i>naG7{9gd1yH$kQR?_BQkzuJOx8EBL)c5 z&hg4{XP|j@4eDXGyLu|JY5mb?!S7#a?;@$Q{+hU6Tnnd+aS>#5N?pJbg$lJw{$YZES zL}{7QdnJz;is2Xxoo755b*(e#*okrf_NB)sMwC(%L<7{;!A?lg`CyS|*rwPDPk(4v z2O@nZH1*#M(a-FZ8U9InGvH?T!d-mvk>yFF|Fsb6+rgjN+Tm5VyDcwzykhJ>MsuT$ zC#2iUVyQh_0hiN86#*z!&ZH)|XO%B2Qn6$`6SdunDl7?zc%eDXtFWNaoAG3Km!Cv+ zKbG#Qa}0~M$aH^a_vd$sO=?gihac6cHJYzG=1X6JGi1H(v;uhF)b-gkOuiYJ!mXJb zOHwhK#Go!ZV^Nx|e5VPYKa|7148?;Ne=eG-Xn8V%xmei!K$=v)LmR(#L3u&&FA-R3 zT1aTQbv?Qr0m;4c@LpeVFckJ(G;%??e_9+|gl7)`kFo=KyDP;I1c`Mm<8N(0Tp)~O zYhx0iXy{L4J0^*s%=c1LCyDdpx^ zM^T`F?6;43c4bzAaRShFAz(z8dw&)Vcj^@t_#oGlNhug2gxz{5)pO%O2|&|+rP%kI zVyXmX$s1cv#Jf>HO91c{xh$WKcDhF3V0z-SV!_Ae!H50hIF`TZ(+yeJhLF?E;!DT_ z2o@H2V)?z^F~e$4vl0klLdN_KzYx+^g=Y6tV|u~9NdgIAWReKw*{QB^C}qOFHIKsX zXQ2nL0jGjh^IuJQM-6C?$*xd}#_ub45AF1t3ob2S*%K!k>DD;KGm#|XNGBLCrp!y+ zq2t0HUEJ&=>lQB~FD!-w5~k*NX;zo z@JX*=0Yvnf;!qx4-5#;_?$Y)=s9WW3Ar$qXof>QxA(Q^|OBIJFBoCCltanpQ9=EP| zjA{i#!rY7}35CFK|4a@B9piI5Z2yspw z7HQQF$Vhm>D2f>_OhAHgGiO_?V-G0duG|w2e-YpzOWy!1p3!zT5p+NSad9GwpsiPgu1Y^ z@kClNc%0*am!-|Vt-nnnQ(F7|JtfAdYdE}#`W_gy1-M_7azUN zqid432dCjtq(~?Yc0`k2~=%&|1SU0FCNk zf)?5@bWu6cq&|mICQ2Y^+uswaQm_e&4ANp?2!v248C9i-@Q4tuTZAI~kzmfYTJ?|Q zTf4rxFZLI(CgX(O$SRmv)w}SSkssaJy>aYyc+UUfH(8?^%tJ@DdM7nuC@DLvxFROni0r%iiu8PE=qSbFaM>U*%~M;82E6w9Wgb z^qe8`GgFyL4ZlN(kG+9Ml@l)nwe8@ItcGY78T@v}`;s;@=Dl&9?@|QGAfp3O1Zggo z8*y9arz?rfSFvxU#Z|^qO!=D8f@hbi-fCTM1!`%W&c8DytU~Sm`-41rh&oXm9=p>$^tlGw7BXhogdXHW+Jyo8$MuCUd0u@aMDA|r zqPytAd!PWygt;bK9%4XdZ^C0=c!mY6NFRU*+E?Tjck;p}y#+hPtxAOVHr&25;-*#2 z;S>FSTwH3gidojqW5mL5-=CBM_MA|v;-7N+Xr@6 z6`;HIb~&^CHHEcm{O)$nbo22;|K>&tDl;p1H-FC(*!Wo2sVi6z^S9{m#0I_kn38V* zJZU`(30c2XQ>|(J;TgwCqoJjFgz7|)Fx;p53vHZ}O3{$?NOjrS?e`0-sr*3WcLY}p zx<1b$JX+bmD%?IQWV|yooMq)U!2XGk)_<|^Ev!ui96gz?M3Qf0+bjju`EpIBxDm)n zh5Ha&8Mn8`Y0BY%ChCWkect$WAdXq!MFw&OCt^A`?B}~o)nqj;M@JVBpNewAkB6wM zLgwRQOSQXncCp2Y6IVmGq9O6m0kNa3c6uyY`0m)#>5xVeJBSXFHv#^a?Sb5_bock$ z@_tWbiCuyrMNq3aw3q+Q<4+l(7y3`p$-CWTfD>tyhn>%9niiSuyo=<4(QAUiW&$W} z#Is#d3K=@~v^1owSEt{cMh22%>}s!z{pspmqV$?p9jL6^2P9EIi$|VVahTRUhrwk^ zcxAur<&UH{;pd$W;GbK*U)nkR6TRcWSMPia&VHxqJWC!3;iVC}|3=YlG@c^v zx7_%1jDb?kFH%XOE^O%?UW(7&1`aX)j~A*4{2hz-nX3oGfM^ct$0K(CVG ze_ABr6Oio{M7E8hyuo-;JpTN3{QOP8FGf9f`hE{5p5c(mXMJdkI)!A_pj?1_xJb2}53$QpyPS#wWTgb8;Yo{2SRWDOE zHsq8ZD^i}9B)G-pXkNWlZ2ThLVZ4tr+H#kIN(8BB6G&_K&TKB zR_*XGb|Q#4F499U2d7nvr>PK{L?7M<5=Zd$p-oHZ)p_Z0?nDxBm7em(68x_KCx$)y zj=zZ&>S)_*hIk*9X`rFq{8}5CqY(1Oc@p#_ooYvY_@f6EpBU{WLSoMHjgSWmpEzX| z>JOT~qrHTd$g8i!YGU2_Xx1SyHMM-IneReV^Sk+e4c+a?3Av$oqZ~O`yxV&1T@Sf# zQAZNT-fLnTiVj5UaYUXpp?3F?yWPJk;VdB)uEzSt+V+A(-n!Y z-CyK=kECf`^Sv70SRzG`a&1%4;#&7-7f&k658^e$P^jyT7|76R#SCITd2-o-K4boM zw}YYB59g7U%Ym6ab~PUf0Qzs<=q)EeOdJavxZW*IJc`wOgHhid7<*PkP9Zrna0xSZLFQB43eLr#nI7Yz38b162|5%fWQcPZN>`N z%i)-P4Y%Wzzr1eg*Myoeu#UotC zlN{&Uv2PT;cvLId?BQIcA;FI_q4vcUJ_a2hgASj!ul{9bqLMz!rxNjFP3SYpaFVK5 z-BQWN%5vcGip|Jvy0fPq` zHh87e5>|)CUD97+N65=d^3#nh#=0Q=94caMtsWmT2W4B{n_Kax4>cic#s9?mw1a1W zrE69Lv#Ta4Y@~=EVHv5wt_~T=P3dM9F%pI7*?gEcW7Z_B%yaw^OE|JKg>gtfRlac* zR9PS8ZzhV}Y0>1)g-wJs=%-cwpc%%u5A5rQ{6~A*lQ)O{tVSRs{H3(qWSc)(dN^E~ zE_eNtHRJkQM$|_9Nw@gOnQTcr;@FWCv6kr^&Z;h19)J&To8kyk4C1$MQ3An zQ_d3zy!SJg!QmkOnNf<1t%w_P*836%FR!Q3-uT8oMidM39tFL*5?LLH!qg0lVDw<( zR)gj!sK9$f(VBWP%9bVK%V-EBU$Rsy_Bpyspxr??g~SiwMJPzFiXACT29#lV8FkJ{Ew;gy&_A zVS&xYjtORE03fyV`Uh0b*CLUl9vnz)b-6}A`h1fgC-7IzDZ(B_p&XvTX*!v94DKcw zpn(;ZCa!50n&{*C&9snHC3?n3a=URA*WOfo!{?6r@3~_*2Ay>&{?Ko88x7~wgH-3m zZ)Kqo-<8lQNsA+9T)a7+mD~+^kTun@WSA~xT|2d8snlo1_=ghX(Z$Z6cWhX!DyRX0 ztJ+PH;I~kDh@{}*Mx09i0d+~ zUGIFon=2{EauMOubv+)c@LJ0@w7U`y z_*(-a{6S=-vVJ3dUb{*O+1BkK>Kev!Jd~6bSGy=UR&Cof-E|xsO1*;d$2x|TtV9__ zDMczYa|Xvs+mzxALw$XL8awGb(q3FIBk*ZnD-~-6P(3!##j+28$N6#qJF;Ol>QtE} zq_J=PoEcq`UfZmZVelj?>LeApiDy@rqFBR%VoNF|+Dh?Rl9fET3kO3?1r@4QAl_(w zT;)UXH>h{om|mTd|aL)nw_zN--duBuw(u zi>{Az^icyWh2-Gp!>uo9#%xe52B~4af#Z}pr8?~83Et9eL-9MuLHbqrk>(QF?B*Ev z-1%q|@0$!OTn8D9!AlDRdh~wCtQz3jo0Drh`2unI| zY%R2jqu?Jh3*|rQ8rwbZekZB>&2ib+<6CDtkZK3UW#sUy>%i@vI=MT(Y;xUE-+-Nv z^aaj@g;Ek$P|@bZkg>``0rMx}8=kw;xuWgc@!KEZ3J$=LTmls`>dbMzeKzm_qJf5E zT#H(6ZY?CzuF{&=(JFfegJ>=YFsdww6t)3oQ?%1m8>qtpO12K@90P&F5LZ*+o zm-@vu(=GB2Ybg)5+)F_F8nu9zR)f|zvB5lx3l6(iZ&4W%8kSb$939T>-UHm#7lepC zCNF!tcPSmNLG9_#3Jv}(&Y1qv0K_lYLH38Jscx=O5;_Cib`YGi5b}7Ze-9A{{oU#L z$j`!xo;Ue>+mlaYU1A4cu6gYxNDrm`%@cWzUw#bq&8;MubIi#$Jy1_6WG{6L5EUZa&PP8ZW!tW$by1lxz9JAvA9pv|GetkXoetnyw>K>`~Zj1V^ zIAU!yxUS#%h!UgEnEG`%nj^+00CP{SnYg=>x^C-YhFw67ZRD$UyQ2Cc;WR$0ZDEyW zx;u;+36U``B=&u%_#Mr)cHD8XuQGJzpuz=ua5?b_y_4q&CqnkG+hz`fs?pbwHH{>@ z9L{{+i_3;!v~^mr1KWT1HE&&K)yj zROwz~L5jYBp*RJg33h_7?Z}xR?l_d6>bT@VnAsW5dW4tPNWL>_RpmG_X8)&9 zvoXi?{UCDBadxXi#=Go7oPJByQu~y~o15>GEs=hwz4Eowd~c`MNg9!h{FmEhD27@# z-65kzhVFoQ-|-Fy$m1t{eWaU-TAMJRMfjX4sFPLp_g~kB7!_s<)Q`|~z<$Wm+17-u(A}Wj-_TE|3`H8p& zV;=em;hEy|)=pE#c=8i(o`}90;jt4V^b;M|D~k_UKZMW~8re_sub8YKIrVV_u__uR z5ef#(fIA0?JPb6cK#y0bT1|G&gox`Pv``J-zf_QLjPi`tErQ0+2${9GMwS1A0>&QL zQ#Sr8&)=M69Hr1hIpk=As9)@SN3d26Xk``bd*tpdJt2p(oLhdaS;LT5T9qNab=kDq zA&p0_MQ<*&ghs{>tq~JU;ghj&1}2b9x7gFcGS3&CeR>+)uLIt%A9UQTV~U7t)xZUA zd(xJj!DU@?%dk@kU5(+F0<|3Xoz0UYehIA(=O*NFQ`t$x5u61AY+5Nb@M5c#`G=@q} z6;u@1w1)LRU}lM%%IVU0cGlHnKd7m~Q|F#yzCl^@uT#;({+il%lljo#mTPU7ZXWZ2 zL@bk*U~WZ>J)aWu89+WSIn{}|Y^bNswj~*+mNJ^2td&L|?^yh?vU&}xcnVjSIO!;_ zQSk)ecixNKjVnWJCJZL@zqKF;>98Ib3Bn*~U3ChO9evZB3vp zICKzKDEegJPOra5w#IzQ9K=OoKz|o*Z;q6yWE$pIAo67Y=5oG}e$ht7y8}jDA`f-P zK>eBjB7HMXUfTJq+^FOGefrVX={E8m46S6A6>i4+JxVqw#_^Gb#4pkvJI24>nC?I8NM`8`gy@lEwqh+0&4TYRHk#(U|K*_YyMeT4I=JgMTl`?C-X`V zn1KBAky-=`*-7wj!_n8(6|Qco(tqw-$c@;Z_cX-tL_5n*0j$zUf3W-O8$Wj4tjzN^cDI(5>C0`KD52R;jq{1FFF^hai|?&3(2We(Z(PLwDbr-G`x zz}^OP6e|UBh>ky6W@lA?ki4N+PpD2JM*9BZd|Is_Y^B1Sk<}wv@5cj1H`$!;Rzw3`~dMncP6r*Iz86R-# zb!|1*WMYM0{6s#{Ykj;&S(RA(u&uHJznq&cMn|k6yVvi^RU^)rC(>$lU5UGS;F87i z#))}-NXr4f9$j0k6C3dFlx$Y+cPCSvl}!Lz{Xgd{r493bz$_~ybl7z20#N=0Q9rm9&q)!6gOsP2t5jvqVq&$k zu5%gNoQ46PmwwEMAp&IEQ)N->y?wfEl#1-j*mYTRXJ6T@eAzsM+2QY@&R^Kog0TfL z1sFxmeWE!9`Hyf7hjH=7U|RDp@)GfD%#*!8`wPYLe7~-u%y4ZAgXRB3C08dgEFH4c z>U@sP`#Wnem`g7U z(3IoKFQiV`%j9J)MVQYb{OcbQ-KI_!j>+JC8>|{j{TKRTIJt*k;{`|Q4()FK1 zl8CZl@h1-Y&~W|xvl9y-$9S(|LQEF}i4t70qT7PQb%Re93$1Vewnd>P|ao@a~SBs#UZp!d`VhX`&r0lR{FsZNQ$>?6V}9l;nKvv zs^HN-e_A;O)mbR4vw~>()a6q6se<}01l>(JQ$k+%iAL&CxPSB?Pn`xF>Bi@b$HWd;@l*2 zZjA3%8ujUEvDI8q`QWr50L=j-KHCfMGl%MzYQIS);KUp>K@g55Kp3Nw|IW>mmD=g0 zVamAb>q%@~Edrw#6^7~bdhP00o+>6EjFRHilx8Oxit~-XEnwvYP1;3EdBb7%tx9Y; zR}W4V9i~2(2#7Z*U1tmX~B)|7nc}ajrpFw6Ycq7s9Rhy9AHX&gWE7 zu86#Vz8~$o%1tj89PJ-D*CSp|X|`^$t)oh^jY$KF@mh0j^Mijw)3ZL*5W^1Cd@vPK zn-@Cm5v2WdR`E?Ww?s`4V%umFvawEwTkY&-n>BiJd-AgqPt00Fh1*(IHFgeZ3nXrh z?Fm9;lj86ziKLzNqf=3#edfZEFpFZMg+BQh>!Ab8Y`coumO2T3S<<(LI@w_&Ki|p& zkVV;qnCOnm1j=7j9D=exDLqvdQfiutL0B)Mow=1TzpdXw)Yr?7NymtPx@H{8y6;;I zV(cfn018@~mxLf+`Fm=U5`i6abkQ#({|=o!FhibB1GD1R>=B)fa9b9!er?jP#0XMw z1=OK8N5y?8jO51gEt=hbq(B$TWPI=hPyp1pSdwcTy1yasFRuYw9R}t z6!}}l2e4ex{16Gp15u@@Bf72?Y zW2<(enthznSZFPem9iaH{mijqlUl)E&-L%iAS&bmq$&W;li_M&oB5h%zowh|U;V{{ z-tayKof=62AJoPT9e?N@dAyUns6syZaWgz14fUN#9;3qT#wVgMUaRV2vO6A~&ArYr zdrB+BAVo4_Ys&F`e}Qk2_mMP!$=}?idkZPXcW}J)q;$slKSIBTIa9VjST24mV@x}fZO7bX@0MfZr@Hh@xT2FSNOB)AlKFBhBV9PZu_;F z&%aVJFABob?Bepk56ryuufitE@8$jsVH=y^WFUFTm8Sgx;t3pI{W{(z>)&1p7t8T- z|D5y*Lx=AXO@T$bD|by&lgD=EcdbeB3HvMJoy;qeb^x2sqLBR1gS;@9=6>DRh*~w% zgHhM3#(>>&(@4NoBKw3!JB=2D7wG$=gj;{Y9fyS}b$HuwhOPgSe6f#SHa19zp;+ZG z80uN*8CAgH6h=kXc_sfo_zxjQl92qD?_VFhDbX8?+^3Y{J{4@6H~txtXyI8X;C%h_ znk@zGsVvKLs;5%5ZxQuQtuw>Q-w3BR=oPm`&GQwe@!zX=gTmp@41sQU>m4Xo$ah4q z1%b1~oIxZ_sGkc>aBtVy7en+^Y5CN3f4-)sz};bw1te#sV+y{-{q5zb>6!LAvL&}| zgPeX-j$Uc!AgU z$Gq>NeC*4EL}{N88KvXE4YN98IyQ9r81LZ3Fs|5|+nBFvpS~5zIwtW?%pV}dV^TOB z-?-B2&L8ocv+91e(mt|!%WX42%OglT#CsiIV92Dl;Ne#f3W8?375R3>CLJ?($e< z`8!G1h5kUM6?OamrE7K9H*Yn`eZqnx`a|>2l8TlS7&s0%tM{(c=&lS!)$vv)@7l(d zRx*KLfGyS%Irf^j*7n$PEfc;C>fKB01WL$V#?_o{M#h}$lc=A4fIu0ndZ_JqZO!aX z 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 |%F(BJFHCq3pF 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

YBvu9UVk?n=SJ=$+2ziatfpq&?O74_ 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

Qf((sXQc*MjpgC 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≫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�{S+3fZ-hMfu{#Z(yVPGkh*hYRfc)(cg0-9cef(Gw5tG+NE2Qh^& ztn7IO9SF_lhQ#^Xu-4lA@w?1C*fm)?-Ih3ADgxl+02u&d5~R(=gJvn-eXBY|0#nL|?fEGq~nPBe0rc`&HSHokHof$WLnlDMjPa#5ehL_B8LV08NZr_N3B&PWXOT zw3X1Xd?+5>uv_$}SqaF?hW8_oK=r>_9Ax@4DX5U(e5~&-N?Dn{;Q`p$0^TUY#f&)$M0k18G&xvcFlzz!Te5_JRgsx&DG9es5B))aLSM zY+}Tn@M@1ArC8+B*AM5Knh6^hm(rW2Vld8%J&W?Fhf(jDY4|&L?=6ToG3YC&@1D?Q zlEmjDM6Xbbmq7FhezYvE)P3y7i;32~SpRIm_64h9(v3v!ro)2fE?Lj&J}BMp8(;0Fxz=oe zF;kA`r|B*+;-hm_KDbUC*vD!_)QKet?9ddMabk*e2e*zyZXafUA+z0LsAYxY)RH5? zpCZ_gngRlS<4m5z2|1syb9YBCs2tl(m*R&~o%*JV3(mOIab?eHO@8FT$-o%I z$sh(*%mCI_3pA`GwW6f7D79EWsVKiZFFv(0Rj(i~J;0ll4WyI_2&;kgmNumMeshes, 2u); + EXPECT_EQ(scene->mNumMaterials, 2u); + EXPECT_EQ(scene->mNumAnimations, 0u); + EXPECT_EQ(scene->mNumTextures, 2u); + EXPECT_EQ(scene->mNumLights, 0u); + EXPECT_EQ(scene->mNumCameras, 0u); + + // Expected common metadata + aiString value; + EXPECT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT, value)) << "No importer format metadata"; + EXPECT_STREQ("Collada Importer", value.C_Str()); + + EXPECT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT_VERSION, value)) << "No format version metadata"; + EXPECT_STREQ("1.4.1", value.C_Str()); +} From 6e863b2435b59857f2d9d41262b6b110698f638b Mon Sep 17 00:00:00 2001 From: Rahul Sheth Date: Wed, 14 Jul 2021 08:27:04 -0400 Subject: [PATCH 196/335] Find stb for Assimp --- cmake/assimp-hunter-config.cmake.in | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/assimp-hunter-config.cmake.in b/cmake/assimp-hunter-config.cmake.in index 91efcbf24..5c5aeedfd 100644 --- a/cmake/assimp-hunter-config.cmake.in +++ b/cmake/assimp-hunter-config.cmake.in @@ -9,6 +9,7 @@ find_package(poly2tri CONFIG REQUIRED) find_package(polyclipping CONFIG REQUIRED) find_package(zip CONFIG REQUIRED) find_package(pugixml CONFIG REQUIRED) +find_package(stb CONFIG REQUIRED) if(@ASSIMP_BUILD_DRACO@) find_package(draco CONFIG REQUIRED) From 632b2db97c57d2147bf6e8393ba8e35d2b2f4bad Mon Sep 17 00:00:00 2001 From: RichardTea <31507749+RichardTea@users.noreply.github.com> Date: Wed, 14 Jul 2021 13:39:41 +0100 Subject: [PATCH 197/335] Ensure glTFv2 scene name is unique Use the provided scene name if extant Fixes issue #3978 --- code/AssetLib/glTF2/glTF2Exporter.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index 02c3233f3..47780606f 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -88,15 +88,13 @@ namespace Assimp { } // end of namespace Assimp glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const aiScene* pScene, - const ExportProperties* pProperties, bool isBinary) + const ExportProperties* pProperties, bool isBinary) : mFilename(filename) , mIOSystem(pIOSystem) + , mScene(pScene) , mProperties(pProperties) + , mAsset(new Asset(pIOSystem)) { - mScene = pScene; - - mAsset.reset( new Asset( pIOSystem ) ); - // Always on as our triangulation process is aware of this type of encoding mAsset->extensionsUsed.FB_ngon_encoding = true; @@ -1338,8 +1336,11 @@ unsigned int glTF2Exporter::ExportNode(const aiNode* n, Ref& parent) void glTF2Exporter::ExportScene() { - const char* sceneName = "defaultScene"; - Ref scene = mAsset->scenes.Create(sceneName); + // Use the name of the scene if specified + const std::string sceneName = (mScene->mName.length > 0) ? mScene->mName.C_Str() : "defaultScene"; + + // Ensure unique + Ref scene = mAsset->scenes.Create(mAsset->FindUniqueID(sceneName, "")); // root node will be the first one exported (idx 0) if (mAsset->nodes.Size() > 0) { From aa883eda195fd144a27789f69059a8742ad93103 Mon Sep 17 00:00:00 2001 From: Hill Ma Date: Thu, 15 Jul 2021 11:54:55 -0700 Subject: [PATCH 198/335] FBX: fix double precision build. --- code/AssetLib/FBX/FBXExporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/FBX/FBXExporter.cpp b/code/AssetLib/FBX/FBXExporter.cpp index ccff15c15..c19c593dd 100644 --- a/code/AssetLib/FBX/FBXExporter.cpp +++ b/code/AssetLib/FBX/FBXExporter.cpp @@ -1690,7 +1690,7 @@ void FBXExporter::WriteObjects () aiUVTransform trafo; unsigned int max = sizeof(aiUVTransform); - aiGetMaterialFloatArray(mat, AI_MATKEY_UVTRANSFORM(aiTextureType_DIFFUSE, 0), (float *)&trafo, &max); + aiGetMaterialFloatArray(mat, AI_MATKEY_UVTRANSFORM(aiTextureType_DIFFUSE, 0), (ai_real *)&trafo, &max); // now write the actual texture node FBX::Node tnode("Texture"); From 5171aa52d1cba2fee4ab456bc4c18a2b98c8e883 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 16 Jul 2021 11:47:38 +0200 Subject: [PATCH 199/335] Remove dead code --- code/AssetLib/Collada/ColladaParser.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/code/AssetLib/Collada/ColladaParser.cpp b/code/AssetLib/Collada/ColladaParser.cpp index a58cc6003..37c7274f4 100644 --- a/code/AssetLib/Collada/ColladaParser.cpp +++ b/code/AssetLib/Collada/ColladaParser.cpp @@ -626,8 +626,6 @@ void ColladaParser::ReadController(XmlNode &node, Collada::Controller &controlle XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); XmlNode currentNode; while (xmlIt.getNext(currentNode)) { - - //for (XmlNode ¤tNode : node.children()) { const std::string ¤tName = currentNode.name(); if (currentName == "morph") { controller.mType = Morph; @@ -1439,9 +1437,8 @@ void ColladaParser::ReadDataArray(XmlNode &node) { throw DeadlyImportError("Expected more values while reading float_array contents."); } - ai_real value; // read a number - //SkipSpacesAndLineEnd(&content); + ai_real value; content = fast_atoreal_move(content, value); data.mValues.push_back(value); // skip whitespace after it @@ -1489,11 +1486,10 @@ void ColladaParser::ReadAccessor(XmlNode &node, const std::string &pID) { std::string name; if (XmlParser::hasAttribute(currentNode, "name")) { XmlParser::getStdStrAttribute(currentNode, "name", name); - //name = mReader->getAttributeValue(attrName); // analyse for common type components and store it's sub-offset in the corresponding field - /* Cartesian coordinates */ + // Cartesian coordinates if (name == "X") acc.mSubOffset[0] = acc.mParams.size(); else if (name == "Y") From 5285736ba008044969a5701a62fff927e3f37a86 Mon Sep 17 00:00:00 2001 From: Adrian Perez Date: Fri, 16 Jul 2021 14:21:32 -0700 Subject: [PATCH 200/335] Fix issues encountered during integration atempt --- code/AssetLib/glTF/glTFCommon.h | 7 +++---- code/AssetLib/glTF2/glTF2Asset.inl | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/code/AssetLib/glTF/glTFCommon.h b/code/AssetLib/glTF/glTFCommon.h index d99ffbe86..500298d80 100644 --- a/code/AssetLib/glTF/glTFCommon.h +++ b/code/AssetLib/glTF/glTFCommon.h @@ -194,12 +194,11 @@ inline void CopyValue(const glTFCommon::mat4 &v, aiMatrix4x4 &o) { inline std::string getCurrentAssetDir(const std::string &pFile) { std::string path = pFile; - int pos = std::max(int(pFile.rfind('/')), int(pFile.rfind('\\'))); - if (pos != int(std::string::npos)) { - path = pFile.substr(0, pos + 1); + if (pos == int(std::string::npos)) { + return ""; } - return path; + return pFile.substr(0, pos + 1); } #if _MSC_VER # pragma warning(pop) diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index fd509bada..58095c7bd 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -965,9 +965,9 @@ inline void Accessor::Read(Value &obj, Asset &r) { componentType = MemberOrDefault(obj, "componentType", ComponentType_BYTE); { const Value* countValue = FindUInt(obj, "count"); - if (!countValue || countValue->GetUint() < 1) + if (!countValue) { - throw DeadlyImportError("A strictly positive count value is required, when reading ", id.c_str(), name.empty() ? "" : " (" + name + ")"); + throw DeadlyImportError("A count value is required, when reading ", id.c_str(), name.empty() ? "" : " (" + name + ")"); } count = countValue->GetUint(); } From 30d342534a1ea5d71f87dd5f1f255f9d6146c86e Mon Sep 17 00:00:00 2001 From: Adrian Perez Date: Fri, 16 Jul 2021 14:21:32 -0700 Subject: [PATCH 201/335] Fix issues encountered during integration atempt --- code/AssetLib/glTF/glTFCommon.h | 6 +++--- code/AssetLib/glTF2/glTF2Asset.inl | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/code/AssetLib/glTF/glTFCommon.h b/code/AssetLib/glTF/glTFCommon.h index d99ffbe86..0506ad056 100644 --- a/code/AssetLib/glTF/glTFCommon.h +++ b/code/AssetLib/glTF/glTFCommon.h @@ -195,11 +195,11 @@ inline void CopyValue(const glTFCommon::mat4 &v, aiMatrix4x4 &o) { inline std::string getCurrentAssetDir(const std::string &pFile) { std::string path = pFile; int pos = std::max(int(pFile.rfind('/')), int(pFile.rfind('\\'))); - if (pos != int(std::string::npos)) { - path = pFile.substr(0, pos + 1); + if (pos == int(std::string::npos)) { + return ""; } - return path; + return pFile.substr(0, pos + 1); } #if _MSC_VER # pragma warning(pop) diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index fd509bada..58095c7bd 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -965,9 +965,9 @@ inline void Accessor::Read(Value &obj, Asset &r) { componentType = MemberOrDefault(obj, "componentType", ComponentType_BYTE); { const Value* countValue = FindUInt(obj, "count"); - if (!countValue || countValue->GetUint() < 1) + if (!countValue) { - throw DeadlyImportError("A strictly positive count value is required, when reading ", id.c_str(), name.empty() ? "" : " (" + name + ")"); + throw DeadlyImportError("A count value is required, when reading ", id.c_str(), name.empty() ? "" : " (" + name + ")"); } count = countValue->GetUint(); } From e13ce4b624728bdcf4475fe511fdbbeab7fc1095 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 21 Jul 2021 09:46:23 +0200 Subject: [PATCH 202/335] Fix version, remove deprecated doc files, fix some path errors --- doc/Doxyfile.in | 36 ++++++------------------------------ 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index ffe39f9f7..8b1e802d2 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -32,7 +32,7 @@ PROJECT_NAME = Assimp # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = "v4.1. (December 2018)" +PROJECT_NUMBER = "v5.0.1. (December 2020)" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer @@ -197,7 +197,7 @@ SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. -TAB_SIZE = 8 +TAB_SIZE = 4 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". @@ -663,11 +663,7 @@ WARN_LOGFILE = # with spaces. INPUT = @doxy_main_page@ \ - @PROJECT_SOURCE_DIR@ \ - @PROJECT_BINARY_DIR@ \ - @PROJECT_SOURCE_DIR@/include/ \ - @PROJECT_SOURCE_DIR@/doc/dox.h \ - @PROJECT_SOURCE_DIR@/code/BaseImporter.h + @PROJECT_SOURCE_DIR@/include/ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is @@ -690,28 +686,9 @@ FILE_PATTERNS = *.c \ *.cxx \ *.cpp \ *.c++ \ - *.d \ - *.java \ - *.ii \ - *.ixx \ - *.ipp \ - *.i++ \ *.inl \ *.h \ - *.hh \ - *.hxx \ - *.hpp \ - *.h++ \ - *.idl \ - *.odl \ - *.cs \ - *.php \ - *.php3 \ - *.inc \ - *.m \ - *.mm \ - *.dox \ - *.py + *.dox # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. @@ -725,7 +702,7 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = irrXML.h +EXCLUDE = contrib/* # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded @@ -739,8 +716,7 @@ EXCLUDE_SYMLINKS = NO # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* -EXCLUDE_PATTERNS = */.svn/* \ - */.svn +EXCLUDE_PATTERNS = */.git/* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the From aa8628abef7b766cf75908d0d91f1f11b138c6a1 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 21 Jul 2021 20:38:58 +0200 Subject: [PATCH 203/335] Disable html and enable xml --- doc/Doxyfile.in | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index 8b1e802d2..e499f09ea 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -636,7 +636,7 @@ WARN_IF_DOC_ERROR = YES # wrong or incomplete parameter documentation, but not about the absence of # documentation. -WARN_NO_PARAMDOC = NO +WARN_NO_PARAMDOC = YES # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text @@ -877,7 +877,7 @@ IGNORE_PREFIX = # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. -GENERATE_HTML = YES +GENERATE_HTML = NO # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be @@ -1460,7 +1460,7 @@ MAN_LINKS = NO # generate an XML file that captures the structure of # the code including all documentation. -GENERATE_XML = NO +GENERATE_XML = YES # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be From d554b4950d991caf110819966f852eb712425d08 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 22 Jul 2021 20:48:25 +0200 Subject: [PATCH 204/335] Add hpp to dxygen filter --- doc/Doxyfile.in | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index e499f09ea..54273b90c 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -688,6 +688,7 @@ FILE_PATTERNS = *.c \ *.c++ \ *.inl \ *.h \ + *.hpp \ *.dox # The RECURSIVE tag can be used to turn specify whether or not subdirectories From b7f88f30636659ced8cddfdce12309b8f78c631b Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 26 Jul 2021 10:51:16 +0200 Subject: [PATCH 205/335] closes https://github.com/assimp/assimp/issues/3957: checkj for empty positions. --- code/AssetLib/Collada/ColladaLoader.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/code/AssetLib/Collada/ColladaLoader.cpp b/code/AssetLib/Collada/ColladaLoader.cpp index 492e6971f..ee8f97203 100644 --- a/code/AssetLib/Collada/ColladaLoader.cpp +++ b/code/AssetLib/Collada/ColladaLoader.cpp @@ -619,6 +619,10 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrc dstMesh->mName = pSrcMesh->mId; } + if (pSrcMesh->mPositions.empty()) { + return dstMesh.release(); + } + // count the vertices addressed by its faces const size_t numVertices = std::accumulate(pSrcMesh->mFaceSize.begin() + pStartFace, pSrcMesh->mFaceSize.begin() + pStartFace + pSubMesh.mNumFaces, size_t(0)); From 16c3f82222211f7708b8607e4946b0fd25141ed7 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 26 Jul 2021 11:24:18 +0200 Subject: [PATCH 206/335] closes https://github.com/assimp/assimp/issues/3975: Use latest version of OpenDDL-Parser --- contrib/openddlparser/code/OpenDDLParser.cpp | 30 ++++++++++++++----- contrib/openddlparser/code/Value.cpp | 9 +++--- .../include/openddlparser/OpenDDLParser.h | 13 ++++++-- 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/contrib/openddlparser/code/OpenDDLParser.cpp b/contrib/openddlparser/code/OpenDDLParser.cpp index 6a9f802ec..0c9e0bd98 100644 --- a/contrib/openddlparser/code/OpenDDLParser.cpp +++ b/contrib/openddlparser/code/OpenDDLParser.cpp @@ -132,6 +132,24 @@ OpenDDLParser::~OpenDDLParser() { clear(); } +void OpenDDLParser::logToStream(FILE *f, LogSeverity severity, const std::string &message) { + if (f) { + const char *tag = "none"; + switch (severity) { + case ddl_debug_msg: tag = "debug"; break; + case ddl_info_msg: tag = "info"; break; + case ddl_warn_msg: tag = "warn"; break; + case ddl_error_msg: tag = "error"; break; + } + fprintf(f, "OpenDDLParser: (%5s) %s\n", tag, message.c_str()); + } +} + +OpenDDLParser::logCallback OpenDDLParser::StdLogCallback (FILE *destination) { + using namespace std::placeholders; + return std::bind(logToStream, destination ? destination : stderr, _1, _2); +} + void OpenDDLParser::setLogCallback(logCallback callback) { // install user-specific log callback; null = no log callback m_logCallback = callback; @@ -171,12 +189,8 @@ size_t OpenDDLParser::getBufferSize() const { void OpenDDLParser::clear() { m_buffer.resize(0); - if (nullptr != m_context) { - delete m_context; - m_context = nullptr; - } - - // DDLNode::releaseNodes(); + delete m_context; + m_context = nullptr; } bool OpenDDLParser::validate() { @@ -506,7 +520,9 @@ char *OpenDDLParser::parseName(char *in, char *end, Name **name) { in = parseIdentifier(in, end, &id); if (id) { currentName = new Name(ntype, id); - *name = currentName; + if (currentName) { + *name = currentName; + } } return in; diff --git a/contrib/openddlparser/code/Value.cpp b/contrib/openddlparser/code/Value.cpp index 708a6878f..5a8aa39be 100644 --- a/contrib/openddlparser/code/Value.cpp +++ b/contrib/openddlparser/code/Value.cpp @@ -113,13 +113,14 @@ Value::~Value() { if (m_data != nullptr) { if (m_type == ValueType::ddl_ref) { Reference *tmp = (Reference *)m_data; - if (tmp != nullptr) + if (tmp != nullptr) { delete tmp; - } else + } + } else { delete[] m_data; + } } - if (m_next != nullptr) - delete m_next; + delete m_next; } void Value::setBool(bool value) { diff --git a/contrib/openddlparser/include/openddlparser/OpenDDLParser.h b/contrib/openddlparser/include/openddlparser/OpenDDLParser.h index 5794add90..3fbb4b6af 100644 --- a/contrib/openddlparser/include/openddlparser/OpenDDLParser.h +++ b/contrib/openddlparser/include/openddlparser/OpenDDLParser.h @@ -29,6 +29,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +#include BEGIN_ODDLPARSER_NS @@ -97,8 +98,8 @@ DLL_ODDLPARSER_EXPORT const char *getTypeToken(Value::ValueType type); //------------------------------------------------------------------------------------------------- class DLL_ODDLPARSER_EXPORT OpenDDLParser { public: - /// @brief The log callback function pointer. - typedef void (*logCallback)(LogSeverity severity, const std::string &msg); + /// @brief The log callback function. + typedef std::function logCallback; public: /// @brief The default class constructor. @@ -120,6 +121,11 @@ public: /// @return The current log callback. logCallback getLogCallback() const; + /// @brief A default log callback that writes to a FILE. + /// @param destination [in] Output stream. NULL will use stderr. + /// @return A callback that you can pass to setLogCallback. + static logCallback StdLogCallback(FILE *destination = nullptr); + /// @brief Assigns a new buffer to parse. /// @param buffer [in] The buffer /// @param len [in] Size of the buffer @@ -192,6 +198,9 @@ private: typedef std::vector DDLNodeStack; DDLNodeStack m_stack; Context *m_context; + + /// @brief Callback for StdLogCallback(). Not meant to be called directly. + static void logToStream (FILE *, LogSeverity, const std::string &); }; END_ODDLPARSER_NS From df2e7208fbca1b609f20e994d5829a419d17a924 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 26 Jul 2021 11:56:26 +0200 Subject: [PATCH 207/335] Fix fuzzer issue in m3d-importer - closes https://github.com/assimp/assimp/issues/3974 - Check for nullptr before dereferencing name in m3d-data-instance. --- code/AssetLib/M3D/M3DWrapper.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/code/AssetLib/M3D/M3DWrapper.h b/code/AssetLib/M3D/M3DWrapper.h index 54d7a2eec..d5fc9eaa5 100644 --- a/code/AssetLib/M3D/M3DWrapper.h +++ b/code/AssetLib/M3D/M3DWrapper.h @@ -83,7 +83,11 @@ public: // Name inline std::string Name() const { - if (m3d_) return std::string(m3d_->name); + if (nullptr != m3d_) { + if (nullptr!0m3d_->name) { + return std::string(m3d_->name); + } + } return std::string(); } From 291c0a4faa37581e056b43d882e92556cbdc98f1 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 26 Jul 2021 13:13:21 +0200 Subject: [PATCH 208/335] Fix build failure - Fix the failure - Put inlined stuff out of declaration - Add some docu --- code/AssetLib/M3D/M3DWrapper.h | 68 +++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 22 deletions(-) diff --git a/code/AssetLib/M3D/M3DWrapper.h b/code/AssetLib/M3D/M3DWrapper.h index d5fc9eaa5..96db9c8dd 100644 --- a/code/AssetLib/M3D/M3DWrapper.h +++ b/code/AssetLib/M3D/M3DWrapper.h @@ -46,6 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef AI_M3DWRAPPER_H_INC #define AI_M3DWRAPPER_H_INC + #if !(ASSIMP_BUILD_NO_EXPORT || ASSIMP_BUILD_NO_M3D_EXPORTER) || !ASSIMP_BUILD_NO_M3D_IMPORTER #include @@ -62,45 +63,68 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "m3d.h" namespace Assimp { + class IOSystem; +/// brief The M3D-Wrapper, provudes c++ access to the data. class M3DWrapper { - m3d_t *m3d_ = nullptr; - unsigned char *saved_output_ = nullptr; - public: - // Construct an empty M3D model + /// Construct an empty M3D model explicit M3DWrapper(); - // Construct an M3D model from provided buffer - // NOTE: The m3d.h SDK function does not mark the data as const. Have assumed it does not write. - // BUG: SECURITY: The m3d.h SDK cannot be informed of the buffer size. BUFFER OVERFLOW IS CERTAIN + /// Construct an M3D model from provided buffer + /// @note The m3d.h SDK function does not mark the data as const. Have assumed it does not write. + /// BUG: SECURITY: The m3d.h SDK cannot be informed of the buffer size. BUFFER OVERFLOW IS CERTAIN explicit M3DWrapper(IOSystem *pIOHandler, const std::vector &buffer); - ~M3DWrapper(); + /// Theclasss destructor. + ~M3DWrapper(); - void reset(); + /// Will reset the wrapper, all data will become nullptr. + void reset(); - // Name - inline std::string Name() const { - if (nullptr != m3d_) { - if (nullptr!0m3d_->name) { - return std::string(m3d_->name); - } - } - return std::string(); - } + // The Name access, empty string returned when no m3d instance. + std::string Name() const; - // Execute a save + /// Executes a save. unsigned char *Save(int quality, int flags, unsigned int &size); + + /// Clearer void ClearSave(); - inline explicit operator bool() const { return m3d_ != nullptr; } + /// True for m3d instance exists. + explicit operator bool() const; // Allow direct access to M3D API - inline m3d_t *operator->() const { return m3d_; } - inline m3d_t *M3D() const { return m3d_; } + m3d_t *operator->(); + m3d_t *M3D() const; + +private: + m3d_t *m3d_ = nullptr; + unsigned char *saved_output_ = nullptr; }; + +inline std::string M3DWrapper::Name() const { + if (nullptr != m3d_) { + if (nullptr != m3d_->name) { + return std::string(m3d_->name); + } + } + return std::string(); +} + +inline explicit operator M3DWrapper::bool() const { + return m3d_ != nullptr; +} + +inline m3d_t *M3DWrapper::operator->() const { + return m3d_; +} + +inline m3d_t *M3DWrapper::M3D() const { + return m3d_; +} + } // namespace Assimp #endif From e8e720d584813ad41703e9e96f1798f7ad11b360 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 26 Jul 2021 13:41:54 +0200 Subject: [PATCH 209/335] Update M3DWrapper.h --- code/AssetLib/M3D/M3DWrapper.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/M3D/M3DWrapper.h b/code/AssetLib/M3D/M3DWrapper.h index 96db9c8dd..83bbdfb85 100644 --- a/code/AssetLib/M3D/M3DWrapper.h +++ b/code/AssetLib/M3D/M3DWrapper.h @@ -113,7 +113,7 @@ inline std::string M3DWrapper::Name() const { return std::string(); } -inline explicit operator M3DWrapper::bool() const { +inline M3DWrapper::operator bool() const { return m3d_ != nullptr; } From aeae2cf242046bfe16a0e4c15e52e7651da45f62 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 26 Jul 2021 14:44:26 +0200 Subject: [PATCH 210/335] Update M3DWrapper.h --- code/AssetLib/M3D/M3DWrapper.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/M3D/M3DWrapper.h b/code/AssetLib/M3D/M3DWrapper.h index 83bbdfb85..ba838d71d 100644 --- a/code/AssetLib/M3D/M3DWrapper.h +++ b/code/AssetLib/M3D/M3DWrapper.h @@ -96,7 +96,7 @@ public: explicit operator bool() const; // Allow direct access to M3D API - m3d_t *operator->(); + m3d_t *operator->() const; m3d_t *M3D() const; private: From 92af44f0925c190c1226dc9d4bff962001ff3d55 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 26 Jul 2021 15:47:19 +0200 Subject: [PATCH 211/335] Fix euler angles --- include/assimp/quaternion.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/assimp/quaternion.h b/include/assimp/quaternion.h index 7c096fe00..6941fbbc3 100644 --- a/include/assimp/quaternion.h +++ b/include/assimp/quaternion.h @@ -73,7 +73,7 @@ public: explicit aiQuaterniont( const aiMatrix3x3t& pRotMatrix); /** Construct from euler angles */ - aiQuaterniont( TReal rotx, TReal roty, TReal rotz); + aiQuaterniont( TReal roty, TReal rotz, TReal rotx); /** Construct from an axis-angle pair */ aiQuaterniont( aiVector3t axis, TReal angle); From 8ee2c721d91522db582301c2f0ce2d8a403f910c Mon Sep 17 00:00:00 2001 From: xiaohunqupo Date: Wed, 28 Jul 2021 16:32:27 +0800 Subject: [PATCH 212/335] StepExporter support polygon mesh StepExporter support polygon mesh --- code/AssetLib/Step/StepExporter.cpp | 120 ++++++++++++++++++---------- 1 file changed, 76 insertions(+), 44 deletions(-) diff --git a/code/AssetLib/Step/StepExporter.cpp b/code/AssetLib/Step/StepExporter.cpp index dfe5bab67..1228c72ea 100644 --- a/code/AssetLib/Step/StepExporter.cpp +++ b/code/AssetLib/Step/StepExporter.cpp @@ -175,12 +175,11 @@ void StepExporter::WriteFile() fColor.b = 0.8f; int ind = 100; // the start index to be used - int faceEntryLen = 30; // number of entries for a triangle/face + std::vector faceEntryLen; // numbers of entries for a triangle/face // prepare unique (count triangles and vertices) VectorIndexUMap uniqueVerts; // use a map to reduce find complexity to log(n) VectorIndexUMap::iterator it; - int countFace = 0; for (unsigned int i=0; imNumMeshes; ++i) { @@ -189,7 +188,7 @@ void StepExporter::WriteFile() { aiFace* face = &(mesh->mFaces[j]); - if (face->mNumIndices == 3) countFace++; + if (face->mNumIndices >= 3) faceEntryLen.push_back(15 + 5 * face->mNumIndices); } for (unsigned int j=0; jmNumVertices; ++j) { @@ -218,10 +217,13 @@ void StepExporter::WriteFile() // write the top of data mOutput << "DATA" << endstr; mOutput << "#1=MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION(' ',("; - for (int i=0; imFaces[j]); - if (face->mNumIndices != 3) continue; + const int numIndices = face->mNumIndices; + if (numIndices < 3) continue; - aiVector3D* v1 = &(mesh->mVertices[face->mIndices[0]]); - aiVector3D* v2 = &(mesh->mVertices[face->mIndices[1]]); - aiVector3D* v3 = &(mesh->mVertices[face->mIndices[2]]); - aiVector3D dv12 = *v2 - *v1; - aiVector3D dv23 = *v3 - *v2; - aiVector3D dv31 = *v1 - *v3; - aiVector3D dv13 = *v3 - *v1; - dv12.Normalize(); - dv23.Normalize(); - dv31.Normalize(); - dv13.Normalize(); + std::vector pidArray(numIndices, -1); // vertex id + std::vector dvArray(numIndices); // edge dir + for (int k = 0; k < numIndices; ++k) + { + aiVector3D *v1 = &(mesh->mVertices[face->mIndices[k]]); + pidArray[k] = uniqueVerts.find(v1)->second; - aiVector3D dvY = dv12; - aiVector3D dvX = dvY ^ dv13; + aiVector3D *v2 = nullptr; + if (k + 1 == numIndices) + v2 = &(mesh->mVertices[face->mIndices[0]]); + else + v2 = &(mesh->mVertices[face->mIndices[k + 1]]); + dvArray[k] = *v2 - *v1; + dvArray[k].Normalize(); + } + + aiVector3D dvY = dvArray[1]; + aiVector3D dvX = dvY ^ dvArray[0]; dvX.Normalize(); - int pid1 = uniqueVerts.find(v1)->second; - int pid2 = uniqueVerts.find(v2)->second; - int pid3 = uniqueVerts.find(v3)->second; - // mean vertex color for the face if available if (mesh->HasVertexColors(0)) { @@ -339,35 +344,62 @@ void StepExporter::WriteFile() /* 2 directions of the plane */ mOutput << "#" << sid+9 << "=PLANE('',#" << sid+10 << ")" << endstr; - mOutput << "#" << sid+10 << "=AXIS2_PLACEMENT_3D('',#" << pid1 << ", #" << sid+11 << ",#" << sid+12 << ")" << endstr; + mOutput << "#" << sid+10 << "=AXIS2_PLACEMENT_3D('',#" << pidArray[0] << ",#" << sid+11 << ",#" << sid+12 << ")" << endstr; mOutput << "#" << sid + 11 << "=DIRECTION('',(" << dvX.x << "," << dvX.y << "," << dvX.z << "))" << endstr; mOutput << "#" << sid + 12 << "=DIRECTION('',(" << dvY.x << "," << dvY.y << "," << dvY.z << "))" << endstr; mOutput << "#" << sid+13 << "=FACE_BOUND('',#" << sid+14 << ",.T.)" << endstr; - mOutput << "#" << sid+14 << "=EDGE_LOOP('',(#" << sid+15 << ",#" << sid+16 << ",#" << sid+17 << "))" << endstr; + mOutput << "#" << sid+14 << "=EDGE_LOOP('',("; + int edgeLoopStart = sid + 15; + for (int k = 0; k < numIndices; ++k) + { + if (k == 0) + mOutput << "#"; + else + mOutput << ",#"; + mOutput << edgeLoopStart + k; + } + mOutput << "))" << endstr; /* edge loop */ - mOutput << "#" << sid+15 << "=ORIENTED_EDGE('',*,*,#" << sid+18 << ",.T.)" << endstr; - mOutput << "#" << sid+16 << "=ORIENTED_EDGE('',*,*,#" << sid+19 << ",.T.)" << endstr; - mOutput << "#" << sid+17 << "=ORIENTED_EDGE('',*,*,#" << sid+20 << ",.T.)" << endstr; + int orientedEdgesStart = edgeLoopStart + numIndices; + for (int k=0; k < numIndices; k++) + { + mOutput << "#" << edgeLoopStart+k << "=ORIENTED_EDGE('',*,*,#" << orientedEdgesStart + k << ",.T.)" << endstr; + } /* oriented edges */ - mOutput << "#" << sid+18 << "=EDGE_CURVE('',#" << pid1+1 << ",#" << pid2+1 << ",#" << sid+21 << ",.F.)" << endstr; - mOutput << "#" << sid+19 << "=EDGE_CURVE('',#" << pid2+1 << ",#" << pid3+1 << ",#" << sid+22 << ",.T.)" << endstr; - mOutput << "#" << sid+20 << "=EDGE_CURVE('',#" << pid3+1 << ",#" << pid1+1 << ",#" << sid+23 << ",.T.)" << endstr; + int lineStart = orientedEdgesStart + numIndices; + for (int k=0; k < numIndices; ++k) + { + if (k == 0) + mOutput << "#" << orientedEdgesStart+k << "=EDGE_CURVE('',#" << pidArray[k]+1 << ",#" << pidArray[k+1]+1 << ",#" << lineStart+k << ",.F.)" << endstr; + else if (k+1 == numIndices) + mOutput << "#" << orientedEdgesStart+k << "=EDGE_CURVE('',#" << pidArray[k]+1 << ",#" << pidArray[0]+1 << ",#" << lineStart+k << ",.T.)" << endstr; + else + mOutput << "#" << orientedEdgesStart+k << "=EDGE_CURVE('',#" << pidArray[k]+1 << ",#" << pidArray[k+1]+1 << ",#" << lineStart+k << ",.T.)" << endstr; + } - /* 3 lines and 3 vectors for the lines for the 3 edge curves */ - mOutput << "#" << sid+21 << "=LINE('',#" << pid1 << ",#" << sid+24 << ")" << endstr; - mOutput << "#" << sid+22 << "=LINE('',#" << pid2 << ",#" << sid+25 << ")" << endstr; - mOutput << "#" << sid+23 << "=LINE('',#" << pid3 << ",#" << sid+26 << ")" << endstr; - mOutput << "#" << sid+24 << "=VECTOR('',#" << sid+27 << ",1.0)" << endstr; - mOutput << "#" << sid+25 << "=VECTOR('',#" << sid+28 << ",1.0)" << endstr; - mOutput << "#" << sid+26 << "=VECTOR('',#" << sid+29 << ",1.0)" << endstr; - mOutput << "#" << sid+27 << "=DIRECTION('',(" << dv12.x << "," << dv12.y << "," << dv12.z << "))" << endstr; - mOutput << "#" << sid+28 << "=DIRECTION('',(" << dv23.x << "," << dv23.y << "," << dv23.z << "))" << endstr; - mOutput << "#" << sid+29 << "=DIRECTION('',(" << dv31.x << "," << dv31.y << "," << dv31.z << "))" << endstr; - ind += faceEntryLen; // increase counter + /* n lines and n vectors for the lines for the n edge curves */ + int vectorStart = lineStart + numIndices; + for (int k=0; k < numIndices; ++k) + { + mOutput << "#" << lineStart+k << "=LINE('',#" << pidArray[k] << ",#" << vectorStart+k << ")" << endstr; + } + + int directionStart = vectorStart + numIndices; + for (int k=0; k < numIndices; ++k) + { + mOutput << "#" << vectorStart+k << "=VECTOR('',#" << directionStart+k << ",1.0)" << endstr; + } + + for (int k=0; k < numIndices; ++k) + { + const aiVector3D &dv = dvArray[k]; + mOutput << "#" << directionStart + k << "=DIRECTION('',(" << dv.x << "," << dv.y << "," << dv.z << "))" << endstr; + } + ind += 15 + 5*numIndices; // increase counter } } From f87550fdbcb567a08a06f28d10310da35ee5f2d4 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 28 Jul 2021 10:39:39 +0200 Subject: [PATCH 213/335] Fix Issue3760 - Convert left-handed coordinate system to right-handed coordinate system - Rescale model by 0.01 - closes https://github.com/assimp/assimp/issues/3760 --- code/AssetLib/C4D/C4DImporter.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/code/AssetLib/C4D/C4DImporter.cpp b/code/AssetLib/C4D/C4DImporter.cpp index 434d1429e..5408daa68 100644 --- a/code/AssetLib/C4D/C4DImporter.cpp +++ b/code/AssetLib/C4D/C4DImporter.cpp @@ -146,8 +146,14 @@ void C4DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS ThrowException("failed to read document " + pFile); } + // Generate the root-node pScene->mRootNode = new aiNode(""); - + + // convert left-handed to right-handed + pScene->mRootNode->mTransformation.a1 = 0.01f; + pScene->mRootNode->mTransformation.b2 = 0.01f; + pScene->mRootNode->mTransformation.c3 = -0.01f; + // first convert all materials ReadMaterials(doc->GetFirstMaterial()); From 738c31c3eabb2b35af5d6eeb69b368f1128971e2 Mon Sep 17 00:00:00 2001 From: Krishty Date: Wed, 28 Jul 2021 16:44:46 +0200 Subject: [PATCH 214/335] removed useless code Found while reviewing #3880 --- code/AssetLib/Collada/ColladaParser.cpp | 4 ---- code/AssetLib/X/XFileImporter.cpp | 4 +--- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/code/AssetLib/Collada/ColladaParser.cpp b/code/AssetLib/Collada/ColladaParser.cpp index 37c7274f4..7aa37a112 100644 --- a/code/AssetLib/Collada/ColladaParser.cpp +++ b/code/AssetLib/Collada/ColladaParser.cpp @@ -231,11 +231,7 @@ void ColladaParser::UriDecodePath(aiString &ss) { // Maxon Cinema Collada Export writes "file:///C:\andsoon" with three slashes... // I need to filter it without destroying linux paths starting with "/somewhere" -#if defined(_MSC_VER) if (ss.data[0] == '/' && isalpha((unsigned char)ss.data[1]) && ss.data[2] == ':') { -#else - if (ss.data[0] == '/' && isalpha((unsigned char)ss.data[1]) && ss.data[2] == ':') { -#endif --ss.length; ::memmove(ss.data, ss.data + 1, ss.length); ss.data[ss.length] = 0; diff --git a/code/AssetLib/X/XFileImporter.cpp b/code/AssetLib/X/XFileImporter.cpp index df1aba331..4c8c54551 100644 --- a/code/AssetLib/X/XFileImporter.cpp +++ b/code/AssetLib/X/XFileImporter.cpp @@ -667,9 +667,7 @@ void XFileImporter::ConvertMaterials( aiScene* pScene, std::vector Date: Thu, 29 Jul 2021 13:28:51 +0200 Subject: [PATCH 215/335] removed trailing spaces and tabs from source and text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit ignores the “contrib” folder in order to prevent merge conflicts in dependencies, should these be updated via git. --- code/AssetLib/3DS/3DSExporter.cpp | 2 +- code/AssetLib/3MF/D3MFImporter.cpp | 2 +- code/AssetLib/AMF/AMFImporter.cpp | 2 +- code/AssetLib/AMF/AMFImporter_Geometry.cpp | 6 +- code/AssetLib/AMF/AMFImporter_Postprocess.cpp | 6 +- code/AssetLib/Assjson/mesh_splitter.cpp | 8 +- code/AssetLib/Assjson/mesh_splitter.h | 6 +- code/AssetLib/Assxml/AssxmlExporter.cpp | 2 +- code/AssetLib/Blender/BlenderModifier.h | 18 +- code/AssetLib/C4D/C4DImporter.cpp | 6 +- code/AssetLib/COB/COBLoader.cpp | 2 +- code/AssetLib/COB/COBLoader.h | 2 +- code/AssetLib/Collada/ColladaLoader.cpp | 2 +- code/AssetLib/DXF/DXFLoader.cpp | 2 +- code/AssetLib/DXF/DXFLoader.h | 2 +- code/AssetLib/FBX/FBXConverter.cpp | 4 +- code/AssetLib/FBX/FBXConverter.h | 4 +- code/AssetLib/FBX/FBXDocument.cpp | 2 +- code/AssetLib/FBX/FBXExportNode.h | 2 +- code/AssetLib/FBX/FBXExporter.cpp | 12 +- code/AssetLib/FBX/FBXMaterial.cpp | 8 +- code/AssetLib/FBX/FBXMeshGeometry.cpp | 2 +- code/AssetLib/FBX/FBXMeshGeometry.h | 8 +- code/AssetLib/FBX/FBXProperties.h | 6 +- code/AssetLib/FBX/FBXUtil.cpp | 2 +- code/AssetLib/HMP/HMPLoader.cpp | 4 +- code/AssetLib/IFC/IFCCurve.cpp | 2 +- code/AssetLib/IFC/IFCOpenings.cpp | 8 +- code/AssetLib/IFC/IFCReaderGen1_2x3.cpp | 232 +++++------ code/AssetLib/IFC/IFCReaderGen2_2x3.cpp | 162 ++++---- code/AssetLib/IFC/IFCReaderGen_4.cpp | 392 +++++++++--------- code/AssetLib/IFC/IFCReaderGen_4.h | 26 +- code/AssetLib/Irr/IRRLoader.h | 2 +- code/AssetLib/LWO/LWOAnimation.h | 2 +- code/AssetLib/LWS/LWSLoader.cpp | 4 +- code/AssetLib/M3D/M3DWrapper.h | 12 +- code/AssetLib/M3D/m3d.h | 6 +- code/AssetLib/MD5/MD5Loader.cpp | 2 +- code/AssetLib/MDC/MDCFileData.h | 4 +- code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp | 4 +- code/AssetLib/MMD/MMDPmxParser.cpp | 2 +- code/AssetLib/OFF/OFFLoader.cpp | 10 +- code/AssetLib/Obj/ObjExporter.h | 18 +- code/AssetLib/Obj/ObjFileMtlImporter.cpp | 2 +- code/AssetLib/SMD/SMDLoader.cpp | 2 +- code/AssetLib/STL/STLExporter.cpp | 8 +- code/AssetLib/STL/STLLoader.cpp | 4 +- code/AssetLib/X/XFileExporter.cpp | 2 +- code/AssetLib/X/XFileExporter.h | 6 +- code/AssetLib/X3D/X3DImporter.cpp | 2 +- code/AssetLib/XGL/XGLLoader.cpp | 2 +- code/AssetLib/glTF2/glTF2Asset.h | 4 +- code/AssetLib/glTF2/glTF2Asset.inl | 32 +- code/AssetLib/glTF2/glTF2AssetWriter.inl | 16 +- code/Common/DefaultIOStream.cpp | 6 +- code/Common/DefaultIOSystem.cpp | 6 +- code/Common/Exporter.cpp | 4 +- code/Common/FileSystemFilter.h | 4 +- code/Common/Importer.cpp | 80 ++-- code/Common/Importer.h | 6 +- code/Common/ImporterRegistry.cpp | 4 +- code/Common/Win32DebugLogStream.h | 8 +- code/PostProcessing/ArmaturePopulate.h | 2 +- .../PostProcessing/DropFaceNormalsProcess.cpp | 2 +- code/PostProcessing/EmbedTexturesProcess.cpp | 2 +- code/PostProcessing/FindDegenerates.cpp | 2 +- code/PostProcessing/FindInstancesProcess.cpp | 2 +- code/PostProcessing/MakeVerboseFormat.h | 2 +- .../RemoveRedundantMaterials.cpp | 2 +- code/PostProcessing/ScaleProcess.cpp | 38 +- code/PostProcessing/ScaleProcess.h | 4 +- .../SplitByBoneCountProcess.cpp | 12 +- code/PostProcessing/SplitLargeMeshes.cpp | 4 +- code/PostProcessing/TextureTransform.cpp | 2 +- code/PostProcessing/TriangulateProcess.cpp | 12 +- doc/dox.h | 18 +- doc/dox_cmd.h | 38 +- fuzz/assimp_fuzzer.cc | 2 +- include/assimp/BaseImporter.h | 2 +- include/assimp/Compiler/poppack1.h | 4 +- include/assimp/Compiler/pushpack1.h | 4 +- include/assimp/Exceptional.h | 2 +- include/assimp/Exporter.hpp | 2 +- include/assimp/IOStreamBuffer.h | 6 +- include/assimp/IOSystem.hpp | 8 +- include/assimp/Logger.hpp | 2 +- include/assimp/MemoryIOWrapper.h | 6 +- include/assimp/SmallVector.h | 8 +- include/assimp/SmoothingGroups.inl | 22 +- include/assimp/XmlParser.h | 10 +- include/assimp/ai_assert.h | 2 +- include/assimp/anim.h | 6 +- include/assimp/cimport.h | 12 +- include/assimp/defs.h | 2 +- include/assimp/light.h | 2 +- include/assimp/material.h | 8 +- include/assimp/matrix4x4.h | 2 +- include/assimp/matrix4x4.inl | 2 +- include/assimp/mesh.h | 10 +- include/assimp/postprocess.h | 14 +- include/assimp/scene.h | 26 +- include/assimp/vector2.inl | 14 +- .../windows-innosetup/readme_installer.txt | 2 +- .../readme_installer_vieweronly.txt | 2 +- packaging/windows-mkzip/bin_readme.txt | 2 +- port/AndroidJNI/CMakeLists.txt | 4 +- port/AssimpDelphi/Readme.txt | 2 +- port/jassimp/jassimp-native/src/jassimp.cpp | 164 ++++---- .../ModelLoaderHelperClasses.h | 26 +- samples/SimpleAssimpViewX/MyDocument.h | 18 +- samples/SimpleOpenGL/Sample_SimpleOpenGL.c | 4 +- .../SimpleTexturedDirectx11/CMakeLists.txt | 6 +- .../SimpleTexturedDirectx11/ModelLoader.cpp | 2 +- .../SimpleTexturedDirectx11/TextureLoader.cpp | 52 +-- .../SimpleTexturedDirectx11/main.cpp | 10 +- .../src/model_loading.cpp | 4 +- test/models-nonbsd/3D/mar_rifle.source.txt | 6 +- test/models-nonbsd/3DS/cart_wheel.source.txt | 6 +- test/models-nonbsd/3DS/mar_rifle.source.txt | 6 +- test/models-nonbsd/3DS/mp5_sil.source.txt | 6 +- test/models-nonbsd/ASE/Rifle.source.txt | 6 +- test/models-nonbsd/ASE/Rifle2.source.txt | 6 +- .../BLEND/fleurOptonl.source.txt | 16 +- test/models-nonbsd/DXF/rifle.source.txt | 6 +- .../FBX/2013_ASCII/cart_wheel.source.txt | 6 +- .../kwxport_test_vcolors.fbx.source.txt | 6 +- .../FBX/2013_ASCII/mar_rifle.source.txt | 6 +- .../FBX/2013_ASCII/mp5_sil.source.txt | 6 +- .../FBX/2013_BINARY/cart_wheel.source.txt | 6 +- .../kwxport_test_vcolors.fbx.source.txt | 6 +- .../FBX/2013_BINARY/mar_rifle.source.txt | 6 +- .../FBX/2013_BINARY/mp5_sil.source.txt | 6 +- .../LWO2/LWSReferences/QuickDraw.source.txt | 10 +- test/models-nonbsd/LWO/LWO2/rifle.source.txt | 6 +- test/models-nonbsd/MD2/source.txt | 6 +- test/models-nonbsd/MD5/BoarMan.source.txt | 4 +- .../MDL/IDPO (Quake1)/gijoe-readme.txt | 14 +- test/models-nonbsd/MDL/IDPO (Quake1)/steg.txt | 8 +- .../MDL/IDPO (Quake1)/tekmechbot.txt | 6 +- test/models-nonbsd/NFF/NFFSense8/credits.txt | 2 +- test/models-nonbsd/OBJ/rifle.source.txt | 6 +- test/models/3DS/UVTransformTest/note.txt | 4 +- test/models/ASE/MotionCaptureROM.source.txt | 2 +- .../kwxport_test_vcolors.dae.source.txt | 6 +- .../IRR/warn_dwarf_scaling_is_intended.txt | 2 +- test/models/MD2/faerie-source.txt | 2 +- test/models/MD2/sidney-source.txt | 2 +- test/models/Q3D/E-AT-AT.source.txt | 2 +- test/models/Q3D/earth.source.txt | 2 +- test/models/WRL/credits.txt | 2 +- test/models/X/anim_test.txt | 2 +- .../X/kwxport_test_cubewithvcolors.source.txt | 6 +- test/models/X/test.txt | 2 +- test/models/invalid/readme.txt | 8 +- test/regression/README.txt | 6 +- test/unit/AbstractImportExportBase.h | 2 +- test/unit/Common/utStandardShapes.cpp | 2 +- .../MDL/utMDLImporter_HL1_Nodes.cpp | 4 +- test/unit/RandomNumberGeneration.h | 4 +- test/unit/SceneDiffer.cpp | 2 +- test/unit/SceneDiffer.h | 2 +- test/unit/TestIOSystem.h | 2 +- test/unit/utDefaultIOStream.cpp | 2 +- test/unit/utFBXImporterExporter.cpp | 4 +- test/unit/utFindDegenerates.cpp | 4 +- test/unit/utIOStreamBuffer.cpp | 6 +- test/unit/utIOSystem.cpp | 10 +- test/unit/utIssues.cpp | 2 +- test/unit/utVersion.cpp | 2 +- test/unit/utglTF2ImportExport.cpp | 2 +- tools/assimp_cmd/CMakeLists.txt | 2 +- tools/assimp_cmd/Export.cpp | 18 +- tools/assimp_cmd/ImageExtractor.cpp | 20 +- tools/assimp_cmd/Info.cpp | 2 +- tools/assimp_cmd/Main.cpp | 62 +-- tools/assimp_cmd/Main.h | 40 +- tools/assimp_cmd/WriteDump.cpp | 18 +- tools/assimp_cmd/resource.h | 2 +- tools/assimp_view/AnimEvaluator.h | 12 +- tools/assimp_view/CMakeLists.txt | 2 +- tools/assimp_view/Display.cpp | 6 +- tools/assimp_view/MessageProc.cpp | 2 +- tools/assimp_view/Shaders.cpp | 18 +- tools/assimp_view/assimp_view.cpp | 18 +- tools/assimp_view/resource.h | 2 +- 185 files changed, 1158 insertions(+), 1158 deletions(-) diff --git a/code/AssetLib/3DS/3DSExporter.cpp b/code/AssetLib/3DS/3DSExporter.cpp index 92a6d5aa7..0beecd563 100644 --- a/code/AssetLib/3DS/3DSExporter.cpp +++ b/code/AssetLib/3DS/3DSExporter.cpp @@ -291,7 +291,7 @@ void Discreet3DSExporter::WriteMaterials() { ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_SPECULAR); WriteColor(color); } - + if (mat.Get(AI_MATKEY_COLOR_AMBIENT, color) == AI_SUCCESS) { ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_AMBIENT); WriteColor(color); diff --git a/code/AssetLib/3MF/D3MFImporter.cpp b/code/AssetLib/3MF/D3MFImporter.cpp index 747af7cfc..a56b82d63 100644 --- a/code/AssetLib/3MF/D3MFImporter.cpp +++ b/code/AssetLib/3MF/D3MFImporter.cpp @@ -274,7 +274,7 @@ private: if (ret) { value = std::atoi(strValue.c_str()); return true; - } + } return false; } diff --git a/code/AssetLib/AMF/AMFImporter.cpp b/code/AssetLib/AMF/AMFImporter.cpp index 615882b6a..88a38b827 100644 --- a/code/AssetLib/AMF/AMFImporter.cpp +++ b/code/AssetLib/AMF/AMFImporter.cpp @@ -303,7 +303,7 @@ void AMFImporter::ParseNode_Root() { } XmlNode node = *root; mUnit = ai_tolower(std::string(node.attribute("unit").as_string())); - + mVersion = node.attribute("version").as_string(); // Read attributes for node . diff --git a/code/AssetLib/AMF/AMFImporter_Geometry.cpp b/code/AssetLib/AMF/AMFImporter_Geometry.cpp index 1fd2c49a4..1d2a1f5b4 100644 --- a/code/AssetLib/AMF/AMFImporter_Geometry.cpp +++ b/code/AssetLib/AMF/AMFImporter_Geometry.cpp @@ -75,7 +75,7 @@ void AMFImporter::ParseNode_Mesh(XmlNode &node) { found_volumes = true; } ParseHelper_Node_Exit(); - } + } if (!found_verts && !found_volumes) { mNodeElement_Cur->Child.push_back(ne); @@ -199,9 +199,9 @@ void AMFImporter::ParseNode_Volume(XmlNode &node) { // Read attributes for node . // and assign read data - + ((AMFVolume *)ne)->MaterialID = node.attribute("materialid").as_string(); - + ((AMFVolume *)ne)->Type = type; // Check for child nodes bool col_read = false; diff --git a/code/AssetLib/AMF/AMFImporter_Postprocess.cpp b/code/AssetLib/AMF/AMFImporter_Postprocess.cpp index 43d0de52f..d56d6681d 100644 --- a/code/AssetLib/AMF/AMFImporter_Postprocess.cpp +++ b/code/AssetLib/AMF/AMFImporter_Postprocess.cpp @@ -69,7 +69,7 @@ aiColor4D AMFImporter::SPP_Material::GetColor(const float /*pX*/, const float /* } tcol = Color->Color; - + // Check if default color must be used if ((tcol.r == 0) && (tcol.g == 0) && (tcol.b == 0) && (tcol.a == 0)) { tcol.r = 0.5f; @@ -99,10 +99,10 @@ void AMFImporter::PostprocessHelper_CreateMeshDataArray(const AMFMesh &nodeEleme } // all coordinates stored as child and we need to reserve space for future push_back's. - vertexCoordinateArray.reserve(vn->Child.size()); + vertexCoordinateArray.reserve(vn->Child.size()); // colors count equal vertices count. - pVertexColorArray.resize(vn->Child.size()); + pVertexColorArray.resize(vn->Child.size()); col_idx = 0; // Inside vertices collect all data and place to arrays diff --git a/code/AssetLib/Assjson/mesh_splitter.cpp b/code/AssetLib/Assjson/mesh_splitter.cpp index 24385f9a0..9301cc27e 100644 --- a/code/AssetLib/Assjson/mesh_splitter.cpp +++ b/code/AssetLib/Assjson/mesh_splitter.cpp @@ -110,7 +110,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vectormNumVertices / LIMIT) + 1; - // create a std::vector to remember which vertices have already + // create a std::vector to remember which vertices have already // been copied and to which position (i.e. output index) std::vector was_copied_to; was_copied_to.resize(in_mesh->mNumVertices,WAS_NOT_COPIED); @@ -125,7 +125,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vectormNumVertices = 0; out_mesh->mMaterialIndex = in_mesh->mMaterialIndex; @@ -179,7 +179,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vectormNumVertices + iNeed > out_vertex_index) { @@ -240,7 +240,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vectormTextureCoords[c][out_mesh->mNumVertices] = in_mesh->mTextureCoords[c][index]; } } - // vertex colors + // vertex colors for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c) { if (in_mesh->HasVertexColors( c)) { out_mesh->mColors[c][out_mesh->mNumVertices] = in_mesh->mColors[c][index]; diff --git a/code/AssetLib/Assjson/mesh_splitter.h b/code/AssetLib/Assjson/mesh_splitter.h index 326f73b41..3bb26118a 100644 --- a/code/AssetLib/Assjson/mesh_splitter.h +++ b/code/AssetLib/Assjson/mesh_splitter.h @@ -22,13 +22,13 @@ struct aiNode; // --------------------------------------------------------------------------- /** Splits meshes of unique vertices into meshes with no more vertices than - * a given, configurable threshold value. + * a given, configurable threshold value. */ -class MeshSplitter +class MeshSplitter { public: - + void SetLimit(unsigned int l) { LIMIT = l; } diff --git a/code/AssetLib/Assxml/AssxmlExporter.cpp b/code/AssetLib/Assxml/AssxmlExporter.cpp index 847ba0d7e..d244813b1 100644 --- a/code/AssetLib/Assxml/AssxmlExporter.cpp +++ b/code/AssetLib/Assxml/AssxmlExporter.cpp @@ -50,7 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -namespace Assimp { +namespace Assimp { void ExportSceneAssxml(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) { diff --git a/code/AssetLib/Blender/BlenderModifier.h b/code/AssetLib/Blender/BlenderModifier.h index daf120087..d2fea43c1 100644 --- a/code/AssetLib/Blender/BlenderModifier.h +++ b/code/AssetLib/Blender/BlenderModifier.h @@ -52,9 +52,9 @@ namespace Assimp { namespace Blender { // ------------------------------------------------------------------------------------------- -/** +/** * Dummy base class for all blender modifiers. Modifiers are reused between imports, so - * they should be stateless and not try to cache model data. + * they should be stateless and not try to cache model data. */ // ------------------------------------------------------------------------------------------- class BlenderModifier { @@ -67,7 +67,7 @@ public: } // -------------------- - /** + /** * Check if *this* modifier is active, given a ModifierData& block. */ virtual bool IsActive( const ModifierData& /*modin*/) { @@ -75,10 +75,10 @@ public: } // -------------------- - /** + /** * Apply the modifier to a given output node. The original data used * to construct the node is given as well. Not called unless IsActive() - * was called and gave positive response. + * was called and gave positive response. */ virtual void DoIt(aiNode& /*out*/, ConversionData& /*conv_data*/, @@ -92,8 +92,8 @@ public: }; // ------------------------------------------------------------------------------------------- -/** - * Manage all known modifiers and instance and apply them if necessary +/** + * Manage all known modifiers and instance and apply them if necessary */ // ------------------------------------------------------------------------------------------- class BlenderModifierShowcase { @@ -113,8 +113,8 @@ private: // MODIFIERS ///////////////////////////////////////////////////////////////////////////////// // ------------------------------------------------------------------------------------------- -/** - * Mirror modifier. Status: implemented. +/** + * Mirror modifier. Status: implemented. */ // ------------------------------------------------------------------------------------------- class BlenderModifier_Mirror : public BlenderModifier { diff --git a/code/AssetLib/C4D/C4DImporter.cpp b/code/AssetLib/C4D/C4DImporter.cpp index 5408daa68..14a94958b 100644 --- a/code/AssetLib/C4D/C4DImporter.cpp +++ b/code/AssetLib/C4D/C4DImporter.cpp @@ -148,12 +148,12 @@ void C4DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS // Generate the root-node pScene->mRootNode = new aiNode(""); - + // convert left-handed to right-handed pScene->mRootNode->mTransformation.a1 = 0.01f; pScene->mRootNode->mTransformation.b2 = 0.01f; - pScene->mRootNode->mTransformation.c3 = -0.01f; - + pScene->mRootNode->mTransformation.c3 = -0.01f; + // first convert all materials ReadMaterials(doc->GetFirstMaterial()); diff --git a/code/AssetLib/COB/COBLoader.cpp b/code/AssetLib/COB/COBLoader.cpp index 822bce16d..3bef260e5 100644 --- a/code/AssetLib/COB/COBLoader.cpp +++ b/code/AssetLib/COB/COBLoader.cpp @@ -884,7 +884,7 @@ void COBImporter::ReadBinaryFile(Scene &out, StreamReaderLE *reader) { std::string type; type += reader->GetI1(); type += reader->GetI1(); - type += reader->GetI1(); + type += reader->GetI1(); type += reader->GetI1(); ChunkInfo nfo; diff --git a/code/AssetLib/COB/COBLoader.h b/code/AssetLib/COB/COBLoader.h index 2317d094e..e4d41e500 100644 --- a/code/AssetLib/COB/COBLoader.h +++ b/code/AssetLib/COB/COBLoader.h @@ -77,7 +77,7 @@ class COBImporter : public BaseImporter public: COBImporter(); ~COBImporter(); - + // -------------------- bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const; diff --git a/code/AssetLib/Collada/ColladaLoader.cpp b/code/AssetLib/Collada/ColladaLoader.cpp index ee8f97203..ccb2bb336 100644 --- a/code/AssetLib/Collada/ColladaLoader.cpp +++ b/code/AssetLib/Collada/ColladaLoader.cpp @@ -1675,7 +1675,7 @@ void ColladaLoader::BuildMaterials(ColladaParser &pParser, aiScene * /*pScene*/) const Material &material = matIt->second; // a material is only a reference to an effect ColladaParser::EffectLibrary::iterator effIt = pParser.mEffectLibrary.find(material.mEffect); - if (effIt == pParser.mEffectLibrary.end()) + if (effIt == pParser.mEffectLibrary.end()) continue; Effect &effect = effIt->second; diff --git a/code/AssetLib/DXF/DXFLoader.cpp b/code/AssetLib/DXF/DXFLoader.cpp index 49d572b0b..2e38ed976 100644 --- a/code/AssetLib/DXF/DXFLoader.cpp +++ b/code/AssetLib/DXF/DXFLoader.cpp @@ -547,7 +547,7 @@ void DXFImporter::ParseEntities(DXF::LineReader& reader, DXF::FileData& output) ++reader; } - ASSIMP_LOG_VERBOSE_DEBUG( "DXF: got ", block.lines.size()," polylines and ", block.insertions.size(), + ASSIMP_LOG_VERBOSE_DEBUG( "DXF: got ", block.lines.size()," polylines and ", block.insertions.size(), " inserted blocks in ENTITIES" ); } diff --git a/code/AssetLib/DXF/DXFLoader.h b/code/AssetLib/DXF/DXFLoader.h index 5319d2528..6649deb34 100644 --- a/code/AssetLib/DXF/DXFLoader.h +++ b/code/AssetLib/DXF/DXFLoader.h @@ -63,7 +63,7 @@ namespace DXF { } // --------------------------------------------------------------------------- -/** +/** * @brief DXF importer implementation. */ class DXFImporter : public BaseImporter { diff --git a/code/AssetLib/FBX/FBXConverter.cpp b/code/AssetLib/FBX/FBXConverter.cpp index f489e37a4..e0da78583 100644 --- a/code/AssetLib/FBX/FBXConverter.cpp +++ b/code/AssetLib/FBX/FBXConverter.cpp @@ -862,7 +862,7 @@ bool FBXConverter::GenerateTransformationNodeChain(const Model &model, const std output_nodes.push_back(std::move(nd)); return false; } - + void FBXConverter::SetupNodeMetadata(const Model &model, aiNode &nd) { const PropertyTable &props = model.Props(); DirectPropertyMap unparsedProperties = props.GetUnparsedProperties(); @@ -3572,7 +3572,7 @@ void FBXConverter::ConvertOrphanedEmbeddedTextures() { if (texture->Media() && texture->Media()->ContentLength() > 0) { realTexture = texture; } - } + } } } catch (...) { // do nothing diff --git a/code/AssetLib/FBX/FBXConverter.h b/code/AssetLib/FBX/FBXConverter.h index d208ab429..b9a494695 100644 --- a/code/AssetLib/FBX/FBXConverter.h +++ b/code/AssetLib/FBX/FBXConverter.h @@ -76,7 +76,7 @@ namespace Assimp { namespace FBX { class Document; -/** +/** * Convert a FBX #Document to #aiScene * @param out Empty scene to be populated * @param doc Parsed FBX document @@ -182,7 +182,7 @@ private: // ------------------------------------------------------------------------------------------------ void ConvertModel(const Model &model, aiNode *parent, aiNode *root_node, const aiMatrix4x4 &absolute_transform); - + // ------------------------------------------------------------------------------------------------ // MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed std::vector diff --git a/code/AssetLib/FBX/FBXDocument.cpp b/code/AssetLib/FBX/FBXDocument.cpp index 0c4435348..8e0439e18 100644 --- a/code/AssetLib/FBX/FBXDocument.cpp +++ b/code/AssetLib/FBX/FBXDocument.cpp @@ -635,7 +635,7 @@ std::vector Document::GetConnectionsBySourceSequenced(uint64_ } // ------------------------------------------------------------------------------------------------ -std::vector Document::GetConnectionsBySourceSequenced(uint64_t source, +std::vector Document::GetConnectionsBySourceSequenced(uint64_t source, const char* const* classnames, size_t count) const { return GetConnectionsSequenced(source, true, ConnectionsBySource(),classnames, count); diff --git a/code/AssetLib/FBX/FBXExportNode.h b/code/AssetLib/FBX/FBXExportNode.h index 6ef27972d..3aca98939 100644 --- a/code/AssetLib/FBX/FBXExportNode.h +++ b/code/AssetLib/FBX/FBXExportNode.h @@ -60,7 +60,7 @@ namespace FBX { } class FBX::Node { -public: +public: // TODO: accessors std::string name; // node name std::vector properties; // node properties diff --git a/code/AssetLib/FBX/FBXExporter.cpp b/code/AssetLib/FBX/FBXExporter.cpp index c19c593dd..84a77e18d 100644 --- a/code/AssetLib/FBX/FBXExporter.cpp +++ b/code/AssetLib/FBX/FBXExporter.cpp @@ -498,7 +498,7 @@ void FBXExporter::WriteDocuments () if (!binary) { WriteAsciiSectionHeader("Documents Description"); } - + // not sure what the use of multiple documents would be, // or whether any end-application supports it FBX::Node docs("Documents"); @@ -1258,7 +1258,7 @@ void FBXExporter::WriteObjects () indent = 2; vertexcolors.End(outstream, binary, indent, true); } - + // uvs, if any for (size_t uvi = 0; uvi < m->GetNumUVChannels(); ++uvi) { if (m->mNumUVComponents[uvi] > 2) { @@ -1751,7 +1751,7 @@ void FBXExporter::WriteObjects () bsnode.AddProperty(blendshape_uid); bsnode.AddProperty(blendshape_name + FBX::SEPARATOR + "Blendshape"); bsnode.AddProperty("Shape"); - bsnode.AddChild("Version", int32_t(100)); + bsnode.AddChild("Version", int32_t(100)); bsnode.Begin(outstream, binary, indent); bsnode.DumpProperties(outstream, binary, indent); bsnode.EndProperties(outstream, binary, indent); @@ -1877,7 +1877,7 @@ void FBXExporter::WriteObjects () // at the same time we can build a list of all the skeleton nodes, // which will be used later to mark them as type "limbNode". std::unordered_set limbnodes; - + //actual bone nodes in fbx, without parenting-up std::unordered_set setAllBoneNamesInScene; for(unsigned int m = 0; m < mScene->mNumMeshes; ++ m) @@ -1887,7 +1887,7 @@ void FBXExporter::WriteObjects () setAllBoneNamesInScene.insert(pMesh->mBones[b]->mName.data); } aiMatrix4x4 mxTransIdentity; - + // and a map of nodes by bone name, as finding them is annoying. std::map node_by_bone; for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { @@ -1956,7 +1956,7 @@ void FBXExporter::WriteObjects () } if (end) { break; } } - + // if it was the skeleton root we can finish here if (end) { break; } } diff --git a/code/AssetLib/FBX/FBXMaterial.cpp b/code/AssetLib/FBX/FBXMaterial.cpp index aaa043c12..7eb047177 100644 --- a/code/AssetLib/FBX/FBXMaterial.cpp +++ b/code/AssetLib/FBX/FBXMaterial.cpp @@ -142,8 +142,8 @@ Material::~Material() { // ------------------------------------------------------------------------------------------------ Texture::Texture(uint64_t id, const Element& element, const Document& doc, const std::string& name) : - Object(id,element,name), - uvScaling(1.0f,1.0f), + Object(id,element,name), + uvScaling(1.0f,1.0f), media(0) { const Scope& sc = GetRequiredScope(element); @@ -278,8 +278,8 @@ void LayeredTexture::fillTexture(const Document& doc) { // ------------------------------------------------------------------------------------------------ Video::Video(uint64_t id, const Element& element, const Document& doc, const std::string& name) : - Object(id,element,name), - contentLength(0), + Object(id,element,name), + contentLength(0), content(0) { const Scope& sc = GetRequiredScope(element); diff --git a/code/AssetLib/FBX/FBXMeshGeometry.cpp b/code/AssetLib/FBX/FBXMeshGeometry.cpp index 5aecb61b5..6aeebcbe3 100644 --- a/code/AssetLib/FBX/FBXMeshGeometry.cpp +++ b/code/AssetLib/FBX/FBXMeshGeometry.cpp @@ -633,7 +633,7 @@ void MeshGeometry::ReadVertexDataMaterials(std::vector& materials_out, cons { return; } - + // materials are handled separately. First of all, they are assigned per-face // and not per polyvert. Secondly, ReferenceInformationType=IndexToDirect // has a slightly different meaning for materials. diff --git a/code/AssetLib/FBX/FBXMeshGeometry.h b/code/AssetLib/FBX/FBXMeshGeometry.h index ae17860e3..862693b4b 100644 --- a/code/AssetLib/FBX/FBXMeshGeometry.h +++ b/code/AssetLib/FBX/FBXMeshGeometry.h @@ -52,8 +52,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { namespace FBX { -/** - * DOM base class for all kinds of FBX geometry +/** + * DOM base class for all kinds of FBX geometry */ class Geometry : public Object { @@ -76,7 +76,7 @@ private: typedef std::vector MatIndexArray; -/** +/** * DOM class for FBX geometry of type "Mesh" */ class MeshGeometry : public Geometry @@ -84,7 +84,7 @@ class MeshGeometry : public Geometry public: /** The class constructor */ MeshGeometry( uint64_t id, const Element& element, const std::string& name, const Document& doc ); - + /** The class destructor */ virtual ~MeshGeometry(); diff --git a/code/AssetLib/FBX/FBXProperties.h b/code/AssetLib/FBX/FBXProperties.h index 9ab784fa7..d1d6b87ab 100644 --- a/code/AssetLib/FBX/FBXProperties.h +++ b/code/AssetLib/FBX/FBXProperties.h @@ -98,7 +98,7 @@ typedef std::fbx_unordered_map > DirectPro typedef std::fbx_unordered_map PropertyMap; typedef std::fbx_unordered_map LazyPropertyMap; -/** +/** * Represents a property table as can be found in the newer FBX files (Properties60, Properties70) */ class PropertyTable { @@ -130,7 +130,7 @@ private: // ------------------------------------------------------------------------------------------------ template -inline +inline T PropertyGet(const PropertyTable& in, const std::string& name, const T& defaultValue) { const Property* const prop = in.Get(name); if( nullptr == prop) { @@ -148,7 +148,7 @@ T PropertyGet(const PropertyTable& in, const std::string& name, const T& default // ------------------------------------------------------------------------------------------------ template -inline +inline T PropertyGet(const PropertyTable& in, const std::string& name, bool& result, bool useTemplate=false ) { const Property* prop = in.Get(name); if( nullptr == prop) { diff --git a/code/AssetLib/FBX/FBXUtil.cpp b/code/AssetLib/FBX/FBXUtil.cpp index 66abf0565..3fe791b97 100644 --- a/code/AssetLib/FBX/FBXUtil.cpp +++ b/code/AssetLib/FBX/FBXUtil.cpp @@ -101,7 +101,7 @@ std::string GetLineAndColumnText(unsigned int line, unsigned int column) std::string GetTokenText(const Token* tok) { if(tok->IsBinary()) { - return static_cast( Formatter::format() << + return static_cast( Formatter::format() << " (" << TokenTypeString(tok->Type()) << ", offset 0x" << std::hex << tok->Offset() << ") " ); } diff --git a/code/AssetLib/HMP/HMPLoader.cpp b/code/AssetLib/HMP/HMPLoader.cpp index cd14cb9c3..97c1858fb 100644 --- a/code/AssetLib/HMP/HMPLoader.cpp +++ b/code/AssetLib/HMP/HMPLoader.cpp @@ -153,10 +153,10 @@ void HMPImporter::InternReadFile(const std::string &pFile, } else { // Print the magic word to the logger std::string szBuffer = ai_str_toprintable((const char *)&iMagic, sizeof(iMagic)); - + delete[] mBuffer; mBuffer = nullptr; - + // We're definitely unable to load this file throw DeadlyImportError("Unknown HMP subformat ", pFile, ". Magic word (", szBuffer, ") is not known"); diff --git a/code/AssetLib/IFC/IFCCurve.cpp b/code/AssetLib/IFC/IFCCurve.cpp index 28cd9690c..3ded43bc0 100644 --- a/code/AssetLib/IFC/IFCCurve.cpp +++ b/code/AssetLib/IFC/IFCCurve.cpp @@ -514,7 +514,7 @@ IfcFloat Curve::GetParametricRangeDelta() const { // ------------------------------------------------------------------------------------------------ size_t Curve::EstimateSampleCount(IfcFloat a, IfcFloat b) const { - (void)(a); (void)(b); + (void)(a); (void)(b); ai_assert( InRange( a ) ); ai_assert( InRange( b ) ); diff --git a/code/AssetLib/IFC/IFCOpenings.cpp b/code/AssetLib/IFC/IFCOpenings.cpp index b7665582c..d6671b885 100644 --- a/code/AssetLib/IFC/IFCOpenings.cpp +++ b/code/AssetLib/IFC/IFCOpenings.cpp @@ -911,14 +911,14 @@ size_t CloseWindows(ContourVector& contours, // compare base poly normal and contour normal to detect if we need to reverse the face winding if(curmesh.mVertcnt.size() > 0) { IfcVector3 basePolyNormal = TempMesh::ComputePolygonNormal(curmesh.mVerts.data(), curmesh.mVertcnt.front()); - + std::vector worldSpaceContourVtx(it->contour.size()); - + for(size_t a = 0; a < it->contour.size(); ++a) worldSpaceContourVtx[a] = minv * IfcVector3(it->contour[a].x, it->contour[a].y, 0.0); - + IfcVector3 contourNormal = TempMesh::ComputePolygonNormal(worldSpaceContourVtx.data(), worldSpaceContourVtx.size()); - + reverseCountourFaces = (contourNormal * basePolyNormal) > 0.0; } diff --git a/code/AssetLib/IFC/IFCReaderGen1_2x3.cpp b/code/AssetLib/IFC/IFCReaderGen1_2x3.cpp index 2cfa22530..a6f7ae3eb 100644 --- a/code/AssetLib/IFC/IFCReaderGen1_2x3.cpp +++ b/code/AssetLib/IFC/IFCReaderGen1_2x3.cpp @@ -5,8 +5,8 @@ Open Asset Import Library (ASSIMP) Copyright (c) 2006-2020, ASSIMP Development Team All rights reserved. -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the +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 @@ -23,16 +23,16 @@ following conditions are met: derived from this software without specific prior written permission of the ASSIMP Development Team. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +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 +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 +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 +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. ---------------------------------------------------------------------- @@ -1063,27 +1063,27 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcRoo if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcRoot"); } do { // convert the 'GlobalId' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->GlobalId, arg, db ); break; } + try { GenericConvert( in->GlobalId, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRoot to be a `IfcGloballyUniqueId`")); } } while(0); do { // convert the 'OwnerHistory' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->OwnerHistory, arg, db ); break; } + try { GenericConvert( in->OwnerHistory, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRoot to be a `IfcOwnerHistory`")); } } while(0); do { // convert the 'Name' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcRoot to be a `IfcLabel`")); } } while(0); do { // convert the 'Description' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Description, arg, db ); break; } + try { GenericConvert( in->Description, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRoot to be a `IfcText`")); } } while(0); return base; @@ -1150,27 +1150,27 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcRepresentation"); } do { // convert the 'ContextOfItems' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->ContextOfItems, arg, db ); break; } + try { GenericConvert( in->ContextOfItems, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRepresentation to be a `IfcRepresentationContext`")); } } while(0); do { // convert the 'RepresentationIdentifier' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RepresentationIdentifier, arg, db ); break; } + try { GenericConvert( in->RepresentationIdentifier, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRepresentation to be a `IfcLabel`")); } } while(0); do { // convert the 'RepresentationType' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RepresentationType, arg, db ); break; } + try { GenericConvert( in->RepresentationType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcRepresentation to be a `IfcLabel`")); } } while(0); do { // convert the 'Items' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } - try { GenericConvert( in->Items, arg, db ); break; } + try { GenericConvert( in->Items, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRepresentation to be a `SET [1:?] OF IfcRepresentationItem`")); } } while(0); return base; @@ -1237,7 +1237,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcO std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ObjectType, arg, db ); break; } + try { GenericConvert( in->ObjectType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcObject to be a `IfcLabel`")); } } while(0); return base; @@ -1290,20 +1290,20 @@ template <> size_t GenericFill(const DB& db, const LIS std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcProductRepresentation to be a `IfcLabel`")); } } while(0); do { // convert the 'Description' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Description, arg, db ); break; } + try { GenericConvert( in->Description, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcProductRepresentation to be a `IfcText`")); } } while(0); do { // convert the 'Representations' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->Representations, arg, db ); break; } + try { GenericConvert( in->Representations, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcProductRepresentation to be a `LIST [1:?] OF IfcRepresentation`")); } } while(0); return base; @@ -1316,14 +1316,14 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ObjectPlacement, arg, db ); break; } + try { GenericConvert( in->ObjectPlacement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcProduct to be a `IfcObjectPlacement`")); } } while(0); do { // convert the 'Representation' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Representation, arg, db ); break; } + try { GenericConvert( in->Representation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcProduct to be a `IfcProductRepresentation`")); } } while(0); return base; @@ -1336,7 +1336,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Tag, arg, db ); break; } + try { GenericConvert( in->Tag, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcElement to be a `IfcIdentifier`")); } } while(0); return base; @@ -1374,13 +1374,13 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcCompositeCurve"); } do { // convert the 'Segments' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Segments, arg, db ); break; } + try { GenericConvert( in->Segments, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCompositeCurve to be a `LIST [1:?] OF IfcCompositeCurveSegment`")); } } while(0); do { // convert the 'SelfIntersect' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->SelfIntersect, arg, db ); break; } + try { GenericConvert( in->SelfIntersect, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCompositeCurve to be a `LOGICAL`")); } } while(0); return base; @@ -1400,27 +1400,27 @@ template <> size_t GenericFill(const DB& db, std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis1, arg, db ); break; } + try { GenericConvert( in->Axis1, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCartesianTransformationOperator to be a `IfcDirection`")); } } while(0); do { // convert the 'Axis2' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis2, arg, db ); break; } + try { GenericConvert( in->Axis2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCartesianTransformationOperator to be a `IfcDirection`")); } } while(0); do { // convert the 'LocalOrigin' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->LocalOrigin, arg, db ); break; } + try { GenericConvert( in->LocalOrigin, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcCartesianTransformationOperator to be a `IfcCartesianPoint`")); } } while(0); do { // convert the 'Scale' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Scale, arg, db ); break; } + try { GenericConvert( in->Scale, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcCartesianTransformationOperator to be a `REAL`")); } } while(0); return base; @@ -1433,7 +1433,7 @@ template <> size_t GenericFill(const DB& d std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis3, arg, db ); break; } + try { GenericConvert( in->Axis3, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcCartesianTransformationOperator3D to be a `IfcDirection`")); } } while(0); return base; @@ -1445,14 +1445,14 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcProperty"); } do { // convert the 'Name' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcProperty to be a `IfcIdentifier`")); } } while(0); do { // convert the 'Description' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Description, arg, db ); break; } + try { GenericConvert( in->Description, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcProperty to be a `IfcText`")); } } while(0); return base; @@ -1497,7 +1497,7 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcElementarySurface"); } do { // convert the 'Position' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcElementarySurface to be a `IfcAxis2Placement3D`")); } } while(0); return base; @@ -1515,19 +1515,19 @@ template <> size_t GenericFill(const DB& db, const LIST& param if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcBooleanResult"); } do { // convert the 'Operator' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Operator, arg, db ); break; } + try { GenericConvert( in->Operator, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcBooleanResult to be a `IfcBooleanOperator`")); } } while(0); do { // convert the 'FirstOperand' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->FirstOperand, arg, db ); break; } + try { GenericConvert( in->FirstOperand, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcBooleanResult to be a `IfcBooleanOperand`")); } } while(0); do { // convert the 'SecondOperand' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->SecondOperand, arg, db ); break; } + try { GenericConvert( in->SecondOperand, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcBooleanResult to be a `IfcBooleanOperand`")); } } while(0); return base; @@ -1551,7 +1551,7 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcManifoldSolidBrep"); } do { // convert the 'Outer' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Outer, arg, db ); break; } + try { GenericConvert( in->Outer, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcManifoldSolidBrep to be a `IfcClosedShell`")); } } while(0); return base; @@ -1630,12 +1630,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelFillsElement"); } do { // convert the 'RelatingOpeningElement' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingOpeningElement, arg, db ); break; } + try { GenericConvert( in->RelatingOpeningElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelFillsElement to be a `IfcOpeningElement`")); } } while(0); do { // convert the 'RelatedBuildingElement' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedBuildingElement, arg, db ); break; } + try { GenericConvert( in->RelatedBuildingElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelFillsElement to be a `IfcElement`")); } } while(0); return base; @@ -1681,12 +1681,12 @@ template <> size_t GenericFill(const DB& db, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelContainedInSpatialStructure"); } do { // convert the 'RelatedElements' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedElements, arg, db ); break; } + try { GenericConvert( in->RelatedElements, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelContainedInSpatialStructure to be a `SET [1:?] OF IfcProduct`")); } } while(0); do { // convert the 'RelatingStructure' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingStructure, arg, db ); break; } + try { GenericConvert( in->RelatingStructure, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelContainedInSpatialStructure to be a `IfcSpatialStructureElement`")); } } while(0); return base; @@ -1772,7 +1772,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcDirection"); } do { // convert the 'DirectionRatios' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->DirectionRatios, arg, db ); break; } + try { GenericConvert( in->DirectionRatios, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcDirection to be a `LIST [2:3] OF REAL`")); } } while(0); return base; @@ -1784,14 +1784,14 @@ template <> size_t GenericFill(const DB& db, const LIST& params, if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcProfileDef"); } do { // convert the 'ProfileType' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->ProfileType, arg, db ); break; } + try { GenericConvert( in->ProfileType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcProfileDef to be a `IfcProfileTypeEnum`")); } } while(0); do { // convert the 'ProfileName' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ProfileName, arg, db ); break; } + try { GenericConvert( in->ProfileName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcProfileDef to be a `IfcLabel`")); } } while(0); return base; @@ -1803,7 +1803,7 @@ template <> size_t GenericFill(const DB& db, const L if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcParameterizedProfileDef"); } do { // convert the 'Position' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcParameterizedProfileDef to be a `IfcAxis2Placement2D`")); } } while(0); return base; @@ -1910,7 +1910,7 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcCircleProfileDef"); } do { // convert the 'Radius' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Radius, arg, db ); break; } + try { GenericConvert( in->Radius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcCircleProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -1921,7 +1921,7 @@ template <> size_t GenericFill(const DB& db, const LI size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcCircleHollowProfileDef"); } do { // convert the 'WallThickness' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->WallThickness, arg, db ); break; } + try { GenericConvert( in->WallThickness, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcCircleHollowProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -1933,7 +1933,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPlacement"); } do { // convert the 'Location' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Location, arg, db ); break; } + try { GenericConvert( in->Location, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPlacement to be a `IfcCartesianPoint`")); } } while(0); return base; @@ -1945,13 +1945,13 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcAxis2Placement3D"); } do { // convert the 'Axis' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis, arg, db ); break; } + try { GenericConvert( in->Axis, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcAxis2Placement3D to be a `IfcDirection`")); } } while(0); do { // convert the 'RefDirection' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefDirection, arg, db ); break; } + try { GenericConvert( in->RefDirection, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcAxis2Placement3D to be a `IfcDirection`")); } } while(0); return base; @@ -1964,7 +1964,7 @@ template <> size_t GenericFill(const DB& db, const LIST& p std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPresentationStyle to be a `IfcLabel`")); } } while(0); return base; @@ -1982,17 +1982,17 @@ template <> size_t GenericFill(const DB& db, const LIS size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcCompositeCurveSegment"); } do { // convert the 'Transition' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Transition, arg, db ); break; } + try { GenericConvert( in->Transition, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCompositeCurveSegment to be a `IfcTransitionCode`")); } } while(0); do { // convert the 'SameSense' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->SameSense, arg, db ); break; } + try { GenericConvert( in->SameSense, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCompositeCurveSegment to be a `BOOLEAN`")); } } while(0); do { // convert the 'ParentCurve' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ParentCurve, arg, db ); break; } + try { GenericConvert( in->ParentCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcCompositeCurveSegment to be a `IfcCurve`")); } } while(0); return base; @@ -2004,13 +2004,13 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcRectangleProfileDef"); } do { // convert the 'XDim' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->XDim, arg, db ); break; } + try { GenericConvert( in->XDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRectangleProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'YDim' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->YDim, arg, db ); break; } + try { GenericConvert( in->YDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRectangleProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2106,12 +2106,12 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcLocalPlacement"); } do { // convert the 'PlacementRelTo' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->PlacementRelTo, arg, db ); break; } + try { GenericConvert( in->PlacementRelTo, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcLocalPlacement to be a `IfcObjectPlacement`")); } } while(0); do { // convert the 'RelativePlacement' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelativePlacement, arg, db ); break; } + try { GenericConvert( in->RelativePlacement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcLocalPlacement to be a `IfcAxis2Placement`")); } } while(0); return base; @@ -2123,13 +2123,13 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcSweptAreaSolid"); } do { // convert the 'SweptArea' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->SweptArea, arg, db ); break; } + try { GenericConvert( in->SweptArea, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSweptAreaSolid to be a `IfcProfileDef`")); } } while(0); do { // convert the 'Position' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSweptAreaSolid to be a `IfcAxis2Placement3D`")); } } while(0); return base; @@ -2140,12 +2140,12 @@ template <> size_t GenericFill(const DB& db, const LIST& p size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcRevolvedAreaSolid"); } do { // convert the 'Axis' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Axis, arg, db ); break; } + try { GenericConvert( in->Axis, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcRevolvedAreaSolid to be a `IfcAxis1Placement`")); } } while(0); do { // convert the 'Angle' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Angle, arg, db ); break; } + try { GenericConvert( in->Angle, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRevolvedAreaSolid to be a `IfcPlaneAngleMeasure`")); } } while(0); return base; @@ -2170,28 +2170,28 @@ template <> size_t GenericFill(const DB& db, const LIST& para size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcSweptDiskSolid"); } do { // convert the 'Directrix' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Directrix, arg, db ); break; } + try { GenericConvert( in->Directrix, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSweptDiskSolid to be a `IfcCurve`")); } } while(0); do { // convert the 'Radius' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Radius, arg, db ); break; } + try { GenericConvert( in->Radius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSweptDiskSolid to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'InnerRadius' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->InnerRadius, arg, db ); break; } + try { GenericConvert( in->InnerRadius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSweptDiskSolid to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'StartParam' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->StartParam, arg, db ); break; } + try { GenericConvert( in->StartParam, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcSweptDiskSolid to be a `IfcParameterValue`")); } } while(0); do { // convert the 'EndParam' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->EndParam, arg, db ); break; } + try { GenericConvert( in->EndParam, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcSweptDiskSolid to be a `IfcParameterValue`")); } } while(0); return base; @@ -2203,13 +2203,13 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcHalfSpaceSolid"); } do { // convert the 'BaseSurface' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->BaseSurface, arg, db ); break; } + try { GenericConvert( in->BaseSurface, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcHalfSpaceSolid to be a `IfcSurface`")); } } while(0); do { // convert the 'AgreementFlag' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->AgreementFlag, arg, db ); break; } + try { GenericConvert( in->AgreementFlag, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcHalfSpaceSolid to be a `BOOLEAN`")); } } while(0); return base; @@ -2220,12 +2220,12 @@ template <> size_t GenericFill(const DB& db, const size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcPolygonalBoundedHalfSpace"); } do { // convert the 'Position' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcPolygonalBoundedHalfSpace to be a `IfcAxis2Placement3D`")); } } while(0); do { // convert the 'PolygonalBoundary' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->PolygonalBoundary, arg, db ); break; } + try { GenericConvert( in->PolygonalBoundary, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcPolygonalBoundedHalfSpace to be a `IfcBoundedCurve`")); } } while(0); return base; @@ -2251,23 +2251,23 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc if (params.GetSize() < 9) { throw STEP::TypeError("expected 9 arguments to IfcProject"); } do { // convert the 'LongName' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->LongName, arg, db ); break; } + try { GenericConvert( in->LongName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcProject to be a `IfcLabel`")); } } while(0); do { // convert the 'Phase' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Phase, arg, db ); break; } + try { GenericConvert( in->Phase, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcProject to be a `IfcLabel`")); } } while(0); do { // convert the 'RepresentationContexts' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RepresentationContexts, arg, db ); break; } + try { GenericConvert( in->RepresentationContexts, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcProject to be a `SET [1:?] OF IfcRepresentationContext`")); } } while(0); do { // convert the 'UnitsInContext' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->UnitsInContext, arg, db ); break; } + try { GenericConvert( in->UnitsInContext, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcProject to be a `IfcUnitAssignment`")); } } while(0); return base; @@ -2327,27 +2327,27 @@ template <> size_t GenericFill(const DB& db, const LIST& params size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcTrimmedCurve"); } do { // convert the 'BasisCurve' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->BasisCurve, arg, db ); break; } + try { GenericConvert( in->BasisCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcTrimmedCurve to be a `IfcCurve`")); } } while(0); do { // convert the 'Trim1' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Trim1, arg, db ); break; } + try { GenericConvert( in->Trim1, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcTrimmedCurve to be a `SET [1:2] OF IfcTrimmingSelect`")); } } while(0); do { // convert the 'Trim2' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Trim2, arg, db ); break; } + try { GenericConvert( in->Trim2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcTrimmedCurve to be a `SET [1:2] OF IfcTrimmingSelect`")); } } while(0); do { // convert the 'SenseAgreement' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->SenseAgreement, arg, db ); break; } + try { GenericConvert( in->SenseAgreement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcTrimmedCurve to be a `BOOLEAN`")); } } while(0); do { // convert the 'MasterRepresentation' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->MasterRepresentation, arg, db ); break; } + try { GenericConvert( in->MasterRepresentation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcTrimmedCurve to be a `IfcTrimmingPreference`")); } } while(0); return base; @@ -2359,7 +2359,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcRelDefines"); } do { // convert the 'RelatedObjects' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->RelatedObjects, arg, db ); break; } + try { GenericConvert( in->RelatedObjects, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelDefines to be a `SET [1:?] OF IfcObject`")); } } while(0); return base; @@ -2371,7 +2371,7 @@ template <> size_t GenericFill(const DB& db, const LI if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelDefinesByProperties"); } do { // convert the 'RelatingPropertyDefinition' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->RelatingPropertyDefinition, arg, db ); break; } + try { GenericConvert( in->RelatingPropertyDefinition, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelDefinesByProperties to be a `IfcPropertySetDefinition`")); } } while(0); return base; @@ -2404,7 +2404,7 @@ template <> size_t GenericFill(const DB& db, const L if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcArbitraryOpenProfileDef"); } do { // convert the 'Curve' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Curve, arg, db ); break; } + try { GenericConvert( in->Curve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcArbitraryOpenProfileDef to be a `IfcBoundedCurve`")); } } while(0); return base; @@ -2570,13 +2570,13 @@ template <> size_t GenericFill(const DB& db, const LIST& param if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelDecomposes"); } do { // convert the 'RelatingObject' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->RelatingObject, arg, db ); break; } + try { GenericConvert( in->RelatingObject, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelDecomposes to be a `IfcObjectDefinition`")); } } while(0); do { // convert the 'RelatedObjects' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->RelatedObjects, arg, db ); break; } + try { GenericConvert( in->RelatedObjects, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelDecomposes to be a `SET [1:?] OF IfcObjectDefinition`")); } } while(0); return base; @@ -2594,7 +2594,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPolyline"); } do { // convert the 'Points' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Points, arg, db ); break; } + try { GenericConvert( in->Points, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPolyline to be a `LIST [2:?] OF IfcCartesianPoint`")); } } while(0); return base; @@ -2626,12 +2626,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcMappedItem"); } do { // convert the 'MappingSource' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappingSource, arg, db ); break; } + try { GenericConvert( in->MappingSource, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcMappedItem to be a `IfcRepresentationMap`")); } } while(0); do { // convert the 'MappingTarget' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappingTarget, arg, db ); break; } + try { GenericConvert( in->MappingTarget, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcMappedItem to be a `IfcCartesianTransformationOperator`")); } } while(0); return base; @@ -2658,13 +2658,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Dimensions, arg, db ); break; } + try { GenericConvert( in->Dimensions, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcNamedUnit to be a `IfcDimensionalExponents`")); } } while(0); do { // convert the 'UnitType' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->UnitType, arg, db ); break; } + try { GenericConvert( in->UnitType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcNamedUnit to be a `IfcUnitEnum`")); } } while(0); return base; @@ -2719,13 +2719,13 @@ template <> size_t GenericFill(const DB& db, const L std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->LongName, arg, db ); break; } + try { GenericConvert( in->LongName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcSpatialStructureElement to be a `IfcLabel`")); } } while(0); do { // convert the 'CompositionType' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->CompositionType, arg, db ); break; } + try { GenericConvert( in->CompositionType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcSpatialStructureElement to be a `IfcElementCompositionEnum`")); } } while(0); return base; @@ -2737,19 +2737,19 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If if (params.GetSize() < 12) { throw STEP::TypeError("expected 12 arguments to IfcBuilding"); } do { // convert the 'ElevationOfRefHeight' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ElevationOfRefHeight, arg, db ); break; } + try { GenericConvert( in->ElevationOfRefHeight, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcBuilding to be a `IfcLengthMeasure`")); } } while(0); do { // convert the 'ElevationOfTerrain' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ElevationOfTerrain, arg, db ); break; } + try { GenericConvert( in->ElevationOfTerrain, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcBuilding to be a `IfcLengthMeasure`")); } } while(0); do { // convert the 'BuildingAddress' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->BuildingAddress, arg, db ); break; } + try { GenericConvert( in->BuildingAddress, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 11 to IfcBuilding to be a `IfcPostalAddress`")); } } while(0); return base; @@ -2761,7 +2761,7 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcConnectedFaceSet"); } do { // convert the 'CfsFaces' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->CfsFaces, arg, db ); break; } + try { GenericConvert( in->CfsFaces, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcConnectedFaceSet to be a `SET [1:?] OF IfcFace`")); } } while(0); return base; @@ -2787,7 +2787,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcCo if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcConic"); } do { // convert the 'Position' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcConic to be a `IfcAxis2Placement`")); } } while(0); return base; @@ -2834,32 +2834,32 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 8) { throw STEP::TypeError("expected 8 arguments to IfcIShapeProfileDef"); } do { // convert the 'OverallWidth' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->OverallWidth, arg, db ); break; } + try { GenericConvert( in->OverallWidth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'OverallDepth' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->OverallDepth, arg, db ); break; } + try { GenericConvert( in->OverallDepth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'WebThickness' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->WebThickness, arg, db ); break; } + try { GenericConvert( in->WebThickness, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'FlangeThickness' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } - try { GenericConvert( in->FlangeThickness, arg, db ); break; } + try { GenericConvert( in->FlangeThickness, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'FilletRadius' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[4]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->FilletRadius, arg, db ); break; } + try { GenericConvert( in->FilletRadius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2933,13 +2933,13 @@ template <> size_t GenericFill(const DB& db, const LIST& p size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcPropertyListValue"); } do { // convert the 'ListValues' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ListValues, arg, db ); break; } + try { GenericConvert( in->ListValues, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcPropertyListValue to be a `LIST [1:?] OF IfcValue`")); } } while(0); do { // convert the 'Unit' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Unit, arg, db ); break; } + try { GenericConvert( in->Unit, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcPropertyListValue to be a `IfcUnit`")); } } while(0); return base; @@ -2965,13 +2965,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcDoo if (params.GetSize() < 10) { throw STEP::TypeError("expected 10 arguments to IfcDoor"); } do { // convert the 'OverallHeight' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->OverallHeight, arg, db ); break; } + try { GenericConvert( in->OverallHeight, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcDoor to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'OverallWidth' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->OverallWidth, arg, db ); break; } + try { GenericConvert( in->OverallWidth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcDoor to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2984,20 +2984,20 @@ template <> size_t GenericFill(const DB& db, const LIST& params, std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Item, arg, db ); break; } + try { GenericConvert( in->Item, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcStyledItem to be a `IfcRepresentationItem`")); } } while(0); do { // convert the 'Styles' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Styles, arg, db ); break; } + try { GenericConvert( in->Styles, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcStyledItem to be a `SET [1:?] OF IfcPresentationStyleAssignment`")); } } while(0); do { // convert the 'Name' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcStyledItem to be a `IfcLabel`")); } } while(0); return base; @@ -3023,7 +3023,7 @@ template <> size_t GenericFill(const DB& db, const if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcArbitraryClosedProfileDef"); } do { // convert the 'OuterCurve' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->OuterCurve, arg, db ); break; } + try { GenericConvert( in->OuterCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcArbitraryClosedProfileDef to be a `IfcCurve`")); } } while(0); return base; @@ -3041,12 +3041,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcLin size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcLine"); } do { // convert the 'Pnt' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Pnt, arg, db ); break; } + try { GenericConvert( in->Pnt, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcLine to be a `IfcCartesianPoint`")); } } while(0); do { // convert the 'Dir' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Dir, arg, db ); break; } + try { GenericConvert( in->Dir, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcLine to be a `IfcVector`")); } } while(0); return base; @@ -3072,13 +3072,13 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcPropertySingleValue"); } do { // convert the 'NominalValue' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->NominalValue, arg, db ); break; } + try { GenericConvert( in->NominalValue, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcPropertySingleValue to be a `IfcValue`")); } } while(0); do { // convert the 'Unit' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Unit, arg, db ); break; } + try { GenericConvert( in->Unit, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcPropertySingleValue to be a `IfcUnit`")); } } while(0); return base; @@ -3111,7 +3111,7 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcSurfaceStyleShading"); } do { // convert the 'SurfaceColour' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->SurfaceColour, arg, db ); break; } + try { GenericConvert( in->SurfaceColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSurfaceStyleShading to be a `IfcColourRgb`")); } } while(0); return base; diff --git a/code/AssetLib/IFC/IFCReaderGen2_2x3.cpp b/code/AssetLib/IFC/IFCReaderGen2_2x3.cpp index c58c7c42f..0d7051195 100644 --- a/code/AssetLib/IFC/IFCReaderGen2_2x3.cpp +++ b/code/AssetLib/IFC/IFCReaderGen2_2x3.cpp @@ -5,8 +5,8 @@ Open Asset Import Library (ASSIMP) Copyright (c) 2006-2020, ASSIMP Development Team All rights reserved. -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the +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 @@ -23,16 +23,16 @@ following conditions are met: derived from this software without specific prior written permission of the ASSIMP Development Team. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +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 +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 +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 +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. ---------------------------------------------------------------------- @@ -59,12 +59,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcSurfaceStyle"); } do { // convert the 'Side' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Side, arg, db ); break; } + try { GenericConvert( in->Side, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSurfaceStyle to be a `IfcSurfaceSide`")); } } while(0); do { // convert the 'Styles' argument std::shared_ptr arg = params[ base++ ]; - try { GenericConvert( in->Styles, arg, db ); break; } + try { GenericConvert( in->Styles, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSurfaceStyle to be a `SET [1:5] OF IfcSurfaceStyleElementSelect`")); } } while(0); return base; @@ -118,7 +118,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcFac if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcFace"); } do { // convert the 'Bounds' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Bounds, arg, db ); break; } + try { GenericConvert( in->Bounds, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcFace to be a `SET [1:?] OF IfcFaceBound`")); } } while(0); return base; @@ -173,7 +173,7 @@ template <> size_t GenericFill(const DB& db, const LIST& std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcColourSpecification to be a `IfcLabel`")); } } while(0); return base; @@ -184,12 +184,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcV size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcVector"); } do { // convert the 'Orientation' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Orientation, arg, db ); break; } + try { GenericConvert( in->Orientation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcVector to be a `IfcDirection`")); } } while(0); do { // convert the 'Magnitude' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Magnitude, arg, db ); break; } + try { GenericConvert( in->Magnitude, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcVector to be a `IfcLengthMeasure`")); } } while(0); return base; @@ -207,17 +207,17 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcColourRgb"); } do { // convert the 'Red' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Red, arg, db ); break; } + try { GenericConvert( in->Red, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcColourRgb to be a `IfcNormalisedRatioMeasure`")); } } while(0); do { // convert the 'Green' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Green, arg, db ); break; } + try { GenericConvert( in->Green, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcColourRgb to be a `IfcNormalisedRatioMeasure`")); } } while(0); do { // convert the 'Blue' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Blue, arg, db ); break; } + try { GenericConvert( in->Blue, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcColourRgb to be a `IfcNormalisedRatioMeasure`")); } } while(0); return base; @@ -243,31 +243,31 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcSit if (params.GetSize() < 14) { throw STEP::TypeError("expected 14 arguments to IfcSite"); } do { // convert the 'RefLatitude' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefLatitude, arg, db ); break; } + try { GenericConvert( in->RefLatitude, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcSite to be a `IfcCompoundPlaneAngleMeasure`")); } } while(0); do { // convert the 'RefLongitude' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefLongitude, arg, db ); break; } + try { GenericConvert( in->RefLongitude, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcSite to be a `IfcCompoundPlaneAngleMeasure`")); } } while(0); do { // convert the 'RefElevation' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefElevation, arg, db ); break; } + try { GenericConvert( in->RefElevation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 11 to IfcSite to be a `IfcLengthMeasure`")); } } while(0); do { // convert the 'LandTitleNumber' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->LandTitleNumber, arg, db ); break; } + try { GenericConvert( in->LandTitleNumber, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 12 to IfcSite to be a `IfcLabel`")); } } while(0); do { // convert the 'SiteAddress' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->SiteAddress, arg, db ); break; } + try { GenericConvert( in->SiteAddress, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 13 to IfcSite to be a `IfcPostalAddress`")); } } while(0); return base; @@ -412,31 +412,31 @@ template <> size_t GenericFill(const DB& db, const LIST& params if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcBSplineCurve"); } do { // convert the 'Degree' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Degree, arg, db ); break; } + try { GenericConvert( in->Degree, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcBSplineCurve to be a `INTEGER`")); } } while(0); do { // convert the 'ControlPointsList' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->ControlPointsList, arg, db ); break; } + try { GenericConvert( in->ControlPointsList, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcBSplineCurve to be a `LIST [2:?] OF IfcCartesianPoint`")); } } while(0); do { // convert the 'CurveForm' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->CurveForm, arg, db ); break; } + try { GenericConvert( in->CurveForm, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcBSplineCurve to be a `IfcBSplineCurveForm`")); } } while(0); do { // convert the 'ClosedCurve' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } - try { GenericConvert( in->ClosedCurve, arg, db ); break; } + try { GenericConvert( in->ClosedCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcBSplineCurve to be a `LOGICAL`")); } } while(0); do { // convert the 'SelfIntersect' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[4]=true; break; } - try { GenericConvert( in->SelfIntersect, arg, db ); break; } + try { GenericConvert( in->SelfIntersect, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcBSplineCurve to be a `LOGICAL`")); } } while(0); return base; @@ -474,7 +474,7 @@ template <> size_t GenericFill(const DB& db, const LI size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcShellBasedSurfaceModel"); } do { // convert the 'SbsmBoundary' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->SbsmBoundary, arg, db ); break; } + try { GenericConvert( in->SbsmBoundary, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcShellBasedSurfaceModel to be a `SET [1:?] OF IfcShell`")); } } while(0); return base; @@ -492,12 +492,12 @@ template <> size_t GenericFill(const DB& db, const LIST& p size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcExtrudedAreaSolid"); } do { // convert the 'ExtrudedDirection' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ExtrudedDirection, arg, db ); break; } + try { GenericConvert( in->ExtrudedDirection, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcExtrudedAreaSolid to be a `IfcDirection`")); } } while(0); do { // convert the 'Depth' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Depth, arg, db ); break; } + try { GenericConvert( in->Depth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcExtrudedAreaSolid to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -522,12 +522,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelVoidsElement"); } do { // convert the 'RelatingBuildingElement' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingBuildingElement, arg, db ); break; } + try { GenericConvert( in->RelatingBuildingElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelVoidsElement to be a `IfcElement`")); } } while(0); do { // convert the 'RelatedOpeningElement' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedOpeningElement, arg, db ); break; } + try { GenericConvert( in->RelatedOpeningElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelVoidsElement to be a `IfcFeatureElementSubtraction`")); } } while(0); return base; @@ -546,13 +546,13 @@ template <> size_t GenericFill(c if (params.GetSize() < 7) { throw STEP::TypeError("expected 7 arguments to IfcCartesianTransformationOperator3DnonUniform"); } do { // convert the 'Scale2' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Scale2, arg, db ); break; } + try { GenericConvert( in->Scale2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcCartesianTransformationOperator3DnonUniform to be a `REAL`")); } } while(0); do { // convert the 'Scale3' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Scale3, arg, db ); break; } + try { GenericConvert( in->Scale3, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcCartesianTransformationOperator3DnonUniform to be a `REAL`")); } } while(0); return base; @@ -634,7 +634,7 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcAxis2Placement2D"); } do { // convert the 'RefDirection' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefDirection, arg, db ); break; } + try { GenericConvert( in->RefDirection, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcAxis2Placement2D to be a `IfcDirection`")); } } while(0); return base; @@ -658,7 +658,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcCartesianPoint"); } do { // convert the 'Coordinates' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Coordinates, arg, db ); break; } + try { GenericConvert( in->Coordinates, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCartesianPoint to be a `LIST [1:3] OF IfcLengthMeasure`")); } } while(0); return base; @@ -682,7 +682,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPolyLoop"); } do { // convert the 'Polygon' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Polygon, arg, db ); break; } + try { GenericConvert( in->Polygon, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPolyLoop to be a `LIST [3:?] OF IfcCartesianPoint`")); } } while(0); return base; @@ -716,14 +716,14 @@ template <> size_t GenericFill(const DB& db, const LIS std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ContextIdentifier, arg, db ); break; } + try { GenericConvert( in->ContextIdentifier, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRepresentationContext to be a `IfcLabel`")); } } while(0); do { // convert the 'ContextType' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ContextType, arg, db ); break; } + try { GenericConvert( in->ContextType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRepresentationContext to be a `IfcLabel`")); } } while(0); return base; @@ -735,27 +735,27 @@ template <> size_t GenericFill(const DB& db, if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcGeometricRepresentationContext"); } do { // convert the 'CoordinateSpaceDimension' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->CoordinateSpaceDimension, arg, db ); break; } + try { GenericConvert( in->CoordinateSpaceDimension, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcGeometricRepresentationContext to be a `IfcDimensionCount`")); } } while(0); do { // convert the 'Precision' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Precision, arg, db ); break; } + try { GenericConvert( in->Precision, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcGeometricRepresentationContext to be a `REAL`")); } } while(0); do { // convert the 'WorldCoordinateSystem' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->WorldCoordinateSystem, arg, db ); break; } + try { GenericConvert( in->WorldCoordinateSystem, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcGeometricRepresentationContext to be a `IfcAxis2Placement`")); } } while(0); do { // convert the 'TrueNorth' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->TrueNorth, arg, db ); break; } + try { GenericConvert( in->TrueNorth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcGeometricRepresentationContext to be a `IfcDirection`")); } } while(0); return base; @@ -774,12 +774,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcS if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcSIUnit"); } do { // convert the 'Prefix' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Prefix, arg, db ); break; } + try { GenericConvert( in->Prefix, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSIUnit to be a `IfcSIPrefix`")); } } while(0); do { // convert the 'Name' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcSIUnit to be a `IfcSIUnitName`")); } } while(0); return base; @@ -805,7 +805,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcAxis1Placement"); } do { // convert the 'Axis' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis, arg, db ); break; } + try { GenericConvert( in->Axis, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcAxis1Placement to be a `IfcDirection`")); } } while(0); return base; @@ -858,12 +858,12 @@ template <> size_t GenericFill(const DB& db, const LIST& p size_t base = 0; if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcRepresentationMap"); } do { // convert the 'MappingOrigin' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappingOrigin, arg, db ); break; } + try { GenericConvert( in->MappingOrigin, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRepresentationMap to be a `IfcAxis2Placement`")); } } while(0); do { // convert the 'MappedRepresentation' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappedRepresentation, arg, db ); break; } + try { GenericConvert( in->MappedRepresentation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRepresentationMap to be a `IfcRepresentation`")); } } while(0); return base; @@ -1012,12 +1012,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = 0; if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcMeasureWithUnit"); } do { // convert the 'ValueComponent' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ValueComponent, arg, db ); break; } + try { GenericConvert( in->ValueComponent, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcMeasureWithUnit to be a `IfcValue`")); } } while(0); do { // convert the 'UnitComponent' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->UnitComponent, arg, db ); break; } + try { GenericConvert( in->UnitComponent, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcMeasureWithUnit to be a `IfcUnit`")); } } while(0); return base; @@ -1125,7 +1125,7 @@ template <> size_t GenericFill(const DB& db, const LIS size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcFaceBasedSurfaceModel"); } do { // convert the 'FbsmFaces' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->FbsmFaces, arg, db ); break; } + try { GenericConvert( in->FbsmFaces, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcFaceBasedSurfaceModel to be a `SET [1:?] OF IfcConnectedFaceSet`")); } } while(0); return base; @@ -1172,13 +1172,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcFaceBound"); } do { // convert the 'Bound' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Bound, arg, db ); break; } + try { GenericConvert( in->Bound, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcFaceBound to be a `IfcLoop`")); } } while(0); do { // convert the 'Orientation' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Orientation, arg, db ); break; } + try { GenericConvert( in->Orientation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcFaceBound to be a `BOOLEAN`")); } } while(0); return base; @@ -1216,12 +1216,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcComplexProperty"); } do { // convert the 'UsageName' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->UsageName, arg, db ); break; } + try { GenericConvert( in->UsageName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcComplexProperty to be a `IfcIdentifier`")); } } while(0); do { // convert the 'HasProperties' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->HasProperties, arg, db ); break; } + try { GenericConvert( in->HasProperties, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcComplexProperty to be a `SET [1:?] OF IfcProperty`")); } } while(0); return base; @@ -1274,7 +1274,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para size_t base = 0; if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcUnitAssignment"); } do { // convert the 'Units' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Units, arg, db ); break; } + try { GenericConvert( in->Units, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcUnitAssignment to be a `SET [1:?] OF IfcUnit`")); } } while(0); return base; @@ -1307,12 +1307,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcElementQuantity"); } do { // convert the 'MethodOfMeasurement' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->MethodOfMeasurement, arg, db ); break; } + try { GenericConvert( in->MethodOfMeasurement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcElementQuantity to be a `IfcLabel`")); } } while(0); do { // convert the 'Quantities' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Quantities, arg, db ); break; } + try { GenericConvert( in->Quantities, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcElementQuantity to be a `SET [1:?] OF IfcPhysicalQuantity`")); } } while(0); return base; @@ -1379,7 +1379,7 @@ template <> size_t GenericFill(const DB& db, con size_t base = 0; if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPresentationStyleAssignment"); } do { // convert the 'Styles' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Styles, arg, db ); break; } + try { GenericConvert( in->Styles, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPresentationStyleAssignment to be a `SET [1:?] OF IfcPresentationStyleSelect`")); } } while(0); return base; @@ -1418,13 +1418,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcSp size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 11) { throw STEP::TypeError("expected 11 arguments to IfcSpace"); } do { // convert the 'InteriorOrExteriorSpace' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->InteriorOrExteriorSpace, arg, db ); break; } + try { GenericConvert( in->InteriorOrExteriorSpace, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcSpace to be a `IfcInternalOrExternalEnum`")); } } while(0); do { // convert the 'ElevationWithFlooring' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ElevationWithFlooring, arg, db ); break; } + try { GenericConvert( in->ElevationWithFlooring, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcSpace to be a `IfcLengthMeasure`")); } } while(0); return base; @@ -1484,7 +1484,7 @@ template <> size_t GenericFill(const DB& db, const size_t base = 0; if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcSurfaceStyleWithTextures"); } do { // convert the 'Textures' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Textures, arg, db ); break; } + try { GenericConvert( in->Textures, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSurfaceStyleWithTextures to be a `LIST [1:?] OF IfcSurfaceTexture`")); } } while(0); return base; @@ -1495,22 +1495,22 @@ template <> size_t GenericFill(const DB& db, const LIST& params, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcBoundingBox"); } do { // convert the 'Corner' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Corner, arg, db ); break; } + try { GenericConvert( in->Corner, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcBoundingBox to be a `IfcCartesianPoint`")); } } while(0); do { // convert the 'XDim' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->XDim, arg, db ); break; } + try { GenericConvert( in->XDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcBoundingBox to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'YDim' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->YDim, arg, db ); break; } + try { GenericConvert( in->YDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcBoundingBox to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'ZDim' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ZDim, arg, db ); break; } + try { GenericConvert( in->ZDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcBoundingBox to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -1535,7 +1535,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcC size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcCircle"); } do { // convert the 'Radius' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Radius, arg, db ); break; } + try { GenericConvert( in->Radius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCircle to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -1623,12 +1623,12 @@ template <> size_t GenericFill(const DB& db, const LIST& size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcConversionBasedUnit"); } do { // convert the 'Name' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcConversionBasedUnit to be a `IfcLabel`")); } } while(0); do { // convert the 'ConversionFactor' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ConversionFactor, arg, db ); break; } + try { GenericConvert( in->ConversionFactor, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcConversionBasedUnit to be a `IfcMeasureWithUnit`")); } } while(0); return base; @@ -1744,12 +1744,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcEllipse"); } do { // convert the 'SemiAxis1' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->SemiAxis1, arg, db ); break; } + try { GenericConvert( in->SemiAxis1, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcEllipse to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'SemiAxis2' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->SemiAxis2, arg, db ); break; } + try { GenericConvert( in->SemiAxis2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcEllipse to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -1816,7 +1816,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcPropertySet"); } do { // convert the 'HasProperties' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->HasProperties, arg, db ); break; } + try { GenericConvert( in->HasProperties, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcPropertySet to be a `SET [1:?] OF IfcProperty`")); } } while(0); return base; @@ -1828,48 +1828,48 @@ template <> size_t GenericFill(const DB& db, const LIS if (params.GetSize() < 9) { throw STEP::TypeError("expected 9 arguments to IfcSurfaceStyleRendering"); } do { // convert the 'Transparency' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Transparency, arg, db ); break; } + try { GenericConvert( in->Transparency, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSurfaceStyleRendering to be a `IfcNormalisedRatioMeasure`")); } } while(0); do { // convert the 'DiffuseColour' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->DiffuseColour, arg, db ); break; } + try { GenericConvert( in->DiffuseColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'TransmissionColour' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->TransmissionColour, arg, db ); break; } + try { GenericConvert( in->TransmissionColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'DiffuseTransmissionColour' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->DiffuseTransmissionColour, arg, db ); break; } + try { GenericConvert( in->DiffuseTransmissionColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'ReflectionColour' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ReflectionColour, arg, db ); break; } + try { GenericConvert( in->ReflectionColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'SpecularColour' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->SpecularColour, arg, db ); break; } + try { GenericConvert( in->SpecularColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'SpecularHighlight' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->SpecularHighlight, arg, db ); break; } + try { GenericConvert( in->SpecularHighlight, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcSurfaceStyleRendering to be a `IfcSpecularHighlightSelect`")); } } while(0); do { // convert the 'ReflectanceMethod' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ReflectanceMethod, arg, db ); break; } + try { GenericConvert( in->ReflectanceMethod, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcSurfaceStyleRendering to be a `IfcReflectanceMethodEnum`")); } } while(0); return base; diff --git a/code/AssetLib/IFC/IFCReaderGen_4.cpp b/code/AssetLib/IFC/IFCReaderGen_4.cpp index 9eb3e2446..179322902 100644 --- a/code/AssetLib/IFC/IFCReaderGen_4.cpp +++ b/code/AssetLib/IFC/IFCReaderGen_4.cpp @@ -5,8 +5,8 @@ Open Asset Import Library (ASSIMP) Copyright (c) 2006-2020, ASSIMP Development Team All rights reserved. -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the +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 @@ -23,16 +23,16 @@ following conditions are met: derived from this software without specific prior written permission of the ASSIMP Development Team. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +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 +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 +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 +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. ---------------------------------------------------------------------- @@ -1254,28 +1254,28 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcRoo if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcRoot"); } do { // convert the 'GlobalId' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->GlobalId, arg, db ); break; } + try { GenericConvert( in->GlobalId, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRoot to be a `IfcGloballyUniqueId`")); } } while(0); do { // convert the 'OwnerHistory' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->OwnerHistory, arg, db ); break; } + try { GenericConvert( in->OwnerHistory, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRoot to be a `IfcOwnerHistory`")); } } while(0); do { // convert the 'Name' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcRoot to be a `IfcLabel`")); } } while(0); do { // convert the 'Description' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Description, arg, db ); break; } + try { GenericConvert( in->Description, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRoot to be a `IfcText`")); } } while(0); return base; @@ -1294,7 +1294,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcO boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ObjectType, arg, db ); break; } + try { GenericConvert( in->ObjectType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcObject to be a `IfcLabel`")); } } while(0); return base; @@ -1328,14 +1328,14 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ObjectPlacement, arg, db ); break; } + try { GenericConvert( in->ObjectPlacement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcProduct to be a `IfcObjectPlacement`")); } } while(0); do { // convert the 'Representation' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Representation, arg, db ); break; } + try { GenericConvert( in->Representation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcProduct to be a `IfcProductRepresentation`")); } } while(0); return base; @@ -1348,7 +1348,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Tag, arg, db ); break; } + try { GenericConvert( in->Tag, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcElement to be a `IfcIdentifier`")); } } while(0); return base; @@ -1441,7 +1441,7 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcManifoldSolidBrep"); } do { // convert the 'Outer' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Outer, arg, db ); break; } + try { GenericConvert( in->Outer, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcManifoldSolidBrep to be a `IfcClosedShell`")); } } while(0); return base; @@ -1473,7 +1473,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcFac if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcFace"); } do { // convert the 'Bounds' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Bounds, arg, db ); break; } + try { GenericConvert( in->Bounds, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcFace to be a `SET [1:?] OF IfcFaceBound`")); } } while(0); return base; @@ -1624,14 +1624,14 @@ template <> size_t GenericFill(const DB& db, const LIST& params, if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcProfileDef"); } do { // convert the 'ProfileType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->ProfileType, arg, db ); break; } + try { GenericConvert( in->ProfileType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcProfileDef to be a `IfcProfileTypeEnum`")); } } while(0); do { // convert the 'ProfileName' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ProfileName, arg, db ); break; } + try { GenericConvert( in->ProfileName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcProfileDef to be a `IfcLabel`")); } } while(0); return base; @@ -1643,7 +1643,7 @@ template <> size_t GenericFill(const DB& db, const if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcArbitraryClosedProfileDef"); } do { // convert the 'OuterCurve' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->OuterCurve, arg, db ); break; } + try { GenericConvert( in->OuterCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcArbitraryClosedProfileDef to be a `IfcCurve`")); } } while(0); return base; @@ -1655,7 +1655,7 @@ template <> size_t GenericFill(const DB& db, const L if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcArbitraryOpenProfileDef"); } do { // convert the 'Curve' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Curve, arg, db ); break; } + try { GenericConvert( in->Curve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcArbitraryOpenProfileDef to be a `IfcBoundedCurve`")); } } while(0); return base; @@ -1666,7 +1666,7 @@ template <> size_t GenericFill(const DB& db, co size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcArbitraryProfileDefWithVoids"); } do { // convert the 'InnerCurves' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->InnerCurves, arg, db ); break; } + try { GenericConvert( in->InnerCurves, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcArbitraryProfileDefWithVoids to be a `SET [1:?] OF IfcCurve`")); } } while(0); return base; @@ -1693,7 +1693,7 @@ template <> size_t GenericFill(const DB& db, const L boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcParameterizedProfileDef to be a `IfcAxis2Placement2D`")); } } while(0); return base; @@ -1726,7 +1726,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPlacement"); } do { // convert the 'Location' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Location, arg, db ); break; } + try { GenericConvert( in->Location, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPlacement to be a `IfcCartesianPoint`")); } } while(0); return base; @@ -1738,7 +1738,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcAxis1Placement"); } do { // convert the 'Axis' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis, arg, db ); break; } + try { GenericConvert( in->Axis, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcAxis1Placement to be a `IfcDirection`")); } } while(0); return base; @@ -1750,7 +1750,7 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcAxis2Placement2D"); } do { // convert the 'RefDirection' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefDirection, arg, db ); break; } + try { GenericConvert( in->RefDirection, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcAxis2Placement2D to be a `IfcDirection`")); } } while(0); return base; @@ -1762,13 +1762,13 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcAxis2Placement3D"); } do { // convert the 'Axis' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis, arg, db ); break; } + try { GenericConvert( in->Axis, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcAxis2Placement3D to be a `IfcDirection`")); } } while(0); do { // convert the 'RefDirection' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefDirection, arg, db ); break; } + try { GenericConvert( in->RefDirection, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcAxis2Placement3D to be a `IfcDirection`")); } } while(0); return base; @@ -1792,31 +1792,31 @@ template <> size_t GenericFill(const DB& db, const LIST& params if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcBSplineCurve"); } do { // convert the 'Degree' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Degree, arg, db ); break; } + try { GenericConvert( in->Degree, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcBSplineCurve to be a `IfcInteger`")); } } while(0); do { // convert the 'ControlPointsList' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->ControlPointsList, arg, db ); break; } + try { GenericConvert( in->ControlPointsList, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcBSplineCurve to be a `LIST [2:?] OF IfcCartesianPoint`")); } } while(0); do { // convert the 'CurveForm' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->CurveForm, arg, db ); break; } + try { GenericConvert( in->CurveForm, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcBSplineCurve to be a `IfcBSplineCurveForm`")); } } while(0); do { // convert the 'ClosedCurve' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } - try { GenericConvert( in->ClosedCurve, arg, db ); break; } + try { GenericConvert( in->ClosedCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcBSplineCurve to be a `IfcLogical`")); } } while(0); do { // convert the 'SelfIntersect' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[4]=true; break; } - try { GenericConvert( in->SelfIntersect, arg, db ); break; } + try { GenericConvert( in->SelfIntersect, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcBSplineCurve to be a `IfcLogical`")); } } while(0); return base; @@ -1930,19 +1930,19 @@ template <> size_t GenericFill(const DB& db, const LIST& param if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcBooleanResult"); } do { // convert the 'Operator' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Operator, arg, db ); break; } + try { GenericConvert( in->Operator, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcBooleanResult to be a `IfcBooleanOperator`")); } } while(0); do { // convert the 'FirstOperand' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->FirstOperand, arg, db ); break; } + try { GenericConvert( in->FirstOperand, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcBooleanResult to be a `IfcBooleanOperand`")); } } while(0); do { // convert the 'SecondOperand' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->SecondOperand, arg, db ); break; } + try { GenericConvert( in->SecondOperand, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcBooleanResult to be a `IfcBooleanOperand`")); } } while(0); return base; @@ -1960,13 +1960,13 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcCompositeCurve"); } do { // convert the 'Segments' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Segments, arg, db ); break; } + try { GenericConvert( in->Segments, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCompositeCurve to be a `LIST [1:?] OF IfcCompositeCurveSegment`")); } } while(0); do { // convert the 'SelfIntersect' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->SelfIntersect, arg, db ); break; } + try { GenericConvert( in->SelfIntersect, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCompositeCurve to be a `IfcLogical`")); } } while(0); return base; @@ -1991,22 +1991,22 @@ template <> size_t GenericFill(const DB& db, const LIST& params, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcBoundingBox"); } do { // convert the 'Corner' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Corner, arg, db ); break; } + try { GenericConvert( in->Corner, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcBoundingBox to be a `IfcCartesianPoint`")); } } while(0); do { // convert the 'XDim' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->XDim, arg, db ); break; } + try { GenericConvert( in->XDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcBoundingBox to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'YDim' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->YDim, arg, db ); break; } + try { GenericConvert( in->YDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcBoundingBox to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'ZDim' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->ZDim, arg, db ); break; } + try { GenericConvert( in->ZDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcBoundingBox to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2018,13 +2018,13 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcHalfSpaceSolid"); } do { // convert the 'BaseSurface' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->BaseSurface, arg, db ); break; } + try { GenericConvert( in->BaseSurface, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcHalfSpaceSolid to be a `IfcSurface`")); } } while(0); do { // convert the 'AgreementFlag' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->AgreementFlag, arg, db ); break; } + try { GenericConvert( in->AgreementFlag, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcHalfSpaceSolid to be a `IfcBoolean`")); } } while(0); return base; @@ -2044,7 +2044,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->LongName, arg, db ); break; } + try { GenericConvert( in->LongName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcSpatialElement to be a `IfcLabel`")); } } while(0); return base; @@ -2057,7 +2057,7 @@ template <> size_t GenericFill(const DB& db, const L boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->CompositionType, arg, db ); break; } + try { GenericConvert( in->CompositionType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcSpatialStructureElement to be a `IfcElementCompositionEnum`")); } } while(0); return base; @@ -2069,19 +2069,19 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If if (params.GetSize() < 12) { throw STEP::TypeError("expected 12 arguments to IfcBuilding"); } do { // convert the 'ElevationOfRefHeight' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ElevationOfRefHeight, arg, db ); break; } + try { GenericConvert( in->ElevationOfRefHeight, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcBuilding to be a `IfcLengthMeasure`")); } } while(0); do { // convert the 'ElevationOfTerrain' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ElevationOfTerrain, arg, db ); break; } + try { GenericConvert( in->ElevationOfTerrain, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcBuilding to be a `IfcLengthMeasure`")); } } while(0); do { // convert the 'BuildingAddress' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->BuildingAddress, arg, db ); break; } + try { GenericConvert( in->BuildingAddress, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 11 to IfcBuilding to be a `IfcPostalAddress`")); } } while(0); return base; @@ -2266,7 +2266,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcCartesianPoint"); } do { // convert the 'Coordinates' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Coordinates, arg, db ); break; } + try { GenericConvert( in->Coordinates, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCartesianPoint to be a `LIST [1:3] OF IfcLengthMeasure`")); } } while(0); return base; @@ -2300,27 +2300,27 @@ template <> size_t GenericFill(const DB& db, boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis1, arg, db ); break; } + try { GenericConvert( in->Axis1, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCartesianTransformationOperator to be a `IfcDirection`")); } } while(0); do { // convert the 'Axis2' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis2, arg, db ); break; } + try { GenericConvert( in->Axis2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCartesianTransformationOperator to be a `IfcDirection`")); } } while(0); do { // convert the 'LocalOrigin' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->LocalOrigin, arg, db ); break; } + try { GenericConvert( in->LocalOrigin, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcCartesianTransformationOperator to be a `IfcCartesianPoint`")); } } while(0); do { // convert the 'Scale' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Scale, arg, db ); break; } + try { GenericConvert( in->Scale, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcCartesianTransformationOperator to be a `IfcReal`")); } } while(0); return base; @@ -2347,7 +2347,7 @@ template <> size_t GenericFill(const DB& d boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis3, arg, db ); break; } + try { GenericConvert( in->Axis3, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcCartesianTransformationOperator3D to be a `IfcDirection`")); } } while(0); return base; @@ -2359,13 +2359,13 @@ template <> size_t GenericFill(c if (params.GetSize() < 7) { throw STEP::TypeError("expected 7 arguments to IfcCartesianTransformationOperator3DnonUniform"); } do { // convert the 'Scale2' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Scale2, arg, db ); break; } + try { GenericConvert( in->Scale2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcCartesianTransformationOperator3DnonUniform to be a `IfcReal`")); } } while(0); do { // convert the 'Scale3' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Scale3, arg, db ); break; } + try { GenericConvert( in->Scale3, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcCartesianTransformationOperator3DnonUniform to be a `IfcReal`")); } } while(0); return base; @@ -2412,7 +2412,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcCo if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcConic"); } do { // convert the 'Position' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcConic to be a `IfcAxis2Placement`")); } } while(0); return base; @@ -2423,7 +2423,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcC size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcCircle"); } do { // convert the 'Radius' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Radius, arg, db ); break; } + try { GenericConvert( in->Radius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCircle to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2435,7 +2435,7 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcCircleProfileDef"); } do { // convert the 'Radius' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Radius, arg, db ); break; } + try { GenericConvert( in->Radius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcCircleProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2446,7 +2446,7 @@ template <> size_t GenericFill(const DB& db, const LI size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcCircleHollowProfileDef"); } do { // convert the 'WallThickness' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->WallThickness, arg, db ); break; } + try { GenericConvert( in->WallThickness, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcCircleHollowProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2472,7 +2472,7 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcConnectedFaceSet"); } do { // convert the 'CfsFaces' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->CfsFaces, arg, db ); break; } + try { GenericConvert( in->CfsFaces, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcConnectedFaceSet to be a `SET [1:?] OF IfcFace`")); } } while(0); return base; @@ -2505,7 +2505,7 @@ template <> size_t GenericFill(const DB& db, const LIST& boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcColourSpecification to be a `IfcLabel`")); } } while(0); return base; @@ -2516,17 +2516,17 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcColourRgb"); } do { // convert the 'Red' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Red, arg, db ); break; } + try { GenericConvert( in->Red, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcColourRgb to be a `IfcNormalisedRatioMeasure`")); } } while(0); do { // convert the 'Green' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Green, arg, db ); break; } + try { GenericConvert( in->Green, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcColourRgb to be a `IfcNormalisedRatioMeasure`")); } } while(0); do { // convert the 'Blue' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Blue, arg, db ); break; } + try { GenericConvert( in->Blue, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcColourRgb to be a `IfcNormalisedRatioMeasure`")); } } while(0); return base; @@ -2579,14 +2579,14 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcProperty"); } do { // convert the 'Name' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcProperty to be a `IfcIdentifier`")); } } while(0); do { // convert the 'Description' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Description, arg, db ); break; } + try { GenericConvert( in->Description, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcProperty to be a `IfcText`")); } } while(0); return base; @@ -2597,12 +2597,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcComplexProperty"); } do { // convert the 'UsageName' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->UsageName, arg, db ); break; } + try { GenericConvert( in->UsageName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcComplexProperty to be a `IfcIdentifier`")); } } while(0); do { // convert the 'HasProperties' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->HasProperties, arg, db ); break; } + try { GenericConvert( in->HasProperties, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcComplexProperty to be a `SET [1:?] OF IfcProperty`")); } } while(0); return base; @@ -2620,19 +2620,19 @@ template <> size_t GenericFill(const DB& db, const LIS if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcCompositeCurveSegment"); } do { // convert the 'Transition' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Transition, arg, db ); break; } + try { GenericConvert( in->Transition, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCompositeCurveSegment to be a `IfcTransitionCode`")); } } while(0); do { // convert the 'SameSense' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->SameSense, arg, db ); break; } + try { GenericConvert( in->SameSense, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCompositeCurveSegment to be a `IfcBoolean`")); } } while(0); do { // convert the 'ParentCurve' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->ParentCurve, arg, db ); break; } + try { GenericConvert( in->ParentCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcCompositeCurveSegment to be a `IfcCurve`")); } } while(0); return base; @@ -2764,35 +2764,35 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ObjectType, arg, db ); break; } + try { GenericConvert( in->ObjectType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcContext to be a `IfcLabel`")); } } while(0); do { // convert the 'LongName' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->LongName, arg, db ); break; } + try { GenericConvert( in->LongName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcContext to be a `IfcLabel`")); } } while(0); do { // convert the 'Phase' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Phase, arg, db ); break; } + try { GenericConvert( in->Phase, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcContext to be a `IfcLabel`")); } } while(0); do { // convert the 'RepresentationContexts' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RepresentationContexts, arg, db ); break; } + try { GenericConvert( in->RepresentationContexts, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcContext to be a `SET [1:?] OF IfcRepresentationContext`")); } } while(0); do { // convert the 'UnitsInContext' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[4]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->UnitsInContext, arg, db ); break; } + try { GenericConvert( in->UnitsInContext, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcContext to be a `IfcUnitAssignment`")); } } while(0); return base; @@ -2804,13 +2804,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcNamedUnit"); } do { // convert the 'Dimensions' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Dimensions, arg, db ); break; } + try { GenericConvert( in->Dimensions, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcNamedUnit to be a `IfcDimensionalExponents`")); } } while(0); do { // convert the 'UnitType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->UnitType, arg, db ); break; } + try { GenericConvert( in->UnitType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcNamedUnit to be a `IfcUnitEnum`")); } } while(0); return base; @@ -2843,13 +2843,13 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcConversionBasedUnit"); } do { // convert the 'Name' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcConversionBasedUnit to be a `IfcLabel`")); } } while(0); do { // convert the 'ConversionFactor' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->ConversionFactor, arg, db ); break; } + try { GenericConvert( in->ConversionFactor, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcConversionBasedUnit to be a `IfcMeasureWithUnit`")); } } while(0); return base; @@ -2974,7 +2974,7 @@ template <> size_t GenericFill(const DB& db, const LIST& p boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPresentationStyle to be a `IfcLabel`")); } } while(0); return base; @@ -2986,7 +2986,7 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcElementarySurface"); } do { // convert the 'Position' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcElementarySurface to be a `IfcAxis2Placement3D`")); } } while(0); return base; @@ -3025,7 +3025,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcDirection"); } do { // convert the 'DirectionRatios' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->DirectionRatios, arg, db ); break; } + try { GenericConvert( in->DirectionRatios, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcDirection to be a `LIST [2:3] OF IfcReal`")); } } while(0); return base; @@ -3094,35 +3094,35 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcDoo boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->OverallHeight, arg, db ); break; } + try { GenericConvert( in->OverallHeight, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcDoor to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'OverallWidth' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->OverallWidth, arg, db ); break; } + try { GenericConvert( in->OverallWidth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcDoor to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'PredefinedType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->PredefinedType, arg, db ); break; } + try { GenericConvert( in->PredefinedType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcDoor to be a `IfcDoorTypeEnum`")); } } while(0); do { // convert the 'OperationType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->OperationType, arg, db ); break; } + try { GenericConvert( in->OperationType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 11 to IfcDoor to be a `IfcDoorTypeOperationEnum`")); } } while(0); do { // convert the 'UserDefinedOperationType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[4]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->UserDefinedOperationType, arg, db ); break; } + try { GenericConvert( in->UserDefinedOperationType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 12 to IfcDoor to be a `IfcLabel`")); } } while(0); return base; @@ -3362,12 +3362,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcElementQuantity"); } do { // convert the 'MethodOfMeasurement' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->MethodOfMeasurement, arg, db ); break; } + try { GenericConvert( in->MethodOfMeasurement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcElementQuantity to be a `IfcLabel`")); } } while(0); do { // convert the 'Quantities' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Quantities, arg, db ); break; } + try { GenericConvert( in->Quantities, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcElementQuantity to be a `SET [1:?] OF IfcPhysicalQuantity`")); } } while(0); return base; @@ -3378,12 +3378,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcEllipse"); } do { // convert the 'SemiAxis1' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->SemiAxis1, arg, db ); break; } + try { GenericConvert( in->SemiAxis1, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcEllipse to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'SemiAxis2' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->SemiAxis2, arg, db ); break; } + try { GenericConvert( in->SemiAxis2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcEllipse to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -3486,14 +3486,14 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcSweptAreaSolid"); } do { // convert the 'SweptArea' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->SweptArea, arg, db ); break; } + try { GenericConvert( in->SweptArea, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSweptAreaSolid to be a `IfcProfileDef`")); } } while(0); do { // convert the 'Position' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSweptAreaSolid to be a `IfcAxis2Placement3D`")); } } while(0); return base; @@ -3505,13 +3505,13 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcExtrudedAreaSolid"); } do { // convert the 'ExtrudedDirection' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->ExtrudedDirection, arg, db ); break; } + try { GenericConvert( in->ExtrudedDirection, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcExtrudedAreaSolid to be a `IfcDirection`")); } } while(0); do { // convert the 'Depth' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Depth, arg, db ); break; } + try { GenericConvert( in->Depth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcExtrudedAreaSolid to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -3529,7 +3529,7 @@ template <> size_t GenericFill(const DB& db, const LIS size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcFaceBasedSurfaceModel"); } do { // convert the 'FbsmFaces' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->FbsmFaces, arg, db ); break; } + try { GenericConvert( in->FbsmFaces, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcFaceBasedSurfaceModel to be a `SET [1:?] OF IfcConnectedFaceSet`")); } } while(0); return base; @@ -3541,13 +3541,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcFaceBound"); } do { // convert the 'Bound' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Bound, arg, db ); break; } + try { GenericConvert( in->Bound, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcFaceBound to be a `IfcLoop`")); } } while(0); do { // convert the 'Orientation' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Orientation, arg, db ); break; } + try { GenericConvert( in->Orientation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcFaceBound to be a `IfcBoolean`")); } } while(0); return base; @@ -3774,14 +3774,14 @@ template <> size_t GenericFill(const DB& db, const LIS boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ContextIdentifier, arg, db ); break; } + try { GenericConvert( in->ContextIdentifier, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRepresentationContext to be a `IfcLabel`")); } } while(0); do { // convert the 'ContextType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ContextType, arg, db ); break; } + try { GenericConvert( in->ContextType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRepresentationContext to be a `IfcLabel`")); } } while(0); return base; @@ -3793,27 +3793,27 @@ template <> size_t GenericFill(const DB& db, if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcGeometricRepresentationContext"); } do { // convert the 'CoordinateSpaceDimension' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->CoordinateSpaceDimension, arg, db ); break; } + try { GenericConvert( in->CoordinateSpaceDimension, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcGeometricRepresentationContext to be a `IfcDimensionCount`")); } } while(0); do { // convert the 'Precision' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Precision, arg, db ); break; } + try { GenericConvert( in->Precision, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcGeometricRepresentationContext to be a `IfcReal`")); } } while(0); do { // convert the 'WorldCoordinateSystem' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->WorldCoordinateSystem, arg, db ); break; } + try { GenericConvert( in->WorldCoordinateSystem, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcGeometricRepresentationContext to be a `IfcAxis2Placement`")); } } while(0); do { // convert the 'TrueNorth' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->TrueNorth, arg, db ); break; } + try { GenericConvert( in->TrueNorth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcGeometricRepresentationContext to be a `IfcDirection`")); } } while(0); return base; @@ -3879,40 +3879,40 @@ template <> size_t GenericFill(const DB& db, const LIST& pa size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 10) { throw STEP::TypeError("expected 10 arguments to IfcIShapeProfileDef"); } do { // convert the 'OverallWidth' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->OverallWidth, arg, db ); break; } + try { GenericConvert( in->OverallWidth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'OverallDepth' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->OverallDepth, arg, db ); break; } + try { GenericConvert( in->OverallDepth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'WebThickness' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->WebThickness, arg, db ); break; } + try { GenericConvert( in->WebThickness, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'FlangeThickness' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->FlangeThickness, arg, db ); break; } + try { GenericConvert( in->FlangeThickness, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'FilletRadius' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->FilletRadius, arg, db ); break; } + try { GenericConvert( in->FilletRadius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcIShapeProfileDef to be a `IfcNonNegativeLengthMeasure`")); } } while(0); do { // convert the 'FlangeEdgeRadius' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->FlangeEdgeRadius, arg, db ); break; } + try { GenericConvert( in->FlangeEdgeRadius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcIShapeProfileDef to be a `IfcNonNegativeLengthMeasure`")); } } while(0); do { // convert the 'FlangeSlope' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->FlangeSlope, arg, db ); break; } + try { GenericConvert( in->FlangeSlope, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcIShapeProfileDef to be a `IfcPlaneAngleMeasure`")); } } while(0); return base; @@ -4091,12 +4091,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcLin size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcLine"); } do { // convert the 'Pnt' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Pnt, arg, db ); break; } + try { GenericConvert( in->Pnt, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcLine to be a `IfcCartesianPoint`")); } } while(0); do { // convert the 'Dir' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Dir, arg, db ); break; } + try { GenericConvert( in->Dir, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcLine to be a `IfcVector`")); } } while(0); return base; @@ -4108,12 +4108,12 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcLocalPlacement"); } do { // convert the 'PlacementRelTo' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->PlacementRelTo, arg, db ); break; } + try { GenericConvert( in->PlacementRelTo, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcLocalPlacement to be a `IfcObjectPlacement`")); } } while(0); do { // convert the 'RelativePlacement' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelativePlacement, arg, db ); break; } + try { GenericConvert( in->RelativePlacement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcLocalPlacement to be a `IfcAxis2Placement`")); } } while(0); return base; @@ -4124,12 +4124,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcMappedItem"); } do { // convert the 'MappingSource' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappingSource, arg, db ); break; } + try { GenericConvert( in->MappingSource, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcMappedItem to be a `IfcRepresentationMap`")); } } while(0); do { // convert the 'MappingTarget' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappingTarget, arg, db ); break; } + try { GenericConvert( in->MappingTarget, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcMappedItem to be a `IfcCartesianTransformationOperator`")); } } while(0); return base; @@ -4142,20 +4142,20 @@ template <> size_t GenericFill(const DB& db, const LIS boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcProductRepresentation to be a `IfcLabel`")); } } while(0); do { // convert the 'Description' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Description, arg, db ); break; } + try { GenericConvert( in->Description, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcProductRepresentation to be a `IfcText`")); } } while(0); do { // convert the 'Representations' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->Representations, arg, db ); break; } + try { GenericConvert( in->Representations, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcProductRepresentation to be a `LIST [1:?] OF IfcRepresentation`")); } } while(0); return base; @@ -4173,12 +4173,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = 0; if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcMeasureWithUnit"); } do { // convert the 'ValueComponent' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->ValueComponent, arg, db ); break; } + try { GenericConvert( in->ValueComponent, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcMeasureWithUnit to be a `IfcValue`")); } } while(0); do { // convert the 'UnitComponent' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->UnitComponent, arg, db ); break; } + try { GenericConvert( in->UnitComponent, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcMeasureWithUnit to be a `IfcUnit`")); } } while(0); return base; @@ -4289,7 +4289,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->PredefinedType, arg, db ); break; } + try { GenericConvert( in->PredefinedType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcOpeningElement to be a `IfcOpeningElementTypeEnum`")); } } while(0); return base; @@ -4460,7 +4460,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPolyLoop"); } do { // convert the 'Polygon' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Polygon, arg, db ); break; } + try { GenericConvert( in->Polygon, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPolyLoop to be a `LIST [3:?] OF IfcCartesianPoint`")); } } while(0); return base; @@ -4471,12 +4471,12 @@ template <> size_t GenericFill(const DB& db, const size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcPolygonalBoundedHalfSpace"); } do { // convert the 'Position' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcPolygonalBoundedHalfSpace to be a `IfcAxis2Placement3D`")); } } while(0); do { // convert the 'PolygonalBoundary' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->PolygonalBoundary, arg, db ); break; } + try { GenericConvert( in->PolygonalBoundary, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcPolygonalBoundedHalfSpace to be a `IfcBoundedCurve`")); } } while(0); return base; @@ -4501,7 +4501,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPolyline"); } do { // convert the 'Points' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Points, arg, db ); break; } + try { GenericConvert( in->Points, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPolyline to be a `LIST [2:?] OF IfcCartesianPoint`")); } } while(0); return base; @@ -4512,7 +4512,7 @@ template <> size_t GenericFill(const DB& db, con size_t base = 0; if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPresentationStyleAssignment"); } do { // convert the 'Styles' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Styles, arg, db ); break; } + try { GenericConvert( in->Styles, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPresentationStyleAssignment to be a `SET [1:?] OF IfcPresentationStyleSelect`")); } } while(0); return base; @@ -4592,13 +4592,13 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcPropertyListValue"); } do { // convert the 'ListValues' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ListValues, arg, db ); break; } + try { GenericConvert( in->ListValues, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcPropertyListValue to be a `LIST [1:?] OF IfcValue`")); } } while(0); do { // convert the 'Unit' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Unit, arg, db ); break; } + try { GenericConvert( in->Unit, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcPropertyListValue to be a `IfcUnit`")); } } while(0); return base; @@ -4616,7 +4616,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcPropertySet"); } do { // convert the 'HasProperties' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->HasProperties, arg, db ); break; } + try { GenericConvert( in->HasProperties, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcPropertySet to be a `SET [1:?] OF IfcProperty`")); } } while(0); return base; @@ -4628,13 +4628,13 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcPropertySingleValue"); } do { // convert the 'NominalValue' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->NominalValue, arg, db ); break; } + try { GenericConvert( in->NominalValue, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcPropertySingleValue to be a `IfcValue`")); } } while(0); do { // convert the 'Unit' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Unit, arg, db ); break; } + try { GenericConvert( in->Unit, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcPropertySingleValue to be a `IfcUnit`")); } } while(0); return base; @@ -4758,13 +4758,13 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcRectangleProfileDef"); } do { // convert the 'XDim' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->XDim, arg, db ); break; } + try { GenericConvert( in->XDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRectangleProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'YDim' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->YDim, arg, db ); break; } + try { GenericConvert( in->YDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRectangleProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -4850,12 +4850,12 @@ template <> size_t GenericFill(const DB& db, const LIST& param size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelAggregates"); } do { // convert the 'RelatingObject' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingObject, arg, db ); break; } + try { GenericConvert( in->RelatingObject, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelAggregates to be a `IfcObjectDefinition`")); } } while(0); do { // convert the 'RelatedObjects' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedObjects, arg, db ); break; } + try { GenericConvert( in->RelatedObjects, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelAggregates to be a `SET [1:?] OF IfcObjectDefinition`")); } } while(0); return base; @@ -4872,12 +4872,12 @@ template <> size_t GenericFill(const DB& db, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelContainedInSpatialStructure"); } do { // convert the 'RelatedElements' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedElements, arg, db ); break; } + try { GenericConvert( in->RelatedElements, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelContainedInSpatialStructure to be a `SET [1:?] OF IfcProduct`")); } } while(0); do { // convert the 'RelatingStructure' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingStructure, arg, db ); break; } + try { GenericConvert( in->RelatingStructure, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelContainedInSpatialStructure to be a `IfcSpatialElement`")); } } while(0); return base; @@ -4894,12 +4894,12 @@ template <> size_t GenericFill(const DB& db, const LI size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelDefinesByProperties"); } do { // convert the 'RelatedObjects' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedObjects, arg, db ); break; } + try { GenericConvert( in->RelatedObjects, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelDefinesByProperties to be a `SET [1:?] OF IfcObjectDefinition`")); } } while(0); do { // convert the 'RelatingPropertyDefinition' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingPropertyDefinition, arg, db ); break; } + try { GenericConvert( in->RelatingPropertyDefinition, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelDefinesByProperties to be a `IfcPropertySetDefinitionSelect`")); } } while(0); return base; @@ -4910,12 +4910,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelFillsElement"); } do { // convert the 'RelatingOpeningElement' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingOpeningElement, arg, db ); break; } + try { GenericConvert( in->RelatingOpeningElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelFillsElement to be a `IfcOpeningElement`")); } } while(0); do { // convert the 'RelatedBuildingElement' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedBuildingElement, arg, db ); break; } + try { GenericConvert( in->RelatedBuildingElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelFillsElement to be a `IfcElement`")); } } while(0); return base; @@ -4926,12 +4926,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelVoidsElement"); } do { // convert the 'RelatingBuildingElement' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingBuildingElement, arg, db ); break; } + try { GenericConvert( in->RelatingBuildingElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelVoidsElement to be a `IfcElement`")); } } while(0); do { // convert the 'RelatedOpeningElement' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedOpeningElement, arg, db ); break; } + try { GenericConvert( in->RelatedOpeningElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelVoidsElement to be a `IfcFeatureElementSubtraction`")); } } while(0); return base; @@ -4950,27 +4950,27 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcRepresentation"); } do { // convert the 'ContextOfItems' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->ContextOfItems, arg, db ); break; } + try { GenericConvert( in->ContextOfItems, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRepresentation to be a `IfcRepresentationContext`")); } } while(0); do { // convert the 'RepresentationIdentifier' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RepresentationIdentifier, arg, db ); break; } + try { GenericConvert( in->RepresentationIdentifier, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRepresentation to be a `IfcLabel`")); } } while(0); do { // convert the 'RepresentationType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RepresentationType, arg, db ); break; } + try { GenericConvert( in->RepresentationType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcRepresentation to be a `IfcLabel`")); } } while(0); do { // convert the 'Items' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } - try { GenericConvert( in->Items, arg, db ); break; } + try { GenericConvert( in->Items, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRepresentation to be a `SET [1:?] OF IfcRepresentationItem`")); } } while(0); return base; @@ -4981,12 +4981,12 @@ template <> size_t GenericFill(const DB& db, const LIST& p size_t base = 0; if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcRepresentationMap"); } do { // convert the 'MappingOrigin' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappingOrigin, arg, db ); break; } + try { GenericConvert( in->MappingOrigin, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRepresentationMap to be a `IfcAxis2Placement`")); } } while(0); do { // convert the 'MappedRepresentation' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappedRepresentation, arg, db ); break; } + try { GenericConvert( in->MappedRepresentation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRepresentationMap to be a `IfcRepresentation`")); } } while(0); return base; @@ -4998,13 +4998,13 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcRevolvedAreaSolid"); } do { // convert the 'Axis' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Axis, arg, db ); break; } + try { GenericConvert( in->Axis, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcRevolvedAreaSolid to be a `IfcAxis1Placement`")); } } while(0); do { // convert the 'Angle' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Angle, arg, db ); break; } + try { GenericConvert( in->Angle, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRevolvedAreaSolid to be a `IfcPlaneAngleMeasure`")); } } while(0); return base; @@ -5058,12 +5058,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcS if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcSIUnit"); } do { // convert the 'Prefix' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Prefix, arg, db ); break; } + try { GenericConvert( in->Prefix, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSIUnit to be a `IfcSIPrefix`")); } } while(0); do { // convert the 'Name' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcSIUnit to be a `IfcSIUnitName`")); } } while(0); return base; @@ -5144,7 +5144,7 @@ template <> size_t GenericFill(const DB& db, const LI size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcShellBasedSurfaceModel"); } do { // convert the 'SbsmBoundary' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->SbsmBoundary, arg, db ); break; } + try { GenericConvert( in->SbsmBoundary, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcShellBasedSurfaceModel to be a `SET [1:?] OF IfcShell`")); } } while(0); return base; @@ -5156,31 +5156,31 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcSit if (params.GetSize() < 14) { throw STEP::TypeError("expected 14 arguments to IfcSite"); } do { // convert the 'RefLatitude' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefLatitude, arg, db ); break; } + try { GenericConvert( in->RefLatitude, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcSite to be a `IfcCompoundPlaneAngleMeasure`")); } } while(0); do { // convert the 'RefLongitude' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefLongitude, arg, db ); break; } + try { GenericConvert( in->RefLongitude, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcSite to be a `IfcCompoundPlaneAngleMeasure`")); } } while(0); do { // convert the 'RefElevation' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefElevation, arg, db ); break; } + try { GenericConvert( in->RefElevation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 11 to IfcSite to be a `IfcLengthMeasure`")); } } while(0); do { // convert the 'LandTitleNumber' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->LandTitleNumber, arg, db ); break; } + try { GenericConvert( in->LandTitleNumber, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 12 to IfcSite to be a `IfcLabel`")); } } while(0); do { // convert the 'SiteAddress' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->SiteAddress, arg, db ); break; } + try { GenericConvert( in->SiteAddress, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 13 to IfcSite to be a `IfcPostalAddress`")); } } while(0); return base; @@ -5234,13 +5234,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcSp if (params.GetSize() < 11) { throw STEP::TypeError("expected 11 arguments to IfcSpace"); } do { // convert the 'PredefinedType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->PredefinedType, arg, db ); break; } + try { GenericConvert( in->PredefinedType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcSpace to be a `IfcSpaceTypeEnum`")); } } while(0); do { // convert the 'ElevationWithFlooring' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ElevationWithFlooring, arg, db ); break; } + try { GenericConvert( in->ElevationWithFlooring, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcSpace to be a `IfcLengthMeasure`")); } } while(0); return base; @@ -5539,18 +5539,18 @@ template <> size_t GenericFill(const DB& db, const LIST& params, if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcStyledItem"); } do { // convert the 'Item' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Item, arg, db ); break; } + try { GenericConvert( in->Item, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcStyledItem to be a `IfcRepresentationItem`")); } } while(0); do { // convert the 'Styles' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Styles, arg, db ); break; } + try { GenericConvert( in->Styles, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcStyledItem to be a `SET [1:?] OF IfcStyleAssignmentSelect`")); } } while(0); do { // convert the 'Name' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcStyledItem to be a `IfcLabel`")); } } while(0); return base; @@ -5624,12 +5624,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcSurfaceStyle"); } do { // convert the 'Side' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Side, arg, db ); break; } + try { GenericConvert( in->Side, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSurfaceStyle to be a `IfcSurfaceSide`")); } } while(0); do { // convert the 'Styles' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Styles, arg, db ); break; } + try { GenericConvert( in->Styles, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSurfaceStyle to be a `SET [1:5] OF IfcSurfaceStyleElementSelect`")); } } while(0); return base; @@ -5641,14 +5641,14 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcSurfaceStyleShading"); } do { // convert the 'SurfaceColour' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->SurfaceColour, arg, db ); break; } + try { GenericConvert( in->SurfaceColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSurfaceStyleShading to be a `IfcColourRgb`")); } } while(0); do { // convert the 'Transparency' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Transparency, arg, db ); break; } + try { GenericConvert( in->Transparency, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSurfaceStyleShading to be a `IfcNormalisedRatioMeasure`")); } } while(0); return base; @@ -5660,42 +5660,42 @@ template <> size_t GenericFill(const DB& db, const LIS if (params.GetSize() < 9) { throw STEP::TypeError("expected 9 arguments to IfcSurfaceStyleRendering"); } do { // convert the 'DiffuseColour' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->DiffuseColour, arg, db ); break; } + try { GenericConvert( in->DiffuseColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'TransmissionColour' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->TransmissionColour, arg, db ); break; } + try { GenericConvert( in->TransmissionColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'DiffuseTransmissionColour' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->DiffuseTransmissionColour, arg, db ); break; } + try { GenericConvert( in->DiffuseTransmissionColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'ReflectionColour' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ReflectionColour, arg, db ); break; } + try { GenericConvert( in->ReflectionColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'SpecularColour' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->SpecularColour, arg, db ); break; } + try { GenericConvert( in->SpecularColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'SpecularHighlight' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->SpecularHighlight, arg, db ); break; } + try { GenericConvert( in->SpecularHighlight, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcSurfaceStyleRendering to be a `IfcSpecularHighlightSelect`")); } } while(0); do { // convert the 'ReflectanceMethod' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->ReflectanceMethod, arg, db ); break; } + try { GenericConvert( in->ReflectanceMethod, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcSurfaceStyleRendering to be a `IfcReflectanceMethodEnum`")); } } while(0); return base; @@ -5706,7 +5706,7 @@ template <> size_t GenericFill(const DB& db, const size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcSurfaceStyleWithTextures"); } do { // convert the 'Textures' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Textures, arg, db ); break; } + try { GenericConvert( in->Textures, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSurfaceStyleWithTextures to be a `LIST [1:?] OF IfcSurfaceTexture`")); } } while(0); return base; @@ -5718,34 +5718,34 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcSweptDiskSolid"); } do { // convert the 'Directrix' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Directrix, arg, db ); break; } + try { GenericConvert( in->Directrix, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSweptDiskSolid to be a `IfcCurve`")); } } while(0); do { // convert the 'Radius' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Radius, arg, db ); break; } + try { GenericConvert( in->Radius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSweptDiskSolid to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'InnerRadius' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->InnerRadius, arg, db ); break; } + try { GenericConvert( in->InnerRadius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSweptDiskSolid to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'StartParam' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->StartParam, arg, db ); break; } + try { GenericConvert( in->StartParam, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcSweptDiskSolid to be a `IfcParameterValue`")); } } while(0); do { // convert the 'EndParam' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[4]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->EndParam, arg, db ); break; } + try { GenericConvert( in->EndParam, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcSweptDiskSolid to be a `IfcParameterValue`")); } } while(0); return base; @@ -5924,27 +5924,27 @@ template <> size_t GenericFill(const DB& db, const LIST& params size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcTrimmedCurve"); } do { // convert the 'BasisCurve' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->BasisCurve, arg, db ); break; } + try { GenericConvert( in->BasisCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcTrimmedCurve to be a `IfcCurve`")); } } while(0); do { // convert the 'Trim1' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Trim1, arg, db ); break; } + try { GenericConvert( in->Trim1, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcTrimmedCurve to be a `SET [1:2] OF IfcTrimmingSelect`")); } } while(0); do { // convert the 'Trim2' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Trim2, arg, db ); break; } + try { GenericConvert( in->Trim2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcTrimmedCurve to be a `SET [1:2] OF IfcTrimmingSelect`")); } } while(0); do { // convert the 'SenseAgreement' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->SenseAgreement, arg, db ); break; } + try { GenericConvert( in->SenseAgreement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcTrimmedCurve to be a `IfcBoolean`")); } } while(0); do { // convert the 'MasterRepresentation' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->MasterRepresentation, arg, db ); break; } + try { GenericConvert( in->MasterRepresentation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcTrimmedCurve to be a `IfcTrimmingPreference`")); } } while(0); return base; @@ -5976,7 +5976,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para size_t base = 0; if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcUnitAssignment"); } do { // convert the 'Units' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Units, arg, db ); break; } + try { GenericConvert( in->Units, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcUnitAssignment to be a `SET [1:?] OF IfcUnit`")); } } while(0); return base; @@ -6029,12 +6029,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcV size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcVector"); } do { // convert the 'Orientation' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Orientation, arg, db ); break; } + try { GenericConvert( in->Orientation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcVector to be a `IfcDirection`")); } } while(0); do { // convert the 'Magnitude' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Magnitude, arg, db ); break; } + try { GenericConvert( in->Magnitude, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcVector to be a `IfcLengthMeasure`")); } } while(0); return base; diff --git a/code/AssetLib/IFC/IFCReaderGen_4.h b/code/AssetLib/IFC/IFCReaderGen_4.h index 0f184cd02..abf021911 100644 --- a/code/AssetLib/IFC/IFCReaderGen_4.h +++ b/code/AssetLib/IFC/IFCReaderGen_4.h @@ -5,8 +5,8 @@ Open Asset Import Library (ASSIMP) Copyright (c) 2006-2020, ASSIMP Development Team All rights reserved. -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the +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 @@ -23,16 +23,16 @@ following conditions are met: derived from this software without specific prior written permission of the ASSIMP Development Team. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +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 +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 +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 +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. ---------------------------------------------------------------------- @@ -51,12 +51,12 @@ namespace Schema_4 { using namespace STEP; using namespace STEP::EXPRESS; - - + + struct NotImplemented : public ObjectHelper { - + }; - + // ****************************************************************************** // IFC Custom data types diff --git a/code/AssetLib/Irr/IRRLoader.h b/code/AssetLib/Irr/IRRLoader.h index 535f6481d..da90902ed 100644 --- a/code/AssetLib/Irr/IRRLoader.h +++ b/code/AssetLib/Irr/IRRLoader.h @@ -273,7 +273,7 @@ private: std::vector& anims); private: - /// Configuration option: desired output FPS + /// Configuration option: desired output FPS double fps; /// Configuration option: speed flag was set? diff --git a/code/AssetLib/LWO/LWOAnimation.h b/code/AssetLib/LWO/LWOAnimation.h index 1ed8caf88..64aa5980a 100644 --- a/code/AssetLib/LWO/LWOAnimation.h +++ b/code/AssetLib/LWO/LWOAnimation.h @@ -114,7 +114,7 @@ enum PrePostBehaviour /** \brief Data structure for a LWO animation keyframe */ struct Key { - Key() AI_NO_EXCEPT + Key() AI_NO_EXCEPT : time() , value() , inter(IT_LINE) diff --git a/code/AssetLib/LWS/LWSLoader.cpp b/code/AssetLib/LWS/LWSLoader.cpp index 01a50b6e4..cb07787fa 100644 --- a/code/AssetLib/LWS/LWSLoader.cpp +++ b/code/AssetLib/LWS/LWSLoader.cpp @@ -200,7 +200,7 @@ void LWSImporter::ReadEnvelope(const LWS::Element &dad, LWO::Envelope &fill) { // reserve enough storage std::list::const_iterator it = dad.children.begin(); - + fill.keys.reserve(strtoul10(it->tokens[1].c_str())); for (++it; it != dad.children.end(); ++it) { @@ -466,7 +466,7 @@ std::string LWSImporter::FindLWOFile(const std::string &in) { std::string tmp(in); if (in.length() > 3 && in[1] == ':' && in[2] != '\\' && in[2] != '/') { tmp = in[0] + (std::string(":\\") + in.substr(2)); - } + } if (io->Exists(tmp)) { return in; diff --git a/code/AssetLib/M3D/M3DWrapper.h b/code/AssetLib/M3D/M3DWrapper.h index ba838d71d..dcb82a83a 100644 --- a/code/AssetLib/M3D/M3DWrapper.h +++ b/code/AssetLib/M3D/M3DWrapper.h @@ -84,11 +84,11 @@ public: void reset(); // The Name access, empty string returned when no m3d instance. - std::string Name() const; + std::string Name() const; /// Executes a save. unsigned char *Save(int quality, int flags, unsigned int &size); - + /// Clearer void ClearSave(); @@ -113,16 +113,16 @@ inline std::string M3DWrapper::Name() const { return std::string(); } -inline M3DWrapper::operator bool() const { - return m3d_ != nullptr; +inline M3DWrapper::operator bool() const { + return m3d_ != nullptr; } inline m3d_t *M3DWrapper::operator->() const { - return m3d_; + return m3d_; } inline m3d_t *M3DWrapper::M3D() const { - return m3d_; + return m3d_; } } // namespace Assimp diff --git a/code/AssetLib/M3D/m3d.h b/code/AssetLib/M3D/m3d.h index 28bf19482..3adcd5bef 100644 --- a/code/AssetLib/M3D/m3d.h +++ b/code/AssetLib/M3D/m3d.h @@ -829,7 +829,7 @@ unsigned char *_m3dstbi_zlib_compress(unsigned char *data, int data_len, int *ou #include #endif -#if !defined(M3D_NOIMPORTER) +#if !defined(M3D_NOIMPORTER) /* helper functions for the ASCII parser */ static char *_m3d_findarg(char *s) { while (s && *s && *s != ' ' && *s != '\t' && *s != '\r' && *s != '\n') @@ -4516,7 +4516,7 @@ unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size } if (length) { uint32_t v = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t *)h + len)); - memcpy( length, &v, sizeof(uint32_t)); + memcpy( length, &v, sizeof(uint32_t)); len += v; } out = NULL; @@ -4548,7 +4548,7 @@ unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size } } uint32_t v = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t *)h + len)); - memcpy( length, &v, sizeof(uint32_t)); + memcpy( length, &v, sizeof(uint32_t)); len += v; out = NULL; } diff --git a/code/AssetLib/MD5/MD5Loader.cpp b/code/AssetLib/MD5/MD5Loader.cpp index 0d9c77b71..9fba60c61 100644 --- a/code/AssetLib/MD5/MD5Loader.cpp +++ b/code/AssetLib/MD5/MD5Loader.cpp @@ -485,7 +485,7 @@ void MD5Importer::LoadMD5MeshFile() { } MD5::WeightDesc &weightDesc = meshSrc.mWeights[w]; - if (weightDesc.mWeight < AI_MD5_WEIGHT_EPSILON && weightDesc.mWeight >= -AI_MD5_WEIGHT_EPSILON) { + if (weightDesc.mWeight < AI_MD5_WEIGHT_EPSILON && weightDesc.mWeight >= -AI_MD5_WEIGHT_EPSILON) { continue; } diff --git a/code/AssetLib/MDC/MDCFileData.h b/code/AssetLib/MDC/MDCFileData.h index 616556f03..a8f3aea43 100644 --- a/code/AssetLib/MDC/MDCFileData.h +++ b/code/AssetLib/MDC/MDCFileData.h @@ -120,13 +120,13 @@ struct Surface { , ulFlags() , ulNumCompFrames() , ulNumBaseFrames() - , ulNumShaders() + , ulNumShaders() , ulNumVertices() , ulNumTriangles() , ulOffsetTriangles() , ulOffsetShaders() , ulOffsetTexCoords() - , ulOffsetBaseVerts() + , ulOffsetBaseVerts() , ulOffsetCompVerts() , ulOffsetFrameBaseFrames() , ulOffsetFrameCompFrames() diff --git a/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp b/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp index e6576b344..1ff86fe27 100644 --- a/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp +++ b/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp @@ -629,7 +629,7 @@ void HL1MDLLoader::read_meshes() { +-- bodypart --+-- model -- [mesh index, mesh index, ...] | | | +-- model -- [mesh index, mesh index, ...] - | | + | | | ... | |-- bodypart -- ... @@ -1298,7 +1298,7 @@ void HL1MDLLoader::read_global_info() { * @note The structure of this method is taken from HL2 source code. * Although this is from HL2, it's implementation is almost identical * to code found in HL1 SDK. See HL1 and HL2 SDKs for more info. -* +* * source: * HL1 source code. * file: studio_render.cpp diff --git a/code/AssetLib/MMD/MMDPmxParser.cpp b/code/AssetLib/MMD/MMDPmxParser.cpp index d57dc169a..82cc75ca2 100644 --- a/code/AssetLib/MMD/MMDPmxParser.cpp +++ b/code/AssetLib/MMD/MMDPmxParser.cpp @@ -102,7 +102,7 @@ namespace pmx const unsigned int targetSize = size * 3; // enough to encode char *targetStart = new char[targetSize]; std::memset(targetStart, 0, targetSize * sizeof(char)); - + utf8::utf16to8( sourceStart, sourceStart + size/2, targetStart ); std::string result(targetStart); diff --git a/code/AssetLib/OFF/OFFLoader.cpp b/code/AssetLib/OFF/OFFLoader.cpp index 360ffc51b..fb8f3b424 100644 --- a/code/AssetLib/OFF/OFFLoader.cpp +++ b/code/AssetLib/OFF/OFFLoader.cpp @@ -138,7 +138,7 @@ void OFFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS const char* car = buffer; const char* end = buffer + mBuffer2.size(); NextToken(&car, end); - + if (car < end - 2 && car[0] == 'S' && car[1] == 'T') { hasTexCoord = true; car += 2; } @@ -164,7 +164,7 @@ void OFFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS dimensions = 3; hasHomogenous = false; NextToken(&car, end); - + // at this point the next token should be an integer number if (car >= end - 1 || *car < '0' || *car > '9') { throw DeadlyImportError("OFF: Header is invalid"); @@ -223,7 +223,7 @@ void OFFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS ASSIMP_LOG_ERROR("OFF: The number of verts in the header is incorrect"); break; } - aiVector3D& v = mesh->mVertices[i]; + aiVector3D& v = mesh->mVertices[i]; sz = line; // helper array to write a for loop over possible dimension values @@ -255,7 +255,7 @@ void OFFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS SkipSpaces(&sz); fast_atoreal_move(sz,(ai_real&)n.z); } - + // reading colors is a pain because the specification says it can be // integers or floats, and any number of them between 1 and 4 included, // until the next comment or end of line @@ -321,7 +321,7 @@ void OFFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS ++i; ++faces; } - + // generate the output node graph pScene->mRootNode = new aiNode(); pScene->mRootNode->mName.Set(""); diff --git a/code/AssetLib/Obj/ObjExporter.h b/code/AssetLib/Obj/ObjExporter.h index 3a46da780..a64f38f74 100644 --- a/code/AssetLib/Obj/ObjExporter.h +++ b/code/AssetLib/Obj/ObjExporter.h @@ -67,7 +67,7 @@ public: ~ObjExporter(); std::string GetMaterialLibName(); std::string GetMaterialLibFileName(); - + /// public string-streams to write all output into std::ostringstream mOutput, mOutputMat; @@ -137,13 +137,13 @@ private: } }; - struct aiVectorCompare { - bool operator() (const aiVector3D& a, const aiVector3D& b) const { - if(a.x < b.x) return true; - if(a.x > b.x) return false; - if(a.y < b.y) return true; - if(a.y > b.y) return false; - if(a.z < b.z) return true; + struct aiVectorCompare { + bool operator() (const aiVector3D& a, const aiVector3D& b) const { + if(a.x < b.x) return true; + if(a.x > b.x) return false; + if(a.y < b.y) return true; + if(a.y > b.y) return false; + if(a.z < b.z) return true; return false; } }; @@ -153,7 +153,7 @@ private: int mNextIndex; typedef std::map dataType; dataType vecMap; - + public: indexMap() : mNextIndex(1) { diff --git a/code/AssetLib/Obj/ObjFileMtlImporter.cpp b/code/AssetLib/Obj/ObjFileMtlImporter.cpp index bf1b70c90..94e57c26b 100644 --- a/code/AssetLib/Obj/ObjFileMtlImporter.cpp +++ b/code/AssetLib/Obj/ObjFileMtlImporter.cpp @@ -146,7 +146,7 @@ void ObjFileMtlImporter::load() { ++m_DataIt; ai_real d; getFloatValue(d); - m_pModel->m_pCurrentMaterial->alpha = static_cast(1.0) - d; + m_pModel->m_pCurrentMaterial->alpha = static_cast(1.0) - d; } m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); } break; diff --git a/code/AssetLib/SMD/SMDLoader.cpp b/code/AssetLib/SMD/SMDLoader.cpp index de9c65c4a..90f0b7697 100644 --- a/code/AssetLib/SMD/SMDLoader.cpp +++ b/code/AssetLib/SMD/SMDLoader.cpp @@ -438,7 +438,7 @@ void SMDImporter::AddBoneChildren(aiNode* pcNode, uint32_t iParent) { pc->mTransformation = bone.sAnim.asKeys[0].matrix; } - if (bone.iParent == static_cast(-1)) { + if (bone.iParent == static_cast(-1)) { bone.mOffsetMatrix = pc->mTransformation; } else { bone.mOffsetMatrix = asBones[bone.iParent].mOffsetMatrix * pc->mTransformation; diff --git a/code/AssetLib/STL/STLExporter.cpp b/code/AssetLib/STL/STLExporter.cpp index bd4d96c71..59c6148ee 100644 --- a/code/AssetLib/STL/STLExporter.cpp +++ b/code/AssetLib/STL/STLExporter.cpp @@ -69,7 +69,7 @@ void ExportSceneSTL(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene if (exporter.mOutput.fail()) { throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile)); } - + // we're still here - export successfully completed. Write the file. std::unique_ptr outfile (pIOSystem->Open(pFile,"wt")); if (outfile == nullptr) { @@ -88,7 +88,7 @@ void ExportSceneSTLBinary(const char* pFile,IOSystem* pIOSystem, const aiScene* if (exporter.mOutput.fail()) { throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile)); } - + // we're still here - export successfully completed. Write the file. std::unique_ptr outfile (pIOSystem->Open(pFile,"wb")); if (outfile == nullptr) { @@ -139,9 +139,9 @@ STLExporter::STLExporter(const char* _filename, const aiScene* pScene, bool expo if (exportPointClouds) { WritePointCloud("Assimp_Pointcloud", pScene ); return; - } + } - // Export the assimp mesh + // Export the assimp mesh const std::string name = "AssimpScene"; mOutput << SolidToken << " " << name << endl; for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) { diff --git a/code/AssetLib/STL/STLLoader.cpp b/code/AssetLib/STL/STLLoader.cpp index 433fb14c7..8cfe63e0d 100644 --- a/code/AssetLib/STL/STLLoader.cpp +++ b/code/AssetLib/STL/STLLoader.cpp @@ -372,7 +372,7 @@ void STLImporter::LoadASCIIFile(aiNode *root) { pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; for (size_t i=0; imNumVertices; ++i ) { pMesh->mVertices[i].x = positionBuffer[i].x; - pMesh->mVertices[i].y = positionBuffer[i].y; + pMesh->mVertices[i].y = positionBuffer[i].y; pMesh->mVertices[i].z = positionBuffer[i].z; } positionBuffer.clear(); @@ -382,7 +382,7 @@ void STLImporter::LoadASCIIFile(aiNode *root) { pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; for (size_t i=0; imNumVertices; ++i ) { pMesh->mNormals[i].x = normalBuffer[i].x; - pMesh->mNormals[i].y = normalBuffer[i].y; + pMesh->mNormals[i].y = normalBuffer[i].y; pMesh->mNormals[i].z = normalBuffer[i].z; } normalBuffer.clear(); diff --git a/code/AssetLib/X/XFileExporter.cpp b/code/AssetLib/X/XFileExporter.cpp index bd997a3c5..b95cb7abf 100644 --- a/code/AssetLib/X/XFileExporter.cpp +++ b/code/AssetLib/X/XFileExporter.cpp @@ -86,7 +86,7 @@ void ExportSceneXFile(const char* pFile,IOSystem* pIOSystem, const aiScene* pSce if (iDoTheExportThing.mOutput.fail()) { throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile)); } - + // we're still here - export successfully completed. Write result to the given IOSYstem std::unique_ptr outfile (pIOSystem->Open(pFile,"wt")); if (outfile == nullptr) { diff --git a/code/AssetLib/X/XFileExporter.h b/code/AssetLib/X/XFileExporter.h index 32b75b3d1..620d282b6 100644 --- a/code/AssetLib/X/XFileExporter.h +++ b/code/AssetLib/X/XFileExporter.h @@ -94,9 +94,9 @@ protected: void PushTag() { startstr.append( " "); } /// Leaves an element, decreasing the indentation - void PopTag() { - ai_assert( startstr.length() > 1); - startstr.erase( startstr.length() - 2); + void PopTag() { + ai_assert( startstr.length() > 1); + startstr.erase( startstr.length() - 2); } public: diff --git a/code/AssetLib/X3D/X3DImporter.cpp b/code/AssetLib/X3D/X3DImporter.cpp index 121b7490e..71c35c369 100644 --- a/code/AssetLib/X3D/X3DImporter.cpp +++ b/code/AssetLib/X3D/X3DImporter.cpp @@ -180,6 +180,6 @@ const aiImporterDesc *X3DImporter::GetInfo() const { return &Description; } -} +} #endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/XGL/XGLLoader.cpp b/code/AssetLib/XGL/XGLLoader.cpp index 3b84d7ba9..20c2c7079 100644 --- a/code/AssetLib/XGL/XGLLoader.cpp +++ b/code/AssetLib/XGL/XGLLoader.cpp @@ -240,7 +240,7 @@ void XGLImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy void XGLImporter::ReadWorld(XmlNode &node, TempScope &scope) { for (XmlNode ¤tNode : node.children()) { const std::string &s = ai_stdStrToLower(currentNode.name()); - + // XXX right now we'd skip if it comes after // or if (s == "lighting") { diff --git a/code/AssetLib/glTF2/glTF2Asset.h b/code/AssetLib/glTF2/glTF2Asset.h index bb97e3732..cd6c1c89d 100644 --- a/code/AssetLib/glTF2/glTF2Asset.h +++ b/code/AssetLib/glTF2/glTF2Asset.h @@ -388,9 +388,9 @@ struct CustomExtension { } CustomExtension() = default; - + ~CustomExtension() = default; - + CustomExtension(const CustomExtension &other) : name(other.name), mStringValue(other.mStringValue), diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index 58095c7bd..d5f5b5cd0 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -410,14 +410,14 @@ inline void SetDecodedIndexBuffer_Draco(const draco::Mesh &dracoMesh, Mesh::Prim // Not same size, convert switch (componentBytes) { - case sizeof(uint32_t): - CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); + case sizeof(uint32_t): + CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); break; - case sizeof(uint16_t): - CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); + case sizeof(uint16_t): + CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); break; - case sizeof(uint8_t): - CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); + case sizeof(uint8_t): + CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); break; default: ai_assert(false); @@ -460,23 +460,23 @@ inline void SetDecodedAttributeBuffer_Draco(const draco::Mesh &dracoMesh, uint32 decodedAttribBuffer->Grow(dracoMesh.num_points() * pDracoAttribute->num_components() * componentBytes); switch (accessor.componentType) { - case ComponentType_BYTE: - GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); + case ComponentType_BYTE: + GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break; case ComponentType_UNSIGNED_BYTE: - GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); + GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break; case ComponentType_SHORT: GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break; - case ComponentType_UNSIGNED_SHORT: - GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); + case ComponentType_UNSIGNED_SHORT: + GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break; - case ComponentType_UNSIGNED_INT: - GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); + case ComponentType_UNSIGNED_INT: + GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break; - case ComponentType_FLOAT: - GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); + case ComponentType_FLOAT: + GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break; default: ai_assert(false); @@ -840,7 +840,7 @@ inline bool Buffer::ReplaceData_joint(const size_t pBufferData_Offset, const siz inline size_t Buffer::AppendData(uint8_t *data, size_t length) { const size_t offset = this->byteLength; - + // Force alignment to 4 bits const size_t paddedLength = (length + 3) & ~3; Grow(paddedLength); diff --git a/code/AssetLib/glTF2/glTF2AssetWriter.inl b/code/AssetLib/glTF2/glTF2AssetWriter.inl index bf7dbbb2e..115cdf903 100644 --- a/code/AssetLib/glTF2/glTF2AssetWriter.inl +++ b/code/AssetLib/glTF2/glTF2AssetWriter.inl @@ -452,7 +452,7 @@ namespace glTF2 { WriteTex(materialClearcoat, clearcoat.clearcoatTexture, "clearcoatTexture", w.mAl); WriteTex(materialClearcoat, clearcoat.clearcoatRoughnessTexture, "clearcoatRoughnessTexture", w.mAl); WriteTex(materialClearcoat, clearcoat.clearcoatNormalTexture, "clearcoatNormalTexture", w.mAl); - + if (!materialClearcoat.ObjectEmpty()) { exts.AddMember("KHR_materials_clearcoat", materialClearcoat, w.mAl); } @@ -468,7 +468,7 @@ namespace glTF2 { } WriteTex(materialTransmission, transmission.transmissionTexture, "transmissionTexture", w.mAl); - + if (!materialTransmission.ObjectEmpty()) { exts.AddMember("KHR_materials_transmission", materialTransmission, w.mAl); } @@ -613,7 +613,7 @@ namespace glTF2 { if (n.skin) { obj.AddMember("skin", n.skin->index, w.mAl); } - + //gltf2 spec does not support "skeletons" under node if(n.skeletons.size()) { AddRefsVector(obj, "skeletons", n.skeletons, w.mAl); @@ -711,7 +711,7 @@ namespace glTF2 { if (mAsset.scene) { mDoc.AddMember("scene", mAsset.scene->index, mAl); } - + if(mAsset.extras) { mDoc.AddMember("extras", *mAsset.extras, mAl); } @@ -812,7 +812,7 @@ namespace glTF2 { uint32_t binaryChunkLength = 0; if (bodyBuffer->byteLength > 0) { binaryChunkLength = (bodyBuffer->byteLength + 3) & ~3; // Round up to next multiple of 4 - + auto curPaddingLength = binaryChunkLength - bodyBuffer->byteLength; ++GLB_Chunk_count; @@ -881,7 +881,7 @@ namespace glTF2 { if (this->mAsset.extensionsUsed.KHR_materials_sheen) { exts.PushBack(StringRef("KHR_materials_sheen"), mAl); } - + if (this->mAsset.extensionsUsed.KHR_materials_clearcoat) { exts.PushBack(StringRef("KHR_materials_clearcoat"), mAl); } @@ -893,7 +893,7 @@ namespace glTF2 { if (this->mAsset.extensionsUsed.FB_ngon_encoding) { exts.PushBack(StringRef("FB_ngon_encoding"), mAl); } - + if (this->mAsset.extensionsUsed.KHR_texture_basisu) { exts.PushBack(StringRef("KHR_texture_basisu"), mAl); } @@ -901,7 +901,7 @@ namespace glTF2 { if (!exts.Empty()) mDoc.AddMember("extensionsUsed", exts, mAl); - + //basisu extensionRequired Value extsReq; extsReq.SetArray(); diff --git a/code/Common/DefaultIOStream.cpp b/code/Common/DefaultIOStream.cpp index ba9b9d625..557fbc78f 100644 --- a/code/Common/DefaultIOStream.cpp +++ b/code/Common/DefaultIOStream.cpp @@ -62,7 +62,7 @@ inline int select_fseek(FILE *file, int64_t offset, int origin) { } - + #if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601) template <> inline size_t select_ftell<8>(FILE *file) { @@ -75,7 +75,7 @@ inline int select_fseek<8>(FILE *file, int64_t offset, int origin) { } #endif // #if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601) - + } // namespace // ---------------------------------------------------------------------------------- @@ -95,7 +95,7 @@ size_t DefaultIOStream::Read(void *pvBuffer, } ai_assert(nullptr != pvBuffer); ai_assert(0 != pSize); - + return (mFile ? ::fread(pvBuffer, pSize, pCount, mFile) : 0); } diff --git a/code/Common/DefaultIOSystem.cpp b/code/Common/DefaultIOSystem.cpp index 98d51a17d..1ed615b84 100644 --- a/code/Common/DefaultIOSystem.cpp +++ b/code/Common/DefaultIOSystem.cpp @@ -71,7 +71,7 @@ static std::wstring Utf8ToWide(const char *in) { // size includes terminating null; std::wstring adds null automatically std::wstring out(static_cast(size) - 1, L'\0'); MultiByteToWideChar(CP_UTF8, 0, in, -1, &out[0], size); - + return out; } @@ -85,7 +85,7 @@ static std::string WideToUtf8(const wchar_t *in) { // size includes terminating null; std::string adds null automatically std::string out(static_cast(size) - 1, '\0'); WideCharToMultiByte(CP_UTF8, 0, in, -1, &out[0], size, nullptr, nullptr); - + return out; } #endif @@ -121,7 +121,7 @@ IOStream *DefaultIOSystem::Open(const char *strFile, const char *strMode) { if (name.empty()) { return nullptr; } - + file = ::_wfopen(name.c_str(), Utf8ToWide(strMode).c_str()); #else file = ::fopen(strFile, strMode); diff --git a/code/Common/Exporter.cpp b/code/Common/Exporter.cpp index ebcc955df..512bbf447 100644 --- a/code/Common/Exporter.cpp +++ b/code/Common/Exporter.cpp @@ -83,7 +83,7 @@ namespace Assimp { void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out); // ------------------------------------------------------------------------------------------------ -// Exporter worker function prototypes. Do not use const, because some exporter need to convert +// Exporter worker function prototypes. Do not use const, because some exporter need to convert // the scene temporary #ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER void ExportSceneCollada(const char*,IOSystem*, const aiScene*, const ExportProperties*); @@ -343,7 +343,7 @@ const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const cha delete pimpl->blob; pimpl->blob = nullptr; } - + auto baseName = pProperties ? pProperties->GetPropertyString(AI_CONFIG_EXPORT_BLOB_NAME, AI_BLOBIO_MAGIC) : AI_BLOBIO_MAGIC; std::shared_ptr old = pimpl->mIOSystem; diff --git a/code/Common/FileSystemFilter.h b/code/Common/FileSystemFilter.h index 6585f9df6..6782dd9e5 100644 --- a/code/Common/FileSystemFilter.h +++ b/code/Common/FileSystemFilter.h @@ -101,7 +101,7 @@ public: /** Tests for the existence of a file at the given path. */ bool Exists( const char* pFile) const { ai_assert( nullptr != mWrapped ); - + std::string tmp = pFile; // Currently this IOSystem is also used to open THE ONE FILE. @@ -126,7 +126,7 @@ public: if ( nullptr == pFile || nullptr == pMode ) { return nullptr; } - + ai_assert( nullptr != pFile ); ai_assert( nullptr != pMode ); diff --git a/code/Common/Importer.cpp b/code/Common/Importer.cpp index a2ad041fb..d0ed3c788 100644 --- a/code/Common/Importer.cpp +++ b/code/Common/Importer.cpp @@ -201,7 +201,7 @@ Importer::~Importer() { // Register a custom post-processing step aiReturn Importer::RegisterPPStep(BaseProcess* pImp) { ai_assert( nullptr != pImp ); - + ASSIMP_BEGIN_EXCEPTION_REGION(); pimpl->mPostProcessingSteps.push_back(pImp); @@ -215,7 +215,7 @@ aiReturn Importer::RegisterPPStep(BaseProcess* pImp) { // Register a custom loader plugin aiReturn Importer::RegisterLoader(BaseImporter* pImp) { ai_assert(nullptr != pImp); - + ASSIMP_BEGIN_EXCEPTION_REGION(); // -------------------------------------------------------------------- @@ -242,7 +242,7 @@ aiReturn Importer::RegisterLoader(BaseImporter* pImp) { pimpl->mImporter.push_back(pImp); ASSIMP_LOG_INFO("Registering custom importer for these file extensions: ", baked); ASSIMP_END_EXCEPTION_REGION(aiReturn); - + return AI_SUCCESS; } @@ -296,7 +296,7 @@ aiReturn Importer::UnregisterPPStep(BaseProcess* pImp) { // Supplies a custom IO handler to the importer to open and access files. void Importer::SetIOHandler( IOSystem* pIOHandler) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); // If the new handler is zero, allocate a default IO implementation. if (!pIOHandler) { @@ -315,7 +315,7 @@ void Importer::SetIOHandler( IOSystem* pIOHandler) { // Get the currently set IO handler IOSystem* Importer::GetIOHandler() const { ai_assert(nullptr != pimpl); - + return pimpl->mIOHandler; } @@ -323,7 +323,7 @@ IOSystem* Importer::GetIOHandler() const { // Check whether a custom IO handler is currently set bool Importer::IsDefaultIOHandler() const { ai_assert(nullptr != pimpl); - + return pimpl->mIsDefaultHandler; } @@ -331,9 +331,9 @@ bool Importer::IsDefaultIOHandler() const { // Supplies a custom progress handler to get regular callbacks during importing void Importer::SetProgressHandler ( ProgressHandler* pHandler ) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); - + // If the new handler is zero, allocate a default implementation. if (!pHandler) { // Release pointer in the possession of the caller @@ -351,7 +351,7 @@ void Importer::SetProgressHandler ( ProgressHandler* pHandler ) { // Get the currently set progress handler ProgressHandler* Importer::GetProgressHandler() const { ai_assert(nullptr != pimpl); - + return pimpl->mProgressHandler; } @@ -359,7 +359,7 @@ ProgressHandler* Importer::GetProgressHandler() const { // Check whether a custom progress handler is currently set bool Importer::IsDefaultProgressHandler() const { ai_assert(nullptr != pimpl); - + return pimpl->mIsDefaultProgressHandler; } @@ -381,7 +381,7 @@ bool _ValidateFlags(unsigned int pFlags) { // Free the current scene void Importer::FreeScene( ) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); delete pimpl->mScene; @@ -396,14 +396,14 @@ void Importer::FreeScene( ) { // Get the current error string, if any const char* Importer::GetErrorString() const { ai_assert(nullptr != pimpl); - + // Must remain valid as long as ReadFile() or FreeFile() are not called return pimpl->mErrorString.c_str(); } const std::exception_ptr& Importer::GetException() const { ai_assert(nullptr != pimpl); - + // Must remain valid as long as ReadFile() or FreeFile() are not called return pimpl->mException; } @@ -412,7 +412,7 @@ const std::exception_ptr& Importer::GetException() const { // Enable extra-verbose mode void Importer::SetExtraVerbose(bool bDo) { ai_assert(nullptr != pimpl); - + pimpl->bExtraVerbose = bDo; } @@ -420,7 +420,7 @@ void Importer::SetExtraVerbose(bool bDo) { // Get the current scene const aiScene* Importer::GetScene() const { ai_assert(nullptr != pimpl); - + return pimpl->mScene; } @@ -428,7 +428,7 @@ const aiScene* Importer::GetScene() const { // Orphan the current scene and return it. aiScene* Importer::GetOrphanedScene() { ai_assert(nullptr != pimpl); - + aiScene* s = pimpl->mScene; ASSIMP_BEGIN_EXCEPTION_REGION(); @@ -437,7 +437,7 @@ aiScene* Importer::GetOrphanedScene() { pimpl->mErrorString = std::string(); pimpl->mException = std::exception_ptr(); ASSIMP_END_EXCEPTION_REGION(aiScene*); - + return s; } @@ -487,7 +487,7 @@ const aiScene* Importer::ReadFileFromMemory( const void* pBuffer, unsigned int pFlags, const char* pHint /*= ""*/) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); if (!pHint) { pHint = ""; @@ -518,7 +518,7 @@ const aiScene* Importer::ReadFileFromMemory( const void* pBuffer, // ------------------------------------------------------------------------------------------------ void WriteLogOpening(const std::string& file) { - + ASSIMP_LOG_INFO("Load ", file); // print a full version dump. This is nice because we don't @@ -580,7 +580,7 @@ void WriteLogOpening(const std::string& file) { // Reads the given file and returns its contents if successful. const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); const std::string pFile(_pFile); @@ -745,7 +745,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) { // either successful or failure - the pointer expresses it anyways ASSIMP_END_EXCEPTION_REGION_WITH_ERROR_STRING(const aiScene*, pimpl->mErrorString, pimpl->mException); - + return pimpl->mScene; } @@ -754,7 +754,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) { // Apply post-processing to the currently bound scene const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); // Return immediately if no scene is active if (!pimpl->mScene) { @@ -832,7 +832,7 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) { } #endif // ! DEBUG } - pimpl->mProgressHandler->UpdatePostProcess( static_cast(pimpl->mPostProcessingSteps.size()), + pimpl->mProgressHandler->UpdatePostProcess( static_cast(pimpl->mPostProcessingSteps.size()), static_cast(pimpl->mPostProcessingSteps.size()) ); // update private scene flags @@ -845,14 +845,14 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) { ASSIMP_LOG_INFO("Leaving post processing pipeline"); ASSIMP_END_EXCEPTION_REGION(const aiScene*); - + return pimpl->mScene; } // ------------------------------------------------------------------------------------------------ const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess, bool requestValidation ) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); // Return immediately if no scene is active @@ -934,14 +934,14 @@ bool Importer::IsExtensionSupported(const char* szExtension) const { // ------------------------------------------------------------------------------------------------ size_t Importer::GetImporterCount() const { ai_assert(nullptr != pimpl); - + return pimpl->mImporter.size(); } // ------------------------------------------------------------------------------------------------ const aiImporterDesc* Importer::GetImporterInfo(size_t index) const { ai_assert(nullptr != pimpl); - + if (index >= pimpl->mImporter.size()) { return nullptr; } @@ -952,7 +952,7 @@ const aiImporterDesc* Importer::GetImporterInfo(size_t index) const { // ------------------------------------------------------------------------------------------------ BaseImporter* Importer::GetImporter (size_t index) const { ai_assert(nullptr != pimpl); - + if (index >= pimpl->mImporter.size()) { return nullptr; } @@ -963,7 +963,7 @@ BaseImporter* Importer::GetImporter (size_t index) const { // Find a loader plugin for a given file extension BaseImporter* Importer::GetImporter (const char* szExtension) const { ai_assert(nullptr != pimpl); - + return GetImporter(GetImporterIndex(szExtension)); } @@ -1002,7 +1002,7 @@ size_t Importer::GetImporterIndex (const char* szExtension) const { // Helper function to build a list of all file extensions supported by ASSIMP void Importer::GetExtensionList(aiString& szOut) const { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); std::set str; for (std::vector::const_iterator i = pimpl->mImporter.begin();i != pimpl->mImporter.end();++i) { @@ -1028,7 +1028,7 @@ void Importer::GetExtensionList(aiString& szOut) const { // Set a configuration property bool Importer::SetPropertyInteger(const char* szName, int iValue) { ai_assert(nullptr != pimpl); - + bool existing; ASSIMP_BEGIN_EXCEPTION_REGION(); existing = SetGenericProperty(pimpl->mIntProperties, szName,iValue); @@ -1040,7 +1040,7 @@ bool Importer::SetPropertyInteger(const char* szName, int iValue) { // Set a configuration property bool Importer::SetPropertyFloat(const char* szName, ai_real iValue) { ai_assert(nullptr != pimpl); - + bool existing; ASSIMP_BEGIN_EXCEPTION_REGION(); existing = SetGenericProperty(pimpl->mFloatProperties, szName,iValue); @@ -1052,7 +1052,7 @@ bool Importer::SetPropertyFloat(const char* szName, ai_real iValue) { // Set a configuration property bool Importer::SetPropertyString(const char* szName, const std::string& value) { ai_assert(nullptr != pimpl); - + bool existing; ASSIMP_BEGIN_EXCEPTION_REGION(); existing = SetGenericProperty(pimpl->mStringProperties, szName,value); @@ -1064,7 +1064,7 @@ bool Importer::SetPropertyString(const char* szName, const std::string& value) { // Set a configuration property bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) { ai_assert(nullptr != pimpl); - + bool existing; ASSIMP_BEGIN_EXCEPTION_REGION(); existing = SetGenericProperty(pimpl->mMatrixProperties, szName,value); @@ -1076,7 +1076,7 @@ bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) { // Get a configuration property int Importer::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffffffff*/) const { ai_assert(nullptr != pimpl); - + return GetGenericProperty(pimpl->mIntProperties,szName,iErrorReturn); } @@ -1084,7 +1084,7 @@ int Importer::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffff // Get a configuration property ai_real Importer::GetPropertyFloat(const char* szName, ai_real iErrorReturn /*= 10e10*/) const { ai_assert(nullptr != pimpl); - + return GetGenericProperty(pimpl->mFloatProperties,szName,iErrorReturn); } @@ -1092,7 +1092,7 @@ ai_real Importer::GetPropertyFloat(const char* szName, ai_real iErrorReturn /*= // Get a configuration property std::string Importer::GetPropertyString(const char* szName, const std::string& iErrorReturn /*= ""*/) const { ai_assert(nullptr != pimpl); - + return GetGenericProperty(pimpl->mStringProperties,szName,iErrorReturn); } @@ -1100,13 +1100,13 @@ std::string Importer::GetPropertyString(const char* szName, const std::string& i // Get a configuration property aiMatrix4x4 Importer::GetPropertyMatrix(const char* szName, const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const { ai_assert(nullptr != pimpl); - + return GetGenericProperty(pimpl->mMatrixProperties,szName,iErrorReturn); } // ------------------------------------------------------------------------------------------------ // Get the memory requirements of a single node -inline +inline void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode) { if ( nullptr == pcNode ) { return; @@ -1124,7 +1124,7 @@ void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode) { // Get the memory requirements of the scene void Importer::GetMemoryRequirements(aiMemoryInfo& in) const { ai_assert(nullptr != pimpl); - + in = aiMemoryInfo(); aiScene* mScene = pimpl->mScene; diff --git a/code/Common/Importer.h b/code/Common/Importer.h index e7da7f439..0e04f9452 100644 --- a/code/Common/Importer.h +++ b/code/Common/Importer.h @@ -183,7 +183,7 @@ public: // ------------------------------------------------------------------- /** Construct a batch loader from a given IO system to be used - * to access external files + * to access external files */ explicit BatchLoader(IOSystem* pIO, bool validate = false ); @@ -197,13 +197,13 @@ public: * @param enable True for validation. */ void setValidation( bool enabled ); - + // ------------------------------------------------------------------- /** Returns the current validation step. * @return The current validation step. */ bool getValidation() const; - + // ------------------------------------------------------------------- /** Add a new file to the list of files to be loaded. * @param file File to be loaded diff --git a/code/Common/ImporterRegistry.cpp b/code/Common/ImporterRegistry.cpp index ddfcf6798..5df096166 100644 --- a/code/Common/ImporterRegistry.cpp +++ b/code/Common/ImporterRegistry.cpp @@ -205,8 +205,8 @@ corresponding preprocessor flag to selectively disable formats. namespace Assimp { // ------------------------------------------------------------------------------------------------ -void GetImporterInstanceList(std::vector &out) { - +void GetImporterInstanceList(std::vector &out) { + // Some importers may be unimplemented or otherwise unsuitable for general use // in their current state. Devs can set ASSIMP_ENABLE_DEV_IMPORTERS in their // local environment to enable them, otherwise they're left out of the registry. diff --git a/code/Common/Win32DebugLogStream.h b/code/Common/Win32DebugLogStream.h index 3385aa8ce..51f1ab178 100644 --- a/code/Common/Win32DebugLogStream.h +++ b/code/Common/Win32DebugLogStream.h @@ -71,19 +71,19 @@ public: }; // --------------------------------------------------------------------------- -inline -Win32DebugLogStream::Win32DebugLogStream(){ +inline +Win32DebugLogStream::Win32DebugLogStream(){ // empty } // --------------------------------------------------------------------------- -inline +inline Win32DebugLogStream::~Win32DebugLogStream(){ // empty } // --------------------------------------------------------------------------- -inline +inline void Win32DebugLogStream::write(const char* message) { ::OutputDebugStringA( message); } diff --git a/code/PostProcessing/ArmaturePopulate.h b/code/PostProcessing/ArmaturePopulate.h index 08a72bd66..ded8b4886 100644 --- a/code/PostProcessing/ArmaturePopulate.h +++ b/code/PostProcessing/ArmaturePopulate.h @@ -97,7 +97,7 @@ public: static void BuildBoneList(aiNode *current_node, const aiNode *root_node, const aiScene *scene, - std::vector &bones); + std::vector &bones); static void BuildBoneStack(aiNode *current_node, const aiNode *root_node, const aiScene *scene, diff --git a/code/PostProcessing/DropFaceNormalsProcess.cpp b/code/PostProcessing/DropFaceNormalsProcess.cpp index c01ac35e5..21abf9693 100644 --- a/code/PostProcessing/DropFaceNormalsProcess.cpp +++ b/code/PostProcessing/DropFaceNormalsProcess.cpp @@ -104,7 +104,7 @@ bool DropFaceNormalsProcess::DropMeshFaceNormals (aiMesh* mesh) { if (nullptr == mesh->mNormals) { return false; } - + delete[] mesh->mNormals; mesh->mNormals = nullptr; return true; diff --git a/code/PostProcessing/EmbedTexturesProcess.cpp b/code/PostProcessing/EmbedTexturesProcess.cpp index 500032c39..d7720de98 100644 --- a/code/PostProcessing/EmbedTexturesProcess.cpp +++ b/code/PostProcessing/EmbedTexturesProcess.cpp @@ -137,7 +137,7 @@ bool EmbedTexturesProcess::addTexture(aiScene *pScene, const std::string &path) pScene->mTextures = new aiTexture*[pScene->mNumTextures]; ::memmove(pScene->mTextures, oldTextures, sizeof(aiTexture*) * (pScene->mNumTextures - 1u)); delete [] oldTextures; - + // Add the new texture auto pTexture = new aiTexture; pTexture->mHeight = 0; // Means that this is still compressed diff --git a/code/PostProcessing/FindDegenerates.cpp b/code/PostProcessing/FindDegenerates.cpp index eae91ff02..3809abf50 100644 --- a/code/PostProcessing/FindDegenerates.cpp +++ b/code/PostProcessing/FindDegenerates.cpp @@ -90,7 +90,7 @@ void FindDegeneratesProcess::Execute( aiScene* pScene) { if ( nullptr == pScene) { return; } - + std::unordered_map meshMap; meshMap.reserve(pScene->mNumMeshes); diff --git a/code/PostProcessing/FindInstancesProcess.cpp b/code/PostProcessing/FindInstancesProcess.cpp index ab5f52b78..d46afc201 100644 --- a/code/PostProcessing/FindInstancesProcess.cpp +++ b/code/PostProcessing/FindInstancesProcess.cpp @@ -137,7 +137,7 @@ void FindInstancesProcess::Execute( aiScene* pScene) aiMesh* inst = pScene->mMeshes[i]; hashes[i] = GetMeshHash(inst); - // Find an appropriate epsilon + // Find an appropriate epsilon // to compare position differences against float epsilon = ComputePositionEpsilon(inst); epsilon *= epsilon; diff --git a/code/PostProcessing/MakeVerboseFormat.h b/code/PostProcessing/MakeVerboseFormat.h index c1db76dca..4304a3afa 100644 --- a/code/PostProcessing/MakeVerboseFormat.h +++ b/code/PostProcessing/MakeVerboseFormat.h @@ -98,7 +98,7 @@ public: // ------------------------------------------------------------------- /** Checks whether the scene is already in verbose format. - * @param pScene The data to check. + * @param pScene The data to check. * @return true if the scene is already in verbose format. */ static bool IsVerboseFormat(const aiScene* pScene); diff --git a/code/PostProcessing/RemoveRedundantMaterials.cpp b/code/PostProcessing/RemoveRedundantMaterials.cpp index c252f37a5..36745fb1d 100644 --- a/code/PostProcessing/RemoveRedundantMaterials.cpp +++ b/code/PostProcessing/RemoveRedundantMaterials.cpp @@ -215,7 +215,7 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene) } else { - ASSIMP_LOG_INFO("RemoveRedundantMatsProcess finished. Removed ", redundantRemoved, " redundant and ", + ASSIMP_LOG_INFO("RemoveRedundantMatsProcess finished. Removed ", redundantRemoved, " redundant and ", unreferencedRemoved, " unused materials."); } } diff --git a/code/PostProcessing/ScaleProcess.cpp b/code/PostProcessing/ScaleProcess.cpp index 0a3e29c42..63dd0443d 100644 --- a/code/PostProcessing/ScaleProcess.cpp +++ b/code/PostProcessing/ScaleProcess.cpp @@ -75,7 +75,7 @@ void ScaleProcess::SetupProperties( const Importer* pImp ) { // File scaling * Application Scaling float importerScale = pImp->GetPropertyFloat( AI_CONFIG_APP_SCALE_KEY, 1.0f ); - // apply scale to the scale + // apply scale to the scale // helps prevent bugs with backward compatibility for anyone using normal scaling. mScale *= importerScale; } @@ -84,7 +84,7 @@ void ScaleProcess::Execute( aiScene* pScene ) { if(mScale == 1.0f) { return; // nothing to scale } - + ai_assert( mScale != 0 ); ai_assert( nullptr != pScene ); ai_assert( nullptr != pScene->mRootNode ); @@ -96,7 +96,7 @@ void ScaleProcess::Execute( aiScene* pScene ) { if ( nullptr == pScene->mRootNode ) { return; } - + // Process animations and update position transform to new unit system for( unsigned int animationID = 0; animationID < pScene->mNumAnimations; animationID++ ) { @@ -105,7 +105,7 @@ void ScaleProcess::Execute( aiScene* pScene ) { for( unsigned int animationChannel = 0; animationChannel < animation->mNumChannels; animationChannel++) { aiNodeAnim* anim = animation->mChannels[animationChannel]; - + for( unsigned int posKey = 0; posKey < anim->mNumPositionKeys; posKey++) { aiVectorKey& vectorKey = anim->mPositionKeys[posKey]; @@ -116,8 +116,8 @@ void ScaleProcess::Execute( aiScene* pScene ) { for( unsigned int meshID = 0; meshID < pScene->mNumMeshes; meshID++) { - aiMesh *mesh = pScene->mMeshes[meshID]; - + aiMesh *mesh = pScene->mMeshes[meshID]; + // Reconstruct mesh vertexes to the new unit system for( unsigned int vertexID = 0; vertexID < mesh->mNumVertices; vertexID++) { @@ -129,9 +129,9 @@ void ScaleProcess::Execute( aiScene* pScene ) { // bone placement / scaling for( unsigned int boneID = 0; boneID < mesh->mNumBones; boneID++) { - // Reconstruct matrix by transform rather than by scale + // Reconstruct matrix by transform rather than by scale // This prevent scale values being changed which can - // be meaningful in some cases + // be meaningful in some cases // like when you want the modeller to see 1:1 compatibility. aiBone* bone = mesh->mBones[boneID]; @@ -139,10 +139,10 @@ void ScaleProcess::Execute( aiScene* pScene ) { aiQuaternion rotation; bone->mOffsetMatrix.Decompose( scale, rotation, pos); - + aiMatrix4x4 translation; aiMatrix4x4::Translation( pos * mScale, translation ); - + aiMatrix4x4 scaling; aiMatrix4x4::Scaling( aiVector3D(scale), scaling ); @@ -157,7 +157,7 @@ void ScaleProcess::Execute( aiScene* pScene ) { for( unsigned int animMeshID = 0; animMeshID < mesh->mNumAnimMeshes; animMeshID++) { aiAnimMesh * animMesh = mesh->mAnimMeshes[animMeshID]; - + for( unsigned int vertexID = 0; vertexID < animMesh->mNumVertices; vertexID++) { aiVector3D& vertex = animMesh->mVertices[vertexID]; @@ -169,31 +169,31 @@ void ScaleProcess::Execute( aiScene* pScene ) { traverseNodes( pScene->mRootNode ); } -void ScaleProcess::traverseNodes( aiNode *node, unsigned int nested_node_id ) { +void ScaleProcess::traverseNodes( aiNode *node, unsigned int nested_node_id ) { applyScaling( node ); for( size_t i = 0; i < node->mNumChildren; i++) { // recurse into the tree until we are done! - traverseNodes( node->mChildren[i], nested_node_id+1 ); + traverseNodes( node->mChildren[i], nested_node_id+1 ); } } void ScaleProcess::applyScaling( aiNode *currentNode ) { if ( nullptr != currentNode ) { - // Reconstruct matrix by transform rather than by scale + // Reconstruct matrix by transform rather than by scale // This prevent scale values being changed which can - // be meaningful in some cases - // like when you want the modeller to + // be meaningful in some cases + // like when you want the modeller to // see 1:1 compatibility. - + aiVector3D pos, scale; aiQuaternion rotation; currentNode->mTransformation.Decompose( scale, rotation, pos); - + aiMatrix4x4 translation; aiMatrix4x4::Translation( pos * mScale, translation ); - + aiMatrix4x4 scaling; // note: we do not use mScale here, this is on purpose. diff --git a/code/PostProcessing/ScaleProcess.h b/code/PostProcessing/ScaleProcess.h index d88490b1c..4dc7e3559 100644 --- a/code/PostProcessing/ScaleProcess.h +++ b/code/PostProcessing/ScaleProcess.h @@ -55,8 +55,8 @@ namespace Assimp { // --------------------------------------------------------------------------- /** ScaleProcess: Class to rescale the whole model. * Now rescales animations, bones, and blend shapes properly. - * Please note this will not write to 'scale' transform it will rewrite mesh - * and matrixes so that your scale values + * Please note this will not write to 'scale' transform it will rewrite mesh + * and matrixes so that your scale values * from your model package are preserved, so this is completely intentional * bugs should be reported as soon as they are found. */ diff --git a/code/PostProcessing/SplitByBoneCountProcess.cpp b/code/PostProcessing/SplitByBoneCountProcess.cpp index 2613d8561..ed5b9411e 100644 --- a/code/PostProcessing/SplitByBoneCountProcess.cpp +++ b/code/PostProcessing/SplitByBoneCountProcess.cpp @@ -209,7 +209,7 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector newBonesAtCurrentFace; - + const aiFace& face = pMesh->mFaces[a]; // check every vertex if its bones would still fit into the current submesh for( unsigned int b = 0; b < face.mNumIndices; ++b ) @@ -221,7 +221,7 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vectormNumAnimMeshes > 0) { newMesh->mNumAnimMeshes = pMesh->mNumAnimMeshes; newMesh->mAnimMeshes = new aiAnimMesh*[newMesh->mNumAnimMeshes]; - + for (unsigned int morphIdx = 0; morphIdx < newMesh->mNumAnimMeshes; ++morphIdx) { aiAnimMesh* origTarget = pMesh->mAnimMeshes[morphIdx]; aiAnimMesh* newTarget = new aiAnimMesh; @@ -421,16 +421,16 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vectormNumVertices = numSubMeshVertices; newTarget->mVertices = new aiVector3D[numSubMeshVertices]; newMesh->mAnimMeshes[morphIdx] = newTarget; - + if (origTarget->HasNormals()) { newTarget->mNormals = new aiVector3D[numSubMeshVertices]; } - + if (origTarget->HasTangentsAndBitangents()) { newTarget->mTangents = new aiVector3D[numSubMeshVertices]; newTarget->mBitangents = new aiVector3D[numSubMeshVertices]; } - + for( unsigned int vi = 0; vi < numSubMeshVertices; ++vi) { // find the source vertex for it in the source mesh unsigned int previousIndex = previousVertexIndices[vi]; diff --git a/code/PostProcessing/SplitLargeMeshes.cpp b/code/PostProcessing/SplitLargeMeshes.cpp index ed680cf7f..cb614edaa 100644 --- a/code/PostProcessing/SplitLargeMeshes.cpp +++ b/code/PostProcessing/SplitLargeMeshes.cpp @@ -40,7 +40,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ -/** +/** * @file Implementation of the SplitLargeMeshes postprocessing step */ @@ -353,7 +353,7 @@ void SplitLargeMeshesProcess_Vertex::Execute( aiScene* pScene) { std::vector > avList; - //Check for point cloud first, + //Check for point cloud first, //Do not process point cloud, splitMesh works only with faces data for (unsigned int a = 0; a < pScene->mNumMeshes; a++) { if ( pScene->mMeshes[a]->mPrimitiveTypes == aiPrimitiveType_POINT ) { diff --git a/code/PostProcessing/TextureTransform.cpp b/code/PostProcessing/TextureTransform.cpp index 681b047c0..74b00d92c 100644 --- a/code/PostProcessing/TextureTransform.cpp +++ b/code/PostProcessing/TextureTransform.cpp @@ -448,7 +448,7 @@ void TextureTransformStep::Execute( aiScene* pScene) if (size > AI_MAX_NUMBER_OF_TEXTURECOORDS) { if (!DefaultLogger::isNullLogger()) { - ASSIMP_LOG_ERROR(static_cast(trafo.size()), " UV channels required but just ", + ASSIMP_LOG_ERROR(static_cast(trafo.size()), " UV channels required but just ", AI_MAX_NUMBER_OF_TEXTURECOORDS, " available"); } size = AI_MAX_NUMBER_OF_TEXTURECOORDS; diff --git a/code/PostProcessing/TriangulateProcess.cpp b/code/PostProcessing/TriangulateProcess.cpp index 0f71320b8..b7928ee59 100644 --- a/code/PostProcessing/TriangulateProcess.cpp +++ b/code/PostProcessing/TriangulateProcess.cpp @@ -86,11 +86,11 @@ namespace { /** * @brief Encode the current triangle, and make sure it is recognized as a triangle. - * + * * This method will rotate indices in tri if needed in order to avoid tri to be considered * part of the previous ngon. This method is to be used whenever you want to emit a real triangle, * and make sure it is seen as a triangle. - * + * * @param tri Triangle to encode. */ void ngonEncodeTriangle(aiFace * tri) { @@ -108,10 +108,10 @@ namespace { /** * @brief Encode a quad (2 triangles) in ngon encoding, and make sure they are seen as a single ngon. - * + * * @param tri1 First quad triangle * @param tri2 Second quad triangle - * + * * @pre Triangles must be properly fanned from the most appropriate vertex. */ void ngonEncodeQuad(aiFace *tri1, aiFace *tri2) { @@ -140,7 +140,7 @@ namespace { /** * @brief Check whether this triangle would be considered part of the lastly emitted ngon or not. - * + * * @param tri Current triangle. * @return true If used as is, this triangle will be part of last ngon. * @return false If used as is, this triangle is not considered part of the last ngon. @@ -512,7 +512,7 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) num = 0; break; - /*curOut -= (max-num); // undo all previous work + /*curOut -= (max-num); // undo all previous work for (tmp = 0; tmp < max-2; ++tmp) { aiFace& nface = *curOut++; diff --git a/doc/dox.h b/doc/dox.h index a4516dc7a..409e775d4 100644 --- a/doc/dox.h +++ b/doc/dox.h @@ -90,8 +90,8 @@ but not all of them are *open-source*. If there's an accompagning '\source @section main_install Installation assimp can be used in two ways: linking against the pre-built libraries or building the library on your own. The former -option is the easiest, but the assimp distribution contains pre-built libraries only for Visual C++ 2013, 2015 and 2017. -For other compilers you'll have to build assimp for yourself. Which is hopefully as hassle-free as the other way, but +option is the easiest, but the assimp distribution contains pre-built libraries only for Visual C++ 2013, 2015 and 2017. +For other compilers you'll have to build assimp for yourself. Which is hopefully as hassle-free as the other way, but needs a bit more work. Both ways are described at the @link install Installation page. @endlink If you want to use assimp on Ubuntu you can install it via the following command: @@ -145,7 +145,7 @@ to your include paths (Menu->Extras->Options->Projects and Solutions-&g and the assimp/lib/<Compiler> path to your linker paths (Menu->Extras->Options->Projects and Solutions->VC++ Directories->Library files). This is necessary only once to setup all paths inside you IDE. -To use the library in your C++ project you can simply generate a project file via cmake. One way is to add the assimp-folder +To use the library in your C++ project you can simply generate a project file via cmake. One way is to add the assimp-folder as a subdirectory via the cmake-command @code @@ -158,7 +158,7 @@ Now just add the assimp-dependency to your application: TARGET_LINK_LIBRARIES(my_game assimp) @endcode -If done correctly you should now be able to compile, link, run and use the application. +If done correctly you should now be able to compile, link, run and use the application. @section install_own Building the library from scratch @@ -170,7 +170,7 @@ to build the library just open a command-prompt / bash, navigate into the repo-f cmake CMakeLists.txt @endcode -A project-file of your default make-system ( like gnu-make on linux or Visual-Studio on Windows ) will be generated. +A project-file of your default make-system ( like gnu-make on linux or Visual-Studio on Windows ) will be generated. Run the build and you are done. You can find the libs at assimp/lib and the dll's / so's at bin. @section assimp_dll Windows DLL Build @@ -496,10 +496,10 @@ X3 Y3 Z3 T3 @endcode with (X1, X2, X3) being the local X base vector, (Y1, Y2, Y3) being the local Y base vector, (Z1, Z2, Z3) being the local Z base vector and (T1, T2, T3) being the -offset of the local origin (the translational part). +offset of the local origin (the translational part). All matrices in the library use row-major storage order. That means that the matrix elements are -stored row-by-row, i.e. they end up like this in memory: -[X1, Y1, Z1, T1, X2, Y2, Z2, T2, X3, Y3, Z3, T3, 0, 0, 0, 1]. +stored row-by-row, i.e. they end up like this in memory: +[X1, Y1, Z1, T1, X2, Y2, Z2, T2, X3, Y3, Z3, T3, 0, 0, 0, 1]. Note that this is neither the OpenGL format nor the DirectX format, because both of them specify the matrix layout such that the translational part occupies three consecutive addresses in memory (so those @@ -1498,7 +1498,7 @@ Just copy'n'paste the template from Appendix A and adapt it for your needs. with DefaultLogger::get()->[error, warn, debug, info].
  • -Make sure that your loader compiles against all build configurations on all supported platforms. You can use our CI-build to check several platforms +Make sure that your loader compiles against all build configurations on all supported platforms. You can use our CI-build to check several platforms like Windows and Linux ( 32 bit and 64 bit ).
  • diff --git a/doc/dox_cmd.h b/doc/dox_cmd.h index 78e755d56..79ea3a861 100644 --- a/doc/dox_cmd.h +++ b/doc/dox_cmd.h @@ -12,7 +12,7 @@ @section intro Introduction -This document describes the usage of assimp's command line tools. +This document describes the usage of assimp's command line tools. This is *not* the SDK reference and programming-related stuff is not covered here.

    NOTE: For simplicity, the following sections are written with Windows in mind. However @@ -29,7 +29,7 @@ assimp [command] [parameters] The following commands are available: - + @@ -184,7 +184,7 @@ Generate a text or binary dump of a model. This is the core component of Assimp' regression test suite but it could also be useful for other developers to quickly examine the contents of a model. Note that text dumps are not intended to be used as intermediate format, Assimp is not able to read them again, nor is the file format -stable or well-defined. It may change with every revision without notice. +stable or well-defined. It may change with every revision without notice. Binary dumps (*.assbin) are backwards- and forwards-compatible.

    Syntax:

    @@ -199,7 +199,7 @@ assimp dump [] [-b] [-s] [common parameters]

    model

    -Required. Relative or absolute path to the input model. +Required. Relative or absolute path to the input model.

    @@ -220,7 +220,7 @@ The long form of this parameter is --binary.
    Optional. If this switch is specified, the dump is shortened to include only min/max values for all vertex components and animation channels. The resulting -file is much smaller, but the original model can't be reconstructed from it. This is +file is much smaller, but the original model can't be reconstructed from it. This is used by Assimp's regression test suite, comparing those minidumps provides a fast way to verify whether a loader works correctly or not. The long form of this parameter is --short. @@ -229,7 +229,7 @@ The long form of this parameter is --short.

    common parameters

    -Optional. Import configuration & postprocessing. +Optional. Import configuration & postprocessing. See the @link common common parameters page @endlink for more information.

    @@ -248,7 +248,7 @@ The log output is included with the dump. @code assimp dump files\*.* -assimp dump files\*.* +assimp dump files\*.* @endcode Dumps all loadable model files in the 'files' subdir. The output dumps are named @@ -275,14 +275,14 @@ assimp extract [] [-t] [-f] [-ba] [-s] [common parameters]

    model

    -Required. Relative or absolute path to the input model. +Required. Relative or absolute path to the input model.

    out

    Optional. Relative or absolute path to write the output images to. If the file name is omitted the output images are named
    -The suffix _img<n> is appended to the file name if the -s switch is not specified +The suffix _img<n> is appended to the file name if the -s switch is not specified (where <n> is the zero-based index of the texture in the model file).
    The output file format is determined from the given file extension. Supported @@ -296,7 +296,7 @@ written in their native file format (e.g. jpg).

    -t<n>

    -Optional. Specifies the (zero-based) index of the embedded texture to be extracted from +Optional. Specifies the (zero-based) index of the embedded texture to be extracted from the model. If this option is *not* specified all textures found are exported. The long form of this parameter is --texture=<n>.

    @@ -348,8 +348,8 @@ imported data structure and writes it to test_img0.bmp. @code -assimp extract files\*.mdl *.bmp -assimp extract files\*.mdl *.bmp +assimp extract files\*.mdl *.bmp +assimp extract files\*.mdl *.bmp @endcode Extracts all embedded textures from all loadable .mdl files in the 'files' subdirectory @@ -361,10 +361,10 @@ and writes them to bitmaps which are named _img.bmp /** @page common Common parameters -The parameters described on this page are commonly used by almost every assimp command. They +The parameters described on this page are commonly used by almost every assimp command. They specify how the library will postprocess the imported data. This is done by several configurable pipeline stages, called 'post processing steps'. Below you can find a list -of all supported steps along with short descriptions of what they're doing.
    Programmers: +of all supported steps along with short descriptions of what they're doing.
    Programmers: more information can be found in the aiPostProcess.h header.
    @link version version @endlink Retrieve the current version of assimp
    @@ -376,7 +376,7 @@ more information can be found in the aiPostProcess.h header. - @@ -428,7 +428,7 @@ more information can be found in the aiPostProcess.h header. - @@ -515,7 +515,7 @@ For convenience some default postprocessing configurations are provided. The corresponding command line parameter is -c<name> (or --config=<name>).
    -ptv --pretransform-verticesMove all vertices into worldspace and collapse the scene graph. Animation data is lost. + Move all vertices into worldspace and collapse the scene graph. Animation data is lost. This is intended for applications which don't support scenegraph-oriented rendering.
    -icl --improve-cache-localityImprove the cache locality of the vertex buffer by reordering the index buffer + Improve the cache locality of the vertex buffer by reordering the index buffer to achieve a lower ACMR (average post-transform vertex cache miss ratio)
    - + @@ -543,7 +543,7 @@ The corresponding command line parameter is -c<name> (or --co There are also some common flags to customize Assimp's logging behaviour:
    Name Description
    - + @@ -558,7 +558,7 @@ There are also some common flags to customize Assimp's logging behaviour: -
    Name Description
    -v or --verboseEnables verbose logging. Debug messages will be produced too. This might + Enables verbose logging. Debug messages will be produced too. This might decrease loading performance and result in *very* long logs ... use with caution if you experience strange issues.
    diff --git a/fuzz/assimp_fuzzer.cc b/fuzz/assimp_fuzzer.cc index b65ee0236..33748c10f 100644 --- a/fuzz/assimp_fuzzer.cc +++ b/fuzz/assimp_fuzzer.cc @@ -54,6 +54,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataSize) { aiProcessPreset_TargetRealtime_Quality, nullptr ); aiDetachLogStream(&stream); - + return 0; } diff --git a/include/assimp/BaseImporter.h b/include/assimp/BaseImporter.h index 38bec1afd..54b5daac1 100644 --- a/include/assimp/BaseImporter.h +++ b/include/assimp/BaseImporter.h @@ -154,7 +154,7 @@ public: /** Returns the exception of the last exception that occurred. * Note: Exceptions are not the only source of error details, so GetErrorText * should be consulted too. - * @return The last exception that occurred. + * @return The last exception that occurred. */ const std::exception_ptr& GetException() const { return m_Exception; diff --git a/include/assimp/Compiler/poppack1.h b/include/assimp/Compiler/poppack1.h index a8e9a3c7e..ff501bc0c 100644 --- a/include/assimp/Compiler/poppack1.h +++ b/include/assimp/Compiler/poppack1.h @@ -1,7 +1,7 @@ // =============================================================================== -// May be included multiple times - resets structure packing to the defaults -// for all supported compilers. Reverts the changes made by #include +// May be included multiple times - resets structure packing to the defaults +// for all supported compilers. Reverts the changes made by #include // // Currently this works on the following compilers: // MSVC 7,8,9 diff --git a/include/assimp/Compiler/pushpack1.h b/include/assimp/Compiler/pushpack1.h index 2a5e2dfe6..b32ed172c 100644 --- a/include/assimp/Compiler/pushpack1.h +++ b/include/assimp/Compiler/pushpack1.h @@ -1,7 +1,7 @@ // =============================================================================== -// May be included multiple times - sets structure packing to 1 +// May be included multiple times - sets structure packing to 1 // for all supported compilers. #include reverts the changes. // // Currently this works on the following compilers: @@ -37,7 +37,7 @@ #if defined(_MSC_VER) // C4103: Packing was changed after the inclusion of the header, probably missing #pragma pop -# pragma warning (disable : 4103) +# pragma warning (disable : 4103) #endif #define AI_PUSHPACK_IS_DEFINED diff --git a/include/assimp/Exceptional.h b/include/assimp/Exceptional.h index 98e2a3321..1bf399cbc 100644 --- a/include/assimp/Exceptional.h +++ b/include/assimp/Exceptional.h @@ -59,7 +59,7 @@ using std::runtime_error; class ASSIMP_API DeadlyErrorBase : public runtime_error { protected: DeadlyErrorBase(Assimp::Formatter::format f); - + template DeadlyErrorBase(Assimp::Formatter::format f, U&& u, T&&... args) : DeadlyErrorBase(std::move(f << std::forward(u)), std::forward(args)...) {} diff --git a/include/assimp/Exporter.hpp b/include/assimp/Exporter.hpp index 8e78ac954..6ab35a8f0 100644 --- a/include/assimp/Exporter.hpp +++ b/include/assimp/Exporter.hpp @@ -390,7 +390,7 @@ public: * @see SetPropertyInteger() */ bool SetPropertyMatrix(const char *szName, const aiMatrix4x4 &sValue); - + bool SetPropertyCallback(const char *szName, const std::function &f); // ------------------------------------------------------------------- diff --git a/include/assimp/IOStreamBuffer.h b/include/assimp/IOStreamBuffer.h index ee4ac0953..c3b7327ff 100644 --- a/include/assimp/IOStreamBuffer.h +++ b/include/assimp/IOStreamBuffer.h @@ -81,7 +81,7 @@ public: /// @brief Returns the file-size. /// @return The file-size. size_t size() const; - + /// @brief Returns the cache size. /// @return The cache size. size_t cacheSize() const; @@ -278,7 +278,7 @@ bool IOStreamBuffer::getNextDataLine( std::vector &buffer, T continuationT } } } - + buffer[ i ] = '\n'; ++m_cachePos; @@ -334,7 +334,7 @@ template AI_FORCE_INLINE bool IOStreamBuffer::getNextBlock( std::vector &buffer) { // Return the last block-value if getNextLine was used before - if ( 0 != m_cachePos ) { + if ( 0 != m_cachePos ) { buffer = std::vector( m_cache.begin() + m_cachePos, m_cache.end() ); m_cachePos = 0; } else { diff --git a/include/assimp/IOSystem.hpp b/include/assimp/IOSystem.hpp index 76f876440..7be373cf1 100644 --- a/include/assimp/IOSystem.hpp +++ b/include/assimp/IOSystem.hpp @@ -62,9 +62,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "types.h" #ifdef _WIN32 -# include -# include -# include +# include +# include +# include #else # include # include @@ -84,7 +84,7 @@ namespace Assimp { * to the importer library. If you implement this interface, you also want to * supply a custom implementation for IOStream. * - * @see Importer::SetIOHandler() + * @see Importer::SetIOHandler() */ class ASSIMP_API IOSystem #ifndef SWIG diff --git a/include/assimp/Logger.hpp b/include/assimp/Logger.hpp index 3ca4a6cb2..85204c88d 100644 --- a/include/assimp/Logger.hpp +++ b/include/assimp/Logger.hpp @@ -112,7 +112,7 @@ public: /** @brief Writes a debug message * @param message Debug message*/ void verboseDebug(const char* message); - + template void verboseDebug(T&&... args) { verboseDebug(formatMessage(std::forward(args)...).c_str()); diff --git a/include/assimp/MemoryIOWrapper.h b/include/assimp/MemoryIOWrapper.h index 86dc5ea60..24f6a85d1 100644 --- a/include/assimp/MemoryIOWrapper.h +++ b/include/assimp/MemoryIOWrapper.h @@ -57,7 +57,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include namespace Assimp { - + #define AI_MEMORYIO_MAGIC_FILENAME "$$$___magic___$$$" #define AI_MEMORYIO_MAGIC_FILENAME_LENGTH 17 @@ -85,7 +85,7 @@ public: size_t Read(void* pvBuffer, size_t pSize, size_t pCount) { ai_assert(nullptr != pvBuffer); ai_assert(0 != pSize); - + const size_t cnt = std::min( pCount, (length-pos) / pSize); const size_t ofs = pSize * cnt; @@ -209,7 +209,7 @@ public: return existing_io ? existing_io->ComparePaths(one, second) : false; } - bool PushDirectory( const std::string &path ) override { + bool PushDirectory( const std::string &path ) override { return existing_io ? existing_io->PushDirectory(path) : false; } diff --git a/include/assimp/SmallVector.h b/include/assimp/SmallVector.h index bcb8482a9..fb78f5a97 100644 --- a/include/assimp/SmallVector.h +++ b/include/assimp/SmallVector.h @@ -53,17 +53,17 @@ Based on CppCon 2016: Chandler Carruth "High Performance Code 201: Hybrid Data S namespace Assimp { // -------------------------------------------------------------------------------------------- -/// @brief Small vector with inplace storage. +/// @brief Small vector with inplace storage. /// /// Reduces heap allocations when list is shorter. It uses a small array for a dedicated size. -/// When the growing gets bigger than this small cache a dynamic growing algorithm will be +/// When the growing gets bigger than this small cache a dynamic growing algorithm will be /// used. // -------------------------------------------------------------------------------------------- template class SmallVector { public: /// @brief The default class constructor. - SmallVector() : + SmallVector() : mStorage(mInplaceStorage), mSize(0), mCapacity(Capacity) { @@ -84,7 +84,7 @@ public: mStorage[mSize++] = item; return; } - + push_back_and_grow(item); } diff --git a/include/assimp/SmoothingGroups.inl b/include/assimp/SmoothingGroups.inl index a32626e00..08c2dfa53 100644 --- a/include/assimp/SmoothingGroups.inl +++ b/include/assimp/SmoothingGroups.inl @@ -7,8 +7,8 @@ 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 +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 @@ -25,16 +25,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ @@ -77,7 +77,7 @@ void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups& sMesh) sMesh.mNormals[face.mIndices[c]] = vNor; } - // calculate the position bounds so we have a reliable epsilon to check position differences against + // calculate the position bounds so we have a reliable epsilon to check position differences against aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f); for( unsigned int a = 0; a < sMesh.mPositions.size(); a++) { @@ -91,7 +91,7 @@ void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups& sMesh) const float posEpsilon = (maxVec - minVec).Length() * 1e-5f; std::vector avNormals; avNormals.resize(sMesh.mNormals.size()); - + // now generate the spatial sort tree SGSpatialSort sSort; for( typename std::vector::iterator i = sMesh.mFaces.begin(); diff --git a/include/assimp/XmlParser.h b/include/assimp/XmlParser.h index 18d48f337..9c2dc419e 100644 --- a/include/assimp/XmlParser.h +++ b/include/assimp/XmlParser.h @@ -94,7 +94,7 @@ using XmlAttribute = pugi::xml_attribute; /// } /// } /// @endcode -/// @tparam TNodeType +/// @tparam TNodeType template class TXmlParser { public: @@ -123,7 +123,7 @@ public: /// @brief Will search for a child-node by its name /// @param name [in] The name of the child-node. - /// @return The node instance or nullptr, if nothing was found. + /// @return The node instance or nullptr, if nothing was found. TNodeType *findNode(const std::string &name) { if (name.empty()) { return nullptr; @@ -162,12 +162,12 @@ public: mData.resize(len + 1); memset(&mData[0], '\0', len + 1); stream->Read(&mData[0], 1, len); - + mDoc = new pugi::xml_document(); pugi::xml_parse_result parse_result = mDoc->load_string(&mData[0], pugi::parse_full); if (parse_result.status == pugi::status_ok) { return true; - } + } ASSIMP_LOG_DEBUG("Error while parse xml.", std::string(parse_result.description()), " @ ", parse_result.offset); @@ -457,7 +457,7 @@ public: } private: - XmlNode &mParent; + XmlNode &mParent; std::vector mNodes; size_t mIndex; }; diff --git a/include/assimp/ai_assert.h b/include/assimp/ai_assert.h index 6736b24c9..b377b6e8b 100644 --- a/include/assimp/ai_assert.h +++ b/include/assimp/ai_assert.h @@ -57,7 +57,7 @@ namespace Assimp #else # define ai_assert(expression) -# define ai_assert_entry() +# define ai_assert_entry() #endif // ASSIMP_BUILD_DEBUG #endif // AI_ASSERT_H_INC diff --git a/include/assimp/anim.h b/include/assimp/anim.h index 79841a537..dcd054d1e 100644 --- a/include/assimp/anim.h +++ b/include/assimp/anim.h @@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** +/** * @file anim.h * @brief Defines the data structures in which the imported animations * are returned. @@ -478,11 +478,11 @@ struct aiAnimation { namespace Assimp { // --------------------------------------------------------------------------- -/** +/** * @brief CPP-API: Utility class to simplify interpolations of various data types. * * The type of interpolation is chosen automatically depending on the - * types of the arguments. + * types of the arguments. */ template struct Interpolator { diff --git a/include/assimp/cimport.h b/include/assimp/cimport.h index b4a172742..d660eebb1 100644 --- a/include/assimp/cimport.h +++ b/include/assimp/cimport.h @@ -894,7 +894,7 @@ ASSIMP_API float aiMatrix3Determinant( // -------------------------------------------------------------------------------- /** Get a 3x3 rotation matrix around the Z axis. - * @param mat Receives the output matrix + * @param mat Receives the output matrix * @param angle Rotation angle, in radians */ ASSIMP_API void aiMatrix3RotationZ( @@ -903,7 +903,7 @@ ASSIMP_API void aiMatrix3RotationZ( // -------------------------------------------------------------------------------- /** Returns a 3x3 rotation matrix for a rotation around an arbitrary axis. - * @param mat Receives the output matrix + * @param mat Receives the output matrix * @param axis Rotation axis, should be a normalized vector * @param angle Rotation angle, in radians */ @@ -914,7 +914,7 @@ ASSIMP_API void aiMatrix3FromRotationAroundAxis( // -------------------------------------------------------------------------------- /** Get a 3x3 translation matrix. - * @param mat Receives the output matrix + * @param mat Receives the output matrix * @param translation The translation vector */ ASSIMP_API void aiMatrix3Translation( @@ -923,7 +923,7 @@ ASSIMP_API void aiMatrix3Translation( // -------------------------------------------------------------------------------- /** Create a 3x3 matrix that rotates one vector to another vector. - * @param mat Receives the output matrix + * @param mat Receives the output matrix * @param from Vector to rotate from * @param to Vector to rotate to */ @@ -1059,7 +1059,7 @@ ASSIMP_API void aiMatrix4DecomposeNoScaling( // -------------------------------------------------------------------------------- /** Creates a 4x4 matrix from a set of euler angles. - * @param mat Receives the output matrix + * @param mat Receives the output matrix * @param x Rotation angle for the x-axis, in radians * @param y Rotation angle for the y-axis, in radians * @param z Rotation angle for the z-axis, in radians @@ -1137,7 +1137,7 @@ ASSIMP_API void aiMatrix4FromTo( // -------------------------------------------------------------------------------- /** Create a Quaternion from euler angles. - * @param q Receives the output quaternion + * @param q Receives the output quaternion * @param x Rotation angle for the x-axis, in radians * @param y Rotation angle for the y-axis, in radians * @param z Rotation angle for the z-axis, in radians diff --git a/include/assimp/defs.h b/include/assimp/defs.h index d61fd7901..8d1011a28 100644 --- a/include/assimp/defs.h +++ b/include/assimp/defs.h @@ -161,7 +161,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma warning(disable : 4251) #endif /* Force the compiler to inline a function, if possible */ - #define AI_FORCE_INLINE inline + #define AI_FORCE_INLINE inline /* Tells the compiler that a function never returns. Used in code analysis * to skip dead paths (e.g. after an assertion evaluated to false). */ diff --git a/include/assimp/light.h b/include/assimp/light.h index bc37de43a..48efdaebd 100644 --- a/include/assimp/light.h +++ b/include/assimp/light.h @@ -257,7 +257,7 @@ struct aiLight #ifdef __cplusplus } -#endif +#endif #endif // !! AI_LIGHT_H_INC diff --git a/include/assimp/material.h b/include/assimp/material.h index f0207c6de..250ad90d5 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -194,8 +194,8 @@ enum aiTextureType { */ aiTextureType_NONE = 0, - /** LEGACY API MATERIALS - * Legacy refers to materials which + /** LEGACY API MATERIALS + * Legacy refers to materials which * Were originally implemented in the specifications around 2000. * These must never be removed, as most engines support them. */ @@ -339,9 +339,9 @@ ASSIMP_API const char *TextureTypeToString(enum aiTextureType in); // --------------------------------------------------------------------------- /** @brief Defines all shading models supported by the library - * + * * Property: #AI_MATKEY_SHADING_MODEL - * + * * The list of shading modes has been taken from Blender. * See Blender documentation for more information. The API does * not distinguish between "specular" and "diffuse" shaders (thus the diff --git a/include/assimp/matrix4x4.h b/include/assimp/matrix4x4.h index c929ef2a9..6caf7686c 100644 --- a/include/assimp/matrix4x4.h +++ b/include/assimp/matrix4x4.h @@ -230,7 +230,7 @@ public: * @param out Receives the output matrix * @return Reference to the output matrix */ - static aiMatrix4x4t& Translation( const aiVector3t& v, + static aiMatrix4x4t& Translation( const aiVector3t& v, aiMatrix4x4t& out); // ------------------------------------------------------------------- diff --git a/include/assimp/matrix4x4.inl b/include/assimp/matrix4x4.inl index 88315ac28..c1dd87b65 100644 --- a/include/assimp/matrix4x4.inl +++ b/include/assimp/matrix4x4.inl @@ -421,7 +421,7 @@ void aiMatrix4x4t::Decompose(aiVector3t& pScaling, aiVector3tmArmature and aiBone->mNode generically * The point of these is it saves you later having to calculate these elements * This is useful when handling rest information or skin information - * If you have multiple armatures on your models we strongly recommend enabling this - * Instead of writing your own multi-root, multi-armature lookups we have done the + * If you have multiple armatures on your models we strongly recommend enabling this + * Instead of writing your own multi-root, multi-armature lookups we have done the * hard work for you :) */ aiProcess_PopulateArmatureData = 0x4000, @@ -579,7 +579,7 @@ enum aiPostProcessSteps * of the imported model. And if so, it uses that. */ aiProcess_EmbedTextures = 0x10000000, - + // aiProcess_GenEntityMeshes = 0x100000, // aiProcess_OptimizeAnimations = 0x200000 // aiProcess_FixTexturePaths = 0x200000 diff --git a/include/assimp/scene.h b/include/assimp/scene.h index 522ddc6dc..25858ac38 100644 --- a/include/assimp/scene.h +++ b/include/assimp/scene.h @@ -70,7 +70,7 @@ extern "C" { #endif // ------------------------------------------------------------------------------- -/** +/** * A node in the imported hierarchy. * * Each node has name, a parent node (except for the root node), @@ -149,12 +149,12 @@ struct ASSIMP_API aiNode * @param name Name to search for * @return nullptr or a valid Node if the search was successful. */ - inline + inline const aiNode* FindNode(const aiString& name) const { return FindNode(name.data); } - inline + inline aiNode* FindNode(const aiString& name) { return FindNode(name.data); } @@ -353,34 +353,34 @@ struct aiScene //! Check whether the scene contains meshes //! Unless no special scene flags are set this will always be true. - inline bool HasMeshes() const { - return mMeshes != nullptr && mNumMeshes > 0; + inline bool HasMeshes() const { + return mMeshes != nullptr && mNumMeshes > 0; } //! Check whether the scene contains materials //! Unless no special scene flags are set this will always be true. - inline bool HasMaterials() const { - return mMaterials != nullptr && mNumMaterials > 0; + inline bool HasMaterials() const { + return mMaterials != nullptr && mNumMaterials > 0; } //! Check whether the scene contains lights - inline bool HasLights() const { - return mLights != nullptr && mNumLights > 0; + inline bool HasLights() const { + return mLights != nullptr && mNumLights > 0; } //! Check whether the scene contains textures inline bool HasTextures() const { - return mTextures != nullptr && mNumTextures > 0; + return mTextures != nullptr && mNumTextures > 0; } //! Check whether the scene contains cameras inline bool HasCameras() const { - return mCameras != nullptr && mNumCameras > 0; + return mCameras != nullptr && mNumCameras > 0; } //! Check whether the scene contains animations - inline bool HasAnimations() const { - return mAnimations != nullptr && mNumAnimations > 0; + inline bool HasAnimations() const { + return mAnimations != nullptr && mNumAnimations > 0; } //! Returns a short filename from a full path diff --git a/include/assimp/vector2.inl b/include/assimp/vector2.inl index 0ca440e72..b51dd0ec2 100644 --- a/include/assimp/vector2.inl +++ b/include/assimp/vector2.inl @@ -190,7 +190,7 @@ aiVector2t operator + (const aiVector2t& v1, const aiVector2t -inline +inline aiVector2t operator - (const aiVector2t& v1, const aiVector2t& v2) { return aiVector2t( v1.x - v2.x, v1.y - v2.y); } @@ -198,7 +198,7 @@ aiVector2t operator - (const aiVector2t& v1, const aiVector2t -inline +inline TReal operator * (const aiVector2t& v1, const aiVector2t& v2) { return v1.x*v2.x + v1.y*v2.y; } @@ -206,7 +206,7 @@ TReal operator * (const aiVector2t& v1, const aiVector2t& v2) { // ------------------------------------------------------------------------------------------------ // scalar multiplication template -inline +inline aiVector2t operator * ( TReal f, const aiVector2t& v) { return aiVector2t( f*v.x, f*v.y); } @@ -214,7 +214,7 @@ aiVector2t operator * ( TReal f, const aiVector2t& v) { // ------------------------------------------------------------------------------------------------ // and the other way around template -inline +inline aiVector2t operator * ( const aiVector2t& v, TReal f) { return aiVector2t( f*v.x, f*v.y); } @@ -222,7 +222,7 @@ aiVector2t operator * ( const aiVector2t& v, TReal f) { // ------------------------------------------------------------------------------------------------ // scalar division template -inline +inline aiVector2t operator / ( const aiVector2t& v, TReal f) { return v * (1/f); } @@ -230,7 +230,7 @@ aiVector2t operator / ( const aiVector2t& v, TReal f) { // ------------------------------------------------------------------------------------------------ // vector division template -inline +inline aiVector2t operator / ( const aiVector2t& v, const aiVector2t& v2) { return aiVector2t(v.x / v2.x,v.y / v2.y); } @@ -238,7 +238,7 @@ aiVector2t operator / ( const aiVector2t& v, const aiVector2t -inline +inline aiVector2t operator - ( const aiVector2t& v) { return aiVector2t( -v.x, -v.y); } diff --git a/packaging/windows-innosetup/readme_installer.txt b/packaging/windows-innosetup/readme_installer.txt index 6ea969dc1..252c396af 100644 --- a/packaging/windows-innosetup/readme_installer.txt +++ b/packaging/windows-innosetup/readme_installer.txt @@ -10,7 +10,7 @@ http://assimp.sf.net Troubleshooting =============== -1. Missing d3dx9_(some-number).dll? +1. Missing d3dx9_(some-number).dll? Install the latest DirectX runtime or grab the file from somewhere (that's evil but mostly fine). 2. Application configuration not correct / missing msvcr***.dll? diff --git a/packaging/windows-innosetup/readme_installer_vieweronly.txt b/packaging/windows-innosetup/readme_installer_vieweronly.txt index 1e84c577d..0acd6ef52 100644 --- a/packaging/windows-innosetup/readme_installer_vieweronly.txt +++ b/packaging/windows-innosetup/readme_installer_vieweronly.txt @@ -19,7 +19,7 @@ Viewer Troubleshooting =============== -1. Missing d3dx9_(number).dll? +1. Missing d3dx9_(number).dll? Install the latest DirectX runtime or grab the file from somewhere (that's evil but mostly fine). 2. Application configuration not correct / missing msvcr***.dll? diff --git a/packaging/windows-mkzip/bin_readme.txt b/packaging/windows-mkzip/bin_readme.txt index 5cff1f30e..f4402d6bf 100644 --- a/packaging/windows-mkzip/bin_readme.txt +++ b/packaging/windows-mkzip/bin_readme.txt @@ -19,7 +19,7 @@ Viewer Troubleshooting =============== -1. Missing d3dx9_42.dll? +1. Missing d3dx9_42.dll? Install the latest DirectX runtime or grab the file from somewhere (that's evil but mostly fine). 2. Application configuration not correct / missing msv*** DLLs? diff --git a/port/AndroidJNI/CMakeLists.txt b/port/AndroidJNI/CMakeLists.txt index 43e842848..8c821c72a 100644 --- a/port/AndroidJNI/CMakeLists.txt +++ b/port/AndroidJNI/CMakeLists.txt @@ -3,10 +3,10 @@ cmake_minimum_required(VERSION 3.10) include_directories(./) include_directories(./../../) add_library( # Defines the name of the library. - android_jniiosystem + android_jniiosystem # Implements a static library. - STATIC + STATIC # relative path to source file(s). AndroidJNIIOSystem.cpp diff --git a/port/AssimpDelphi/Readme.txt b/port/AssimpDelphi/Readme.txt index 07d6935ae..1ec6d2109 100644 --- a/port/AssimpDelphi/Readme.txt +++ b/port/AssimpDelphi/Readme.txt @@ -1,6 +1,6 @@ This is a set of Delphi units for using the Assimp C DLL. This was created for use with Delphi 7, but should be usable as-is or with minimal modifications with later Delphi versions. -This set of headers is enough to load and display a model with external textures. Since I'm not familiar with animated models and some of the other functionality of the assimp library, I did not convert the headers for those features. +This set of headers is enough to load and display a model with external textures. Since I'm not familiar with animated models and some of the other functionality of the assimp library, I did not convert the headers for those features. See http://sourceforge.net/tracker/?func=detail&aid=3212646&group_id=226462&atid=1067634 for the original patch diff --git a/port/jassimp/jassimp-native/src/jassimp.cpp b/port/jassimp/jassimp-native/src/jassimp.cpp index 0cf01b1e3..6661ce9c4 100644 --- a/port/jassimp/jassimp-native/src/jassimp.cpp +++ b/port/jassimp/jassimp-native/src/jassimp.cpp @@ -15,7 +15,7 @@ #define lprintf(...) printf (__VA_ARGS__) #endif /* ANDROID */ #else -#define lprintf +#define lprintf #endif static std::string gLastErrorString; @@ -63,7 +63,7 @@ static bool createInstance(JNIEnv *env, const char* className, jobject& newInsta newInstance = env->NewObject(clazz, ctr_id); - if (NULL == newInstance) + if (NULL == newInstance) { lprintf("error calling no-arg constructor for class %s\n", className); return false; @@ -94,7 +94,7 @@ static bool createInstance(JNIEnv *env, const char* className, const char* signa newInstance = env->NewObjectA(clazz, ctr_id, params); - if (NULL == newInstance) + if (NULL == newInstance) { lprintf("error calling constructor for class %s, signature %s\n", className, signature); return false; @@ -229,7 +229,7 @@ static bool getStaticField(JNIEnv *env, const char* className, const char* field } -static bool call(JNIEnv *env, jobject object, const char* typeName, const char* methodName, +static bool call(JNIEnv *env, jobject object, const char* typeName, const char* methodName, const char* signature,/* const*/ jvalue* params) { jclass clazz = env->FindClass(typeName); @@ -275,7 +275,7 @@ static bool callv(JNIEnv *env, jobject object, const char* typeName, return true; } -static jobject callo(JNIEnv *env, jobject object, const char* typeName, const char* methodName, +static jobject callo(JNIEnv *env, jobject object, const char* typeName, const char* methodName, const char* signature,/* const*/ jvalue* params) { jclass clazz = env->FindClass(typeName); @@ -300,7 +300,7 @@ static jobject callo(JNIEnv *env, jobject object, const char* typeName, const ch return jReturnValue; } -static int calli(JNIEnv *env, jobject object, const char* typeName, const char* methodName, +static int calli(JNIEnv *env, jobject object, const char* typeName, const char* methodName, const char* signature) { jclass clazz = env->FindClass(typeName); @@ -325,7 +325,7 @@ static int calli(JNIEnv *env, jobject object, const char* typeName, const char* return (int) jReturnValue; } -static int callc(JNIEnv *env, jobject object, const char* typeName, const char* methodName, +static int callc(JNIEnv *env, jobject object, const char* typeName, const char* methodName, const char* signature) { jclass clazz = env->FindClass(typeName); @@ -351,7 +351,7 @@ static int callc(JNIEnv *env, jobject object, const char* typeName, const char* } -static bool callStaticObject(JNIEnv *env, const char* typeName, const char* methodName, +static bool callStaticObject(JNIEnv *env, const char* typeName, const char* methodName, const char* signature,/* const*/ jvalue* params, jobject& returnValue) { jclass clazz = env->FindClass(typeName); @@ -441,13 +441,13 @@ static bool copyBufferArray(JNIEnv *env, jobject jMesh, const char* jBufferName, class JavaIOStream : public Assimp::IOStream { -private: +private: size_t pos; size_t size; char* buffer; jobject jIOStream; - + public: JavaIOStream(size_t size, char* buffer, jobject jIOStream) : pos(0), @@ -455,28 +455,28 @@ public: buffer(buffer), jIOStream(jIOStream) {}; - - - ~JavaIOStream(void) + + + ~JavaIOStream(void) { free(buffer); - }; + }; size_t Read(void* pvBuffer, size_t pSize, size_t pCount) { const size_t cnt = std::min(pCount,(size - pos)/pSize); const size_t ofs = pSize*cnt; - + memcpy(pvBuffer, buffer + pos, ofs); pos += ofs; - + return cnt; }; - size_t Write(const void* pvBuffer, size_t pSize, size_t pCount) + size_t Write(const void* pvBuffer, size_t pSize, size_t pCount) { return 0; }; - + aiReturn Seek(size_t pOffset, aiOrigin pOrigin) { if (aiOrigin_SET == pOrigin) { @@ -499,40 +499,40 @@ public: } return AI_SUCCESS; }; - + size_t Tell(void) const { return pos; }; - + size_t FileSize() const { return size; }; - + void Flush() {}; - - + + jobject javaObject() { return jIOStream; }; - - + + }; - + class JavaIOSystem : public Assimp::IOSystem { private: JNIEnv* mJniEnv; jobject& mJavaIOSystem; - + public: JavaIOSystem(JNIEnv* env, jobject& javaIOSystem) : mJniEnv(env), mJavaIOSystem(javaIOSystem) {}; - + bool Exists( const char* pFile) const { jvalue params[1]; @@ -544,27 +544,27 @@ class JavaIOSystem : public Assimp::IOSystem { { return (char) callc(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "getOsSeparator", "()C"); }; - + Assimp::IOStream* Open(const char* pFile,const char* pMode = "rb") { jvalue params[2]; params[0].l = mJniEnv->NewStringUTF(pFile); params[1].l = mJniEnv->NewStringUTF(pMode); - - + + jobject jStream = callo(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "open", "(Ljava/lang/String;Ljava/lang/String;)Ljassimp/AiIOStream;", params); if(NULL == jStream) { lprintf("NULL object from AiIOSystem.open\n"); return NULL; } - + size_t size = calli(mJniEnv, jStream, "jassimp/AiIOStream", "getFileSize", "()I"); lprintf("Model file size is %d\n", size); - + char* buffer = (char*)malloc(size); jobject javaBuffer = mJniEnv->NewDirectByteBuffer(buffer, size); - + jvalue readParams[1]; readParams[0].l = javaBuffer; if(call(mJniEnv, jStream, "jassimp/AiIOStream", "read", "(Ljava/nio/ByteBuffer;)Z", readParams)) @@ -581,28 +581,28 @@ class JavaIOSystem : public Assimp::IOSystem { }; void Close( Assimp::IOStream* pFile) { - + jvalue params[1]; params[0].l = ((JavaIOStream*) pFile)->javaObject(); callv(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "close", "(Ljassimp/AiIOStream;)V", params); delete pFile; }; - - + + }; class JavaProgressHandler : public Assimp::ProgressHandler { private: JNIEnv* mJniEnv; jobject& mJavaProgressHandler; - + public: JavaProgressHandler(JNIEnv* env, jobject& javaProgressHandler) : mJniEnv(env), mJavaProgressHandler(javaProgressHandler) {}; - + bool Update(float percentage) { jvalue params[1]; @@ -623,12 +623,12 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) jobject jMesh = NULL; SmartLocalRef refMesh(env, jMesh); - if (!createInstance(env, "jassimp/AiMesh", jMesh)) + if (!createInstance(env, "jassimp/AiMesh", jMesh)) { return false; } - + /* add mesh to m_meshes java.util.List */ jobject jMeshes = NULL; SmartLocalRef refMeshes(env, jMeshes); @@ -671,7 +671,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) /* determine face buffer size */ bool isPureTriangle = cMesh->mPrimitiveTypes == aiPrimitiveType_TRIANGLE; size_t faceBufferSize; - if (isPureTriangle) + if (isPureTriangle) { faceBufferSize = cMesh->mNumFaces * 3 * sizeof(unsigned int); } @@ -715,7 +715,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) /* push face data to java */ if (cMesh->mNumFaces > 0) { - if (isPureTriangle) + if (isPureTriangle) { char* faceBuffer = (char*) malloc(faceBufferSize); @@ -729,7 +729,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) free(faceBuffer); - if (!res) + if (!res) { lprintf("could not copy face data\n"); return false; @@ -750,7 +750,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) memcpy(faceBuffer + faceBufferPos, cMesh->mFaces[face].mIndices, faceDataSize); faceBufferPos += faceDataSize; } - + if (faceBufferPos != faceBufferSize) { /* this should really not happen */ @@ -766,7 +766,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) free(faceBuffer); free(offsetBuffer); - if (!res) + if (!res) { lprintf("could not copy face data\n"); return false; @@ -933,7 +933,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) jobject jBone; SmartLocalRef refBone(env, jBone); - if (!createInstance(env, "jassimp/AiBone", jBone)) + if (!createInstance(env, "jassimp/AiBone", jBone)) { return false; } @@ -988,7 +988,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) wrapParams[0].l = jMatrixArr; jobject jMatrix; SmartLocalRef refMatrix(env, jMatrix); - + if (!callStaticObject(env, "jassimp/Jassimp", "wrapMatrix", "([F)Ljava/lang/Object;", wrapParams, jMatrix)) { return false; @@ -1012,7 +1012,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) { return false; } - + if (!setFloatField(env, jBoneWeight, "m_weight", cBone->mWeights[w].mWeight)) { return false; @@ -1228,7 +1228,7 @@ static bool loadSceneNode(JNIEnv *env, const aiNode *cNode, jobject parent, jobj wrapNodeParams[3].l = jNodeName; jobject jNode; if (!callStaticObject(env, "jassimp/Jassimp", "wrapSceneNode", - "(Ljava/lang/Object;Ljava/lang/Object;[ILjava/lang/String;)Ljava/lang/Object;", wrapNodeParams, jNode)) + "(Ljava/lang/Object;Ljava/lang/Object;[ILjava/lang/String;)Ljava/lang/Object;", wrapNodeParams, jNode)) { return false; } @@ -1287,7 +1287,7 @@ static bool loadSceneGraph(JNIEnv *env, const aiScene* cScene, jobject& jScene) } -static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) +static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) { for (unsigned int m = 0; m < cScene->mNumMaterials; m++) { @@ -1320,7 +1320,7 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) } /* set texture numbers */ - for (int ttInd = aiTextureType_DIFFUSE; ttInd < aiTextureType_UNKNOWN; ttInd++) + for (int ttInd = aiTextureType_DIFFUSE; ttInd < aiTextureType_UNKNOWN; ttInd++) { aiTextureType tt = static_cast(ttInd); @@ -1341,7 +1341,7 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) for (unsigned int p = 0; p < cMaterial->mNumProperties; p++) { - //printf("%s - %u - %u\n", cScene->mMaterials[m]->mProperties[p]->mKey.C_Str(), + //printf("%s - %u - %u\n", cScene->mMaterials[m]->mProperties[p]->mKey.C_Str(), // cScene->mMaterials[m]->mProperties[p]->mSemantic, // cScene->mMaterials[m]->mProperties[p]->mDataLength); @@ -1362,9 +1362,9 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) /* special case conversion for color3 */ - if (NULL != strstr(cProperty->mKey.C_Str(), "clr") && + if (NULL != strstr(cProperty->mKey.C_Str(), "clr") && cProperty->mType == aiPTI_Float && - cProperty->mDataLength == 3 * sizeof(float)) + cProperty->mDataLength == 3 * sizeof(float)) { jobject jData = NULL; SmartLocalRef refData(env, jData); @@ -1380,16 +1380,16 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) } constructorParams[4].l = jData; - if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", + if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", constructorParams, jProperty)) { return false; } } /* special case conversion for color4 */ - else if (NULL != strstr(cProperty->mKey.C_Str(), "clr") && + else if (NULL != strstr(cProperty->mKey.C_Str(), "clr") && cProperty->mType == aiPTI_Float && - cProperty->mDataLength == 4 * sizeof(float)) + cProperty->mDataLength == 4 * sizeof(float)) { jobject jData = NULL; SmartLocalRef refData(env, jData); @@ -1406,13 +1406,13 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) } constructorParams[4].l = jData; - if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", + if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", constructorParams, jProperty)) { return false; } } - else if (cProperty->mType == aiPTI_Float && cProperty->mDataLength == sizeof(float)) + else if (cProperty->mType == aiPTI_Float && cProperty->mDataLength == sizeof(float)) { jobject jData = NULL; SmartLocalRef refData(env, jData); @@ -1425,13 +1425,13 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) } constructorParams[4].l = jData; - if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", + if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", constructorParams, jProperty)) { return false; } } - else if (cProperty->mType == aiPTI_Integer && cProperty->mDataLength == sizeof(int)) + else if (cProperty->mType == aiPTI_Integer && cProperty->mDataLength == sizeof(int)) { jobject jData = NULL; SmartLocalRef refData(env, jData); @@ -1444,26 +1444,26 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) } constructorParams[4].l = jData; - if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", + if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", constructorParams, jProperty)) { return false; } } - else if (cProperty->mType == aiPTI_String) + else if (cProperty->mType == aiPTI_String) { /* skip length prefix */ jobject jData = env->NewStringUTF(cProperty->mData + 4); SmartLocalRef refData(env, jData); constructorParams[4].l = jData; - if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", + if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", constructorParams, jProperty)) { return false; } } - else + else { constructorParams[4].i = cProperty->mDataLength; @@ -1521,7 +1521,7 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) } -static bool loadAnimations(JNIEnv *env, const aiScene* cScene, jobject& jScene) +static bool loadAnimations(JNIEnv *env, const aiScene* cScene, jobject& jScene) { lprintf("converting %d animations ...\n", cScene->mNumAnimations); @@ -1603,19 +1603,19 @@ static bool loadAnimations(JNIEnv *env, const aiScene* cScene, jobject& jScene) } /* copy keys */ - if (!copyBuffer(env, jNodeAnim, "m_posKeys", cNodeAnim->mPositionKeys, + if (!copyBuffer(env, jNodeAnim, "m_posKeys", cNodeAnim->mPositionKeys, cNodeAnim->mNumPositionKeys * sizeof(aiVectorKey))) { return false; } - if (!copyBuffer(env, jNodeAnim, "m_rotKeys", cNodeAnim->mRotationKeys, + if (!copyBuffer(env, jNodeAnim, "m_rotKeys", cNodeAnim->mRotationKeys, cNodeAnim->mNumRotationKeys * sizeof(aiQuatKey))) { return false; } - if (!copyBuffer(env, jNodeAnim, "m_scaleKeys", cNodeAnim->mScalingKeys, + if (!copyBuffer(env, jNodeAnim, "m_scaleKeys", cNodeAnim->mScalingKeys, cNodeAnim->mNumScalingKeys * sizeof(aiVectorKey))) { return false; @@ -1629,7 +1629,7 @@ static bool loadAnimations(JNIEnv *env, const aiScene* cScene, jobject& jScene) } -static bool loadLights(JNIEnv *env, const aiScene* cScene, jobject& jScene) +static bool loadLights(JNIEnv *env, const aiScene* cScene, jobject& jScene) { lprintf("converting %d lights ...\n", cScene->mNumLights); @@ -1712,7 +1712,7 @@ static bool loadLights(JNIEnv *env, const aiScene* cScene, jobject& jScene) params[9].l = jAmbient; params[10].f = cLight->mAngleInnerCone; params[11].f = cLight->mAngleOuterCone; - + if (!createInstance(env, "jassimp/AiLight", "(Ljava/lang/String;ILjava/lang/Object;Ljava/lang/Object;FFFLjava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;FF)V", params, jLight)) { @@ -1736,13 +1736,13 @@ static bool loadLights(JNIEnv *env, const aiScene* cScene, jobject& jScene) } } - lprintf("converting lights finished ...\n"); + lprintf("converting lights finished ...\n"); return true; } -static bool loadCameras(JNIEnv *env, const aiScene* cScene, jobject& jScene) +static bool loadCameras(JNIEnv *env, const aiScene* cScene, jobject& jScene) { lprintf("converting %d cameras ...\n", cScene->mNumCameras); @@ -1799,7 +1799,7 @@ static bool loadCameras(JNIEnv *env, const aiScene* cScene, jobject& jScene) params[5].f = cCamera->mClipPlaneNear; params[6].f = cCamera->mClipPlaneFar; params[7].f = cCamera->mAspect; - + if (!createInstance(env, "jassimp/AiCamera", "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;FFFF)V", params, jCamera)) { @@ -1901,25 +1901,25 @@ JNIEXPORT jstring JNICALL Java_jassimp_Jassimp_getErrorString JNIEXPORT jobject JNICALL Java_jassimp_Jassimp_aiImportFile (JNIEnv *env, jclass jClazz, jstring jFilename, jlong postProcess, jobject ioSystem, jobject progressHandler) { - jobject jScene = NULL; + jobject jScene = NULL; /* convert params */ const char* cFilename = env->GetStringUTFChars(jFilename, NULL); - + Assimp::Importer imp; - + if(ioSystem != NULL) { - imp.SetIOHandler(new JavaIOSystem(env, ioSystem)); + imp.SetIOHandler(new JavaIOSystem(env, ioSystem)); lprintf("Created aiFileIO\n"); } - + if(progressHandler != NULL) { imp.SetProgressHandler(new JavaProgressHandler(env, progressHandler)); } - + lprintf("opening file: %s\n", cFilename); /* do import */ @@ -1931,7 +1931,7 @@ JNIEXPORT jobject JNICALL Java_jassimp_Jassimp_aiImportFile goto error; } - if (!createInstance(env, "jassimp/AiScene", jScene)) + if (!createInstance(env, "jassimp/AiScene", jScene)) { goto error; } diff --git a/samples/SimpleAssimpViewX/ModelLoaderHelperClasses.h b/samples/SimpleAssimpViewX/ModelLoaderHelperClasses.h index 0e6dfab3c..1803ad122 100644 --- a/samples/SimpleAssimpViewX/ModelLoaderHelperClasses.h +++ b/samples/SimpleAssimpViewX/ModelLoaderHelperClasses.h @@ -15,7 +15,7 @@ /* workflow: - 1) create a new scene wrapper + 1) create a new scene wrapper 2) populate an array of of meshHelpers for each mesh in the original scene 3) (eventually) create an animator instance 4) scale the asset (needed?) @@ -24,16 +24,16 @@ 5b) create a static vertex buffer 5c) create index buffer 5d) populate the index buffer - 5e) (eventually) gather weights + 5e) (eventually) gather weights */ #define BUFFER_OFFSET(i) ((char *)NULL + (i)) -struct Vertex +struct Vertex { aiVector3D vPosition; aiVector3D vNormal; - + aiColor4D dColorDiffuse; aiVector3D vTangent; aiVector3D vBitangent; @@ -46,33 +46,33 @@ struct Vertex // Helper Class to store GPU related resources from a given aiMesh // Modeled after AssimpView asset helper -@interface MeshHelper : NSObject -{ +@interface MeshHelper : NSObject +{ // Display list ID, this one shots *all drawing* of the mesh. Only ever use this to draw. Booya. GLuint displayList; - + // VAO that encapsulates all VBO drawing state GLuint vao; - + // VBOs GLuint vertexBuffer; GLuint indexBuffer; GLuint normalBuffer; GLuint numIndices; - + // texture GLuint textureID; - - // Material + + // Material aiColor4D diffuseColor; aiColor4D specularColor; aiColor4D ambientColor; aiColor4D emissiveColor; - + GLfloat opacity; GLfloat shininess; GLfloat specularStrength; - + BOOL twoSided; } diff --git a/samples/SimpleAssimpViewX/MyDocument.h b/samples/SimpleAssimpViewX/MyDocument.h index 16745dc3c..67675e306 100644 --- a/samples/SimpleAssimpViewX/MyDocument.h +++ b/samples/SimpleAssimpViewX/MyDocument.h @@ -20,27 +20,27 @@ #import -@interface MyDocument : NSPersistentDocument +@interface MyDocument : NSPersistentDocument { CVDisplayLinkRef _displayLink; NSOpenGLContext* _glContext; NSOpenGLPixelFormat* _glPixelFormat; - + NSView* _view; - + // Assimp Stuff aiScene* _scene; aiVector3D scene_min, scene_max, scene_center; - double normalizedScale; - + double normalizedScale; + // Our array of textures. GLuint *textureIds; - + // only used if we use - NSMutableArray* modelMeshes; + NSMutableArray* modelMeshes; BOOL builtBuffers; - - NSMutableDictionary* textureDictionary; // Array of Dicionaries that map image filenames to textureIds + + NSMutableDictionary* textureDictionary; // Array of Dicionaries that map image filenames to textureIds } @property (retain) IBOutlet NSView* _view; diff --git a/samples/SimpleOpenGL/Sample_SimpleOpenGL.c b/samples/SimpleOpenGL/Sample_SimpleOpenGL.c index bcb109564..7aa306ed4 100644 --- a/samples/SimpleOpenGL/Sample_SimpleOpenGL.c +++ b/samples/SimpleOpenGL/Sample_SimpleOpenGL.c @@ -29,7 +29,7 @@ /* ---------------------------------------------------------------------------- */ inline static void print_run_command(const char* command_name) { - printf("Run '%s %s' for more information.\n", + printf("Run '%s %s' for more information.\n", PROJECT_NAME, command_name); } @@ -43,7 +43,7 @@ inline static void print_error(const char* msg) { /* ---------------------------------------------------------------------------- */ inline static void print_usage() { - static const char* usage_format = + static const char* usage_format = "Usage: " PROJECT_NAME " " DOUBLE_NEW_LINE diff --git a/samples/SimpleTexturedDirectx11/CMakeLists.txt b/samples/SimpleTexturedDirectx11/CMakeLists.txt index 82144caa9..007ada3af 100644 --- a/samples/SimpleTexturedDirectx11/CMakeLists.txt +++ b/samples/SimpleTexturedDirectx11/CMakeLists.txt @@ -24,13 +24,13 @@ LINK_DIRECTORIES( ) ADD_EXECUTABLE( assimp_simpletextureddirectx11 WIN32 - SimpleTexturedDirectx11/Mesh.h + SimpleTexturedDirectx11/Mesh.h SimpleTexturedDirectx11/ModelLoader.cpp SimpleTexturedDirectx11/ModelLoader.h #SimpleTexturedDirectx11/PixelShader.hlsl SimpleTexturedDirectx11/TextureLoader.cpp - SimpleTexturedDirectx11/TextureLoader.h - #SimpleTexturedDirectx11/VertexShader.hlsl + SimpleTexturedDirectx11/TextureLoader.h + #SimpleTexturedDirectx11/VertexShader.hlsl SimpleTexturedDirectx11/main.cpp SimpleTexturedDirectx11/SafeRelease.hpp ${SAMPLES_SHARED_CODE_DIR}/UTFConverter.cpp diff --git a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/ModelLoader.cpp b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/ModelLoader.cpp index 92760d691..18bb10f1e 100644 --- a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/ModelLoader.cpp +++ b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/ModelLoader.cpp @@ -1,6 +1,6 @@ #include "ModelLoader.h" -ModelLoader::ModelLoader() : +ModelLoader::ModelLoader() : dev_(nullptr), devcon_(nullptr), meshes_(), diff --git a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/TextureLoader.cpp b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/TextureLoader.cpp index 01ba343e8..a02c53ca6 100644 --- a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/TextureLoader.cpp +++ b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/TextureLoader.cpp @@ -140,16 +140,16 @@ static WICConvert g_WICConvert[] = { GUID_WICPixelFormatBlackWhite, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM - { GUID_WICPixelFormat1bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat2bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat4bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat8bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat1bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat2bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat4bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat8bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat2bppGray, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM - { GUID_WICPixelFormat4bppGray, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM + { GUID_WICPixelFormat2bppGray, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM + { GUID_WICPixelFormat4bppGray, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM - { GUID_WICPixelFormat16bppGrayFixedPoint, GUID_WICPixelFormat16bppGrayHalf }, // DXGI_FORMAT_R16_FLOAT - { GUID_WICPixelFormat32bppGrayFixedPoint, GUID_WICPixelFormat32bppGrayFloat }, // DXGI_FORMAT_R32_FLOAT + { GUID_WICPixelFormat16bppGrayFixedPoint, GUID_WICPixelFormat16bppGrayHalf }, // DXGI_FORMAT_R16_FLOAT + { GUID_WICPixelFormat32bppGrayFixedPoint, GUID_WICPixelFormat32bppGrayFloat }, // DXGI_FORMAT_R32_FLOAT #ifdef DXGI_1_2_FORMATS @@ -165,10 +165,10 @@ static WICConvert g_WICConvert[] = { GUID_WICPixelFormat32bppBGR101010, GUID_WICPixelFormat32bppRGBA1010102 }, // DXGI_FORMAT_R10G10B10A2_UNORM - { GUID_WICPixelFormat24bppBGR, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat24bppRGB, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat32bppPBGRA, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat32bppPRGBA, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat24bppBGR, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat24bppRGB, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat32bppPBGRA, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat32bppPRGBA, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM { GUID_WICPixelFormat48bppRGB, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM { GUID_WICPixelFormat48bppBGR, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM @@ -176,21 +176,21 @@ static WICConvert g_WICConvert[] = { GUID_WICPixelFormat64bppPRGBA, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM { GUID_WICPixelFormat64bppPBGRA, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM - { GUID_WICPixelFormat48bppRGBFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat48bppBGRFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat64bppRGBAFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat64bppBGRAFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat64bppRGBFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat64bppRGBHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat48bppRGBHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat48bppRGBFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat48bppBGRFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppRGBAFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppBGRAFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppRGBFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppRGBHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat48bppRGBHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat96bppRGBFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT - { GUID_WICPixelFormat128bppPRGBAFloat, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT - { GUID_WICPixelFormat128bppRGBFloat, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT - { GUID_WICPixelFormat128bppRGBAFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT - { GUID_WICPixelFormat128bppRGBFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat96bppRGBFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppPRGBAFloat, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppRGBFloat, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppRGBAFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppRGBFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT - { GUID_WICPixelFormat32bppCMYK, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat32bppCMYK, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM { GUID_WICPixelFormat64bppCMYK, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM { GUID_WICPixelFormat40bppCMYKAlpha, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM { GUID_WICPixelFormat80bppCMYKAlpha, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM @@ -198,7 +198,7 @@ static WICConvert g_WICConvert[] = #if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) { GUID_WICPixelFormat32bppRGB, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM { GUID_WICPixelFormat64bppRGB, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM - { GUID_WICPixelFormat64bppPRGBAHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppPRGBAHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT #endif // We don't support n-channel formats diff --git a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/main.cpp b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/main.cpp index 02e2b6088..3f8d15320 100644 --- a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/main.cpp +++ b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/main.cpp @@ -128,7 +128,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, int argc; LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc); if (!argv) { - MessageBox(nullptr, + MessageBox(nullptr, TEXT("An error occured while reading command line arguments."), TEXT("Error!"), MB_ICONERROR | MB_OK); @@ -145,8 +145,8 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, // Ensure that a model file has been specified. if (argc < 2) { - MessageBox(nullptr, - TEXT("No model file specified. The program will now close."), + MessageBox(nullptr, + TEXT("No model file specified. The program will now close."), TEXT("Error!"), MB_ICONERROR | MB_OK); free_command_line_allocated_memory(); @@ -157,7 +157,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, g_ModelPath = UTFConverter(argv[1]).str(); free_command_line_allocated_memory(); - + WNDCLASSEX wc; MSG msg; @@ -573,7 +573,7 @@ void InitGraphics() HRESULT CompileShaderFromFile(LPCWSTR pFileName, const D3D_SHADER_MACRO* pDefines, LPCSTR pEntryPoint, LPCSTR pShaderModel, ID3DBlob** ppBytecodeBlob) { UINT compileFlags = D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR; - + #ifdef _DEBUG compileFlags |= D3DCOMPILE_DEBUG; #endif diff --git a/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp b/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp index aa2344118..9ceeec62e 100644 --- a/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp +++ b/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp @@ -579,7 +579,7 @@ void KillGLWindow() // Properly Kill The Window if (!DestroyWindow(g_hWnd)) // Are We Able To Destroy The Window MessageBox(nullptr, TEXT("Could Not Release hWnd."), TEXT("SHUTDOWN ERROR"), MB_OK | MB_ICONINFORMATION); g_hWnd = nullptr; - } + } if (g_hInstance) { @@ -727,7 +727,7 @@ BOOL CreateGLWindow(const char* title, int width, int height, int bits, bool ful return FALSE; } - if (nullptr == (hRC=wglCreateContext(hDC))) + if (nullptr == (hRC=wglCreateContext(hDC))) { abortGLInit("Can't Create A GL Rendering Context."); return FALSE; diff --git a/test/models-nonbsd/3D/mar_rifle.source.txt b/test/models-nonbsd/3D/mar_rifle.source.txt index c0cd5fe6d..d7183b7b6 100644 --- a/test/models-nonbsd/3D/mar_rifle.source.txt +++ b/test/models-nonbsd/3D/mar_rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/3DS/cart_wheel.source.txt b/test/models-nonbsd/3DS/cart_wheel.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/3DS/cart_wheel.source.txt +++ b/test/models-nonbsd/3DS/cart_wheel.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/3DS/mar_rifle.source.txt b/test/models-nonbsd/3DS/mar_rifle.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/3DS/mar_rifle.source.txt +++ b/test/models-nonbsd/3DS/mar_rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/3DS/mp5_sil.source.txt b/test/models-nonbsd/3DS/mp5_sil.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/3DS/mp5_sil.source.txt +++ b/test/models-nonbsd/3DS/mp5_sil.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/ASE/Rifle.source.txt b/test/models-nonbsd/ASE/Rifle.source.txt index 1b96f8564..802c57045 100644 --- a/test/models-nonbsd/ASE/Rifle.source.txt +++ b/test/models-nonbsd/ASE/Rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,9 +11,9 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/ASE/Rifle2.source.txt b/test/models-nonbsd/ASE/Rifle2.source.txt index 3fa628db4..7b50d1645 100644 --- a/test/models-nonbsd/ASE/Rifle2.source.txt +++ b/test/models-nonbsd/ASE/Rifle2.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,9 +11,9 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/BLEND/fleurOptonl.source.txt b/test/models-nonbsd/BLEND/fleurOptonl.source.txt index b9c58b5d9..d4c583620 100644 --- a/test/models-nonbsd/BLEND/fleurOptonl.source.txt +++ b/test/models-nonbsd/BLEND/fleurOptonl.source.txt @@ -10,7 +10,7 @@ Puoi utilizzarlo liberamente, modificarlo e migliorarlo. ************* Ma attenzione!! ********************* Nel modificare e utilizzare i modelli, ricorda comunque sempre che : -"il diritto morale all'integrità dell'opera (diritto dell'autore originale) non ti consente di apportare all'opera deformazioni o modificazioni, od ogni altro atto a danno dell'opera stessa, che possano essere di pregiudizio all'onore o alla reputazione dell'autore (la valutazione della lesione dell'onore o della reputazione avviene sulla base di elementi psicologici soggettivi)" +"il diritto morale all'integrità dell'opera (diritto dell'autore originale) non ti consente di apportare all'opera deformazioni o modificazioni, od ogni altro atto a danno dell'opera stessa, che possano essere di pregiudizio all'onore o alla reputazione dell'autore (la valutazione della lesione dell'onore o della reputazione avviene sulla base di elementi psicologici soggettivi)" (dalle faq di Creative Commons Italia) http://www.creativecommons.it/node/165#27 @@ -26,7 +26,7 @@ In particolare, sara' da me considerata lesione d'onore l'uso e/o l'adattamento Se lo fate rischiate la denuncia e il risarcimento danni. Per qualsiasi chiarimento in proposito potete comunque scrivermi. -Questo e' un diritto garantito per legge a me come ad ogni altro artista. +Questo e' un diritto garantito per legge a me come ad ogni altro artista. L'utilizzo della Creative Commons non influisce su questo diritto. ************************************************ @@ -45,7 +45,7 @@ work, so you are not allowed to do exactely what you want with my models. The author (that is me) has the right to prevent distortion, mutilation, or other modification of his work which would be prejudicial to his or her honor or reputation. Me, i consider to be prejudicial to my honor, the modification and/or the use of my models in projects that are related to: - + Racism and hatred instigation. War promotion. Cruelty toward animals. @@ -69,15 +69,15 @@ Note Questo e' stato il mio secondo modello completo. Per questo motivo potrebbe contenere errori e imprecisioni. - + Al momento, questi sono i difetti che ho notato: - + - Non e' nell'origine degli assi. - lo scheletro ha il centro spostato di lato - Nel texture sheet c'e' spazio sprecato. - - + + ################### Notes @@ -86,7 +86,7 @@ This was my first complete model, so it probably contains something wrong. At the moment, this is what i noticed: - + - She's not in the origin of axis - Armature's center is not in armature's center. - Texture sheet contains wasted space. diff --git a/test/models-nonbsd/DXF/rifle.source.txt b/test/models-nonbsd/DXF/rifle.source.txt index a2585afad..fa25f10db 100644 --- a/test/models-nonbsd/DXF/rifle.source.txt +++ b/test/models-nonbsd/DXF/rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,9 +11,9 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/FBX/2013_ASCII/cart_wheel.source.txt b/test/models-nonbsd/FBX/2013_ASCII/cart_wheel.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/FBX/2013_ASCII/cart_wheel.source.txt +++ b/test/models-nonbsd/FBX/2013_ASCII/cart_wheel.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/FBX/2013_ASCII/kwxport_test_vcolors.fbx.source.txt b/test/models-nonbsd/FBX/2013_ASCII/kwxport_test_vcolors.fbx.source.txt index 94ee48e4a..7c81e612f 100644 --- a/test/models-nonbsd/FBX/2013_ASCII/kwxport_test_vcolors.fbx.source.txt +++ b/test/models-nonbsd/FBX/2013_ASCII/kwxport_test_vcolors.fbx.source.txt @@ -1,9 +1,9 @@ From kwxport http://www.kwxport.org/ ->> -The kW Xport plug-in source is released under the MIT license. -Basically, it means "feel free to use it; credit the source; don't sue me +>> +The kW Xport plug-in source is released under the MIT license. +Basically, it means "feel free to use it; credit the source; don't sue me if something goes wrong." >> diff --git a/test/models-nonbsd/FBX/2013_ASCII/mar_rifle.source.txt b/test/models-nonbsd/FBX/2013_ASCII/mar_rifle.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/FBX/2013_ASCII/mar_rifle.source.txt +++ b/test/models-nonbsd/FBX/2013_ASCII/mar_rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/FBX/2013_ASCII/mp5_sil.source.txt b/test/models-nonbsd/FBX/2013_ASCII/mp5_sil.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/FBX/2013_ASCII/mp5_sil.source.txt +++ b/test/models-nonbsd/FBX/2013_ASCII/mp5_sil.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/FBX/2013_BINARY/cart_wheel.source.txt b/test/models-nonbsd/FBX/2013_BINARY/cart_wheel.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/FBX/2013_BINARY/cart_wheel.source.txt +++ b/test/models-nonbsd/FBX/2013_BINARY/cart_wheel.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/FBX/2013_BINARY/kwxport_test_vcolors.fbx.source.txt b/test/models-nonbsd/FBX/2013_BINARY/kwxport_test_vcolors.fbx.source.txt index 94ee48e4a..7c81e612f 100644 --- a/test/models-nonbsd/FBX/2013_BINARY/kwxport_test_vcolors.fbx.source.txt +++ b/test/models-nonbsd/FBX/2013_BINARY/kwxport_test_vcolors.fbx.source.txt @@ -1,9 +1,9 @@ From kwxport http://www.kwxport.org/ ->> -The kW Xport plug-in source is released under the MIT license. -Basically, it means "feel free to use it; credit the source; don't sue me +>> +The kW Xport plug-in source is released under the MIT license. +Basically, it means "feel free to use it; credit the source; don't sue me if something goes wrong." >> diff --git a/test/models-nonbsd/FBX/2013_BINARY/mar_rifle.source.txt b/test/models-nonbsd/FBX/2013_BINARY/mar_rifle.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/FBX/2013_BINARY/mar_rifle.source.txt +++ b/test/models-nonbsd/FBX/2013_BINARY/mar_rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/FBX/2013_BINARY/mp5_sil.source.txt b/test/models-nonbsd/FBX/2013_BINARY/mp5_sil.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/FBX/2013_BINARY/mp5_sil.source.txt +++ b/test/models-nonbsd/FBX/2013_BINARY/mp5_sil.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/LWO/LWO2/LWSReferences/QuickDraw.source.txt b/test/models-nonbsd/LWO/LWO2/LWSReferences/QuickDraw.source.txt index aaa244217..80afd30a7 100644 --- a/test/models-nonbsd/LWO/LWO2/LWSReferences/QuickDraw.source.txt +++ b/test/models-nonbsd/LWO/LWO2/LWSReferences/QuickDraw.source.txt @@ -1,10 +1,10 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas -"These 3d models are contributed by John Hoffman and are based on -characters from a cartoon show called "Jayce and the wheel warriors" +"These 3d models are contributed by John Hoffman and are based on +characters from a cartoon show called "Jayce and the wheel warriors" (except the marauder) John's site: http://www3.sympatico.ca/john.hoffman" ===================================================================== @@ -15,9 +15,9 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/LWO/LWO2/rifle.source.txt b/test/models-nonbsd/LWO/LWO2/rifle.source.txt index 5523bbc0c..5774ecc0e 100644 --- a/test/models-nonbsd/LWO/LWO2/rifle.source.txt +++ b/test/models-nonbsd/LWO/LWO2/rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,9 +11,9 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/MD2/source.txt b/test/models-nonbsd/MD2/source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/MD2/source.txt +++ b/test/models-nonbsd/MD2/source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/MD5/BoarMan.source.txt b/test/models-nonbsd/MD5/BoarMan.source.txt index 50b2dfb53..69f0c79a2 100644 --- a/test/models-nonbsd/MD5/BoarMan.source.txt +++ b/test/models-nonbsd/MD5/BoarMan.source.txt @@ -1,8 +1,8 @@ -License: Creative Commons +License: Creative Commons - Remix - Share alike - Attribution Author: zphr (Christian Lenke) - + diff --git a/test/models-nonbsd/MDL/IDPO (Quake1)/gijoe-readme.txt b/test/models-nonbsd/MDL/IDPO (Quake1)/gijoe-readme.txt index 2febb0583..84ce9fd95 100644 --- a/test/models-nonbsd/MDL/IDPO (Quake1)/gijoe-readme.txt +++ b/test/models-nonbsd/MDL/IDPO (Quake1)/gijoe-readme.txt @@ -4,9 +4,9 @@ Version : 1 Date : 11/05/97 Author : Kenneth Whelan Email : JWHELAN@pop.prodigy.net -Credits : id software, Larry Hama, Steven Polge, and Rene Post for making Quake ME - - +Credits : id software, Larry Hama, Steven Polge, and Rene Post for making Quake ME + + Build time: ??? Time??? @@ -14,7 +14,7 @@ Type of Mod ----------- Quake C : no Sound : no -MDL : Yes +MDL : Yes Format of QuakeC (if a Quake C Mod) @@ -29,8 +29,8 @@ Description of the Modification ------------------------------- This is a new player.mdl for quake. It's main use is for bots. The Skins are Snake Eyes v4, Duke v3, Low-Light, -Storm Shadow v2, Shockwave, Repeater, Gung-Ho, Shipwreck, Dusty v3, and -Tunnel Rat v2. +Storm Shadow v2, Shockwave, Repeater, Gung-Ho, Shipwreck, Dusty v3, and +Tunnel Rat v2. @@ -40,7 +40,7 @@ None that I know of. How to Install the Modification ------------------------------- -First back up the current player.mdl(copy player.mdl player.bak). Just put +First back up the current player.mdl(copy player.mdl player.bak). Just put it in the progs dir in the hack your using, if any. Technical Details diff --git a/test/models-nonbsd/MDL/IDPO (Quake1)/steg.txt b/test/models-nonbsd/MDL/IDPO (Quake1)/steg.txt index c07c2f126..cbddccb7e 100644 --- a/test/models-nonbsd/MDL/IDPO (Quake1)/steg.txt +++ b/test/models-nonbsd/MDL/IDPO (Quake1)/steg.txt @@ -16,17 +16,17 @@ E-mail: sgalbrai@linknet.kitsap.lib.wa.us WWW: www.oz.net/~simitar -This model can be used or modified for any purpose +This model can be used or modified for any purpose as long as this text document is included with it -and all modellers listed in this document are +and all modellers listed in this document are listed wherever credits are appropriate for that purpose. -Help Wanted: +Help Wanted: The Free Models Project can use help with -models and in other areas, such as legal boilerplate +models and in other areas, such as legal boilerplate for models. WWW: www.oz.net/~simitar/model.html diff --git a/test/models-nonbsd/MDL/IDPO (Quake1)/tekmechbot.txt b/test/models-nonbsd/MDL/IDPO (Quake1)/tekmechbot.txt index dc0149b01..3b3dd9042 100644 --- a/test/models-nonbsd/MDL/IDPO (Quake1)/tekmechbot.txt +++ b/test/models-nonbsd/MDL/IDPO (Quake1)/tekmechbot.txt @@ -8,7 +8,7 @@ that the FMP is a great concept !! There is some movement in the model. The legs can be animated for walking, stomping, or running -around ! +around ! Ok, it's my first model, so I will work on less polygony in the future ;-) @@ -18,9 +18,9 @@ Contact: ebuy@optelnow.net (E-MAIL) Date Created: 7/07/2000 ====================================================== -This model can be used or modified for any purpose +This model can be used or modified for any purpose as long as this text document is included with it -and all modelers listed in this document are +and all modelers listed in this document are listed wherever credits are appropriate for that purpose. diff --git a/test/models-nonbsd/NFF/NFFSense8/credits.txt b/test/models-nonbsd/NFF/NFFSense8/credits.txt index f3cef4d09..bad7fbf15 100644 --- a/test/models-nonbsd/NFF/NFFSense8/credits.txt +++ b/test/models-nonbsd/NFF/NFFSense8/credits.txt @@ -1,4 +1,4 @@ teapot.nff, home4.nff - http://www.martinreddy.net/ukvrsig/wtk.html -cokecan.nff -www.vrupl.evl.uic.edu/Eng591_Pages/cokecan.nff +cokecan.nff -www.vrupl.evl.uic.edu/Eng591_Pages/cokecan.nff TODO: License status to be confirmed diff --git a/test/models-nonbsd/OBJ/rifle.source.txt b/test/models-nonbsd/OBJ/rifle.source.txt index 1d2cec5cf..f7b93fd0f 100644 --- a/test/models-nonbsd/OBJ/rifle.source.txt +++ b/test/models-nonbsd/OBJ/rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,9 +11,9 @@ Notices found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models/3DS/UVTransformTest/note.txt b/test/models/3DS/UVTransformTest/note.txt index 4c8bfedd2..9a6bab030 100644 --- a/test/models/3DS/UVTransformTest/note.txt +++ b/test/models/3DS/UVTransformTest/note.txt @@ -1,5 +1,5 @@ -All 'mirror' files are not absolutely correct. That's mainly -because it's difficult convert Max' handling of mirroring to +All 'mirror' files are not absolutely correct. That's mainly +because it's difficult convert Max' handling of mirroring to our's. In other words: TO DO, but only if someone REALLY needs it. diff --git a/test/models/ASE/MotionCaptureROM.source.txt b/test/models/ASE/MotionCaptureROM.source.txt index 2b961906a..f34e056ef 100644 --- a/test/models/ASE/MotionCaptureROM.source.txt +++ b/test/models/ASE/MotionCaptureROM.source.txt @@ -1,3 +1,3 @@ -"MotionCaptureROM.ase" - Free for any purpose. +"MotionCaptureROM.ase" - Free for any purpose. NOTE: The errors in the middle of the animation are caused by problems during recording, it's not an importer issue. diff --git a/test/models/Collada/kwxport_test_vcolors.dae.source.txt b/test/models/Collada/kwxport_test_vcolors.dae.source.txt index 94ee48e4a..7c81e612f 100644 --- a/test/models/Collada/kwxport_test_vcolors.dae.source.txt +++ b/test/models/Collada/kwxport_test_vcolors.dae.source.txt @@ -1,9 +1,9 @@ From kwxport http://www.kwxport.org/ ->> -The kW Xport plug-in source is released under the MIT license. -Basically, it means "feel free to use it; credit the source; don't sue me +>> +The kW Xport plug-in source is released under the MIT license. +Basically, it means "feel free to use it; credit the source; don't sue me if something goes wrong." >> diff --git a/test/models/IRR/warn_dwarf_scaling_is_intended.txt b/test/models/IRR/warn_dwarf_scaling_is_intended.txt index f651a8643..d0e4ea361 100644 --- a/test/models/IRR/warn_dwarf_scaling_is_intended.txt +++ b/test/models/IRR/warn_dwarf_scaling_is_intended.txt @@ -1,3 +1,3 @@ for dawfInCellar_ChildOfCellar & dawfInCellar_SameHierarchy: -the strange scalings of cellar and dwarf are intended. +the strange scalings of cellar and dwarf are intended. diff --git a/test/models/MD2/faerie-source.txt b/test/models/MD2/faerie-source.txt index 4906ff4d1..afb00a170 100644 --- a/test/models/MD2/faerie-source.txt +++ b/test/models/MD2/faerie-source.txt @@ -1,5 +1,5 @@ -From IRRLICHT/media +From IRRLICHT/media The Irrlicht Engine License diff --git a/test/models/MD2/sidney-source.txt b/test/models/MD2/sidney-source.txt index 4906ff4d1..afb00a170 100644 --- a/test/models/MD2/sidney-source.txt +++ b/test/models/MD2/sidney-source.txt @@ -1,5 +1,5 @@ -From IRRLICHT/media +From IRRLICHT/media The Irrlicht Engine License diff --git a/test/models/Q3D/E-AT-AT.source.txt b/test/models/Q3D/E-AT-AT.source.txt index 2df8826f1..900ee552c 100644 --- a/test/models/Q3D/E-AT-AT.source.txt +++ b/test/models/Q3D/E-AT-AT.source.txt @@ -5,4 +5,4 @@ Downloaded 4th November 08 (Obama ftw!) Copyright notice found on the page: Where do the models in the archive come from? -All 3D files available from the3darchive.com are from the public domain. +All 3D files available from the3darchive.com are from the public domain. diff --git a/test/models/Q3D/earth.source.txt b/test/models/Q3D/earth.source.txt index 2df8826f1..900ee552c 100644 --- a/test/models/Q3D/earth.source.txt +++ b/test/models/Q3D/earth.source.txt @@ -5,4 +5,4 @@ Downloaded 4th November 08 (Obama ftw!) Copyright notice found on the page: Where do the models in the archive come from? -All 3D files available from the3darchive.com are from the public domain. +All 3D files available from the3darchive.com are from the public domain. diff --git a/test/models/WRL/credits.txt b/test/models/WRL/credits.txt index 055f73734..7be7fa192 100644 --- a/test/models/WRL/credits.txt +++ b/test/models/WRL/credits.txt @@ -1,2 +1,2 @@ "MotionCaptureROM.ase" Recorded using ViconIQ. -Converted to VRML with 3DS Max 2008. +Converted to VRML with 3DS Max 2008. diff --git a/test/models/X/anim_test.txt b/test/models/X/anim_test.txt index 3d270dc7d..f2ef6c056 100644 --- a/test/models/X/anim_test.txt +++ b/test/models/X/anim_test.txt @@ -4,6 +4,6 @@ Frame 1 - 10: Zylinder knickt ein, so dass der Knick in Richtung z+ zeigt. Frame 10 - 18: Zylinder-Spitze streckt sich in Richtung z-. Frame 18 - 24: Zylinder-Spitze bewegt sich zu Position in Richtung x+ -Remarks: The exporter failed here for some reasons... although the mesh referres to four bones, only two of them are stored in the corresponding node hierarchy. So you have a mesh with 4 bones, a hirarchy with 2 nodes and a animation that affects only those two nodes. +Remarks: The exporter failed here for some reasons... although the mesh referres to four bones, only two of them are stored in the corresponding node hierarchy. So you have a mesh with 4 bones, a hirarchy with 2 nodes and a animation that affects only those two nodes. There is no timing given for the animation. You have to scale the animation manually. For this file, the timing seems to be 24 ticks per second. diff --git a/test/models/X/kwxport_test_cubewithvcolors.source.txt b/test/models/X/kwxport_test_cubewithvcolors.source.txt index 94ee48e4a..7c81e612f 100644 --- a/test/models/X/kwxport_test_cubewithvcolors.source.txt +++ b/test/models/X/kwxport_test_cubewithvcolors.source.txt @@ -1,9 +1,9 @@ From kwxport http://www.kwxport.org/ ->> -The kW Xport plug-in source is released under the MIT license. -Basically, it means "feel free to use it; credit the source; don't sue me +>> +The kW Xport plug-in source is released under the MIT license. +Basically, it means "feel free to use it; credit the source; don't sue me if something goes wrong." >> diff --git a/test/models/X/test.txt b/test/models/X/test.txt index eaf9d9c3c..9452855b3 100644 --- a/test/models/X/test.txt +++ b/test/models/X/test.txt @@ -1,3 +1,3 @@ Simple textured test cube exported from Maya. Has a texture that does label each cube side uniquely, but the sides do not match DirectX coordinate space. -Is not readable using D3DXLoadFrameHierarchy, needs custom text parsing. +Is not readable using D3DXLoadFrameHierarchy, needs custom text parsing. diff --git a/test/models/invalid/readme.txt b/test/models/invalid/readme.txt index cab740a84..6ad8b4380 100644 --- a/test/models/invalid/readme.txt +++ b/test/models/invalid/readme.txt @@ -4,9 +4,9 @@ GENERAL ********************************************************* -The files in this directory are invalid ... some of them are empty, +The files in this directory are invalid ... some of them are empty, others have invalid vertices or faces, others are prepared to make - assimp allocate a few hundreds gigs of memory ... most are + assimp allocate a few hundreds gigs of memory ... most are actually regression tests, i.e. there was once a bugfix that fixed the respective loaders. @@ -18,8 +18,8 @@ crash. FILES ********************************************************* -OutOfMemory.off - the number of faces is invalid. There won't be - enough memory so std::vector::reserve() will most likely fail. +OutOfMemory.off - the number of faces is invalid. There won't be + enough memory so std::vector::reserve() will most likely fail. The exception should be caught in Importer.cpp. empty. - These files are completely empty. The corresponding diff --git a/test/regression/README.txt b/test/regression/README.txt index 3e90a143b..a37da9255 100644 --- a/test/regression/README.txt +++ b/test/regression/README.txt @@ -8,7 +8,7 @@ against a regression database provided with assimp (db.zip). A few failures are totally fine (see sections 7+). You need to worry if a huge majority of all files in a particular format (or post-processing configuration) fails as this might be a sign of a recent regression in assimp's codebase or -gross incompatibility with your system or compiler. +gross incompatibility with your system or compiler. 2) What do I need? --------------------------------------------------------------------------------- @@ -53,8 +53,8 @@ Edit the reg_settings.py file and add the path to your repository to The regression database includes mini dumps of the aiScene data structure, i.e. the scene hierarchy plus the sizes of all data arrays MUST match. Floating-point data buffers, such as vertex positions are handled less strictly: min, max and -average values are stored with low precision. This takes hardware- or -compiler-specific differences in floating-point computations into account. +average values are stored with low precision. This takes hardware- or +compiler-specific differences in floating-point computations into account. Generally, almost all significant regressions will be detected while the number of false positives is relatively low. diff --git a/test/unit/AbstractImportExportBase.h b/test/unit/AbstractImportExportBase.h index 72530aedc..7651d2e52 100644 --- a/test/unit/AbstractImportExportBase.h +++ b/test/unit/AbstractImportExportBase.h @@ -67,7 +67,7 @@ bool AbstractImportExportBase::importerTest() { return true; } -inline +inline bool AbstractImportExportBase::exporterTest() { return true; } diff --git a/test/unit/Common/utStandardShapes.cpp b/test/unit/Common/utStandardShapes.cpp index a5df5d898..e1bb6eef9 100644 --- a/test/unit/Common/utStandardShapes.cpp +++ b/test/unit/Common/utStandardShapes.cpp @@ -51,7 +51,7 @@ TEST_F( utStandardShapes, testMakeMesh ) { // The mNumIndices member of the second face is now incorrect const auto& face = aiMeshPtr->mFaces[0]; - EXPECT_EQ(face.mNumIndices, numIndicesPerPrimitive); + EXPECT_EQ(face.mNumIndices, numIndicesPerPrimitive); delete aiMeshPtr; } diff --git a/test/unit/ImportExport/MDL/utMDLImporter_HL1_Nodes.cpp b/test/unit/ImportExport/MDL/utMDLImporter_HL1_Nodes.cpp index ff3b4930c..6fa92f950 100644 --- a/test/unit/ImportExport/MDL/utMDLImporter_HL1_Nodes.cpp +++ b/test/unit/ImportExport/MDL/utMDLImporter_HL1_Nodes.cpp @@ -73,7 +73,7 @@ public: "Bone_3" | "" <----+ "Bone_2" | - "Bone_5" | + "Bone_5" | "" <----+ "" <----+ */ @@ -139,7 +139,7 @@ public: $body "Bodypart_1" <--+ | $body "Bodypart_2" | | $body "Bodypart1" | | - $body "Bodypart" ---|--+ + $body "Bodypart" ---|--+ $body "Bodypart_1" ---+ | $body "Bodypart2" | $body "Bodypart" ------+ diff --git a/test/unit/RandomNumberGeneration.h b/test/unit/RandomNumberGeneration.h index 81fcfb59c..892e78c06 100644 --- a/test/unit/RandomNumberGeneration.h +++ b/test/unit/RandomNumberGeneration.h @@ -53,11 +53,11 @@ class RandomUniformRealGenerator { public: RandomUniformRealGenerator() : dist_(), - rd_(), + rd_(), re_(rd_()) { // empty } - + RandomUniformRealGenerator(T min, T max) : dist_(min, max), rd_(), diff --git a/test/unit/SceneDiffer.cpp b/test/unit/SceneDiffer.cpp index 6ea28671a..368589bf3 100644 --- a/test/unit/SceneDiffer.cpp +++ b/test/unit/SceneDiffer.cpp @@ -48,7 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { -SceneDiffer::SceneDiffer() +SceneDiffer::SceneDiffer() : m_diffs() { // empty } diff --git a/test/unit/SceneDiffer.h b/test/unit/SceneDiffer.h index 9e04c7210..e0dc6005a 100644 --- a/test/unit/SceneDiffer.h +++ b/test/unit/SceneDiffer.h @@ -72,4 +72,4 @@ private: std::vector m_diffs; }; -} +} diff --git a/test/unit/TestIOSystem.h b/test/unit/TestIOSystem.h index fdc3cc49b..4a42b23f0 100644 --- a/test/unit/TestIOSystem.h +++ b/test/unit/TestIOSystem.h @@ -61,7 +61,7 @@ public: virtual ~TestIOSystem() { // empty } - + virtual bool Exists( const char* ) const { return true; } diff --git a/test/unit/utDefaultIOStream.cpp b/test/unit/utDefaultIOStream.cpp index 800fddbc9..d3e2c8a7e 100644 --- a/test/unit/utDefaultIOStream.cpp +++ b/test/unit/utDefaultIOStream.cpp @@ -66,7 +66,7 @@ TEST_F( utDefaultIOStream, FileSizeTest ) { { auto written = std::fwrite(data, sizeof(*data), dataCount, fs ); EXPECT_NE( 0U, written ); - + auto vflush = std::fflush( fs ); ASSERT_EQ(vflush, 0); diff --git a/test/unit/utFBXImporterExporter.cpp b/test/unit/utFBXImporterExporter.cpp index c78d56b97..4cfc9b152 100644 --- a/test/unit/utFBXImporterExporter.cpp +++ b/test/unit/utFBXImporterExporter.cpp @@ -365,7 +365,7 @@ TEST_F(utFBXImporterExporter, importMaxPbrMaterialsMetalRoughness) { float bumpMapAmt; // Presumably amount. ASSERT_EQ(mat->Get("$raw.3dsMax|main|bump_map_amt", aiTextureType_NONE, 0, bumpMapAmt), aiReturn_SUCCESS); EXPECT_EQ(bumpMapAmt, 0.75f); - + aiColor4D emitColor; ASSERT_EQ(mat->Get("$raw.3dsMax|main|emit_color", aiTextureType_NONE, 0, emitColor), aiReturn_SUCCESS); EXPECT_EQ(emitColor, aiColor4D(1, 1, 0, 1)); @@ -418,7 +418,7 @@ TEST_F(utFBXImporterExporter, importMaxPbrMaterialsSpecularGloss) { float bumpMapAmt; // Presumably amount. ASSERT_EQ(mat->Get("$raw.3dsMax|main|bump_map_amt", aiTextureType_NONE, 0, bumpMapAmt), aiReturn_SUCCESS); EXPECT_EQ(bumpMapAmt, 0.66f); - + aiColor4D emitColor; ASSERT_EQ(mat->Get("$raw.3dsMax|main|emit_color", aiTextureType_NONE, 0, emitColor), aiReturn_SUCCESS); EXPECT_EQ(emitColor, aiColor4D(1, 0, 1, 1)); diff --git a/test/unit/utFindDegenerates.cpp b/test/unit/utFindDegenerates.cpp index 1f8e8e93f..6f2abebfb 100644 --- a/test/unit/utFindDegenerates.cpp +++ b/test/unit/utFindDegenerates.cpp @@ -199,10 +199,10 @@ TEST_F(FindDegeneratesProcessTest, meshRemoval) { scene->mRootNode->mMeshes[3] = 3; scene->mRootNode->mMeshes[4] = 4; - mProcess->Execute(scene.get()); + mProcess->Execute(scene.get()); EXPECT_EQ(scene->mNumMeshes, 1u); EXPECT_EQ(scene->mMeshes[0], meshWhichSurvives); EXPECT_EQ(scene->mRootNode->mNumMeshes, 1u); - EXPECT_EQ(scene->mRootNode->mMeshes[0], 0u); + EXPECT_EQ(scene->mRootNode->mMeshes[0], 0u); } diff --git a/test/unit/utIOStreamBuffer.cpp b/test/unit/utIOStreamBuffer.cpp index 6d0d6a7d7..a0e4660df 100644 --- a/test/unit/utIOStreamBuffer.cpp +++ b/test/unit/utIOStreamBuffer.cpp @@ -81,14 +81,14 @@ TEST_F( IOStreamBufferTest, open_close_Test ) { EXPECT_FALSE( myBuffer.open( nullptr ) ); EXPECT_FALSE( myBuffer.close() ); - + const auto dataSize = sizeof(data); const auto dataCount = dataSize / sizeof(*data); char fname[]={ "octest.XXXXXX" }; auto* fs = MakeTmpFile(fname); ASSERT_NE(nullptr, fs); - + auto written = std::fwrite( data, sizeof(*data), dataCount, fs ); EXPECT_NE( 0U, written ); auto flushResult = std::fflush( fs ); @@ -107,7 +107,7 @@ TEST_F( IOStreamBufferTest, open_close_Test ) { } TEST_F( IOStreamBufferTest, readlineTest ) { - + const auto dataSize = sizeof(data); const auto dataCount = dataSize / sizeof(*data); diff --git a/test/unit/utIOSystem.cpp b/test/unit/utIOSystem.cpp index 767984deb..1e866515e 100644 --- a/test/unit/utIOSystem.cpp +++ b/test/unit/utIOSystem.cpp @@ -50,12 +50,12 @@ using namespace Assimp; class IOSystemTest : public ::testing::Test { public: - virtual void SetUp() { - pImp = new TestIOSystem(); + virtual void SetUp() { + pImp = new TestIOSystem(); } - - virtual void TearDown() { - delete pImp; + + virtual void TearDown() { + delete pImp; } protected: diff --git a/test/unit/utIssues.cpp b/test/unit/utIssues.cpp index cb1adb22c..5eeed6ad8 100644 --- a/test/unit/utIssues.cpp +++ b/test/unit/utIssues.cpp @@ -62,7 +62,7 @@ TEST_F( utIssues, OpacityBugWhenExporting_727 ) { aiScene *scene( TestModelFacttory::createDefaultTestModel( opacity ) ); Assimp::Importer importer; Assimp::Exporter exporter; - + std::string path = "dae"; const aiExportFormatDesc *desc = exporter.GetExportFormatDescription( 0 ); EXPECT_NE( desc, nullptr ); diff --git a/test/unit/utVersion.cpp b/test/unit/utVersion.cpp index 0189cd2a9..0de6ef39c 100644 --- a/test/unit/utVersion.cpp +++ b/test/unit/utVersion.cpp @@ -55,7 +55,7 @@ TEST_F( utVersion, aiGetLegalStringTest ) { TEST_F( utVersion, aiGetVersionMinorTest ) { EXPECT_EQ( aiGetVersionMinor(), 0U ); } - + TEST_F( utVersion, aiGetVersionMajorTest ) { EXPECT_EQ( aiGetVersionMajor(), 5U ); } diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index 2c000bb37..90070d63e 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -692,7 +692,7 @@ TEST_F(utglTF2ImportExport, indexOutOfRange) { } }; LogObserver logObserver; - + DefaultLogger::get()->attachStream(&logObserver); const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/IndexOutOfRange/IndexOutOfRange.gltf", aiProcess_ValidateDataStructure); ASSERT_NE(scene, nullptr); diff --git a/tools/assimp_cmd/CMakeLists.txt b/tools/assimp_cmd/CMakeLists.txt index 3a39fa748..5aeac0f7b 100644 --- a/tools/assimp_cmd/CMakeLists.txt +++ b/tools/assimp_cmd/CMakeLists.txt @@ -1,6 +1,6 @@ # Open Asset Import Library (assimp) # ---------------------------------------------------------------------- -# +# # Copyright (c) 2006-2021, assimp team diff --git a/tools/assimp_cmd/Export.cpp b/tools/assimp_cmd/Export.cpp index 1e2f10541..6c3c41de9 100644 --- a/tools/assimp_cmd/Export.cpp +++ b/tools/assimp_cmd/Export.cpp @@ -7,8 +7,8 @@ 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 +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 @@ -25,16 +25,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ diff --git a/tools/assimp_cmd/ImageExtractor.cpp b/tools/assimp_cmd/ImageExtractor.cpp index 105c4fe37..23aa9c249 100644 --- a/tools/assimp_cmd/ImageExtractor.cpp +++ b/tools/assimp_cmd/ImageExtractor.cpp @@ -7,8 +7,8 @@ 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 +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 @@ -25,16 +25,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ @@ -356,6 +356,6 @@ int Assimp_Extract(const char *const *params, unsigned int num) { return m; } } - + return AssimpCmdError::Success; } diff --git a/tools/assimp_cmd/Info.cpp b/tools/assimp_cmd/Info.cpp index e0d511a73..2c35ba227 100644 --- a/tools/assimp_cmd/Info.cpp +++ b/tools/assimp_cmd/Info.cpp @@ -316,7 +316,7 @@ int Assimp_Info (const char* const* params, unsigned int num) { printf("assimp info: Invalid arguments, verbose and silent at the same time are forbitten. "); return AssimpCmdInfoError::InvalidCombinaisonOfArguments; } - + // Parse post-processing flags unless -r was specified ImportData import; if (!raw) { diff --git a/tools/assimp_cmd/Main.cpp b/tools/assimp_cmd/Main.cpp index 2fb7559bb..8d76e1f5e 100644 --- a/tools/assimp_cmd/Main.cpp +++ b/tools/assimp_cmd/Main.cpp @@ -9,8 +9,8 @@ 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 +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 @@ -27,16 +27,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ @@ -47,7 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "Main.h" -const char* AICMD_MSG_ABOUT = +const char* AICMD_MSG_ABOUT = "------------------------------------------------------ \n" "Open Asset Import Library (\"Assimp\", https://github.com/assimp/assimp) \n" " -- Commandline toolchain --\n" @@ -55,7 +55,7 @@ const char* AICMD_MSG_ABOUT = "Version %i.%i %s%s%s%s%s(GIT commit %x)\n\n"; -const char* AICMD_MSG_HELP = +const char* AICMD_MSG_HELP = "assimp \n\n" " verbs:\n" " \tinfo - Quick file stats\n" @@ -106,7 +106,7 @@ int main (int argc, char* argv[]) } // assimp help - // Display some basic help (--help and -h work as well + // Display some basic help (--help and -h work as well // because people could try them intuitively) if (!strcmp(argv[1], "help") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) { printf("%s",AICMD_MSG_HELP); @@ -114,7 +114,7 @@ int main (int argc, char* argv[]) } // assimp cmpdump - // Compare two mini model dumps (regression suite) + // Compare two mini model dumps (regression suite) if (! strcmp(argv[1], "cmpdump")) { return Assimp_CompareDump (&argv[2],argc-2); } @@ -125,7 +125,7 @@ int main (int argc, char* argv[]) globalImporter = &imp; #ifndef ASSIMP_BUILD_NO_EXPORT - // + // Assimp::Exporter exp; globalExporter = &exp; #endif @@ -145,7 +145,7 @@ int main (int argc, char* argv[]) // List all export file formats supported by Assimp (not the file extensions, just the format identifiers!) if (! strcmp(argv[1], "listexport")) { aiString s; - + for(size_t i = 0, end = exp.GetExportFormatCount(); i < end; ++i) { const aiExportFormatDesc* const e = exp.GetExportFormatDescription(i); s.Append( e->id ); @@ -176,7 +176,7 @@ int main (int argc, char* argv[]) return AssimpCmdError::Success; } } - + printf("Unknown file format id: \'%s\'\n",argv[2]); return AssimpCmdError::UnknownFileFormat; } @@ -207,13 +207,13 @@ int main (int argc, char* argv[]) return Assimp_Info ((const char**)&argv[2],argc-2); } - // assimp dump - // Dump a model to a file + // assimp dump + // Dump a model to a file if (! strcmp(argv[1], "dump")) { return Assimp_Dump (&argv[2],argc-2); } - // assimp extract + // assimp extract // Extract an embedded texture from a file if (! strcmp(argv[1], "extract")) { return Assimp_Extract (&argv[2],argc-2); @@ -236,7 +236,7 @@ int main (int argc, char* argv[]) void SetLogStreams(const ImportData& imp) { printf("\nAttaching log stream ... OK\n"); - + unsigned int flags = 0; if (imp.logFile.length()) { flags |= aiDefaultLogStream_FILE; @@ -264,7 +264,7 @@ void PrintHorBar() // ------------------------------------------------------------------------------ // Import a specific file const aiScene* ImportModel( - const ImportData& imp, + const ImportData& imp, const std::string& path) { // Attach log streams @@ -282,7 +282,7 @@ const aiScene* ImportModel( if (imp.showLog) { PrintHorBar(); } - + // do the actual import, measure time const clock_t first = clock(); @@ -302,7 +302,7 @@ const aiScene* ImportModel( printf("Importing file ... OK \n import took approx. %.5f seconds\n" "\n",seconds); - if (imp.log) { + if (imp.log) { FreeLogStreams(); } return scene; @@ -310,8 +310,8 @@ const aiScene* ImportModel( #ifndef ASSIMP_BUILD_NO_EXPORT // ------------------------------------------------------------------------------ -bool ExportModel(const aiScene* pOut, - const ImportData& imp, +bool ExportModel(const aiScene* pOut, + const ImportData& imp, const std::string& path, const char* pID) { @@ -352,7 +352,7 @@ bool ExportModel(const aiScene* pOut, printf("Exporting file ... OK \n export took approx. %.5f seconds\n" "\n",seconds); - if (imp.log) { + if (imp.log) { FreeLogStreams(); } @@ -363,7 +363,7 @@ bool ExportModel(const aiScene* pOut, // ------------------------------------------------------------------------------ // Process standard arguments int ProcessStandardArguments( - ImportData& fill, + ImportData& fill, const char* const * params, unsigned int num) { @@ -396,7 +396,7 @@ int ProcessStandardArguments( // // -c --config-file= - for (unsigned int i = 0; i < num;++i) + for (unsigned int i = 0; i < num;++i) { const char *param = params[ i ]; printf( "param = %s\n", param ); @@ -504,11 +504,11 @@ int ProcessStandardArguments( else if (!strncmp(params[i], "-rx=", 4) || !strncmp(params[i], "--rotation-x=", 13)) { std::string value = std::string(params[i] + (params[i][1] == '-' ? 13 : 4)); fill.rot.x = std::stof(value); - } + } else if (!strncmp(params[i], "-ry=", 4) || !strncmp(params[i], "--rotation-y=", 13)) { std::string value = std::string(params[i] + (params[i][1] == '-' ? 13 : 4)); fill.rot.y = std::stof(value); - } + } else if (!strncmp(params[i], "-rz=", 4) || !strncmp(params[i], "--rotation-z=", 13)) { std::string value = std::string(params[i] + (params[i][1] == '-' ? 13 : 4)); fill.rot.z = std::stof(value); @@ -530,7 +530,7 @@ int ProcessStandardArguments( // ------------------------------------------------------------------------------ int Assimp_TestBatchLoad ( - const char* const* params, + const char* const* params, unsigned int num) { for(unsigned int i = 0; i < num; ++i) { diff --git a/tools/assimp_cmd/Main.h b/tools/assimp_cmd/Main.h index e7fbb6c75..5ac306abd 100644 --- a/tools/assimp_cmd/Main.h +++ b/tools/assimp_cmd/Main.h @@ -7,8 +7,8 @@ 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 +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 @@ -25,16 +25,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ @@ -144,7 +144,7 @@ enum AssimpCmdError { * @param params Command line parameters to be processed * @param num NUmber of params * @return An #AssimpCmdError value. */ -int ProcessStandardArguments(ImportData& fill, +int ProcessStandardArguments(ImportData& fill, const char* const* params, unsigned int num); @@ -153,7 +153,7 @@ int ProcessStandardArguments(ImportData& fill, * @param imp Import configuration to be used * @param path Path to the file to be read */ const aiScene* ImportModel( - const ImportData& imp, + const ImportData& imp, const std::string& path); #ifndef ASSIMP_BUILD_NO_EXPORT @@ -163,8 +163,8 @@ const aiScene* ImportModel( * @param imp Import configuration to be used * @param path Path to the file to be written * @param format Format id*/ -bool ExportModel(const aiScene* pOut, - const ImportData& imp, +bool ExportModel(const aiScene* pOut, + const ImportData& imp, const std::string& path, const char* pID); @@ -176,7 +176,7 @@ bool ExportModel(const aiScene* pOut, * @param Number of params * @return An #AssimpCmdError value.*/ int Assimp_Dump ( - const char* const* params, + const char* const* params, unsigned int num); /// \enum AssimpCmdExportError @@ -186,7 +186,7 @@ enum AssimpCmdExportError { FailedToExportModel, // Add new error codes here... - + LastAssimpCmdExportError, // Must be last. }; @@ -196,7 +196,7 @@ enum AssimpCmdExportError { * @param Number of params * @return Either an #AssimpCmdError or #AssimpCmdExportError value. */ int Assimp_Export ( - const char* const* params, + const char* const* params, unsigned int num); /// \enum AssimpCmdExtractError @@ -217,7 +217,7 @@ enum AssimpCmdExtractError { * @param Number of params * @return Either an #AssimpCmdError or #AssimpCmdExtractError value. */ int Assimp_Extract ( - const char* const* params, + const char* const* params, unsigned int num); /// \enum AssimpCmdCompareDumpError @@ -238,7 +238,7 @@ enum AssimpCmdCompareDumpError { * @param Number of params * @return Either an #AssimpCmdError or #AssimpCmdCompareDumpError. */ int Assimp_CompareDump ( - const char* const* params, + const char* const* params, unsigned int num); /// \enum AssimpCmdInfoError @@ -257,7 +257,7 @@ enum AssimpCmdInfoError { * @param Number of params * @return Either an #AssimpCmdError or #AssimpCmdInfoError value. */ int Assimp_Info ( - const char* const* params, + const char* const* params, unsigned int num); // ------------------------------------------------------------------------------ @@ -266,7 +266,7 @@ int Assimp_Info ( * @param Number of params * @return An #AssimpCmdError value. */ int Assimp_TestBatchLoad ( - const char* const* params, + const char* const* params, unsigned int num); diff --git a/tools/assimp_cmd/WriteDump.cpp b/tools/assimp_cmd/WriteDump.cpp index 5809d4ce6..fd8839a17 100644 --- a/tools/assimp_cmd/WriteDump.cpp +++ b/tools/assimp_cmd/WriteDump.cpp @@ -7,8 +7,8 @@ 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 +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 @@ -25,16 +25,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ diff --git a/tools/assimp_cmd/resource.h b/tools/assimp_cmd/resource.h index c516b5e5c..caf3a0a69 100644 --- a/tools/assimp_cmd/resource.h +++ b/tools/assimp_cmd/resource.h @@ -9,7 +9,7 @@ // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 diff --git a/tools/assimp_view/AnimEvaluator.h b/tools/assimp_view/AnimEvaluator.h index 566247604..394ebef4a 100644 --- a/tools/assimp_view/AnimEvaluator.h +++ b/tools/assimp_view/AnimEvaluator.h @@ -53,7 +53,7 @@ struct aiAnimation; namespace AssimpView { -/** +/** * @brief Calculates transformations for a given timestamp from a set of animation tracks. Not directly useful, * better use the AnimPlayer class. */ @@ -68,15 +68,15 @@ public: /// @brief The class destructor. ~AnimEvaluator(); - /// @brief Evaluates the animation tracks for a given time stamp. - /// The calculated pose can be retrieved as an array of transformation + /// @brief Evaluates the animation tracks for a given time stamp. + /// The calculated pose can be retrieved as an array of transformation /// matrices afterwards by calling GetTransformations(). - /// @param pTime The time for which you want to evaluate the animation, in seconds. - /// Will be mapped into the animation cycle, so it can get an arbitrary + /// @param pTime The time for which you want to evaluate the animation, in seconds. + /// Will be mapped into the animation cycle, so it can get an arbitrary /// value. Best use with ever-increasing time stamps. void Evaluate(double pTime); - /// @brief Returns the transform matrices calculated at the last Evaluate() call. + /// @brief Returns the transform matrices calculated at the last Evaluate() call. /// The array matches the mChannels array of the aiAnimation. const std::vector &GetTransformations() const { return mTransforms; } diff --git a/tools/assimp_view/CMakeLists.txt b/tools/assimp_view/CMakeLists.txt index 8ff556f05..0199392fe 100644 --- a/tools/assimp_view/CMakeLists.txt +++ b/tools/assimp_view/CMakeLists.txt @@ -1,6 +1,6 @@ # Open Asset Import Library (assimp) # ---------------------------------------------------------------------- -# +# # Copyright (c) 2006-2021, assimp team diff --git a/tools/assimp_view/Display.cpp b/tools/assimp_view/Display.cpp index 4178ab955..ac9aa5329 100644 --- a/tools/assimp_view/Display.cpp +++ b/tools/assimp_view/Display.cpp @@ -105,7 +105,7 @@ void GetNodeCount(aiNode* pcNode, unsigned int* piCnt) int CDisplay::EnableAnimTools(BOOL hm) { EnableWindow(GetDlgItem(g_hDlg,IDC_PLAY),hm); EnableWindow(GetDlgItem(g_hDlg,IDC_SLIDERANIM),hm); - + return 1; } @@ -171,7 +171,7 @@ int CDisplay::AddNodeToDisplayList( { iIndex += iDepth * 100; } - else + else iIndex += iDepth * 10; ai_snprintf(chTemp, MAXLEN,"Node %u",iIndex); } @@ -1053,7 +1053,7 @@ int CDisplay::OnSetupTextureView(TextureInfo* pcNew) case aiTextureOp_SmoothAdd: szOp = "addsmooth"; break; - default: + default: szOp = "mul"; break; }; diff --git a/tools/assimp_view/MessageProc.cpp b/tools/assimp_view/MessageProc.cpp index 580d35bf1..56831eb2a 100644 --- a/tools/assimp_view/MessageProc.cpp +++ b/tools/assimp_view/MessageProc.cpp @@ -2206,7 +2206,7 @@ int APIENTRY _tWinMain(HINSTANCE hInstance, "ASSIMP ModelViewer",MB_OK); return -4; } - + CLogDisplay::Instance().AddEntry("[OK] Here we go!"); // create the log window diff --git a/tools/assimp_view/Shaders.cpp b/tools/assimp_view/Shaders.cpp index a3404d5bf..b8ee8dbf8 100644 --- a/tools/assimp_view/Shaders.cpp +++ b/tools/assimp_view/Shaders.cpp @@ -7,8 +7,8 @@ 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 +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 @@ -25,16 +25,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ diff --git a/tools/assimp_view/assimp_view.cpp b/tools/assimp_view/assimp_view.cpp index 5ab7c53ad..e780e2aaf 100644 --- a/tools/assimp_view/assimp_view.cpp +++ b/tools/assimp_view/assimp_view.cpp @@ -7,8 +7,8 @@ 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 +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 @@ -25,16 +25,16 @@ 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ diff --git a/tools/assimp_view/resource.h b/tools/assimp_view/resource.h index 5077f6ccf..754eb69bd 100644 --- a/tools/assimp_view/resource.h +++ b/tools/assimp_view/resource.h @@ -223,7 +223,7 @@ #define IDC_STATIC -1 // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 From 4991f728c897a303b9cb0b307e164017fe8b4eca Mon Sep 17 00:00:00 2001 From: Krishty Date: Thu, 29 Jul 2021 14:23:52 +0200 Subject: [PATCH 216/335] =?UTF-8?q?style=20fix=C2=A0=E2=80=93=20initializi?= =?UTF-8?q?ng=20and=20assigning=20empty=20std::string=20properly?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit for details, see #3764 --- code/AssetLib/STEPParser/STEPFileReader.cpp | 4 ++-- code/AssetLib/glTF/glTFCommon.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/AssetLib/STEPParser/STEPFileReader.cpp b/code/AssetLib/STEPParser/STEPFileReader.cpp index ac6d83672..360277912 100644 --- a/code/AssetLib/STEPParser/STEPFileReader.cpp +++ b/code/AssetLib/STEPParser/STEPFileReader.cpp @@ -58,13 +58,13 @@ using namespace Assimp; namespace EXPRESS = STEP::EXPRESS; // ------------------------------------------------------------------------------------------------ -std::string AddLineNumber(const std::string& s,uint64_t line /*= LINE_NOT_SPECIFIED*/, const std::string& prefix = "") +std::string AddLineNumber(const std::string& s,uint64_t line /*= LINE_NOT_SPECIFIED*/, const std::string& prefix = std::string()) { return line == STEP::SyntaxError::LINE_NOT_SPECIFIED ? prefix+s : static_cast( (Formatter::format(),prefix,"(line ",line,") ",s) ); } // ------------------------------------------------------------------------------------------------ -std::string AddEntityID(const std::string& s,uint64_t entity /*= ENTITY_NOT_SPECIFIED*/, const std::string& prefix = "") +std::string AddEntityID(const std::string& s,uint64_t entity /*= ENTITY_NOT_SPECIFIED*/, const std::string& prefix = std::string()) { return entity == STEP::TypeError::ENTITY_NOT_SPECIFIED ? prefix+s : static_cast( (Formatter::format(),prefix,"(entity #",entity,") ",s)); } diff --git a/code/AssetLib/glTF/glTFCommon.h b/code/AssetLib/glTF/glTFCommon.h index 0506ad056..6f35e7881 100644 --- a/code/AssetLib/glTF/glTFCommon.h +++ b/code/AssetLib/glTF/glTFCommon.h @@ -196,7 +196,7 @@ inline std::string getCurrentAssetDir(const std::string &pFile) { std::string path = pFile; int pos = std::max(int(pFile.rfind('/')), int(pFile.rfind('\\'))); if (pos == int(std::string::npos)) { - return ""; + return std::string(); } return pFile.substr(0, pos + 1); From 42a7611f8511e5af0e357a56b658b381b14c8454 Mon Sep 17 00:00:00 2001 From: Krishty Date: Thu, 29 Jul 2021 14:39:22 +0200 Subject: [PATCH 217/335] style fix: indentation --- code/AssetLib/3MF/3MFXmlTags.h | 2 +- code/AssetLib/MMD/MMDPmxParser.cpp | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/code/AssetLib/3MF/3MFXmlTags.h b/code/AssetLib/3MF/3MFXmlTags.h index d447556d6..9fa2affd4 100644 --- a/code/AssetLib/3MF/3MFXmlTags.h +++ b/code/AssetLib/3MF/3MFXmlTags.h @@ -103,7 +103,7 @@ namespace XmlTag { const char* const PACKAGE_TEXTURE_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dtexture"; const char* const PACKAGE_CORE_PROPERTIES_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"; const char* const PACKAGE_THUMBNAIL_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"; - } +} } // Namespace D3MF } // Namespace Assimp diff --git a/code/AssetLib/MMD/MMDPmxParser.cpp b/code/AssetLib/MMD/MMDPmxParser.cpp index d57dc169a..ba8efa8e9 100644 --- a/code/AssetLib/MMD/MMDPmxParser.cpp +++ b/code/AssetLib/MMD/MMDPmxParser.cpp @@ -516,13 +516,13 @@ namespace pmx stream->read((char*) magic, sizeof(char) * 4); if (magic[0] != 0x50 || magic[1] != 0x4d || magic[2] != 0x58 || magic[3] != 0x20) { - throw DeadlyImportError("MMD: Invalid magic number."); - } + throw DeadlyImportError("MMD: Invalid magic number."); + } stream->read((char*) &version, sizeof(float)); if (version != 2.0f && version != 2.1f) { throw DeadlyImportError("MMD: Unsupported version (must be 2.0 or 2.1): ", ai_to_string(version)); - } + } this->setting.Read(stream); this->model_name = ReadString(stream, setting.encoding); From a1eaaaa0e398ce860c0ecfe42d61f98222f57208 Mon Sep 17 00:00:00 2001 From: Krishty Date: Thu, 29 Jul 2021 14:45:39 +0200 Subject: [PATCH 218/335] fix comments fixes some copy-paste errors in logger comments introduced with 89584c167aaf341a6d717083bb9a52a4c1b0ace1 --- include/assimp/Logger.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/assimp/Logger.hpp b/include/assimp/Logger.hpp index 3ca4a6cb2..18e913953 100644 --- a/include/assimp/Logger.hpp +++ b/include/assimp/Logger.hpp @@ -99,8 +99,8 @@ public: virtual ~Logger(); // ---------------------------------------------------------------------- - /** @brief Writes a info message - * @param message Info message*/ + /** @brief Writes a debug message + * @param message Debug message*/ void debug(const char* message); template @@ -109,7 +109,7 @@ public: } // ---------------------------------------------------------------------- - /** @brief Writes a debug message + /** @brief Writes a debug message * @param message Debug message*/ void verboseDebug(const char* message); @@ -140,7 +140,7 @@ public: // ---------------------------------------------------------------------- /** @brief Writes an error message - * @param message Info message*/ + * @param message Error message*/ void error(const char* message); template From bb53961fa98c226267813a80ed2b59f940f5ab20 Mon Sep 17 00:00:00 2001 From: Krishty Date: Thu, 29 Jul 2021 14:57:25 +0200 Subject: [PATCH 219/335] more range-based for f6b4370f6ac1bf2db0ef9edeb0ff1ce8c7e61aab and 7c822f23bd075d3621d2e2f33188e1659f657b31 introduced raw loops on data types with heavy nesting; range-based for suits better here --- code/AssetLib/glTF2/glTF2Importer.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index d80df97d2..921aaad9e 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -992,8 +992,8 @@ void ParseExtensions(aiMetadata *metadata, const CustomExtension &extension) { metadata->Add(extension.name.c_str(), extension.mBoolValue.value); } else if (extension.mValues.isPresent) { aiMetadata val; - for (size_t i = 0; i < extension.mValues.value.size(); ++i) { - ParseExtensions(&val, extension.mValues.value[i]); + for (auto const & subExtension : extension.mValues.value) { + ParseExtensions(&val, subExtension); } metadata->Add(extension.name.c_str(), val); } @@ -1001,8 +1001,8 @@ void ParseExtensions(aiMetadata *metadata, const CustomExtension &extension) { void ParseExtras(aiMetadata *metadata, const CustomExtension &extension) { if (extension.mValues.isPresent) { - for (size_t i = 0; i < extension.mValues.value.size(); ++i) { - ParseExtensions(metadata, extension.mValues.value[i]); + for (auto const & subExtension : extension.mValues.value) { + ParseExtensions(metadata, subExtension); } } } From 6ce524893169285bf523da430fc230f0caa0a151 Mon Sep 17 00:00:00 2001 From: Spectrum76 <49938052+Spectrum76@users.noreply.github.com> Date: Thu, 29 Jul 2021 21:38:23 +0530 Subject: [PATCH 220/335] Update .gitignore --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 7849cab65..09d631ee8 100644 --- a/.gitignore +++ b/.gitignore @@ -94,6 +94,7 @@ test/gtest/src/gtest-stamp/gtest-gitinfo.txt test/gtest/src/gtest-stamp/gtest-gitclone-lastrun.txt Assimp.opensdf contrib/zlib/CTestTestfile.cmake +contrib/zlib/Debug/zlibstaticd.pdb ipch/assimp_viewer-44bbbcd1/assimp_viewerd-ccc45335.ipch bin64/assimp-vc140-mt.dll bin64/assimp-vc140-mtd.dll @@ -118,4 +119,4 @@ tools/assimp_qt_viewer/moc_mainwindow.cpp_parameters tools/assimp_qt_viewer/ui_mainwindow.h #Generated directory -generated/* \ No newline at end of file +generated/* From 8d6d6b48c3dad5bb244c0bb7bb6097be4ff083eb Mon Sep 17 00:00:00 2001 From: Hill Ma Date: Mon, 2 Aug 2021 11:58:46 -0700 Subject: [PATCH 221/335] Obj: make a predicate more robust. Since we might encounter invalid input it is a good idea to check the actual size of the array. --- code/AssetLib/Obj/ObjFileImporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/Obj/ObjFileImporter.cpp b/code/AssetLib/Obj/ObjFileImporter.cpp index d6232be81..5e2b48e24 100644 --- a/code/AssetLib/Obj/ObjFileImporter.cpp +++ b/code/AssetLib/Obj/ObjFileImporter.cpp @@ -468,7 +468,7 @@ void ObjFileImporter::createVertexArray(const ObjFile::Model *pModel, } // Copy all vertex colors - if (!pModel->m_VertexColors.empty()) { + if (vertex < pModel->m_VertexColors.size()) { const aiVector3D &color = pModel->m_VertexColors[vertex]; pMesh->mColors[0][newIndex] = aiColor4D(color.x, color.y, color.z, 1.0); } From 538cb3125c6d83a0a4d76ec589e21a787093a2f0 Mon Sep 17 00:00:00 2001 From: Hill Ma Date: Mon, 2 Aug 2021 11:47:35 -0700 Subject: [PATCH 222/335] Use strlen() rather than fixed length in fast_atof.h This avoids reading past the length of the input string. --- include/assimp/fast_atof.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/assimp/fast_atof.h b/include/assimp/fast_atof.h index aea793f35..43bbbff64 100644 --- a/include/assimp/fast_atof.h +++ b/include/assimp/fast_atof.h @@ -194,7 +194,7 @@ uint64_t strtoul10_64( const char* in, const char** out=0, unsigned int* max_ino if ( *in < '0' || *in > '9' ) { // The string is known to be bad, so don't risk printing the whole thing. - throw ExceptionType("The string \"", ai_str_toprintable(in, 30), "\" cannot be converted into a value." ); + throw ExceptionType("The string \"", ai_str_toprintable(in, (int)strlen(in)), "\" cannot be converted into a value." ); } for ( ;; ) { @@ -294,7 +294,7 @@ const char* fast_atoreal_move(const char* c, Real& out, bool check_comma = true) if (!(c[0] >= '0' && c[0] <= '9') && !((c[0] == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9')) { // The string is known to be bad, so don't risk printing the whole thing. - throw ExceptionType("Cannot parse string \"", ai_str_toprintable(c, 30), + throw ExceptionType("Cannot parse string \"", ai_str_toprintable(c, (int)strlen(c)), "\" as a real number: does not start with digit " "or decimal point followed by digit."); } From c3fcbfd2c11d8bb1f62cbabf5654c322a3ed77a7 Mon Sep 17 00:00:00 2001 From: Mykhailo Smoliakov Date: Wed, 11 Aug 2021 08:54:34 +0300 Subject: [PATCH 223/335] Fix issue of incorrect reading of PBR properties such as base and emissive color in FBX --- code/AssetLib/FBX/FBXConverter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/AssetLib/FBX/FBXConverter.cpp b/code/AssetLib/FBX/FBXConverter.cpp index e0da78583..a92745fb6 100644 --- a/code/AssetLib/FBX/FBXConverter.cpp +++ b/code/AssetLib/FBX/FBXConverter.cpp @@ -2131,7 +2131,7 @@ void FBXConverter::SetShadingPropertiesCommon(aiMaterial *out_mat, const Propert if (ok) { out_mat->AddProperty(&Emissive, 1, AI_MATKEY_COLOR_EMISSIVE); } else { - const aiColor3D &emissiveColor = GetColorPropertyFromMaterial(props, "Maya|emissive", ok); + const aiColor3D &emissiveColor = GetColorProperty(props, "Maya|emissive", ok); if (ok) { out_mat->AddProperty(&emissiveColor, 1, AI_MATKEY_COLOR_EMISSIVE); } @@ -2218,7 +2218,7 @@ void FBXConverter::SetShadingPropertiesCommon(aiMaterial *out_mat, const Propert } // PBR material information - const aiColor3D &baseColor = GetColorPropertyFromMaterial(props, "Maya|base_color", ok); + const aiColor3D &baseColor = GetColorProperty(props, "Maya|base_color", ok); if (ok) { out_mat->AddProperty(&baseColor, 1, AI_MATKEY_BASE_COLOR); } From 69c152d7c130e96287eada4467611322f8bd64ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Ubi=C3=B1as?= Date: Wed, 11 Aug 2021 18:57:21 -0400 Subject: [PATCH 224/335] Add missing diagnostic push --- code/AssetLib/glTF/glTFExporter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/code/AssetLib/glTF/glTFExporter.cpp b/code/AssetLib/glTF/glTFExporter.cpp index 810263f52..10943905c 100644 --- a/code/AssetLib/glTF/glTFExporter.cpp +++ b/code/AssetLib/glTF/glTFExporter.cpp @@ -526,6 +526,7 @@ void ExportSkin(Asset& mAsset, const aiMesh* aimesh, Ref& meshRef, Ref Date: Wed, 11 Aug 2021 19:32:26 -0400 Subject: [PATCH 225/335] Disable diagnostic for LogStream comparator --- code/Common/Assimp.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/code/Common/Assimp.cpp b/code/Common/Assimp.cpp index ca0912979..3a0ec7d60 100644 --- a/code/Common/Assimp.cpp +++ b/code/Common/Assimp.cpp @@ -72,12 +72,25 @@ namespace Assimp { // underlying structure for aiPropertyStore typedef BatchLoader::PropertyMap PropertyMap; +#if defined(__has_warning) +#if __has_warning("-Wordered-compare-function-pointers") +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wordered-compare-function-pointers" +#endif +#endif + /** Stores the LogStream objects for all active C log streams */ struct mpred { bool operator()(const aiLogStream &s0, const aiLogStream &s1) const { return s0.callback < s1.callback && s0.user < s1.user; } }; + +#if defined(__has_warning) +#if __has_warning("-Wordered-compare-function-pointers") +#pragma GCC diagnostic pop +#endif +#endif typedef std::map LogStreamMap; /** Stores the LogStream objects allocated by #aiGetPredefinedLogStream */ From b95df54225c0406290143d0d4ed08d7439420652 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Robert=20Ubi=C3=B1as?= Date: Wed, 11 Aug 2021 19:33:10 -0400 Subject: [PATCH 226/335] Remove unused code --- contrib/openddlparser/code/OpenDDLExport.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/contrib/openddlparser/code/OpenDDLExport.cpp b/contrib/openddlparser/code/OpenDDLExport.cpp index 6d6f2458d..d235b553b 100644 --- a/contrib/openddlparser/code/OpenDDLExport.cpp +++ b/contrib/openddlparser/code/OpenDDLExport.cpp @@ -134,10 +134,9 @@ bool OpenDDLExport::writeToStream(const std::string &statement) { } bool OpenDDLExport::writeNode(DDLNode *node, std::string &statement) { - bool success(true); writeNodeHeader(node, statement); if (node->hasProperties()) { - success |= writeProperties(node, statement); + writeProperties(node, statement); } writeLineEnd(statement); From 4b1ff645e3d21c370ffd7447063263dfe1711cd1 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 12 Aug 2021 21:13:07 +0200 Subject: [PATCH 227/335] closes https://github.com/assimp/assimp/issues/3398: Add support for embedded textures --- code/AssetLib/3DS/3DSConverter.cpp | 11 +- code/AssetLib/3DS/3DSLoader.h | 9 + code/AssetLib/3MF/3MFTypes.h | 125 +++++ code/AssetLib/3MF/3MFXmlTags.h | 10 +- code/AssetLib/3MF/D3MFImporter.cpp | 524 +-------------------- code/AssetLib/3MF/D3MFOpcPackage.cpp | 60 ++- code/AssetLib/3MF/D3MFOpcPackage.h | 12 +- code/AssetLib/3MF/XmlSerializer.cpp | 590 ++++++++++++++++++++++++ code/AssetLib/3MF/XmlSerializer.h | 104 +++++ code/AssetLib/Collada/ColladaLoader.cpp | 23 +- code/AssetLib/Collada/ColladaParser.cpp | 6 +- code/AssetLib/Obj/ObjFileImporter.cpp | 2 +- code/AssetLib/glTF2/glTF2Importer.cpp | 5 +- code/CMakeLists.txt | 5 +- include/assimp/scene.h | 2 +- include/assimp/vector3.h | 33 +- tools/assimp_view/MaterialManager.h | 73 ++- tools/assimp_view/MeshRenderer.cpp | 9 +- 18 files changed, 1006 insertions(+), 597 deletions(-) create mode 100644 code/AssetLib/3MF/3MFTypes.h create mode 100644 code/AssetLib/3MF/XmlSerializer.cpp create mode 100644 code/AssetLib/3MF/XmlSerializer.h diff --git a/code/AssetLib/3DS/3DSConverter.cpp b/code/AssetLib/3DS/3DSConverter.cpp index aca16b0d6..add1553bc 100644 --- a/code/AssetLib/3DS/3DSConverter.cpp +++ b/code/AssetLib/3DS/3DSConverter.cpp @@ -68,8 +68,8 @@ void Discreet3DSImporter::ReplaceDefaultMaterial() { unsigned int idx(NotSet); for (unsigned int i = 0; i < mScene->mMaterials.size(); ++i) { std::string s = mScene->mMaterials[i].mName; - for (std::string::iterator it = s.begin(); it != s.end(); ++it) { - *it = static_cast(::tolower(static_cast(*it))); + for (char & it : s) { + it = static_cast(::tolower(static_cast(it))); } if (std::string::npos == s.find("default")) continue; @@ -79,12 +79,7 @@ void Discreet3DSImporter::ReplaceDefaultMaterial() { mScene->mMaterials[i].mDiffuse.r != mScene->mMaterials[i].mDiffuse.b) continue; - if (mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0 || - mScene->mMaterials[i].sTexBump.mMapName.length() != 0 || - mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 || - mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0 || - mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0 || - mScene->mMaterials[i].sTexShininess.mMapName.length() != 0) { + if (ContainsTextures(i)) { continue; } idx = i; diff --git a/code/AssetLib/3DS/3DSLoader.h b/code/AssetLib/3DS/3DSLoader.h index 2091fbeb7..dba20eede 100644 --- a/code/AssetLib/3DS/3DSLoader.h +++ b/code/AssetLib/3DS/3DSLoader.h @@ -208,6 +208,15 @@ protected: */ void ReplaceDefaultMaterial(); + bool ContainsTextures(unsigned int i) const { + return mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0 || + mScene->mMaterials[i].sTexBump.mMapName.length() != 0 || + mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 || + mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0 || + mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0 || + mScene->mMaterials[i].sTexShininess.mMapName.length() != 0; + } + // ------------------------------------------------------------------- /** Convert the whole scene */ diff --git a/code/AssetLib/3MF/3MFTypes.h b/code/AssetLib/3MF/3MFTypes.h new file mode 100644 index 000000000..e404bf012 --- /dev/null +++ b/code/AssetLib/3MF/3MFTypes.h @@ -0,0 +1,125 @@ +#pragma once + +#include +#include +#include +#include +#include + +struct aiMaterial; +struct aiMesh; + +namespace Assimp { +namespace D3MF { + +enum class ResourceType { + RT_Object, + RT_BaseMaterials, + RT_EmbeddedTexture2D, + RT_Texture2DGroup, + RT_Unknown +}; // To be extended with other resource types (eg. material extension resources like Texture2d, Texture2dGroup...) + +class Resource { +public: + int mId; + + Resource(int id) : + mId(id) { + // empty + } + + virtual ~Resource() { + // empty + } + + virtual ResourceType getType() const { + return ResourceType::RT_Unknown; + } +}; + +class EmbeddedTexture : public Resource { +public: + std::string mPath; + std::string mContentType; + std::string mTilestyleU; + std::string mTilestyleV; + std::vector mBuffer; + + EmbeddedTexture(int id) : + Resource(id), + mPath(), + mContentType(), + mTilestyleU(), + mTilestyleV() { + // empty + } + + ~EmbeddedTexture() = default; + + ResourceType getType() const override { + return ResourceType::RT_EmbeddedTexture2D; + } +}; + +class Texture2DGroup : public Resource { +public: + std::vector mTex2dCoords; + int mTexId; + Texture2DGroup(int id) : + Resource(id), + mTexId(-1) { + // empty + } + + ~Texture2DGroup() = default; + + ResourceType getType() const override { + return ResourceType::RT_Texture2DGroup; + } +}; + +class BaseMaterials : public Resource { +public: + std::vector mMaterialIndex; + + BaseMaterials(int id) : + Resource(id), + mMaterialIndex() { + // empty + } + + ~BaseMaterials() = default; + + ResourceType getType() const override { + return ResourceType::RT_BaseMaterials; + } +}; + +struct Component { + int mObjectId; + aiMatrix4x4 mTransformation; +}; + +class Object : public Resource { +public: + std::vector mMeshes; + std::vector mMeshIndex; + std::vector mComponents; + std::string mName; + + Object(int id) : + Resource(id), + mName(std::string("Object_") + ai_to_string(id)) { + // empty + } + + ~Object() = default; + + ResourceType getType() const override { + return ResourceType::RT_Object; + } +}; + +} // namespace D3MF +} // namespace Assimp diff --git a/code/AssetLib/3MF/3MFXmlTags.h b/code/AssetLib/3MF/3MFXmlTags.h index d447556d6..28b198f5d 100644 --- a/code/AssetLib/3MF/3MFXmlTags.h +++ b/code/AssetLib/3MF/3MFXmlTags.h @@ -80,13 +80,21 @@ namespace XmlTag { const char* const item = "item"; const char* const objectid = "objectid"; const char* const transform = "transform"; + const char *const path = "path"; // Material definitions const char* const basematerials = "basematerials"; - const char* const basematerials_id = "id"; const char* const basematerials_base = "base"; const char* const basematerials_name = "name"; const char* const basematerials_displaycolor = "displaycolor"; + const char* const texture_2d = "m:texture2d"; + const char *const texture_group = "m:texture2dgroup"; + const char *const texture_content_type = "contenttype"; + const char *const texture_tilestyleu = "tilestyleu"; + const char *const texture_tilestylev = "tilestylev"; + const char *const texture_2d_coord = "m:tex2coord"; + const char *const texture_cuurd_u = "u"; + const char *const texture_cuurd_v = "v"; // Meta info tags const char* const CONTENT_TYPES_ARCHIVE = "[Content_Types].xml"; diff --git a/code/AssetLib/3MF/D3MFImporter.cpp b/code/AssetLib/3MF/D3MFImporter.cpp index 747af7cfc..fe3c07744 100644 --- a/code/AssetLib/3MF/D3MFImporter.cpp +++ b/code/AssetLib/3MF/D3MFImporter.cpp @@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "D3MFImporter.h" #include "3MFXmlTags.h" #include "D3MFOpcPackage.h" +#include "XmlSerializer.h" #include #include @@ -61,513 +62,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include +#include namespace Assimp { -namespace D3MF { - -enum class ResourceType { - RT_Object, - RT_BaseMaterials, - RT_Unknown -}; // To be extended with other resource types (eg. material extension resources like Texture2d, Texture2dGroup...) - -class Resource { -public: - int mId; - - Resource(int id) : - mId(id) { - // empty - } - - virtual ~Resource() { - // empty - } - - virtual ResourceType getType() const { - return ResourceType::RT_Unknown; - } -}; - -class BaseMaterials : public Resource { -public: - std::vector mMaterials; - std::vector mMaterialIndex; - - BaseMaterials(int id) : - Resource(id), - mMaterials(), - mMaterialIndex() { - // empty - } - - ~BaseMaterials() = default; - - ResourceType getType() const override { - return ResourceType::RT_BaseMaterials; - } -}; - -struct Component { - int mObjectId; - aiMatrix4x4 mTransformation; -}; - -class Object : public Resource { -public: - std::vector mMeshes; - std::vector mMeshIndex; - std::vector mComponents; - std::string mName; - - Object(int id) : - Resource(id), - mName(std::string("Object_") + ai_to_string(id)) { - // empty - } - - ~Object() = default; - - ResourceType getType() const override { - return ResourceType::RT_Object; - } -}; - -class XmlSerializer { -public: - XmlSerializer(XmlParser *xmlParser) : - mResourcesDictionnary(), - mMaterialCount(0), - mMeshCount(0), - mXmlParser(xmlParser) { - // empty - } - - ~XmlSerializer() { - for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); ++it ) { - delete it->second; - } - } - - void ImportXml(aiScene *scene) { - if (nullptr == scene) { - return; - } - - scene->mRootNode = new aiNode(XmlTag::RootTag); - - XmlNode node = mXmlParser->getRootNode().child(XmlTag::model); - if (node.empty()) { - return; - } - XmlNode resNode = node.child(XmlTag::resources); - for (auto ¤tNode : resNode.children()) { - const std::string currentNodeName = currentNode.name(); - if (currentNodeName == XmlTag::object) { - ReadObject(currentNode); - } else if (currentNodeName == XmlTag::basematerials) { - ReadBaseMaterials(currentNode); - } else if (currentNodeName == XmlTag::meta) { - ReadMetadata(currentNode); - } - } - - XmlNode buildNode = node.child(XmlTag::build); - for (auto ¤tNode : buildNode.children()) { - const std::string currentNodeName = currentNode.name(); - if (currentNodeName == XmlTag::item) { - int objectId = -1; - std::string transformationMatrixStr; - aiMatrix4x4 transformationMatrix; - getNodeAttribute(currentNode, D3MF::XmlTag::objectid, objectId); - bool hasTransform = getNodeAttribute(currentNode, D3MF::XmlTag::transform, transformationMatrixStr); - - auto it = mResourcesDictionnary.find(objectId); - if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) { - Object *obj = static_cast(it->second); - if (hasTransform) { - transformationMatrix = parseTransformMatrix(transformationMatrixStr); - } - - addObjectToNode(scene->mRootNode, obj, transformationMatrix); - } - } - } - - // import the metadata - if (!mMetaData.empty()) { - const size_t numMeta = mMetaData.size(); - scene->mMetaData = aiMetadata::Alloc(static_cast(numMeta)); - for (size_t i = 0; i < numMeta; ++i) { - aiString val(mMetaData[i].value); - scene->mMetaData->Set(static_cast(i), mMetaData[i].name, val); - } - } - - // import the meshes - scene->mNumMeshes = static_cast(mMeshCount); - if (scene->mNumMeshes != 0) { - scene->mMeshes = new aiMesh *[scene->mNumMeshes](); - for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); ++it) { - if (it->second->getType() == ResourceType::RT_Object) { - Object *obj = static_cast(it->second); - ai_assert(nullptr != obj); - for (unsigned int i = 0; i < obj->mMeshes.size(); ++i) { - scene->mMeshes[obj->mMeshIndex[i]] = obj->mMeshes[i]; - } - } - } - } - - // import the materials - scene->mNumMaterials = mMaterialCount; - if (scene->mNumMaterials != 0) { - scene->mMaterials = new aiMaterial *[scene->mNumMaterials]; - for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); ++it) { - if (it->second->getType() == ResourceType::RT_BaseMaterials) { - BaseMaterials *baseMaterials = static_cast(it->second); - for (unsigned int i = 0; i < baseMaterials->mMaterials.size(); ++i) { - scene->mMaterials[baseMaterials->mMaterialIndex[i]] = baseMaterials->mMaterials[i]; - } - } - } - } - } - -private: - void addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform) { - ai_assert(nullptr != obj); - - aiNode *sceneNode = new aiNode(obj->mName); - sceneNode->mNumMeshes = static_cast(obj->mMeshes.size()); - sceneNode->mMeshes = new unsigned int[sceneNode->mNumMeshes]; - std::copy(obj->mMeshIndex.begin(), obj->mMeshIndex.end(), sceneNode->mMeshes); - - sceneNode->mTransformation = nodeTransform; - if (nullptr != parent) { - parent->addChildren(1, &sceneNode); - } - - for (size_t i = 0; i < obj->mComponents.size(); ++i) { - Component c = obj->mComponents[i]; - auto it = mResourcesDictionnary.find(c.mObjectId); - if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) { - addObjectToNode(sceneNode, static_cast(it->second), c.mTransformation); - } - } - } - - bool getNodeAttribute(const XmlNode &node, const std::string &attribute, std::string &value) { - pugi::xml_attribute objectAttribute = node.attribute(attribute.c_str()); - if (!objectAttribute.empty()) { - value = objectAttribute.as_string(); - return true; - } - - return false; - } - - bool getNodeAttribute(const XmlNode &node, const std::string &attribute, int &value) { - std::string strValue; - bool ret = getNodeAttribute(node, attribute, strValue); - if (ret) { - value = std::atoi(strValue.c_str()); - return true; - } - - return false; - } - - aiMatrix4x4 parseTransformMatrix(std::string matrixStr) { - // split the string - std::vector numbers; - std::string currentNumber; - for (size_t i = 0; i < matrixStr.size(); ++i) { - const char c = matrixStr[i]; - if (c == ' ') { - if (currentNumber.size() > 0) { - float f = std::stof(currentNumber); - numbers.push_back(f); - currentNumber.clear(); - } - } else { - currentNumber.push_back(c); - } - } - if (currentNumber.size() > 0) { - const float f = std::stof(currentNumber); - numbers.push_back(f); - } - - aiMatrix4x4 transformMatrix; - transformMatrix.a1 = numbers[0]; - transformMatrix.b1 = numbers[1]; - transformMatrix.c1 = numbers[2]; - transformMatrix.d1 = 0; - - transformMatrix.a2 = numbers[3]; - transformMatrix.b2 = numbers[4]; - transformMatrix.c2 = numbers[5]; - transformMatrix.d2 = 0; - - transformMatrix.a3 = numbers[6]; - transformMatrix.b3 = numbers[7]; - transformMatrix.c3 = numbers[8]; - transformMatrix.d3 = 0; - - transformMatrix.a4 = numbers[9]; - transformMatrix.b4 = numbers[10]; - transformMatrix.c4 = numbers[11]; - transformMatrix.d4 = 1; - - return transformMatrix; - } - - void ReadObject(XmlNode &node) { - int id = -1, pid = -1, pindex = -1; - bool hasId = getNodeAttribute(node, XmlTag::id, id); - bool hasPid = getNodeAttribute(node, XmlTag::pid, pid); - bool hasPindex = getNodeAttribute(node, XmlTag::pindex, pindex); - if (!hasId) { - return; - } - - Object *obj = new Object(id); - - for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == D3MF::XmlTag::mesh) { - auto mesh = ReadMesh(currentNode); - mesh->mName.Set(ai_to_string(id)); - - if (hasPid) { - auto it = mResourcesDictionnary.find(pid); - if (hasPindex && it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_BaseMaterials) { - BaseMaterials *materials = static_cast(it->second); - mesh->mMaterialIndex = materials->mMaterialIndex[pindex]; - } - } - - obj->mMeshes.push_back(mesh); - obj->mMeshIndex.push_back(mMeshCount); - mMeshCount++; - } else if (currentName == D3MF::XmlTag::components) { - for (XmlNode ¤tSubNode : currentNode.children()) { - const std::string subNodeName = currentSubNode.name(); - if (subNodeName == D3MF::XmlTag::component) { - int objectId = -1; - std::string componentTransformStr; - aiMatrix4x4 componentTransform; - if (getNodeAttribute(currentSubNode, D3MF::XmlTag::transform, componentTransformStr)) { - componentTransform = parseTransformMatrix(componentTransformStr); - } - - if (getNodeAttribute(currentSubNode, D3MF::XmlTag::objectid, objectId)) { - obj->mComponents.push_back({ objectId, componentTransform }); - } - } - } - } - } - - mResourcesDictionnary.insert(std::make_pair(id, obj)); - } - - aiMesh *ReadMesh(XmlNode &node) { - aiMesh *mesh = new aiMesh(); - - for (XmlNode ¤tNode : node.children()) { - const std::string currentName = currentNode.name(); - if (currentName == XmlTag::vertices) { - ImportVertices(currentNode, mesh); - } else if (currentName == XmlTag::triangles) { - ImportTriangles(currentNode, mesh); - } - } - - return mesh; - } - - void ReadMetadata(XmlNode &node) { - pugi::xml_attribute attribute = node.attribute(D3MF::XmlTag::meta_name); - const std::string name = attribute.as_string(); - const std::string value = node.value(); - if (name.empty()) { - return; - } - - MetaEntry entry; - entry.name = name; - entry.value = value; - mMetaData.push_back(entry); - } - - void ImportVertices(XmlNode &node, aiMesh *mesh) { - std::vector vertices; - for (XmlNode ¤tNode : node.children()) { - const std::string currentName = currentNode.name(); - if (currentName == XmlTag::vertex) { - vertices.push_back(ReadVertex(currentNode)); - } - } - - mesh->mNumVertices = static_cast(vertices.size()); - mesh->mVertices = new aiVector3D[mesh->mNumVertices]; - std::copy(vertices.begin(), vertices.end(), mesh->mVertices); - } - - aiVector3D ReadVertex(XmlNode &node) { - aiVector3D vertex; - vertex.x = ai_strtof(node.attribute(XmlTag::x).as_string(), nullptr); - vertex.y = ai_strtof(node.attribute(XmlTag::y).as_string(), nullptr); - vertex.z = ai_strtof(node.attribute(XmlTag::z).as_string(), nullptr); - - return vertex; - } - - void ImportTriangles(XmlNode &node, aiMesh *mesh) { - std::vector faces; - for (XmlNode ¤tNode : node.children()) { - const std::string currentName = currentNode.name(); - if (currentName == XmlTag::triangle) { - aiFace face = ReadTriangle(currentNode); - faces.push_back(face); - - int pid = 0, p1 = 0; - bool hasPid = getNodeAttribute(currentNode, D3MF::XmlTag::pid, pid); - bool hasP1 = getNodeAttribute(currentNode, D3MF::XmlTag::p1, p1); - - if (hasPid && hasP1) { - auto it = mResourcesDictionnary.find(pid); - if (it != mResourcesDictionnary.end()) { - if (it->second->getType() == ResourceType::RT_BaseMaterials) { - BaseMaterials *baseMaterials = static_cast(it->second); - mesh->mMaterialIndex = baseMaterials->mMaterialIndex[p1]; - } - // TODO: manage the separation into several meshes if the triangles of the mesh do not all refer to the same material - } - } - } - } - - mesh->mNumFaces = static_cast(faces.size()); - mesh->mFaces = new aiFace[mesh->mNumFaces]; - mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; - - std::copy(faces.begin(), faces.end(), mesh->mFaces); - } - - aiFace ReadTriangle(XmlNode &node) { - aiFace face; - - face.mNumIndices = 3; - face.mIndices = new unsigned int[face.mNumIndices]; - face.mIndices[0] = static_cast(std::atoi(node.attribute(XmlTag::v1).as_string())); - face.mIndices[1] = static_cast(std::atoi(node.attribute(XmlTag::v2).as_string())); - face.mIndices[2] = static_cast(std::atoi(node.attribute(XmlTag::v3).as_string())); - - return face; - } - - void ReadBaseMaterials(XmlNode &node) { - int id = -1; - if (getNodeAttribute(node, D3MF::XmlTag::basematerials_id, id)) { - BaseMaterials *baseMaterials = new BaseMaterials(id); - - for (XmlNode ¤tNode : node.children()) { - const std::string currentName = currentNode.name(); - if (currentName == XmlTag::basematerials_base) { - baseMaterials->mMaterialIndex.push_back(mMaterialCount); - baseMaterials->mMaterials.push_back(readMaterialDef(currentNode, id)); - ++mMaterialCount; - } - } - - mResourcesDictionnary.insert(std::make_pair(id, baseMaterials)); - } - } - - bool parseColor(const char *color, aiColor4D &diffuse) { - if (nullptr == color) { - return false; - } - - //format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1) - const size_t len = strlen(color); - if (9 != len && 7 != len) { - return false; - } - - const char *buf(color); - if ('#' != buf[0]) { - return false; - } - - char r[3] = { buf[1], buf[2], '\0' }; - diffuse.r = static_cast(strtol(r, nullptr, 16)) / ai_real(255.0); - - char g[3] = { buf[3], buf[4], '\0' }; - diffuse.g = static_cast(strtol(g, nullptr, 16)) / ai_real(255.0); - - char b[3] = { buf[5], buf[6], '\0' }; - diffuse.b = static_cast(strtol(b, nullptr, 16)) / ai_real(255.0); - - if (7 == len) - return true; - - char a[3] = { buf[7], buf[8], '\0' }; - diffuse.a = static_cast(strtol(a, nullptr, 16)) / ai_real(255.0); - - return true; - } - - void assignDiffuseColor(XmlNode &node, aiMaterial *mat) { - const char *color = node.attribute(XmlTag::basematerials_displaycolor).as_string(); - aiColor4D diffuse; - if (parseColor(color, diffuse)) { - mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE); - } - } - - aiMaterial *readMaterialDef(XmlNode &node, unsigned int basematerialsId) { - aiMaterial *material = new aiMaterial(); - material->mNumProperties = 0; - std::string name; - bool hasName = getNodeAttribute(node, D3MF::XmlTag::basematerials_name, name); - - std::string stdMaterialName; - const std::string strId(ai_to_string(basematerialsId)); - stdMaterialName += "id"; - stdMaterialName += strId; - stdMaterialName += "_"; - if (hasName) { - stdMaterialName += std::string(name); - } else { - stdMaterialName += "basemat_"; - stdMaterialName += ai_to_string(mMaterialCount - basematerialsId); - } - - aiString assimpMaterialName(stdMaterialName); - material->AddProperty(&assimpMaterialName, AI_MATKEY_NAME); - - assignDiffuseColor(node, material); - - return material; - } - -private: - struct MetaEntry { - std::string name; - std::string value; - }; - std::vector mMetaData; - std::map mResourcesDictionnary; - unsigned int mMaterialCount, mMeshCount; - XmlParser *mXmlParser; -}; - -} //namespace D3MF using namespace D3MF; @@ -597,7 +94,9 @@ bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bo const std::string extension(GetExtension(filename)); if (extension == desc.mFileExtensions) { return true; - } else if (!extension.length() || checkSig) { + } + + if (!extension.length() || checkSig) { if (nullptr == pIOHandler) { return false; } @@ -611,7 +110,7 @@ bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bo return false; } -void D3MFImporter::SetupProperties(const Importer * /*pImp*/) { +void D3MFImporter::SetupProperties(const Importer*) { // empty } @@ -624,8 +123,17 @@ void D3MFImporter::InternReadFile(const std::string &filename, aiScene *pScene, XmlParser xmlParser; if (xmlParser.parse(opcPackage.RootStream())) { - XmlSerializer xmlSerializer(&xmlParser); + XmlSerializer xmlSerializer(&xmlParser, &opcPackage); xmlSerializer.ImportXml(pScene); + + const std::vector &tex = opcPackage.GetEmbeddedTextures(); + if (!tex.empty()) { + pScene->mNumTextures = static_cast(tex.size()); + pScene->mTextures = new aiTexture *[pScene->mNumTextures]; + for (unsigned int i = 0; i < pScene->mNumTextures; ++i) { + pScene->mTextures[i] = tex[i]; + } + } } } diff --git a/code/AssetLib/3MF/D3MFOpcPackage.cpp b/code/AssetLib/3MF/D3MFOpcPackage.cpp index dbf4f2e10..d3b667583 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.cpp +++ b/code/AssetLib/3MF/D3MFOpcPackage.cpp @@ -43,14 +43,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "D3MFOpcPackage.h" #include - #include #include #include #include #include #include - +#include #include "3MFXmlTags.h" #include #include @@ -64,11 +63,12 @@ namespace Assimp { namespace D3MF { // ------------------------------------------------------------------------------------------------ -typedef std::shared_ptr OpcPackageRelationshipPtr; +using OpcPackageRelationshipPtr = std::shared_ptr; class OpcPackageRelationshipReader { public: - OpcPackageRelationshipReader(XmlParser &parser) { + OpcPackageRelationshipReader(XmlParser &parser) : + m_relationShips() { XmlNode root = parser.getRootNode(); ParseRootNode(root); } @@ -91,6 +91,7 @@ public: if (relPtr->id.empty() || relPtr->type.empty() || relPtr->target.empty()) { return false; } + return true; } @@ -100,7 +101,7 @@ public: } for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { - std::string name = currentNode.name(); + const std::string name = currentNode.name(); if (name == "Relationship") { OpcPackageRelationshipPtr relPtr(new OpcPackageRelationship()); relPtr->id = currentNode.attribute(XmlTag::RELS_ATTRIB_ID).as_string(); @@ -116,11 +117,20 @@ public: std::vector m_relationShips; }; +static bool IsEmbeddedTexture( const std::string &filename ) { + const std::string extension = BaseImporter::GetExtension(filename); + + if (extension == "jpg" || extension == "png") { + return true; + } + + return false; +} // ------------------------------------------------------------------------------------------------ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : mRootStream(nullptr), mZipArchive() { - mZipArchive.reset(new ZipArchiveIOSystem(pIOHandler, rFile)); + mZipArchive = std::make_unique(pIOHandler, rFile); if (!mZipArchive->isOpen()) { throw DeadlyImportError("Failed to open file ", rFile, "."); } @@ -141,13 +151,13 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : } std::string rootFile = ReadPackageRootRelationship(fileStream); - if (rootFile.size() > 0 && rootFile[0] == '/') { + if (!rootFile.empty() && rootFile[0] == '/') { rootFile = rootFile.substr(1); if (rootFile[0] == '/') { // deal with zip-bug rootFile = rootFile.substr(1); } - } + } ASSIMP_LOG_VERBOSE_DEBUG(rootFile); @@ -158,9 +168,12 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : if (nullptr == mRootStream) { throw DeadlyImportError("Cannot open root-file in archive : " + rootFile); } - } else if (file == D3MF::XmlTag::CONTENT_TYPES_ARCHIVE) { ASSIMP_LOG_WARN("Ignored file of unsupported type CONTENT_TYPES_ARCHIVES", file); + } else if (IsEmbeddedTexture(file)) { + IOStream *fileStream = mZipArchive->Open(file.c_str()); + LoadEmbeddedTextures(fileStream, file); + mZipArchive->Close(fileStream); } else { ASSIMP_LOG_WARN("Ignored file of unknown type: ", file); } @@ -169,20 +182,25 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : D3MFOpcPackage::~D3MFOpcPackage() { mZipArchive->Close(mRootStream); + mZipArchive = nullptr; } IOStream *D3MFOpcPackage::RootStream() const { return mRootStream; } -static const std::string ModelRef = "3D/3dmodel.model"; +const std::vector &D3MFOpcPackage::GetEmbeddedTextures() const { + return mEmbeddedTextures; +} + +static const char *const ModelRef = "3D/3dmodel.model"; bool D3MFOpcPackage::validate() { if (nullptr == mRootStream || nullptr == mZipArchive) { return false; } - return mZipArchive->Exists(ModelRef.c_str()); + return mZipArchive->Exists(ModelRef); } std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream *stream) { @@ -204,6 +222,26 @@ std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream *stream) { return (*itr)->target; } +void D3MFOpcPackage::LoadEmbeddedTextures(IOStream *fileStream, const std::string &filename) { + if (nullptr == fileStream) { + return; + } + + const size_t size = fileStream->FileSize(); + if (0 == size) { + return; + } + + char *data = new char[size]; + fileStream->Read(data, 1, size); + aiTexture *texture = new aiTexture; + texture->mFilename.Set(filename.c_str()); + texture->mWidth = static_cast(size); + texture->mHeight = 0; + texture->pcData = (aiTexel*) data; + mEmbeddedTextures.emplace_back(texture); +} + } // Namespace D3MF } // Namespace Assimp diff --git a/code/AssetLib/3MF/D3MFOpcPackage.h b/code/AssetLib/3MF/D3MFOpcPackage.h index 22b4510d0..93928b9a7 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.h +++ b/code/AssetLib/3MF/D3MFOpcPackage.h @@ -46,8 +46,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +struct aiTexture; + namespace Assimp { - class ZipArchiveIOSystem; + +class ZipArchiveIOSystem; namespace D3MF { @@ -63,16 +66,19 @@ public: ~D3MFOpcPackage(); IOStream* RootStream() const; bool validate(); + const std::vector &GetEmbeddedTextures() const; protected: std::string ReadPackageRootRelationship(IOStream* stream); + void LoadEmbeddedTextures(IOStream *fileStream, const std::string &filename); private: IOStream* mRootStream; std::unique_ptr mZipArchive; + std::vector mEmbeddedTextures; }; -} // Namespace D3MF -} // Namespace Assimp +} // namespace D3MF +} // namespace Assimp #endif // D3MFOPCPACKAGE_H diff --git a/code/AssetLib/3MF/XmlSerializer.cpp b/code/AssetLib/3MF/XmlSerializer.cpp new file mode 100644 index 000000000..d93c7b121 --- /dev/null +++ b/code/AssetLib/3MF/XmlSerializer.cpp @@ -0,0 +1,590 @@ +/* +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. + +---------------------------------------------------------------------- +*/ +#include "XmlSerializer.h" +#include "D3MFOpcPackage.h" +#include "3MFXmlTags.h" +#include "3MFTypes.h" +#include + +namespace Assimp { +namespace D3MF { + +static const int IdNotSet = -1; + +XmlSerializer::XmlSerializer(XmlParser *xmlParser, D3MFOpcPackage *archive) : + mResourcesDictionnary(), + mMeshCount(0), + mXmlParser(xmlParser), + mD3MFOpcPackage(archive) { + ai_assert(nullptr != xmlParser); + ai_assert(nullptr != archive); +} + +XmlSerializer::~XmlSerializer() { + for (auto &it : mResourcesDictionnary) { + delete it.second; + } +} + +void XmlSerializer::ImportXml(aiScene *scene) { + if (nullptr == scene) { + return; + } + + scene->mRootNode = new aiNode(XmlTag::RootTag); + XmlNode node = mXmlParser->getRootNode().child(XmlTag::model); + if (node.empty()) { + return; + } + + XmlNode resNode = node.child(XmlTag::resources); + for (auto ¤tNode : resNode.children()) { + const std::string currentNodeName = currentNode.name(); + if (currentNodeName == XmlTag::texture_2d) { + ReadEmbeddecTexture(currentNode); + } else if (currentNodeName == XmlTag::texture_group) { + ReadTextureGroup(currentNode); + } else if (currentNodeName == XmlTag::object) { + ReadObject(currentNode); + } else if (currentNodeName == XmlTag::basematerials) { + ReadBaseMaterials(currentNode); + } else if (currentNodeName == XmlTag::meta) { + ReadMetadata(currentNode); + } + } + StoreMaterialsInScene(scene); + XmlNode buildNode = node.child(XmlTag::build); + if (buildNode.empty()) { + return; + } + + for (auto ¤tNode : buildNode.children()) { + const std::string currentNodeName = currentNode.name(); + if (currentNodeName == XmlTag::item) { + int objectId = IdNotSet; + std::string transformationMatrixStr; + aiMatrix4x4 transformationMatrix; + getNodeAttribute(currentNode, D3MF::XmlTag::objectid, objectId); + bool hasTransform = getNodeAttribute(currentNode, D3MF::XmlTag::transform, transformationMatrixStr); + + auto it = mResourcesDictionnary.find(objectId); + if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) { + Object *obj = static_cast(it->second); + if (hasTransform) { + transformationMatrix = parseTransformMatrix(transformationMatrixStr); + } + + addObjectToNode(scene->mRootNode, obj, transformationMatrix); + } + } + } + + // import the metadata + if (!mMetaData.empty()) { + const size_t numMeta = mMetaData.size(); + scene->mMetaData = aiMetadata::Alloc(static_cast(numMeta)); + for (size_t i = 0; i < numMeta; ++i) { + aiString val(mMetaData[i].value); + scene->mMetaData->Set(static_cast(i), mMetaData[i].name, val); + } + } + + // import the meshes, materials are already stored + scene->mNumMeshes = static_cast(mMeshCount); + if (scene->mNumMeshes != 0) { + scene->mMeshes = new aiMesh *[scene->mNumMeshes](); + for (auto &it : mResourcesDictionnary) { + if (it.second->getType() == ResourceType::RT_Object) { + Object *obj = static_cast(it.second); + ai_assert(nullptr != obj); + for (unsigned int i = 0; i < obj->mMeshes.size(); ++i) { + scene->mMeshes[obj->mMeshIndex[i]] = obj->mMeshes[i]; + } + } + } + } +} + +void XmlSerializer::addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform) { + ai_assert(nullptr != obj); + + aiNode *sceneNode = new aiNode(obj->mName); + sceneNode->mNumMeshes = static_cast(obj->mMeshes.size()); + sceneNode->mMeshes = new unsigned int[sceneNode->mNumMeshes]; + std::copy(obj->mMeshIndex.begin(), obj->mMeshIndex.end(), sceneNode->mMeshes); + + sceneNode->mTransformation = nodeTransform; + if (nullptr != parent) { + parent->addChildren(1, &sceneNode); + } + + for (Assimp::D3MF::Component c : obj->mComponents) { + auto it = mResourcesDictionnary.find(c.mObjectId); + if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) { + addObjectToNode(sceneNode, static_cast(it->second), c.mTransformation); + } + } +} + +bool XmlSerializer::getNodeAttribute(const XmlNode &node, const std::string &attribute, std::string &value) { + pugi::xml_attribute objectAttribute = node.attribute(attribute.c_str()); + if (!objectAttribute.empty()) { + value = objectAttribute.as_string(); + return true; + } + + return false; +} + +bool XmlSerializer::getNodeAttribute(const XmlNode &node, const std::string &attribute, int &value) { + std::string strValue; + bool ret = getNodeAttribute(node, attribute, strValue); + if (ret) { + value = std::atoi(strValue.c_str()); + return true; + } + + return false; +} + +aiMatrix4x4 XmlSerializer::parseTransformMatrix(std::string matrixStr) { + // split the string + std::vector numbers; + std::string currentNumber; + for (char c : matrixStr) { + if (c == ' ') { + if (!currentNumber.empty()) { + float f = std::stof(currentNumber); + numbers.push_back(f); + currentNumber.clear(); + } + } else { + currentNumber.push_back(c); + } + } + if (!currentNumber.empty()) { + const float f = std::stof(currentNumber); + numbers.push_back(f); + } + + aiMatrix4x4 transformMatrix; + transformMatrix.a1 = numbers[0]; + transformMatrix.b1 = numbers[1]; + transformMatrix.c1 = numbers[2]; + transformMatrix.d1 = 0; + + transformMatrix.a2 = numbers[3]; + transformMatrix.b2 = numbers[4]; + transformMatrix.c2 = numbers[5]; + transformMatrix.d2 = 0; + + transformMatrix.a3 = numbers[6]; + transformMatrix.b3 = numbers[7]; + transformMatrix.c3 = numbers[8]; + transformMatrix.d3 = 0; + + transformMatrix.a4 = numbers[9]; + transformMatrix.b4 = numbers[10]; + transformMatrix.c4 = numbers[11]; + transformMatrix.d4 = 1; + + return transformMatrix; +} + +void XmlSerializer::ReadObject(XmlNode &node) { + int id = IdNotSet, pid = IdNotSet, pindex = IdNotSet; + bool hasId = getNodeAttribute(node, XmlTag::id, id); + if (!hasId) { + return; + } + + bool hasPid = getNodeAttribute(node, XmlTag::pid, pid); + bool hasPindex = getNodeAttribute(node, XmlTag::pindex, pindex); + + Object *obj = new Object(id); + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == D3MF::XmlTag::mesh) { + auto mesh = ReadMesh(currentNode); + mesh->mName.Set(ai_to_string(id)); + + if (hasPid) { + auto it = mResourcesDictionnary.find(pid); + if (hasPindex && it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_BaseMaterials) { + BaseMaterials *materials = static_cast(it->second); + mesh->mMaterialIndex = materials->mMaterialIndex[pindex]; + } + } + + obj->mMeshes.push_back(mesh); + obj->mMeshIndex.push_back(mMeshCount); + mMeshCount++; + } else if (currentName == D3MF::XmlTag::components) { + for (XmlNode ¤tSubNode : currentNode.children()) { + const std::string subNodeName = currentSubNode.name(); + if (subNodeName == D3MF::XmlTag::component) { + int objectId = IdNotSet; + std::string componentTransformStr; + aiMatrix4x4 componentTransform; + if (getNodeAttribute(currentSubNode, D3MF::XmlTag::transform, componentTransformStr)) { + componentTransform = parseTransformMatrix(componentTransformStr); + } + + if (getNodeAttribute(currentSubNode, D3MF::XmlTag::objectid, objectId)) { + obj->mComponents.push_back({ objectId, componentTransform }); + } + } + } + } + } + + mResourcesDictionnary.insert(std::make_pair(id, obj)); +} + +aiMesh *XmlSerializer::ReadMesh(XmlNode &node) { + if (node.empty()) { + return nullptr; + } + + aiMesh *mesh = new aiMesh(); + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == XmlTag::vertices) { + ImportVertices(currentNode, mesh); + } else if (currentName == XmlTag::triangles) { + ImportTriangles(currentNode, mesh); + } + } + + return mesh; +} + +void XmlSerializer::ReadMetadata(XmlNode &node) { + pugi::xml_attribute attribute = node.attribute(D3MF::XmlTag::meta_name); + const std::string name = attribute.as_string(); + const std::string value = node.value(); + if (name.empty()) { + return; + } + + MetaEntry entry; + entry.name = name; + entry.value = value; + mMetaData.push_back(entry); +} + +void XmlSerializer::ImportVertices(XmlNode &node, aiMesh *mesh) { + ai_assert(nullptr != mesh); + + std::vector vertices; + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == XmlTag::vertex) { + vertices.push_back(ReadVertex(currentNode)); + } + } + + mesh->mNumVertices = static_cast(vertices.size()); + mesh->mVertices = new aiVector3D[mesh->mNumVertices]; + std::copy(vertices.begin(), vertices.end(), mesh->mVertices); +} + +aiVector3D XmlSerializer::ReadVertex(XmlNode &node) { + aiVector3D vertex; + vertex.x = ai_strtof(node.attribute(XmlTag::x).as_string(), nullptr); + vertex.y = ai_strtof(node.attribute(XmlTag::y).as_string(), nullptr); + vertex.z = ai_strtof(node.attribute(XmlTag::z).as_string(), nullptr); + + return vertex; +} + +void XmlSerializer::ImportTriangles(XmlNode &node, aiMesh *mesh) { + std::vector faces; + + const size_t numTriangles = std::distance(node.children(XmlTag::triangle).begin(), node.children(XmlTag::triangle).end()); + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == XmlTag::triangle) { + int pid = IdNotSet, p1 = IdNotSet; + bool hasPid = getNodeAttribute(currentNode, D3MF::XmlTag::pid, pid); + bool hasP1 = getNodeAttribute(currentNode, D3MF::XmlTag::p1, p1); + + if (hasPid && hasP1) { + auto it = mResourcesDictionnary.find(pid); + if (it != mResourcesDictionnary.end()) { + if (it->second->getType() == ResourceType::RT_BaseMaterials) { + BaseMaterials *baseMaterials = static_cast(it->second); + mesh->mMaterialIndex = baseMaterials->mMaterialIndex[p1]; + } else if (it->second->getType() == ResourceType::RT_Texture2DGroup) { + if (mesh->mTextureCoords[0] == nullptr) { + Texture2DGroup *group = static_cast(it->second); + const std::string name = ai_to_string(group->mTexId); + for (size_t i = 0; i < mMaterials.size(); ++i) { + if (name == mMaterials[i]->GetName().C_Str()) { + mesh->mMaterialIndex = static_cast(i); + } + } + mesh->mTextureCoords[0] = new aiVector3D[group->mTex2dCoords.size()]; + for (unsigned int i = 0; i < group->mTex2dCoords.size(); ++i) { + mesh->mTextureCoords[0][i] = aiVector3D(group->mTex2dCoords[i].x, group->mTex2dCoords[i].y, 0); + } + } + } + } + } + + aiFace face = ReadTriangle(currentNode); + faces.push_back(face); + } + } + + mesh->mNumFaces = static_cast(faces.size()); + mesh->mFaces = new aiFace[mesh->mNumFaces]; + mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + + std::copy(faces.begin(), faces.end(), mesh->mFaces); +} + +aiFace XmlSerializer::ReadTriangle(XmlNode &node) { + aiFace face; + + face.mNumIndices = 3; + face.mIndices = new unsigned int[face.mNumIndices]; + face.mIndices[0] = static_cast(std::atoi(node.attribute(XmlTag::v1).as_string())); + face.mIndices[1] = static_cast(std::atoi(node.attribute(XmlTag::v2).as_string())); + face.mIndices[2] = static_cast(std::atoi(node.attribute(XmlTag::v3).as_string())); + + return face; +} + +void XmlSerializer::ReadBaseMaterials(XmlNode &node) { + int id = IdNotSet; + if (getNodeAttribute(node, D3MF::XmlTag::id, id)) { + BaseMaterials *baseMaterials = new BaseMaterials(id); + + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == XmlTag::basematerials_base) { + baseMaterials->mMaterialIndex.push_back(static_cast(mMaterials.size())); + mMaterials.push_back(readMaterialDef(currentNode, id)); + } + } + + mResourcesDictionnary.insert(std::make_pair(id, baseMaterials)); + } +} + +static const size_t ColRGBA_Len = 9; +static const size_t ColRGB_Len = 7; + +// format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1) +static bool validateColorString(const char *color) { + const size_t len = strlen(color); + if (ColRGBA_Len != len && ColRGB_Len != len) { + return false; + } + + return true; +} + +bool XmlSerializer::parseColor(const char *color, aiColor4D &diffuse) { + if (nullptr == color) { + return false; + } + + if (!validateColorString(color)) { + return false; + } + + //const char *buf(color); + if ('#' != color[0]) { + return false; + } + + char r[3] = { color[1], color[2], '\0' }; + diffuse.r = static_cast(strtol(r, nullptr, 16)) / ai_real(255.0); + + char g[3] = { color[3], color[4], '\0' }; + diffuse.g = static_cast(strtol(g, nullptr, 16)) / ai_real(255.0); + + char b[3] = { color[5], color[6], '\0' }; + diffuse.b = static_cast(strtol(b, nullptr, 16)) / ai_real(255.0); + const size_t len = strlen(color); + if (ColRGB_Len == len) { + return true; + } + + char a[3] = { color[7], color[8], '\0' }; + diffuse.a = static_cast(strtol(a, nullptr, 16)) / ai_real(255.0); + + return true; +} + +void XmlSerializer::ReadEmbeddecTexture(XmlNode &node) { + if (node.empty()) { + return; + } + + std::string value; + EmbeddedTexture *tex2D = nullptr; + if (XmlParser::getStdStrAttribute(node, XmlTag::id, value)) { + tex2D = new EmbeddedTexture(atoi(value.c_str())); + } + if (nullptr == tex2D) { + return; + } + + if (XmlParser::getStdStrAttribute(node, XmlTag::path, value)) { + tex2D->mPath = value; + } + if (XmlParser::getStdStrAttribute(node, XmlTag::texture_content_type, value)) { + tex2D->mContentType = value; + } + if (XmlParser::getStdStrAttribute(node, XmlTag::texture_tilestyleu, value)) { + tex2D->mTilestyleU = value; + } + if (XmlParser::getStdStrAttribute(node, XmlTag::texture_tilestylev, value)) { + tex2D->mTilestyleV = value; + } + mEmbeddedTextures.emplace_back(tex2D); + StoreEmbeddedTexture(tex2D); +} + +void XmlSerializer::StoreEmbeddedTexture(EmbeddedTexture *tex) { + aiMaterial *mat = new aiMaterial; + aiString s; + s.Set(ai_to_string(tex->mId).c_str()); + mat->AddProperty(&s, AI_MATKEY_NAME); + s.Set(tex->mPath); + mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(0)); + + aiColor3D col; + mat->AddProperty(&col, 1, AI_MATKEY_COLOR_DIFFUSE); + mat->AddProperty(&col, 1, AI_MATKEY_COLOR_AMBIENT); + mat->AddProperty(&col, 1, AI_MATKEY_COLOR_EMISSIVE); + mat->AddProperty(&col, 1, AI_MATKEY_COLOR_SPECULAR); + mMaterials.emplace_back(mat); +} + +void XmlSerializer::ReadTextureCoords2D(XmlNode &node, Texture2DGroup *tex2DGroup) { + if (node.empty() || nullptr == tex2DGroup) { + return; + } + int id = IdNotSet; + if (XmlParser::getIntAttribute(node, "texid", id)) { + tex2DGroup->mTexId = id; + } + double value; + for (XmlNode currentNode : node.children()) { + const std::string currentName = currentNode.name(); + aiVector2D texCoord; + if (currentName == XmlTag::texture_2d_coord) { + XmlParser::getDoubleAttribute(currentNode, XmlTag::texture_cuurd_u, value); + texCoord.x = (ai_real)value; + XmlParser::getDoubleAttribute(currentNode, XmlTag::texture_cuurd_v, value); + texCoord.y = (ai_real)value; + tex2DGroup->mTex2dCoords.push_back(texCoord); + } + } +} + +void XmlSerializer::ReadTextureGroup(XmlNode &node) { + if (node.empty()) { + return; + } + + int id = IdNotSet; + if (!XmlParser::getIntAttribute(node, XmlTag::id, id)) { + return; + } + + Texture2DGroup *group = new Texture2DGroup(id); + ReadTextureCoords2D(node, group); + mResourcesDictionnary.insert(std::make_pair(id, group)); +} + +void XmlSerializer::assignDiffuseColor(XmlNode &node, aiMaterial *mat) { + const char *color = node.attribute(XmlTag::basematerials_displaycolor).as_string(); + aiColor4D diffuse; + if (parseColor(color, diffuse)) { + mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE); + } +} + +aiMaterial *XmlSerializer::readMaterialDef(XmlNode &node, unsigned int basematerialsId) { + aiMaterial *material = new aiMaterial(); + material->mNumProperties = 0; + std::string name; + bool hasName = getNodeAttribute(node, D3MF::XmlTag::basematerials_name, name); + + std::string stdMaterialName; + const std::string strId(ai_to_string(basematerialsId)); + stdMaterialName += "id"; + stdMaterialName += strId; + stdMaterialName += "_"; + if (hasName) { + stdMaterialName += std::string(name); + } else { + stdMaterialName += "basemat_"; + stdMaterialName += ai_to_string(mMaterials.size()); + } + + aiString assimpMaterialName(stdMaterialName); + material->AddProperty(&assimpMaterialName, AI_MATKEY_NAME); + + assignDiffuseColor(node, material); + + return material; +} + +void XmlSerializer::StoreMaterialsInScene( aiScene *scene ) { + if (nullptr == scene || mMaterials.empty()) { + return; + } + + scene->mNumMaterials = static_cast(mMaterials.size()); + scene->mMaterials = new aiMaterial*[scene->mNumMaterials]; + for (size_t i = 0; i < mMaterials.size(); ++i) { + scene->mMaterials[i] = mMaterials[i]; + } +} +} // namespace D3MF +} // namespace Assimp diff --git a/code/AssetLib/3MF/XmlSerializer.h b/code/AssetLib/3MF/XmlSerializer.h new file mode 100644 index 000000000..40300d70d --- /dev/null +++ b/code/AssetLib/3MF/XmlSerializer.h @@ -0,0 +1,104 @@ +/* +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. + +---------------------------------------------------------------------- +*/ +#pragma once + +#include +#include +#include +#include + +struct aiNode; +struct aiMesh; +struct aiMaterial; + +namespace Assimp { +namespace D3MF { + +class Resource; +class D3MFOpcPackage; +class Object; +class Texture2DGroup; +class EmbeddedTexture; + +class XmlSerializer { +public: + XmlSerializer(XmlParser *xmlParser, D3MFOpcPackage *archive); + ~XmlSerializer(); + void ImportXml(aiScene *scene); + +private: + void addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform); + bool getNodeAttribute(const XmlNode &node, const std::string &attribute, std::string &value); + bool getNodeAttribute(const XmlNode &node, const std::string &attribute, int &value); + aiMatrix4x4 parseTransformMatrix(std::string matrixStr); + void ReadObject(XmlNode &node); + aiMesh *ReadMesh(XmlNode &node); + void ReadMetadata(XmlNode &node); + void ImportVertices(XmlNode &node, aiMesh *mesh); + aiVector3D ReadVertex(XmlNode &node); + void ImportTriangles(XmlNode &node, aiMesh *mesh); + aiFace ReadTriangle(XmlNode &node); + void ReadBaseMaterials(XmlNode &node); + bool parseColor(const char *color, aiColor4D &diffuse); + void ReadEmbeddecTexture(XmlNode &node); + void StoreEmbeddedTexture(EmbeddedTexture *tex); + void ReadTextureCoords2D(XmlNode &node, Texture2DGroup *tex2DGroup); + void ReadTextureGroup(XmlNode &node); + void assignDiffuseColor(XmlNode &node, aiMaterial *mat); + aiMaterial *readMaterialDef(XmlNode &node, unsigned int basematerialsId); + void StoreMaterialsInScene(aiScene *scene); + +private: + struct MetaEntry { + std::string name; + std::string value; + }; + std::vector mMetaData; + std::vector mEmbeddedTextures; + std::vector mMaterials; + std::map mResourcesDictionnary; + unsigned int mMeshCount; + XmlParser *mXmlParser; + D3MFOpcPackage *mD3MFOpcPackage; +}; + +} // namespace D3MF +} // namespace Assimp diff --git a/code/AssetLib/Collada/ColladaLoader.cpp b/code/AssetLib/Collada/ColladaLoader.cpp index 492e6971f..876a6fd1d 100644 --- a/code/AssetLib/Collada/ColladaLoader.cpp +++ b/code/AssetLib/Collada/ColladaLoader.cpp @@ -135,14 +135,15 @@ bool ColladaLoader::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool // XML - too generic, we need to open the file and search for typical keywords if (extension == "xml" || !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) { + // 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 *tokens[] = { "mNumMeshes = static_cast(newMeshRefs.size()); - if (newMeshRefs.size()) { + if (!newMeshRefs.empty()) { struct UIntTypeConverter { unsigned int operator()(const size_t &v) const { return static_cast(v); @@ -1538,9 +1539,9 @@ void ColladaLoader::AddTexture(aiMaterial &mat, map = sampler.mUVId; } else { map = -1; - for (std::string::const_iterator it = sampler.mUVChannel.begin(); it != sampler.mUVChannel.end(); ++it) { - if (IsNumeric(*it)) { - map = strtoul10(&(*it)); + for (char it : sampler.mUVChannel) { + if (IsNumeric(it)) { + map = strtoul10(&it); break; } } @@ -1682,7 +1683,7 @@ void ColladaLoader::BuildMaterials(ColladaParser &pParser, aiScene * /*pScene*/) // store the material mMaterialIndexByName[matIt->first] = newMats.size(); - newMats.push_back(std::pair(&effect, mat)); + newMats.emplace_back(&effect, mat); } // ScenePreprocessor generates a default material automatically if none is there. // All further code here in this loader works well without a valid material so diff --git a/code/AssetLib/Collada/ColladaParser.cpp b/code/AssetLib/Collada/ColladaParser.cpp index 37c7274f4..bc5f951ab 100644 --- a/code/AssetLib/Collada/ColladaParser.cpp +++ b/code/AssetLib/Collada/ColladaParser.cpp @@ -918,7 +918,7 @@ void ColladaParser::ReadMaterial(XmlNode &node, Collada::Material &pMaterial) { if (currentName == "instance_effect") { std::string url; readUrlAttribute(currentNode, url); - pMaterial.mEffect = url.c_str(); + pMaterial.mEffect = url; } } } @@ -2208,8 +2208,8 @@ void ColladaParser::ReadMaterialVertexInputBinding(XmlNode &node, Collada::Seman void ColladaParser::ReadEmbeddedTextures(ZipArchiveIOSystem &zip_archive) { // Attempt to load any undefined Collada::Image in ImageLibrary - for (ImageLibrary::iterator it = mImageLibrary.begin(); it != mImageLibrary.end(); ++it) { - Collada::Image &image = (*it).second; + for (auto & it : mImageLibrary) { + Collada::Image &image = it.second; if (image.mImageData.empty()) { std::unique_ptr image_file(zip_archive.Open(image.mFileName.c_str())); diff --git a/code/AssetLib/Obj/ObjFileImporter.cpp b/code/AssetLib/Obj/ObjFileImporter.cpp index d6232be81..e45533c30 100644 --- a/code/AssetLib/Obj/ObjFileImporter.cpp +++ b/code/AssetLib/Obj/ObjFileImporter.cpp @@ -162,7 +162,7 @@ void ObjFileImporter::InternReadFile(const std::string &file, aiScene *pScene, I // ------------------------------------------------------------------------------------------------ // Create the data from parsed obj-file void ObjFileImporter::CreateDataFromImport(const ObjFile::Model *pModel, aiScene *pScene) { - if (0L == pModel) { + if (nullptr == pModel) { return; } diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index d80df97d2..08a1ea0ed 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1470,10 +1470,11 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) { } } - if (numEmbeddedTexs == 0) + if (numEmbeddedTexs == 0) { return; + } - ASSIMP_LOG_DEBUG("Importing ", numEmbeddedTexs, " embedded textures"); + ASSIMP_LOG_DEBUG("Importing ", numEmbeddedTexs, " embedded textures"); mScene->mTextures = new aiTexture *[numEmbeddedTexs]; std::fill(mScene->mTextures, mScene->mTextures + numEmbeddedTexs, nullptr); diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index fd92d41e3..130ab8870 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -820,7 +820,10 @@ ADD_ASSIMP_IMPORTER( GLTF AssetLib/glTF2/glTF2Importer.h ) -ADD_ASSIMP_IMPORTER( 3MF +ADD_ASSIMP_IMPORTER(3MF + AssetLib/3MF/3MFTypes.h + AssetLib/3MF/XmlSerializer.h + AssetLib/3MF/XmlSerializer.cpp AssetLib/3MF/D3MFImporter.h AssetLib/3MF/D3MFImporter.cpp AssetLib/3MF/D3MFOpcPackage.h diff --git a/include/assimp/scene.h b/include/assimp/scene.h index 522ddc6dc..b54c29dfa 100644 --- a/include/assimp/scene.h +++ b/include/assimp/scene.h @@ -400,7 +400,7 @@ struct aiScene //! Returns an embedded texture and its index std::pair GetEmbeddedTextureAndIndex(const char* filename) const { - if(nullptr==filename) { + if (nullptr==filename) { return std::make_pair(nullptr, -1); } // lookup using texture ID (if referenced like: "*1", "*2", etc.) diff --git a/include/assimp/vector3.h b/include/assimp/vector3.h index b084cf9c3..e3ad0b680 100644 --- a/include/assimp/vector3.h +++ b/include/assimp/vector3.h @@ -5,8 +5,6 @@ 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, @@ -65,27 +63,49 @@ template class aiMatrix3x3t; template class aiMatrix4x4t; // --------------------------------------------------------------------------- -/** Represents a three-dimensional vector. */ +/// @brief Represents a three-dimensional vector. +// --------------------------------------------------------------------------- template class aiVector3t { public: + /// @brief The default class constructor. aiVector3t() AI_NO_EXCEPT : x(), y(), z() {} + + /// @brief The class constructor with the components. + /// @param _x The x-component for the vector. + /// @param _y The y-component for the vector. + /// @param _z The z-component for the vector. aiVector3t(TReal _x, TReal _y, TReal _z) : x(_x), y(_y), z(_z) {} + + /// @brief The class constructor with a default value. + /// @param _xyz The value for x, y and z. explicit aiVector3t (TReal _xyz ) : x(_xyz), y(_xyz), z(_xyz) {} + + /// @brief The copy constructor. + /// @param o The instance to copy from. aiVector3t( const aiVector3t& o ) = default; - // combined operators + /// @brief combined operators + /// @brief The copy constructor. const aiVector3t& operator += (const aiVector3t& o); + + /// @brief The copy constructor. const aiVector3t& operator -= (const aiVector3t& o); + + /// @brief The copy constructor. const aiVector3t& operator *= (TReal f); + + /// @brief The copy constructor. const aiVector3t& operator /= (TReal f); - // transform vector by matrix + /// @brief Transform vector by matrix aiVector3t& operator *= (const aiMatrix3x3t& mat); aiVector3t& operator *= (const aiMatrix4x4t& mat); - // access a single element + /// @brief access a single element, const. TReal operator[](unsigned int i) const; + + /// @brief access a single element, non-const. TReal& operator[](unsigned int i); // comparison @@ -93,6 +113,7 @@ public: bool operator!= (const aiVector3t& other) const; bool operator < (const aiVector3t& other) const; + /// @brief bool Equal(const aiVector3t& other, TReal epsilon = 1e-6) const; template diff --git a/tools/assimp_view/MaterialManager.h b/tools/assimp_view/MaterialManager.h index 77ca4224f..9aeb5e992 100644 --- a/tools/assimp_view/MaterialManager.h +++ b/tools/assimp_view/MaterialManager.h @@ -52,24 +52,11 @@ namespace AssimpView { */ //------------------------------------------------------------------------------- class CMaterialManager { -private: friend class CDisplay; - // default constructor - CMaterialManager() : - m_iShaderCount(0), sDefaultTexture() {} - - ~CMaterialManager() { - if (sDefaultTexture) { - sDefaultTexture->Release(); - } - Reset(); - } - public: //------------------------------------------------------------------ // Singleton accessors - static CMaterialManager s_cInstance; inline static CMaterialManager &Instance() { return s_cInstance; } @@ -80,24 +67,20 @@ public: // Must be called before CreateMaterial() to prevent memory leaking void DeleteMaterial(AssetHelper::MeshHelper *pcIn); - //------------------------------------------------------------------ - // Create the material for a mesh. - // - // The function checks whether an identical shader is already in use. - // A shader is considered to be identical if it has the same input - // signature and takes the same number of texture channels. - int CreateMaterial(AssetHelper::MeshHelper *pcMesh, - const aiMesh *pcSource); - - //------------------------------------------------------------------ - // Setup the material for a given mesh - // pcMesh Mesh to be rendered - // pcProj Projection matrix - // aiMe Current world matrix - // pcCam Camera matrix - // vPos Position of the camera - // TODO: Extract camera position from matrix ... - // + /// @brief Create the material for a mesh. + /// + /// The function checks whether an identical shader is already in use. + /// A shader is considered to be identical if it has the same input + /// signature and takes the same number of texture channels. + int CreateMaterial(AssetHelper::MeshHelper *pcMesh, const aiMesh *pcSource); + + /// @brief Setup the material for a given mesh. + /// @param pcMesh Mesh to be rendered + /// @param pcProj Projection matrix + /// @param aiMe Current world matrix + /// @param pcCam Camera matrix + /// @param vPos Position of the camera + /// @return 0 if successful. int SetupMaterial(AssetHelper::MeshHelper *pcMesh, const aiMatrix4x4 &pcProj, const aiMatrix4x4 &aiMe, @@ -143,14 +126,29 @@ public: // Reset the state of the class // Called whenever a new asset is loaded inline void Reset() { - this->m_iShaderCount = 0; - for (TextureCache::iterator it = sCachedTextures.begin(); it != sCachedTextures.end(); ++it) { - (*it).second->Release(); + m_iShaderCount = 0; + for (auto & sCachedTexture : sCachedTextures) { + sCachedTexture.second->Release(); } sCachedTextures.clear(); } private: + // The default constructor + CMaterialManager() : + m_iShaderCount(0), + sDefaultTexture() { + // empty + } + + // Destructor, private. + ~CMaterialManager() { + if (sDefaultTexture) { + sDefaultTexture->Release(); + } + Reset(); + } + //------------------------------------------------------------------ // find a valid path to a texture file // @@ -183,15 +181,14 @@ private: bool HasAlphaPixels(IDirect3DTexture9 *piTexture); private: - // + static CMaterialManager s_cInstance; + // Specifies the number of different shaders generated for // the current asset. This number is incremented by CreateMaterial() // each time a shader isn't found in cache and needs to be created - // unsigned int m_iShaderCount; IDirect3DTexture9 *sDefaultTexture; - - typedef std::map TextureCache; + using TextureCache = std::map; TextureCache sCachedTextures; }; diff --git a/tools/assimp_view/MeshRenderer.cpp b/tools/assimp_view/MeshRenderer.cpp index 0ec12e5b1..bc1a5236f 100644 --- a/tools/assimp_view/MeshRenderer.cpp +++ b/tools/assimp_view/MeshRenderer.cpp @@ -61,11 +61,14 @@ int CMeshRenderer::DrawUnsorted(unsigned int iIndex) { D3DPRIMITIVETYPE type = D3DPT_POINTLIST; switch (g_pcAsset->pcScene->mMeshes[iIndex]->mPrimitiveTypes) { case aiPrimitiveType_POINT: - type = D3DPT_POINTLIST;break; + type = D3DPT_POINTLIST; + break; case aiPrimitiveType_LINE: - type = D3DPT_LINELIST;break; + type = D3DPT_LINELIST; + break; case aiPrimitiveType_TRIANGLE: - type = D3DPT_TRIANGLELIST;break; + type = D3DPT_TRIANGLELIST; + break; } // and draw the mesh g_piDevice->DrawIndexedPrimitive(type, From de2f5cf0213ffb575c5b1381e39a2660cde13fba Mon Sep 17 00:00:00 2001 From: "Max Vollmer (Microsoft Havok)" <60260460+ms-maxvollmer@users.noreply.github.com> Date: Fri, 13 Aug 2021 16:14:28 +0100 Subject: [PATCH 228/335] Crash fixes --- code/AssetLib/FBX/FBXParser.cpp | 24 ++++++++---------------- code/AssetLib/glTF2/glTF2Asset.inl | 2 +- code/AssetLib/glTF2/glTF2Importer.cpp | 6 ++++++ 3 files changed, 15 insertions(+), 17 deletions(-) diff --git a/code/AssetLib/FBX/FBXParser.cpp b/code/AssetLib/FBX/FBXParser.cpp index 37cf83cf9..61e53c2d0 100644 --- a/code/AssetLib/FBX/FBXParser.cpp +++ b/code/AssetLib/FBX/FBXParser.cpp @@ -642,8 +642,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * (type == 'd' ? 8 : 4); - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -733,8 +732,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * (type == 'd' ? 8 : 4); - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -816,8 +814,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * (type == 'd' ? 8 : 4); - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -892,8 +889,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * 4; - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -954,8 +950,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * (type == 'd' ? 8 : 4); - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -1019,8 +1014,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * 4; - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -1088,8 +1082,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * 8; - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -1150,8 +1143,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * 8; - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index d5f5b5cd0..9f793d7c0 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -1522,7 +1522,7 @@ inline bool GetAttribTargetVector(Mesh::Primitive &p, const int targetIndex, con inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) { Value *curName = FindMember(pJSON_Object, "name"); - if (nullptr != curName) { + if (nullptr != curName && curName->IsString()) { name = curName->GetString(); } diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index 921aaad9e..a4f3f0ba1 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1336,6 +1336,12 @@ std::unordered_map GatherSamplers(Animation &an continue; } + auto& animsampler = anim.samplers[channel.sampler]; + if (animsampler.input->count != animsampler.output->count) { + ASSIMP_LOG_WARN("Animation ", anim.name, ": Sampler input size ", animsampler.input->count, " doesn't match output size ", animsampler.output->count); + continue; + } + const unsigned int node_index = channel.target.node.GetIndex(); AnimationSamplers &sampler = samplers[node_index]; From c1e830cf3b62de1f0fcc7ec14dcd374392e2e393 Mon Sep 17 00:00:00 2001 From: "Max Vollmer (Microsoft Havok)" <60260460+ms-maxvollmer@users.noreply.github.com> Date: Fri, 13 Aug 2021 17:46:10 +0100 Subject: [PATCH 229/335] The GLTF2 specs aren't very specific about the restrictions of the number of keyframes in sampler input and output. It seems logical that they should be the same, but there is an official sample model from Khronos where output has more keyframes. I thus assume that the GLTF2 standard allows more keyframes in output, but not in input. Fixed the check accordingly. --- code/AssetLib/glTF2/glTF2Importer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index a4f3f0ba1..7a781ba04 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1337,8 +1337,8 @@ std::unordered_map GatherSamplers(Animation &an } auto& animsampler = anim.samplers[channel.sampler]; - if (animsampler.input->count != animsampler.output->count) { - ASSIMP_LOG_WARN("Animation ", anim.name, ": Sampler input size ", animsampler.input->count, " doesn't match output size ", animsampler.output->count); + if (animsampler.input->count > animsampler.output->count) { + ASSIMP_LOG_WARN("Animation ", anim.name, ": Number of keyframes in sampler input ", animsampler.input->count, " exceeds number of keyframes in sampler output ", animsampler.output->count); continue; } From d926c5ed61727564b1db0a802a1aae51b0eb6916 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=B6=E6=B5=A9=E7=84=B6?= Date: Thu, 19 Aug 2021 18:49:43 +0800 Subject: [PATCH 230/335] fix sample build error --- .../SimpleTexturedOpenGL/src/model_loading.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp b/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp index 9ceeec62e..be0e651f1 100644 --- a/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp +++ b/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp @@ -23,7 +23,7 @@ #endif // _MSC_VER #define STB_IMAGE_IMPLEMENTATION -#include "contrib/stb_image/stb_image.h" +#include "contrib/stb/stb_image.h" #ifdef _MSC_VER #pragma warning(default: 4100) // Enable warning 'unreferenced formal parameter' From 468aa50aed40ca3a49b03e9e7766785467869682 Mon Sep 17 00:00:00 2001 From: kovacsv Date: Thu, 19 Aug 2021 13:50:26 +0200 Subject: [PATCH 231/335] mingw build fix --- code/AssetLib/Assbin/AssbinFileWriter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/Assbin/AssbinFileWriter.cpp b/code/AssetLib/Assbin/AssbinFileWriter.cpp index 95379a303..2519b0b93 100644 --- a/code/AssetLib/Assbin/AssbinFileWriter.cpp +++ b/code/AssetLib/Assbin/AssbinFileWriter.cpp @@ -172,7 +172,7 @@ inline size_t Write(IOStream *stream, const aiQuaternion &v) { t += Write(stream, v.z); ai_assert(t == 16); - return 16; + return t; } // ----------------------------------------------------------------------------------- From c396bc78b1ea5acc5c6f40401951d5994edd0171 Mon Sep 17 00:00:00 2001 From: kimmi Date: Fri, 20 Aug 2021 19:40:04 +0200 Subject: [PATCH 232/335] closes https://github.com/assimp/assimp/issues/3951: Use using directive to define type. --- test/unit/utglTF2ImportExport.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index 90070d63e..0b9d4d70d 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -748,7 +748,8 @@ TEST_F(utglTF2ImportExport, import_dracoEncoded) { TEST_F(utglTF2ImportExport, wrongTypes) { // Deliberately broken version of the BoxTextured.gltf asset. - std::vector> wrongTypes = { + using tup_T = std::tuple; + std::vector wrongTypes = { { "/glTF2/wrongTypes/badArray.gltf", "array", "primitives", "meshes[0]" }, { "/glTF2/wrongTypes/badString.gltf", "string", "name", "scenes[0]" }, { "/glTF2/wrongTypes/badUint.gltf", "uint", "index", "materials[0]" }, From 38dcd3583c4026574dc0a61cd0683331064bc63a Mon Sep 17 00:00:00 2001 From: kimmi Date: Fri, 20 Aug 2021 20:20:47 +0200 Subject: [PATCH 233/335] Fix compiler warnings: comparison signed unsigned. --- test/unit/utglTF2ImportExport.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index 0b9d4d70d..e29d09145 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -615,12 +615,12 @@ TEST_F(utglTF2ImportExport, texcoords) { aiTextureMapMode modes[2]; EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_BASE_COLOR_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); EXPECT_STREQ(path.C_Str(), "texture.png"); - EXPECT_EQ(uvIndex, 0); + EXPECT_EQ(uvIndex, 0u); uvIndex = 255; EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); EXPECT_STREQ(path.C_Str(), "texture.png"); - EXPECT_EQ(uvIndex, 1); + EXPECT_EQ(uvIndex, 1u); } #ifndef ASSIMP_BUILD_NO_EXPORT @@ -646,12 +646,12 @@ TEST_F(utglTF2ImportExport, texcoords_export) { aiTextureMapMode modes[2]; EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_BASE_COLOR_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); EXPECT_STREQ(path.C_Str(), "texture.png"); - EXPECT_EQ(uvIndex, 0); + EXPECT_EQ(uvIndex, 0u); uvIndex = 255; EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); EXPECT_STREQ(path.C_Str(), "texture.png"); - EXPECT_EQ(uvIndex, 1); + EXPECT_EQ(uvIndex, 1u); } #endif // ASSIMP_BUILD_NO_EXPORT From 18f58947a476071c4cb18000cc6fbf976aff1b53 Mon Sep 17 00:00:00 2001 From: kimmi Date: Fri, 20 Aug 2021 20:42:02 +0200 Subject: [PATCH 234/335] CMake: move hunter cmake-scripts into cmake-modules --- CMakeLists.txt | 6 +++--- {cmake => cmake-modules}/HunterGate.cmake | 0 cmake-modules/assimp-hunter-config.cmake.in | 19 +++++++++++++++++++ .../assimp-plain-config.cmake.in | 0 cmake/assimp-hunter-config.cmake.in | 19 ------------------- 5 files changed, 22 insertions(+), 22 deletions(-) rename {cmake => cmake-modules}/HunterGate.cmake (100%) create mode 100644 cmake-modules/assimp-hunter-config.cmake.in rename {cmake => cmake-modules}/assimp-plain-config.cmake.in (100%) delete mode 100644 cmake/assimp-hunter-config.cmake.in diff --git a/CMakeLists.txt b/CMakeLists.txt index 2dfe592bc..217d558fa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,7 +44,7 @@ CMAKE_MINIMUM_REQUIRED( VERSION 3.10 ) option(ASSIMP_HUNTER_ENABLED "Enable Hunter package manager support" OFF) IF(ASSIMP_HUNTER_ENABLED) - include("cmake/HunterGate.cmake") + include("cmake-modules/HunterGate.cmake") HunterGate( URL "https://github.com/cpp-pm/hunter/archive/v0.23.311.tar.gz" SHA1 "1a82b9b73055879181cb1466b2ab5d48ee8ae410" @@ -397,14 +397,14 @@ set(GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated") IF(ASSIMP_HUNTER_ENABLED) set(CONFIG_INSTALL_DIR "lib/cmake/${PROJECT_NAME}") - set(CMAKE_CONFIG_TEMPLATE_FILE "cmake/assimp-hunter-config.cmake.in") + set(CMAKE_CONFIG_TEMPLATE_FILE "cmake-mocules/assimp-hunter-config.cmake.in") set(NAMESPACE "${PROJECT_NAME}::") set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets") set(VERSION_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}ConfigVersion.cmake") set(PROJECT_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}Config.cmake") ELSE() set(CONFIG_INSTALL_DIR "${ASSIMP_LIB_INSTALL_DIR}/cmake/assimp-${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}") - set(CMAKE_CONFIG_TEMPLATE_FILE "cmake/assimp-plain-config.cmake.in") + set(CMAKE_CONFIG_TEMPLATE_FILE "cmake-modules/assimp-plain-config.cmake.in") string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWERCASE) set(NAMESPACE "${PROJECT_NAME_LOWERCASE}::") set(TARGETS_EXPORT_NAME "${PROJECT_NAME_LOWERCASE}Targets") diff --git a/cmake/HunterGate.cmake b/cmake-modules/HunterGate.cmake similarity index 100% rename from cmake/HunterGate.cmake rename to cmake-modules/HunterGate.cmake diff --git a/cmake-modules/assimp-hunter-config.cmake.in b/cmake-modules/assimp-hunter-config.cmake.in new file mode 100644 index 000000000..1988f7e7d --- /dev/null +++ b/cmake-modules/assimp-hunter-config.cmake.in @@ -0,0 +1,19 @@ +@PACKAGE_INIT@ + +find_package(RapidJSON CONFIG REQUIRED) +find_package(ZLIB CONFIG REQUIRED) +find_package(utf8cpp CONFIG REQUIRED) +find_package(minizip CONFIG REQUIRED) +find_package(openddlparser CONFIG REQUIRED) +find_package(poly2tri CONFIG REQUIRED) +find_package(polyclipping CONFIG REQUIRED) +find_package(zip CONFIG REQUIRED) +find_package(pugixml CONFIG REQUIRED) +find_package(stb CONFIG REQUIRED) + +if(@ASSIMP_BUILD_DRACO@) + find_package(draco CONFIG REQUIRED) +endif() + +include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") +check_required_components("@PROJECT_NAME@") diff --git a/cmake/assimp-plain-config.cmake.in b/cmake-modules/assimp-plain-config.cmake.in similarity index 100% rename from cmake/assimp-plain-config.cmake.in rename to cmake-modules/assimp-plain-config.cmake.in diff --git a/cmake/assimp-hunter-config.cmake.in b/cmake/assimp-hunter-config.cmake.in deleted file mode 100644 index 5c5aeedfd..000000000 --- a/cmake/assimp-hunter-config.cmake.in +++ /dev/null @@ -1,19 +0,0 @@ -@PACKAGE_INIT@ - -find_package(RapidJSON CONFIG REQUIRED) -find_package(ZLIB CONFIG REQUIRED) -find_package(utf8cpp CONFIG REQUIRED) -find_package(minizip CONFIG REQUIRED) -find_package(openddlparser CONFIG REQUIRED) -find_package(poly2tri CONFIG REQUIRED) -find_package(polyclipping CONFIG REQUIRED) -find_package(zip CONFIG REQUIRED) -find_package(pugixml CONFIG REQUIRED) -find_package(stb CONFIG REQUIRED) - -if(@ASSIMP_BUILD_DRACO@) - find_package(draco CONFIG REQUIRED) -endif() - -include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") -check_required_components("@PROJECT_NAME@") From f24101546bd0acd19d3fa02cd7e6515c3ff14b57 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 20 Aug 2021 21:02:42 +0200 Subject: [PATCH 235/335] Fix typo in path --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 217d558fa..6a938f0d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -397,7 +397,7 @@ set(GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated") IF(ASSIMP_HUNTER_ENABLED) set(CONFIG_INSTALL_DIR "lib/cmake/${PROJECT_NAME}") - set(CMAKE_CONFIG_TEMPLATE_FILE "cmake-mocules/assimp-hunter-config.cmake.in") + set(CMAKE_CONFIG_TEMPLATE_FILE "cmake-modules/assimp-hunter-config.cmake.in") set(NAMESPACE "${PROJECT_NAME}::") set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets") set(VERSION_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}ConfigVersion.cmake") From 0590a39159beabd8263e891390997f752478bd25 Mon Sep 17 00:00:00 2001 From: kovacsv Date: Tue, 24 Aug 2021 07:26:20 +0200 Subject: [PATCH 236/335] Fix M3D import crash and memory leak. The same default material pointer was assigned to all the materials in the scene, so poor destructor tried to free the same pointer multiple times. --- code/AssetLib/M3D/M3DImporter.cpp | 14 +++++----- test/unit/utM3DImportExport.cpp | 43 ++++++++++++++----------------- 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/code/AssetLib/M3D/M3DImporter.cpp b/code/AssetLib/M3D/M3DImporter.cpp index 4b2f9bd77..efa1d5475 100644 --- a/code/AssetLib/M3D/M3DImporter.cpp +++ b/code/AssetLib/M3D/M3DImporter.cpp @@ -233,12 +233,12 @@ void M3DImporter::importMaterials(const M3DWrapper &m3d) { ASSIMP_LOG_DEBUG("M3D: importMaterials ", mScene->mNumMaterials); // add a default material as first - aiMaterial *mat = new aiMaterial; - mat->AddProperty(&name, AI_MATKEY_NAME); + aiMaterial *defaultMat = new aiMaterial; + defaultMat->AddProperty(&name, AI_MATKEY_NAME); c.a = 1.0f; c.b = c.g = c.r = 0.6f; - mat->AddProperty(&c, 1, AI_MATKEY_COLOR_DIFFUSE); - mScene->mMaterials[0] = mat; + defaultMat->AddProperty(&c, 1, AI_MATKEY_COLOR_DIFFUSE); + mScene->mMaterials[0] = defaultMat; if (!m3d->nummaterial || !m3d->material) { return; @@ -300,12 +300,12 @@ void M3DImporter::importMaterials(const M3DWrapper &m3d) { m->prop[j].value.textureid < m3d->numtexture && m3d->texture[m->prop[j].value.textureid].name) { name.Set(std::string(std::string(m3d->texture[m->prop[j].value.textureid].name) + ".png")); - mat->AddProperty(&name, aiTxProps[k].pKey, aiTxProps[k].type, aiTxProps[k].index); + newMat->AddProperty(&name, aiTxProps[k].pKey, aiTxProps[k].type, aiTxProps[k].index); n = 0; - mat->AddProperty(&n, 1, _AI_MATKEY_UVWSRC_BASE, aiProps[k].type, aiProps[k].index); + newMat->AddProperty(&n, 1, _AI_MATKEY_UVWSRC_BASE, aiProps[k].type, aiProps[k].index); } } - mScene->mMaterials[i + 1] = mat; + mScene->mMaterials[i + 1] = newMat; } } diff --git a/test/unit/utM3DImportExport.cpp b/test/unit/utM3DImportExport.cpp index bd9fca168..24359d319 100644 --- a/test/unit/utM3DImportExport.cpp +++ b/test/unit/utM3DImportExport.cpp @@ -50,35 +50,32 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace Assimp; -class utM3DImportExport : public AbstractImportExportBase { -public: - bool importerTest() override { - Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals.m3d", aiProcess_ValidateDataStructure); +TEST(utM3DImportExport, import_cube_normals) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals.m3d", aiProcess_ValidateDataStructure); #ifndef ASSIMP_BUILD_NO_M3D_IMPORTER - return nullptr != scene; + ASSERT_NE(nullptr, scene); #else - return nullptr == scene; + ASSERT_EQ(nullptr, scene); #endif // ASSIMP_BUILD_NO_M3D_IMPORTER - } +} -#ifndef ASSIMP_BUILD_NO_EXPORT - bool exporterTest() override { - Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals.m3d", aiProcess_ValidateDataStructure); - Exporter exporter; - aiReturn ret = exporter.Export(scene, "m3d", ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals_out.m3d"); - return ret == AI_SUCCESS; - } -#endif -}; - -TEST_F(utM3DImportExport, importM3DFromFileTest) { - EXPECT_TRUE(importerTest()); +TEST(utM3DImportExport, import_cube_usemtl) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/M3D/cube_usemtl.m3d", aiProcess_ValidateDataStructure); +#ifndef ASSIMP_BUILD_NO_M3D_IMPORTER + ASSERT_NE(nullptr, scene); +#else + ASSERT_EQ(nullptr, scene); +#endif // ASSIMP_BUILD_NO_M3D_IMPORTER } #ifndef ASSIMP_BUILD_NO_EXPORT -TEST_F(utM3DImportExport, exportM3DFromFileTest) { - EXPECT_TRUE(exporterTest()); +TEST(utM3DImportExport, export_cube_normals) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals.m3d", aiProcess_ValidateDataStructure); + Exporter exporter; + aiReturn ret = exporter.Export(scene, "m3d", ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals_out.m3d"); + ASSERT_EQ(AI_SUCCESS, ret); } #endif // ASSIMP_BUILD_NO_EXPORT From 37ba06783957c455d0a4a08e75aca600a78c7bb6 Mon Sep 17 00:00:00 2001 From: kovacsv Date: Tue, 24 Aug 2021 09:09:35 +0200 Subject: [PATCH 237/335] Revert back test, because the new one revealed an undefined behavior error. --- test/unit/utM3DImportExport.cpp | 46 +++++++++++++++++---------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/test/unit/utM3DImportExport.cpp b/test/unit/utM3DImportExport.cpp index 24359d319..7efc24985 100644 --- a/test/unit/utM3DImportExport.cpp +++ b/test/unit/utM3DImportExport.cpp @@ -43,39 +43,41 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include "UnitTestPCH.h" -#include -#include #include +#include #include using namespace Assimp; -TEST(utM3DImportExport, import_cube_normals) { - Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals.m3d", aiProcess_ValidateDataStructure); +class utM3DImportExport : public AbstractImportExportBase { +public: + bool importerTest() override { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals.m3d", aiProcess_ValidateDataStructure); #ifndef ASSIMP_BUILD_NO_M3D_IMPORTER - ASSERT_NE(nullptr, scene); + return nullptr != scene; #else - ASSERT_EQ(nullptr, scene); + return nullptr == scene; #endif // ASSIMP_BUILD_NO_M3D_IMPORTER -} + } -TEST(utM3DImportExport, import_cube_usemtl) { - Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/M3D/cube_usemtl.m3d", aiProcess_ValidateDataStructure); -#ifndef ASSIMP_BUILD_NO_M3D_IMPORTER - ASSERT_NE(nullptr, scene); -#else - ASSERT_EQ(nullptr, scene); -#endif // ASSIMP_BUILD_NO_M3D_IMPORTER +#ifndef ASSIMP_BUILD_NO_EXPORT + bool exporterTest() override { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals.m3d", aiProcess_ValidateDataStructure); + Exporter exporter; + aiReturn ret = exporter.Export(scene, "m3d", ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals_out.m3d"); + return ret == AI_SUCCESS; + } +#endif +}; + +TEST_F(utM3DImportExport, importM3DFromFileTest) { + EXPECT_TRUE(importerTest()); } #ifndef ASSIMP_BUILD_NO_EXPORT -TEST(utM3DImportExport, export_cube_normals) { - Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals.m3d", aiProcess_ValidateDataStructure); - Exporter exporter; - aiReturn ret = exporter.Export(scene, "m3d", ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals_out.m3d"); - ASSERT_EQ(AI_SUCCESS, ret); +TEST_F(utM3DImportExport, exportM3DFromFileTest) { + EXPECT_TRUE(exporterTest()); } #endif // ASSIMP_BUILD_NO_EXPORT From cb262dab5e5599d922633600292e3a59cc2a7f98 Mon Sep 17 00:00:00 2001 From: kovacsv Date: Tue, 24 Aug 2021 11:02:09 +0200 Subject: [PATCH 238/335] Revert test back to the exact same version. --- test/unit/utM3DImportExport.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/test/unit/utM3DImportExport.cpp b/test/unit/utM3DImportExport.cpp index 7efc24985..bd9fca168 100644 --- a/test/unit/utM3DImportExport.cpp +++ b/test/unit/utM3DImportExport.cpp @@ -43,19 +43,20 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AbstractImportExportBase.h" #include "UnitTestPCH.h" -#include +#include #include +#include #include using namespace Assimp; class utM3DImportExport : public AbstractImportExportBase { public: - bool importerTest() override { + bool importerTest() override { Assimp::Importer importer; const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals.m3d", aiProcess_ValidateDataStructure); #ifndef ASSIMP_BUILD_NO_M3D_IMPORTER - return nullptr != scene; + return nullptr != scene; #else return nullptr == scene; #endif // ASSIMP_BUILD_NO_M3D_IMPORTER @@ -63,11 +64,11 @@ public: #ifndef ASSIMP_BUILD_NO_EXPORT bool exporterTest() override { - Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals.m3d", aiProcess_ValidateDataStructure); - Exporter exporter; - aiReturn ret = exporter.Export(scene, "m3d", ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals_out.m3d"); - return ret == AI_SUCCESS; + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals.m3d", aiProcess_ValidateDataStructure); + Exporter exporter; + aiReturn ret = exporter.Export(scene, "m3d", ASSIMP_TEST_MODELS_DIR "/M3D/cube_normals_out.m3d"); + return ret == AI_SUCCESS; } #endif }; @@ -78,6 +79,6 @@ TEST_F(utM3DImportExport, importM3DFromFileTest) { #ifndef ASSIMP_BUILD_NO_EXPORT TEST_F(utM3DImportExport, exportM3DFromFileTest) { - EXPECT_TRUE(exporterTest()); + EXPECT_TRUE(exporterTest()); } #endif // ASSIMP_BUILD_NO_EXPORT From a71baaeedf6d471860b638737f1338509ab72fe3 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Tue, 24 Aug 2021 19:22:15 +0200 Subject: [PATCH 239/335] Enable Viewer only for VS-Builds --- CMakeLists.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a938f0d9..bfa30e96a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -135,11 +135,11 @@ IF ( WIN32 ) # Use subset of Windows.h ADD_DEFINITIONS( -DWIN32_LEAN_AND_MEAN ) - OPTION ( ASSIMP_BUILD_ASSIMP_VIEW - "If the Assimp view tool is built. (requires DirectX)" - OFF ) - IF(MSVC) + OPTION ( ASSIMP_BUILD_ASSIMP_VIEW + "If the Assimp view tool is built. (requires DirectX)" + OFF ) + OPTION( ASSIMP_INSTALL_PDB "Install MSVC debug files." ON ) From eabfc05bbbfba39a6f78f023ac0fb4d2c9fdbc00 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 27 Aug 2021 10:41:25 +0200 Subject: [PATCH 240/335] Handle empty keys --- code/AssetLib/FBX/FBXParser.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/code/AssetLib/FBX/FBXParser.cpp b/code/AssetLib/FBX/FBXParser.cpp index 61e53c2d0..582940363 100644 --- a/code/AssetLib/FBX/FBXParser.cpp +++ b/code/AssetLib/FBX/FBXParser.cpp @@ -192,6 +192,10 @@ Scope::Scope(Parser& parser,bool topLevel) } const std::string& str = n->StringContents(); + if (str.empty()) { + ParseError("unexpected content: empty string."); + } + elements.insert(ElementMap::value_type(str,new_Element(*n,parser))); // Element() should stop at the next Key token (or right after a Close token) From a45878c41ad735a1e5f1755fcf0be14788f42021 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 27 Aug 2021 14:04:00 +0200 Subject: [PATCH 241/335] Fix possible overrun - closes https://github.com/assimp/assimp/issues/3425 --- code/Common/RemoveComments.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/code/Common/RemoveComments.cpp b/code/Common/RemoveComments.cpp index d1c2ac391..e1ba99761 100644 --- a/code/Common/RemoveComments.cpp +++ b/code/Common/RemoveComments.cpp @@ -59,13 +59,16 @@ void CommentRemover::RemoveLineComments(const char* szComment, ai_assert(nullptr != szBuffer); ai_assert(*szComment); - const size_t len = strlen(szComment); + size_t len = strlen(szComment); + const size_t lenBuffer = strlen(szBuffer); + if (len > lenBuffer) { + len = lenBuffer; + } while (*szBuffer) { // skip over quotes if (*szBuffer == '\"' || *szBuffer == '\'') while (*szBuffer++ && *szBuffer != '\"' && *szBuffer != '\''); - if (!strncmp(szBuffer,szComment,len)) { while (!IsLineEnd(*szBuffer)) *szBuffer++ = chReplacement; From 999192489c379814fc108761e6acec814d0b2c9b Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Fri, 27 Aug 2021 14:52:48 +0200 Subject: [PATCH 242/335] Delete FindIrrXML.cmake - Deprecated --- cmake-modules/FindIrrXML.cmake | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 cmake-modules/FindIrrXML.cmake diff --git a/cmake-modules/FindIrrXML.cmake b/cmake-modules/FindIrrXML.cmake deleted file mode 100644 index 5434e0b86..000000000 --- a/cmake-modules/FindIrrXML.cmake +++ /dev/null @@ -1,17 +0,0 @@ -# Find IrrXMl from irrlicht project -# -# Find LibIrrXML headers and library -# -# IRRXML_FOUND - IrrXML found -# IRRXML_INCLUDE_DIR - Headers location -# IRRXML_LIBRARY - IrrXML main library - -find_path(IRRXML_INCLUDE_DIR irrXML.h - PATH_SUFFIXES include/irrlicht include/irrxml) -find_library(IRRXML_LIBRARY IrrXML) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(IrrXML REQUIRED_VARS IRRXML_INCLUDE_DIR IRRXML_LIBRARY) - - -mark_as_advanced(IRRXML_INCLUDE_DIR IRRXML_LIBRARY) From b39f38b73c5fb55bacce66e9505f166571fa9ac2 Mon Sep 17 00:00:00 2001 From: kovacsv Date: Fri, 27 Aug 2021 18:17:27 +0200 Subject: [PATCH 243/335] Add export property for assimp json exporter to write compressed json (without whitespaces). --- code/AssetLib/Assjson/json_exporter.cpp | 42 ++++++++++++------- .../ImportExport/utAssjsonImportExport.cpp | 13 +++++- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/code/AssetLib/Assjson/json_exporter.cpp b/code/AssetLib/Assjson/json_exporter.cpp index b9099d392..7b2c8ec81 100644 --- a/code/AssetLib/Assjson/json_exporter.cpp +++ b/code/AssetLib/Assjson/json_exporter.cpp @@ -41,12 +41,17 @@ public: enum { Flag_DoNotIndent = 0x1, Flag_WriteSpecialFloats = 0x2, + Flag_SkipWhitespaces = 0x4 }; - + JSONWriter(Assimp::IOStream &out, unsigned int flags = 0u) : - out(out), first(), flags(flags) { + out(out), indent (""), newline("\n"), space(" "), buff (), first(false), flags(flags) { // make sure that all formatting happens using the standard, C locale and not the user's current locale buff.imbue(std::locale("C")); + if (flags & Flag_SkipWhitespaces) { + newline = ""; + space = ""; + } } ~JSONWriter() { @@ -70,7 +75,7 @@ public: void Key(const std::string &name) { AddIndentation(); Delimit(); - buff << '\"' + name + "\": "; + buff << '\"' + name + "\":" << space; } template @@ -78,12 +83,12 @@ public: AddIndentation(); Delimit(); - LiteralToString(buff, name) << '\n'; + LiteralToString(buff, name) << newline; } template void SimpleValue(const Literal &s) { - LiteralToString(buff, s) << '\n'; + LiteralToString(buff, s) << newline; } void SimpleValue(const void *buffer, size_t len) { @@ -102,7 +107,7 @@ public: } } - buff << '\"' << cur_out << "\"\n"; + buff << '\"' << cur_out << "\"" << newline; delete[] cur_out; } @@ -115,7 +120,7 @@ public: } } first = true; - buff << "{\n"; + buff << "{" << newline; PushIndent(); } @@ -123,7 +128,7 @@ public: PopIndent(); AddIndentation(); first = false; - buff << "}\n"; + buff << "}" << newline; } void StartArray(bool is_element = false) { @@ -135,19 +140,19 @@ public: } } first = true; - buff << "[\n"; + buff << "[" << newline; PushIndent(); } void EndArray() { PopIndent(); AddIndentation(); - buff << "]\n"; + buff << "]" << newline; first = false; } void AddIndentation() { - if (!(flags & Flag_DoNotIndent)) { + if (!(flags & Flag_DoNotIndent) && !(flags & Flag_SkipWhitespaces)) { buff << indent; } } @@ -156,7 +161,7 @@ public: if (!first) { buff << ','; } else { - buff << ' '; + buff << space; first = false; } } @@ -227,7 +232,9 @@ private: private: Assimp::IOStream &out; - std::string indent, newline; + std::string indent; + std::string newline; + std::string space; std::stringstream buff; bool first; @@ -765,7 +772,7 @@ void Write(JSONWriter &out, const aiScene &ai) { out.EndObj(); } -void ExportAssimp2Json(const char *file, Assimp::IOSystem *io, const aiScene *scene, const Assimp::ExportProperties *) { +void ExportAssimp2Json(const char *file, Assimp::IOSystem *io, const aiScene *scene, const Assimp::ExportProperties *pProperties) { std::unique_ptr str(io->Open(file, "wt")); if (!str) { throw DeadlyExportError("could not open output file"); @@ -782,7 +789,12 @@ void ExportAssimp2Json(const char *file, Assimp::IOSystem *io, const aiScene *sc splitter.Execute(scenecopy_tmp); // XXX Flag_WriteSpecialFloats is turned on by default, right now we don't have a configuration interface for exporters - JSONWriter s(*str, JSONWriter::Flag_WriteSpecialFloats); + + unsigned int flags = JSONWriter::Flag_WriteSpecialFloats; + if (pProperties->GetPropertyBool("JSON_SKIP_WHITESPACES", false)) { + flags |= JSONWriter::Flag_SkipWhitespaces; + } + JSONWriter s(*str, flags); Write(s, *scenecopy_tmp); } catch (...) { diff --git a/test/unit/ImportExport/utAssjsonImportExport.cpp b/test/unit/ImportExport/utAssjsonImportExport.cpp index 7987804a9..13724f755 100644 --- a/test/unit/ImportExport/utAssjsonImportExport.cpp +++ b/test/unit/ImportExport/utAssjsonImportExport.cpp @@ -58,7 +58,18 @@ public: Exporter exporter; aiReturn res = exporter.Export(scene, "assjson", "./spider_test.json"); - return aiReturn_SUCCESS == res; + if (aiReturn_SUCCESS != res) { + return false; + } + + Assimp::ExportProperties exportProperties; + exportProperties.SetPropertyBool("JSON_SKIP_WHITESPACES", true); + aiReturn resNoWhitespace = exporter.Export(scene, "assjson", "./spider_test_nowhitespace.json", 0u, &exportProperties); + if (aiReturn_SUCCESS != resNoWhitespace) { + return false; + } + + return true; } }; From 3e090b21f5fa6969106c29196231718dcd26b15b Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 28 Aug 2021 13:33:25 +0200 Subject: [PATCH 244/335] Fix setup of embedded texture loading --- code/AssetLib/3MF/D3MFOpcPackage.cpp | 16 +++++++++++++--- code/AssetLib/3MF/XmlSerializer.cpp | 3 ++- tools/assimp_view/Material.cpp | 7 ++++--- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/code/AssetLib/3MF/D3MFOpcPackage.cpp b/code/AssetLib/3MF/D3MFOpcPackage.cpp index d3b667583..5a8c9a2bf 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.cpp +++ b/code/AssetLib/3MF/D3MFOpcPackage.cpp @@ -58,6 +58,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include "contrib/stb/stb_image.h" + namespace Assimp { namespace D3MF { @@ -119,8 +121,11 @@ public: static bool IsEmbeddedTexture( const std::string &filename ) { const std::string extension = BaseImporter::GetExtension(filename); - if (extension == "jpg" || extension == "png") { + std::string::size_type pos = filename.find("thumbnail"); + if (pos == std::string::npos) { + return false; + } return true; } @@ -232,12 +237,17 @@ void D3MFOpcPackage::LoadEmbeddedTextures(IOStream *fileStream, const std::strin return; } - char *data = new char[size]; + unsigned char *data = new unsigned char[size]; fileStream->Read(data, 1, size); aiTexture *texture = new aiTexture; - texture->mFilename.Set(filename.c_str()); + std::string embName = "*" + filename; + texture->mFilename.Set(embName.c_str()); texture->mWidth = static_cast(size); texture->mHeight = 0; + texture->achFormatHint[0] = 'p'; + texture->achFormatHint[1] = 'n'; + texture->achFormatHint[2] = 'g'; + texture->achFormatHint[3] = '\0'; texture->pcData = (aiTexel*) data; mEmbeddedTextures.emplace_back(texture); } diff --git a/code/AssetLib/3MF/XmlSerializer.cpp b/code/AssetLib/3MF/XmlSerializer.cpp index d93c7b121..a86f4901b 100644 --- a/code/AssetLib/3MF/XmlSerializer.cpp +++ b/code/AssetLib/3MF/XmlSerializer.cpp @@ -493,7 +493,8 @@ void XmlSerializer::StoreEmbeddedTexture(EmbeddedTexture *tex) { aiString s; s.Set(ai_to_string(tex->mId).c_str()); mat->AddProperty(&s, AI_MATKEY_NAME); - s.Set(tex->mPath); + const std::string name = "*" + tex->mPath; + s.Set(name); mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(0)); aiColor3D col; diff --git a/tools/assimp_view/Material.cpp b/tools/assimp_view/Material.cpp index bcc93011e..100074445 100644 --- a/tools/assimp_view/Material.cpp +++ b/tools/assimp_view/Material.cpp @@ -325,9 +325,10 @@ int CMaterialManager::FindValidPath(aiString* p_szString) // first check whether we can directly load the file FILE* pFile = fopen(p_szString->data,"rb"); - if (pFile)fclose(pFile); - else - { + if (pFile) { + fclose(pFile); + } + else { // check whether we can use the directory of the asset as relative base char szTemp[MAX_PATH*2], tmp2[MAX_PATH*2]; strcpy(szTemp, g_szFileName); From a7bc858698a910cf955e13b501208e11ee2061e8 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 28 Aug 2021 13:36:57 +0200 Subject: [PATCH 245/335] Fix review finding. --- code/AssetLib/3DS/3DSLoader.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/code/AssetLib/3DS/3DSLoader.h b/code/AssetLib/3DS/3DSLoader.h index dba20eede..04dcac237 100644 --- a/code/AssetLib/3DS/3DSLoader.h +++ b/code/AssetLib/3DS/3DSLoader.h @@ -209,12 +209,12 @@ protected: void ReplaceDefaultMaterial(); bool ContainsTextures(unsigned int i) const { - return mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0 || - mScene->mMaterials[i].sTexBump.mMapName.length() != 0 || - mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 || - mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0 || - mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0 || - mScene->mMaterials[i].sTexShininess.mMapName.length() != 0; + return !mScene->mMaterials[i].sTexDiffuse.mMapName.empty() || + !mScene->mMaterials[i].sTexBump.mMapName.empty() || + !mScene->mMaterials[i].sTexOpacity.mMapName.empty() || + !mScene->mMaterials[i].sTexEmissive.mMapName.empty() || + !mScene->mMaterials[i].sTexSpecular.mMapName.empty() || + !mScene->mMaterials[i].sTexShininess.mMapName.empty() ; } // ------------------------------------------------------------------- From 69051bbc2c4cc456e916e43ba1b25075aa42e9b5 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 28 Aug 2021 13:46:41 +0200 Subject: [PATCH 246/335] Add missing docu --- code/AssetLib/3MF/3MFTypes.h | 40 ++++++++++++++++++++++++++++++++ code/AssetLib/3MF/D3MFImporter.h | 34 ++++++++++++++++++++++----- 2 files changed, 68 insertions(+), 6 deletions(-) diff --git a/code/AssetLib/3MF/3MFTypes.h b/code/AssetLib/3MF/3MFTypes.h index e404bf012..c4e9e4243 100644 --- a/code/AssetLib/3MF/3MFTypes.h +++ b/code/AssetLib/3MF/3MFTypes.h @@ -1,3 +1,43 @@ +/* +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. + +---------------------------------------------------------------------- +*/ #pragma once #include diff --git a/code/AssetLib/3MF/D3MFImporter.h b/code/AssetLib/3MF/D3MFImporter.h index 811c463b6..2b37010d9 100644 --- a/code/AssetLib/3MF/D3MFImporter.h +++ b/code/AssetLib/3MF/D3MFImporter.h @@ -4,7 +4,6 @@ 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, @@ -47,17 +46,40 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { +// --------------------------------------------------------------------------- /// @brief The 3MF-importer class. +/// +/// Implements the basic topology import and embedded textures. +// --------------------------------------------------------------------------- class D3MFImporter : public BaseImporter { public: + /// @brief The default class constructor. D3MFImporter(); - ~D3MFImporter(); - bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const; - void SetupProperties(const Importer *pImp); - const aiImporterDesc *GetInfo() const; + + /// @brief The class destructor. + ~D3MFImporter() override; + + /// @brief Performs the data format detection. + /// @param pFile The filename to check. + /// @param pIOHandler The used IO-System. + /// @param checkSig true for signature checking. + /// @return true for can be loaded, false for not. + bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override; + + /// @brief Not used + /// @param pImp Not used + void SetupProperties(const Importer *pImp) override; + + /// @brief The importer description getter. + /// @return The info + const aiImporterDesc *GetInfo() const override; protected: - void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler); + /// @brief Internal read function, performs the file parsing. + /// @param pFile The filename + /// @param pScene The scene to load in. + /// @param pIOHandler The io-system + void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override; }; } // Namespace Assimp From c9b76f5255d86fd9936310a99e0729beaaa9bc28 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sat, 28 Aug 2021 14:20:12 +0200 Subject: [PATCH 247/335] Decrease xml-serializer complexity --- code/AssetLib/3MF/XmlSerializer.cpp | 295 ++++++++++++++-------------- code/AssetLib/3MF/XmlSerializer.h | 7 - 2 files changed, 150 insertions(+), 152 deletions(-) diff --git a/code/AssetLib/3MF/XmlSerializer.cpp b/code/AssetLib/3MF/XmlSerializer.cpp index a86f4901b..daef9a8ac 100644 --- a/code/AssetLib/3MF/XmlSerializer.cpp +++ b/code/AssetLib/3MF/XmlSerializer.cpp @@ -49,6 +49,150 @@ namespace D3MF { static const int IdNotSet = -1; +namespace { + +static const size_t ColRGBA_Len = 9; +static const size_t ColRGB_Len = 7; + +// format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1) +bool validateColorString(const char *color) { + const size_t len = strlen(color); + if (ColRGBA_Len != len && ColRGB_Len != len) { + return false; + } + + return true; +} + +aiFace ReadTriangle(XmlNode &node) { + aiFace face; + + face.mNumIndices = 3; + face.mIndices = new unsigned int[face.mNumIndices]; + face.mIndices[0] = static_cast(std::atoi(node.attribute(XmlTag::v1).as_string())); + face.mIndices[1] = static_cast(std::atoi(node.attribute(XmlTag::v2).as_string())); + face.mIndices[2] = static_cast(std::atoi(node.attribute(XmlTag::v3).as_string())); + + return face; +} + +aiVector3D ReadVertex(XmlNode &node) { + aiVector3D vertex; + vertex.x = ai_strtof(node.attribute(XmlTag::x).as_string(), nullptr); + vertex.y = ai_strtof(node.attribute(XmlTag::y).as_string(), nullptr); + vertex.z = ai_strtof(node.attribute(XmlTag::z).as_string(), nullptr); + + return vertex; +} + +bool getNodeAttribute(const XmlNode &node, const std::string &attribute, std::string &value) { + pugi::xml_attribute objectAttribute = node.attribute(attribute.c_str()); + if (!objectAttribute.empty()) { + value = objectAttribute.as_string(); + return true; + } + + return false; +} + +bool getNodeAttribute(const XmlNode &node, const std::string &attribute, int &value) { + std::string strValue; + const bool ret = getNodeAttribute(node, attribute, strValue); + if (ret) { + value = std::atoi(strValue.c_str()); + return true; + } + + return false; +} + +aiMatrix4x4 parseTransformMatrix(std::string matrixStr) { + // split the string + std::vector numbers; + std::string currentNumber; + for (char c : matrixStr) { + if (c == ' ') { + if (!currentNumber.empty()) { + float f = std::stof(currentNumber); + numbers.push_back(f); + currentNumber.clear(); + } + } else { + currentNumber.push_back(c); + } + } + if (!currentNumber.empty()) { + const float f = std::stof(currentNumber); + numbers.push_back(f); + } + + aiMatrix4x4 transformMatrix; + transformMatrix.a1 = numbers[0]; + transformMatrix.b1 = numbers[1]; + transformMatrix.c1 = numbers[2]; + transformMatrix.d1 = 0; + + transformMatrix.a2 = numbers[3]; + transformMatrix.b2 = numbers[4]; + transformMatrix.c2 = numbers[5]; + transformMatrix.d2 = 0; + + transformMatrix.a3 = numbers[6]; + transformMatrix.b3 = numbers[7]; + transformMatrix.c3 = numbers[8]; + transformMatrix.d3 = 0; + + transformMatrix.a4 = numbers[9]; + transformMatrix.b4 = numbers[10]; + transformMatrix.c4 = numbers[11]; + transformMatrix.d4 = 1; + + return transformMatrix; +} + +bool parseColor(const char *color, aiColor4D &diffuse) { + if (nullptr == color) { + return false; + } + + if (!validateColorString(color)) { + return false; + } + + //const char *buf(color); + if ('#' != color[0]) { + return false; + } + + char r[3] = { color[1], color[2], '\0' }; + diffuse.r = static_cast(strtol(r, nullptr, 16)) / ai_real(255.0); + + char g[3] = { color[3], color[4], '\0' }; + diffuse.g = static_cast(strtol(g, nullptr, 16)) / ai_real(255.0); + + char b[3] = { color[5], color[6], '\0' }; + diffuse.b = static_cast(strtol(b, nullptr, 16)) / ai_real(255.0); + const size_t len = strlen(color); + if (ColRGB_Len == len) { + return true; + } + + char a[3] = { color[7], color[8], '\0' }; + diffuse.a = static_cast(strtol(a, nullptr, 16)) / ai_real(255.0); + + return true; +} + +void assignDiffuseColor(XmlNode &node, aiMaterial *mat) { + const char *color = node.attribute(XmlTag::basematerials_displaycolor).as_string(); + aiColor4D diffuse; + if (parseColor(color, diffuse)) { + mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE); + } +} + +} // namespace + XmlSerializer::XmlSerializer(XmlParser *xmlParser, D3MFOpcPackage *archive) : mResourcesDictionnary(), mMeshCount(0), @@ -164,71 +308,6 @@ void XmlSerializer::addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nod } } -bool XmlSerializer::getNodeAttribute(const XmlNode &node, const std::string &attribute, std::string &value) { - pugi::xml_attribute objectAttribute = node.attribute(attribute.c_str()); - if (!objectAttribute.empty()) { - value = objectAttribute.as_string(); - return true; - } - - return false; -} - -bool XmlSerializer::getNodeAttribute(const XmlNode &node, const std::string &attribute, int &value) { - std::string strValue; - bool ret = getNodeAttribute(node, attribute, strValue); - if (ret) { - value = std::atoi(strValue.c_str()); - return true; - } - - return false; -} - -aiMatrix4x4 XmlSerializer::parseTransformMatrix(std::string matrixStr) { - // split the string - std::vector numbers; - std::string currentNumber; - for (char c : matrixStr) { - if (c == ' ') { - if (!currentNumber.empty()) { - float f = std::stof(currentNumber); - numbers.push_back(f); - currentNumber.clear(); - } - } else { - currentNumber.push_back(c); - } - } - if (!currentNumber.empty()) { - const float f = std::stof(currentNumber); - numbers.push_back(f); - } - - aiMatrix4x4 transformMatrix; - transformMatrix.a1 = numbers[0]; - transformMatrix.b1 = numbers[1]; - transformMatrix.c1 = numbers[2]; - transformMatrix.d1 = 0; - - transformMatrix.a2 = numbers[3]; - transformMatrix.b2 = numbers[4]; - transformMatrix.c2 = numbers[5]; - transformMatrix.d2 = 0; - - transformMatrix.a3 = numbers[6]; - transformMatrix.b3 = numbers[7]; - transformMatrix.c3 = numbers[8]; - transformMatrix.d3 = 0; - - transformMatrix.a4 = numbers[9]; - transformMatrix.b4 = numbers[10]; - transformMatrix.c4 = numbers[11]; - transformMatrix.d4 = 1; - - return transformMatrix; -} - void XmlSerializer::ReadObject(XmlNode &node) { int id = IdNotSet, pid = IdNotSet, pindex = IdNotSet; bool hasId = getNodeAttribute(node, XmlTag::id, id); @@ -327,19 +406,8 @@ void XmlSerializer::ImportVertices(XmlNode &node, aiMesh *mesh) { std::copy(vertices.begin(), vertices.end(), mesh->mVertices); } -aiVector3D XmlSerializer::ReadVertex(XmlNode &node) { - aiVector3D vertex; - vertex.x = ai_strtof(node.attribute(XmlTag::x).as_string(), nullptr); - vertex.y = ai_strtof(node.attribute(XmlTag::y).as_string(), nullptr); - vertex.z = ai_strtof(node.attribute(XmlTag::z).as_string(), nullptr); - - return vertex; -} - void XmlSerializer::ImportTriangles(XmlNode &node, aiMesh *mesh) { std::vector faces; - - const size_t numTriangles = std::distance(node.children(XmlTag::triangle).begin(), node.children(XmlTag::triangle).end()); for (XmlNode ¤tNode : node.children()) { const std::string currentName = currentNode.name(); if (currentName == XmlTag::triangle) { @@ -383,18 +451,6 @@ void XmlSerializer::ImportTriangles(XmlNode &node, aiMesh *mesh) { std::copy(faces.begin(), faces.end(), mesh->mFaces); } -aiFace XmlSerializer::ReadTriangle(XmlNode &node) { - aiFace face; - - face.mNumIndices = 3; - face.mIndices = new unsigned int[face.mNumIndices]; - face.mIndices[0] = static_cast(std::atoi(node.attribute(XmlTag::v1).as_string())); - face.mIndices[1] = static_cast(std::atoi(node.attribute(XmlTag::v2).as_string())); - face.mIndices[2] = static_cast(std::atoi(node.attribute(XmlTag::v3).as_string())); - - return face; -} - void XmlSerializer::ReadBaseMaterials(XmlNode &node) { int id = IdNotSet; if (getNodeAttribute(node, D3MF::XmlTag::id, id)) { @@ -412,52 +468,6 @@ void XmlSerializer::ReadBaseMaterials(XmlNode &node) { } } -static const size_t ColRGBA_Len = 9; -static const size_t ColRGB_Len = 7; - -// format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1) -static bool validateColorString(const char *color) { - const size_t len = strlen(color); - if (ColRGBA_Len != len && ColRGB_Len != len) { - return false; - } - - return true; -} - -bool XmlSerializer::parseColor(const char *color, aiColor4D &diffuse) { - if (nullptr == color) { - return false; - } - - if (!validateColorString(color)) { - return false; - } - - //const char *buf(color); - if ('#' != color[0]) { - return false; - } - - char r[3] = { color[1], color[2], '\0' }; - diffuse.r = static_cast(strtol(r, nullptr, 16)) / ai_real(255.0); - - char g[3] = { color[3], color[4], '\0' }; - diffuse.g = static_cast(strtol(g, nullptr, 16)) / ai_real(255.0); - - char b[3] = { color[5], color[6], '\0' }; - diffuse.b = static_cast(strtol(b, nullptr, 16)) / ai_real(255.0); - const size_t len = strlen(color); - if (ColRGB_Len == len) { - return true; - } - - char a[3] = { color[7], color[8], '\0' }; - diffuse.a = static_cast(strtol(a, nullptr, 16)) / ai_real(255.0); - - return true; -} - void XmlSerializer::ReadEmbeddecTexture(XmlNode &node) { if (node.empty()) { return; @@ -509,11 +519,13 @@ void XmlSerializer::ReadTextureCoords2D(XmlNode &node, Texture2DGroup *tex2DGrou if (node.empty() || nullptr == tex2DGroup) { return; } + int id = IdNotSet; if (XmlParser::getIntAttribute(node, "texid", id)) { tex2DGroup->mTexId = id; } - double value; + + double value = 0.0; for (XmlNode currentNode : node.children()) { const std::string currentName = currentNode.name(); aiVector2D texCoord; @@ -542,14 +554,6 @@ void XmlSerializer::ReadTextureGroup(XmlNode &node) { mResourcesDictionnary.insert(std::make_pair(id, group)); } -void XmlSerializer::assignDiffuseColor(XmlNode &node, aiMaterial *mat) { - const char *color = node.attribute(XmlTag::basematerials_displaycolor).as_string(); - aiColor4D diffuse; - if (parseColor(color, diffuse)) { - mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE); - } -} - aiMaterial *XmlSerializer::readMaterialDef(XmlNode &node, unsigned int basematerialsId) { aiMaterial *material = new aiMaterial(); material->mNumProperties = 0; @@ -576,16 +580,17 @@ aiMaterial *XmlSerializer::readMaterialDef(XmlNode &node, unsigned int basemater return material; } -void XmlSerializer::StoreMaterialsInScene( aiScene *scene ) { +void XmlSerializer::StoreMaterialsInScene(aiScene *scene) { if (nullptr == scene || mMaterials.empty()) { return; } scene->mNumMaterials = static_cast(mMaterials.size()); - scene->mMaterials = new aiMaterial*[scene->mNumMaterials]; + scene->mMaterials = new aiMaterial *[scene->mNumMaterials]; for (size_t i = 0; i < mMaterials.size(); ++i) { scene->mMaterials[i] = mMaterials[i]; } } + } // namespace D3MF } // namespace Assimp diff --git a/code/AssetLib/3MF/XmlSerializer.h b/code/AssetLib/3MF/XmlSerializer.h index 40300d70d..e6714b250 100644 --- a/code/AssetLib/3MF/XmlSerializer.h +++ b/code/AssetLib/3MF/XmlSerializer.h @@ -66,23 +66,16 @@ public: private: void addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform); - bool getNodeAttribute(const XmlNode &node, const std::string &attribute, std::string &value); - bool getNodeAttribute(const XmlNode &node, const std::string &attribute, int &value); - aiMatrix4x4 parseTransformMatrix(std::string matrixStr); void ReadObject(XmlNode &node); aiMesh *ReadMesh(XmlNode &node); void ReadMetadata(XmlNode &node); void ImportVertices(XmlNode &node, aiMesh *mesh); - aiVector3D ReadVertex(XmlNode &node); void ImportTriangles(XmlNode &node, aiMesh *mesh); - aiFace ReadTriangle(XmlNode &node); void ReadBaseMaterials(XmlNode &node); - bool parseColor(const char *color, aiColor4D &diffuse); void ReadEmbeddecTexture(XmlNode &node); void StoreEmbeddedTexture(EmbeddedTexture *tex); void ReadTextureCoords2D(XmlNode &node, Texture2DGroup *tex2DGroup); void ReadTextureGroup(XmlNode &node); - void assignDiffuseColor(XmlNode &node, aiMaterial *mat); aiMaterial *readMaterialDef(XmlNode &node, unsigned int basematerialsId); void StoreMaterialsInScene(aiScene *scene); From 5ca2cbb7ae0bf43be94683d0f244b44e2a7d3a51 Mon Sep 17 00:00:00 2001 From: kirillsurkov Date: Sat, 28 Aug 2021 20:04:37 +0300 Subject: [PATCH 248/335] Fix MinGW build --- code/AssetLib/FBX/FBXExporter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/code/AssetLib/FBX/FBXExporter.cpp b/code/AssetLib/FBX/FBXExporter.cpp index 84a77e18d..f4a3017d8 100644 --- a/code/AssetLib/FBX/FBXExporter.cpp +++ b/code/AssetLib/FBX/FBXExporter.cpp @@ -1803,7 +1803,7 @@ void FBXExporter::WriteObjects () blendchannel_uid, blendshape_name + FBX::SEPARATOR + "SubDeformer", "BlendShapeChannel" ); sdnode.AddChild("Version", int32_t(100)); - sdnode.AddChild("DeformPercent", float_t(0.0)); + sdnode.AddChild("DeformPercent", float(0.0)); FBX::Node p("Properties70"); p.AddP70numberA("DeformPercent", 0.0); sdnode.AddChild(p); From ceafa95610cbab9c9ec29b6ca00c16c0d25b229d Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 29 Aug 2021 10:36:02 +0200 Subject: [PATCH 249/335] Remove unused header --- code/AssetLib/3MF/D3MFOpcPackage.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/code/AssetLib/3MF/D3MFOpcPackage.cpp b/code/AssetLib/3MF/D3MFOpcPackage.cpp index 5a8c9a2bf..25ed4a368 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.cpp +++ b/code/AssetLib/3MF/D3MFOpcPackage.cpp @@ -58,8 +58,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -#include "contrib/stb/stb_image.h" - namespace Assimp { namespace D3MF { From c9d35b6edcc87ca2f85742b23c2a3dae08ef23b1 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 29 Aug 2021 18:35:44 +0200 Subject: [PATCH 250/335] Remove C++14 feature. --- code/AssetLib/3MF/D3MFOpcPackage.cpp | 3 ++- code/AssetLib/3MF/D3MFOpcPackage.h | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/code/AssetLib/3MF/D3MFOpcPackage.cpp b/code/AssetLib/3MF/D3MFOpcPackage.cpp index 25ed4a368..c29cec368 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.cpp +++ b/code/AssetLib/3MF/D3MFOpcPackage.cpp @@ -133,7 +133,7 @@ static bool IsEmbeddedTexture( const std::string &filename ) { D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : mRootStream(nullptr), mZipArchive() { - mZipArchive = std::make_unique(pIOHandler, rFile); + mZipArchive = new ZipArchiveIOSystem(pIOHandler, rFile); if (!mZipArchive->isOpen()) { throw DeadlyImportError("Failed to open file ", rFile, "."); } @@ -185,6 +185,7 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : D3MFOpcPackage::~D3MFOpcPackage() { mZipArchive->Close(mRootStream); + delete mZipArchive; mZipArchive = nullptr; } diff --git a/code/AssetLib/3MF/D3MFOpcPackage.h b/code/AssetLib/3MF/D3MFOpcPackage.h index 93928b9a7..fda74a879 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.h +++ b/code/AssetLib/3MF/D3MFOpcPackage.h @@ -74,7 +74,7 @@ protected: private: IOStream* mRootStream; - std::unique_ptr mZipArchive; + ZipArchiveIOSystem *mZipArchive; std::vector mEmbeddedTextures; }; From e2c2a60c45aeb3cd9cac2db5861e8dc44a523dbd Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Sun, 29 Aug 2021 18:41:07 +0200 Subject: [PATCH 251/335] Remove not used attribute --- code/AssetLib/3MF/D3MFImporter.cpp | 2 +- code/AssetLib/3MF/XmlSerializer.cpp | 6 ++---- code/AssetLib/3MF/XmlSerializer.h | 3 +-- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/code/AssetLib/3MF/D3MFImporter.cpp b/code/AssetLib/3MF/D3MFImporter.cpp index fe3c07744..58dde9738 100644 --- a/code/AssetLib/3MF/D3MFImporter.cpp +++ b/code/AssetLib/3MF/D3MFImporter.cpp @@ -123,7 +123,7 @@ void D3MFImporter::InternReadFile(const std::string &filename, aiScene *pScene, XmlParser xmlParser; if (xmlParser.parse(opcPackage.RootStream())) { - XmlSerializer xmlSerializer(&xmlParser, &opcPackage); + XmlSerializer xmlSerializer(&xmlParser); xmlSerializer.ImportXml(pScene); const std::vector &tex = opcPackage.GetEmbeddedTextures(); diff --git a/code/AssetLib/3MF/XmlSerializer.cpp b/code/AssetLib/3MF/XmlSerializer.cpp index daef9a8ac..7a33d08ed 100644 --- a/code/AssetLib/3MF/XmlSerializer.cpp +++ b/code/AssetLib/3MF/XmlSerializer.cpp @@ -193,13 +193,11 @@ void assignDiffuseColor(XmlNode &node, aiMaterial *mat) { } // namespace -XmlSerializer::XmlSerializer(XmlParser *xmlParser, D3MFOpcPackage *archive) : +XmlSerializer::XmlSerializer(XmlParser *xmlParser) : mResourcesDictionnary(), mMeshCount(0), - mXmlParser(xmlParser), - mD3MFOpcPackage(archive) { + mXmlParser(xmlParser) { ai_assert(nullptr != xmlParser); - ai_assert(nullptr != archive); } XmlSerializer::~XmlSerializer() { diff --git a/code/AssetLib/3MF/XmlSerializer.h b/code/AssetLib/3MF/XmlSerializer.h index e6714b250..14da82e99 100644 --- a/code/AssetLib/3MF/XmlSerializer.h +++ b/code/AssetLib/3MF/XmlSerializer.h @@ -60,7 +60,7 @@ class EmbeddedTexture; class XmlSerializer { public: - XmlSerializer(XmlParser *xmlParser, D3MFOpcPackage *archive); + XmlSerializer(XmlParser *xmlParser); ~XmlSerializer(); void ImportXml(aiScene *scene); @@ -90,7 +90,6 @@ private: std::map mResourcesDictionnary; unsigned int mMeshCount; XmlParser *mXmlParser; - D3MFOpcPackage *mD3MFOpcPackage; }; } // namespace D3MF From 38c611a02c3ed8af773710e2d2345b8e42893647 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 30 Aug 2021 08:33:35 +0200 Subject: [PATCH 252/335] Update ColladaLoader.cpp --- code/AssetLib/Collada/ColladaLoader.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/code/AssetLib/Collada/ColladaLoader.cpp b/code/AssetLib/Collada/ColladaLoader.cpp index a7b5ef5ef..f7b5f2278 100644 --- a/code/AssetLib/Collada/ColladaLoader.cpp +++ b/code/AssetLib/Collada/ColladaLoader.cpp @@ -1543,9 +1543,9 @@ void ColladaLoader::AddTexture(aiMaterial &mat, map = sampler.mUVId; } else { map = -1; - for (char it : sampler.mUVChannel) { - if (IsNumeric(it)) { - map = strtoul10(&it); + for (std::string::const_iterator it = sampler.mUVChannel.begin(); it != sampler.mUVChannel.end(); ++it) { + if (IsNumeric(*it)) { + map = strtoul10(&(*it)); break; } } From 51f294c5876b2e41f72cd49561394d003c7c060a Mon Sep 17 00:00:00 2001 From: "Max Vollmer (Microsoft Havok)" <60260460+ms-maxvollmer@users.noreply.github.com> Date: Mon, 30 Aug 2021 14:59:17 +0100 Subject: [PATCH 253/335] Fixes issues our internal compliance and code quality tool found: * Adds nullptr checks and asserts to protect certain code paths * Fixes wrong integer type in a printf call * Adds const to const values * Prevents integer overflow with explicit casts --- code/AssetLib/FBX/FBXConverter.cpp | 4 +++- code/Common/DefaultIOSystem.cpp | 2 +- code/Common/ScenePreprocessor.cpp | 3 +++ code/Material/MaterialSystem.cpp | 12 +++++++++--- code/PostProcessing/OptimizeGraph.cpp | 2 +- code/PostProcessing/PretransformVertices.cpp | 2 +- code/PostProcessing/SortByPTypeProcess.cpp | 2 +- 7 files changed, 19 insertions(+), 8 deletions(-) diff --git a/code/AssetLib/FBX/FBXConverter.cpp b/code/AssetLib/FBX/FBXConverter.cpp index a92745fb6..fa7ee3986 100644 --- a/code/AssetLib/FBX/FBXConverter.cpp +++ b/code/AssetLib/FBX/FBXConverter.cpp @@ -917,8 +917,10 @@ void FBXConverter::ConvertModel(const Model &model, aiNode *parent, aiNode *root } else if (line) { const std::vector &indices = ConvertLine(*line, root_node); std::copy(indices.begin(), indices.end(), std::back_inserter(meshes)); - } else { + } else if (geo) { FBXImporter::LogWarn("ignoring unrecognized geometry: ", geo->Name()); + } else { + FBXImporter::LogWarn("skipping null geometry"); } } diff --git a/code/Common/DefaultIOSystem.cpp b/code/Common/DefaultIOSystem.cpp index 1ed615b84..de93909de 100644 --- a/code/Common/DefaultIOSystem.cpp +++ b/code/Common/DefaultIOSystem.cpp @@ -173,7 +173,7 @@ inline static std::string MakeAbsolutePath(const char *in) { free(ret); } #endif - if (!ret) { + else { // preserve the input path, maybe someone else is able to fix // the path before it is accessed (e.g. our file system filter) ASSIMP_LOG_WARN("Invalid path: ", std::string(in)); diff --git a/code/Common/ScenePreprocessor.cpp b/code/Common/ScenePreprocessor.cpp index 132b32df7..2ea17c643 100644 --- a/code/Common/ScenePreprocessor.cpp +++ b/code/Common/ScenePreprocessor.cpp @@ -89,6 +89,9 @@ void ScenePreprocessor::ProcessScene() { ASSIMP_LOG_DEBUG("ScenePreprocessor: Adding default material \'" AI_DEFAULT_MATERIAL_NAME "\'"); for (unsigned int i = 0; i < scene->mNumMeshes; ++i) { + if (nullptr == scene->mMeshes[i]) { + continue; + } scene->mMeshes[i]->mMaterialIndex = scene->mNumMaterials; } diff --git a/code/Material/MaterialSystem.cpp b/code/Material/MaterialSystem.cpp index c35a1aa93..23d198953 100644 --- a/code/Material/MaterialSystem.cpp +++ b/code/Material/MaterialSystem.cpp @@ -555,17 +555,23 @@ uint32_t Assimp::ComputeMaterialHash(const aiMaterial *mat, bool includeMatName } // ------------------------------------------------------------------------------------------------ -void aiMaterial::CopyPropertyList(aiMaterial *pcDest, +void aiMaterial::CopyPropertyList(aiMaterial *const pcDest, const aiMaterial *pcSrc) { ai_assert(nullptr != pcDest); ai_assert(nullptr != pcSrc); + ai_assert(pcDest->mNumProperties <= pcDest->mNumAllocated); + ai_assert(pcSrc->mNumProperties <= pcSrc->mNumAllocated); - unsigned int iOldNum = pcDest->mNumProperties; + const unsigned int iOldNum = pcDest->mNumProperties; pcDest->mNumAllocated += pcSrc->mNumAllocated; pcDest->mNumProperties += pcSrc->mNumProperties; + const unsigned int numAllocated = pcDest->mNumAllocated; aiMaterialProperty **pcOld = pcDest->mProperties; - pcDest->mProperties = new aiMaterialProperty *[pcDest->mNumAllocated]; + pcDest->mProperties = new aiMaterialProperty *[numAllocated]; + + ai_assert(!iOldNum || pcOld); + ai_assert(iOldNum < numAllocated); if (iOldNum && pcOld) { for (unsigned int i = 0; i < iOldNum; ++i) { diff --git a/code/PostProcessing/OptimizeGraph.cpp b/code/PostProcessing/OptimizeGraph.cpp index e33c2ab18..d7bcf3fec 100644 --- a/code/PostProcessing/OptimizeGraph.cpp +++ b/code/PostProcessing/OptimizeGraph.cpp @@ -170,7 +170,7 @@ void OptimizeGraphProcess::CollectNewChildren(aiNode *nd, std::list &n ++it; } if (join_master && !join.empty()) { - join_master->mName.length = ::ai_snprintf(join_master->mName.data, MAXLEN, "$MergedNode_%i", count_merged++); + join_master->mName.length = ::ai_snprintf(join_master->mName.data, MAXLEN, "$MergedNode_%u", count_merged++); unsigned int out_meshes = 0; for (std::list::const_iterator it = join.cbegin(); it != join.cend(); ++it) { diff --git a/code/PostProcessing/PretransformVertices.cpp b/code/PostProcessing/PretransformVertices.cpp index e9a3af0d2..fa95319ff 100644 --- a/code/PostProcessing/PretransformVertices.cpp +++ b/code/PostProcessing/PretransformVertices.cpp @@ -481,7 +481,7 @@ void PretransformVertices::Execute(aiScene *pScene) { pScene->mMeshes[i]->mNumBones = 0; } } else { - apcOutMeshes.reserve(pScene->mNumMaterials << 1u); + apcOutMeshes.reserve(static_cast(pScene->mNumMaterials) << 1u); std::list aiVFormats; std::vector s(pScene->mNumMeshes, 0); diff --git a/code/PostProcessing/SortByPTypeProcess.cpp b/code/PostProcessing/SortByPTypeProcess.cpp index 20ab63249..3787be51e 100644 --- a/code/PostProcessing/SortByPTypeProcess.cpp +++ b/code/PostProcessing/SortByPTypeProcess.cpp @@ -127,7 +127,7 @@ void SortByPTypeProcess::Execute(aiScene *pScene) { unsigned int aiNumMeshesPerPType[4] = { 0, 0, 0, 0 }; std::vector outMeshes; - outMeshes.reserve(pScene->mNumMeshes << 1u); + outMeshes.reserve(static_cast(pScene->mNumMeshes) << 1u); bool bAnyChanges = false; From 96f0787f51e91b759132ef5ecf631dd530e749f0 Mon Sep 17 00:00:00 2001 From: Doug Roeper Date: Mon, 30 Aug 2021 18:15:37 -0400 Subject: [PATCH 254/335] Fix the -Werror=unused-but-set-parameter warning by removing the skipFirst variable. --- code/AssetLib/XGL/XGLLoader.cpp | 7 ++----- code/AssetLib/XGL/XGLLoader.h | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/code/AssetLib/XGL/XGLLoader.cpp b/code/AssetLib/XGL/XGLLoader.cpp index 20c2c7079..bbfa31829 100644 --- a/code/AssetLib/XGL/XGLLoader.cpp +++ b/code/AssetLib/XGL/XGLLoader.cpp @@ -250,7 +250,7 @@ void XGLImporter::ReadWorld(XmlNode &node, TempScope &scope) { } } - aiNode *const nd = ReadObject(node, scope, true); + aiNode *const nd = ReadObject(node, scope); if (!nd) { ThrowException("failure reading "); } @@ -296,16 +296,13 @@ aiLight *XGLImporter::ReadDirectionalLight(XmlNode &node) { } // ------------------------------------------------------------------------------------------------ -aiNode *XGLImporter::ReadObject(XmlNode &node, TempScope &scope, bool skipFirst/*, const char *closetag */) { +aiNode *XGLImporter::ReadObject(XmlNode &node, TempScope &scope) { aiNode *nd = new aiNode; std::vector children; std::vector meshes; try { for (XmlNode &child : node.children()) { - - skipFirst = false; - const std::string &s = ai_stdStrToLower(child.name()); if (s == "mesh") { const size_t prev = scope.meshes_linear.size(); diff --git a/code/AssetLib/XGL/XGLLoader.h b/code/AssetLib/XGL/XGLLoader.h index f7da4e0a7..a2b224ac9 100644 --- a/code/AssetLib/XGL/XGLLoader.h +++ b/code/AssetLib/XGL/XGLLoader.h @@ -185,7 +185,7 @@ private: void ReadWorld(XmlNode &node, TempScope &scope); void ReadLighting(XmlNode &node, TempScope &scope); aiLight *ReadDirectionalLight(XmlNode &node); - aiNode *ReadObject(XmlNode &node, TempScope &scope, bool skipFirst = false/*, const char *closetag = "object"*/); + aiNode *ReadObject(XmlNode &node, TempScope &scope); bool ReadMesh(XmlNode &node, TempScope &scope); void ReadMaterial(XmlNode &node, TempScope &scope); aiVector2D ReadVec2(XmlNode &node); From 9b535d1c159d8501a97f4399abf78e256afb29bb Mon Sep 17 00:00:00 2001 From: Madrich Date: Tue, 31 Aug 2021 12:59:31 +0200 Subject: [PATCH 255/335] Fix Double Precision errors/warnings --- CMakeLists.txt | 3 ++- code/AssetLib/3DS/3DSLoader.cpp | 2 +- code/PostProcessing/CalcTangentsProcess.cpp | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bfa30e96a..9f893646a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -265,8 +265,9 @@ ELSEIF(MSVC) ENDIF() # disable "elements of array '' will be default initialized" warning on MSVC2013 IF(MSVC12) - ADD_COMPILE_OPTIONS(/wd4351) + ADD_COMPILE_OPTIONS(/wd4351) ENDIF() + ADD_COMPILE_OPTIONS(/wd4244) #supress warning for double to float conversion if Double precission is activated SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_DEBUG /Zi /Od") SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi") SET(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG:FULL /PDBALTPATH:%_PDB% /OPT:REF /OPT:ICF") diff --git a/code/AssetLib/3DS/3DSLoader.cpp b/code/AssetLib/3DS/3DSLoader.cpp index b5e6f749b..05b5117ba 100644 --- a/code/AssetLib/3DS/3DSLoader.cpp +++ b/code/AssetLib/3DS/3DSLoader.cpp @@ -449,7 +449,7 @@ void Discreet3DSImporter::ParseChunk(const char *name, unsigned int num) { // Read the lense angle camera->mHorizontalFOV = AI_DEG_TO_RAD(stream->GetF4()); if (camera->mHorizontalFOV < 0.001f) { - camera->mHorizontalFOV = AI_DEG_TO_RAD(45.f); + camera->mHorizontalFOV = float(AI_DEG_TO_RAD(45.f)); } // Now check for further subchunks diff --git a/code/PostProcessing/CalcTangentsProcess.cpp b/code/PostProcessing/CalcTangentsProcess.cpp index 46feff4a1..3e6bb0270 100644 --- a/code/PostProcessing/CalcTangentsProcess.cpp +++ b/code/PostProcessing/CalcTangentsProcess.cpp @@ -56,7 +56,7 @@ using namespace Assimp; // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer CalcTangentsProcess::CalcTangentsProcess() : - configMaxAngle(AI_DEG_TO_RAD(45.f)), configSourceUV(0) { + configMaxAngle(float(AI_DEG_TO_RAD(45.f))), configSourceUV(0) { // nothing to do here } From 4c86772091dc5a9b0373adf3faabc4a321b92437 Mon Sep 17 00:00:00 2001 From: "Max Vollmer (Microsoft Havok)" <60260460+ms-maxvollmer@users.noreply.github.com> Date: Thu, 2 Sep 2021 08:27:03 +0100 Subject: [PATCH 256/335] Added another nullptr safety check --- code/AssetLib/glTF2/glTF2Importer.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index 08573bce7..ada7aa046 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1337,6 +1337,17 @@ std::unordered_map GatherSamplers(Animation &an } auto& animsampler = anim.samplers[channel.sampler]; + + if (!animsampler.input) { + ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler input. Skipping."); + continue; + } + + if (!animsampler.output) { + ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler output. Skipping."); + continue; + } + if (animsampler.input->count > animsampler.output->count) { ASSIMP_LOG_WARN("Animation ", anim.name, ": Number of keyframes in sampler input ", animsampler.input->count, " exceeds number of keyframes in sampler output ", animsampler.output->count); continue; From d710d0700fbfaf7c20153e4d89aa1eccc2aa232e Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Thu, 2 Sep 2021 10:10:42 +0200 Subject: [PATCH 257/335] Make nullptr test more explicit. --- code/AssetLib/glTF2/glTF2Importer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index ada7aa046..2786614f3 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1338,12 +1338,12 @@ std::unordered_map GatherSamplers(Animation &an auto& animsampler = anim.samplers[channel.sampler]; - if (!animsampler.input) { + if (nullptr == animsampler.input) { ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler input. Skipping."); continue; } - if (!animsampler.output) { + if (nullptr == animsampler.output) { ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler output. Skipping."); continue; } From 72ea80b41f7364ffacd5878ce4c357aaef0dd92d Mon Sep 17 00:00:00 2001 From: "Max Vollmer (Microsoft Havok)" <60260460+ms-maxvollmer@users.noreply.github.com> Date: Thu, 2 Sep 2021 10:00:56 +0100 Subject: [PATCH 258/335] Revert last change (gltf2::Ref type is not a pointer and has a bool() operator) --- code/AssetLib/glTF2/glTF2Importer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index 2786614f3..aa48b7330 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1338,12 +1338,12 @@ std::unordered_map GatherSamplers(Animation &an auto& animsampler = anim.samplers[channel.sampler]; - if (nullptr == animsampler.input) { + if (animsampler.input) { ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler input. Skipping."); continue; } - if (nullptr == animsampler.output) { + if (animsampler.output) { ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler output. Skipping."); continue; } From bf8e36ae28247bbb36a243215945748366be4f4d Mon Sep 17 00:00:00 2001 From: "Max Vollmer (Microsoft Havok)" <60260460+ms-maxvollmer@users.noreply.github.com> Date: Thu, 2 Sep 2021 10:07:28 +0100 Subject: [PATCH 259/335] Fixed typo --- code/AssetLib/glTF2/glTF2Importer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index aa48b7330..ada7aa046 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -1338,12 +1338,12 @@ std::unordered_map GatherSamplers(Animation &an auto& animsampler = anim.samplers[channel.sampler]; - if (animsampler.input) { + if (!animsampler.input) { ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler input. Skipping."); continue; } - if (animsampler.output) { + if (!animsampler.output) { ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler output. Skipping."); continue; } From 3001d88172dfccbe917afd0815381d5524b3e854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Martin?= Date: Tue, 7 Sep 2021 15:04:08 +0200 Subject: [PATCH 260/335] Merge branch 'master' into x3d_pugi_migration --- .gitignore | 2 +- CMakeLists.txt | 20 +- cmake-modules/FindIrrXML.cmake | 17 - {cmake => cmake-modules}/HunterGate.cmake | 0 cmake-modules/assimp-hunter-config.cmake.in | 19 + .../assimp-plain-config.cmake.in | 0 cmake/assimp-hunter-config.cmake.in | 18 - code/AssetLib/3DS/3DSConverter.cpp | 11 +- code/AssetLib/3DS/3DSExporter.cpp | 2 +- code/AssetLib/3DS/3DSHelper.h | 76 +- code/AssetLib/3DS/3DSLoader.h | 9 + code/AssetLib/3MF/3MFTypes.h | 165 + code/AssetLib/3MF/3MFXmlTags.h | 12 +- code/AssetLib/3MF/D3MFImporter.cpp | 522 +- code/AssetLib/3MF/D3MFImporter.h | 34 +- code/AssetLib/3MF/D3MFOpcPackage.cpp | 69 +- code/AssetLib/3MF/D3MFOpcPackage.h | 14 +- code/AssetLib/3MF/XmlSerializer.cpp | 594 ++ code/AssetLib/3MF/XmlSerializer.h | 96 + code/AssetLib/AMF/AMFImporter.cpp | 2 +- code/AssetLib/AMF/AMFImporter_Geometry.cpp | 6 +- code/AssetLib/AMF/AMFImporter_Postprocess.cpp | 6 +- code/AssetLib/ASE/ASEParser.h | 8 +- code/AssetLib/Assbin/AssbinFileWriter.cpp | 2 +- code/AssetLib/Assjson/json_exporter.cpp | 42 +- code/AssetLib/Assjson/mesh_splitter.cpp | 8 +- code/AssetLib/Assjson/mesh_splitter.h | 6 +- code/AssetLib/Assxml/AssxmlExporter.cpp | 2 +- code/AssetLib/B3D/B3DImporter.cpp | 2 +- code/AssetLib/B3D/B3DImporter.h | 2 +- code/AssetLib/Blender/BlenderLoader.cpp | 2 +- code/AssetLib/Blender/BlenderModifier.h | 18 +- code/AssetLib/C4D/C4DImporter.cpp | 6 + code/AssetLib/COB/COBLoader.cpp | 4 +- code/AssetLib/COB/COBLoader.h | 2 +- code/AssetLib/Collada/ColladaLoader.cpp | 25 +- code/AssetLib/Collada/ColladaParser.cpp | 241 +- code/AssetLib/DXF/DXFLoader.cpp | 4 +- code/AssetLib/DXF/DXFLoader.h | 2 +- code/AssetLib/FBX/FBXConverter.cpp | 17 +- code/AssetLib/FBX/FBXConverter.h | 4 +- code/AssetLib/FBX/FBXDocument.cpp | 11 +- code/AssetLib/FBX/FBXDocument.h | 5 + code/AssetLib/FBX/FBXExportNode.cpp | 5 +- code/AssetLib/FBX/FBXExportNode.h | 7 +- code/AssetLib/FBX/FBXExporter.cpp | 145 +- code/AssetLib/FBX/FBXExporter.h | 21 +- code/AssetLib/FBX/FBXMaterial.cpp | 13 +- code/AssetLib/FBX/FBXMeshGeometry.cpp | 2 +- code/AssetLib/FBX/FBXMeshGeometry.h | 8 +- code/AssetLib/FBX/FBXParser.cpp | 28 +- code/AssetLib/FBX/FBXProperties.cpp | 9 +- code/AssetLib/FBX/FBXProperties.h | 6 +- code/AssetLib/FBX/FBXUtil.cpp | 2 +- code/AssetLib/HMP/HMPLoader.cpp | 4 +- code/AssetLib/IFC/IFCBoolean.cpp | 2 +- code/AssetLib/IFC/IFCCurve.cpp | 2 +- code/AssetLib/IFC/IFCGeometry.cpp | 2 +- code/AssetLib/IFC/IFCMaterial.cpp | 6 +- code/AssetLib/IFC/IFCOpenings.cpp | 8 +- code/AssetLib/IFC/IFCReaderGen1_2x3.cpp | 232 +- code/AssetLib/IFC/IFCReaderGen2_2x3.cpp | 162 +- code/AssetLib/IFC/IFCReaderGen_4.cpp | 392 +- code/AssetLib/IFC/IFCReaderGen_4.h | 26 +- code/AssetLib/IFC/IFCUtil.h | 14 +- code/AssetLib/Irr/IRRLoader.h | 2 +- code/AssetLib/LWO/LWOAnimation.h | 2 +- code/AssetLib/LWS/LWSLoader.cpp | 6 +- code/AssetLib/M3D/M3DImporter.cpp | 16 +- code/AssetLib/M3D/M3DImporter.h | 4 +- code/AssetLib/M3D/M3DWrapper.h | 68 +- code/AssetLib/M3D/m3d.h | 1230 +-- code/AssetLib/MD5/MD5Loader.cpp | 2 +- code/AssetLib/MDC/MDCFileData.h | 4 +- code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp | 4 +- code/AssetLib/MMD/MMDPmxParser.cpp | 8 +- code/AssetLib/OFF/OFFLoader.cpp | 10 +- code/AssetLib/Obj/ObjExporter.h | 18 +- code/AssetLib/Obj/ObjFileImporter.cpp | 4 +- code/AssetLib/Obj/ObjFileMtlImporter.cpp | 2 +- code/AssetLib/Ogre/OgreMaterial.cpp | 4 +- code/AssetLib/Ply/PlyParser.cpp | 8 +- code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp | 4 +- code/AssetLib/SMD/SMDLoader.cpp | 2 +- code/AssetLib/STEPParser/STEPFileReader.cpp | 9 +- code/AssetLib/STL/STLExporter.cpp | 8 +- code/AssetLib/STL/STLLoader.cpp | 4 +- code/AssetLib/Step/STEPFile.h | 4 +- code/AssetLib/Step/StepExporter.cpp | 120 +- code/AssetLib/X/XFileExporter.cpp | 6 +- code/AssetLib/X/XFileExporter.h | 6 +- code/AssetLib/X/XFileImporter.cpp | 4 +- code/AssetLib/X3D/X3DExporter.hpp | 6 +- code/AssetLib/X3D/X3DImporter.cpp | 1 - code/AssetLib/XGL/XGLLoader.cpp | 9 +- code/AssetLib/XGL/XGLLoader.h | 2 +- code/AssetLib/glTF/glTFAsset.h | 10 +- code/AssetLib/glTF/glTFAsset.inl | 2 +- code/AssetLib/glTF/glTFCommon.h | 6 +- code/AssetLib/glTF/glTFExporter.cpp | 4 +- code/AssetLib/glTF2/glTF2Asset.h | 103 +- code/AssetLib/glTF2/glTF2Asset.inl | 184 +- code/AssetLib/glTF2/glTF2AssetWriter.inl | 16 +- code/AssetLib/glTF2/glTF2Exporter.cpp | 314 +- code/AssetLib/glTF2/glTF2Exporter.h | 26 +- code/AssetLib/glTF2/glTF2Importer.cpp | 120 +- code/CMakeLists.txt | 28 +- code/Common/Assimp.cpp | 46 + code/Common/DefaultIOStream.cpp | 6 +- code/Common/DefaultIOSystem.cpp | 8 +- code/Common/Exporter.cpp | 4 +- code/Common/FileSystemFilter.h | 4 +- code/Common/Importer.cpp | 80 +- code/Common/Importer.h | 6 +- code/Common/ImporterRegistry.cpp | 4 +- code/Common/RemoveComments.cpp | 7 +- code/Common/SceneCombiner.cpp | 18 +- code/Common/ScenePreprocessor.cpp | 3 + code/Common/Win32DebugLogStream.h | 8 +- code/Common/material.cpp | 12 +- code/Material/MaterialSystem.cpp | 12 +- code/Pbrt/PbrtExporter.cpp | 26 +- code/Pbrt/PbrtExporter.h | 4 +- code/PostProcessing/ArmaturePopulate.h | 2 +- .../PostProcessing/DropFaceNormalsProcess.cpp | 2 +- code/PostProcessing/EmbedTexturesProcess.cpp | 40 +- code/PostProcessing/EmbedTexturesProcess.h | 5 +- code/PostProcessing/FindDegenerates.cpp | 2 +- code/PostProcessing/FindInstancesProcess.cpp | 2 +- code/PostProcessing/MakeVerboseFormat.h | 2 +- code/PostProcessing/OptimizeGraph.cpp | 2 +- code/PostProcessing/PretransformVertices.cpp | 2 +- .../RemoveRedundantMaterials.cpp | 2 +- code/PostProcessing/ScaleProcess.cpp | 38 +- code/PostProcessing/ScaleProcess.h | 4 +- code/PostProcessing/SortByPTypeProcess.cpp | 2 +- .../SplitByBoneCountProcess.cpp | 12 +- code/PostProcessing/SplitLargeMeshes.cpp | 4 +- code/PostProcessing/TextureTransform.cpp | 2 +- code/PostProcessing/TriangulateProcess.cpp | 12 +- contrib/draco/.ruby-version | 1 - contrib/draco/.travis.yml | 31 - contrib/draco/CMakeLists.txt | 8 +- contrib/draco/README.md | 6 +- .../draco/cmake/draco_build_definitions.cmake | 9 +- contrib/draco/cmake/draco_features.cmake | 63 - contrib/draco/cmake/draco_flags.cmake | 9 + contrib/draco/cmake/draco_install.cmake | 2 +- contrib/draco/cmake/draco_sanitizer.cmake | 20 +- contrib/draco/cmake/draco_targets.cmake | 24 +- contrib/draco/src/draco/core/cycle_timer.cc | 14 +- contrib/draco/src/draco/core/cycle_timer.h | 7 +- contrib/draco/src/draco/io/parser_utils.cc | 3 +- contrib/draco/src/draco/io/ply_reader.cc | 4 +- .../draco/src/draco/io/stdio_file_reader.cc | 7 + contrib/openddlparser/code/OpenDDLExport.cpp | 3 +- contrib/openddlparser/code/OpenDDLParser.cpp | 30 +- contrib/openddlparser/code/Value.cpp | 9 +- .../include/openddlparser/OpenDDLParser.h | 13 +- contrib/poly2tri/poly2tri/sweep/sweep.cc | 2 +- {code/Pbrt => contrib/stb}/stb_image.h | 0 contrib/stb_image/stb_image.h | 7462 ----------------- doc/Doxyfile.in | 41 +- doc/dox.h | 18 +- doc/dox_cmd.h | 38 +- fuzz/assimp_fuzzer.cc | 2 +- include/assimp/BaseImporter.h | 2 +- include/assimp/Compiler/poppack1.h | 4 +- include/assimp/Compiler/pushpack1.h | 4 +- include/assimp/Exceptional.h | 2 +- include/assimp/Exporter.hpp | 2 +- include/assimp/IOStreamBuffer.h | 6 +- include/assimp/IOSystem.hpp | 8 +- include/assimp/Logger.hpp | 10 +- include/assimp/MemoryIOWrapper.h | 6 +- include/assimp/SmallVector.h | 8 +- include/assimp/SmoothingGroups.inl | 22 +- include/assimp/XmlParser.h | 10 +- include/assimp/ai_assert.h | 2 +- include/assimp/anim.h | 6 +- include/assimp/cimport.h | 12 +- include/assimp/defs.h | 2 +- include/assimp/fast_atof.h | 4 +- include/assimp/light.h | 2 +- include/assimp/material.h | 130 +- include/assimp/matrix4x4.h | 2 +- include/assimp/matrix4x4.inl | 2 +- include/assimp/mesh.h | 10 +- include/assimp/metadata.h | 2 +- include/assimp/pbrmaterial.h | 46 +- include/assimp/postprocess.h | 14 +- include/assimp/quaternion.h | 2 +- include/assimp/scene.h | 49 +- include/assimp/vector2.inl | 14 +- include/assimp/vector3.h | 33 +- .../windows-innosetup/readme_installer.txt | 2 +- .../readme_installer_vieweronly.txt | 2 +- packaging/windows-mkzip/bin_readme.txt | 2 +- port/AndroidJNI/CMakeLists.txt | 4 +- port/AssimpDelphi/Readme.txt | 2 +- port/PyAssimp/pyassimp/helper.py | 3 +- port/jassimp/jassimp-native/src/jassimp.cpp | 164 +- .../ModelLoaderHelperClasses.h | 26 +- samples/SimpleAssimpViewX/MyDocument.h | 18 +- samples/SimpleOpenGL/Sample_SimpleOpenGL.c | 4 +- .../SimpleTexturedDirectx11/CMakeLists.txt | 6 +- .../SimpleTexturedDirectx11/ModelLoader.cpp | 2 +- .../SimpleTexturedDirectx11/TextureLoader.cpp | 52 +- .../SimpleTexturedDirectx11/main.cpp | 10 +- .../src/model_loading.cpp | 6 +- test/models-nonbsd/3D/mar_rifle.source.txt | 6 +- test/models-nonbsd/3DS/cart_wheel.source.txt | 6 +- test/models-nonbsd/3DS/mar_rifle.source.txt | 6 +- test/models-nonbsd/3DS/mp5_sil.source.txt | 6 +- test/models-nonbsd/ASE/Rifle.source.txt | 6 +- test/models-nonbsd/ASE/Rifle2.source.txt | 6 +- .../BLEND/fleurOptonl.source.txt | 16 +- test/models-nonbsd/DXF/rifle.source.txt | 6 +- .../FBX/2013_ASCII/cart_wheel.source.txt | 6 +- .../kwxport_test_vcolors.fbx.source.txt | 6 +- .../FBX/2013_ASCII/mar_rifle.source.txt | 6 +- .../FBX/2013_ASCII/mp5_sil.source.txt | 6 +- .../FBX/2013_BINARY/cart_wheel.source.txt | 6 +- .../kwxport_test_vcolors.fbx.source.txt | 6 +- .../FBX/2013_BINARY/mar_rifle.source.txt | 6 +- .../FBX/2013_BINARY/mp5_sil.source.txt | 6 +- .../LWO2/LWSReferences/QuickDraw.source.txt | 10 +- test/models-nonbsd/LWO/LWO2/rifle.source.txt | 6 +- test/models-nonbsd/MD2/source.txt | 6 +- test/models-nonbsd/MD5/BoarMan.source.txt | 4 +- .../MDL/IDPO (Quake1)/gijoe-readme.txt | 14 +- test/models-nonbsd/MDL/IDPO (Quake1)/steg.txt | 8 +- .../MDL/IDPO (Quake1)/tekmechbot.txt | 6 +- test/models-nonbsd/NFF/NFFSense8/credits.txt | 2 +- test/models-nonbsd/OBJ/rifle.source.txt | 6 +- test/models/3DS/UVTransformTest/note.txt | 4 +- test/models/ASE/MotionCaptureROM.source.txt | 2 +- test/models/Collada/human.zae | Bin 0 -> 1093924 bytes .../kwxport_test_vcolors.dae.source.txt | 6 +- .../IRR/warn_dwarf_scaling_is_intended.txt | 2 +- test/models/MD2/faerie-source.txt | 2 +- test/models/MD2/sidney-source.txt | 2 +- test/models/Q3D/E-AT-AT.source.txt | 2 +- test/models/Q3D/earth.source.txt | 2 +- test/models/WRL/credits.txt | 2 +- test/models/X/anim_test.txt | 2 +- .../X/kwxport_test_cubewithvcolors.source.txt | 6 +- test/models/X/test.txt | 2 +- .../glTF2/ClearCoat-glTF/ClearCoatLabels.png | Bin 0 -> 10270 bytes .../glTF2/ClearCoat-glTF/ClearCoatTest.bin | Bin 0 -> 50328 bytes .../glTF2/ClearCoat-glTF/ClearCoatTest.gltf | 1669 ++++ .../glTF2/ClearCoat-glTF/PartialCoating.png | Bin 0 -> 5077 bytes .../ClearCoat-glTF/PartialCoating_Alpha.png | Bin 0 -> 5065 bytes .../ClearCoat-glTF/PlasticWrap_normals.jpg | Bin 0 -> 144210 bytes .../glTF2/ClearCoat-glTF/RibsNormal.png | Bin 0 -> 1605 bytes .../glTF2/ClearCoat-glTF/RoughnessStripes.png | Bin 0 -> 5033 bytes test/models/invalid/readme.txt | 8 +- test/regression/README.txt | 6 +- test/unit/AbstractImportExportBase.h | 2 +- test/unit/Common/utStandardShapes.cpp | 2 +- .../MDL/utMDLImporter_HL1_Nodes.cpp | 4 +- .../ImportExport/utAssjsonImportExport.cpp | 13 +- test/unit/RandomNumberGeneration.h | 4 +- test/unit/SceneDiffer.cpp | 2 +- test/unit/SceneDiffer.h | 2 +- test/unit/TestIOSystem.h | 2 +- test/unit/utColladaImportExport.cpp | 22 + test/unit/utDefaultIOStream.cpp | 2 +- test/unit/utFBXImporterExporter.cpp | 4 +- test/unit/utFindDegenerates.cpp | 4 +- test/unit/utIOStreamBuffer.cpp | 6 +- test/unit/utIOSystem.cpp | 10 +- test/unit/utIssues.cpp | 2 +- test/unit/utTypes.cpp | 4 +- test/unit/utVersion.cpp | 2 +- test/unit/utglTF2ImportExport.cpp | 186 +- tools/assimp_cmd/CMakeLists.txt | 2 +- tools/assimp_cmd/Export.cpp | 18 +- tools/assimp_cmd/ImageExtractor.cpp | 20 +- tools/assimp_cmd/Info.cpp | 2 +- tools/assimp_cmd/Main.cpp | 62 +- tools/assimp_cmd/Main.h | 40 +- tools/assimp_cmd/WriteDump.cpp | 18 +- tools/assimp_cmd/resource.h | 2 +- tools/assimp_view/AnimEvaluator.cpp | 5 +- tools/assimp_view/AnimEvaluator.h | 26 +- tools/assimp_view/CMakeLists.txt | 2 +- tools/assimp_view/Display.cpp | 6 +- tools/assimp_view/Material.cpp | 7 +- tools/assimp_view/MaterialManager.h | 73 +- tools/assimp_view/MeshRenderer.cpp | 9 +- tools/assimp_view/MessageProc.cpp | 2 +- tools/assimp_view/Shaders.cpp | 18 +- tools/assimp_view/assimp_view.cpp | 25 +- tools/assimp_view/resource.h | 2 +- 295 files changed, 5518 insertions(+), 11500 deletions(-) delete mode 100644 cmake-modules/FindIrrXML.cmake rename {cmake => cmake-modules}/HunterGate.cmake (100%) create mode 100644 cmake-modules/assimp-hunter-config.cmake.in rename {cmake => cmake-modules}/assimp-plain-config.cmake.in (100%) delete mode 100644 cmake/assimp-hunter-config.cmake.in create mode 100644 code/AssetLib/3MF/3MFTypes.h create mode 100644 code/AssetLib/3MF/XmlSerializer.cpp create mode 100644 code/AssetLib/3MF/XmlSerializer.h delete mode 100644 contrib/draco/.ruby-version delete mode 100644 contrib/draco/.travis.yml delete mode 100644 contrib/draco/cmake/draco_features.cmake rename {code/Pbrt => contrib/stb}/stb_image.h (100%) delete mode 100644 contrib/stb_image/stb_image.h create mode 100644 test/models/Collada/human.zae create mode 100644 test/models/glTF2/ClearCoat-glTF/ClearCoatLabels.png create mode 100644 test/models/glTF2/ClearCoat-glTF/ClearCoatTest.bin create mode 100644 test/models/glTF2/ClearCoat-glTF/ClearCoatTest.gltf create mode 100644 test/models/glTF2/ClearCoat-glTF/PartialCoating.png create mode 100644 test/models/glTF2/ClearCoat-glTF/PartialCoating_Alpha.png create mode 100644 test/models/glTF2/ClearCoat-glTF/PlasticWrap_normals.jpg create mode 100644 test/models/glTF2/ClearCoat-glTF/RibsNormal.png create mode 100644 test/models/glTF2/ClearCoat-glTF/RoughnessStripes.png diff --git a/.gitignore b/.gitignore index 0a999d3aa..7849cab65 100644 --- a/.gitignore +++ b/.gitignore @@ -25,7 +25,7 @@ CMakeSettings.json # Output bin/ lib/ - +x64/ # QtCreator CMakeLists.txt.user diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b9d6fc55..bfa30e96a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,10 +44,10 @@ CMAKE_MINIMUM_REQUIRED( VERSION 3.10 ) option(ASSIMP_HUNTER_ENABLED "Enable Hunter package manager support" OFF) IF(ASSIMP_HUNTER_ENABLED) - include("cmake/HunterGate.cmake") + include("cmake-modules/HunterGate.cmake") HunterGate( - URL "https://github.com/cpp-pm/hunter/archive/v0.23.293.tar.gz" - SHA1 "e8e5470652db77149d9b38656db2a6c0b7642693" + URL "https://github.com/cpp-pm/hunter/archive/v0.23.311.tar.gz" + SHA1 "1a82b9b73055879181cb1466b2ab5d48ee8ae410" ) add_definitions(-DASSIMP_USE_HUNTER) @@ -135,11 +135,11 @@ IF ( WIN32 ) # Use subset of Windows.h ADD_DEFINITIONS( -DWIN32_LEAN_AND_MEAN ) - OPTION ( ASSIMP_BUILD_ASSIMP_VIEW - "If the Assimp view tool is built. (requires DirectX)" - OFF ) - IF(MSVC) + OPTION ( ASSIMP_BUILD_ASSIMP_VIEW + "If the Assimp view tool is built. (requires DirectX)" + OFF ) + OPTION( ASSIMP_INSTALL_PDB "Install MSVC debug files." ON ) @@ -268,6 +268,8 @@ ELSEIF(MSVC) ADD_COMPILE_OPTIONS(/wd4351) ENDIF() SET(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_DEBUG /Zi /Od") + SET(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} /Zi") + SET(CMAKE_SHARED_LINKER_FLAGS_RELEASE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE} /DEBUG:FULL /PDBALTPATH:%_PDB% /OPT:REF /OPT:ICF") ELSEIF (CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) IF(NOT ASSIMP_HUNTER_ENABLED) SET(CMAKE_CXX_STANDARD 11) @@ -395,14 +397,14 @@ set(GENERATED_DIR "${CMAKE_CURRENT_BINARY_DIR}/generated") IF(ASSIMP_HUNTER_ENABLED) set(CONFIG_INSTALL_DIR "lib/cmake/${PROJECT_NAME}") - set(CMAKE_CONFIG_TEMPLATE_FILE "cmake/assimp-hunter-config.cmake.in") + set(CMAKE_CONFIG_TEMPLATE_FILE "cmake-modules/assimp-hunter-config.cmake.in") set(NAMESPACE "${PROJECT_NAME}::") set(TARGETS_EXPORT_NAME "${PROJECT_NAME}Targets") set(VERSION_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}ConfigVersion.cmake") set(PROJECT_CONFIG "${GENERATED_DIR}/${PROJECT_NAME}Config.cmake") ELSE() set(CONFIG_INSTALL_DIR "${ASSIMP_LIB_INSTALL_DIR}/cmake/assimp-${ASSIMP_VERSION_MAJOR}.${ASSIMP_VERSION_MINOR}") - set(CMAKE_CONFIG_TEMPLATE_FILE "cmake/assimp-plain-config.cmake.in") + set(CMAKE_CONFIG_TEMPLATE_FILE "cmake-modules/assimp-plain-config.cmake.in") string(TOLOWER ${PROJECT_NAME} PROJECT_NAME_LOWERCASE) set(NAMESPACE "${PROJECT_NAME_LOWERCASE}::") set(TARGETS_EXPORT_NAME "${PROJECT_NAME_LOWERCASE}Targets") diff --git a/cmake-modules/FindIrrXML.cmake b/cmake-modules/FindIrrXML.cmake deleted file mode 100644 index 5434e0b86..000000000 --- a/cmake-modules/FindIrrXML.cmake +++ /dev/null @@ -1,17 +0,0 @@ -# Find IrrXMl from irrlicht project -# -# Find LibIrrXML headers and library -# -# IRRXML_FOUND - IrrXML found -# IRRXML_INCLUDE_DIR - Headers location -# IRRXML_LIBRARY - IrrXML main library - -find_path(IRRXML_INCLUDE_DIR irrXML.h - PATH_SUFFIXES include/irrlicht include/irrxml) -find_library(IRRXML_LIBRARY IrrXML) - -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(IrrXML REQUIRED_VARS IRRXML_INCLUDE_DIR IRRXML_LIBRARY) - - -mark_as_advanced(IRRXML_INCLUDE_DIR IRRXML_LIBRARY) diff --git a/cmake/HunterGate.cmake b/cmake-modules/HunterGate.cmake similarity index 100% rename from cmake/HunterGate.cmake rename to cmake-modules/HunterGate.cmake diff --git a/cmake-modules/assimp-hunter-config.cmake.in b/cmake-modules/assimp-hunter-config.cmake.in new file mode 100644 index 000000000..1988f7e7d --- /dev/null +++ b/cmake-modules/assimp-hunter-config.cmake.in @@ -0,0 +1,19 @@ +@PACKAGE_INIT@ + +find_package(RapidJSON CONFIG REQUIRED) +find_package(ZLIB CONFIG REQUIRED) +find_package(utf8cpp CONFIG REQUIRED) +find_package(minizip CONFIG REQUIRED) +find_package(openddlparser CONFIG REQUIRED) +find_package(poly2tri CONFIG REQUIRED) +find_package(polyclipping CONFIG REQUIRED) +find_package(zip CONFIG REQUIRED) +find_package(pugixml CONFIG REQUIRED) +find_package(stb CONFIG REQUIRED) + +if(@ASSIMP_BUILD_DRACO@) + find_package(draco CONFIG REQUIRED) +endif() + +include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") +check_required_components("@PROJECT_NAME@") diff --git a/cmake/assimp-plain-config.cmake.in b/cmake-modules/assimp-plain-config.cmake.in similarity index 100% rename from cmake/assimp-plain-config.cmake.in rename to cmake-modules/assimp-plain-config.cmake.in diff --git a/cmake/assimp-hunter-config.cmake.in b/cmake/assimp-hunter-config.cmake.in deleted file mode 100644 index 91efcbf24..000000000 --- a/cmake/assimp-hunter-config.cmake.in +++ /dev/null @@ -1,18 +0,0 @@ -@PACKAGE_INIT@ - -find_package(RapidJSON CONFIG REQUIRED) -find_package(ZLIB CONFIG REQUIRED) -find_package(utf8cpp CONFIG REQUIRED) -find_package(minizip CONFIG REQUIRED) -find_package(openddlparser CONFIG REQUIRED) -find_package(poly2tri CONFIG REQUIRED) -find_package(polyclipping CONFIG REQUIRED) -find_package(zip CONFIG REQUIRED) -find_package(pugixml CONFIG REQUIRED) - -if(@ASSIMP_BUILD_DRACO@) - find_package(draco CONFIG REQUIRED) -endif() - -include("${CMAKE_CURRENT_LIST_DIR}/@TARGETS_EXPORT_NAME@.cmake") -check_required_components("@PROJECT_NAME@") diff --git a/code/AssetLib/3DS/3DSConverter.cpp b/code/AssetLib/3DS/3DSConverter.cpp index aca16b0d6..add1553bc 100644 --- a/code/AssetLib/3DS/3DSConverter.cpp +++ b/code/AssetLib/3DS/3DSConverter.cpp @@ -68,8 +68,8 @@ void Discreet3DSImporter::ReplaceDefaultMaterial() { unsigned int idx(NotSet); for (unsigned int i = 0; i < mScene->mMaterials.size(); ++i) { std::string s = mScene->mMaterials[i].mName; - for (std::string::iterator it = s.begin(); it != s.end(); ++it) { - *it = static_cast(::tolower(static_cast(*it))); + for (char & it : s) { + it = static_cast(::tolower(static_cast(it))); } if (std::string::npos == s.find("default")) continue; @@ -79,12 +79,7 @@ void Discreet3DSImporter::ReplaceDefaultMaterial() { mScene->mMaterials[i].mDiffuse.r != mScene->mMaterials[i].mDiffuse.b) continue; - if (mScene->mMaterials[i].sTexDiffuse.mMapName.length() != 0 || - mScene->mMaterials[i].sTexBump.mMapName.length() != 0 || - mScene->mMaterials[i].sTexOpacity.mMapName.length() != 0 || - mScene->mMaterials[i].sTexEmissive.mMapName.length() != 0 || - mScene->mMaterials[i].sTexSpecular.mMapName.length() != 0 || - mScene->mMaterials[i].sTexShininess.mMapName.length() != 0) { + if (ContainsTextures(i)) { continue; } idx = i; diff --git a/code/AssetLib/3DS/3DSExporter.cpp b/code/AssetLib/3DS/3DSExporter.cpp index 92a6d5aa7..0beecd563 100644 --- a/code/AssetLib/3DS/3DSExporter.cpp +++ b/code/AssetLib/3DS/3DSExporter.cpp @@ -291,7 +291,7 @@ void Discreet3DSExporter::WriteMaterials() { ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_SPECULAR); WriteColor(color); } - + if (mat.Get(AI_MATKEY_COLOR_AMBIENT, color) == AI_SUCCESS) { ChunkWriter curChunk(writer, Discreet3DS::CHUNK_MAT_AMBIENT); WriteColor(color); diff --git a/code/AssetLib/3DS/3DSHelper.h b/code/AssetLib/3DS/3DSHelper.h index 1930c0c40..e8efbf949 100644 --- a/code/AssetLib/3DS/3DSHelper.h +++ b/code/AssetLib/3DS/3DSHelper.h @@ -348,16 +348,16 @@ struct Texture { // empty } - Texture(Texture &&other) AI_NO_EXCEPT : mTextureBlend(std::move(other.mTextureBlend)), + Texture(Texture &&other) AI_NO_EXCEPT : mTextureBlend(other.mTextureBlend), mMapName(std::move(other.mMapName)), - mOffsetU(std::move(other.mOffsetU)), - mOffsetV(std::move(other.mOffsetV)), - mScaleU(std::move(other.mScaleU)), - mScaleV(std::move(other.mScaleV)), - mRotation(std::move(other.mRotation)), - mMapMode(std::move(other.mMapMode)), - bPrivate(std::move(other.bPrivate)), - iUVSrc(std::move(other.iUVSrc)) { + mOffsetU(other.mOffsetU), + mOffsetV(other.mOffsetV), + mScaleU(other.mScaleU), + mScaleV(other.mScaleV), + mRotation(other.mRotation), + mMapMode(other.mMapMode), + bPrivate(other.bPrivate), + iUVSrc(other.iUVSrc) { // empty } @@ -366,16 +366,16 @@ struct Texture { return *this; } - mTextureBlend = std::move(other.mTextureBlend); + mTextureBlend = other.mTextureBlend; mMapName = std::move(other.mMapName); - mOffsetU = std::move(other.mOffsetU); - mOffsetV = std::move(other.mOffsetV); - mScaleU = std::move(other.mScaleU); - mScaleV = std::move(other.mScaleV); - mRotation = std::move(other.mRotation); - mMapMode = std::move(other.mMapMode); - bPrivate = std::move(other.bPrivate); - iUVSrc = std::move(other.iUVSrc); + mOffsetU = other.mOffsetU; + mOffsetV = other.mOffsetV; + mScaleU = other.mScaleU; + mScaleV = other.mScaleV; + mRotation = other.mRotation; + mMapMode = other.mMapMode; + bPrivate = other.bPrivate; + iUVSrc = other.iUVSrc; return *this; } @@ -461,13 +461,13 @@ struct Material { //! Move constructor. This is explicitly written because MSVC doesn't support defaulting it Material(Material &&other) AI_NO_EXCEPT : mName(std::move(other.mName)), - mDiffuse(std::move(other.mDiffuse)), - mSpecularExponent(std::move(other.mSpecularExponent)), - mShininessStrength(std::move(other.mShininessStrength)), - mSpecular(std::move(other.mSpecular)), - mAmbient(std::move(other.mAmbient)), - mShading(std::move(other.mShading)), - mTransparency(std::move(other.mTransparency)), + mDiffuse(other.mDiffuse), + mSpecularExponent(other.mSpecularExponent), + mShininessStrength(other.mShininessStrength), + mSpecular(other.mSpecular), + mAmbient(other.mAmbient), + mShading(other.mShading), + mTransparency(other.mTransparency), sTexDiffuse(std::move(other.sTexDiffuse)), sTexOpacity(std::move(other.sTexOpacity)), sTexSpecular(std::move(other.sTexSpecular)), @@ -475,10 +475,10 @@ struct Material { sTexBump(std::move(other.sTexBump)), sTexEmissive(std::move(other.sTexEmissive)), sTexShininess(std::move(other.sTexShininess)), - mBumpHeight(std::move(other.mBumpHeight)), - mEmissive(std::move(other.mEmissive)), + mBumpHeight(other.mBumpHeight), + mEmissive(other.mEmissive), sTexAmbient(std::move(other.sTexAmbient)), - mTwoSided(std::move(other.mTwoSided)) { + mTwoSided(other.mTwoSided) { // empty } @@ -488,13 +488,13 @@ struct Material { } mName = std::move(other.mName); - mDiffuse = std::move(other.mDiffuse); - mSpecularExponent = std::move(other.mSpecularExponent); - mShininessStrength = std::move(other.mShininessStrength), - mSpecular = std::move(other.mSpecular); - mAmbient = std::move(other.mAmbient); - mShading = std::move(other.mShading); - mTransparency = std::move(other.mTransparency); + mDiffuse = other.mDiffuse; + mSpecularExponent = other.mSpecularExponent; + mShininessStrength = other.mShininessStrength, + mSpecular = other.mSpecular; + mAmbient = other.mAmbient; + mShading = other.mShading; + mTransparency = other.mTransparency; sTexDiffuse = std::move(other.sTexDiffuse); sTexOpacity = std::move(other.sTexOpacity); sTexSpecular = std::move(other.sTexSpecular); @@ -502,10 +502,10 @@ struct Material { sTexBump = std::move(other.sTexBump); sTexEmissive = std::move(other.sTexEmissive); sTexShininess = std::move(other.sTexShininess); - mBumpHeight = std::move(other.mBumpHeight); - mEmissive = std::move(other.mEmissive); + mBumpHeight = other.mBumpHeight; + mEmissive = other.mEmissive; sTexAmbient = std::move(other.sTexAmbient); - mTwoSided = std::move(other.mTwoSided); + mTwoSided = other.mTwoSided; return *this; } diff --git a/code/AssetLib/3DS/3DSLoader.h b/code/AssetLib/3DS/3DSLoader.h index 2091fbeb7..04dcac237 100644 --- a/code/AssetLib/3DS/3DSLoader.h +++ b/code/AssetLib/3DS/3DSLoader.h @@ -208,6 +208,15 @@ protected: */ void ReplaceDefaultMaterial(); + bool ContainsTextures(unsigned int i) const { + return !mScene->mMaterials[i].sTexDiffuse.mMapName.empty() || + !mScene->mMaterials[i].sTexBump.mMapName.empty() || + !mScene->mMaterials[i].sTexOpacity.mMapName.empty() || + !mScene->mMaterials[i].sTexEmissive.mMapName.empty() || + !mScene->mMaterials[i].sTexSpecular.mMapName.empty() || + !mScene->mMaterials[i].sTexShininess.mMapName.empty() ; + } + // ------------------------------------------------------------------- /** Convert the whole scene */ diff --git a/code/AssetLib/3MF/3MFTypes.h b/code/AssetLib/3MF/3MFTypes.h new file mode 100644 index 000000000..c4e9e4243 --- /dev/null +++ b/code/AssetLib/3MF/3MFTypes.h @@ -0,0 +1,165 @@ +/* +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. + +---------------------------------------------------------------------- +*/ +#pragma once + +#include +#include +#include +#include +#include + +struct aiMaterial; +struct aiMesh; + +namespace Assimp { +namespace D3MF { + +enum class ResourceType { + RT_Object, + RT_BaseMaterials, + RT_EmbeddedTexture2D, + RT_Texture2DGroup, + RT_Unknown +}; // To be extended with other resource types (eg. material extension resources like Texture2d, Texture2dGroup...) + +class Resource { +public: + int mId; + + Resource(int id) : + mId(id) { + // empty + } + + virtual ~Resource() { + // empty + } + + virtual ResourceType getType() const { + return ResourceType::RT_Unknown; + } +}; + +class EmbeddedTexture : public Resource { +public: + std::string mPath; + std::string mContentType; + std::string mTilestyleU; + std::string mTilestyleV; + std::vector mBuffer; + + EmbeddedTexture(int id) : + Resource(id), + mPath(), + mContentType(), + mTilestyleU(), + mTilestyleV() { + // empty + } + + ~EmbeddedTexture() = default; + + ResourceType getType() const override { + return ResourceType::RT_EmbeddedTexture2D; + } +}; + +class Texture2DGroup : public Resource { +public: + std::vector mTex2dCoords; + int mTexId; + Texture2DGroup(int id) : + Resource(id), + mTexId(-1) { + // empty + } + + ~Texture2DGroup() = default; + + ResourceType getType() const override { + return ResourceType::RT_Texture2DGroup; + } +}; + +class BaseMaterials : public Resource { +public: + std::vector mMaterialIndex; + + BaseMaterials(int id) : + Resource(id), + mMaterialIndex() { + // empty + } + + ~BaseMaterials() = default; + + ResourceType getType() const override { + return ResourceType::RT_BaseMaterials; + } +}; + +struct Component { + int mObjectId; + aiMatrix4x4 mTransformation; +}; + +class Object : public Resource { +public: + std::vector mMeshes; + std::vector mMeshIndex; + std::vector mComponents; + std::string mName; + + Object(int id) : + Resource(id), + mName(std::string("Object_") + ai_to_string(id)) { + // empty + } + + ~Object() = default; + + ResourceType getType() const override { + return ResourceType::RT_Object; + } +}; + +} // namespace D3MF +} // namespace Assimp diff --git a/code/AssetLib/3MF/3MFXmlTags.h b/code/AssetLib/3MF/3MFXmlTags.h index d447556d6..a6e9758c1 100644 --- a/code/AssetLib/3MF/3MFXmlTags.h +++ b/code/AssetLib/3MF/3MFXmlTags.h @@ -80,13 +80,21 @@ namespace XmlTag { const char* const item = "item"; const char* const objectid = "objectid"; const char* const transform = "transform"; + const char *const path = "path"; // Material definitions const char* const basematerials = "basematerials"; - const char* const basematerials_id = "id"; const char* const basematerials_base = "base"; const char* const basematerials_name = "name"; const char* const basematerials_displaycolor = "displaycolor"; + const char* const texture_2d = "m:texture2d"; + const char *const texture_group = "m:texture2dgroup"; + const char *const texture_content_type = "contenttype"; + const char *const texture_tilestyleu = "tilestyleu"; + const char *const texture_tilestylev = "tilestylev"; + const char *const texture_2d_coord = "m:tex2coord"; + const char *const texture_cuurd_u = "u"; + const char *const texture_cuurd_v = "v"; // Meta info tags const char* const CONTENT_TYPES_ARCHIVE = "[Content_Types].xml"; @@ -103,7 +111,7 @@ namespace XmlTag { const char* const PACKAGE_TEXTURE_RELATIONSHIP_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dtexture"; const char* const PACKAGE_CORE_PROPERTIES_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties"; const char* const PACKAGE_THUMBNAIL_RELATIONSHIP_TYPE = "http://schemas.openxmlformats.org/package/2006/relationships/metadata/thumbnail"; - } +} } // Namespace D3MF } // Namespace Assimp diff --git a/code/AssetLib/3MF/D3MFImporter.cpp b/code/AssetLib/3MF/D3MFImporter.cpp index 747af7cfc..58dde9738 100644 --- a/code/AssetLib/3MF/D3MFImporter.cpp +++ b/code/AssetLib/3MF/D3MFImporter.cpp @@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "D3MFImporter.h" #include "3MFXmlTags.h" #include "D3MFOpcPackage.h" +#include "XmlSerializer.h" #include #include @@ -61,513 +62,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include +#include namespace Assimp { -namespace D3MF { - -enum class ResourceType { - RT_Object, - RT_BaseMaterials, - RT_Unknown -}; // To be extended with other resource types (eg. material extension resources like Texture2d, Texture2dGroup...) - -class Resource { -public: - int mId; - - Resource(int id) : - mId(id) { - // empty - } - - virtual ~Resource() { - // empty - } - - virtual ResourceType getType() const { - return ResourceType::RT_Unknown; - } -}; - -class BaseMaterials : public Resource { -public: - std::vector mMaterials; - std::vector mMaterialIndex; - - BaseMaterials(int id) : - Resource(id), - mMaterials(), - mMaterialIndex() { - // empty - } - - ~BaseMaterials() = default; - - ResourceType getType() const override { - return ResourceType::RT_BaseMaterials; - } -}; - -struct Component { - int mObjectId; - aiMatrix4x4 mTransformation; -}; - -class Object : public Resource { -public: - std::vector mMeshes; - std::vector mMeshIndex; - std::vector mComponents; - std::string mName; - - Object(int id) : - Resource(id), - mName(std::string("Object_") + ai_to_string(id)) { - // empty - } - - ~Object() = default; - - ResourceType getType() const override { - return ResourceType::RT_Object; - } -}; - -class XmlSerializer { -public: - XmlSerializer(XmlParser *xmlParser) : - mResourcesDictionnary(), - mMaterialCount(0), - mMeshCount(0), - mXmlParser(xmlParser) { - // empty - } - - ~XmlSerializer() { - for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); ++it ) { - delete it->second; - } - } - - void ImportXml(aiScene *scene) { - if (nullptr == scene) { - return; - } - - scene->mRootNode = new aiNode(XmlTag::RootTag); - - XmlNode node = mXmlParser->getRootNode().child(XmlTag::model); - if (node.empty()) { - return; - } - XmlNode resNode = node.child(XmlTag::resources); - for (auto ¤tNode : resNode.children()) { - const std::string currentNodeName = currentNode.name(); - if (currentNodeName == XmlTag::object) { - ReadObject(currentNode); - } else if (currentNodeName == XmlTag::basematerials) { - ReadBaseMaterials(currentNode); - } else if (currentNodeName == XmlTag::meta) { - ReadMetadata(currentNode); - } - } - - XmlNode buildNode = node.child(XmlTag::build); - for (auto ¤tNode : buildNode.children()) { - const std::string currentNodeName = currentNode.name(); - if (currentNodeName == XmlTag::item) { - int objectId = -1; - std::string transformationMatrixStr; - aiMatrix4x4 transformationMatrix; - getNodeAttribute(currentNode, D3MF::XmlTag::objectid, objectId); - bool hasTransform = getNodeAttribute(currentNode, D3MF::XmlTag::transform, transformationMatrixStr); - - auto it = mResourcesDictionnary.find(objectId); - if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) { - Object *obj = static_cast(it->second); - if (hasTransform) { - transformationMatrix = parseTransformMatrix(transformationMatrixStr); - } - - addObjectToNode(scene->mRootNode, obj, transformationMatrix); - } - } - } - - // import the metadata - if (!mMetaData.empty()) { - const size_t numMeta = mMetaData.size(); - scene->mMetaData = aiMetadata::Alloc(static_cast(numMeta)); - for (size_t i = 0; i < numMeta; ++i) { - aiString val(mMetaData[i].value); - scene->mMetaData->Set(static_cast(i), mMetaData[i].name, val); - } - } - - // import the meshes - scene->mNumMeshes = static_cast(mMeshCount); - if (scene->mNumMeshes != 0) { - scene->mMeshes = new aiMesh *[scene->mNumMeshes](); - for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); ++it) { - if (it->second->getType() == ResourceType::RT_Object) { - Object *obj = static_cast(it->second); - ai_assert(nullptr != obj); - for (unsigned int i = 0; i < obj->mMeshes.size(); ++i) { - scene->mMeshes[obj->mMeshIndex[i]] = obj->mMeshes[i]; - } - } - } - } - - // import the materials - scene->mNumMaterials = mMaterialCount; - if (scene->mNumMaterials != 0) { - scene->mMaterials = new aiMaterial *[scene->mNumMaterials]; - for (auto it = mResourcesDictionnary.begin(); it != mResourcesDictionnary.end(); ++it) { - if (it->second->getType() == ResourceType::RT_BaseMaterials) { - BaseMaterials *baseMaterials = static_cast(it->second); - for (unsigned int i = 0; i < baseMaterials->mMaterials.size(); ++i) { - scene->mMaterials[baseMaterials->mMaterialIndex[i]] = baseMaterials->mMaterials[i]; - } - } - } - } - } - -private: - void addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform) { - ai_assert(nullptr != obj); - - aiNode *sceneNode = new aiNode(obj->mName); - sceneNode->mNumMeshes = static_cast(obj->mMeshes.size()); - sceneNode->mMeshes = new unsigned int[sceneNode->mNumMeshes]; - std::copy(obj->mMeshIndex.begin(), obj->mMeshIndex.end(), sceneNode->mMeshes); - - sceneNode->mTransformation = nodeTransform; - if (nullptr != parent) { - parent->addChildren(1, &sceneNode); - } - - for (size_t i = 0; i < obj->mComponents.size(); ++i) { - Component c = obj->mComponents[i]; - auto it = mResourcesDictionnary.find(c.mObjectId); - if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) { - addObjectToNode(sceneNode, static_cast(it->second), c.mTransformation); - } - } - } - - bool getNodeAttribute(const XmlNode &node, const std::string &attribute, std::string &value) { - pugi::xml_attribute objectAttribute = node.attribute(attribute.c_str()); - if (!objectAttribute.empty()) { - value = objectAttribute.as_string(); - return true; - } - - return false; - } - - bool getNodeAttribute(const XmlNode &node, const std::string &attribute, int &value) { - std::string strValue; - bool ret = getNodeAttribute(node, attribute, strValue); - if (ret) { - value = std::atoi(strValue.c_str()); - return true; - } - - return false; - } - - aiMatrix4x4 parseTransformMatrix(std::string matrixStr) { - // split the string - std::vector numbers; - std::string currentNumber; - for (size_t i = 0; i < matrixStr.size(); ++i) { - const char c = matrixStr[i]; - if (c == ' ') { - if (currentNumber.size() > 0) { - float f = std::stof(currentNumber); - numbers.push_back(f); - currentNumber.clear(); - } - } else { - currentNumber.push_back(c); - } - } - if (currentNumber.size() > 0) { - const float f = std::stof(currentNumber); - numbers.push_back(f); - } - - aiMatrix4x4 transformMatrix; - transformMatrix.a1 = numbers[0]; - transformMatrix.b1 = numbers[1]; - transformMatrix.c1 = numbers[2]; - transformMatrix.d1 = 0; - - transformMatrix.a2 = numbers[3]; - transformMatrix.b2 = numbers[4]; - transformMatrix.c2 = numbers[5]; - transformMatrix.d2 = 0; - - transformMatrix.a3 = numbers[6]; - transformMatrix.b3 = numbers[7]; - transformMatrix.c3 = numbers[8]; - transformMatrix.d3 = 0; - - transformMatrix.a4 = numbers[9]; - transformMatrix.b4 = numbers[10]; - transformMatrix.c4 = numbers[11]; - transformMatrix.d4 = 1; - - return transformMatrix; - } - - void ReadObject(XmlNode &node) { - int id = -1, pid = -1, pindex = -1; - bool hasId = getNodeAttribute(node, XmlTag::id, id); - bool hasPid = getNodeAttribute(node, XmlTag::pid, pid); - bool hasPindex = getNodeAttribute(node, XmlTag::pindex, pindex); - if (!hasId) { - return; - } - - Object *obj = new Object(id); - - for (XmlNode ¤tNode : node.children()) { - const std::string ¤tName = currentNode.name(); - if (currentName == D3MF::XmlTag::mesh) { - auto mesh = ReadMesh(currentNode); - mesh->mName.Set(ai_to_string(id)); - - if (hasPid) { - auto it = mResourcesDictionnary.find(pid); - if (hasPindex && it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_BaseMaterials) { - BaseMaterials *materials = static_cast(it->second); - mesh->mMaterialIndex = materials->mMaterialIndex[pindex]; - } - } - - obj->mMeshes.push_back(mesh); - obj->mMeshIndex.push_back(mMeshCount); - mMeshCount++; - } else if (currentName == D3MF::XmlTag::components) { - for (XmlNode ¤tSubNode : currentNode.children()) { - const std::string subNodeName = currentSubNode.name(); - if (subNodeName == D3MF::XmlTag::component) { - int objectId = -1; - std::string componentTransformStr; - aiMatrix4x4 componentTransform; - if (getNodeAttribute(currentSubNode, D3MF::XmlTag::transform, componentTransformStr)) { - componentTransform = parseTransformMatrix(componentTransformStr); - } - - if (getNodeAttribute(currentSubNode, D3MF::XmlTag::objectid, objectId)) { - obj->mComponents.push_back({ objectId, componentTransform }); - } - } - } - } - } - - mResourcesDictionnary.insert(std::make_pair(id, obj)); - } - - aiMesh *ReadMesh(XmlNode &node) { - aiMesh *mesh = new aiMesh(); - - for (XmlNode ¤tNode : node.children()) { - const std::string currentName = currentNode.name(); - if (currentName == XmlTag::vertices) { - ImportVertices(currentNode, mesh); - } else if (currentName == XmlTag::triangles) { - ImportTriangles(currentNode, mesh); - } - } - - return mesh; - } - - void ReadMetadata(XmlNode &node) { - pugi::xml_attribute attribute = node.attribute(D3MF::XmlTag::meta_name); - const std::string name = attribute.as_string(); - const std::string value = node.value(); - if (name.empty()) { - return; - } - - MetaEntry entry; - entry.name = name; - entry.value = value; - mMetaData.push_back(entry); - } - - void ImportVertices(XmlNode &node, aiMesh *mesh) { - std::vector vertices; - for (XmlNode ¤tNode : node.children()) { - const std::string currentName = currentNode.name(); - if (currentName == XmlTag::vertex) { - vertices.push_back(ReadVertex(currentNode)); - } - } - - mesh->mNumVertices = static_cast(vertices.size()); - mesh->mVertices = new aiVector3D[mesh->mNumVertices]; - std::copy(vertices.begin(), vertices.end(), mesh->mVertices); - } - - aiVector3D ReadVertex(XmlNode &node) { - aiVector3D vertex; - vertex.x = ai_strtof(node.attribute(XmlTag::x).as_string(), nullptr); - vertex.y = ai_strtof(node.attribute(XmlTag::y).as_string(), nullptr); - vertex.z = ai_strtof(node.attribute(XmlTag::z).as_string(), nullptr); - - return vertex; - } - - void ImportTriangles(XmlNode &node, aiMesh *mesh) { - std::vector faces; - for (XmlNode ¤tNode : node.children()) { - const std::string currentName = currentNode.name(); - if (currentName == XmlTag::triangle) { - aiFace face = ReadTriangle(currentNode); - faces.push_back(face); - - int pid = 0, p1 = 0; - bool hasPid = getNodeAttribute(currentNode, D3MF::XmlTag::pid, pid); - bool hasP1 = getNodeAttribute(currentNode, D3MF::XmlTag::p1, p1); - - if (hasPid && hasP1) { - auto it = mResourcesDictionnary.find(pid); - if (it != mResourcesDictionnary.end()) { - if (it->second->getType() == ResourceType::RT_BaseMaterials) { - BaseMaterials *baseMaterials = static_cast(it->second); - mesh->mMaterialIndex = baseMaterials->mMaterialIndex[p1]; - } - // TODO: manage the separation into several meshes if the triangles of the mesh do not all refer to the same material - } - } - } - } - - mesh->mNumFaces = static_cast(faces.size()); - mesh->mFaces = new aiFace[mesh->mNumFaces]; - mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; - - std::copy(faces.begin(), faces.end(), mesh->mFaces); - } - - aiFace ReadTriangle(XmlNode &node) { - aiFace face; - - face.mNumIndices = 3; - face.mIndices = new unsigned int[face.mNumIndices]; - face.mIndices[0] = static_cast(std::atoi(node.attribute(XmlTag::v1).as_string())); - face.mIndices[1] = static_cast(std::atoi(node.attribute(XmlTag::v2).as_string())); - face.mIndices[2] = static_cast(std::atoi(node.attribute(XmlTag::v3).as_string())); - - return face; - } - - void ReadBaseMaterials(XmlNode &node) { - int id = -1; - if (getNodeAttribute(node, D3MF::XmlTag::basematerials_id, id)) { - BaseMaterials *baseMaterials = new BaseMaterials(id); - - for (XmlNode ¤tNode : node.children()) { - const std::string currentName = currentNode.name(); - if (currentName == XmlTag::basematerials_base) { - baseMaterials->mMaterialIndex.push_back(mMaterialCount); - baseMaterials->mMaterials.push_back(readMaterialDef(currentNode, id)); - ++mMaterialCount; - } - } - - mResourcesDictionnary.insert(std::make_pair(id, baseMaterials)); - } - } - - bool parseColor(const char *color, aiColor4D &diffuse) { - if (nullptr == color) { - return false; - } - - //format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1) - const size_t len = strlen(color); - if (9 != len && 7 != len) { - return false; - } - - const char *buf(color); - if ('#' != buf[0]) { - return false; - } - - char r[3] = { buf[1], buf[2], '\0' }; - diffuse.r = static_cast(strtol(r, nullptr, 16)) / ai_real(255.0); - - char g[3] = { buf[3], buf[4], '\0' }; - diffuse.g = static_cast(strtol(g, nullptr, 16)) / ai_real(255.0); - - char b[3] = { buf[5], buf[6], '\0' }; - diffuse.b = static_cast(strtol(b, nullptr, 16)) / ai_real(255.0); - - if (7 == len) - return true; - - char a[3] = { buf[7], buf[8], '\0' }; - diffuse.a = static_cast(strtol(a, nullptr, 16)) / ai_real(255.0); - - return true; - } - - void assignDiffuseColor(XmlNode &node, aiMaterial *mat) { - const char *color = node.attribute(XmlTag::basematerials_displaycolor).as_string(); - aiColor4D diffuse; - if (parseColor(color, diffuse)) { - mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE); - } - } - - aiMaterial *readMaterialDef(XmlNode &node, unsigned int basematerialsId) { - aiMaterial *material = new aiMaterial(); - material->mNumProperties = 0; - std::string name; - bool hasName = getNodeAttribute(node, D3MF::XmlTag::basematerials_name, name); - - std::string stdMaterialName; - const std::string strId(ai_to_string(basematerialsId)); - stdMaterialName += "id"; - stdMaterialName += strId; - stdMaterialName += "_"; - if (hasName) { - stdMaterialName += std::string(name); - } else { - stdMaterialName += "basemat_"; - stdMaterialName += ai_to_string(mMaterialCount - basematerialsId); - } - - aiString assimpMaterialName(stdMaterialName); - material->AddProperty(&assimpMaterialName, AI_MATKEY_NAME); - - assignDiffuseColor(node, material); - - return material; - } - -private: - struct MetaEntry { - std::string name; - std::string value; - }; - std::vector mMetaData; - std::map mResourcesDictionnary; - unsigned int mMaterialCount, mMeshCount; - XmlParser *mXmlParser; -}; - -} //namespace D3MF using namespace D3MF; @@ -597,7 +94,9 @@ bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bo const std::string extension(GetExtension(filename)); if (extension == desc.mFileExtensions) { return true; - } else if (!extension.length() || checkSig) { + } + + if (!extension.length() || checkSig) { if (nullptr == pIOHandler) { return false; } @@ -611,7 +110,7 @@ bool D3MFImporter::CanRead(const std::string &filename, IOSystem *pIOHandler, bo return false; } -void D3MFImporter::SetupProperties(const Importer * /*pImp*/) { +void D3MFImporter::SetupProperties(const Importer*) { // empty } @@ -626,6 +125,15 @@ void D3MFImporter::InternReadFile(const std::string &filename, aiScene *pScene, if (xmlParser.parse(opcPackage.RootStream())) { XmlSerializer xmlSerializer(&xmlParser); xmlSerializer.ImportXml(pScene); + + const std::vector &tex = opcPackage.GetEmbeddedTextures(); + if (!tex.empty()) { + pScene->mNumTextures = static_cast(tex.size()); + pScene->mTextures = new aiTexture *[pScene->mNumTextures]; + for (unsigned int i = 0; i < pScene->mNumTextures; ++i) { + pScene->mTextures[i] = tex[i]; + } + } } } diff --git a/code/AssetLib/3MF/D3MFImporter.h b/code/AssetLib/3MF/D3MFImporter.h index 811c463b6..2b37010d9 100644 --- a/code/AssetLib/3MF/D3MFImporter.h +++ b/code/AssetLib/3MF/D3MFImporter.h @@ -4,7 +4,6 @@ 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, @@ -47,17 +46,40 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { +// --------------------------------------------------------------------------- /// @brief The 3MF-importer class. +/// +/// Implements the basic topology import and embedded textures. +// --------------------------------------------------------------------------- class D3MFImporter : public BaseImporter { public: + /// @brief The default class constructor. D3MFImporter(); - ~D3MFImporter(); - bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const; - void SetupProperties(const Importer *pImp); - const aiImporterDesc *GetInfo() const; + + /// @brief The class destructor. + ~D3MFImporter() override; + + /// @brief Performs the data format detection. + /// @param pFile The filename to check. + /// @param pIOHandler The used IO-System. + /// @param checkSig true for signature checking. + /// @return true for can be loaded, false for not. + bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const override; + + /// @brief Not used + /// @param pImp Not used + void SetupProperties(const Importer *pImp) override; + + /// @brief The importer description getter. + /// @return The info + const aiImporterDesc *GetInfo() const override; protected: - void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler); + /// @brief Internal read function, performs the file parsing. + /// @param pFile The filename + /// @param pScene The scene to load in. + /// @param pIOHandler The io-system + void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override; }; } // Namespace Assimp diff --git a/code/AssetLib/3MF/D3MFOpcPackage.cpp b/code/AssetLib/3MF/D3MFOpcPackage.cpp index dbf4f2e10..c29cec368 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.cpp +++ b/code/AssetLib/3MF/D3MFOpcPackage.cpp @@ -43,14 +43,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "D3MFOpcPackage.h" #include - #include #include #include #include #include #include - +#include #include "3MFXmlTags.h" #include #include @@ -64,11 +63,12 @@ namespace Assimp { namespace D3MF { // ------------------------------------------------------------------------------------------------ -typedef std::shared_ptr OpcPackageRelationshipPtr; +using OpcPackageRelationshipPtr = std::shared_ptr; class OpcPackageRelationshipReader { public: - OpcPackageRelationshipReader(XmlParser &parser) { + OpcPackageRelationshipReader(XmlParser &parser) : + m_relationShips() { XmlNode root = parser.getRootNode(); ParseRootNode(root); } @@ -91,6 +91,7 @@ public: if (relPtr->id.empty() || relPtr->type.empty() || relPtr->target.empty()) { return false; } + return true; } @@ -100,7 +101,7 @@ public: } for (XmlNode currentNode = node.first_child(); currentNode; currentNode = currentNode.next_sibling()) { - std::string name = currentNode.name(); + const std::string name = currentNode.name(); if (name == "Relationship") { OpcPackageRelationshipPtr relPtr(new OpcPackageRelationship()); relPtr->id = currentNode.attribute(XmlTag::RELS_ATTRIB_ID).as_string(); @@ -116,11 +117,23 @@ public: std::vector m_relationShips; }; +static bool IsEmbeddedTexture( const std::string &filename ) { + const std::string extension = BaseImporter::GetExtension(filename); + if (extension == "jpg" || extension == "png") { + std::string::size_type pos = filename.find("thumbnail"); + if (pos == std::string::npos) { + return false; + } + return true; + } + + return false; +} // ------------------------------------------------------------------------------------------------ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : mRootStream(nullptr), mZipArchive() { - mZipArchive.reset(new ZipArchiveIOSystem(pIOHandler, rFile)); + mZipArchive = new ZipArchiveIOSystem(pIOHandler, rFile); if (!mZipArchive->isOpen()) { throw DeadlyImportError("Failed to open file ", rFile, "."); } @@ -141,13 +154,13 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : } std::string rootFile = ReadPackageRootRelationship(fileStream); - if (rootFile.size() > 0 && rootFile[0] == '/') { + if (!rootFile.empty() && rootFile[0] == '/') { rootFile = rootFile.substr(1); if (rootFile[0] == '/') { // deal with zip-bug rootFile = rootFile.substr(1); } - } + } ASSIMP_LOG_VERBOSE_DEBUG(rootFile); @@ -158,9 +171,12 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : if (nullptr == mRootStream) { throw DeadlyImportError("Cannot open root-file in archive : " + rootFile); } - } else if (file == D3MF::XmlTag::CONTENT_TYPES_ARCHIVE) { ASSIMP_LOG_WARN("Ignored file of unsupported type CONTENT_TYPES_ARCHIVES", file); + } else if (IsEmbeddedTexture(file)) { + IOStream *fileStream = mZipArchive->Open(file.c_str()); + LoadEmbeddedTextures(fileStream, file); + mZipArchive->Close(fileStream); } else { ASSIMP_LOG_WARN("Ignored file of unknown type: ", file); } @@ -169,20 +185,26 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : D3MFOpcPackage::~D3MFOpcPackage() { mZipArchive->Close(mRootStream); + delete mZipArchive; + mZipArchive = nullptr; } IOStream *D3MFOpcPackage::RootStream() const { return mRootStream; } -static const std::string ModelRef = "3D/3dmodel.model"; +const std::vector &D3MFOpcPackage::GetEmbeddedTextures() const { + return mEmbeddedTextures; +} + +static const char *const ModelRef = "3D/3dmodel.model"; bool D3MFOpcPackage::validate() { if (nullptr == mRootStream || nullptr == mZipArchive) { return false; } - return mZipArchive->Exists(ModelRef.c_str()); + return mZipArchive->Exists(ModelRef); } std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream *stream) { @@ -204,6 +226,31 @@ std::string D3MFOpcPackage::ReadPackageRootRelationship(IOStream *stream) { return (*itr)->target; } +void D3MFOpcPackage::LoadEmbeddedTextures(IOStream *fileStream, const std::string &filename) { + if (nullptr == fileStream) { + return; + } + + const size_t size = fileStream->FileSize(); + if (0 == size) { + return; + } + + unsigned char *data = new unsigned char[size]; + fileStream->Read(data, 1, size); + aiTexture *texture = new aiTexture; + std::string embName = "*" + filename; + texture->mFilename.Set(embName.c_str()); + texture->mWidth = static_cast(size); + texture->mHeight = 0; + texture->achFormatHint[0] = 'p'; + texture->achFormatHint[1] = 'n'; + texture->achFormatHint[2] = 'g'; + texture->achFormatHint[3] = '\0'; + texture->pcData = (aiTexel*) data; + mEmbeddedTextures.emplace_back(texture); +} + } // Namespace D3MF } // Namespace Assimp diff --git a/code/AssetLib/3MF/D3MFOpcPackage.h b/code/AssetLib/3MF/D3MFOpcPackage.h index 22b4510d0..fda74a879 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.h +++ b/code/AssetLib/3MF/D3MFOpcPackage.h @@ -46,8 +46,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +struct aiTexture; + namespace Assimp { - class ZipArchiveIOSystem; + +class ZipArchiveIOSystem; namespace D3MF { @@ -63,16 +66,19 @@ public: ~D3MFOpcPackage(); IOStream* RootStream() const; bool validate(); + const std::vector &GetEmbeddedTextures() const; protected: std::string ReadPackageRootRelationship(IOStream* stream); + void LoadEmbeddedTextures(IOStream *fileStream, const std::string &filename); private: IOStream* mRootStream; - std::unique_ptr mZipArchive; + ZipArchiveIOSystem *mZipArchive; + std::vector mEmbeddedTextures; }; -} // Namespace D3MF -} // Namespace Assimp +} // namespace D3MF +} // namespace Assimp #endif // D3MFOPCPACKAGE_H diff --git a/code/AssetLib/3MF/XmlSerializer.cpp b/code/AssetLib/3MF/XmlSerializer.cpp new file mode 100644 index 000000000..7a33d08ed --- /dev/null +++ b/code/AssetLib/3MF/XmlSerializer.cpp @@ -0,0 +1,594 @@ +/* +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. + +---------------------------------------------------------------------- +*/ +#include "XmlSerializer.h" +#include "D3MFOpcPackage.h" +#include "3MFXmlTags.h" +#include "3MFTypes.h" +#include + +namespace Assimp { +namespace D3MF { + +static const int IdNotSet = -1; + +namespace { + +static const size_t ColRGBA_Len = 9; +static const size_t ColRGB_Len = 7; + +// format of the color string: #RRGGBBAA or #RRGGBB (3MF Core chapter 5.1.1) +bool validateColorString(const char *color) { + const size_t len = strlen(color); + if (ColRGBA_Len != len && ColRGB_Len != len) { + return false; + } + + return true; +} + +aiFace ReadTriangle(XmlNode &node) { + aiFace face; + + face.mNumIndices = 3; + face.mIndices = new unsigned int[face.mNumIndices]; + face.mIndices[0] = static_cast(std::atoi(node.attribute(XmlTag::v1).as_string())); + face.mIndices[1] = static_cast(std::atoi(node.attribute(XmlTag::v2).as_string())); + face.mIndices[2] = static_cast(std::atoi(node.attribute(XmlTag::v3).as_string())); + + return face; +} + +aiVector3D ReadVertex(XmlNode &node) { + aiVector3D vertex; + vertex.x = ai_strtof(node.attribute(XmlTag::x).as_string(), nullptr); + vertex.y = ai_strtof(node.attribute(XmlTag::y).as_string(), nullptr); + vertex.z = ai_strtof(node.attribute(XmlTag::z).as_string(), nullptr); + + return vertex; +} + +bool getNodeAttribute(const XmlNode &node, const std::string &attribute, std::string &value) { + pugi::xml_attribute objectAttribute = node.attribute(attribute.c_str()); + if (!objectAttribute.empty()) { + value = objectAttribute.as_string(); + return true; + } + + return false; +} + +bool getNodeAttribute(const XmlNode &node, const std::string &attribute, int &value) { + std::string strValue; + const bool ret = getNodeAttribute(node, attribute, strValue); + if (ret) { + value = std::atoi(strValue.c_str()); + return true; + } + + return false; +} + +aiMatrix4x4 parseTransformMatrix(std::string matrixStr) { + // split the string + std::vector numbers; + std::string currentNumber; + for (char c : matrixStr) { + if (c == ' ') { + if (!currentNumber.empty()) { + float f = std::stof(currentNumber); + numbers.push_back(f); + currentNumber.clear(); + } + } else { + currentNumber.push_back(c); + } + } + if (!currentNumber.empty()) { + const float f = std::stof(currentNumber); + numbers.push_back(f); + } + + aiMatrix4x4 transformMatrix; + transformMatrix.a1 = numbers[0]; + transformMatrix.b1 = numbers[1]; + transformMatrix.c1 = numbers[2]; + transformMatrix.d1 = 0; + + transformMatrix.a2 = numbers[3]; + transformMatrix.b2 = numbers[4]; + transformMatrix.c2 = numbers[5]; + transformMatrix.d2 = 0; + + transformMatrix.a3 = numbers[6]; + transformMatrix.b3 = numbers[7]; + transformMatrix.c3 = numbers[8]; + transformMatrix.d3 = 0; + + transformMatrix.a4 = numbers[9]; + transformMatrix.b4 = numbers[10]; + transformMatrix.c4 = numbers[11]; + transformMatrix.d4 = 1; + + return transformMatrix; +} + +bool parseColor(const char *color, aiColor4D &diffuse) { + if (nullptr == color) { + return false; + } + + if (!validateColorString(color)) { + return false; + } + + //const char *buf(color); + if ('#' != color[0]) { + return false; + } + + char r[3] = { color[1], color[2], '\0' }; + diffuse.r = static_cast(strtol(r, nullptr, 16)) / ai_real(255.0); + + char g[3] = { color[3], color[4], '\0' }; + diffuse.g = static_cast(strtol(g, nullptr, 16)) / ai_real(255.0); + + char b[3] = { color[5], color[6], '\0' }; + diffuse.b = static_cast(strtol(b, nullptr, 16)) / ai_real(255.0); + const size_t len = strlen(color); + if (ColRGB_Len == len) { + return true; + } + + char a[3] = { color[7], color[8], '\0' }; + diffuse.a = static_cast(strtol(a, nullptr, 16)) / ai_real(255.0); + + return true; +} + +void assignDiffuseColor(XmlNode &node, aiMaterial *mat) { + const char *color = node.attribute(XmlTag::basematerials_displaycolor).as_string(); + aiColor4D diffuse; + if (parseColor(color, diffuse)) { + mat->AddProperty(&diffuse, 1, AI_MATKEY_COLOR_DIFFUSE); + } +} + +} // namespace + +XmlSerializer::XmlSerializer(XmlParser *xmlParser) : + mResourcesDictionnary(), + mMeshCount(0), + mXmlParser(xmlParser) { + ai_assert(nullptr != xmlParser); +} + +XmlSerializer::~XmlSerializer() { + for (auto &it : mResourcesDictionnary) { + delete it.second; + } +} + +void XmlSerializer::ImportXml(aiScene *scene) { + if (nullptr == scene) { + return; + } + + scene->mRootNode = new aiNode(XmlTag::RootTag); + XmlNode node = mXmlParser->getRootNode().child(XmlTag::model); + if (node.empty()) { + return; + } + + XmlNode resNode = node.child(XmlTag::resources); + for (auto ¤tNode : resNode.children()) { + const std::string currentNodeName = currentNode.name(); + if (currentNodeName == XmlTag::texture_2d) { + ReadEmbeddecTexture(currentNode); + } else if (currentNodeName == XmlTag::texture_group) { + ReadTextureGroup(currentNode); + } else if (currentNodeName == XmlTag::object) { + ReadObject(currentNode); + } else if (currentNodeName == XmlTag::basematerials) { + ReadBaseMaterials(currentNode); + } else if (currentNodeName == XmlTag::meta) { + ReadMetadata(currentNode); + } + } + StoreMaterialsInScene(scene); + XmlNode buildNode = node.child(XmlTag::build); + if (buildNode.empty()) { + return; + } + + for (auto ¤tNode : buildNode.children()) { + const std::string currentNodeName = currentNode.name(); + if (currentNodeName == XmlTag::item) { + int objectId = IdNotSet; + std::string transformationMatrixStr; + aiMatrix4x4 transformationMatrix; + getNodeAttribute(currentNode, D3MF::XmlTag::objectid, objectId); + bool hasTransform = getNodeAttribute(currentNode, D3MF::XmlTag::transform, transformationMatrixStr); + + auto it = mResourcesDictionnary.find(objectId); + if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) { + Object *obj = static_cast(it->second); + if (hasTransform) { + transformationMatrix = parseTransformMatrix(transformationMatrixStr); + } + + addObjectToNode(scene->mRootNode, obj, transformationMatrix); + } + } + } + + // import the metadata + if (!mMetaData.empty()) { + const size_t numMeta = mMetaData.size(); + scene->mMetaData = aiMetadata::Alloc(static_cast(numMeta)); + for (size_t i = 0; i < numMeta; ++i) { + aiString val(mMetaData[i].value); + scene->mMetaData->Set(static_cast(i), mMetaData[i].name, val); + } + } + + // import the meshes, materials are already stored + scene->mNumMeshes = static_cast(mMeshCount); + if (scene->mNumMeshes != 0) { + scene->mMeshes = new aiMesh *[scene->mNumMeshes](); + for (auto &it : mResourcesDictionnary) { + if (it.second->getType() == ResourceType::RT_Object) { + Object *obj = static_cast(it.second); + ai_assert(nullptr != obj); + for (unsigned int i = 0; i < obj->mMeshes.size(); ++i) { + scene->mMeshes[obj->mMeshIndex[i]] = obj->mMeshes[i]; + } + } + } + } +} + +void XmlSerializer::addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform) { + ai_assert(nullptr != obj); + + aiNode *sceneNode = new aiNode(obj->mName); + sceneNode->mNumMeshes = static_cast(obj->mMeshes.size()); + sceneNode->mMeshes = new unsigned int[sceneNode->mNumMeshes]; + std::copy(obj->mMeshIndex.begin(), obj->mMeshIndex.end(), sceneNode->mMeshes); + + sceneNode->mTransformation = nodeTransform; + if (nullptr != parent) { + parent->addChildren(1, &sceneNode); + } + + for (Assimp::D3MF::Component c : obj->mComponents) { + auto it = mResourcesDictionnary.find(c.mObjectId); + if (it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_Object) { + addObjectToNode(sceneNode, static_cast(it->second), c.mTransformation); + } + } +} + +void XmlSerializer::ReadObject(XmlNode &node) { + int id = IdNotSet, pid = IdNotSet, pindex = IdNotSet; + bool hasId = getNodeAttribute(node, XmlTag::id, id); + if (!hasId) { + return; + } + + bool hasPid = getNodeAttribute(node, XmlTag::pid, pid); + bool hasPindex = getNodeAttribute(node, XmlTag::pindex, pindex); + + Object *obj = new Object(id); + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == D3MF::XmlTag::mesh) { + auto mesh = ReadMesh(currentNode); + mesh->mName.Set(ai_to_string(id)); + + if (hasPid) { + auto it = mResourcesDictionnary.find(pid); + if (hasPindex && it != mResourcesDictionnary.end() && it->second->getType() == ResourceType::RT_BaseMaterials) { + BaseMaterials *materials = static_cast(it->second); + mesh->mMaterialIndex = materials->mMaterialIndex[pindex]; + } + } + + obj->mMeshes.push_back(mesh); + obj->mMeshIndex.push_back(mMeshCount); + mMeshCount++; + } else if (currentName == D3MF::XmlTag::components) { + for (XmlNode ¤tSubNode : currentNode.children()) { + const std::string subNodeName = currentSubNode.name(); + if (subNodeName == D3MF::XmlTag::component) { + int objectId = IdNotSet; + std::string componentTransformStr; + aiMatrix4x4 componentTransform; + if (getNodeAttribute(currentSubNode, D3MF::XmlTag::transform, componentTransformStr)) { + componentTransform = parseTransformMatrix(componentTransformStr); + } + + if (getNodeAttribute(currentSubNode, D3MF::XmlTag::objectid, objectId)) { + obj->mComponents.push_back({ objectId, componentTransform }); + } + } + } + } + } + + mResourcesDictionnary.insert(std::make_pair(id, obj)); +} + +aiMesh *XmlSerializer::ReadMesh(XmlNode &node) { + if (node.empty()) { + return nullptr; + } + + aiMesh *mesh = new aiMesh(); + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == XmlTag::vertices) { + ImportVertices(currentNode, mesh); + } else if (currentName == XmlTag::triangles) { + ImportTriangles(currentNode, mesh); + } + } + + return mesh; +} + +void XmlSerializer::ReadMetadata(XmlNode &node) { + pugi::xml_attribute attribute = node.attribute(D3MF::XmlTag::meta_name); + const std::string name = attribute.as_string(); + const std::string value = node.value(); + if (name.empty()) { + return; + } + + MetaEntry entry; + entry.name = name; + entry.value = value; + mMetaData.push_back(entry); +} + +void XmlSerializer::ImportVertices(XmlNode &node, aiMesh *mesh) { + ai_assert(nullptr != mesh); + + std::vector vertices; + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == XmlTag::vertex) { + vertices.push_back(ReadVertex(currentNode)); + } + } + + mesh->mNumVertices = static_cast(vertices.size()); + mesh->mVertices = new aiVector3D[mesh->mNumVertices]; + std::copy(vertices.begin(), vertices.end(), mesh->mVertices); +} + +void XmlSerializer::ImportTriangles(XmlNode &node, aiMesh *mesh) { + std::vector faces; + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == XmlTag::triangle) { + int pid = IdNotSet, p1 = IdNotSet; + bool hasPid = getNodeAttribute(currentNode, D3MF::XmlTag::pid, pid); + bool hasP1 = getNodeAttribute(currentNode, D3MF::XmlTag::p1, p1); + + if (hasPid && hasP1) { + auto it = mResourcesDictionnary.find(pid); + if (it != mResourcesDictionnary.end()) { + if (it->second->getType() == ResourceType::RT_BaseMaterials) { + BaseMaterials *baseMaterials = static_cast(it->second); + mesh->mMaterialIndex = baseMaterials->mMaterialIndex[p1]; + } else if (it->second->getType() == ResourceType::RT_Texture2DGroup) { + if (mesh->mTextureCoords[0] == nullptr) { + Texture2DGroup *group = static_cast(it->second); + const std::string name = ai_to_string(group->mTexId); + for (size_t i = 0; i < mMaterials.size(); ++i) { + if (name == mMaterials[i]->GetName().C_Str()) { + mesh->mMaterialIndex = static_cast(i); + } + } + mesh->mTextureCoords[0] = new aiVector3D[group->mTex2dCoords.size()]; + for (unsigned int i = 0; i < group->mTex2dCoords.size(); ++i) { + mesh->mTextureCoords[0][i] = aiVector3D(group->mTex2dCoords[i].x, group->mTex2dCoords[i].y, 0); + } + } + } + } + } + + aiFace face = ReadTriangle(currentNode); + faces.push_back(face); + } + } + + mesh->mNumFaces = static_cast(faces.size()); + mesh->mFaces = new aiFace[mesh->mNumFaces]; + mesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; + + std::copy(faces.begin(), faces.end(), mesh->mFaces); +} + +void XmlSerializer::ReadBaseMaterials(XmlNode &node) { + int id = IdNotSet; + if (getNodeAttribute(node, D3MF::XmlTag::id, id)) { + BaseMaterials *baseMaterials = new BaseMaterials(id); + + for (XmlNode ¤tNode : node.children()) { + const std::string currentName = currentNode.name(); + if (currentName == XmlTag::basematerials_base) { + baseMaterials->mMaterialIndex.push_back(static_cast(mMaterials.size())); + mMaterials.push_back(readMaterialDef(currentNode, id)); + } + } + + mResourcesDictionnary.insert(std::make_pair(id, baseMaterials)); + } +} + +void XmlSerializer::ReadEmbeddecTexture(XmlNode &node) { + if (node.empty()) { + return; + } + + std::string value; + EmbeddedTexture *tex2D = nullptr; + if (XmlParser::getStdStrAttribute(node, XmlTag::id, value)) { + tex2D = new EmbeddedTexture(atoi(value.c_str())); + } + if (nullptr == tex2D) { + return; + } + + if (XmlParser::getStdStrAttribute(node, XmlTag::path, value)) { + tex2D->mPath = value; + } + if (XmlParser::getStdStrAttribute(node, XmlTag::texture_content_type, value)) { + tex2D->mContentType = value; + } + if (XmlParser::getStdStrAttribute(node, XmlTag::texture_tilestyleu, value)) { + tex2D->mTilestyleU = value; + } + if (XmlParser::getStdStrAttribute(node, XmlTag::texture_tilestylev, value)) { + tex2D->mTilestyleV = value; + } + mEmbeddedTextures.emplace_back(tex2D); + StoreEmbeddedTexture(tex2D); +} + +void XmlSerializer::StoreEmbeddedTexture(EmbeddedTexture *tex) { + aiMaterial *mat = new aiMaterial; + aiString s; + s.Set(ai_to_string(tex->mId).c_str()); + mat->AddProperty(&s, AI_MATKEY_NAME); + const std::string name = "*" + tex->mPath; + s.Set(name); + mat->AddProperty(&s, AI_MATKEY_TEXTURE_DIFFUSE(0)); + + aiColor3D col; + mat->AddProperty(&col, 1, AI_MATKEY_COLOR_DIFFUSE); + mat->AddProperty(&col, 1, AI_MATKEY_COLOR_AMBIENT); + mat->AddProperty(&col, 1, AI_MATKEY_COLOR_EMISSIVE); + mat->AddProperty(&col, 1, AI_MATKEY_COLOR_SPECULAR); + mMaterials.emplace_back(mat); +} + +void XmlSerializer::ReadTextureCoords2D(XmlNode &node, Texture2DGroup *tex2DGroup) { + if (node.empty() || nullptr == tex2DGroup) { + return; + } + + int id = IdNotSet; + if (XmlParser::getIntAttribute(node, "texid", id)) { + tex2DGroup->mTexId = id; + } + + double value = 0.0; + for (XmlNode currentNode : node.children()) { + const std::string currentName = currentNode.name(); + aiVector2D texCoord; + if (currentName == XmlTag::texture_2d_coord) { + XmlParser::getDoubleAttribute(currentNode, XmlTag::texture_cuurd_u, value); + texCoord.x = (ai_real)value; + XmlParser::getDoubleAttribute(currentNode, XmlTag::texture_cuurd_v, value); + texCoord.y = (ai_real)value; + tex2DGroup->mTex2dCoords.push_back(texCoord); + } + } +} + +void XmlSerializer::ReadTextureGroup(XmlNode &node) { + if (node.empty()) { + return; + } + + int id = IdNotSet; + if (!XmlParser::getIntAttribute(node, XmlTag::id, id)) { + return; + } + + Texture2DGroup *group = new Texture2DGroup(id); + ReadTextureCoords2D(node, group); + mResourcesDictionnary.insert(std::make_pair(id, group)); +} + +aiMaterial *XmlSerializer::readMaterialDef(XmlNode &node, unsigned int basematerialsId) { + aiMaterial *material = new aiMaterial(); + material->mNumProperties = 0; + std::string name; + bool hasName = getNodeAttribute(node, D3MF::XmlTag::basematerials_name, name); + + std::string stdMaterialName; + const std::string strId(ai_to_string(basematerialsId)); + stdMaterialName += "id"; + stdMaterialName += strId; + stdMaterialName += "_"; + if (hasName) { + stdMaterialName += std::string(name); + } else { + stdMaterialName += "basemat_"; + stdMaterialName += ai_to_string(mMaterials.size()); + } + + aiString assimpMaterialName(stdMaterialName); + material->AddProperty(&assimpMaterialName, AI_MATKEY_NAME); + + assignDiffuseColor(node, material); + + return material; +} + +void XmlSerializer::StoreMaterialsInScene(aiScene *scene) { + if (nullptr == scene || mMaterials.empty()) { + return; + } + + scene->mNumMaterials = static_cast(mMaterials.size()); + scene->mMaterials = new aiMaterial *[scene->mNumMaterials]; + for (size_t i = 0; i < mMaterials.size(); ++i) { + scene->mMaterials[i] = mMaterials[i]; + } +} + +} // namespace D3MF +} // namespace Assimp diff --git a/code/AssetLib/3MF/XmlSerializer.h b/code/AssetLib/3MF/XmlSerializer.h new file mode 100644 index 000000000..14da82e99 --- /dev/null +++ b/code/AssetLib/3MF/XmlSerializer.h @@ -0,0 +1,96 @@ +/* +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. + +---------------------------------------------------------------------- +*/ +#pragma once + +#include +#include +#include +#include + +struct aiNode; +struct aiMesh; +struct aiMaterial; + +namespace Assimp { +namespace D3MF { + +class Resource; +class D3MFOpcPackage; +class Object; +class Texture2DGroup; +class EmbeddedTexture; + +class XmlSerializer { +public: + XmlSerializer(XmlParser *xmlParser); + ~XmlSerializer(); + void ImportXml(aiScene *scene); + +private: + void addObjectToNode(aiNode *parent, Object *obj, aiMatrix4x4 nodeTransform); + void ReadObject(XmlNode &node); + aiMesh *ReadMesh(XmlNode &node); + void ReadMetadata(XmlNode &node); + void ImportVertices(XmlNode &node, aiMesh *mesh); + void ImportTriangles(XmlNode &node, aiMesh *mesh); + void ReadBaseMaterials(XmlNode &node); + void ReadEmbeddecTexture(XmlNode &node); + void StoreEmbeddedTexture(EmbeddedTexture *tex); + void ReadTextureCoords2D(XmlNode &node, Texture2DGroup *tex2DGroup); + void ReadTextureGroup(XmlNode &node); + aiMaterial *readMaterialDef(XmlNode &node, unsigned int basematerialsId); + void StoreMaterialsInScene(aiScene *scene); + +private: + struct MetaEntry { + std::string name; + std::string value; + }; + std::vector mMetaData; + std::vector mEmbeddedTextures; + std::vector mMaterials; + std::map mResourcesDictionnary; + unsigned int mMeshCount; + XmlParser *mXmlParser; +}; + +} // namespace D3MF +} // namespace Assimp diff --git a/code/AssetLib/AMF/AMFImporter.cpp b/code/AssetLib/AMF/AMFImporter.cpp index 615882b6a..88a38b827 100644 --- a/code/AssetLib/AMF/AMFImporter.cpp +++ b/code/AssetLib/AMF/AMFImporter.cpp @@ -303,7 +303,7 @@ void AMFImporter::ParseNode_Root() { } XmlNode node = *root; mUnit = ai_tolower(std::string(node.attribute("unit").as_string())); - + mVersion = node.attribute("version").as_string(); // Read attributes for node . diff --git a/code/AssetLib/AMF/AMFImporter_Geometry.cpp b/code/AssetLib/AMF/AMFImporter_Geometry.cpp index 1fd2c49a4..1d2a1f5b4 100644 --- a/code/AssetLib/AMF/AMFImporter_Geometry.cpp +++ b/code/AssetLib/AMF/AMFImporter_Geometry.cpp @@ -75,7 +75,7 @@ void AMFImporter::ParseNode_Mesh(XmlNode &node) { found_volumes = true; } ParseHelper_Node_Exit(); - } + } if (!found_verts && !found_volumes) { mNodeElement_Cur->Child.push_back(ne); @@ -199,9 +199,9 @@ void AMFImporter::ParseNode_Volume(XmlNode &node) { // Read attributes for node . // and assign read data - + ((AMFVolume *)ne)->MaterialID = node.attribute("materialid").as_string(); - + ((AMFVolume *)ne)->Type = type; // Check for child nodes bool col_read = false; diff --git a/code/AssetLib/AMF/AMFImporter_Postprocess.cpp b/code/AssetLib/AMF/AMFImporter_Postprocess.cpp index 43d0de52f..d56d6681d 100644 --- a/code/AssetLib/AMF/AMFImporter_Postprocess.cpp +++ b/code/AssetLib/AMF/AMFImporter_Postprocess.cpp @@ -69,7 +69,7 @@ aiColor4D AMFImporter::SPP_Material::GetColor(const float /*pX*/, const float /* } tcol = Color->Color; - + // Check if default color must be used if ((tcol.r == 0) && (tcol.g == 0) && (tcol.b == 0) && (tcol.a == 0)) { tcol.r = 0.5f; @@ -99,10 +99,10 @@ void AMFImporter::PostprocessHelper_CreateMeshDataArray(const AMFMesh &nodeEleme } // all coordinates stored as child and we need to reserve space for future push_back's. - vertexCoordinateArray.reserve(vn->Child.size()); + vertexCoordinateArray.reserve(vn->Child.size()); // colors count equal vertices count. - pVertexColorArray.resize(vn->Child.size()); + pVertexColorArray.resize(vn->Child.size()); col_idx = 0; // Inside vertices collect all data and place to arrays diff --git a/code/AssetLib/ASE/ASEParser.h b/code/AssetLib/ASE/ASEParser.h index d04fc0662..f49cfc36f 100644 --- a/code/AssetLib/ASE/ASEParser.h +++ b/code/AssetLib/ASE/ASEParser.h @@ -95,8 +95,8 @@ struct Material : public D3DS::Material { Material(Material &&other) AI_NO_EXCEPT : D3DS::Material(std::move(other)), avSubMaterials(std::move(other.avSubMaterials)), - pcInstance(std::move(other.pcInstance)), - bNeed(std::move(other.bNeed)) { + pcInstance(other.pcInstance), + bNeed(other.bNeed) { other.pcInstance = nullptr; } @@ -108,8 +108,8 @@ struct Material : public D3DS::Material { //D3DS::Material::operator=(std::move(other)); avSubMaterials = std::move(other.avSubMaterials); - pcInstance = std::move(other.pcInstance); - bNeed = std::move(other.bNeed); + pcInstance = other.pcInstance; + bNeed = other.bNeed; other.pcInstance = nullptr; diff --git a/code/AssetLib/Assbin/AssbinFileWriter.cpp b/code/AssetLib/Assbin/AssbinFileWriter.cpp index 95379a303..2519b0b93 100644 --- a/code/AssetLib/Assbin/AssbinFileWriter.cpp +++ b/code/AssetLib/Assbin/AssbinFileWriter.cpp @@ -172,7 +172,7 @@ inline size_t Write(IOStream *stream, const aiQuaternion &v) { t += Write(stream, v.z); ai_assert(t == 16); - return 16; + return t; } // ----------------------------------------------------------------------------------- diff --git a/code/AssetLib/Assjson/json_exporter.cpp b/code/AssetLib/Assjson/json_exporter.cpp index b9099d392..7b2c8ec81 100644 --- a/code/AssetLib/Assjson/json_exporter.cpp +++ b/code/AssetLib/Assjson/json_exporter.cpp @@ -41,12 +41,17 @@ public: enum { Flag_DoNotIndent = 0x1, Flag_WriteSpecialFloats = 0x2, + Flag_SkipWhitespaces = 0x4 }; - + JSONWriter(Assimp::IOStream &out, unsigned int flags = 0u) : - out(out), first(), flags(flags) { + out(out), indent (""), newline("\n"), space(" "), buff (), first(false), flags(flags) { // make sure that all formatting happens using the standard, C locale and not the user's current locale buff.imbue(std::locale("C")); + if (flags & Flag_SkipWhitespaces) { + newline = ""; + space = ""; + } } ~JSONWriter() { @@ -70,7 +75,7 @@ public: void Key(const std::string &name) { AddIndentation(); Delimit(); - buff << '\"' + name + "\": "; + buff << '\"' + name + "\":" << space; } template @@ -78,12 +83,12 @@ public: AddIndentation(); Delimit(); - LiteralToString(buff, name) << '\n'; + LiteralToString(buff, name) << newline; } template void SimpleValue(const Literal &s) { - LiteralToString(buff, s) << '\n'; + LiteralToString(buff, s) << newline; } void SimpleValue(const void *buffer, size_t len) { @@ -102,7 +107,7 @@ public: } } - buff << '\"' << cur_out << "\"\n"; + buff << '\"' << cur_out << "\"" << newline; delete[] cur_out; } @@ -115,7 +120,7 @@ public: } } first = true; - buff << "{\n"; + buff << "{" << newline; PushIndent(); } @@ -123,7 +128,7 @@ public: PopIndent(); AddIndentation(); first = false; - buff << "}\n"; + buff << "}" << newline; } void StartArray(bool is_element = false) { @@ -135,19 +140,19 @@ public: } } first = true; - buff << "[\n"; + buff << "[" << newline; PushIndent(); } void EndArray() { PopIndent(); AddIndentation(); - buff << "]\n"; + buff << "]" << newline; first = false; } void AddIndentation() { - if (!(flags & Flag_DoNotIndent)) { + if (!(flags & Flag_DoNotIndent) && !(flags & Flag_SkipWhitespaces)) { buff << indent; } } @@ -156,7 +161,7 @@ public: if (!first) { buff << ','; } else { - buff << ' '; + buff << space; first = false; } } @@ -227,7 +232,9 @@ private: private: Assimp::IOStream &out; - std::string indent, newline; + std::string indent; + std::string newline; + std::string space; std::stringstream buff; bool first; @@ -765,7 +772,7 @@ void Write(JSONWriter &out, const aiScene &ai) { out.EndObj(); } -void ExportAssimp2Json(const char *file, Assimp::IOSystem *io, const aiScene *scene, const Assimp::ExportProperties *) { +void ExportAssimp2Json(const char *file, Assimp::IOSystem *io, const aiScene *scene, const Assimp::ExportProperties *pProperties) { std::unique_ptr str(io->Open(file, "wt")); if (!str) { throw DeadlyExportError("could not open output file"); @@ -782,7 +789,12 @@ void ExportAssimp2Json(const char *file, Assimp::IOSystem *io, const aiScene *sc splitter.Execute(scenecopy_tmp); // XXX Flag_WriteSpecialFloats is turned on by default, right now we don't have a configuration interface for exporters - JSONWriter s(*str, JSONWriter::Flag_WriteSpecialFloats); + + unsigned int flags = JSONWriter::Flag_WriteSpecialFloats; + if (pProperties->GetPropertyBool("JSON_SKIP_WHITESPACES", false)) { + flags |= JSONWriter::Flag_SkipWhitespaces; + } + JSONWriter s(*str, flags); Write(s, *scenecopy_tmp); } catch (...) { diff --git a/code/AssetLib/Assjson/mesh_splitter.cpp b/code/AssetLib/Assjson/mesh_splitter.cpp index 24385f9a0..9301cc27e 100644 --- a/code/AssetLib/Assjson/mesh_splitter.cpp +++ b/code/AssetLib/Assjson/mesh_splitter.cpp @@ -110,7 +110,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vectormNumVertices / LIMIT) + 1; - // create a std::vector to remember which vertices have already + // create a std::vector to remember which vertices have already // been copied and to which position (i.e. output index) std::vector was_copied_to; was_copied_to.resize(in_mesh->mNumVertices,WAS_NOT_COPIED); @@ -125,7 +125,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vectormNumVertices = 0; out_mesh->mMaterialIndex = in_mesh->mMaterialIndex; @@ -179,7 +179,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vectormNumVertices + iNeed > out_vertex_index) { @@ -240,7 +240,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vectormTextureCoords[c][out_mesh->mNumVertices] = in_mesh->mTextureCoords[c][index]; } } - // vertex colors + // vertex colors for (unsigned int c = 0; c < AI_MAX_NUMBER_OF_COLOR_SETS;++c) { if (in_mesh->HasVertexColors( c)) { out_mesh->mColors[c][out_mesh->mNumVertices] = in_mesh->mColors[c][index]; diff --git a/code/AssetLib/Assjson/mesh_splitter.h b/code/AssetLib/Assjson/mesh_splitter.h index 326f73b41..3bb26118a 100644 --- a/code/AssetLib/Assjson/mesh_splitter.h +++ b/code/AssetLib/Assjson/mesh_splitter.h @@ -22,13 +22,13 @@ struct aiNode; // --------------------------------------------------------------------------- /** Splits meshes of unique vertices into meshes with no more vertices than - * a given, configurable threshold value. + * a given, configurable threshold value. */ -class MeshSplitter +class MeshSplitter { public: - + void SetLimit(unsigned int l) { LIMIT = l; } diff --git a/code/AssetLib/Assxml/AssxmlExporter.cpp b/code/AssetLib/Assxml/AssxmlExporter.cpp index 847ba0d7e..d244813b1 100644 --- a/code/AssetLib/Assxml/AssxmlExporter.cpp +++ b/code/AssetLib/Assxml/AssxmlExporter.cpp @@ -50,7 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -namespace Assimp { +namespace Assimp { void ExportSceneAssxml(const char* pFile, IOSystem* pIOSystem, const aiScene* pScene, const ExportProperties* /*pProperties*/) { diff --git a/code/AssetLib/B3D/B3DImporter.cpp b/code/AssetLib/B3D/B3DImporter.cpp index 3d9a5075a..11f7bcd14 100644 --- a/code/AssetLib/B3D/B3DImporter.cpp +++ b/code/AssetLib/B3D/B3DImporter.cpp @@ -143,7 +143,7 @@ AI_WONT_RETURN void B3DImporter::Oops() { } // ------------------------------------------------------------------------------------------------ -AI_WONT_RETURN void B3DImporter::Fail(string str) { +AI_WONT_RETURN void B3DImporter::Fail(const string &str) { #ifdef DEBUG_B3D ASSIMP_LOG_ERROR("Error in B3D file data: ", str); #endif diff --git a/code/AssetLib/B3D/B3DImporter.h b/code/AssetLib/B3D/B3DImporter.h index e2a75abdf..a7ed65c3b 100644 --- a/code/AssetLib/B3D/B3DImporter.h +++ b/code/AssetLib/B3D/B3DImporter.h @@ -96,7 +96,7 @@ private: }; AI_WONT_RETURN void Oops() AI_WONT_RETURN_SUFFIX; - AI_WONT_RETURN void Fail( std::string str ) AI_WONT_RETURN_SUFFIX; + AI_WONT_RETURN void Fail(const std::string &str) AI_WONT_RETURN_SUFFIX; void ReadTEXS(); void ReadBRUS(); diff --git a/code/AssetLib/Blender/BlenderLoader.cpp b/code/AssetLib/Blender/BlenderLoader.cpp index 7cf4e070e..42a7a1723 100644 --- a/code/AssetLib/Blender/BlenderLoader.cpp +++ b/code/AssetLib/Blender/BlenderLoader.cpp @@ -679,7 +679,7 @@ void BlenderImporter::BuildMaterials(ConversionData &conv_data) { BuildDefaultMaterial(conv_data); - for (std::shared_ptr mat : conv_data.materials_raw) { + for (const std::shared_ptr &mat : conv_data.materials_raw) { // reset per material global counters for (size_t i = 0; i < sizeof(conv_data.next_texture) / sizeof(conv_data.next_texture[0]); ++i) { diff --git a/code/AssetLib/Blender/BlenderModifier.h b/code/AssetLib/Blender/BlenderModifier.h index daf120087..d2fea43c1 100644 --- a/code/AssetLib/Blender/BlenderModifier.h +++ b/code/AssetLib/Blender/BlenderModifier.h @@ -52,9 +52,9 @@ namespace Assimp { namespace Blender { // ------------------------------------------------------------------------------------------- -/** +/** * Dummy base class for all blender modifiers. Modifiers are reused between imports, so - * they should be stateless and not try to cache model data. + * they should be stateless and not try to cache model data. */ // ------------------------------------------------------------------------------------------- class BlenderModifier { @@ -67,7 +67,7 @@ public: } // -------------------- - /** + /** * Check if *this* modifier is active, given a ModifierData& block. */ virtual bool IsActive( const ModifierData& /*modin*/) { @@ -75,10 +75,10 @@ public: } // -------------------- - /** + /** * Apply the modifier to a given output node. The original data used * to construct the node is given as well. Not called unless IsActive() - * was called and gave positive response. + * was called and gave positive response. */ virtual void DoIt(aiNode& /*out*/, ConversionData& /*conv_data*/, @@ -92,8 +92,8 @@ public: }; // ------------------------------------------------------------------------------------------- -/** - * Manage all known modifiers and instance and apply them if necessary +/** + * Manage all known modifiers and instance and apply them if necessary */ // ------------------------------------------------------------------------------------------- class BlenderModifierShowcase { @@ -113,8 +113,8 @@ private: // MODIFIERS ///////////////////////////////////////////////////////////////////////////////// // ------------------------------------------------------------------------------------------- -/** - * Mirror modifier. Status: implemented. +/** + * Mirror modifier. Status: implemented. */ // ------------------------------------------------------------------------------------------- class BlenderModifier_Mirror : public BlenderModifier { diff --git a/code/AssetLib/C4D/C4DImporter.cpp b/code/AssetLib/C4D/C4DImporter.cpp index 434d1429e..14a94958b 100644 --- a/code/AssetLib/C4D/C4DImporter.cpp +++ b/code/AssetLib/C4D/C4DImporter.cpp @@ -146,8 +146,14 @@ void C4DImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS ThrowException("failed to read document " + pFile); } + // Generate the root-node pScene->mRootNode = new aiNode(""); + // convert left-handed to right-handed + pScene->mRootNode->mTransformation.a1 = 0.01f; + pScene->mRootNode->mTransformation.b2 = 0.01f; + pScene->mRootNode->mTransformation.c3 = -0.01f; + // first convert all materials ReadMaterials(doc->GetFirstMaterial()); diff --git a/code/AssetLib/COB/COBLoader.cpp b/code/AssetLib/COB/COBLoader.cpp index 94327c683..3bef260e5 100644 --- a/code/AssetLib/COB/COBLoader.cpp +++ b/code/AssetLib/COB/COBLoader.cpp @@ -230,7 +230,7 @@ void COBImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSy } // ------------------------------------------------------------------------------------------------ -void ConvertTexture(std::shared_ptr tex, aiMaterial *out, aiTextureType type) { +void ConvertTexture(const std::shared_ptr &tex, aiMaterial *out, aiTextureType type) { const aiString path(tex->path); out->AddProperty(&path, AI_MATKEY_TEXTURE(type, 0)); out->AddProperty(&tex->transform, 1, AI_MATKEY_UVTRANSFORM(type, 0)); @@ -884,7 +884,7 @@ void COBImporter::ReadBinaryFile(Scene &out, StreamReaderLE *reader) { std::string type; type += reader->GetI1(); type += reader->GetI1(); - type += reader->GetI1(); + type += reader->GetI1(); type += reader->GetI1(); ChunkInfo nfo; diff --git a/code/AssetLib/COB/COBLoader.h b/code/AssetLib/COB/COBLoader.h index 2317d094e..e4d41e500 100644 --- a/code/AssetLib/COB/COBLoader.h +++ b/code/AssetLib/COB/COBLoader.h @@ -77,7 +77,7 @@ class COBImporter : public BaseImporter public: COBImporter(); ~COBImporter(); - + // -------------------- bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const; diff --git a/code/AssetLib/Collada/ColladaLoader.cpp b/code/AssetLib/Collada/ColladaLoader.cpp index 492e6971f..f7b5f2278 100644 --- a/code/AssetLib/Collada/ColladaLoader.cpp +++ b/code/AssetLib/Collada/ColladaLoader.cpp @@ -135,14 +135,15 @@ bool ColladaLoader::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool // XML - too generic, we need to open the file and search for typical keywords if (extension == "xml" || !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) { + // 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 *tokens[] = { "mNumMeshes = static_cast(newMeshRefs.size()); - if (newMeshRefs.size()) { + if (!newMeshRefs.empty()) { struct UIntTypeConverter { unsigned int operator()(const size_t &v) const { return static_cast(v); @@ -619,6 +620,10 @@ aiMesh *ColladaLoader::CreateMesh(const ColladaParser &pParser, const Mesh *pSrc dstMesh->mName = pSrcMesh->mId; } + if (pSrcMesh->mPositions.empty()) { + return dstMesh.release(); + } + // count the vertices addressed by its faces const size_t numVertices = std::accumulate(pSrcMesh->mFaceSize.begin() + pStartFace, pSrcMesh->mFaceSize.begin() + pStartFace + pSubMesh.mNumFaces, size_t(0)); @@ -1540,7 +1545,7 @@ void ColladaLoader::AddTexture(aiMaterial &mat, map = -1; for (std::string::const_iterator it = sampler.mUVChannel.begin(); it != sampler.mUVChannel.end(); ++it) { if (IsNumeric(*it)) { - map = strtoul10(&(*it)); + map = strtoul10(&(*it)); break; } } @@ -1671,7 +1676,7 @@ void ColladaLoader::BuildMaterials(ColladaParser &pParser, aiScene * /*pScene*/) const Material &material = matIt->second; // a material is only a reference to an effect ColladaParser::EffectLibrary::iterator effIt = pParser.mEffectLibrary.find(material.mEffect); - if (effIt == pParser.mEffectLibrary.end()) + if (effIt == pParser.mEffectLibrary.end()) continue; Effect &effect = effIt->second; @@ -1682,7 +1687,7 @@ void ColladaLoader::BuildMaterials(ColladaParser &pParser, aiScene * /*pScene*/) // store the material mMaterialIndexByName[matIt->first] = newMats.size(); - newMats.push_back(std::pair(&effect, mat)); + newMats.emplace_back(&effect, mat); } // ScenePreprocessor generates a default material automatically if none is there. // All further code here in this loader works well without a valid material so diff --git a/code/AssetLib/Collada/ColladaParser.cpp b/code/AssetLib/Collada/ColladaParser.cpp index 5dbf0a567..94b9b18ed 100644 --- a/code/AssetLib/Collada/ColladaParser.cpp +++ b/code/AssetLib/Collada/ColladaParser.cpp @@ -170,10 +170,10 @@ ColladaParser::ColladaParser(IOSystem *pIOHandler, const std::string &pFile) : // ------------------------------------------------------------------------------------------------ // Destructor, private as well ColladaParser::~ColladaParser() { - for (auto & it : mNodeLibrary) { + for (auto &it : mNodeLibrary) { delete it.second; } - for (auto & it : mMeshLibrary) { + for (auto &it : mMeshLibrary) { delete it.second; } } @@ -231,11 +231,7 @@ void ColladaParser::UriDecodePath(aiString &ss) { // Maxon Cinema Collada Export writes "file:///C:\andsoon" with three slashes... // I need to filter it without destroying linux paths starting with "/somewhere" -#if defined(_MSC_VER) if (ss.data[0] == '/' && isalpha((unsigned char)ss.data[1]) && ss.data[2] == ':') { -#else - if (ss.data[0] == '/' && isalpha((unsigned char)ss.data[1]) && ss.data[2] == ':') { -#endif --ss.length; ::memmove(ss.data, ss.data + 1, ss.length); ss.data[ss.length] = 0; @@ -396,7 +392,7 @@ void ColladaParser::ReadAnimationClipLibrary(XmlNode &node) { std::string animName; if (!XmlParser::getStdStrAttribute(node, "name", animName)) { - if (!XmlParser::getStdStrAttribute( node, "id", animName )) { + if (!XmlParser::getStdStrAttribute(node, "id", animName)) { animName = std::string("animation_") + ai_to_string(mAnimationClipLibrary.size()); } } @@ -420,7 +416,7 @@ void ColladaParser::ReadAnimationClipLibrary(XmlNode &node) { void ColladaParser::PostProcessControllers() { std::string meshId; - for (auto & it : mControllerLibrary) { + for (auto &it : mControllerLibrary) { meshId = it.second.mMeshId; if (meshId.empty()) { continue; @@ -445,7 +441,7 @@ void ColladaParser::PostProcessRootAnimations() { } Animation temp; - for (auto & it : mAnimationClipLibrary) { + for (auto &it : mAnimationClipLibrary) { std::string clipName = it.first; Animation *clip = new Animation(); @@ -453,7 +449,7 @@ void ColladaParser::PostProcessRootAnimations() { temp.mSubAnims.push_back(clip); - for (std::string animationID : it.second) { + for (const std::string &animationID : it.second) { AnimationLibrary::iterator animation = mAnimationLibrary.find(animationID); if (animation != mAnimationLibrary.end()) { @@ -529,7 +525,7 @@ void ColladaParser::ReadAnimation(XmlNode &node, Collada::Animation *pParent) { // have it read into a channel ChannelMap::iterator newChannel = channels.insert(std::make_pair(id, AnimationChannel())).first; ReadAnimationSampler(currentNode, newChannel->second); - } + } } else if (currentName == "channel") { std::string source_name, target; XmlParser::getStdStrAttribute(currentNode, "source", source_name); @@ -552,7 +548,7 @@ void ColladaParser::ReadAnimation(XmlNode &node, Collada::Animation *pParent) { pParent->mSubAnims.push_back(anim); } - for (const auto & channel : channels) { + for (const auto &channel : channels) { anim->mChannels.push_back(channel.second); } @@ -626,8 +622,6 @@ void ColladaParser::ReadController(XmlNode &node, Collada::Controller &controlle XmlNodeIterator xmlIt(node, XmlNodeIterator::PreOrderMode); XmlNode currentNode; while (xmlIt.getNext(currentNode)) { - - //for (XmlNode ¤tNode : node.children()) { const std::string ¤tName = currentNode.name(); if (currentName == "morph") { controller.mType = Morph; @@ -644,7 +638,7 @@ void ColladaParser::ReadController(XmlNode &node, Collada::Controller &controlle } else if (currentName == "skin") { std::string id; if (XmlParser::getStdStrAttribute(currentNode, "source", id)) { - controller.mMeshId = id.substr(1, id.size()-1); + controller.mMeshId = id.substr(1, id.size() - 1); } } else if (currentName == "bind_shape_matrix") { std::string v; @@ -698,7 +692,7 @@ void ColladaParser::ReadControllerJoints(XmlNode &node, Collada::Controller &pCo } else if (strcmp(attrSemantic, "INV_BIND_MATRIX") == 0) { pController.mJointOffsetMatrixSource = attrSource; } else { - throw DeadlyImportError("Unknown semantic \"" , attrSemantic , "\" in data element"); + throw DeadlyImportError("Unknown semantic \"", attrSemantic, "\" in data element"); } } } @@ -708,7 +702,7 @@ void ColladaParser::ReadControllerJoints(XmlNode &node, Collada::Controller &pCo // Reads the joint weights for the given controller void ColladaParser::ReadControllerWeights(XmlNode &node, Collada::Controller &pController) { // Read vertex count from attributes and resize the array accordingly - int vertexCount=0; + int vertexCount = 0; XmlParser::getIntAttribute(node, "count", vertexCount); pController.mWeightCounts.resize(vertexCount); @@ -723,7 +717,7 @@ void ColladaParser::ReadControllerWeights(XmlNode &node, Collada::Controller &pC // local URLS always start with a '#'. We don't support global URLs if (attrSource[0] != '#') { - throw DeadlyImportError( "Unsupported URL format in \"", attrSource, "\" in source attribute of data element"); + throw DeadlyImportError("Unsupported URL format in \"", attrSource, "\" in source attribute of data element"); } channel.mAccessor = attrSource + 1; @@ -777,7 +771,7 @@ void ColladaParser::ReadImageLibrary(XmlNode &node) { const std::string ¤tName = currentNode.name(); if (currentName == "image") { std::string id; - if (XmlParser::getStdStrAttribute( currentNode, "id", id )) { + if (XmlParser::getStdStrAttribute(currentNode, "id", id)) { mImageLibrary[id] = Image(); // read on from there ReadImage(currentNode, mImageLibrary[id]); @@ -907,7 +901,7 @@ void ColladaParser::ReadCameraLibrary(XmlNode &node) { if (!name.empty()) { cam.mName = name; } - ReadCamera(currentNode, cam); + ReadCamera(currentNode, cam); } } } @@ -920,7 +914,7 @@ void ColladaParser::ReadMaterial(XmlNode &node, Collada::Material &pMaterial) { if (currentName == "instance_effect") { std::string url; readUrlAttribute(currentNode, url); - pMaterial.mEffect = url.c_str(); + pMaterial.mEffect = url; } } } @@ -1361,8 +1355,8 @@ void ColladaParser::ReadMesh(XmlNode &node, Mesh &pMesh) { } else if (currentName == "vertices") { ReadVertexData(currentNode, pMesh); } else if (currentName == "triangles" || currentName == "lines" || currentName == "linestrips" || - currentName == "polygons" || currentName == "polylist" || currentName == "trifans" || - currentName == "tristrips") { + currentName == "polygons" || currentName == "polylist" || currentName == "trifans" || + currentName == "tristrips") { ReadIndexData(currentNode, pMesh); } } @@ -1439,9 +1433,8 @@ void ColladaParser::ReadDataArray(XmlNode &node) { throw DeadlyImportError("Expected more values while reading float_array contents."); } - ai_real value; // read a number - //SkipSpacesAndLineEnd(&content); + ai_real value; content = fast_atoreal_move(content, value); data.mValues.push_back(value); // skip whitespace after it @@ -1489,11 +1482,10 @@ void ColladaParser::ReadAccessor(XmlNode &node, const std::string &pID) { std::string name; if (XmlParser::hasAttribute(currentNode, "name")) { XmlParser::getStdStrAttribute(currentNode, "name", name); - //name = mReader->getAttributeValue(attrName); // analyse for common type components and store it's sub-offset in the corresponding field - /* Cartesian coordinates */ + // Cartesian coordinates if (name == "X") acc.mSubOffset[0] = acc.mParams.size(); else if (name == "Y") @@ -1674,12 +1666,9 @@ void ColladaParser::ReadInputChannel(XmlNode &node, std::vector &p // read set if texture coordinates if (channel.mType == IT_Texcoord || channel.mType == IT_Color) { - int attrSet = -1; - if (XmlParser::hasAttribute(node, "set")) { - XmlParser::getIntAttribute(node, "set", attrSet); - } - - channel.mIndex = attrSet; + unsigned int attrSet = 0; + if (XmlParser::getUIntAttribute(node, "set", attrSet)) + channel.mIndex = attrSet; } // store, if valid type @@ -1704,20 +1693,20 @@ size_t ColladaParser::ReadPrimitives(XmlNode &node, Mesh &pMesh, std::vector 0) { + if (pNumPrimitives > 0) { std::string v; XmlParser::getValueAsString(node, v); const char *content = v.c_str(); @@ -1925,87 +1914,87 @@ void ColladaParser::ExtractDataObjectFromChannel(const InputChannel &pInput, siz // now we reinterpret it according to the type we're reading here switch (pInput.mType) { - case IT_Position: // ignore all position streams except 0 - there can be only one position - if (pInput.mIndex == 0) { - pMesh.mPositions.push_back(aiVector3D(obj[0], obj[1], obj[2])); - } else { - ASSIMP_LOG_ERROR("Collada: just one vertex position stream supported"); - } - break; - case IT_Normal: + case IT_Position: // ignore all position streams except 0 - there can be only one position + if (pInput.mIndex == 0) { + pMesh.mPositions.push_back(aiVector3D(obj[0], obj[1], obj[2])); + } else { + ASSIMP_LOG_ERROR("Collada: just one vertex position stream supported"); + } + break; + case IT_Normal: + // pad to current vertex count if necessary + if (pMesh.mNormals.size() < pMesh.mPositions.size() - 1) + pMesh.mNormals.insert(pMesh.mNormals.end(), pMesh.mPositions.size() - pMesh.mNormals.size() - 1, aiVector3D(0, 1, 0)); + + // ignore all normal streams except 0 - there can be only one normal + if (pInput.mIndex == 0) { + pMesh.mNormals.push_back(aiVector3D(obj[0], obj[1], obj[2])); + } else { + ASSIMP_LOG_ERROR("Collada: just one vertex normal stream supported"); + } + break; + case IT_Tangent: + // pad to current vertex count if necessary + if (pMesh.mTangents.size() < pMesh.mPositions.size() - 1) + pMesh.mTangents.insert(pMesh.mTangents.end(), pMesh.mPositions.size() - pMesh.mTangents.size() - 1, aiVector3D(1, 0, 0)); + + // ignore all tangent streams except 0 - there can be only one tangent + if (pInput.mIndex == 0) { + pMesh.mTangents.push_back(aiVector3D(obj[0], obj[1], obj[2])); + } else { + ASSIMP_LOG_ERROR("Collada: just one vertex tangent stream supported"); + } + break; + case IT_Bitangent: + // pad to current vertex count if necessary + if (pMesh.mBitangents.size() < pMesh.mPositions.size() - 1) { + pMesh.mBitangents.insert(pMesh.mBitangents.end(), pMesh.mPositions.size() - pMesh.mBitangents.size() - 1, aiVector3D(0, 0, 1)); + } + + // ignore all bitangent streams except 0 - there can be only one bitangent + if (pInput.mIndex == 0) { + pMesh.mBitangents.push_back(aiVector3D(obj[0], obj[1], obj[2])); + } else { + ASSIMP_LOG_ERROR("Collada: just one vertex bitangent stream supported"); + } + break; + case IT_Texcoord: + // up to 4 texture coord sets are fine, ignore the others + if (pInput.mIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS) { // pad to current vertex count if necessary - if (pMesh.mNormals.size() < pMesh.mPositions.size() - 1) - pMesh.mNormals.insert(pMesh.mNormals.end(), pMesh.mPositions.size() - pMesh.mNormals.size() - 1, aiVector3D(0, 1, 0)); + if (pMesh.mTexCoords[pInput.mIndex].size() < pMesh.mPositions.size() - 1) + pMesh.mTexCoords[pInput.mIndex].insert(pMesh.mTexCoords[pInput.mIndex].end(), + pMesh.mPositions.size() - pMesh.mTexCoords[pInput.mIndex].size() - 1, aiVector3D(0, 0, 0)); - // ignore all normal streams except 0 - there can be only one normal - if (pInput.mIndex == 0) { - pMesh.mNormals.push_back(aiVector3D(obj[0], obj[1], obj[2])); - } else { - ASSIMP_LOG_ERROR("Collada: just one vertex normal stream supported"); + pMesh.mTexCoords[pInput.mIndex].push_back(aiVector3D(obj[0], obj[1], obj[2])); + if (0 != acc.mSubOffset[2] || 0 != acc.mSubOffset[3]) { + pMesh.mNumUVComponents[pInput.mIndex] = 3; } - break; - case IT_Tangent: + } else { + ASSIMP_LOG_ERROR("Collada: too many texture coordinate sets. Skipping."); + } + break; + case IT_Color: + // up to 4 color sets are fine, ignore the others + if (pInput.mIndex < AI_MAX_NUMBER_OF_COLOR_SETS) { // pad to current vertex count if necessary - if (pMesh.mTangents.size() < pMesh.mPositions.size() - 1) - pMesh.mTangents.insert(pMesh.mTangents.end(), pMesh.mPositions.size() - pMesh.mTangents.size() - 1, aiVector3D(1, 0, 0)); + if (pMesh.mColors[pInput.mIndex].size() < pMesh.mPositions.size() - 1) + pMesh.mColors[pInput.mIndex].insert(pMesh.mColors[pInput.mIndex].end(), + pMesh.mPositions.size() - pMesh.mColors[pInput.mIndex].size() - 1, aiColor4D(0, 0, 0, 1)); - // ignore all tangent streams except 0 - there can be only one tangent - if (pInput.mIndex == 0) { - pMesh.mTangents.push_back(aiVector3D(obj[0], obj[1], obj[2])); - } else { - ASSIMP_LOG_ERROR("Collada: just one vertex tangent stream supported"); - } - break; - case IT_Bitangent: - // pad to current vertex count if necessary - if (pMesh.mBitangents.size() < pMesh.mPositions.size() - 1) { - pMesh.mBitangents.insert(pMesh.mBitangents.end(), pMesh.mPositions.size() - pMesh.mBitangents.size() - 1, aiVector3D(0, 0, 1)); + aiColor4D result(0, 0, 0, 1); + for (size_t i = 0; i < pInput.mResolved->mSize; ++i) { + result[static_cast(i)] = obj[pInput.mResolved->mSubOffset[i]]; } + pMesh.mColors[pInput.mIndex].push_back(result); + } else { + ASSIMP_LOG_ERROR("Collada: too many vertex color sets. Skipping."); + } - // ignore all bitangent streams except 0 - there can be only one bitangent - if (pInput.mIndex == 0) { - pMesh.mBitangents.push_back(aiVector3D(obj[0], obj[1], obj[2])); - } else { - ASSIMP_LOG_ERROR("Collada: just one vertex bitangent stream supported"); - } - break; - case IT_Texcoord: - // up to 4 texture coord sets are fine, ignore the others - if (pInput.mIndex < AI_MAX_NUMBER_OF_TEXTURECOORDS) { - // pad to current vertex count if necessary - if (pMesh.mTexCoords[pInput.mIndex].size() < pMesh.mPositions.size() - 1) - pMesh.mTexCoords[pInput.mIndex].insert(pMesh.mTexCoords[pInput.mIndex].end(), - pMesh.mPositions.size() - pMesh.mTexCoords[pInput.mIndex].size() - 1, aiVector3D(0, 0, 0)); - - pMesh.mTexCoords[pInput.mIndex].push_back(aiVector3D(obj[0], obj[1], obj[2])); - if (0 != acc.mSubOffset[2] || 0 != acc.mSubOffset[3]) { - pMesh.mNumUVComponents[pInput.mIndex] = 3; - } - } else { - ASSIMP_LOG_ERROR("Collada: too many texture coordinate sets. Skipping."); - } - break; - case IT_Color: - // up to 4 color sets are fine, ignore the others - if (pInput.mIndex < AI_MAX_NUMBER_OF_COLOR_SETS) { - // pad to current vertex count if necessary - if (pMesh.mColors[pInput.mIndex].size() < pMesh.mPositions.size() - 1) - pMesh.mColors[pInput.mIndex].insert(pMesh.mColors[pInput.mIndex].end(), - pMesh.mPositions.size() - pMesh.mColors[pInput.mIndex].size() - 1, aiColor4D(0, 0, 0, 1)); - - aiColor4D result(0, 0, 0, 1); - for (size_t i = 0; i < pInput.mResolved->mSize; ++i) { - result[static_cast(i)] = obj[pInput.mResolved->mSubOffset[i]]; - } - pMesh.mColors[pInput.mIndex].push_back(result); - } else { - ASSIMP_LOG_ERROR("Collada: too many vertex color sets. Skipping."); - } - - break; - default: - // IT_Invalid and IT_Vertex - ai_assert(false && "shouldn't ever get here"); + break; + default: + // IT_Invalid and IT_Vertex + ai_assert(false && "shouldn't ever get here"); } } @@ -2170,10 +2159,10 @@ void ColladaParser::ReadNodeTransformation(XmlNode &node, Node *pNode, Transform // read as many parameters and store in the transformation for (unsigned int a = 0; a < sNumParameters[pType]; a++) { + // skip whitespace before the number + SkipSpacesAndLineEnd(&content); // read a number content = fast_atoreal_move(content, tf.f[a]); - // skip whitespace after it - SkipSpacesAndLineEnd(&content); } // place the transformation at the queue of the node @@ -2215,8 +2204,8 @@ void ColladaParser::ReadMaterialVertexInputBinding(XmlNode &node, Collada::Seman void ColladaParser::ReadEmbeddedTextures(ZipArchiveIOSystem &zip_archive) { // Attempt to load any undefined Collada::Image in ImageLibrary - for (ImageLibrary::iterator it = mImageLibrary.begin(); it != mImageLibrary.end(); ++it) { - Collada::Image &image = (*it).second; + for (auto & it : mImageLibrary) { + Collada::Image &image = it.second; if (image.mImageData.empty()) { std::unique_ptr image_file(zip_archive.Open(image.mFileName.c_str())); diff --git a/code/AssetLib/DXF/DXFLoader.cpp b/code/AssetLib/DXF/DXFLoader.cpp index 5d32ed121..2e38ed976 100644 --- a/code/AssetLib/DXF/DXFLoader.cpp +++ b/code/AssetLib/DXF/DXFLoader.cpp @@ -5,8 +5,6 @@ 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, @@ -549,7 +547,7 @@ void DXFImporter::ParseEntities(DXF::LineReader& reader, DXF::FileData& output) ++reader; } - ASSIMP_LOG_VERBOSE_DEBUG( "DXF: got ", block.lines.size()," polylines and ", block.insertions.size(), + ASSIMP_LOG_VERBOSE_DEBUG( "DXF: got ", block.lines.size()," polylines and ", block.insertions.size(), " inserted blocks in ENTITIES" ); } diff --git a/code/AssetLib/DXF/DXFLoader.h b/code/AssetLib/DXF/DXFLoader.h index 5319d2528..6649deb34 100644 --- a/code/AssetLib/DXF/DXFLoader.h +++ b/code/AssetLib/DXF/DXFLoader.h @@ -63,7 +63,7 @@ namespace DXF { } // --------------------------------------------------------------------------- -/** +/** * @brief DXF importer implementation. */ class DXFImporter : public BaseImporter { diff --git a/code/AssetLib/FBX/FBXConverter.cpp b/code/AssetLib/FBX/FBXConverter.cpp index a564b3e9b..fa7ee3986 100644 --- a/code/AssetLib/FBX/FBXConverter.cpp +++ b/code/AssetLib/FBX/FBXConverter.cpp @@ -862,7 +862,7 @@ bool FBXConverter::GenerateTransformationNodeChain(const Model &model, const std output_nodes.push_back(std::move(nd)); return false; } - + void FBXConverter::SetupNodeMetadata(const Model &model, aiNode &nd) { const PropertyTable &props = model.Props(); DirectPropertyMap unparsedProperties = props.GetUnparsedProperties(); @@ -917,8 +917,10 @@ void FBXConverter::ConvertModel(const Model &model, aiNode *parent, aiNode *root } else if (line) { const std::vector &indices = ConvertLine(*line, root_node); std::copy(indices.begin(), indices.end(), std::back_inserter(meshes)); - } else { + } else if (geo) { FBXImporter::LogWarn("ignoring unrecognized geometry: ", geo->Name()); + } else { + FBXImporter::LogWarn("skipping null geometry"); } } @@ -1766,6 +1768,7 @@ void FBXConverter::TrySetTextureProperties(aiMaterial *out_mat, const TextureMap // XXX handle all kinds of UV transformations uvTrafo.mScaling = tex->UVScaling(); uvTrafo.mTranslation = tex->UVTranslation(); + uvTrafo.mRotation = tex->UVRotation(); out_mat->AddProperty(&uvTrafo, 1, _AI_MATKEY_UVTRANSFORM_BASE, target, 0); const PropertyTable &props = tex->Props(); @@ -1885,6 +1888,7 @@ void FBXConverter::TrySetTextureProperties(aiMaterial *out_mat, const LayeredTex // XXX handle all kinds of UV transformations uvTrafo.mScaling = tex->UVScaling(); uvTrafo.mTranslation = tex->UVTranslation(); + uvTrafo.mRotation = tex->UVRotation(); out_mat->AddProperty(&uvTrafo, 1, _AI_MATKEY_UVTRANSFORM_BASE, target, texIndex); const PropertyTable &props = tex->Props(); @@ -2129,7 +2133,7 @@ void FBXConverter::SetShadingPropertiesCommon(aiMaterial *out_mat, const Propert if (ok) { out_mat->AddProperty(&Emissive, 1, AI_MATKEY_COLOR_EMISSIVE); } else { - const aiColor3D &emissiveColor = GetColorPropertyFromMaterial(props, "Maya|emissive", ok); + const aiColor3D &emissiveColor = GetColorProperty(props, "Maya|emissive", ok); if (ok) { out_mat->AddProperty(&emissiveColor, 1, AI_MATKEY_COLOR_EMISSIVE); } @@ -2216,7 +2220,7 @@ void FBXConverter::SetShadingPropertiesCommon(aiMaterial *out_mat, const Propert } // PBR material information - const aiColor3D &baseColor = GetColorPropertyFromMaterial(props, "Maya|base_color", ok); + const aiColor3D &baseColor = GetColorProperty(props, "Maya|base_color", ok); if (ok) { out_mat->AddProperty(&baseColor, 1, AI_MATKEY_BASE_COLOR); } @@ -2324,6 +2328,7 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial *out_mat, const PropertyTa // XXX handle all kinds of UV transformations uvTrafo.mScaling = tex->UVScaling(); uvTrafo.mTranslation = tex->UVTranslation(); + uvTrafo.mRotation = tex->UVRotation(); out_mat->AddProperty(&uvTrafo, 1, (name + "|uvtrafo").c_str(), aiTextureType_UNKNOWN, 0); int uvIndex = 0; @@ -2599,7 +2604,7 @@ void FBXConverter::ConvertAnimationStack(const AnimationStack &st) { anim->mMorphMeshChannels = new aiMeshMorphAnim *[numMorphMeshChannels]; anim->mNumMorphMeshChannels = numMorphMeshChannels; unsigned int i = 0; - for (auto morphAnimIt : morphAnimDatas) { + for (const auto &morphAnimIt : morphAnimDatas) { morphAnimData *animData = morphAnimIt.second; unsigned int numKeys = static_cast(animData->size()); aiMeshMorphAnim *meshMorphAnim = new aiMeshMorphAnim(); @@ -3569,7 +3574,7 @@ void FBXConverter::ConvertOrphanedEmbeddedTextures() { if (texture->Media() && texture->Media()->ContentLength() > 0) { realTexture = texture; } - } + } } } catch (...) { // do nothing diff --git a/code/AssetLib/FBX/FBXConverter.h b/code/AssetLib/FBX/FBXConverter.h index d208ab429..b9a494695 100644 --- a/code/AssetLib/FBX/FBXConverter.h +++ b/code/AssetLib/FBX/FBXConverter.h @@ -76,7 +76,7 @@ namespace Assimp { namespace FBX { class Document; -/** +/** * Convert a FBX #Document to #aiScene * @param out Empty scene to be populated * @param doc Parsed FBX document @@ -182,7 +182,7 @@ private: // ------------------------------------------------------------------------------------------------ void ConvertModel(const Model &model, aiNode *parent, aiNode *root_node, const aiMatrix4x4 &absolute_transform); - + // ------------------------------------------------------------------------------------------------ // MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed std::vector diff --git a/code/AssetLib/FBX/FBXDocument.cpp b/code/AssetLib/FBX/FBXDocument.cpp index 7adaadf6c..8e0439e18 100644 --- a/code/AssetLib/FBX/FBXDocument.cpp +++ b/code/AssetLib/FBX/FBXDocument.cpp @@ -57,9 +57,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include -#include #include #include +#include +#include namespace Assimp { namespace FBX { @@ -248,10 +249,8 @@ Object::~Object() } // ------------------------------------------------------------------------------------------------ -FileGlobalSettings::FileGlobalSettings(const Document& doc, std::shared_ptr props) -: props(props) -, doc(doc) -{ +FileGlobalSettings::FileGlobalSettings(const Document &doc, std::shared_ptr props) : + props(std::move(props)), doc(doc) { // empty } @@ -636,7 +635,7 @@ std::vector Document::GetConnectionsBySourceSequenced(uint64_ } // ------------------------------------------------------------------------------------------------ -std::vector Document::GetConnectionsBySourceSequenced(uint64_t source, +std::vector Document::GetConnectionsBySourceSequenced(uint64_t source, const char* const* classnames, size_t count) const { return GetConnectionsSequenced(source, true, ConnectionsBySource(),classnames, count); diff --git a/code/AssetLib/FBX/FBXDocument.h b/code/AssetLib/FBX/FBXDocument.h index 69cda1c1a..1ee526368 100644 --- a/code/AssetLib/FBX/FBXDocument.h +++ b/code/AssetLib/FBX/FBXDocument.h @@ -500,6 +500,10 @@ public: return uvScaling; } + const ai_real &UVRotation() const { + return uvRotation; + } + const PropertyTable& Props() const { ai_assert(props.get()); return *props.get(); @@ -517,6 +521,7 @@ public: private: aiVector2D uvTrans; aiVector2D uvScaling; + ai_real uvRotation; std::string type; std::string relativeFileName; diff --git a/code/AssetLib/FBX/FBXExportNode.cpp b/code/AssetLib/FBX/FBXExportNode.cpp index 91e421420..817308ec8 100644 --- a/code/AssetLib/FBX/FBXExportNode.cpp +++ b/code/AssetLib/FBX/FBXExportNode.cpp @@ -144,9 +144,8 @@ void FBX::Node::AddP70time( // public member functions for writing nodes to stream void FBX::Node::Dump( - std::shared_ptr outfile, - bool binary, int indent -) { + const std::shared_ptr &outfile, + bool binary, int indent) { if (binary) { Assimp::StreamWriterLE outstream(outfile); DumpBinary(outstream); diff --git a/code/AssetLib/FBX/FBXExportNode.h b/code/AssetLib/FBX/FBXExportNode.h index c5f29ef0f..3aca98939 100644 --- a/code/AssetLib/FBX/FBXExportNode.h +++ b/code/AssetLib/FBX/FBXExportNode.h @@ -60,7 +60,7 @@ namespace FBX { } class FBX::Node { -public: +public: // TODO: accessors std::string name; // node name std::vector properties; // node properties @@ -157,9 +157,8 @@ public: // member functions for writing data to a file or stream // write the full node to the given file or stream void Dump( - std::shared_ptr outfile, - bool binary, int indent - ); + const std::shared_ptr &outfile, + bool binary, int indent); void Dump(Assimp::StreamWriterLE &s, bool binary, int indent); // these other functions are for writing data piece by piece. diff --git a/code/AssetLib/FBX/FBXExporter.cpp b/code/AssetLib/FBX/FBXExporter.cpp index e519f7e77..84a77e18d 100644 --- a/code/AssetLib/FBX/FBXExporter.cpp +++ b/code/AssetLib/FBX/FBXExporter.cpp @@ -498,7 +498,7 @@ void FBXExporter::WriteDocuments () if (!binary) { WriteAsciiSectionHeader("Documents Description"); } - + // not sure what the use of multiple documents would be, // or whether any end-application supports it FBX::Node docs("Documents"); @@ -541,10 +541,17 @@ void FBXExporter::WriteReferences () // (before any actual data is written) // --------------------------------------------------------------- -size_t count_nodes(const aiNode* n) { - size_t count = 1; +size_t count_nodes(const aiNode* n, const aiNode* root) { + size_t count; + if (n == root) { + count = n->mNumMeshes; // (not counting root node) + } else if (n->mNumMeshes > 1) { + count = n->mNumMeshes + 1; + } else { + count = 1; + } for (size_t i = 0; i < n->mNumChildren; ++i) { - count += count_nodes(n->mChildren[i]); + count += count_nodes(n->mChildren[i], root); } return count; } @@ -714,7 +721,7 @@ void FBXExporter::WriteDefinitions () // Model / FbxNode // <~~ node hierarchy - count = int32_t(count_nodes(mScene->mRootNode)) - 1; // (not counting root node) + count = int32_t(count_nodes(mScene->mRootNode, mScene->mRootNode)); if (count) { n = FBX::Node("ObjectType", "Model"); n.AddChild("Count", count); @@ -1251,7 +1258,7 @@ void FBXExporter::WriteObjects () indent = 2; vertexcolors.End(outstream, binary, indent, true); } - + // uvs, if any for (size_t uvi = 0; uvi < m->GetNumUVChannels(); ++uvi) { if (m->mNumUVComponents[uvi] > 2) { @@ -1681,6 +1688,10 @@ void FBXExporter::WriteObjects () // link the image data to the texture connections.emplace_back("C", "OO", image_uid, texture_uid); + aiUVTransform trafo; + unsigned int max = sizeof(aiUVTransform); + aiGetMaterialFloatArray(mat, AI_MATKEY_UVTRANSFORM(aiTextureType_DIFFUSE, 0), (ai_real *)&trafo, &max); + // now write the actual texture node FBX::Node tnode("Texture"); // TODO: some way to determine texture name? @@ -1691,6 +1702,9 @@ void FBXExporter::WriteObjects () tnode.AddChild("Version", int32_t(202)); tnode.AddChild("TextureName", texture_name); FBX::Node p("Properties70"); + p.AddP70vectorA("Translation", trafo.mTranslation[0], trafo.mTranslation[1], 0.0); + p.AddP70vectorA("Rotation", 0, 0, trafo.mRotation); + p.AddP70vectorA("Scaling", trafo.mScaling[0], trafo.mScaling[1], 0.0); p.AddP70enum("CurrentTextureBlendMode", 0); // TODO: verify //p.AddP70string("UVSet", ""); // TODO: how should this work? p.AddP70bool("UseMaterial", 1); @@ -1737,7 +1751,7 @@ void FBXExporter::WriteObjects () bsnode.AddProperty(blendshape_uid); bsnode.AddProperty(blendshape_name + FBX::SEPARATOR + "Blendshape"); bsnode.AddProperty("Shape"); - bsnode.AddChild("Version", int32_t(100)); + bsnode.AddChild("Version", int32_t(100)); bsnode.Begin(outstream, binary, indent); bsnode.DumpProperties(outstream, binary, indent); bsnode.EndProperties(outstream, binary, indent); @@ -1863,7 +1877,7 @@ void FBXExporter::WriteObjects () // at the same time we can build a list of all the skeleton nodes, // which will be used later to mark them as type "limbNode". std::unordered_set limbnodes; - + //actual bone nodes in fbx, without parenting-up std::unordered_set setAllBoneNamesInScene; for(unsigned int m = 0; m < mScene->mNumMeshes; ++ m) @@ -1873,7 +1887,7 @@ void FBXExporter::WriteObjects () setAllBoneNamesInScene.insert(pMesh->mBones[b]->mName.data); } aiMatrix4x4 mxTransIdentity; - + // and a map of nodes by bone name, as finding them is annoying. std::map node_by_bone; for (size_t mi = 0; mi < mScene->mNumMeshes; ++mi) { @@ -1942,7 +1956,7 @@ void FBXExporter::WriteObjects () } if (end) { break; } } - + // if it was the skeleton root we can finish here if (end) { break; } } @@ -2196,7 +2210,65 @@ void FBXExporter::WriteObjects () bpnode.Dump(outstream, binary, indent); }*/ - // TODO: cameras, lights + // lights + indent = 1; + lights_uids.clear(); + for (size_t li = 0; li < mScene->mNumLights; ++li) { + aiLight* l = mScene->mLights[li]; + + int64_t uid = generate_uid(); + const std::string lightNodeAttributeName = l->mName.C_Str() + FBX::SEPARATOR + "NodeAttribute"; + + FBX::Node lna("NodeAttribute"); + lna.AddProperties(uid, lightNodeAttributeName, "Light"); + FBX::Node lnap("Properties70"); + + // Light color. + lnap.AddP70colorA("Color", l->mColorDiffuse.r, l->mColorDiffuse.g, l->mColorDiffuse.b); + + // TODO Assimp light description is quite concise and do not handle light intensity. + // Default value to 1000W. + lnap.AddP70numberA("Intensity", 1000); + + // FBXLight::EType conversion + switch (l->mType) { + case aiLightSource_POINT: + lnap.AddP70enum("LightType", 0); + break; + case aiLightSource_DIRECTIONAL: + lnap.AddP70enum("LightType", 1); + break; + case aiLightSource_SPOT: + lnap.AddP70enum("LightType", 2); + lnap.AddP70numberA("InnerAngle", AI_RAD_TO_DEG(l->mAngleInnerCone)); + lnap.AddP70numberA("OuterAngle", AI_RAD_TO_DEG(l->mAngleOuterCone)); + break; + // TODO Assimp do not handle 'area' nor 'volume' lights, but FBX does. + /*case aiLightSource_AREA: + lnap.AddP70enum("LightType", 3); + lnap.AddP70enum("AreaLightShape", 0); // 0=Rectangle, 1=Sphere + break; + case aiLightSource_VOLUME: + lnap.AddP70enum("LightType", 4); + break;*/ + default: + break; + } + + // Did not understood how to configure the decay so disabling attenuation. + lnap.AddP70enum("DecayType", 0); + + // Dump to FBX stream + lna.AddChild(lnap); + lna.AddChild("TypeFlags", FBX::FBXExportProperty("Light")); + lna.AddChild("GeometryVersion", FBX::FBXExportProperty(int32_t(124))); + lna.Dump(outstream, binary, indent); + + // Store name and uid (will be used later when parsing scene nodes) + lights_uids[l->mName.C_Str()] = uid; + } + + // TODO: cameras // write nodes (i.e. model hierarchy) // start at root node @@ -2600,10 +2672,19 @@ void FBXExporter::WriteModelNodes( // and connect them connections.emplace_back("C", "OO", node_attribute_uid, node_uid); } else { - // generate a null node so we can add children to it - WriteModelNode( - outstream, binary, node, node_uid, "Null", transform_chain - ); + const auto& lightIt = lights_uids.find(node->mName.C_Str()); + if(lightIt != lights_uids.end()) { + // Node has a light connected to it. + WriteModelNode( + outstream, binary, node, node_uid, "Light", transform_chain + ); + connections.emplace_back("C", "OO", lightIt->second, node_uid); + } else { + // generate a null node so we can add children to it + WriteModelNode( + outstream, binary, node, node_uid, "Null", transform_chain + ); + } } // if more than one child mesh, make nodes for each mesh @@ -2625,17 +2706,14 @@ void FBXExporter::WriteModelNodes( ], new_node_uid ); - // write model node - FBX::Node m("Model"); + + aiNode new_node; // take name from mesh name, if it exists - std::string name = mScene->mMeshes[node->mMeshes[i]]->mName.C_Str(); - name += FBX::SEPARATOR + "Model"; - m.AddProperties(new_node_uid, name, "Mesh"); - m.AddChild("Version", int32_t(232)); - FBX::Node p("Properties70"); - p.AddP70enum("InheritType", 1); - m.AddChild(p); - m.Dump(outstream, binary, 1); + new_node.mName = mScene->mMeshes[node->mMeshes[i]]->mName; + // write model node + WriteModelNode( + outstream, binary, &new_node, new_node_uid, "Mesh", std::vector>() + ); } } @@ -2647,16 +2725,14 @@ void FBXExporter::WriteModelNodes( } } - void FBXExporter::WriteAnimationCurveNode( - StreamWriterLE& outstream, - int64_t uid, - const std::string& name, // "T", "R", or "S" - aiVector3D default_value, - std::string property_name, // "Lcl Translation" etc - int64_t layer_uid, - int64_t node_uid -) { + StreamWriterLE &outstream, + int64_t uid, + const std::string &name, // "T", "R", or "S" + aiVector3D default_value, + const std::string &property_name, // "Lcl Translation" etc + int64_t layer_uid, + int64_t node_uid) { FBX::Node n("AnimationCurveNode"); n.AddProperties(uid, name + FBX::SEPARATOR + "AnimCurveNode", ""); FBX::Node p("Properties70"); @@ -2671,7 +2747,6 @@ void FBXExporter::WriteAnimationCurveNode( this->connections.emplace_back("C", "OP", uid, node_uid, property_name); } - void FBXExporter::WriteAnimationCurve( StreamWriterLE& outstream, double default_value, diff --git a/code/AssetLib/FBX/FBXExporter.h b/code/AssetLib/FBX/FBXExporter.h index dcd1d2727..d249b7bee 100644 --- a/code/AssetLib/FBX/FBXExporter.h +++ b/code/AssetLib/FBX/FBXExporter.h @@ -63,10 +63,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. struct aiScene; struct aiNode; -//struct aiMaterial; +struct aiLight; -namespace Assimp -{ +namespace Assimp { class IOSystem; class IOStream; class ExportProperties; @@ -95,6 +94,7 @@ namespace Assimp std::vector mesh_uids; std::vector material_uids; std::map node_uids; + std::map lights_uids; // this crude unique-ID system is actually fine int64_t last_uid = 999999; @@ -154,14 +154,13 @@ namespace Assimp FBX::TransformInheritance ti_type=FBX::TransformInheritance_RSrs ); void WriteAnimationCurveNode( - StreamWriterLE& outstream, - int64_t uid, - const std::string& name, // "T", "R", or "S" - aiVector3D default_value, - std::string property_name, // "Lcl Translation" etc - int64_t animation_layer_uid, - int64_t node_uid - ); + StreamWriterLE &outstream, + int64_t uid, + const std::string &name, // "T", "R", or "S" + aiVector3D default_value, + const std::string &property_name, // "Lcl Translation" etc + int64_t animation_layer_uid, + int64_t node_uid); void WriteAnimationCurve( StreamWriterLE& outstream, double default_value, diff --git a/code/AssetLib/FBX/FBXMaterial.cpp b/code/AssetLib/FBX/FBXMaterial.cpp index 6ada9630b..7eb047177 100644 --- a/code/AssetLib/FBX/FBXMaterial.cpp +++ b/code/AssetLib/FBX/FBXMaterial.cpp @@ -142,8 +142,8 @@ Material::~Material() { // ------------------------------------------------------------------------------------------------ Texture::Texture(uint64_t id, const Element& element, const Document& doc, const std::string& name) : - Object(id,element,name), - uvScaling(1.0f,1.0f), + Object(id,element,name), + uvScaling(1.0f,1.0f), media(0) { const Scope& sc = GetRequiredScope(element); @@ -210,6 +210,11 @@ Texture::Texture(uint64_t id, const Element& element, const Document& doc, const uvTrans.y = trans.y; } + const aiVector3D &rotation = PropertyGet(*props, "Rotation", ok); + if (ok) { + uvRotation = rotation.z; + } + // resolve video links if(doc.Settings().readTextures) { const std::vector& conns = doc.GetConnectionsByDestinationSequenced(ID()); @@ -273,8 +278,8 @@ void LayeredTexture::fillTexture(const Document& doc) { // ------------------------------------------------------------------------------------------------ Video::Video(uint64_t id, const Element& element, const Document& doc, const std::string& name) : - Object(id,element,name), - contentLength(0), + Object(id,element,name), + contentLength(0), content(0) { const Scope& sc = GetRequiredScope(element); diff --git a/code/AssetLib/FBX/FBXMeshGeometry.cpp b/code/AssetLib/FBX/FBXMeshGeometry.cpp index 5aecb61b5..6aeebcbe3 100644 --- a/code/AssetLib/FBX/FBXMeshGeometry.cpp +++ b/code/AssetLib/FBX/FBXMeshGeometry.cpp @@ -633,7 +633,7 @@ void MeshGeometry::ReadVertexDataMaterials(std::vector& materials_out, cons { return; } - + // materials are handled separately. First of all, they are assigned per-face // and not per polyvert. Secondly, ReferenceInformationType=IndexToDirect // has a slightly different meaning for materials. diff --git a/code/AssetLib/FBX/FBXMeshGeometry.h b/code/AssetLib/FBX/FBXMeshGeometry.h index ae17860e3..862693b4b 100644 --- a/code/AssetLib/FBX/FBXMeshGeometry.h +++ b/code/AssetLib/FBX/FBXMeshGeometry.h @@ -52,8 +52,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { namespace FBX { -/** - * DOM base class for all kinds of FBX geometry +/** + * DOM base class for all kinds of FBX geometry */ class Geometry : public Object { @@ -76,7 +76,7 @@ private: typedef std::vector MatIndexArray; -/** +/** * DOM class for FBX geometry of type "Mesh" */ class MeshGeometry : public Geometry @@ -84,7 +84,7 @@ class MeshGeometry : public Geometry public: /** The class constructor */ MeshGeometry( uint64_t id, const Element& element, const std::string& name, const Document& doc ); - + /** The class destructor */ virtual ~MeshGeometry(); diff --git a/code/AssetLib/FBX/FBXParser.cpp b/code/AssetLib/FBX/FBXParser.cpp index 37cf83cf9..582940363 100644 --- a/code/AssetLib/FBX/FBXParser.cpp +++ b/code/AssetLib/FBX/FBXParser.cpp @@ -192,6 +192,10 @@ Scope::Scope(Parser& parser,bool topLevel) } const std::string& str = n->StringContents(); + if (str.empty()) { + ParseError("unexpected content: empty string."); + } + elements.insert(ElementMap::value_type(str,new_Element(*n,parser))); // Element() should stop at the next Key token (or right after a Close token) @@ -642,8 +646,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * (type == 'd' ? 8 : 4); - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -733,8 +736,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * (type == 'd' ? 8 : 4); - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -816,8 +818,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * (type == 'd' ? 8 : 4); - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -892,8 +893,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * 4; - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -954,8 +954,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * (type == 'd' ? 8 : 4); - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -1019,8 +1018,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * 4; - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -1088,8 +1086,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * 8; - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } @@ -1150,8 +1147,7 @@ void ParseVectorDataArray(std::vector& out, const Element& el) ai_assert(data == end); uint64_t dataToRead = static_cast(count) * 8; - ai_assert(buff.size() == dataToRead); - if (dataToRead > buff.size()) { + if (dataToRead != buff.size()) { ParseError("Invalid read size (binary)",&el); } diff --git a/code/AssetLib/FBX/FBXProperties.cpp b/code/AssetLib/FBX/FBXProperties.cpp index 1a5ebffd1..c3f4de260 100644 --- a/code/AssetLib/FBX/FBXProperties.cpp +++ b/code/AssetLib/FBX/FBXProperties.cpp @@ -52,6 +52,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "FBXDocumentUtil.h" #include "FBXProperties.h" +#include + namespace Assimp { namespace FBX { @@ -172,10 +174,8 @@ PropertyTable::PropertyTable() } // ------------------------------------------------------------------------------------------------ -PropertyTable::PropertyTable(const Element& element, std::shared_ptr templateProps) -: templateProps(templateProps) -, element(&element) -{ +PropertyTable::PropertyTable(const Element &element, std::shared_ptr templateProps) : + templateProps(std::move(templateProps)), element(&element) { const Scope& scope = GetRequiredScope(element); for(const ElementMap::value_type& v : scope.Elements()) { if(v.first != "P") { @@ -199,7 +199,6 @@ PropertyTable::PropertyTable(const Element& element, std::shared_ptr > DirectPro typedef std::fbx_unordered_map PropertyMap; typedef std::fbx_unordered_map LazyPropertyMap; -/** +/** * Represents a property table as can be found in the newer FBX files (Properties60, Properties70) */ class PropertyTable { @@ -130,7 +130,7 @@ private: // ------------------------------------------------------------------------------------------------ template -inline +inline T PropertyGet(const PropertyTable& in, const std::string& name, const T& defaultValue) { const Property* const prop = in.Get(name); if( nullptr == prop) { @@ -148,7 +148,7 @@ T PropertyGet(const PropertyTable& in, const std::string& name, const T& default // ------------------------------------------------------------------------------------------------ template -inline +inline T PropertyGet(const PropertyTable& in, const std::string& name, bool& result, bool useTemplate=false ) { const Property* prop = in.Get(name); if( nullptr == prop) { diff --git a/code/AssetLib/FBX/FBXUtil.cpp b/code/AssetLib/FBX/FBXUtil.cpp index 66abf0565..3fe791b97 100644 --- a/code/AssetLib/FBX/FBXUtil.cpp +++ b/code/AssetLib/FBX/FBXUtil.cpp @@ -101,7 +101,7 @@ std::string GetLineAndColumnText(unsigned int line, unsigned int column) std::string GetTokenText(const Token* tok) { if(tok->IsBinary()) { - return static_cast( Formatter::format() << + return static_cast( Formatter::format() << " (" << TokenTypeString(tok->Type()) << ", offset 0x" << std::hex << tok->Offset() << ") " ); } diff --git a/code/AssetLib/HMP/HMPLoader.cpp b/code/AssetLib/HMP/HMPLoader.cpp index cd14cb9c3..97c1858fb 100644 --- a/code/AssetLib/HMP/HMPLoader.cpp +++ b/code/AssetLib/HMP/HMPLoader.cpp @@ -153,10 +153,10 @@ void HMPImporter::InternReadFile(const std::string &pFile, } else { // Print the magic word to the logger std::string szBuffer = ai_str_toprintable((const char *)&iMagic, sizeof(iMagic)); - + delete[] mBuffer; mBuffer = nullptr; - + // We're definitely unable to load this file throw DeadlyImportError("Unknown HMP subformat ", pFile, ". Magic word (", szBuffer, ") is not known"); diff --git a/code/AssetLib/IFC/IFCBoolean.cpp b/code/AssetLib/IFC/IFCBoolean.cpp index 86cac7f46..afd0ad6eb 100644 --- a/code/AssetLib/IFC/IFCBoolean.cpp +++ b/code/AssetLib/IFC/IFCBoolean.cpp @@ -513,7 +513,7 @@ void ProcessPolygonalBoundedBooleanHalfSpaceDifference(const Schema_2x3::IfcPoly } // we got a list of in-out-combinations of intersections. That should be an even number of intersections, or - // we're fucked. + // we are facing a non-recoverable error. if ((intersections.size() & 1) != 0) { IFCImporter::LogWarn("Odd number of intersections, can't work with that. Omitting half space boundary check."); continue; diff --git a/code/AssetLib/IFC/IFCCurve.cpp b/code/AssetLib/IFC/IFCCurve.cpp index 28cd9690c..3ded43bc0 100644 --- a/code/AssetLib/IFC/IFCCurve.cpp +++ b/code/AssetLib/IFC/IFCCurve.cpp @@ -514,7 +514,7 @@ IfcFloat Curve::GetParametricRangeDelta() const { // ------------------------------------------------------------------------------------------------ size_t Curve::EstimateSampleCount(IfcFloat a, IfcFloat b) const { - (void)(a); (void)(b); + (void)(a); (void)(b); ai_assert( InRange( a ) ); ai_assert( InRange( b ) ); diff --git a/code/AssetLib/IFC/IFCGeometry.cpp b/code/AssetLib/IFC/IFCGeometry.cpp index 6e645f1ae..e70c760d8 100644 --- a/code/AssetLib/IFC/IFCGeometry.cpp +++ b/code/AssetLib/IFC/IFCGeometry.cpp @@ -740,7 +740,7 @@ bool ProcessGeometricItem(const Schema_2x3::IfcRepresentationItem& geo, unsigned bool fix_orientation = false; std::shared_ptr< TempMesh > meshtmp = std::make_shared(); if(const Schema_2x3::IfcShellBasedSurfaceModel* shellmod = geo.ToPtr()) { - for(std::shared_ptr shell :shellmod->SbsmBoundary) { + for (const std::shared_ptr &shell : shellmod->SbsmBoundary) { try { const ::Assimp::STEP::EXPRESS::ENTITY& e = shell->To<::Assimp::STEP::EXPRESS::ENTITY>(); const Schema_2x3::IfcConnectedFaceSet& fs = conv.db.MustGetObject(e).To(); diff --git a/code/AssetLib/IFC/IFCMaterial.cpp b/code/AssetLib/IFC/IFCMaterial.cpp index 2a79f0754..c26a3aa0a 100644 --- a/code/AssetLib/IFC/IFCMaterial.cpp +++ b/code/AssetLib/IFC/IFCMaterial.cpp @@ -75,7 +75,7 @@ static void FillMaterial(aiMaterial* mat,const IFC::Schema_2x3::IfcSurfaceStyle* mat->AddProperty(&name,AI_MATKEY_NAME); // now see which kinds of surface information are present - for(std::shared_ptr< const IFC::Schema_2x3::IfcSurfaceStyleElementSelect > sel2 : surf->Styles) { + for (const std::shared_ptr &sel2 : surf->Styles) { if (const IFC::Schema_2x3::IfcSurfaceStyleShading* shade = sel2->ResolveSelectPtr(conv.db)) { aiColor4D col_base,col; @@ -124,7 +124,7 @@ static void FillMaterial(aiMaterial* mat,const IFC::Schema_2x3::IfcSurfaceStyle* } } } - } + } } } @@ -134,7 +134,7 @@ unsigned int ProcessMaterials(uint64_t id, unsigned int prevMatId, ConversionDat for(;range.first != range.second; ++range.first) { if(const IFC::Schema_2x3::IfcStyledItem* const styled = conv.db.GetObject((*range.first).second)->ToPtr()) { for(const IFC::Schema_2x3::IfcPresentationStyleAssignment& as : styled->Styles) { - for(std::shared_ptr sel : as.Styles) { + for (const std::shared_ptr &sel : as.Styles) { if( const IFC::Schema_2x3::IfcSurfaceStyle* const surf = sel->ResolveSelectPtr(conv.db) ) { // try to satisfy from cache diff --git a/code/AssetLib/IFC/IFCOpenings.cpp b/code/AssetLib/IFC/IFCOpenings.cpp index b7665582c..d6671b885 100644 --- a/code/AssetLib/IFC/IFCOpenings.cpp +++ b/code/AssetLib/IFC/IFCOpenings.cpp @@ -911,14 +911,14 @@ size_t CloseWindows(ContourVector& contours, // compare base poly normal and contour normal to detect if we need to reverse the face winding if(curmesh.mVertcnt.size() > 0) { IfcVector3 basePolyNormal = TempMesh::ComputePolygonNormal(curmesh.mVerts.data(), curmesh.mVertcnt.front()); - + std::vector worldSpaceContourVtx(it->contour.size()); - + for(size_t a = 0; a < it->contour.size(); ++a) worldSpaceContourVtx[a] = minv * IfcVector3(it->contour[a].x, it->contour[a].y, 0.0); - + IfcVector3 contourNormal = TempMesh::ComputePolygonNormal(worldSpaceContourVtx.data(), worldSpaceContourVtx.size()); - + reverseCountourFaces = (contourNormal * basePolyNormal) > 0.0; } diff --git a/code/AssetLib/IFC/IFCReaderGen1_2x3.cpp b/code/AssetLib/IFC/IFCReaderGen1_2x3.cpp index 2cfa22530..a6f7ae3eb 100644 --- a/code/AssetLib/IFC/IFCReaderGen1_2x3.cpp +++ b/code/AssetLib/IFC/IFCReaderGen1_2x3.cpp @@ -5,8 +5,8 @@ Open Asset Import Library (ASSIMP) Copyright (c) 2006-2020, ASSIMP Development Team All rights reserved. -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the +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 @@ -23,16 +23,16 @@ following conditions are met: derived from this software without specific prior written permission of the ASSIMP Development Team. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +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 +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 +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 +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. ---------------------------------------------------------------------- @@ -1063,27 +1063,27 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcRoo if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcRoot"); } do { // convert the 'GlobalId' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->GlobalId, arg, db ); break; } + try { GenericConvert( in->GlobalId, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRoot to be a `IfcGloballyUniqueId`")); } } while(0); do { // convert the 'OwnerHistory' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->OwnerHistory, arg, db ); break; } + try { GenericConvert( in->OwnerHistory, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRoot to be a `IfcOwnerHistory`")); } } while(0); do { // convert the 'Name' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcRoot to be a `IfcLabel`")); } } while(0); do { // convert the 'Description' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Description, arg, db ); break; } + try { GenericConvert( in->Description, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRoot to be a `IfcText`")); } } while(0); return base; @@ -1150,27 +1150,27 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcRepresentation"); } do { // convert the 'ContextOfItems' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->ContextOfItems, arg, db ); break; } + try { GenericConvert( in->ContextOfItems, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRepresentation to be a `IfcRepresentationContext`")); } } while(0); do { // convert the 'RepresentationIdentifier' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RepresentationIdentifier, arg, db ); break; } + try { GenericConvert( in->RepresentationIdentifier, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRepresentation to be a `IfcLabel`")); } } while(0); do { // convert the 'RepresentationType' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RepresentationType, arg, db ); break; } + try { GenericConvert( in->RepresentationType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcRepresentation to be a `IfcLabel`")); } } while(0); do { // convert the 'Items' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } - try { GenericConvert( in->Items, arg, db ); break; } + try { GenericConvert( in->Items, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRepresentation to be a `SET [1:?] OF IfcRepresentationItem`")); } } while(0); return base; @@ -1237,7 +1237,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcO std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ObjectType, arg, db ); break; } + try { GenericConvert( in->ObjectType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcObject to be a `IfcLabel`")); } } while(0); return base; @@ -1290,20 +1290,20 @@ template <> size_t GenericFill(const DB& db, const LIS std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcProductRepresentation to be a `IfcLabel`")); } } while(0); do { // convert the 'Description' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Description, arg, db ); break; } + try { GenericConvert( in->Description, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcProductRepresentation to be a `IfcText`")); } } while(0); do { // convert the 'Representations' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->Representations, arg, db ); break; } + try { GenericConvert( in->Representations, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcProductRepresentation to be a `LIST [1:?] OF IfcRepresentation`")); } } while(0); return base; @@ -1316,14 +1316,14 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ObjectPlacement, arg, db ); break; } + try { GenericConvert( in->ObjectPlacement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcProduct to be a `IfcObjectPlacement`")); } } while(0); do { // convert the 'Representation' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Representation, arg, db ); break; } + try { GenericConvert( in->Representation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcProduct to be a `IfcProductRepresentation`")); } } while(0); return base; @@ -1336,7 +1336,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Tag, arg, db ); break; } + try { GenericConvert( in->Tag, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcElement to be a `IfcIdentifier`")); } } while(0); return base; @@ -1374,13 +1374,13 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcCompositeCurve"); } do { // convert the 'Segments' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Segments, arg, db ); break; } + try { GenericConvert( in->Segments, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCompositeCurve to be a `LIST [1:?] OF IfcCompositeCurveSegment`")); } } while(0); do { // convert the 'SelfIntersect' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->SelfIntersect, arg, db ); break; } + try { GenericConvert( in->SelfIntersect, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCompositeCurve to be a `LOGICAL`")); } } while(0); return base; @@ -1400,27 +1400,27 @@ template <> size_t GenericFill(const DB& db, std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis1, arg, db ); break; } + try { GenericConvert( in->Axis1, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCartesianTransformationOperator to be a `IfcDirection`")); } } while(0); do { // convert the 'Axis2' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis2, arg, db ); break; } + try { GenericConvert( in->Axis2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCartesianTransformationOperator to be a `IfcDirection`")); } } while(0); do { // convert the 'LocalOrigin' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->LocalOrigin, arg, db ); break; } + try { GenericConvert( in->LocalOrigin, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcCartesianTransformationOperator to be a `IfcCartesianPoint`")); } } while(0); do { // convert the 'Scale' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Scale, arg, db ); break; } + try { GenericConvert( in->Scale, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcCartesianTransformationOperator to be a `REAL`")); } } while(0); return base; @@ -1433,7 +1433,7 @@ template <> size_t GenericFill(const DB& d std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis3, arg, db ); break; } + try { GenericConvert( in->Axis3, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcCartesianTransformationOperator3D to be a `IfcDirection`")); } } while(0); return base; @@ -1445,14 +1445,14 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcProperty"); } do { // convert the 'Name' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcProperty to be a `IfcIdentifier`")); } } while(0); do { // convert the 'Description' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Description, arg, db ); break; } + try { GenericConvert( in->Description, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcProperty to be a `IfcText`")); } } while(0); return base; @@ -1497,7 +1497,7 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcElementarySurface"); } do { // convert the 'Position' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcElementarySurface to be a `IfcAxis2Placement3D`")); } } while(0); return base; @@ -1515,19 +1515,19 @@ template <> size_t GenericFill(const DB& db, const LIST& param if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcBooleanResult"); } do { // convert the 'Operator' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Operator, arg, db ); break; } + try { GenericConvert( in->Operator, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcBooleanResult to be a `IfcBooleanOperator`")); } } while(0); do { // convert the 'FirstOperand' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->FirstOperand, arg, db ); break; } + try { GenericConvert( in->FirstOperand, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcBooleanResult to be a `IfcBooleanOperand`")); } } while(0); do { // convert the 'SecondOperand' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->SecondOperand, arg, db ); break; } + try { GenericConvert( in->SecondOperand, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcBooleanResult to be a `IfcBooleanOperand`")); } } while(0); return base; @@ -1551,7 +1551,7 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcManifoldSolidBrep"); } do { // convert the 'Outer' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Outer, arg, db ); break; } + try { GenericConvert( in->Outer, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcManifoldSolidBrep to be a `IfcClosedShell`")); } } while(0); return base; @@ -1630,12 +1630,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelFillsElement"); } do { // convert the 'RelatingOpeningElement' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingOpeningElement, arg, db ); break; } + try { GenericConvert( in->RelatingOpeningElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelFillsElement to be a `IfcOpeningElement`")); } } while(0); do { // convert the 'RelatedBuildingElement' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedBuildingElement, arg, db ); break; } + try { GenericConvert( in->RelatedBuildingElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelFillsElement to be a `IfcElement`")); } } while(0); return base; @@ -1681,12 +1681,12 @@ template <> size_t GenericFill(const DB& db, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelContainedInSpatialStructure"); } do { // convert the 'RelatedElements' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedElements, arg, db ); break; } + try { GenericConvert( in->RelatedElements, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelContainedInSpatialStructure to be a `SET [1:?] OF IfcProduct`")); } } while(0); do { // convert the 'RelatingStructure' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingStructure, arg, db ); break; } + try { GenericConvert( in->RelatingStructure, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelContainedInSpatialStructure to be a `IfcSpatialStructureElement`")); } } while(0); return base; @@ -1772,7 +1772,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcDirection"); } do { // convert the 'DirectionRatios' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->DirectionRatios, arg, db ); break; } + try { GenericConvert( in->DirectionRatios, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcDirection to be a `LIST [2:3] OF REAL`")); } } while(0); return base; @@ -1784,14 +1784,14 @@ template <> size_t GenericFill(const DB& db, const LIST& params, if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcProfileDef"); } do { // convert the 'ProfileType' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->ProfileType, arg, db ); break; } + try { GenericConvert( in->ProfileType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcProfileDef to be a `IfcProfileTypeEnum`")); } } while(0); do { // convert the 'ProfileName' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ProfileName, arg, db ); break; } + try { GenericConvert( in->ProfileName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcProfileDef to be a `IfcLabel`")); } } while(0); return base; @@ -1803,7 +1803,7 @@ template <> size_t GenericFill(const DB& db, const L if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcParameterizedProfileDef"); } do { // convert the 'Position' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcParameterizedProfileDef to be a `IfcAxis2Placement2D`")); } } while(0); return base; @@ -1910,7 +1910,7 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcCircleProfileDef"); } do { // convert the 'Radius' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Radius, arg, db ); break; } + try { GenericConvert( in->Radius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcCircleProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -1921,7 +1921,7 @@ template <> size_t GenericFill(const DB& db, const LI size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcCircleHollowProfileDef"); } do { // convert the 'WallThickness' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->WallThickness, arg, db ); break; } + try { GenericConvert( in->WallThickness, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcCircleHollowProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -1933,7 +1933,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPlacement"); } do { // convert the 'Location' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Location, arg, db ); break; } + try { GenericConvert( in->Location, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPlacement to be a `IfcCartesianPoint`")); } } while(0); return base; @@ -1945,13 +1945,13 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcAxis2Placement3D"); } do { // convert the 'Axis' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis, arg, db ); break; } + try { GenericConvert( in->Axis, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcAxis2Placement3D to be a `IfcDirection`")); } } while(0); do { // convert the 'RefDirection' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefDirection, arg, db ); break; } + try { GenericConvert( in->RefDirection, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcAxis2Placement3D to be a `IfcDirection`")); } } while(0); return base; @@ -1964,7 +1964,7 @@ template <> size_t GenericFill(const DB& db, const LIST& p std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPresentationStyle to be a `IfcLabel`")); } } while(0); return base; @@ -1982,17 +1982,17 @@ template <> size_t GenericFill(const DB& db, const LIS size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcCompositeCurveSegment"); } do { // convert the 'Transition' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Transition, arg, db ); break; } + try { GenericConvert( in->Transition, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCompositeCurveSegment to be a `IfcTransitionCode`")); } } while(0); do { // convert the 'SameSense' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->SameSense, arg, db ); break; } + try { GenericConvert( in->SameSense, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCompositeCurveSegment to be a `BOOLEAN`")); } } while(0); do { // convert the 'ParentCurve' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ParentCurve, arg, db ); break; } + try { GenericConvert( in->ParentCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcCompositeCurveSegment to be a `IfcCurve`")); } } while(0); return base; @@ -2004,13 +2004,13 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcRectangleProfileDef"); } do { // convert the 'XDim' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->XDim, arg, db ); break; } + try { GenericConvert( in->XDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRectangleProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'YDim' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->YDim, arg, db ); break; } + try { GenericConvert( in->YDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRectangleProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2106,12 +2106,12 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcLocalPlacement"); } do { // convert the 'PlacementRelTo' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->PlacementRelTo, arg, db ); break; } + try { GenericConvert( in->PlacementRelTo, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcLocalPlacement to be a `IfcObjectPlacement`")); } } while(0); do { // convert the 'RelativePlacement' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelativePlacement, arg, db ); break; } + try { GenericConvert( in->RelativePlacement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcLocalPlacement to be a `IfcAxis2Placement`")); } } while(0); return base; @@ -2123,13 +2123,13 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcSweptAreaSolid"); } do { // convert the 'SweptArea' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->SweptArea, arg, db ); break; } + try { GenericConvert( in->SweptArea, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSweptAreaSolid to be a `IfcProfileDef`")); } } while(0); do { // convert the 'Position' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSweptAreaSolid to be a `IfcAxis2Placement3D`")); } } while(0); return base; @@ -2140,12 +2140,12 @@ template <> size_t GenericFill(const DB& db, const LIST& p size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcRevolvedAreaSolid"); } do { // convert the 'Axis' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Axis, arg, db ); break; } + try { GenericConvert( in->Axis, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcRevolvedAreaSolid to be a `IfcAxis1Placement`")); } } while(0); do { // convert the 'Angle' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Angle, arg, db ); break; } + try { GenericConvert( in->Angle, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRevolvedAreaSolid to be a `IfcPlaneAngleMeasure`")); } } while(0); return base; @@ -2170,28 +2170,28 @@ template <> size_t GenericFill(const DB& db, const LIST& para size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcSweptDiskSolid"); } do { // convert the 'Directrix' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Directrix, arg, db ); break; } + try { GenericConvert( in->Directrix, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSweptDiskSolid to be a `IfcCurve`")); } } while(0); do { // convert the 'Radius' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Radius, arg, db ); break; } + try { GenericConvert( in->Radius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSweptDiskSolid to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'InnerRadius' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->InnerRadius, arg, db ); break; } + try { GenericConvert( in->InnerRadius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSweptDiskSolid to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'StartParam' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->StartParam, arg, db ); break; } + try { GenericConvert( in->StartParam, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcSweptDiskSolid to be a `IfcParameterValue`")); } } while(0); do { // convert the 'EndParam' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->EndParam, arg, db ); break; } + try { GenericConvert( in->EndParam, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcSweptDiskSolid to be a `IfcParameterValue`")); } } while(0); return base; @@ -2203,13 +2203,13 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcHalfSpaceSolid"); } do { // convert the 'BaseSurface' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->BaseSurface, arg, db ); break; } + try { GenericConvert( in->BaseSurface, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcHalfSpaceSolid to be a `IfcSurface`")); } } while(0); do { // convert the 'AgreementFlag' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->AgreementFlag, arg, db ); break; } + try { GenericConvert( in->AgreementFlag, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcHalfSpaceSolid to be a `BOOLEAN`")); } } while(0); return base; @@ -2220,12 +2220,12 @@ template <> size_t GenericFill(const DB& db, const size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcPolygonalBoundedHalfSpace"); } do { // convert the 'Position' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcPolygonalBoundedHalfSpace to be a `IfcAxis2Placement3D`")); } } while(0); do { // convert the 'PolygonalBoundary' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->PolygonalBoundary, arg, db ); break; } + try { GenericConvert( in->PolygonalBoundary, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcPolygonalBoundedHalfSpace to be a `IfcBoundedCurve`")); } } while(0); return base; @@ -2251,23 +2251,23 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc if (params.GetSize() < 9) { throw STEP::TypeError("expected 9 arguments to IfcProject"); } do { // convert the 'LongName' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->LongName, arg, db ); break; } + try { GenericConvert( in->LongName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcProject to be a `IfcLabel`")); } } while(0); do { // convert the 'Phase' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Phase, arg, db ); break; } + try { GenericConvert( in->Phase, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcProject to be a `IfcLabel`")); } } while(0); do { // convert the 'RepresentationContexts' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RepresentationContexts, arg, db ); break; } + try { GenericConvert( in->RepresentationContexts, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcProject to be a `SET [1:?] OF IfcRepresentationContext`")); } } while(0); do { // convert the 'UnitsInContext' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->UnitsInContext, arg, db ); break; } + try { GenericConvert( in->UnitsInContext, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcProject to be a `IfcUnitAssignment`")); } } while(0); return base; @@ -2327,27 +2327,27 @@ template <> size_t GenericFill(const DB& db, const LIST& params size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcTrimmedCurve"); } do { // convert the 'BasisCurve' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->BasisCurve, arg, db ); break; } + try { GenericConvert( in->BasisCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcTrimmedCurve to be a `IfcCurve`")); } } while(0); do { // convert the 'Trim1' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Trim1, arg, db ); break; } + try { GenericConvert( in->Trim1, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcTrimmedCurve to be a `SET [1:2] OF IfcTrimmingSelect`")); } } while(0); do { // convert the 'Trim2' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Trim2, arg, db ); break; } + try { GenericConvert( in->Trim2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcTrimmedCurve to be a `SET [1:2] OF IfcTrimmingSelect`")); } } while(0); do { // convert the 'SenseAgreement' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->SenseAgreement, arg, db ); break; } + try { GenericConvert( in->SenseAgreement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcTrimmedCurve to be a `BOOLEAN`")); } } while(0); do { // convert the 'MasterRepresentation' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->MasterRepresentation, arg, db ); break; } + try { GenericConvert( in->MasterRepresentation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcTrimmedCurve to be a `IfcTrimmingPreference`")); } } while(0); return base; @@ -2359,7 +2359,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcRelDefines"); } do { // convert the 'RelatedObjects' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->RelatedObjects, arg, db ); break; } + try { GenericConvert( in->RelatedObjects, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelDefines to be a `SET [1:?] OF IfcObject`")); } } while(0); return base; @@ -2371,7 +2371,7 @@ template <> size_t GenericFill(const DB& db, const LI if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelDefinesByProperties"); } do { // convert the 'RelatingPropertyDefinition' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->RelatingPropertyDefinition, arg, db ); break; } + try { GenericConvert( in->RelatingPropertyDefinition, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelDefinesByProperties to be a `IfcPropertySetDefinition`")); } } while(0); return base; @@ -2404,7 +2404,7 @@ template <> size_t GenericFill(const DB& db, const L if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcArbitraryOpenProfileDef"); } do { // convert the 'Curve' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Curve, arg, db ); break; } + try { GenericConvert( in->Curve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcArbitraryOpenProfileDef to be a `IfcBoundedCurve`")); } } while(0); return base; @@ -2570,13 +2570,13 @@ template <> size_t GenericFill(const DB& db, const LIST& param if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelDecomposes"); } do { // convert the 'RelatingObject' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->RelatingObject, arg, db ); break; } + try { GenericConvert( in->RelatingObject, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelDecomposes to be a `IfcObjectDefinition`")); } } while(0); do { // convert the 'RelatedObjects' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->RelatedObjects, arg, db ); break; } + try { GenericConvert( in->RelatedObjects, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelDecomposes to be a `SET [1:?] OF IfcObjectDefinition`")); } } while(0); return base; @@ -2594,7 +2594,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPolyline"); } do { // convert the 'Points' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Points, arg, db ); break; } + try { GenericConvert( in->Points, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPolyline to be a `LIST [2:?] OF IfcCartesianPoint`")); } } while(0); return base; @@ -2626,12 +2626,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcMappedItem"); } do { // convert the 'MappingSource' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappingSource, arg, db ); break; } + try { GenericConvert( in->MappingSource, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcMappedItem to be a `IfcRepresentationMap`")); } } while(0); do { // convert the 'MappingTarget' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappingTarget, arg, db ); break; } + try { GenericConvert( in->MappingTarget, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcMappedItem to be a `IfcCartesianTransformationOperator`")); } } while(0); return base; @@ -2658,13 +2658,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Dimensions, arg, db ); break; } + try { GenericConvert( in->Dimensions, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcNamedUnit to be a `IfcDimensionalExponents`")); } } while(0); do { // convert the 'UnitType' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->UnitType, arg, db ); break; } + try { GenericConvert( in->UnitType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcNamedUnit to be a `IfcUnitEnum`")); } } while(0); return base; @@ -2719,13 +2719,13 @@ template <> size_t GenericFill(const DB& db, const L std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->LongName, arg, db ); break; } + try { GenericConvert( in->LongName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcSpatialStructureElement to be a `IfcLabel`")); } } while(0); do { // convert the 'CompositionType' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->CompositionType, arg, db ); break; } + try { GenericConvert( in->CompositionType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcSpatialStructureElement to be a `IfcElementCompositionEnum`")); } } while(0); return base; @@ -2737,19 +2737,19 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If if (params.GetSize() < 12) { throw STEP::TypeError("expected 12 arguments to IfcBuilding"); } do { // convert the 'ElevationOfRefHeight' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ElevationOfRefHeight, arg, db ); break; } + try { GenericConvert( in->ElevationOfRefHeight, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcBuilding to be a `IfcLengthMeasure`")); } } while(0); do { // convert the 'ElevationOfTerrain' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ElevationOfTerrain, arg, db ); break; } + try { GenericConvert( in->ElevationOfTerrain, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcBuilding to be a `IfcLengthMeasure`")); } } while(0); do { // convert the 'BuildingAddress' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->BuildingAddress, arg, db ); break; } + try { GenericConvert( in->BuildingAddress, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 11 to IfcBuilding to be a `IfcPostalAddress`")); } } while(0); return base; @@ -2761,7 +2761,7 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcConnectedFaceSet"); } do { // convert the 'CfsFaces' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->CfsFaces, arg, db ); break; } + try { GenericConvert( in->CfsFaces, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcConnectedFaceSet to be a `SET [1:?] OF IfcFace`")); } } while(0); return base; @@ -2787,7 +2787,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcCo if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcConic"); } do { // convert the 'Position' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcConic to be a `IfcAxis2Placement`")); } } while(0); return base; @@ -2834,32 +2834,32 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 8) { throw STEP::TypeError("expected 8 arguments to IfcIShapeProfileDef"); } do { // convert the 'OverallWidth' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->OverallWidth, arg, db ); break; } + try { GenericConvert( in->OverallWidth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'OverallDepth' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->OverallDepth, arg, db ); break; } + try { GenericConvert( in->OverallDepth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'WebThickness' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->WebThickness, arg, db ); break; } + try { GenericConvert( in->WebThickness, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'FlangeThickness' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } - try { GenericConvert( in->FlangeThickness, arg, db ); break; } + try { GenericConvert( in->FlangeThickness, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'FilletRadius' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[4]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->FilletRadius, arg, db ); break; } + try { GenericConvert( in->FilletRadius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2933,13 +2933,13 @@ template <> size_t GenericFill(const DB& db, const LIST& p size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcPropertyListValue"); } do { // convert the 'ListValues' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ListValues, arg, db ); break; } + try { GenericConvert( in->ListValues, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcPropertyListValue to be a `LIST [1:?] OF IfcValue`")); } } while(0); do { // convert the 'Unit' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Unit, arg, db ); break; } + try { GenericConvert( in->Unit, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcPropertyListValue to be a `IfcUnit`")); } } while(0); return base; @@ -2965,13 +2965,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcDoo if (params.GetSize() < 10) { throw STEP::TypeError("expected 10 arguments to IfcDoor"); } do { // convert the 'OverallHeight' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->OverallHeight, arg, db ); break; } + try { GenericConvert( in->OverallHeight, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcDoor to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'OverallWidth' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->OverallWidth, arg, db ); break; } + try { GenericConvert( in->OverallWidth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcDoor to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2984,20 +2984,20 @@ template <> size_t GenericFill(const DB& db, const LIST& params, std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Item, arg, db ); break; } + try { GenericConvert( in->Item, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcStyledItem to be a `IfcRepresentationItem`")); } } while(0); do { // convert the 'Styles' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Styles, arg, db ); break; } + try { GenericConvert( in->Styles, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcStyledItem to be a `SET [1:?] OF IfcPresentationStyleAssignment`")); } } while(0); do { // convert the 'Name' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcStyledItem to be a `IfcLabel`")); } } while(0); return base; @@ -3023,7 +3023,7 @@ template <> size_t GenericFill(const DB& db, const if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcArbitraryClosedProfileDef"); } do { // convert the 'OuterCurve' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->OuterCurve, arg, db ); break; } + try { GenericConvert( in->OuterCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcArbitraryClosedProfileDef to be a `IfcCurve`")); } } while(0); return base; @@ -3041,12 +3041,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcLin size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcLine"); } do { // convert the 'Pnt' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Pnt, arg, db ); break; } + try { GenericConvert( in->Pnt, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcLine to be a `IfcCartesianPoint`")); } } while(0); do { // convert the 'Dir' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Dir, arg, db ); break; } + try { GenericConvert( in->Dir, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcLine to be a `IfcVector`")); } } while(0); return base; @@ -3072,13 +3072,13 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcPropertySingleValue"); } do { // convert the 'NominalValue' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->NominalValue, arg, db ); break; } + try { GenericConvert( in->NominalValue, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcPropertySingleValue to be a `IfcValue`")); } } while(0); do { // convert the 'Unit' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Unit, arg, db ); break; } + try { GenericConvert( in->Unit, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcPropertySingleValue to be a `IfcUnit`")); } } while(0); return base; @@ -3111,7 +3111,7 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcSurfaceStyleShading"); } do { // convert the 'SurfaceColour' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->SurfaceColour, arg, db ); break; } + try { GenericConvert( in->SurfaceColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSurfaceStyleShading to be a `IfcColourRgb`")); } } while(0); return base; diff --git a/code/AssetLib/IFC/IFCReaderGen2_2x3.cpp b/code/AssetLib/IFC/IFCReaderGen2_2x3.cpp index c58c7c42f..0d7051195 100644 --- a/code/AssetLib/IFC/IFCReaderGen2_2x3.cpp +++ b/code/AssetLib/IFC/IFCReaderGen2_2x3.cpp @@ -5,8 +5,8 @@ Open Asset Import Library (ASSIMP) Copyright (c) 2006-2020, ASSIMP Development Team All rights reserved. -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the +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 @@ -23,16 +23,16 @@ following conditions are met: derived from this software without specific prior written permission of the ASSIMP Development Team. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +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 +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 +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 +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. ---------------------------------------------------------------------- @@ -59,12 +59,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcSurfaceStyle"); } do { // convert the 'Side' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Side, arg, db ); break; } + try { GenericConvert( in->Side, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSurfaceStyle to be a `IfcSurfaceSide`")); } } while(0); do { // convert the 'Styles' argument std::shared_ptr arg = params[ base++ ]; - try { GenericConvert( in->Styles, arg, db ); break; } + try { GenericConvert( in->Styles, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSurfaceStyle to be a `SET [1:5] OF IfcSurfaceStyleElementSelect`")); } } while(0); return base; @@ -118,7 +118,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcFac if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcFace"); } do { // convert the 'Bounds' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Bounds, arg, db ); break; } + try { GenericConvert( in->Bounds, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcFace to be a `SET [1:?] OF IfcFaceBound`")); } } while(0); return base; @@ -173,7 +173,7 @@ template <> size_t GenericFill(const DB& db, const LIST& std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcColourSpecification to be a `IfcLabel`")); } } while(0); return base; @@ -184,12 +184,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcV size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcVector"); } do { // convert the 'Orientation' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Orientation, arg, db ); break; } + try { GenericConvert( in->Orientation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcVector to be a `IfcDirection`")); } } while(0); do { // convert the 'Magnitude' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Magnitude, arg, db ); break; } + try { GenericConvert( in->Magnitude, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcVector to be a `IfcLengthMeasure`")); } } while(0); return base; @@ -207,17 +207,17 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcColourRgb"); } do { // convert the 'Red' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Red, arg, db ); break; } + try { GenericConvert( in->Red, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcColourRgb to be a `IfcNormalisedRatioMeasure`")); } } while(0); do { // convert the 'Green' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Green, arg, db ); break; } + try { GenericConvert( in->Green, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcColourRgb to be a `IfcNormalisedRatioMeasure`")); } } while(0); do { // convert the 'Blue' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Blue, arg, db ); break; } + try { GenericConvert( in->Blue, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcColourRgb to be a `IfcNormalisedRatioMeasure`")); } } while(0); return base; @@ -243,31 +243,31 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcSit if (params.GetSize() < 14) { throw STEP::TypeError("expected 14 arguments to IfcSite"); } do { // convert the 'RefLatitude' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefLatitude, arg, db ); break; } + try { GenericConvert( in->RefLatitude, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcSite to be a `IfcCompoundPlaneAngleMeasure`")); } } while(0); do { // convert the 'RefLongitude' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefLongitude, arg, db ); break; } + try { GenericConvert( in->RefLongitude, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcSite to be a `IfcCompoundPlaneAngleMeasure`")); } } while(0); do { // convert the 'RefElevation' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefElevation, arg, db ); break; } + try { GenericConvert( in->RefElevation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 11 to IfcSite to be a `IfcLengthMeasure`")); } } while(0); do { // convert the 'LandTitleNumber' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->LandTitleNumber, arg, db ); break; } + try { GenericConvert( in->LandTitleNumber, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 12 to IfcSite to be a `IfcLabel`")); } } while(0); do { // convert the 'SiteAddress' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->SiteAddress, arg, db ); break; } + try { GenericConvert( in->SiteAddress, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 13 to IfcSite to be a `IfcPostalAddress`")); } } while(0); return base; @@ -412,31 +412,31 @@ template <> size_t GenericFill(const DB& db, const LIST& params if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcBSplineCurve"); } do { // convert the 'Degree' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Degree, arg, db ); break; } + try { GenericConvert( in->Degree, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcBSplineCurve to be a `INTEGER`")); } } while(0); do { // convert the 'ControlPointsList' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->ControlPointsList, arg, db ); break; } + try { GenericConvert( in->ControlPointsList, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcBSplineCurve to be a `LIST [2:?] OF IfcCartesianPoint`")); } } while(0); do { // convert the 'CurveForm' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->CurveForm, arg, db ); break; } + try { GenericConvert( in->CurveForm, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcBSplineCurve to be a `IfcBSplineCurveForm`")); } } while(0); do { // convert the 'ClosedCurve' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } - try { GenericConvert( in->ClosedCurve, arg, db ); break; } + try { GenericConvert( in->ClosedCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcBSplineCurve to be a `LOGICAL`")); } } while(0); do { // convert the 'SelfIntersect' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[4]=true; break; } - try { GenericConvert( in->SelfIntersect, arg, db ); break; } + try { GenericConvert( in->SelfIntersect, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcBSplineCurve to be a `LOGICAL`")); } } while(0); return base; @@ -474,7 +474,7 @@ template <> size_t GenericFill(const DB& db, const LI size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcShellBasedSurfaceModel"); } do { // convert the 'SbsmBoundary' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->SbsmBoundary, arg, db ); break; } + try { GenericConvert( in->SbsmBoundary, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcShellBasedSurfaceModel to be a `SET [1:?] OF IfcShell`")); } } while(0); return base; @@ -492,12 +492,12 @@ template <> size_t GenericFill(const DB& db, const LIST& p size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcExtrudedAreaSolid"); } do { // convert the 'ExtrudedDirection' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ExtrudedDirection, arg, db ); break; } + try { GenericConvert( in->ExtrudedDirection, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcExtrudedAreaSolid to be a `IfcDirection`")); } } while(0); do { // convert the 'Depth' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Depth, arg, db ); break; } + try { GenericConvert( in->Depth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcExtrudedAreaSolid to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -522,12 +522,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelVoidsElement"); } do { // convert the 'RelatingBuildingElement' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingBuildingElement, arg, db ); break; } + try { GenericConvert( in->RelatingBuildingElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelVoidsElement to be a `IfcElement`")); } } while(0); do { // convert the 'RelatedOpeningElement' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedOpeningElement, arg, db ); break; } + try { GenericConvert( in->RelatedOpeningElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelVoidsElement to be a `IfcFeatureElementSubtraction`")); } } while(0); return base; @@ -546,13 +546,13 @@ template <> size_t GenericFill(c if (params.GetSize() < 7) { throw STEP::TypeError("expected 7 arguments to IfcCartesianTransformationOperator3DnonUniform"); } do { // convert the 'Scale2' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Scale2, arg, db ); break; } + try { GenericConvert( in->Scale2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcCartesianTransformationOperator3DnonUniform to be a `REAL`")); } } while(0); do { // convert the 'Scale3' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Scale3, arg, db ); break; } + try { GenericConvert( in->Scale3, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcCartesianTransformationOperator3DnonUniform to be a `REAL`")); } } while(0); return base; @@ -634,7 +634,7 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcAxis2Placement2D"); } do { // convert the 'RefDirection' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefDirection, arg, db ); break; } + try { GenericConvert( in->RefDirection, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcAxis2Placement2D to be a `IfcDirection`")); } } while(0); return base; @@ -658,7 +658,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcCartesianPoint"); } do { // convert the 'Coordinates' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Coordinates, arg, db ); break; } + try { GenericConvert( in->Coordinates, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCartesianPoint to be a `LIST [1:3] OF IfcLengthMeasure`")); } } while(0); return base; @@ -682,7 +682,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPolyLoop"); } do { // convert the 'Polygon' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Polygon, arg, db ); break; } + try { GenericConvert( in->Polygon, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPolyLoop to be a `LIST [3:?] OF IfcCartesianPoint`")); } } while(0); return base; @@ -716,14 +716,14 @@ template <> size_t GenericFill(const DB& db, const LIS std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ContextIdentifier, arg, db ); break; } + try { GenericConvert( in->ContextIdentifier, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRepresentationContext to be a `IfcLabel`")); } } while(0); do { // convert the 'ContextType' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ContextType, arg, db ); break; } + try { GenericConvert( in->ContextType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRepresentationContext to be a `IfcLabel`")); } } while(0); return base; @@ -735,27 +735,27 @@ template <> size_t GenericFill(const DB& db, if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcGeometricRepresentationContext"); } do { // convert the 'CoordinateSpaceDimension' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->CoordinateSpaceDimension, arg, db ); break; } + try { GenericConvert( in->CoordinateSpaceDimension, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcGeometricRepresentationContext to be a `IfcDimensionCount`")); } } while(0); do { // convert the 'Precision' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Precision, arg, db ); break; } + try { GenericConvert( in->Precision, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcGeometricRepresentationContext to be a `REAL`")); } } while(0); do { // convert the 'WorldCoordinateSystem' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->WorldCoordinateSystem, arg, db ); break; } + try { GenericConvert( in->WorldCoordinateSystem, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcGeometricRepresentationContext to be a `IfcAxis2Placement`")); } } while(0); do { // convert the 'TrueNorth' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->TrueNorth, arg, db ); break; } + try { GenericConvert( in->TrueNorth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcGeometricRepresentationContext to be a `IfcDirection`")); } } while(0); return base; @@ -774,12 +774,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcS if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcSIUnit"); } do { // convert the 'Prefix' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Prefix, arg, db ); break; } + try { GenericConvert( in->Prefix, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSIUnit to be a `IfcSIPrefix`")); } } while(0); do { // convert the 'Name' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcSIUnit to be a `IfcSIUnitName`")); } } while(0); return base; @@ -805,7 +805,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcAxis1Placement"); } do { // convert the 'Axis' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis, arg, db ); break; } + try { GenericConvert( in->Axis, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcAxis1Placement to be a `IfcDirection`")); } } while(0); return base; @@ -858,12 +858,12 @@ template <> size_t GenericFill(const DB& db, const LIST& p size_t base = 0; if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcRepresentationMap"); } do { // convert the 'MappingOrigin' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappingOrigin, arg, db ); break; } + try { GenericConvert( in->MappingOrigin, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRepresentationMap to be a `IfcAxis2Placement`")); } } while(0); do { // convert the 'MappedRepresentation' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappedRepresentation, arg, db ); break; } + try { GenericConvert( in->MappedRepresentation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRepresentationMap to be a `IfcRepresentation`")); } } while(0); return base; @@ -1012,12 +1012,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = 0; if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcMeasureWithUnit"); } do { // convert the 'ValueComponent' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ValueComponent, arg, db ); break; } + try { GenericConvert( in->ValueComponent, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcMeasureWithUnit to be a `IfcValue`")); } } while(0); do { // convert the 'UnitComponent' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->UnitComponent, arg, db ); break; } + try { GenericConvert( in->UnitComponent, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcMeasureWithUnit to be a `IfcUnit`")); } } while(0); return base; @@ -1125,7 +1125,7 @@ template <> size_t GenericFill(const DB& db, const LIS size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcFaceBasedSurfaceModel"); } do { // convert the 'FbsmFaces' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->FbsmFaces, arg, db ); break; } + try { GenericConvert( in->FbsmFaces, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcFaceBasedSurfaceModel to be a `SET [1:?] OF IfcConnectedFaceSet`")); } } while(0); return base; @@ -1172,13 +1172,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcFaceBound"); } do { // convert the 'Bound' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Bound, arg, db ); break; } + try { GenericConvert( in->Bound, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcFaceBound to be a `IfcLoop`")); } } while(0); do { // convert the 'Orientation' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Orientation, arg, db ); break; } + try { GenericConvert( in->Orientation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcFaceBound to be a `BOOLEAN`")); } } while(0); return base; @@ -1216,12 +1216,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcComplexProperty"); } do { // convert the 'UsageName' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->UsageName, arg, db ); break; } + try { GenericConvert( in->UsageName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcComplexProperty to be a `IfcIdentifier`")); } } while(0); do { // convert the 'HasProperties' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->HasProperties, arg, db ); break; } + try { GenericConvert( in->HasProperties, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcComplexProperty to be a `SET [1:?] OF IfcProperty`")); } } while(0); return base; @@ -1274,7 +1274,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para size_t base = 0; if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcUnitAssignment"); } do { // convert the 'Units' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Units, arg, db ); break; } + try { GenericConvert( in->Units, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcUnitAssignment to be a `SET [1:?] OF IfcUnit`")); } } while(0); return base; @@ -1307,12 +1307,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcElementQuantity"); } do { // convert the 'MethodOfMeasurement' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->MethodOfMeasurement, arg, db ); break; } + try { GenericConvert( in->MethodOfMeasurement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcElementQuantity to be a `IfcLabel`")); } } while(0); do { // convert the 'Quantities' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Quantities, arg, db ); break; } + try { GenericConvert( in->Quantities, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcElementQuantity to be a `SET [1:?] OF IfcPhysicalQuantity`")); } } while(0); return base; @@ -1379,7 +1379,7 @@ template <> size_t GenericFill(const DB& db, con size_t base = 0; if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPresentationStyleAssignment"); } do { // convert the 'Styles' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Styles, arg, db ); break; } + try { GenericConvert( in->Styles, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPresentationStyleAssignment to be a `SET [1:?] OF IfcPresentationStyleSelect`")); } } while(0); return base; @@ -1418,13 +1418,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcSp size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 11) { throw STEP::TypeError("expected 11 arguments to IfcSpace"); } do { // convert the 'InteriorOrExteriorSpace' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->InteriorOrExteriorSpace, arg, db ); break; } + try { GenericConvert( in->InteriorOrExteriorSpace, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcSpace to be a `IfcInternalOrExternalEnum`")); } } while(0); do { // convert the 'ElevationWithFlooring' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ElevationWithFlooring, arg, db ); break; } + try { GenericConvert( in->ElevationWithFlooring, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcSpace to be a `IfcLengthMeasure`")); } } while(0); return base; @@ -1484,7 +1484,7 @@ template <> size_t GenericFill(const DB& db, const size_t base = 0; if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcSurfaceStyleWithTextures"); } do { // convert the 'Textures' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Textures, arg, db ); break; } + try { GenericConvert( in->Textures, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSurfaceStyleWithTextures to be a `LIST [1:?] OF IfcSurfaceTexture`")); } } while(0); return base; @@ -1495,22 +1495,22 @@ template <> size_t GenericFill(const DB& db, const LIST& params, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcBoundingBox"); } do { // convert the 'Corner' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Corner, arg, db ); break; } + try { GenericConvert( in->Corner, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcBoundingBox to be a `IfcCartesianPoint`")); } } while(0); do { // convert the 'XDim' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->XDim, arg, db ); break; } + try { GenericConvert( in->XDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcBoundingBox to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'YDim' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->YDim, arg, db ); break; } + try { GenericConvert( in->YDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcBoundingBox to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'ZDim' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ZDim, arg, db ); break; } + try { GenericConvert( in->ZDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcBoundingBox to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -1535,7 +1535,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcC size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcCircle"); } do { // convert the 'Radius' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Radius, arg, db ); break; } + try { GenericConvert( in->Radius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCircle to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -1623,12 +1623,12 @@ template <> size_t GenericFill(const DB& db, const LIST& size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcConversionBasedUnit"); } do { // convert the 'Name' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcConversionBasedUnit to be a `IfcLabel`")); } } while(0); do { // convert the 'ConversionFactor' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ConversionFactor, arg, db ); break; } + try { GenericConvert( in->ConversionFactor, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcConversionBasedUnit to be a `IfcMeasureWithUnit`")); } } while(0); return base; @@ -1744,12 +1744,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcEllipse"); } do { // convert the 'SemiAxis1' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->SemiAxis1, arg, db ); break; } + try { GenericConvert( in->SemiAxis1, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcEllipse to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'SemiAxis2' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->SemiAxis2, arg, db ); break; } + try { GenericConvert( in->SemiAxis2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcEllipse to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -1816,7 +1816,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcPropertySet"); } do { // convert the 'HasProperties' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->HasProperties, arg, db ); break; } + try { GenericConvert( in->HasProperties, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcPropertySet to be a `SET [1:?] OF IfcProperty`")); } } while(0); return base; @@ -1828,48 +1828,48 @@ template <> size_t GenericFill(const DB& db, const LIS if (params.GetSize() < 9) { throw STEP::TypeError("expected 9 arguments to IfcSurfaceStyleRendering"); } do { // convert the 'Transparency' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Transparency, arg, db ); break; } + try { GenericConvert( in->Transparency, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSurfaceStyleRendering to be a `IfcNormalisedRatioMeasure`")); } } while(0); do { // convert the 'DiffuseColour' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->DiffuseColour, arg, db ); break; } + try { GenericConvert( in->DiffuseColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'TransmissionColour' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->TransmissionColour, arg, db ); break; } + try { GenericConvert( in->TransmissionColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'DiffuseTransmissionColour' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->DiffuseTransmissionColour, arg, db ); break; } + try { GenericConvert( in->DiffuseTransmissionColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'ReflectionColour' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ReflectionColour, arg, db ); break; } + try { GenericConvert( in->ReflectionColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'SpecularColour' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->SpecularColour, arg, db ); break; } + try { GenericConvert( in->SpecularColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'SpecularHighlight' argument std::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->SpecularHighlight, arg, db ); break; } + try { GenericConvert( in->SpecularHighlight, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcSurfaceStyleRendering to be a `IfcSpecularHighlightSelect`")); } } while(0); do { // convert the 'ReflectanceMethod' argument std::shared_ptr arg = params[base++]; - try { GenericConvert( in->ReflectanceMethod, arg, db ); break; } + try { GenericConvert( in->ReflectanceMethod, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcSurfaceStyleRendering to be a `IfcReflectanceMethodEnum`")); } } while(0); return base; diff --git a/code/AssetLib/IFC/IFCReaderGen_4.cpp b/code/AssetLib/IFC/IFCReaderGen_4.cpp index 9eb3e2446..179322902 100644 --- a/code/AssetLib/IFC/IFCReaderGen_4.cpp +++ b/code/AssetLib/IFC/IFCReaderGen_4.cpp @@ -5,8 +5,8 @@ Open Asset Import Library (ASSIMP) Copyright (c) 2006-2020, ASSIMP Development Team All rights reserved. -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the +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 @@ -23,16 +23,16 @@ following conditions are met: derived from this software without specific prior written permission of the ASSIMP Development Team. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +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 +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 +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 +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. ---------------------------------------------------------------------- @@ -1254,28 +1254,28 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcRoo if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcRoot"); } do { // convert the 'GlobalId' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->GlobalId, arg, db ); break; } + try { GenericConvert( in->GlobalId, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRoot to be a `IfcGloballyUniqueId`")); } } while(0); do { // convert the 'OwnerHistory' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->OwnerHistory, arg, db ); break; } + try { GenericConvert( in->OwnerHistory, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRoot to be a `IfcOwnerHistory`")); } } while(0); do { // convert the 'Name' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcRoot to be a `IfcLabel`")); } } while(0); do { // convert the 'Description' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Description, arg, db ); break; } + try { GenericConvert( in->Description, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRoot to be a `IfcText`")); } } while(0); return base; @@ -1294,7 +1294,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcO boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ObjectType, arg, db ); break; } + try { GenericConvert( in->ObjectType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcObject to be a `IfcLabel`")); } } while(0); return base; @@ -1328,14 +1328,14 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ObjectPlacement, arg, db ); break; } + try { GenericConvert( in->ObjectPlacement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcProduct to be a `IfcObjectPlacement`")); } } while(0); do { // convert the 'Representation' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Representation, arg, db ); break; } + try { GenericConvert( in->Representation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcProduct to be a `IfcProductRepresentation`")); } } while(0); return base; @@ -1348,7 +1348,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Tag, arg, db ); break; } + try { GenericConvert( in->Tag, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcElement to be a `IfcIdentifier`")); } } while(0); return base; @@ -1441,7 +1441,7 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcManifoldSolidBrep"); } do { // convert the 'Outer' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Outer, arg, db ); break; } + try { GenericConvert( in->Outer, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcManifoldSolidBrep to be a `IfcClosedShell`")); } } while(0); return base; @@ -1473,7 +1473,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcFac if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcFace"); } do { // convert the 'Bounds' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Bounds, arg, db ); break; } + try { GenericConvert( in->Bounds, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcFace to be a `SET [1:?] OF IfcFaceBound`")); } } while(0); return base; @@ -1624,14 +1624,14 @@ template <> size_t GenericFill(const DB& db, const LIST& params, if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcProfileDef"); } do { // convert the 'ProfileType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->ProfileType, arg, db ); break; } + try { GenericConvert( in->ProfileType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcProfileDef to be a `IfcProfileTypeEnum`")); } } while(0); do { // convert the 'ProfileName' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ProfileName, arg, db ); break; } + try { GenericConvert( in->ProfileName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcProfileDef to be a `IfcLabel`")); } } while(0); return base; @@ -1643,7 +1643,7 @@ template <> size_t GenericFill(const DB& db, const if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcArbitraryClosedProfileDef"); } do { // convert the 'OuterCurve' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->OuterCurve, arg, db ); break; } + try { GenericConvert( in->OuterCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcArbitraryClosedProfileDef to be a `IfcCurve`")); } } while(0); return base; @@ -1655,7 +1655,7 @@ template <> size_t GenericFill(const DB& db, const L if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcArbitraryOpenProfileDef"); } do { // convert the 'Curve' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Curve, arg, db ); break; } + try { GenericConvert( in->Curve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcArbitraryOpenProfileDef to be a `IfcBoundedCurve`")); } } while(0); return base; @@ -1666,7 +1666,7 @@ template <> size_t GenericFill(const DB& db, co size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcArbitraryProfileDefWithVoids"); } do { // convert the 'InnerCurves' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->InnerCurves, arg, db ); break; } + try { GenericConvert( in->InnerCurves, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcArbitraryProfileDefWithVoids to be a `SET [1:?] OF IfcCurve`")); } } while(0); return base; @@ -1693,7 +1693,7 @@ template <> size_t GenericFill(const DB& db, const L boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcParameterizedProfileDef to be a `IfcAxis2Placement2D`")); } } while(0); return base; @@ -1726,7 +1726,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPlacement"); } do { // convert the 'Location' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Location, arg, db ); break; } + try { GenericConvert( in->Location, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPlacement to be a `IfcCartesianPoint`")); } } while(0); return base; @@ -1738,7 +1738,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcAxis1Placement"); } do { // convert the 'Axis' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis, arg, db ); break; } + try { GenericConvert( in->Axis, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcAxis1Placement to be a `IfcDirection`")); } } while(0); return base; @@ -1750,7 +1750,7 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcAxis2Placement2D"); } do { // convert the 'RefDirection' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefDirection, arg, db ); break; } + try { GenericConvert( in->RefDirection, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcAxis2Placement2D to be a `IfcDirection`")); } } while(0); return base; @@ -1762,13 +1762,13 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcAxis2Placement3D"); } do { // convert the 'Axis' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis, arg, db ); break; } + try { GenericConvert( in->Axis, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcAxis2Placement3D to be a `IfcDirection`")); } } while(0); do { // convert the 'RefDirection' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefDirection, arg, db ); break; } + try { GenericConvert( in->RefDirection, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcAxis2Placement3D to be a `IfcDirection`")); } } while(0); return base; @@ -1792,31 +1792,31 @@ template <> size_t GenericFill(const DB& db, const LIST& params if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcBSplineCurve"); } do { // convert the 'Degree' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Degree, arg, db ); break; } + try { GenericConvert( in->Degree, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcBSplineCurve to be a `IfcInteger`")); } } while(0); do { // convert the 'ControlPointsList' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->ControlPointsList, arg, db ); break; } + try { GenericConvert( in->ControlPointsList, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcBSplineCurve to be a `LIST [2:?] OF IfcCartesianPoint`")); } } while(0); do { // convert the 'CurveForm' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->CurveForm, arg, db ); break; } + try { GenericConvert( in->CurveForm, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcBSplineCurve to be a `IfcBSplineCurveForm`")); } } while(0); do { // convert the 'ClosedCurve' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } - try { GenericConvert( in->ClosedCurve, arg, db ); break; } + try { GenericConvert( in->ClosedCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcBSplineCurve to be a `IfcLogical`")); } } while(0); do { // convert the 'SelfIntersect' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[4]=true; break; } - try { GenericConvert( in->SelfIntersect, arg, db ); break; } + try { GenericConvert( in->SelfIntersect, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcBSplineCurve to be a `IfcLogical`")); } } while(0); return base; @@ -1930,19 +1930,19 @@ template <> size_t GenericFill(const DB& db, const LIST& param if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcBooleanResult"); } do { // convert the 'Operator' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Operator, arg, db ); break; } + try { GenericConvert( in->Operator, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcBooleanResult to be a `IfcBooleanOperator`")); } } while(0); do { // convert the 'FirstOperand' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->FirstOperand, arg, db ); break; } + try { GenericConvert( in->FirstOperand, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcBooleanResult to be a `IfcBooleanOperand`")); } } while(0); do { // convert the 'SecondOperand' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->SecondOperand, arg, db ); break; } + try { GenericConvert( in->SecondOperand, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcBooleanResult to be a `IfcBooleanOperand`")); } } while(0); return base; @@ -1960,13 +1960,13 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcCompositeCurve"); } do { // convert the 'Segments' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Segments, arg, db ); break; } + try { GenericConvert( in->Segments, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCompositeCurve to be a `LIST [1:?] OF IfcCompositeCurveSegment`")); } } while(0); do { // convert the 'SelfIntersect' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->SelfIntersect, arg, db ); break; } + try { GenericConvert( in->SelfIntersect, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCompositeCurve to be a `IfcLogical`")); } } while(0); return base; @@ -1991,22 +1991,22 @@ template <> size_t GenericFill(const DB& db, const LIST& params, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcBoundingBox"); } do { // convert the 'Corner' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Corner, arg, db ); break; } + try { GenericConvert( in->Corner, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcBoundingBox to be a `IfcCartesianPoint`")); } } while(0); do { // convert the 'XDim' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->XDim, arg, db ); break; } + try { GenericConvert( in->XDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcBoundingBox to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'YDim' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->YDim, arg, db ); break; } + try { GenericConvert( in->YDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcBoundingBox to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'ZDim' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->ZDim, arg, db ); break; } + try { GenericConvert( in->ZDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcBoundingBox to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2018,13 +2018,13 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcHalfSpaceSolid"); } do { // convert the 'BaseSurface' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->BaseSurface, arg, db ); break; } + try { GenericConvert( in->BaseSurface, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcHalfSpaceSolid to be a `IfcSurface`")); } } while(0); do { // convert the 'AgreementFlag' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->AgreementFlag, arg, db ); break; } + try { GenericConvert( in->AgreementFlag, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcHalfSpaceSolid to be a `IfcBoolean`")); } } while(0); return base; @@ -2044,7 +2044,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->LongName, arg, db ); break; } + try { GenericConvert( in->LongName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcSpatialElement to be a `IfcLabel`")); } } while(0); return base; @@ -2057,7 +2057,7 @@ template <> size_t GenericFill(const DB& db, const L boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->CompositionType, arg, db ); break; } + try { GenericConvert( in->CompositionType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcSpatialStructureElement to be a `IfcElementCompositionEnum`")); } } while(0); return base; @@ -2069,19 +2069,19 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If if (params.GetSize() < 12) { throw STEP::TypeError("expected 12 arguments to IfcBuilding"); } do { // convert the 'ElevationOfRefHeight' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ElevationOfRefHeight, arg, db ); break; } + try { GenericConvert( in->ElevationOfRefHeight, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcBuilding to be a `IfcLengthMeasure`")); } } while(0); do { // convert the 'ElevationOfTerrain' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ElevationOfTerrain, arg, db ); break; } + try { GenericConvert( in->ElevationOfTerrain, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcBuilding to be a `IfcLengthMeasure`")); } } while(0); do { // convert the 'BuildingAddress' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->BuildingAddress, arg, db ); break; } + try { GenericConvert( in->BuildingAddress, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 11 to IfcBuilding to be a `IfcPostalAddress`")); } } while(0); return base; @@ -2266,7 +2266,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcCartesianPoint"); } do { // convert the 'Coordinates' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Coordinates, arg, db ); break; } + try { GenericConvert( in->Coordinates, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCartesianPoint to be a `LIST [1:3] OF IfcLengthMeasure`")); } } while(0); return base; @@ -2300,27 +2300,27 @@ template <> size_t GenericFill(const DB& db, boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis1, arg, db ); break; } + try { GenericConvert( in->Axis1, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCartesianTransformationOperator to be a `IfcDirection`")); } } while(0); do { // convert the 'Axis2' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis2, arg, db ); break; } + try { GenericConvert( in->Axis2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCartesianTransformationOperator to be a `IfcDirection`")); } } while(0); do { // convert the 'LocalOrigin' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->LocalOrigin, arg, db ); break; } + try { GenericConvert( in->LocalOrigin, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcCartesianTransformationOperator to be a `IfcCartesianPoint`")); } } while(0); do { // convert the 'Scale' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Scale, arg, db ); break; } + try { GenericConvert( in->Scale, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcCartesianTransformationOperator to be a `IfcReal`")); } } while(0); return base; @@ -2347,7 +2347,7 @@ template <> size_t GenericFill(const DB& d boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Axis3, arg, db ); break; } + try { GenericConvert( in->Axis3, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcCartesianTransformationOperator3D to be a `IfcDirection`")); } } while(0); return base; @@ -2359,13 +2359,13 @@ template <> size_t GenericFill(c if (params.GetSize() < 7) { throw STEP::TypeError("expected 7 arguments to IfcCartesianTransformationOperator3DnonUniform"); } do { // convert the 'Scale2' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Scale2, arg, db ); break; } + try { GenericConvert( in->Scale2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcCartesianTransformationOperator3DnonUniform to be a `IfcReal`")); } } while(0); do { // convert the 'Scale3' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Scale3, arg, db ); break; } + try { GenericConvert( in->Scale3, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcCartesianTransformationOperator3DnonUniform to be a `IfcReal`")); } } while(0); return base; @@ -2412,7 +2412,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcCo if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcConic"); } do { // convert the 'Position' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcConic to be a `IfcAxis2Placement`")); } } while(0); return base; @@ -2423,7 +2423,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcC size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcCircle"); } do { // convert the 'Radius' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Radius, arg, db ); break; } + try { GenericConvert( in->Radius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCircle to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2435,7 +2435,7 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcCircleProfileDef"); } do { // convert the 'Radius' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Radius, arg, db ); break; } + try { GenericConvert( in->Radius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcCircleProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2446,7 +2446,7 @@ template <> size_t GenericFill(const DB& db, const LI size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcCircleHollowProfileDef"); } do { // convert the 'WallThickness' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->WallThickness, arg, db ); break; } + try { GenericConvert( in->WallThickness, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcCircleHollowProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -2472,7 +2472,7 @@ template <> size_t GenericFill(const DB& db, const LIST& pa if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcConnectedFaceSet"); } do { // convert the 'CfsFaces' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->CfsFaces, arg, db ); break; } + try { GenericConvert( in->CfsFaces, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcConnectedFaceSet to be a `SET [1:?] OF IfcFace`")); } } while(0); return base; @@ -2505,7 +2505,7 @@ template <> size_t GenericFill(const DB& db, const LIST& boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcColourSpecification to be a `IfcLabel`")); } } while(0); return base; @@ -2516,17 +2516,17 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcColourRgb"); } do { // convert the 'Red' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Red, arg, db ); break; } + try { GenericConvert( in->Red, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcColourRgb to be a `IfcNormalisedRatioMeasure`")); } } while(0); do { // convert the 'Green' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Green, arg, db ); break; } + try { GenericConvert( in->Green, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcColourRgb to be a `IfcNormalisedRatioMeasure`")); } } while(0); do { // convert the 'Blue' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Blue, arg, db ); break; } + try { GenericConvert( in->Blue, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcColourRgb to be a `IfcNormalisedRatioMeasure`")); } } while(0); return base; @@ -2579,14 +2579,14 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcProperty"); } do { // convert the 'Name' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcProperty to be a `IfcIdentifier`")); } } while(0); do { // convert the 'Description' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Description, arg, db ); break; } + try { GenericConvert( in->Description, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcProperty to be a `IfcText`")); } } while(0); return base; @@ -2597,12 +2597,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcComplexProperty"); } do { // convert the 'UsageName' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->UsageName, arg, db ); break; } + try { GenericConvert( in->UsageName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcComplexProperty to be a `IfcIdentifier`")); } } while(0); do { // convert the 'HasProperties' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->HasProperties, arg, db ); break; } + try { GenericConvert( in->HasProperties, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcComplexProperty to be a `SET [1:?] OF IfcProperty`")); } } while(0); return base; @@ -2620,19 +2620,19 @@ template <> size_t GenericFill(const DB& db, const LIS if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcCompositeCurveSegment"); } do { // convert the 'Transition' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Transition, arg, db ); break; } + try { GenericConvert( in->Transition, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcCompositeCurveSegment to be a `IfcTransitionCode`")); } } while(0); do { // convert the 'SameSense' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->SameSense, arg, db ); break; } + try { GenericConvert( in->SameSense, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcCompositeCurveSegment to be a `IfcBoolean`")); } } while(0); do { // convert the 'ParentCurve' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->ParentCurve, arg, db ); break; } + try { GenericConvert( in->ParentCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcCompositeCurveSegment to be a `IfcCurve`")); } } while(0); return base; @@ -2764,35 +2764,35 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ObjectType, arg, db ); break; } + try { GenericConvert( in->ObjectType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcContext to be a `IfcLabel`")); } } while(0); do { // convert the 'LongName' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->LongName, arg, db ); break; } + try { GenericConvert( in->LongName, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcContext to be a `IfcLabel`")); } } while(0); do { // convert the 'Phase' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Phase, arg, db ); break; } + try { GenericConvert( in->Phase, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcContext to be a `IfcLabel`")); } } while(0); do { // convert the 'RepresentationContexts' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RepresentationContexts, arg, db ); break; } + try { GenericConvert( in->RepresentationContexts, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcContext to be a `SET [1:?] OF IfcRepresentationContext`")); } } while(0); do { // convert the 'UnitsInContext' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[4]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->UnitsInContext, arg, db ); break; } + try { GenericConvert( in->UnitsInContext, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcContext to be a `IfcUnitAssignment`")); } } while(0); return base; @@ -2804,13 +2804,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcNamedUnit"); } do { // convert the 'Dimensions' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Dimensions, arg, db ); break; } + try { GenericConvert( in->Dimensions, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcNamedUnit to be a `IfcDimensionalExponents`")); } } while(0); do { // convert the 'UnitType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->UnitType, arg, db ); break; } + try { GenericConvert( in->UnitType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcNamedUnit to be a `IfcUnitEnum`")); } } while(0); return base; @@ -2843,13 +2843,13 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcConversionBasedUnit"); } do { // convert the 'Name' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcConversionBasedUnit to be a `IfcLabel`")); } } while(0); do { // convert the 'ConversionFactor' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->ConversionFactor, arg, db ); break; } + try { GenericConvert( in->ConversionFactor, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcConversionBasedUnit to be a `IfcMeasureWithUnit`")); } } while(0); return base; @@ -2974,7 +2974,7 @@ template <> size_t GenericFill(const DB& db, const LIST& p boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPresentationStyle to be a `IfcLabel`")); } } while(0); return base; @@ -2986,7 +2986,7 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcElementarySurface"); } do { // convert the 'Position' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcElementarySurface to be a `IfcAxis2Placement3D`")); } } while(0); return base; @@ -3025,7 +3025,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcDirection"); } do { // convert the 'DirectionRatios' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->DirectionRatios, arg, db ); break; } + try { GenericConvert( in->DirectionRatios, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcDirection to be a `LIST [2:3] OF IfcReal`")); } } while(0); return base; @@ -3094,35 +3094,35 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcDoo boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->OverallHeight, arg, db ); break; } + try { GenericConvert( in->OverallHeight, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcDoor to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'OverallWidth' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->OverallWidth, arg, db ); break; } + try { GenericConvert( in->OverallWidth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcDoor to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'PredefinedType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->PredefinedType, arg, db ); break; } + try { GenericConvert( in->PredefinedType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcDoor to be a `IfcDoorTypeEnum`")); } } while(0); do { // convert the 'OperationType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->OperationType, arg, db ); break; } + try { GenericConvert( in->OperationType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 11 to IfcDoor to be a `IfcDoorTypeOperationEnum`")); } } while(0); do { // convert the 'UserDefinedOperationType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[4]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->UserDefinedOperationType, arg, db ); break; } + try { GenericConvert( in->UserDefinedOperationType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 12 to IfcDoor to be a `IfcLabel`")); } } while(0); return base; @@ -3362,12 +3362,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcElementQuantity"); } do { // convert the 'MethodOfMeasurement' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->MethodOfMeasurement, arg, db ); break; } + try { GenericConvert( in->MethodOfMeasurement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcElementQuantity to be a `IfcLabel`")); } } while(0); do { // convert the 'Quantities' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Quantities, arg, db ); break; } + try { GenericConvert( in->Quantities, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcElementQuantity to be a `SET [1:?] OF IfcPhysicalQuantity`")); } } while(0); return base; @@ -3378,12 +3378,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, Ifc size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcEllipse"); } do { // convert the 'SemiAxis1' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->SemiAxis1, arg, db ); break; } + try { GenericConvert( in->SemiAxis1, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcEllipse to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'SemiAxis2' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->SemiAxis2, arg, db ); break; } + try { GenericConvert( in->SemiAxis2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcEllipse to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -3486,14 +3486,14 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcSweptAreaSolid"); } do { // convert the 'SweptArea' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->SweptArea, arg, db ); break; } + try { GenericConvert( in->SweptArea, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSweptAreaSolid to be a `IfcProfileDef`")); } } while(0); do { // convert the 'Position' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSweptAreaSolid to be a `IfcAxis2Placement3D`")); } } while(0); return base; @@ -3505,13 +3505,13 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcExtrudedAreaSolid"); } do { // convert the 'ExtrudedDirection' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->ExtrudedDirection, arg, db ); break; } + try { GenericConvert( in->ExtrudedDirection, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcExtrudedAreaSolid to be a `IfcDirection`")); } } while(0); do { // convert the 'Depth' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Depth, arg, db ); break; } + try { GenericConvert( in->Depth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcExtrudedAreaSolid to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -3529,7 +3529,7 @@ template <> size_t GenericFill(const DB& db, const LIS size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcFaceBasedSurfaceModel"); } do { // convert the 'FbsmFaces' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->FbsmFaces, arg, db ); break; } + try { GenericConvert( in->FbsmFaces, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcFaceBasedSurfaceModel to be a `SET [1:?] OF IfcConnectedFaceSet`")); } } while(0); return base; @@ -3541,13 +3541,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, I if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcFaceBound"); } do { // convert the 'Bound' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Bound, arg, db ); break; } + try { GenericConvert( in->Bound, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcFaceBound to be a `IfcLoop`")); } } while(0); do { // convert the 'Orientation' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Orientation, arg, db ); break; } + try { GenericConvert( in->Orientation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcFaceBound to be a `IfcBoolean`")); } } while(0); return base; @@ -3774,14 +3774,14 @@ template <> size_t GenericFill(const DB& db, const LIS boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ContextIdentifier, arg, db ); break; } + try { GenericConvert( in->ContextIdentifier, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRepresentationContext to be a `IfcLabel`")); } } while(0); do { // convert the 'ContextType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ContextType, arg, db ); break; } + try { GenericConvert( in->ContextType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRepresentationContext to be a `IfcLabel`")); } } while(0); return base; @@ -3793,27 +3793,27 @@ template <> size_t GenericFill(const DB& db, if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcGeometricRepresentationContext"); } do { // convert the 'CoordinateSpaceDimension' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->CoordinateSpaceDimension, arg, db ); break; } + try { GenericConvert( in->CoordinateSpaceDimension, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcGeometricRepresentationContext to be a `IfcDimensionCount`")); } } while(0); do { // convert the 'Precision' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Precision, arg, db ); break; } + try { GenericConvert( in->Precision, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcGeometricRepresentationContext to be a `IfcReal`")); } } while(0); do { // convert the 'WorldCoordinateSystem' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->WorldCoordinateSystem, arg, db ); break; } + try { GenericConvert( in->WorldCoordinateSystem, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcGeometricRepresentationContext to be a `IfcAxis2Placement`")); } } while(0); do { // convert the 'TrueNorth' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->TrueNorth, arg, db ); break; } + try { GenericConvert( in->TrueNorth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcGeometricRepresentationContext to be a `IfcDirection`")); } } while(0); return base; @@ -3879,40 +3879,40 @@ template <> size_t GenericFill(const DB& db, const LIST& pa size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 10) { throw STEP::TypeError("expected 10 arguments to IfcIShapeProfileDef"); } do { // convert the 'OverallWidth' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->OverallWidth, arg, db ); break; } + try { GenericConvert( in->OverallWidth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'OverallDepth' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->OverallDepth, arg, db ); break; } + try { GenericConvert( in->OverallDepth, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'WebThickness' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->WebThickness, arg, db ); break; } + try { GenericConvert( in->WebThickness, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'FlangeThickness' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->FlangeThickness, arg, db ); break; } + try { GenericConvert( in->FlangeThickness, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcIShapeProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'FilletRadius' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->FilletRadius, arg, db ); break; } + try { GenericConvert( in->FilletRadius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcIShapeProfileDef to be a `IfcNonNegativeLengthMeasure`")); } } while(0); do { // convert the 'FlangeEdgeRadius' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->FlangeEdgeRadius, arg, db ); break; } + try { GenericConvert( in->FlangeEdgeRadius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcIShapeProfileDef to be a `IfcNonNegativeLengthMeasure`")); } } while(0); do { // convert the 'FlangeSlope' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->FlangeSlope, arg, db ); break; } + try { GenericConvert( in->FlangeSlope, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcIShapeProfileDef to be a `IfcPlaneAngleMeasure`")); } } while(0); return base; @@ -4091,12 +4091,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcLin size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcLine"); } do { // convert the 'Pnt' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Pnt, arg, db ); break; } + try { GenericConvert( in->Pnt, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcLine to be a `IfcCartesianPoint`")); } } while(0); do { // convert the 'Dir' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Dir, arg, db ); break; } + try { GenericConvert( in->Dir, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcLine to be a `IfcVector`")); } } while(0); return base; @@ -4108,12 +4108,12 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcLocalPlacement"); } do { // convert the 'PlacementRelTo' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->PlacementRelTo, arg, db ); break; } + try { GenericConvert( in->PlacementRelTo, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcLocalPlacement to be a `IfcObjectPlacement`")); } } while(0); do { // convert the 'RelativePlacement' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelativePlacement, arg, db ); break; } + try { GenericConvert( in->RelativePlacement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcLocalPlacement to be a `IfcAxis2Placement`")); } } while(0); return base; @@ -4124,12 +4124,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcMappedItem"); } do { // convert the 'MappingSource' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappingSource, arg, db ); break; } + try { GenericConvert( in->MappingSource, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcMappedItem to be a `IfcRepresentationMap`")); } } while(0); do { // convert the 'MappingTarget' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappingTarget, arg, db ); break; } + try { GenericConvert( in->MappingTarget, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcMappedItem to be a `IfcCartesianTransformationOperator`")); } } while(0); return base; @@ -4142,20 +4142,20 @@ template <> size_t GenericFill(const DB& db, const LIS boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcProductRepresentation to be a `IfcLabel`")); } } while(0); do { // convert the 'Description' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Description, arg, db ); break; } + try { GenericConvert( in->Description, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcProductRepresentation to be a `IfcText`")); } } while(0); do { // convert the 'Representations' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } - try { GenericConvert( in->Representations, arg, db ); break; } + try { GenericConvert( in->Representations, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcProductRepresentation to be a `LIST [1:?] OF IfcRepresentation`")); } } while(0); return base; @@ -4173,12 +4173,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = 0; if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcMeasureWithUnit"); } do { // convert the 'ValueComponent' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->ValueComponent, arg, db ); break; } + try { GenericConvert( in->ValueComponent, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcMeasureWithUnit to be a `IfcValue`")); } } while(0); do { // convert the 'UnitComponent' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->UnitComponent, arg, db ); break; } + try { GenericConvert( in->UnitComponent, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcMeasureWithUnit to be a `IfcUnit`")); } } while(0); return base; @@ -4289,7 +4289,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->PredefinedType, arg, db ); break; } + try { GenericConvert( in->PredefinedType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcOpeningElement to be a `IfcOpeningElementTypeEnum`")); } } while(0); return base; @@ -4460,7 +4460,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPolyLoop"); } do { // convert the 'Polygon' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Polygon, arg, db ); break; } + try { GenericConvert( in->Polygon, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPolyLoop to be a `LIST [3:?] OF IfcCartesianPoint`")); } } while(0); return base; @@ -4471,12 +4471,12 @@ template <> size_t GenericFill(const DB& db, const size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcPolygonalBoundedHalfSpace"); } do { // convert the 'Position' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Position, arg, db ); break; } + try { GenericConvert( in->Position, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcPolygonalBoundedHalfSpace to be a `IfcAxis2Placement3D`")); } } while(0); do { // convert the 'PolygonalBoundary' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->PolygonalBoundary, arg, db ); break; } + try { GenericConvert( in->PolygonalBoundary, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcPolygonalBoundedHalfSpace to be a `IfcBoundedCurve`")); } } while(0); return base; @@ -4501,7 +4501,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, If size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPolyline"); } do { // convert the 'Points' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Points, arg, db ); break; } + try { GenericConvert( in->Points, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPolyline to be a `LIST [2:?] OF IfcCartesianPoint`")); } } while(0); return base; @@ -4512,7 +4512,7 @@ template <> size_t GenericFill(const DB& db, con size_t base = 0; if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcPresentationStyleAssignment"); } do { // convert the 'Styles' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Styles, arg, db ); break; } + try { GenericConvert( in->Styles, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcPresentationStyleAssignment to be a `SET [1:?] OF IfcPresentationStyleSelect`")); } } while(0); return base; @@ -4592,13 +4592,13 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcPropertyListValue"); } do { // convert the 'ListValues' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ListValues, arg, db ); break; } + try { GenericConvert( in->ListValues, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcPropertyListValue to be a `LIST [1:?] OF IfcValue`")); } } while(0); do { // convert the 'Unit' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Unit, arg, db ); break; } + try { GenericConvert( in->Unit, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcPropertyListValue to be a `IfcUnit`")); } } while(0); return base; @@ -4616,7 +4616,7 @@ template <> size_t GenericFill(const DB& db, const LIST& params, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcPropertySet"); } do { // convert the 'HasProperties' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->HasProperties, arg, db ); break; } + try { GenericConvert( in->HasProperties, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcPropertySet to be a `SET [1:?] OF IfcProperty`")); } } while(0); return base; @@ -4628,13 +4628,13 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcPropertySingleValue"); } do { // convert the 'NominalValue' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->NominalValue, arg, db ); break; } + try { GenericConvert( in->NominalValue, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcPropertySingleValue to be a `IfcValue`")); } } while(0); do { // convert the 'Unit' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Unit, arg, db ); break; } + try { GenericConvert( in->Unit, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcPropertySingleValue to be a `IfcUnit`")); } } while(0); return base; @@ -4758,13 +4758,13 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcRectangleProfileDef"); } do { // convert the 'XDim' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->XDim, arg, db ); break; } + try { GenericConvert( in->XDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRectangleProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'YDim' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->YDim, arg, db ); break; } + try { GenericConvert( in->YDim, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRectangleProfileDef to be a `IfcPositiveLengthMeasure`")); } } while(0); return base; @@ -4850,12 +4850,12 @@ template <> size_t GenericFill(const DB& db, const LIST& param size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelAggregates"); } do { // convert the 'RelatingObject' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingObject, arg, db ); break; } + try { GenericConvert( in->RelatingObject, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelAggregates to be a `IfcObjectDefinition`")); } } while(0); do { // convert the 'RelatedObjects' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedObjects, arg, db ); break; } + try { GenericConvert( in->RelatedObjects, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelAggregates to be a `SET [1:?] OF IfcObjectDefinition`")); } } while(0); return base; @@ -4872,12 +4872,12 @@ template <> size_t GenericFill(const DB& db, size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelContainedInSpatialStructure"); } do { // convert the 'RelatedElements' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedElements, arg, db ); break; } + try { GenericConvert( in->RelatedElements, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelContainedInSpatialStructure to be a `SET [1:?] OF IfcProduct`")); } } while(0); do { // convert the 'RelatingStructure' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingStructure, arg, db ); break; } + try { GenericConvert( in->RelatingStructure, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelContainedInSpatialStructure to be a `IfcSpatialElement`")); } } while(0); return base; @@ -4894,12 +4894,12 @@ template <> size_t GenericFill(const DB& db, const LI size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelDefinesByProperties"); } do { // convert the 'RelatedObjects' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedObjects, arg, db ); break; } + try { GenericConvert( in->RelatedObjects, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelDefinesByProperties to be a `SET [1:?] OF IfcObjectDefinition`")); } } while(0); do { // convert the 'RelatingPropertyDefinition' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingPropertyDefinition, arg, db ); break; } + try { GenericConvert( in->RelatingPropertyDefinition, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelDefinesByProperties to be a `IfcPropertySetDefinitionSelect`")); } } while(0); return base; @@ -4910,12 +4910,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelFillsElement"); } do { // convert the 'RelatingOpeningElement' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingOpeningElement, arg, db ); break; } + try { GenericConvert( in->RelatingOpeningElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelFillsElement to be a `IfcOpeningElement`")); } } while(0); do { // convert the 'RelatedBuildingElement' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedBuildingElement, arg, db ); break; } + try { GenericConvert( in->RelatedBuildingElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelFillsElement to be a `IfcElement`")); } } while(0); return base; @@ -4926,12 +4926,12 @@ template <> size_t GenericFill(const DB& db, const LIST& par size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 6) { throw STEP::TypeError("expected 6 arguments to IfcRelVoidsElement"); } do { // convert the 'RelatingBuildingElement' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatingBuildingElement, arg, db ); break; } + try { GenericConvert( in->RelatingBuildingElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcRelVoidsElement to be a `IfcElement`")); } } while(0); do { // convert the 'RelatedOpeningElement' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->RelatedOpeningElement, arg, db ); break; } + try { GenericConvert( in->RelatedOpeningElement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcRelVoidsElement to be a `IfcFeatureElementSubtraction`")); } } while(0); return base; @@ -4950,27 +4950,27 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcRepresentation"); } do { // convert the 'ContextOfItems' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->ContextOfItems, arg, db ); break; } + try { GenericConvert( in->ContextOfItems, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRepresentation to be a `IfcRepresentationContext`")); } } while(0); do { // convert the 'RepresentationIdentifier' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RepresentationIdentifier, arg, db ); break; } + try { GenericConvert( in->RepresentationIdentifier, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRepresentation to be a `IfcLabel`")); } } while(0); do { // convert the 'RepresentationType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RepresentationType, arg, db ); break; } + try { GenericConvert( in->RepresentationType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcRepresentation to be a `IfcLabel`")); } } while(0); do { // convert the 'Items' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } - try { GenericConvert( in->Items, arg, db ); break; } + try { GenericConvert( in->Items, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRepresentation to be a `SET [1:?] OF IfcRepresentationItem`")); } } while(0); return base; @@ -4981,12 +4981,12 @@ template <> size_t GenericFill(const DB& db, const LIST& p size_t base = 0; if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcRepresentationMap"); } do { // convert the 'MappingOrigin' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappingOrigin, arg, db ); break; } + try { GenericConvert( in->MappingOrigin, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcRepresentationMap to be a `IfcAxis2Placement`")); } } while(0); do { // convert the 'MappedRepresentation' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->MappedRepresentation, arg, db ); break; } + try { GenericConvert( in->MappedRepresentation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcRepresentationMap to be a `IfcRepresentation`")); } } while(0); return base; @@ -4998,13 +4998,13 @@ template <> size_t GenericFill(const DB& db, const LIST& p if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcRevolvedAreaSolid"); } do { // convert the 'Axis' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Axis, arg, db ); break; } + try { GenericConvert( in->Axis, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcRevolvedAreaSolid to be a `IfcAxis1Placement`")); } } while(0); do { // convert the 'Angle' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Angle, arg, db ); break; } + try { GenericConvert( in->Angle, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcRevolvedAreaSolid to be a `IfcPlaneAngleMeasure`")); } } while(0); return base; @@ -5058,12 +5058,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcS if (params.GetSize() < 4) { throw STEP::TypeError("expected 4 arguments to IfcSIUnit"); } do { // convert the 'Prefix' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Prefix, arg, db ); break; } + try { GenericConvert( in->Prefix, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSIUnit to be a `IfcSIPrefix`")); } } while(0); do { // convert the 'Name' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcSIUnit to be a `IfcSIUnitName`")); } } while(0); return base; @@ -5144,7 +5144,7 @@ template <> size_t GenericFill(const DB& db, const LI size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcShellBasedSurfaceModel"); } do { // convert the 'SbsmBoundary' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->SbsmBoundary, arg, db ); break; } + try { GenericConvert( in->SbsmBoundary, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcShellBasedSurfaceModel to be a `SET [1:?] OF IfcShell`")); } } while(0); return base; @@ -5156,31 +5156,31 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcSit if (params.GetSize() < 14) { throw STEP::TypeError("expected 14 arguments to IfcSite"); } do { // convert the 'RefLatitude' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefLatitude, arg, db ); break; } + try { GenericConvert( in->RefLatitude, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcSite to be a `IfcCompoundPlaneAngleMeasure`")); } } while(0); do { // convert the 'RefLongitude' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefLongitude, arg, db ); break; } + try { GenericConvert( in->RefLongitude, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcSite to be a `IfcCompoundPlaneAngleMeasure`")); } } while(0); do { // convert the 'RefElevation' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->RefElevation, arg, db ); break; } + try { GenericConvert( in->RefElevation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 11 to IfcSite to be a `IfcLengthMeasure`")); } } while(0); do { // convert the 'LandTitleNumber' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->LandTitleNumber, arg, db ); break; } + try { GenericConvert( in->LandTitleNumber, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 12 to IfcSite to be a `IfcLabel`")); } } while(0); do { // convert the 'SiteAddress' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->SiteAddress, arg, db ); break; } + try { GenericConvert( in->SiteAddress, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 13 to IfcSite to be a `IfcPostalAddress`")); } } while(0); return base; @@ -5234,13 +5234,13 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcSp if (params.GetSize() < 11) { throw STEP::TypeError("expected 11 arguments to IfcSpace"); } do { // convert the 'PredefinedType' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->PredefinedType, arg, db ); break; } + try { GenericConvert( in->PredefinedType, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 9 to IfcSpace to be a `IfcSpaceTypeEnum`")); } } while(0); do { // convert the 'ElevationWithFlooring' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ElevationWithFlooring, arg, db ); break; } + try { GenericConvert( in->ElevationWithFlooring, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 10 to IfcSpace to be a `IfcLengthMeasure`")); } } while(0); return base; @@ -5539,18 +5539,18 @@ template <> size_t GenericFill(const DB& db, const LIST& params, if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcStyledItem"); } do { // convert the 'Item' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Item, arg, db ); break; } + try { GenericConvert( in->Item, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcStyledItem to be a `IfcRepresentationItem`")); } } while(0); do { // convert the 'Styles' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Styles, arg, db ); break; } + try { GenericConvert( in->Styles, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcStyledItem to be a `SET [1:?] OF IfcStyleAssignmentSelect`")); } } while(0); do { // convert the 'Name' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Name, arg, db ); break; } + try { GenericConvert( in->Name, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcStyledItem to be a `IfcLabel`")); } } while(0); return base; @@ -5624,12 +5624,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 3) { throw STEP::TypeError("expected 3 arguments to IfcSurfaceStyle"); } do { // convert the 'Side' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Side, arg, db ); break; } + try { GenericConvert( in->Side, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSurfaceStyle to be a `IfcSurfaceSide`")); } } while(0); do { // convert the 'Styles' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Styles, arg, db ); break; } + try { GenericConvert( in->Styles, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSurfaceStyle to be a `SET [1:5] OF IfcSurfaceStyleElementSelect`")); } } while(0); return base; @@ -5641,14 +5641,14 @@ template <> size_t GenericFill(const DB& db, const LIST& if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcSurfaceStyleShading"); } do { // convert the 'SurfaceColour' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->SurfaceColour, arg, db ); break; } + try { GenericConvert( in->SurfaceColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSurfaceStyleShading to be a `IfcColourRgb`")); } } while(0); do { // convert the 'Transparency' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->Transparency, arg, db ); break; } + try { GenericConvert( in->Transparency, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSurfaceStyleShading to be a `IfcNormalisedRatioMeasure`")); } } while(0); return base; @@ -5660,42 +5660,42 @@ template <> size_t GenericFill(const DB& db, const LIS if (params.GetSize() < 9) { throw STEP::TypeError("expected 9 arguments to IfcSurfaceStyleRendering"); } do { // convert the 'DiffuseColour' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->DiffuseColour, arg, db ); break; } + try { GenericConvert( in->DiffuseColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'TransmissionColour' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->TransmissionColour, arg, db ); break; } + try { GenericConvert( in->TransmissionColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'DiffuseTransmissionColour' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->DiffuseTransmissionColour, arg, db ); break; } + try { GenericConvert( in->DiffuseTransmissionColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'ReflectionColour' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->ReflectionColour, arg, db ); break; } + try { GenericConvert( in->ReflectionColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 5 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'SpecularColour' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->SpecularColour, arg, db ); break; } + try { GenericConvert( in->SpecularColour, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 6 to IfcSurfaceStyleRendering to be a `IfcColourOrFactor`")); } } while(0); do { // convert the 'SpecularHighlight' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->SpecularHighlight, arg, db ); break; } + try { GenericConvert( in->SpecularHighlight, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 7 to IfcSurfaceStyleRendering to be a `IfcSpecularHighlightSelect`")); } } while(0); do { // convert the 'ReflectanceMethod' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->ReflectanceMethod, arg, db ); break; } + try { GenericConvert( in->ReflectanceMethod, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 8 to IfcSurfaceStyleRendering to be a `IfcReflectanceMethodEnum`")); } } while(0); return base; @@ -5706,7 +5706,7 @@ template <> size_t GenericFill(const DB& db, const size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcSurfaceStyleWithTextures"); } do { // convert the 'Textures' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Textures, arg, db ); break; } + try { GenericConvert( in->Textures, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSurfaceStyleWithTextures to be a `LIST [1:?] OF IfcSurfaceTexture`")); } } while(0); return base; @@ -5718,34 +5718,34 @@ template <> size_t GenericFill(const DB& db, const LIST& para if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcSweptDiskSolid"); } do { // convert the 'Directrix' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[0]=true; break; } - try { GenericConvert( in->Directrix, arg, db ); break; } + try { GenericConvert( in->Directrix, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcSweptDiskSolid to be a `IfcCurve`")); } } while(0); do { // convert the 'Radius' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[1]=true; break; } - try { GenericConvert( in->Radius, arg, db ); break; } + try { GenericConvert( in->Radius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcSweptDiskSolid to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'InnerRadius' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[2]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->InnerRadius, arg, db ); break; } + try { GenericConvert( in->InnerRadius, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcSweptDiskSolid to be a `IfcPositiveLengthMeasure`")); } } while(0); do { // convert the 'StartParam' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[3]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->StartParam, arg, db ); break; } + try { GenericConvert( in->StartParam, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcSweptDiskSolid to be a `IfcParameterValue`")); } } while(0); do { // convert the 'EndParam' argument boost::shared_ptr arg = params[base++]; if (dynamic_cast(&*arg)) { in->ObjectHelper::aux_is_derived[4]=true; break; } if (dynamic_cast(&*arg)) break; - try { GenericConvert( in->EndParam, arg, db ); break; } + try { GenericConvert( in->EndParam, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcSweptDiskSolid to be a `IfcParameterValue`")); } } while(0); return base; @@ -5924,27 +5924,27 @@ template <> size_t GenericFill(const DB& db, const LIST& params size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 5) { throw STEP::TypeError("expected 5 arguments to IfcTrimmedCurve"); } do { // convert the 'BasisCurve' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->BasisCurve, arg, db ); break; } + try { GenericConvert( in->BasisCurve, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcTrimmedCurve to be a `IfcCurve`")); } } while(0); do { // convert the 'Trim1' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Trim1, arg, db ); break; } + try { GenericConvert( in->Trim1, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcTrimmedCurve to be a `SET [1:2] OF IfcTrimmingSelect`")); } } while(0); do { // convert the 'Trim2' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Trim2, arg, db ); break; } + try { GenericConvert( in->Trim2, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 2 to IfcTrimmedCurve to be a `SET [1:2] OF IfcTrimmingSelect`")); } } while(0); do { // convert the 'SenseAgreement' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->SenseAgreement, arg, db ); break; } + try { GenericConvert( in->SenseAgreement, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 3 to IfcTrimmedCurve to be a `IfcBoolean`")); } } while(0); do { // convert the 'MasterRepresentation' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->MasterRepresentation, arg, db ); break; } + try { GenericConvert( in->MasterRepresentation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 4 to IfcTrimmedCurve to be a `IfcTrimmingPreference`")); } } while(0); return base; @@ -5976,7 +5976,7 @@ template <> size_t GenericFill(const DB& db, const LIST& para size_t base = 0; if (params.GetSize() < 1) { throw STEP::TypeError("expected 1 arguments to IfcUnitAssignment"); } do { // convert the 'Units' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Units, arg, db ); break; } + try { GenericConvert( in->Units, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcUnitAssignment to be a `SET [1:?] OF IfcUnit`")); } } while(0); return base; @@ -6029,12 +6029,12 @@ template <> size_t GenericFill(const DB& db, const LIST& params, IfcV size_t base = GenericFill(db,params,static_cast(in)); if (params.GetSize() < 2) { throw STEP::TypeError("expected 2 arguments to IfcVector"); } do { // convert the 'Orientation' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Orientation, arg, db ); break; } + try { GenericConvert( in->Orientation, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 0 to IfcVector to be a `IfcDirection`")); } } while(0); do { // convert the 'Magnitude' argument boost::shared_ptr arg = params[base++]; - try { GenericConvert( in->Magnitude, arg, db ); break; } + try { GenericConvert( in->Magnitude, arg, db ); break; } catch (const TypeError& t) { throw TypeError(t.what() + std::string(" - expected argument 1 to IfcVector to be a `IfcLengthMeasure`")); } } while(0); return base; diff --git a/code/AssetLib/IFC/IFCReaderGen_4.h b/code/AssetLib/IFC/IFCReaderGen_4.h index 0f184cd02..abf021911 100644 --- a/code/AssetLib/IFC/IFCReaderGen_4.h +++ b/code/AssetLib/IFC/IFCReaderGen_4.h @@ -5,8 +5,8 @@ Open Asset Import Library (ASSIMP) Copyright (c) 2006-2020, ASSIMP Development Team All rights reserved. -Redistribution and use of this software in source and binary forms, -with or without modification, are permitted provided that the +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 @@ -23,16 +23,16 @@ following conditions are met: derived from this software without specific prior written permission of the ASSIMP Development Team. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +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 +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 +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 +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. ---------------------------------------------------------------------- @@ -51,12 +51,12 @@ namespace Schema_4 { using namespace STEP; using namespace STEP::EXPRESS; - - + + struct NotImplemented : public ObjectHelper { - + }; - + // ****************************************************************************** // IFC Custom data types diff --git a/code/AssetLib/IFC/IFCUtil.h b/code/AssetLib/IFC/IFCUtil.h index a1190746b..b18f35052 100644 --- a/code/AssetLib/IFC/IFCUtil.h +++ b/code/AssetLib/IFC/IFCUtil.h @@ -54,6 +54,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include + struct aiNode; namespace Assimp { @@ -137,14 +139,10 @@ struct TempOpening } // ------------------------------------------------------------------------------ - TempOpening(const IFC::Schema_2x3::IfcSolidModel* solid,IfcVector3 extrusionDir, - std::shared_ptr profileMesh, - std::shared_ptr profileMesh2D) - : solid(solid) - , extrusionDir(extrusionDir) - , profileMesh(profileMesh) - , profileMesh2D(profileMesh2D) - { + TempOpening(const IFC::Schema_2x3::IfcSolidModel *solid, IfcVector3 extrusionDir, + std::shared_ptr profileMesh, + std::shared_ptr profileMesh2D) : + solid(solid), extrusionDir(extrusionDir), profileMesh(std::move(profileMesh)), profileMesh2D(std::move(profileMesh2D)) { } // ------------------------------------------------------------------------------ diff --git a/code/AssetLib/Irr/IRRLoader.h b/code/AssetLib/Irr/IRRLoader.h index 535f6481d..da90902ed 100644 --- a/code/AssetLib/Irr/IRRLoader.h +++ b/code/AssetLib/Irr/IRRLoader.h @@ -273,7 +273,7 @@ private: std::vector& anims); private: - /// Configuration option: desired output FPS + /// Configuration option: desired output FPS double fps; /// Configuration option: speed flag was set? diff --git a/code/AssetLib/LWO/LWOAnimation.h b/code/AssetLib/LWO/LWOAnimation.h index 1ed8caf88..64aa5980a 100644 --- a/code/AssetLib/LWO/LWOAnimation.h +++ b/code/AssetLib/LWO/LWOAnimation.h @@ -114,7 +114,7 @@ enum PrePostBehaviour /** \brief Data structure for a LWO animation keyframe */ struct Key { - Key() AI_NO_EXCEPT + Key() AI_NO_EXCEPT : time() , value() , inter(IT_LINE) diff --git a/code/AssetLib/LWS/LWSLoader.cpp b/code/AssetLib/LWS/LWSLoader.cpp index d469a1064..cb07787fa 100644 --- a/code/AssetLib/LWS/LWSLoader.cpp +++ b/code/AssetLib/LWS/LWSLoader.cpp @@ -200,7 +200,7 @@ void LWSImporter::ReadEnvelope(const LWS::Element &dad, LWO::Envelope &fill) { // reserve enough storage std::list::const_iterator it = dad.children.begin(); - + fill.keys.reserve(strtoul10(it->tokens[1].c_str())); for (++it; it != dad.children.end(); ++it) { @@ -318,7 +318,7 @@ void LWSImporter::SetupNodeName(aiNode *nd, LWS::NodeDesc &src) { } else { ++s; } - std::string::size_type t = src.path.substr(s).find_last_of("."); + std::string::size_type t = src.path.substr(s).find_last_of('.'); nd->mName.length = ::ai_snprintf(nd->mName.data, MAXLEN, "%s_(%08X)", src.path.substr(s).substr(0, t).c_str(), combined); return; @@ -466,7 +466,7 @@ std::string LWSImporter::FindLWOFile(const std::string &in) { std::string tmp(in); if (in.length() > 3 && in[1] == ':' && in[2] != '\\' && in[2] != '/') { tmp = in[0] + (std::string(":\\") + in.substr(2)); - } + } if (io->Exists(tmp)) { return in; diff --git a/code/AssetLib/M3D/M3DImporter.cpp b/code/AssetLib/M3D/M3DImporter.cpp index 38bbd1d4a..efa1d5475 100644 --- a/code/AssetLib/M3D/M3DImporter.cpp +++ b/code/AssetLib/M3D/M3DImporter.cpp @@ -233,12 +233,12 @@ void M3DImporter::importMaterials(const M3DWrapper &m3d) { ASSIMP_LOG_DEBUG("M3D: importMaterials ", mScene->mNumMaterials); // add a default material as first - aiMaterial *mat = new aiMaterial; - mat->AddProperty(&name, AI_MATKEY_NAME); + aiMaterial *defaultMat = new aiMaterial; + defaultMat->AddProperty(&name, AI_MATKEY_NAME); c.a = 1.0f; c.b = c.g = c.r = 0.6f; - mat->AddProperty(&c, 1, AI_MATKEY_COLOR_DIFFUSE); - mScene->mMaterials[0] = mat; + defaultMat->AddProperty(&c, 1, AI_MATKEY_COLOR_DIFFUSE); + mScene->mMaterials[0] = defaultMat; if (!m3d->nummaterial || !m3d->material) { return; @@ -300,12 +300,12 @@ void M3DImporter::importMaterials(const M3DWrapper &m3d) { m->prop[j].value.textureid < m3d->numtexture && m3d->texture[m->prop[j].value.textureid].name) { name.Set(std::string(std::string(m3d->texture[m->prop[j].value.textureid].name) + ".png")); - mat->AddProperty(&name, aiTxProps[k].pKey, aiTxProps[k].type, aiTxProps[k].index); + newMat->AddProperty(&name, aiTxProps[k].pKey, aiTxProps[k].type, aiTxProps[k].index); n = 0; - mat->AddProperty(&n, 1, _AI_MATKEY_UVWSRC_BASE, aiProps[k].type, aiProps[k].index); + newMat->AddProperty(&n, 1, _AI_MATKEY_UVWSRC_BASE, aiProps[k].type, aiProps[k].index); } } - mScene->mMaterials[i + 1] = mat; + mScene->mMaterials[i + 1] = newMat; } } @@ -655,7 +655,7 @@ void M3DImporter::convertPose(const M3DWrapper &m3d, aiMatrix4x4 *m, unsigned in // ------------------------------------------------------------------------------------------------ // find a node by name -aiNode *M3DImporter::findNode(aiNode *pNode, aiString name) { +aiNode *M3DImporter::findNode(aiNode *pNode, const aiString &name) { ai_assert(pNode != nullptr); ai_assert(mScene != nullptr); diff --git a/code/AssetLib/M3D/M3DImporter.h b/code/AssetLib/M3D/M3DImporter.h index 7a2a9fbd3..05e8ced7b 100644 --- a/code/AssetLib/M3D/M3DImporter.h +++ b/code/AssetLib/M3D/M3DImporter.h @@ -89,8 +89,8 @@ private: // helper functions aiColor4D mkColor(uint32_t c); void convertPose(const M3DWrapper &m3d, aiMatrix4x4 *m, unsigned int posid, unsigned int orientid); - aiNode *findNode(aiNode *pNode, aiString name); - void calculateOffsetMatrix(aiNode *pNode, aiMatrix4x4 *m); + aiNode *findNode(aiNode *pNode, const aiString &name); + void calculateOffsetMatrix(aiNode *pNode, aiMatrix4x4 *m); void populateMesh(const M3DWrapper &m3d, aiMesh *pMesh, std::vector *faces, std::vector *verteces, std::vector *normals, std::vector *texcoords, std::vector *colors, std::vector *vertexids); diff --git a/code/AssetLib/M3D/M3DWrapper.h b/code/AssetLib/M3D/M3DWrapper.h index 782e908d2..dcb82a83a 100644 --- a/code/AssetLib/M3D/M3DWrapper.h +++ b/code/AssetLib/M3D/M3DWrapper.h @@ -46,6 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef AI_M3DWRAPPER_H_INC #define AI_M3DWRAPPER_H_INC + #if !(ASSIMP_BUILD_NO_EXPORT || ASSIMP_BUILD_NO_M3D_EXPORTER) || !ASSIMP_BUILD_NO_M3D_IMPORTER #include @@ -55,44 +56,75 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Assimp specific M3D configuration. Comment out these defines to remove functionality //#define ASSIMP_USE_M3D_READFILECB +// Share stb_image's PNG loader with other importers/exporters instead of bringing our own copy. +#define STBI_ONLY_PNG +#include + #include "m3d.h" namespace Assimp { + class IOSystem; +/// brief The M3D-Wrapper, provudes c++ access to the data. class M3DWrapper { - m3d_t *m3d_ = nullptr; - unsigned char *saved_output_ = nullptr; - public: - // Construct an empty M3D model + /// Construct an empty M3D model explicit M3DWrapper(); - // Construct an M3D model from provided buffer - // NOTE: The m3d.h SDK function does not mark the data as const. Have assumed it does not write. - // BUG: SECURITY: The m3d.h SDK cannot be informed of the buffer size. BUFFER OVERFLOW IS CERTAIN + /// Construct an M3D model from provided buffer + /// @note The m3d.h SDK function does not mark the data as const. Have assumed it does not write. + /// BUG: SECURITY: The m3d.h SDK cannot be informed of the buffer size. BUFFER OVERFLOW IS CERTAIN explicit M3DWrapper(IOSystem *pIOHandler, const std::vector &buffer); - ~M3DWrapper(); + /// Theclasss destructor. + ~M3DWrapper(); - void reset(); + /// Will reset the wrapper, all data will become nullptr. + void reset(); - // Name - inline std::string Name() const { - if (m3d_) return std::string(m3d_->name); - return std::string(); - } + // The Name access, empty string returned when no m3d instance. + std::string Name() const; - // Execute a save + /// Executes a save. unsigned char *Save(int quality, int flags, unsigned int &size); + + /// Clearer void ClearSave(); - inline explicit operator bool() const { return m3d_ != nullptr; } + /// True for m3d instance exists. + explicit operator bool() const; // Allow direct access to M3D API - inline m3d_t *operator->() const { return m3d_; } - inline m3d_t *M3D() const { return m3d_; } + m3d_t *operator->() const; + m3d_t *M3D() const; + +private: + m3d_t *m3d_ = nullptr; + unsigned char *saved_output_ = nullptr; }; + +inline std::string M3DWrapper::Name() const { + if (nullptr != m3d_) { + if (nullptr != m3d_->name) { + return std::string(m3d_->name); + } + } + return std::string(); +} + +inline M3DWrapper::operator bool() const { + return m3d_ != nullptr; +} + +inline m3d_t *M3DWrapper::operator->() const { + return m3d_; +} + +inline m3d_t *M3DWrapper::M3D() const { + return m3d_; +} + } // namespace Assimp #endif diff --git a/code/AssetLib/M3D/m3d.h b/code/AssetLib/M3D/m3d.h index 68265959e..3adcd5bef 100644 --- a/code/AssetLib/M3D/m3d.h +++ b/code/AssetLib/M3D/m3d.h @@ -622,1230 +622,6 @@ static m3dcd_t m3d_commandtypes[] = { #include #include -#if !defined(M3D_NOIMPORTER) && !defined(STBI_INCLUDE_STB_IMAGE_H) -/* PNG decompressor from - - stb_image - v2.23 - public domain image loader - http://nothings.org/stb_image.h -*/ -static const char *_m3dstbi__g_failure_reason; - -enum { - STBI_default = 0, - - STBI_grey = 1, - STBI_grey_alpha = 2, - STBI_rgb = 3, - STBI_rgb_alpha = 4 -}; - -enum { - STBI__SCAN_load = 0, - STBI__SCAN_type, - STBI__SCAN_header -}; - -typedef unsigned short _m3dstbi_us; - -typedef uint16_t _m3dstbi__uint16; -typedef int16_t _m3dstbi__int16; -typedef uint32_t _m3dstbi__uint32; -typedef int32_t _m3dstbi__int32; - -typedef struct -{ - _m3dstbi__uint32 img_x, img_y; - int img_n, img_out_n; - - void *io_user_data; - - int read_from_callbacks; - int buflen; - unsigned char buffer_start[128]; - - unsigned char *img_buffer, *img_buffer_end; - unsigned char *img_buffer_original, *img_buffer_original_end; -} _m3dstbi__context; - -typedef struct -{ - int bits_per_channel; - int num_channels; - int channel_order; -} _m3dstbi__result_info; - -#define STBI_ASSERT(v) -#ifdef _MSC_VER -#define STBI_NOTUSED(v) (void)(v) -#else -#define STBI_NOTUSED(v) (void)sizeof(v) -#endif -#define STBI__BYTECAST(x) ((unsigned char)((x)&255)) -#define STBI_MALLOC(sz) M3D_MALLOC(sz) -#define STBI_REALLOC(p, newsz) M3D_REALLOC(p, newsz) -#define STBI_FREE(p) M3D_FREE(p) -#define STBI_REALLOC_SIZED(p, oldsz, newsz) STBI_REALLOC(p, newsz) - -_inline static unsigned char _m3dstbi__get8(_m3dstbi__context *s) { - if (s->img_buffer < s->img_buffer_end) - return *s->img_buffer++; - return 0; -} - -static void _m3dstbi__skip(_m3dstbi__context *s, int n) { - if (n < 0) { - s->img_buffer = s->img_buffer_end; - return; - } - s->img_buffer += n; -} - -static int _m3dstbi__getn(_m3dstbi__context *s, unsigned char *buffer, int n) { - if (s->img_buffer + n <= s->img_buffer_end) { - memcpy(buffer, s->img_buffer, n); - s->img_buffer += n; - return 1; - } else - return 0; -} - -static int _m3dstbi__get16be(_m3dstbi__context *s) { - int z = _m3dstbi__get8(s); - return (z << 8) + _m3dstbi__get8(s); -} - -static _m3dstbi__uint32 _m3dstbi__get32be(_m3dstbi__context *s) { - _m3dstbi__uint32 z = _m3dstbi__get16be(s); - return (z << 16) + _m3dstbi__get16be(s); -} - -#define _m3dstbi__err(x, y) _m3dstbi__errstr(y) -static int _m3dstbi__errstr(const char *str) { - _m3dstbi__g_failure_reason = str; - return 0; -} - -_inline static void *_m3dstbi__malloc(size_t size) { - return STBI_MALLOC(size); -} - -static int _m3dstbi__addsizes_valid(int a, int b) { - if (b < 0) return 0; - return a <= 2147483647 - b; -} - -static int _m3dstbi__mul2sizes_valid(int a, int b) { - if (a < 0 || b < 0) return 0; - if (b == 0) return 1; - return a <= 2147483647 / b; -} - -static int _m3dstbi__mad2sizes_valid(int a, int b, int add) { - return _m3dstbi__mul2sizes_valid(a, b) && _m3dstbi__addsizes_valid(a * b, add); -} - -static int _m3dstbi__mad3sizes_valid(int a, int b, int c, int add) { - return _m3dstbi__mul2sizes_valid(a, b) && _m3dstbi__mul2sizes_valid(a * b, c) && - _m3dstbi__addsizes_valid(a * b * c, add); -} - -static void *_m3dstbi__malloc_mad2(int a, int b, int add) { - if (!_m3dstbi__mad2sizes_valid(a, b, add)) return NULL; - return _m3dstbi__malloc(a * b + add); -} - -static void *_m3dstbi__malloc_mad3(int a, int b, int c, int add) { - if (!_m3dstbi__mad3sizes_valid(a, b, c, add)) return NULL; - return _m3dstbi__malloc(a * b * c + add); -} - -static unsigned char _m3dstbi__compute_y(int r, int g, int b) { - return (unsigned char)(((r * 77) + (g * 150) + (29 * b)) >> 8); -} - -static unsigned char *_m3dstbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) { - int i, j; - unsigned char *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (unsigned char *)_m3dstbi__malloc_mad3(req_comp, x, y, 0); - if (good == NULL) { - STBI_FREE(data); - _m3dstbi__err("outofmem", "Out of memory"); - return NULL; - } - - for (j = 0; j < (int)y; ++j) { - unsigned char *src = data + j * x * img_n; - unsigned char *dest = good + j * x * req_comp; - -#define STBI__COMBO(a, b) ((a)*8 + (b)) -#define STBI__CASE(a, b) \ - case STBI__COMBO(a, b): \ - for (i = x - 1; i >= 0; --i, src += a, dest += b) - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1, 2) { dest[0] = src[0], dest[1] = 255; } - break; - STBI__CASE(1, 3) { dest[0] = dest[1] = dest[2] = src[0]; } - break; - STBI__CASE(1, 4) { dest[0] = dest[1] = dest[2] = src[0], dest[3] = 255; } - break; - STBI__CASE(2, 1) { dest[0] = src[0]; } - break; - STBI__CASE(2, 3) { dest[0] = dest[1] = dest[2] = src[0]; } - break; - STBI__CASE(2, 4) { dest[0] = dest[1] = dest[2] = src[0], dest[3] = src[1]; } - break; - STBI__CASE(3, 4) { dest[0] = src[0], dest[1] = src[1], dest[2] = src[2], dest[3] = 255; } - break; - STBI__CASE(3, 1) { dest[0] = _m3dstbi__compute_y(src[0], src[1], src[2]); } - break; - STBI__CASE(3, 2) { dest[0] = _m3dstbi__compute_y(src[0], src[1], src[2]), dest[1] = 255; } - break; - STBI__CASE(4, 1) { dest[0] = _m3dstbi__compute_y(src[0], src[1], src[2]); } - break; - STBI__CASE(4, 2) { dest[0] = _m3dstbi__compute_y(src[0], src[1], src[2]), dest[1] = src[3]; } - break; - STBI__CASE(4, 3) { dest[0] = src[0], dest[1] = src[1], dest[2] = src[2]; } - break; - default: STBI_ASSERT(0); - } -#undef STBI__CASE - } - - STBI_FREE(data); - return good; -} - -static _m3dstbi__uint16 _m3dstbi__compute_y_16(int r, int g, int b) { - return (_m3dstbi__uint16)(((r * 77) + (g * 150) + (29 * b)) >> 8); -} - -static _m3dstbi__uint16 *_m3dstbi__convert_format16(_m3dstbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) { - int i, j; - _m3dstbi__uint16 *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (_m3dstbi__uint16 *)_m3dstbi__malloc(req_comp * x * y * 2); - if (good == NULL) { - STBI_FREE(data); - _m3dstbi__err("outofmem", "Out of memory"); - return NULL; - } - - for (j = 0; j < (int)y; ++j) { - _m3dstbi__uint16 *src = data + j * x * img_n; - _m3dstbi__uint16 *dest = good + j * x * req_comp; - -#define STBI__COMBO(a, b) ((a)*8 + (b)) -#define STBI__CASE(a, b) \ - case STBI__COMBO(a, b): \ - for (i = x - 1; i >= 0; --i, src += a, dest += b) - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1, 2) { dest[0] = src[0], dest[1] = 0xffff; } - break; - STBI__CASE(1, 3) { dest[0] = dest[1] = dest[2] = src[0]; } - break; - STBI__CASE(1, 4) { dest[0] = dest[1] = dest[2] = src[0], dest[3] = 0xffff; } - break; - STBI__CASE(2, 1) { dest[0] = src[0]; } - break; - STBI__CASE(2, 3) { dest[0] = dest[1] = dest[2] = src[0]; } - break; - STBI__CASE(2, 4) { dest[0] = dest[1] = dest[2] = src[0], dest[3] = src[1]; } - break; - STBI__CASE(3, 4) { dest[0] = src[0], dest[1] = src[1], dest[2] = src[2], dest[3] = 0xffff; } - break; - STBI__CASE(3, 1) { dest[0] = _m3dstbi__compute_y_16(src[0], src[1], src[2]); } - break; - STBI__CASE(3, 2) { dest[0] = _m3dstbi__compute_y_16(src[0], src[1], src[2]), dest[1] = 0xffff; } - break; - STBI__CASE(4, 1) { dest[0] = _m3dstbi__compute_y_16(src[0], src[1], src[2]); } - break; - STBI__CASE(4, 2) { dest[0] = _m3dstbi__compute_y_16(src[0], src[1], src[2]), dest[1] = src[3]; } - break; - STBI__CASE(4, 3) { dest[0] = src[0], dest[1] = src[1], dest[2] = src[2]; } - break; - default: STBI_ASSERT(0); - } -#undef STBI__CASE - } - - STBI_FREE(data); - return good; -} - -#define STBI__ZFAST_BITS 9 -#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) - -typedef struct -{ - _m3dstbi__uint16 fast[1 << STBI__ZFAST_BITS]; - _m3dstbi__uint16 firstcode[16]; - int maxcode[17]; - _m3dstbi__uint16 firstsymbol[16]; - unsigned char size[288]; - _m3dstbi__uint16 value[288]; -} _m3dstbi__zhuffman; - -_inline static int _m3dstbi__bitreverse16(int n) { - n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); - n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); - n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); - n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); - return n; -} - -_inline static int _m3dstbi__bit_reverse(int v, int bits) { - STBI_ASSERT(bits <= 16); - return _m3dstbi__bitreverse16(v) >> (16 - bits); -} - -static int _m3dstbi__zbuild_huffman(_m3dstbi__zhuffman *z, unsigned char *sizelist, int num) { - int i, k = 0; - int code, next_code[16], sizes[17]; - - memset(sizes, 0, sizeof(sizes)); - memset(z->fast, 0, sizeof(z->fast)); - for (i = 0; i < num; ++i) - ++sizes[sizelist[i]]; - sizes[0] = 0; - for (i = 1; i < 16; ++i) - if (sizes[i] > (1 << i)) - return _m3dstbi__err("bad sizes", "Corrupt PNG"); - code = 0; - for (i = 1; i < 16; ++i) { - next_code[i] = code; - z->firstcode[i] = (_m3dstbi__uint16)code; - z->firstsymbol[i] = (_m3dstbi__uint16)k; - code = (code + sizes[i]); - if (sizes[i]) - if (code - 1 >= (1 << i)) return _m3dstbi__err("bad codelengths", "Corrupt PNG"); - z->maxcode[i] = code << (16 - i); - code <<= 1; - k += sizes[i]; - } - z->maxcode[16] = 0x10000; - for (i = 0; i < num; ++i) { - int s = sizelist[i]; - if (s) { - int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; - _m3dstbi__uint16 fastv = (_m3dstbi__uint16)((s << 9) | i); - z->size[c] = (unsigned char)s; - z->value[c] = (_m3dstbi__uint16)i; - if (s <= STBI__ZFAST_BITS) { - int j = _m3dstbi__bit_reverse(next_code[s], s); - while (j < (1 << STBI__ZFAST_BITS)) { - z->fast[j] = fastv; - j += (1 << s); - } - } - ++next_code[s]; - } - } - return 1; -} - -typedef struct -{ - unsigned char *zbuffer, *zbuffer_end; - int num_bits; - _m3dstbi__uint32 code_buffer; - - char *zout; - char *zout_start; - char *zout_end; - int z_expandable; - - _m3dstbi__zhuffman z_length, z_distance; -} _m3dstbi__zbuf; - -_inline static unsigned char _m3dstbi__zget8(_m3dstbi__zbuf *z) { - if (z->zbuffer >= z->zbuffer_end) return 0; - return *z->zbuffer++; -} - -static void _m3dstbi__fill_bits(_m3dstbi__zbuf *z) { - do { - STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); - z->code_buffer |= (unsigned int)_m3dstbi__zget8(z) << z->num_bits; - z->num_bits += 8; - } while (z->num_bits <= 24); -} - -_inline static unsigned int _m3dstbi__zreceive(_m3dstbi__zbuf *z, int n) { - unsigned int k; - if (z->num_bits < n) _m3dstbi__fill_bits(z); - k = z->code_buffer & ((1 << n) - 1); - z->code_buffer >>= n; - z->num_bits -= n; - return k; -} - -static int _m3dstbi__zhuffman_decode_slowpath(_m3dstbi__zbuf *a, _m3dstbi__zhuffman *z) { - int b, s, k; - k = _m3dstbi__bit_reverse(a->code_buffer, 16); - for (s = STBI__ZFAST_BITS + 1;; ++s) - if (k < z->maxcode[s]) - break; - if (s == 16) return -1; - b = (k >> (16 - s)) - z->firstcode[s] + z->firstsymbol[s]; - STBI_ASSERT(z->size[b] == s); - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; -} - -_inline static int _m3dstbi__zhuffman_decode(_m3dstbi__zbuf *a, _m3dstbi__zhuffman *z) { - int b, s; - if (a->num_bits < 16) _m3dstbi__fill_bits(a); - b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; - if (b) { - s = b >> 9; - a->code_buffer >>= s; - a->num_bits -= s; - return b & 511; - } - return _m3dstbi__zhuffman_decode_slowpath(a, z); -} - -static int _m3dstbi__zexpand(_m3dstbi__zbuf *z, char *zout, int n) { - char *q; - int cur, limit, old_limit; - z->zout = zout; - if (!z->z_expandable) return _m3dstbi__err("output buffer limit", "Corrupt PNG"); - cur = (int)(z->zout - z->zout_start); - limit = old_limit = (int)(z->zout_end - z->zout_start); - while (cur + n > limit) - limit *= 2; - q = (char *)STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); - STBI_NOTUSED(old_limit); - if (q == NULL) return _m3dstbi__err("outofmem", "Out of memory"); - z->zout_start = q; - z->zout = q + cur; - z->zout_end = q + limit; - return 1; -} - -static int _m3dstbi__zlength_base[31] = { - 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, - 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, - 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0 -}; - -static int _m3dstbi__zlength_extra[31] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 0, 0 }; - -static int _m3dstbi__zdist_base[32] = { 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, - 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577, 0, 0 }; - -static int _m3dstbi__zdist_extra[32] = { 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 }; - -static int _m3dstbi__parse_huffman_block(_m3dstbi__zbuf *a) { - char *zout = a->zout; - for (;;) { - int z = _m3dstbi__zhuffman_decode(a, &a->z_length); - if (z < 256) { - if (z < 0) return _m3dstbi__err("bad huffman code", "Corrupt PNG"); - if (zout >= a->zout_end) { - if (!_m3dstbi__zexpand(a, zout, 1)) return 0; - zout = a->zout; - } - *zout++ = (char)z; - } else { - unsigned char *p; - int len, dist; - if (z == 256) { - a->zout = zout; - return 1; - } - z -= 257; - len = _m3dstbi__zlength_base[z]; - if (_m3dstbi__zlength_extra[z]) len += _m3dstbi__zreceive(a, _m3dstbi__zlength_extra[z]); - z = _m3dstbi__zhuffman_decode(a, &a->z_distance); - if (z < 0) return _m3dstbi__err("bad huffman code", "Corrupt PNG"); - dist = _m3dstbi__zdist_base[z]; - if (_m3dstbi__zdist_extra[z]) dist += _m3dstbi__zreceive(a, _m3dstbi__zdist_extra[z]); - if (zout - a->zout_start < dist) return _m3dstbi__err("bad dist", "Corrupt PNG"); - if (zout + len > a->zout_end) { - if (!_m3dstbi__zexpand(a, zout, len)) return 0; - zout = a->zout; - } - p = (unsigned char *)(zout - dist); - if (dist == 1) { - unsigned char v = *p; - if (len) { - do - *zout++ = v; - while (--len); - } - } else { - if (len) { - do - *zout++ = *p++; - while (--len); - } - } - } - } -} - -static int _m3dstbi__compute_huffman_codes(_m3dstbi__zbuf *a) { - static unsigned char length_dezigzag[19] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; - _m3dstbi__zhuffman z_codelength; - unsigned char lencodes[286 + 32 + 137]; - unsigned char codelength_sizes[19]; - int i, n; - - int hlit = _m3dstbi__zreceive(a, 5) + 257; - int hdist = _m3dstbi__zreceive(a, 5) + 1; - int hclen = _m3dstbi__zreceive(a, 4) + 4; - int ntot = hlit + hdist; - - memset(codelength_sizes, 0, sizeof(codelength_sizes)); - for (i = 0; i < hclen; ++i) { - int s = _m3dstbi__zreceive(a, 3); - codelength_sizes[length_dezigzag[i]] = (unsigned char)s; - } - if (!_m3dstbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; - - n = 0; - while (n < ntot) { - int c = _m3dstbi__zhuffman_decode(a, &z_codelength); - if (c < 0 || c >= 19) return _m3dstbi__err("bad codelengths", "Corrupt PNG"); - if (c < 16) - lencodes[n++] = (unsigned char)c; - else { - unsigned char fill = 0; - if (c == 16) { - c = _m3dstbi__zreceive(a, 2) + 3; - if (n == 0) return _m3dstbi__err("bad codelengths", "Corrupt PNG"); - fill = lencodes[n - 1]; - } else if (c == 17) - c = _m3dstbi__zreceive(a, 3) + 3; - else { - STBI_ASSERT(c == 18); - c = _m3dstbi__zreceive(a, 7) + 11; - } - if (ntot - n < c) return _m3dstbi__err("bad codelengths", "Corrupt PNG"); - memset(lencodes + n, fill, c); - n += c; - } - } - if (n != ntot) return _m3dstbi__err("bad codelengths", "Corrupt PNG"); - if (!_m3dstbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; - if (!_m3dstbi__zbuild_huffman(&a->z_distance, lencodes + hlit, hdist)) return 0; - return 1; -} - -_inline static int _m3dstbi__parse_uncompressed_block(_m3dstbi__zbuf *a) { - unsigned char header[4]; - int len, nlen, k; - if (a->num_bits & 7) - _m3dstbi__zreceive(a, a->num_bits & 7); - k = 0; - while (a->num_bits > 0) { - header[k++] = (unsigned char)(a->code_buffer & 255); - a->code_buffer >>= 8; - a->num_bits -= 8; - } - STBI_ASSERT(a->num_bits == 0); - while (k < 4) - header[k++] = _m3dstbi__zget8(a); - len = header[1] * 256 + header[0]; - nlen = header[3] * 256 + header[2]; - if (nlen != (len ^ 0xffff)) return _m3dstbi__err("zlib corrupt", "Corrupt PNG"); - if (a->zbuffer + len > a->zbuffer_end) return _m3dstbi__err("read past buffer", "Corrupt PNG"); - if (a->zout + len > a->zout_end) - if (!_m3dstbi__zexpand(a, a->zout, len)) return 0; - memcpy(a->zout, a->zbuffer, len); - a->zbuffer += len; - a->zout += len; - return 1; -} - -static int _m3dstbi__parse_zlib_header(_m3dstbi__zbuf *a) { - int cmf = _m3dstbi__zget8(a); - int cm = cmf & 15; - /* int cinfo = cmf >> 4; */ - int flg = _m3dstbi__zget8(a); - if ((cmf * 256 + flg) % 31 != 0) return _m3dstbi__err("bad zlib header", "Corrupt PNG"); - if (flg & 32) return _m3dstbi__err("no preset dict", "Corrupt PNG"); - if (cm != 8) return _m3dstbi__err("bad compression", "Corrupt PNG"); - return 1; -} - -static unsigned char _m3dstbi__zdefault_length[288], _m3dstbi__zdefault_distance[32]; -static void _m3dstbi__init_zdefaults(void) { - int i; - for (i = 0; i <= 143; ++i) - _m3dstbi__zdefault_length[i] = 8; - for (; i <= 255; ++i) - _m3dstbi__zdefault_length[i] = 9; - for (; i <= 279; ++i) - _m3dstbi__zdefault_length[i] = 7; - for (; i <= 287; ++i) - _m3dstbi__zdefault_length[i] = 8; - - for (i = 0; i <= 31; ++i) - _m3dstbi__zdefault_distance[i] = 5; -} - -static int _m3dstbi__parse_zlib(_m3dstbi__zbuf *a, int parse_header) { - int final, type; - if (parse_header) - if (!_m3dstbi__parse_zlib_header(a)) return 0; - a->num_bits = 0; - a->code_buffer = 0; - do { - final = _m3dstbi__zreceive(a, 1); - type = _m3dstbi__zreceive(a, 2); - if (type == 0) { - if (!_m3dstbi__parse_uncompressed_block(a)) return 0; - } else if (type == 3) { - return 0; - } else { - if (type == 1) { - if (!_m3dstbi__zbuild_huffman(&a->z_length, _m3dstbi__zdefault_length, 288)) return 0; - if (!_m3dstbi__zbuild_huffman(&a->z_distance, _m3dstbi__zdefault_distance, 32)) return 0; - } else { - if (!_m3dstbi__compute_huffman_codes(a)) return 0; - } - if (!_m3dstbi__parse_huffman_block(a)) return 0; - } - } while (!final); - return 1; -} - -static int _m3dstbi__do_zlib(_m3dstbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) { - a->zout_start = obuf; - a->zout = obuf; - a->zout_end = obuf + olen; - a->z_expandable = exp; - _m3dstbi__init_zdefaults(); - return _m3dstbi__parse_zlib(a, parse_header); -} - -char *_m3dstbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) { - _m3dstbi__zbuf a; - char *p = (char *)_m3dstbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (unsigned char *)buffer; - a.zbuffer_end = (unsigned char *)buffer + len; - if (_m3dstbi__do_zlib(&a, p, initial_size, 1, parse_header)) { - if (outlen) *outlen = (int)(a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -typedef struct -{ - _m3dstbi__uint32 length; - _m3dstbi__uint32 type; -} _m3dstbi__pngchunk; - -static _m3dstbi__pngchunk _m3dstbi__get_chunk_header(_m3dstbi__context *s) { - _m3dstbi__pngchunk c; - c.length = _m3dstbi__get32be(s); - c.type = _m3dstbi__get32be(s); - return c; -} - -_inline static int _m3dstbi__check_png_header(_m3dstbi__context *s) { - static unsigned char png_sig[8] = { 137, 80, 78, 71, 13, 10, 26, 10 }; - int i; - for (i = 0; i < 8; ++i) - if (_m3dstbi__get8(s) != png_sig[i]) return _m3dstbi__err("bad png sig", "Not a PNG"); - return 1; -} - -typedef struct -{ - _m3dstbi__context *s; - unsigned char *idata, *expanded, *out; - int depth; -} _m3dstbi__png; - -enum { - STBI__F_none = 0, - STBI__F_sub = 1, - STBI__F_up = 2, - STBI__F_avg = 3, - STBI__F_paeth = 4, - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static unsigned char first_row_filter[5] = { - STBI__F_none, - STBI__F_sub, - STBI__F_none, - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static int _m3dstbi__paeth(int a, int b, int c) { - int p = a + b - c; - int pa = abs(p - a); - int pb = abs(p - b); - int pc = abs(p - c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; -} - -static unsigned char _m3dstbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0, 0, 0, 0x01 }; - -static int _m3dstbi__create_png_image_raw(_m3dstbi__png *a, unsigned char *raw, _m3dstbi__uint32 raw_len, int out_n, _m3dstbi__uint32 x, _m3dstbi__uint32 y, int depth, int color) { - int bytes = (depth == 16 ? 2 : 1); - _m3dstbi__context *s = a->s; - _m3dstbi__uint32 i, j, stride = x * out_n * bytes; - _m3dstbi__uint32 img_len, img_width_bytes; - int k; - int img_n = s->img_n; - - int output_bytes = out_n * bytes; - int filter_bytes = img_n * bytes; - int width = x; - - STBI_ASSERT(out_n == s->img_n || out_n == s->img_n + 1); - a->out = (unsigned char *)_m3dstbi__malloc_mad3(x, y, output_bytes, 0); - if (!a->out) return _m3dstbi__err("outofmem", "Out of memory"); - - if (!_m3dstbi__mad3sizes_valid(img_n, x, depth, 7)) return _m3dstbi__err("too large", "Corrupt PNG"); - img_width_bytes = (((img_n * x * depth) + 7) >> 3); - img_len = (img_width_bytes + 1) * y; - if (s->img_x == x && s->img_y == y) { - if (raw_len != img_len) return _m3dstbi__err("not enough pixels", "Corrupt PNG"); - } else { - if (raw_len < img_len) return _m3dstbi__err("not enough pixels", "Corrupt PNG"); - } - - for (j = 0; j < y; ++j) { - unsigned char *cur = a->out + stride * j; - unsigned char *prior = cur - stride; - int filter = *raw++; - - if (filter > 4) - return _m3dstbi__err("invalid filter", "Corrupt PNG"); - - if (depth < 8) { - STBI_ASSERT(img_width_bytes <= x); - cur += x * out_n - img_width_bytes; - filter_bytes = 1; - width = img_width_bytes; - } - prior = cur - stride; - - if (j == 0) filter = first_row_filter[filter]; - - for (k = 0; k < filter_bytes; ++k) { - switch (filter) { - case STBI__F_none: cur[k] = raw[k]; break; - case STBI__F_sub: cur[k] = raw[k]; break; - case STBI__F_up: cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - case STBI__F_avg: cur[k] = STBI__BYTECAST(raw[k] + (prior[k] >> 1)); break; - case STBI__F_paeth: cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(0, prior[k], 0)); break; - case STBI__F_avg_first: cur[k] = raw[k]; break; - case STBI__F_paeth_first: cur[k] = raw[k]; break; - } - } - - if (depth == 8) { - if (img_n != out_n) - cur[img_n] = 255; - raw += img_n; - cur += out_n; - prior += out_n; - } else if (depth == 16) { - if (img_n != out_n) { - cur[filter_bytes] = 255; - cur[filter_bytes + 1] = 255; - } - raw += filter_bytes; - cur += output_bytes; - prior += output_bytes; - } else { - raw += 1; - cur += 1; - prior += 1; - } - - if (depth < 8 || img_n == out_n) { - int nk = (width - 1) * filter_bytes; -#define STBI__CASE(f) \ - case f: \ - for (k = 0; k < nk; ++k) - switch (filter) { - case STBI__F_none: - memcpy(cur, raw, nk); - break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k - filter_bytes]); } - break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } - break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k - filter_bytes]) >> 1)); } - break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(cur[k - filter_bytes], prior[k], prior[k - filter_bytes])); } - break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k - filter_bytes] >> 1)); } - break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(cur[k - filter_bytes], 0, 0)); } - break; - } -#undef STBI__CASE - raw += nk; - } else { - STBI_ASSERT(img_n + 1 == out_n); -#define STBI__CASE(f) \ - case f: \ - for (i = x - 1; i >= 1; --i, cur[filter_bytes] = 255, raw += filter_bytes, cur += output_bytes, prior += output_bytes) \ - for (k = 0; k < filter_bytes; ++k) - switch (filter) { - STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } - break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k - output_bytes]); } - break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } - break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k - output_bytes]) >> 1)); } - break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(cur[k - output_bytes], prior[k], prior[k - output_bytes])); } - break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k - output_bytes] >> 1)); } - break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + _m3dstbi__paeth(cur[k - output_bytes], 0, 0)); } - break; - } -#undef STBI__CASE - - if (depth == 16) { - cur = a->out + stride * j; - for (i = 0; i < x; ++i, cur += output_bytes) { - cur[filter_bytes + 1] = 255; - } - } - } - } - - if (depth < 8) { - for (j = 0; j < y; ++j) { - unsigned char *cur = a->out + stride * j; - unsigned char *in = a->out + stride * j + x * out_n - img_width_bytes; - unsigned char scale = (color == 0) ? _m3dstbi__depth_scale_table[depth] : 1; - - if (depth == 4) { - for (k = x * img_n; k >= 2; k -= 2, ++in) { - *cur++ = scale * ((*in >> 4)); - *cur++ = scale * ((*in) & 0x0f); - } - if (k > 0) *cur++ = scale * ((*in >> 4)); - } else if (depth == 2) { - for (k = x * img_n; k >= 4; k -= 4, ++in) { - *cur++ = scale * ((*in >> 6)); - *cur++ = scale * ((*in >> 4) & 0x03); - *cur++ = scale * ((*in >> 2) & 0x03); - *cur++ = scale * ((*in) & 0x03); - } - if (k > 0) *cur++ = scale * ((*in >> 6)); - if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); - if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); - } else if (depth == 1) { - for (k = x * img_n; k >= 8; k -= 8, ++in) { - *cur++ = scale * ((*in >> 7)); - *cur++ = scale * ((*in >> 6) & 0x01); - *cur++ = scale * ((*in >> 5) & 0x01); - *cur++ = scale * ((*in >> 4) & 0x01); - *cur++ = scale * ((*in >> 3) & 0x01); - *cur++ = scale * ((*in >> 2) & 0x01); - *cur++ = scale * ((*in >> 1) & 0x01); - *cur++ = scale * ((*in) & 0x01); - } - if (k > 0) *cur++ = scale * ((*in >> 7)); - if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); - if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); - if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); - if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); - if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); - if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); - } - if (img_n != out_n) { - int q; - cur = a->out + stride * j; - if (img_n == 1) { - for (q = x - 1; q >= 0; --q) { - cur[q * 2 + 1] = 255; - cur[q * 2 + 0] = cur[q]; - } - } else { - STBI_ASSERT(img_n == 3); - for (q = x - 1; q >= 0; --q) { - cur[q * 4 + 3] = 255; - cur[q * 4 + 2] = cur[q * 3 + 2]; - cur[q * 4 + 1] = cur[q * 3 + 1]; - cur[q * 4 + 0] = cur[q * 3 + 0]; - } - } - } - } - } else if (depth == 16) { - unsigned char *cur = a->out; - _m3dstbi__uint16 *cur16 = (_m3dstbi__uint16 *)cur; - - for (i = 0; i < x * y * out_n; ++i, cur16++, cur += 2) { - *cur16 = (cur[0] << 8) | cur[1]; - } - } - - return 1; -} - -static int _m3dstbi__create_png_image(_m3dstbi__png *a, unsigned char *image_data, _m3dstbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) { - int bytes = (depth == 16 ? 2 : 1); - int out_bytes = out_n * bytes; - unsigned char *final; - int p; - if (!interlaced) - return _m3dstbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); - - final = (unsigned char *)_m3dstbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); - for (p = 0; p < 7; ++p) { - int xorig[] = { 0, 4, 0, 2, 0, 1, 0 }; - int yorig[] = { 0, 0, 4, 0, 2, 0, 1 }; - int xspc[] = { 8, 8, 4, 4, 2, 2, 1 }; - int yspc[] = { 8, 8, 8, 4, 4, 2, 2 }; - int i, j, x, y; - x = (a->s->img_x - xorig[p] + xspc[p] - 1) / xspc[p]; - y = (a->s->img_y - yorig[p] + yspc[p] - 1) / yspc[p]; - if (x && y) { - _m3dstbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; - if (!_m3dstbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { - STBI_FREE(final); - return 0; - } - for (j = 0; j < y; ++j) { - for (i = 0; i < x; ++i) { - int out_y = j * yspc[p] + yorig[p]; - int out_x = i * xspc[p] + xorig[p]; - memcpy(final + out_y * a->s->img_x * out_bytes + out_x * out_bytes, - a->out + (j * x + i) * out_bytes, out_bytes); - } - } - STBI_FREE(a->out); - image_data += img_len; - image_data_len -= img_len; - } - } - a->out = final; - - return 1; -} - -static int _m3dstbi__compute_transparency(_m3dstbi__png *z, unsigned char* tc, int out_n) { - _m3dstbi__context *s = z->s; - _m3dstbi__uint32 i, pixel_count = s->img_x * s->img_y; - unsigned char *p = z->out; - - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i = 0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 255); - p += 2; - } - } else { - for (i = 0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int _m3dstbi__compute_transparency16(_m3dstbi__png *z, _m3dstbi__uint16 tc[3], int out_n) { - _m3dstbi__context *s = z->s; - _m3dstbi__uint32 i, pixel_count = s->img_x * s->img_y; - _m3dstbi__uint16 *p = (_m3dstbi__uint16 *)z->out; - - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i = 0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 65535); - p += 2; - } - } else { - for (i = 0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int _m3dstbi__expand_png_palette(_m3dstbi__png *a, unsigned char *palette, int len, int pal_img_n) { - _m3dstbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; - unsigned char *p, *temp_out, *orig = a->out; - - p = (unsigned char *)_m3dstbi__malloc_mad2(pixel_count, pal_img_n, 0); - if (p == NULL) return _m3dstbi__err("outofmem", "Out of memory"); - - temp_out = p; - - if (pal_img_n == 3) { - for (i = 0; i < pixel_count; ++i) { - int n = orig[i] * 4; - p[0] = palette[n]; - p[1] = palette[n + 1]; - p[2] = palette[n + 2]; - p += 3; - } - } else { - for (i = 0; i < pixel_count; ++i) { - int n = orig[i] * 4; - p[0] = palette[n]; - p[1] = palette[n + 1]; - p[2] = palette[n + 2]; - p[3] = palette[n + 3]; - p += 4; - } - } - STBI_FREE(a->out); - a->out = temp_out; - - STBI_NOTUSED(len); - - return 1; -} - -#define STBI__PNG_TYPE(a, b, c, d) (((unsigned)(a) << 24) + ((unsigned)(b) << 16) + ((unsigned)(c) << 8) + (unsigned)(d)) - -static int _m3dstbi__parse_png_file(_m3dstbi__png *z, int scan, int req_comp) { - unsigned char palette[1024], pal_img_n = 0; - unsigned char has_trans = 0, tc[3] = {}; - _m3dstbi__uint16 tc16[3] = {}; - _m3dstbi__uint32 ioff = 0, idata_limit = 0, i, pal_len = 0; - int first = 1, k, interlace = 0, color = 0; - _m3dstbi__context *s = z->s; - - z->expanded = NULL; - z->idata = NULL; - z->out = NULL; - - if (!_m3dstbi__check_png_header(s)) return 0; - - if (scan == STBI__SCAN_type) return 1; - - for (;;) { - _m3dstbi__pngchunk c = _m3dstbi__get_chunk_header(s); - switch (c.type) { - case STBI__PNG_TYPE('C', 'g', 'B', 'I'): - _m3dstbi__skip(s, c.length); - break; - case STBI__PNG_TYPE('I', 'H', 'D', 'R'): { - int comp, filter; - if (!first) return _m3dstbi__err("multiple IHDR", "Corrupt PNG"); - first = 0; - if (c.length != 13) return _m3dstbi__err("bad IHDR len", "Corrupt PNG"); - s->img_x = _m3dstbi__get32be(s); - if (s->img_x > (1 << 24)) return _m3dstbi__err("too large", "Very large image (corrupt?)"); - s->img_y = _m3dstbi__get32be(s); - if (s->img_y > (1 << 24)) return _m3dstbi__err("too large", "Very large image (corrupt?)"); - z->depth = _m3dstbi__get8(s); - if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return _m3dstbi__err("1/2/4/8/16-bit only", "PNG not supported: 1/2/4/8/16-bit only"); - color = _m3dstbi__get8(s); - if (color > 6) return _m3dstbi__err("bad ctype", "Corrupt PNG"); - if (color == 3 && z->depth == 16) return _m3dstbi__err("bad ctype", "Corrupt PNG"); - if (color == 3) - pal_img_n = 3; - else if (color & 1) - return _m3dstbi__err("bad ctype", "Corrupt PNG"); - comp = _m3dstbi__get8(s); - if (comp) return _m3dstbi__err("bad comp method", "Corrupt PNG"); - filter = _m3dstbi__get8(s); - if (filter) return _m3dstbi__err("bad filter method", "Corrupt PNG"); - interlace = _m3dstbi__get8(s); - if (interlace > 1) return _m3dstbi__err("bad interlace method", "Corrupt PNG"); - if (!s->img_x || !s->img_y) return _m3dstbi__err("0-pixel image", "Corrupt PNG"); - if (!pal_img_n) { - s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return _m3dstbi__err("too large", "Image too large to decode"); - if (scan == STBI__SCAN_header) return 1; - } else { - s->img_n = 1; - if ((1 << 30) / s->img_x / 4 < s->img_y) return _m3dstbi__err("too large", "Corrupt PNG"); - } - break; - } - - case STBI__PNG_TYPE('P', 'L', 'T', 'E'): { - if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); - if (c.length > 256 * 3) return _m3dstbi__err("invalid PLTE", "Corrupt PNG"); - pal_len = c.length / 3; - if (pal_len * 3 != c.length) return _m3dstbi__err("invalid PLTE", "Corrupt PNG"); - for (i = 0; i < pal_len; ++i) { - palette[i * 4 + 0] = _m3dstbi__get8(s); - palette[i * 4 + 1] = _m3dstbi__get8(s); - palette[i * 4 + 2] = _m3dstbi__get8(s); - palette[i * 4 + 3] = 255; - } - break; - } - - case STBI__PNG_TYPE('t', 'R', 'N', 'S'): { - if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); - if (z->idata) return _m3dstbi__err("tRNS after IDAT", "Corrupt PNG"); - if (pal_img_n) { - if (scan == STBI__SCAN_header) { - s->img_n = 4; - return 1; - } - if (pal_len == 0) return _m3dstbi__err("tRNS before PLTE", "Corrupt PNG"); - if (c.length > pal_len) return _m3dstbi__err("bad tRNS len", "Corrupt PNG"); - pal_img_n = 4; - for (i = 0; i < c.length; ++i) - palette[i * 4 + 3] = _m3dstbi__get8(s); - } else { - if (!(s->img_n & 1)) return _m3dstbi__err("tRNS with alpha", "Corrupt PNG"); - if (c.length != (_m3dstbi__uint32)s->img_n * 2) return _m3dstbi__err("bad tRNS len", "Corrupt PNG"); - has_trans = 1; - if (z->depth == 16) { - for (k = 0; k < s->img_n; ++k) - tc16[k] = (_m3dstbi__uint16)_m3dstbi__get16be(s); - } else { - for (k = 0; k < s->img_n; ++k) - tc[k] = (unsigned char)(_m3dstbi__get16be(s) & 255) * _m3dstbi__depth_scale_table[z->depth]; - } - } - break; - } - - case STBI__PNG_TYPE('I', 'D', 'A', 'T'): { - if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); - if (pal_img_n && !pal_len) return _m3dstbi__err("no PLTE", "Corrupt PNG"); - if (scan == STBI__SCAN_header) { - s->img_n = pal_img_n; - return 1; - } - if ((int)(ioff + c.length) < (int)ioff) return 0; - if (ioff + c.length > idata_limit) { - _m3dstbi__uint32 idata_limit_old = idata_limit; - unsigned char *p; - if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; - while (ioff + c.length > idata_limit) - idata_limit *= 2; - STBI_NOTUSED(idata_limit_old); - p = (unsigned char *)STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); - if (p == NULL) return _m3dstbi__err("outofmem", "Out of memory"); - z->idata = p; - } - if (!_m3dstbi__getn(s, z->idata + ioff, c.length)) return _m3dstbi__err("outofdata", "Corrupt PNG"); - ioff += c.length; - break; - } - - case STBI__PNG_TYPE('I', 'E', 'N', 'D'): { - _m3dstbi__uint32 raw_len, bpl; - if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); - if (scan != STBI__SCAN_load) return 1; - if (z->idata == NULL) return _m3dstbi__err("no IDAT", "Corrupt PNG"); - bpl = (s->img_x * z->depth + 7) / 8; - raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; - z->expanded = (unsigned char *)_m3dstbi_zlib_decode_malloc_guesssize_headerflag((char *)z->idata, ioff, raw_len, (int *)&raw_len, 1); - if (z->expanded == NULL) return 0; - STBI_FREE(z->idata); - z->idata = NULL; - if ((req_comp == s->img_n + 1 && req_comp != 3 && !pal_img_n) || has_trans) - s->img_out_n = s->img_n + 1; - else - s->img_out_n = s->img_n; - if (!_m3dstbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; - if (has_trans) { - if (z->depth == 16) { - if (!_m3dstbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; - } else { - if (!_m3dstbi__compute_transparency(z, tc, s->img_out_n)) return 0; - } - } - if (pal_img_n) { - s->img_n = pal_img_n; - s->img_out_n = pal_img_n; - if (req_comp >= 3) s->img_out_n = req_comp; - if (!_m3dstbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) - return 0; - } else if (has_trans) { - ++s->img_n; - } - STBI_FREE(z->expanded); - z->expanded = NULL; - return 1; - } - - default: - if (first) return _m3dstbi__err("first not IHDR", "Corrupt PNG"); - if ((c.type & (1 << 29)) == 0) { - return _m3dstbi__err("invalid_chunk", "PNG not supported: unknown PNG chunk type"); - } - _m3dstbi__skip(s, c.length); - break; - } - _m3dstbi__get32be(s); - } -} - -static void *_m3dstbi__do_png(_m3dstbi__png *p, int *x, int *y, int *n, int req_comp, _m3dstbi__result_info *ri) { - void *result = NULL; - if (req_comp < 0 || req_comp > 4) { - _m3dstbi__err("bad req_comp", "Internal error"); - return NULL; - } - if (_m3dstbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth < 8) - ri->bits_per_channel = 8; - else - ri->bits_per_channel = p->depth; - result = p->out; - p->out = NULL; - if (req_comp && req_comp != p->s->img_out_n) { - if (ri->bits_per_channel == 8) - result = _m3dstbi__convert_format((unsigned char *)result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - else - result = _m3dstbi__convert_format16((_m3dstbi__uint16 *)result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - p->s->img_out_n = req_comp; - if (result == NULL) return result; - } - *x = p->s->img_x; - *y = p->s->img_y; - if (n) *n = p->s->img_n; - } - STBI_FREE(p->out); - p->out = NULL; - STBI_FREE(p->expanded); - p->expanded = NULL; - STBI_FREE(p->idata); - p->idata = NULL; - - return result; -} - -static void *_m3dstbi__png_load(_m3dstbi__context *s, int *x, int *y, int *comp, int req_comp, _m3dstbi__result_info *ri) { - _m3dstbi__png p; - p.s = s; - return _m3dstbi__do_png(&p, x, y, comp, req_comp, ri); -} -#define stbi__context _m3dstbi__context -#define stbi__result_info _m3dstbi__result_info -#define stbi__png_load _m3dstbi__png_load -#define stbi_zlib_decode_malloc_guesssize_headerflag _m3dstbi_zlib_decode_malloc_guesssize_headerflag -#endif - #if defined(M3D_EXPORTER) && !defined(INCLUDE_STB_IMAGE_WRITE_H) /* zlib_compressor from @@ -2053,7 +829,7 @@ unsigned char *_m3dstbi_zlib_compress(unsigned char *data, int data_len, int *ou #include #endif -#if !defined(M3D_NOIMPORTER) +#if !defined(M3D_NOIMPORTER) /* helper functions for the ASCII parser */ static char *_m3d_findarg(char *s) { while (s && *s && *s != ' ' && *s != '\t' && *s != '\r' && *s != '\n') @@ -5740,7 +4516,7 @@ unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size } if (length) { uint32_t v = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t *)h + len)); - memcpy( length, &v, sizeof(uint32_t)); + memcpy( length, &v, sizeof(uint32_t)); len += v; } out = NULL; @@ -5772,7 +4548,7 @@ unsigned char *m3d_save(m3d_t *model, int quality, int flags, unsigned int *size } } uint32_t v = (uint32_t)((uintptr_t)out - (uintptr_t)((uint8_t *)h + len)); - memcpy( length, &v, sizeof(uint32_t)); + memcpy( length, &v, sizeof(uint32_t)); len += v; out = NULL; } diff --git a/code/AssetLib/MD5/MD5Loader.cpp b/code/AssetLib/MD5/MD5Loader.cpp index 0d9c77b71..9fba60c61 100644 --- a/code/AssetLib/MD5/MD5Loader.cpp +++ b/code/AssetLib/MD5/MD5Loader.cpp @@ -485,7 +485,7 @@ void MD5Importer::LoadMD5MeshFile() { } MD5::WeightDesc &weightDesc = meshSrc.mWeights[w]; - if (weightDesc.mWeight < AI_MD5_WEIGHT_EPSILON && weightDesc.mWeight >= -AI_MD5_WEIGHT_EPSILON) { + if (weightDesc.mWeight < AI_MD5_WEIGHT_EPSILON && weightDesc.mWeight >= -AI_MD5_WEIGHT_EPSILON) { continue; } diff --git a/code/AssetLib/MDC/MDCFileData.h b/code/AssetLib/MDC/MDCFileData.h index 616556f03..a8f3aea43 100644 --- a/code/AssetLib/MDC/MDCFileData.h +++ b/code/AssetLib/MDC/MDCFileData.h @@ -120,13 +120,13 @@ struct Surface { , ulFlags() , ulNumCompFrames() , ulNumBaseFrames() - , ulNumShaders() + , ulNumShaders() , ulNumVertices() , ulNumTriangles() , ulOffsetTriangles() , ulOffsetShaders() , ulOffsetTexCoords() - , ulOffsetBaseVerts() + , ulOffsetBaseVerts() , ulOffsetCompVerts() , ulOffsetFrameBaseFrames() , ulOffsetFrameCompFrames() diff --git a/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp b/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp index e6576b344..1ff86fe27 100644 --- a/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp +++ b/code/AssetLib/MDL/HalfLife/HL1MDLLoader.cpp @@ -629,7 +629,7 @@ void HL1MDLLoader::read_meshes() { +-- bodypart --+-- model -- [mesh index, mesh index, ...] | | | +-- model -- [mesh index, mesh index, ...] - | | + | | | ... | |-- bodypart -- ... @@ -1298,7 +1298,7 @@ void HL1MDLLoader::read_global_info() { * @note The structure of this method is taken from HL2 source code. * Although this is from HL2, it's implementation is almost identical * to code found in HL1 SDK. See HL1 and HL2 SDKs for more info. -* +* * source: * HL1 source code. * file: studio_render.cpp diff --git a/code/AssetLib/MMD/MMDPmxParser.cpp b/code/AssetLib/MMD/MMDPmxParser.cpp index d57dc169a..be3b10248 100644 --- a/code/AssetLib/MMD/MMDPmxParser.cpp +++ b/code/AssetLib/MMD/MMDPmxParser.cpp @@ -102,7 +102,7 @@ namespace pmx const unsigned int targetSize = size * 3; // enough to encode char *targetStart = new char[targetSize]; std::memset(targetStart, 0, targetSize * sizeof(char)); - + utf8::utf16to8( sourceStart, sourceStart + size/2, targetStart ); std::string result(targetStart); @@ -516,13 +516,13 @@ namespace pmx stream->read((char*) magic, sizeof(char) * 4); if (magic[0] != 0x50 || magic[1] != 0x4d || magic[2] != 0x58 || magic[3] != 0x20) { - throw DeadlyImportError("MMD: Invalid magic number."); - } + throw DeadlyImportError("MMD: Invalid magic number."); + } stream->read((char*) &version, sizeof(float)); if (version != 2.0f && version != 2.1f) { throw DeadlyImportError("MMD: Unsupported version (must be 2.0 or 2.1): ", ai_to_string(version)); - } + } this->setting.Read(stream); this->model_name = ReadString(stream, setting.encoding); diff --git a/code/AssetLib/OFF/OFFLoader.cpp b/code/AssetLib/OFF/OFFLoader.cpp index 360ffc51b..fb8f3b424 100644 --- a/code/AssetLib/OFF/OFFLoader.cpp +++ b/code/AssetLib/OFF/OFFLoader.cpp @@ -138,7 +138,7 @@ void OFFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS const char* car = buffer; const char* end = buffer + mBuffer2.size(); NextToken(&car, end); - + if (car < end - 2 && car[0] == 'S' && car[1] == 'T') { hasTexCoord = true; car += 2; } @@ -164,7 +164,7 @@ void OFFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS dimensions = 3; hasHomogenous = false; NextToken(&car, end); - + // at this point the next token should be an integer number if (car >= end - 1 || *car < '0' || *car > '9') { throw DeadlyImportError("OFF: Header is invalid"); @@ -223,7 +223,7 @@ void OFFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS ASSIMP_LOG_ERROR("OFF: The number of verts in the header is incorrect"); break; } - aiVector3D& v = mesh->mVertices[i]; + aiVector3D& v = mesh->mVertices[i]; sz = line; // helper array to write a for loop over possible dimension values @@ -255,7 +255,7 @@ void OFFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS SkipSpaces(&sz); fast_atoreal_move(sz,(ai_real&)n.z); } - + // reading colors is a pain because the specification says it can be // integers or floats, and any number of them between 1 and 4 included, // until the next comment or end of line @@ -321,7 +321,7 @@ void OFFImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS ++i; ++faces; } - + // generate the output node graph pScene->mRootNode = new aiNode(); pScene->mRootNode->mName.Set(""); diff --git a/code/AssetLib/Obj/ObjExporter.h b/code/AssetLib/Obj/ObjExporter.h index 3a46da780..a64f38f74 100644 --- a/code/AssetLib/Obj/ObjExporter.h +++ b/code/AssetLib/Obj/ObjExporter.h @@ -67,7 +67,7 @@ public: ~ObjExporter(); std::string GetMaterialLibName(); std::string GetMaterialLibFileName(); - + /// public string-streams to write all output into std::ostringstream mOutput, mOutputMat; @@ -137,13 +137,13 @@ private: } }; - struct aiVectorCompare { - bool operator() (const aiVector3D& a, const aiVector3D& b) const { - if(a.x < b.x) return true; - if(a.x > b.x) return false; - if(a.y < b.y) return true; - if(a.y > b.y) return false; - if(a.z < b.z) return true; + struct aiVectorCompare { + bool operator() (const aiVector3D& a, const aiVector3D& b) const { + if(a.x < b.x) return true; + if(a.x > b.x) return false; + if(a.y < b.y) return true; + if(a.y > b.y) return false; + if(a.z < b.z) return true; return false; } }; @@ -153,7 +153,7 @@ private: int mNextIndex; typedef std::map dataType; dataType vecMap; - + public: indexMap() : mNextIndex(1) { diff --git a/code/AssetLib/Obj/ObjFileImporter.cpp b/code/AssetLib/Obj/ObjFileImporter.cpp index d6232be81..4e943fb5f 100644 --- a/code/AssetLib/Obj/ObjFileImporter.cpp +++ b/code/AssetLib/Obj/ObjFileImporter.cpp @@ -162,7 +162,7 @@ void ObjFileImporter::InternReadFile(const std::string &file, aiScene *pScene, I // ------------------------------------------------------------------------------------------------ // Create the data from parsed obj-file void ObjFileImporter::CreateDataFromImport(const ObjFile::Model *pModel, aiScene *pScene) { - if (0L == pModel) { + if (nullptr == pModel) { return; } @@ -468,7 +468,7 @@ void ObjFileImporter::createVertexArray(const ObjFile::Model *pModel, } // Copy all vertex colors - if (!pModel->m_VertexColors.empty()) { + if (vertex < pModel->m_VertexColors.size()) { const aiVector3D &color = pModel->m_VertexColors[vertex]; pMesh->mColors[0][newIndex] = aiColor4D(color.x, color.y, color.z, 1.0); } diff --git a/code/AssetLib/Obj/ObjFileMtlImporter.cpp b/code/AssetLib/Obj/ObjFileMtlImporter.cpp index bf1b70c90..94e57c26b 100644 --- a/code/AssetLib/Obj/ObjFileMtlImporter.cpp +++ b/code/AssetLib/Obj/ObjFileMtlImporter.cpp @@ -146,7 +146,7 @@ void ObjFileMtlImporter::load() { ++m_DataIt; ai_real d; getFloatValue(d); - m_pModel->m_pCurrentMaterial->alpha = static_cast(1.0) - d; + m_pModel->m_pCurrentMaterial->alpha = static_cast(1.0) - d; } m_DataIt = skipLine(m_DataIt, m_DataItEnd, m_uiLine); } break; diff --git a/code/AssetLib/Ogre/OgreMaterial.cpp b/code/AssetLib/Ogre/OgreMaterial.cpp index 295dedde6..6da82f89c 100644 --- a/code/AssetLib/Ogre/OgreMaterial.cpp +++ b/code/AssetLib/Ogre/OgreMaterial.cpp @@ -415,8 +415,8 @@ bool OgreImporter::ReadTextureUnit(const std::string &textureUnitName, stringstr // User defined Assimp config property to detect texture type from filename. if (m_detectTextureTypeFromFilename) { - size_t posSuffix = textureRef.find_last_of("."); - size_t posUnderscore = textureRef.find_last_of("_"); + size_t posSuffix = textureRef.find_last_of('.'); + size_t posUnderscore = textureRef.find_last_of('_'); if (posSuffix != string::npos && posUnderscore != string::npos && posSuffix > posUnderscore) { string identifier = ai_tolower(textureRef.substr(posUnderscore, posSuffix - posUnderscore)); diff --git a/code/AssetLib/Ply/PlyParser.cpp b/code/AssetLib/Ply/PlyParser.cpp index 59cb6b976..8af0edfdc 100644 --- a/code/AssetLib/Ply/PlyParser.cpp +++ b/code/AssetLib/Ply/PlyParser.cpp @@ -419,8 +419,7 @@ bool PLY::DOM::ParseHeader(IOStreamBuffer &streamBuffer, std::vector if (PLY::Element::ParseElement(streamBuffer, buffer, &out)) { // add the element to the list of elements alElements.push_back(out); - } else if ( TokenMatch(buffer, "end_header\r", 11) || //checks for header end with /r/n ending - TokenMatch(buffer, "end_header", 10)) { //checks for /n ending, if it doesn't end with /r/n + } else if (TokenMatch(buffer, "end_header", 10)) { //checks for /n ending, if it doesn't end with /r/n // we have reached the end of the header break; } else { @@ -501,6 +500,11 @@ bool PLY::DOM::ParseInstanceBinary(IOStreamBuffer &streamBuffer, DOM *p_pc } streamBuffer.getNextBlock(buffer); + + // remove first char if it's /n in case of file with /r/n + if (((char *)&buffer[0])[0] == '\n') + buffer.erase(buffer.begin(), buffer.begin() + 1); + unsigned int bufferSize = static_cast(buffer.size()); const char *pCur = (char *)&buffer[0]; if (!p_pcOut->ParseElementInstanceListsBinary(streamBuffer, buffer, pCur, bufferSize, loader, p_bBE)) { diff --git a/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp b/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp index becfa41fc..a1da8fd74 100644 --- a/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp +++ b/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp @@ -99,7 +99,7 @@ static void extractIds(const std::string &key, int &id1, int &id2) { return; } - const std::string::size_type pos = key.find("."); + const std::string::size_type pos = key.find('.'); if (std::string::npos == pos) { return; } @@ -208,7 +208,7 @@ void Q3BSPFileImporter::separateMapName(const std::string &importName, std::stri return; } - const std::string::size_type pos = importName.rfind(","); + const std::string::size_type pos = importName.rfind(','); if (std::string::npos == pos) { archiveName = importName; return; diff --git a/code/AssetLib/SMD/SMDLoader.cpp b/code/AssetLib/SMD/SMDLoader.cpp index de9c65c4a..90f0b7697 100644 --- a/code/AssetLib/SMD/SMDLoader.cpp +++ b/code/AssetLib/SMD/SMDLoader.cpp @@ -438,7 +438,7 @@ void SMDImporter::AddBoneChildren(aiNode* pcNode, uint32_t iParent) { pc->mTransformation = bone.sAnim.asKeys[0].matrix; } - if (bone.iParent == static_cast(-1)) { + if (bone.iParent == static_cast(-1)) { bone.mOffsetMatrix = pc->mTransformation; } else { bone.mOffsetMatrix = asBones[bone.iParent].mOffsetMatrix * pc->mTransformation; diff --git a/code/AssetLib/STEPParser/STEPFileReader.cpp b/code/AssetLib/STEPParser/STEPFileReader.cpp index e97ea1e28..360277912 100644 --- a/code/AssetLib/STEPParser/STEPFileReader.cpp +++ b/code/AssetLib/STEPParser/STEPFileReader.cpp @@ -49,21 +49,22 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "STEPFileEncoding.h" #include #include -#include #include +#include +#include using namespace Assimp; namespace EXPRESS = STEP::EXPRESS; // ------------------------------------------------------------------------------------------------ -std::string AddLineNumber(const std::string& s,uint64_t line /*= LINE_NOT_SPECIFIED*/, const std::string& prefix = "") +std::string AddLineNumber(const std::string& s,uint64_t line /*= LINE_NOT_SPECIFIED*/, const std::string& prefix = std::string()) { return line == STEP::SyntaxError::LINE_NOT_SPECIFIED ? prefix+s : static_cast( (Formatter::format(),prefix,"(line ",line,") ",s) ); } // ------------------------------------------------------------------------------------------------ -std::string AddEntityID(const std::string& s,uint64_t entity /*= ENTITY_NOT_SPECIFIED*/, const std::string& prefix = "") +std::string AddEntityID(const std::string& s,uint64_t entity /*= ENTITY_NOT_SPECIFIED*/, const std::string& prefix = std::string()) { return entity == STEP::TypeError::ENTITY_NOT_SPECIFIED ? prefix+s : static_cast( (Formatter::format(),prefix,"(entity #",entity,") ",s)); } @@ -87,7 +88,7 @@ static const char *ISO_Token = "ISO-10303-21;"; static const char *FILE_SCHEMA_Token = "FILE_SCHEMA"; // ------------------------------------------------------------------------------------------------ STEP::DB* STEP::ReadFileHeader(std::shared_ptr stream) { - std::shared_ptr reader = std::shared_ptr(new StreamReaderLE(stream)); + std::shared_ptr reader = std::shared_ptr(new StreamReaderLE(std::move(stream))); std::unique_ptr db = std::unique_ptr(new STEP::DB(reader)); LineSplitter &splitter = db->GetSplitter(); diff --git a/code/AssetLib/STL/STLExporter.cpp b/code/AssetLib/STL/STLExporter.cpp index bd4d96c71..59c6148ee 100644 --- a/code/AssetLib/STL/STLExporter.cpp +++ b/code/AssetLib/STL/STLExporter.cpp @@ -69,7 +69,7 @@ void ExportSceneSTL(const char* pFile,IOSystem* pIOSystem, const aiScene* pScene if (exporter.mOutput.fail()) { throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile)); } - + // we're still here - export successfully completed. Write the file. std::unique_ptr outfile (pIOSystem->Open(pFile,"wt")); if (outfile == nullptr) { @@ -88,7 +88,7 @@ void ExportSceneSTLBinary(const char* pFile,IOSystem* pIOSystem, const aiScene* if (exporter.mOutput.fail()) { throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile)); } - + // we're still here - export successfully completed. Write the file. std::unique_ptr outfile (pIOSystem->Open(pFile,"wb")); if (outfile == nullptr) { @@ -139,9 +139,9 @@ STLExporter::STLExporter(const char* _filename, const aiScene* pScene, bool expo if (exportPointClouds) { WritePointCloud("Assimp_Pointcloud", pScene ); return; - } + } - // Export the assimp mesh + // Export the assimp mesh const std::string name = "AssimpScene"; mOutput << SolidToken << " " << name << endl; for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) { diff --git a/code/AssetLib/STL/STLLoader.cpp b/code/AssetLib/STL/STLLoader.cpp index 433fb14c7..8cfe63e0d 100644 --- a/code/AssetLib/STL/STLLoader.cpp +++ b/code/AssetLib/STL/STLLoader.cpp @@ -372,7 +372,7 @@ void STLImporter::LoadASCIIFile(aiNode *root) { pMesh->mVertices = new aiVector3D[pMesh->mNumVertices]; for (size_t i=0; imNumVertices; ++i ) { pMesh->mVertices[i].x = positionBuffer[i].x; - pMesh->mVertices[i].y = positionBuffer[i].y; + pMesh->mVertices[i].y = positionBuffer[i].y; pMesh->mVertices[i].z = positionBuffer[i].z; } positionBuffer.clear(); @@ -382,7 +382,7 @@ void STLImporter::LoadASCIIFile(aiNode *root) { pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; for (size_t i=0; imNumVertices; ++i ) { pMesh->mNormals[i].x = normalBuffer[i].x; - pMesh->mNormals[i].y = normalBuffer[i].y; + pMesh->mNormals[i].y = normalBuffer[i].y; pMesh->mNormals[i].z = normalBuffer[i].z; } normalBuffer.clear(); diff --git a/code/AssetLib/Step/STEPFile.h b/code/AssetLib/Step/STEPFile.h index 90eaef5f3..e09faad98 100644 --- a/code/AssetLib/Step/STEPFile.h +++ b/code/AssetLib/Step/STEPFile.h @@ -634,7 +634,7 @@ private: }; template -inline bool operator==(std::shared_ptr lo, T whatever) { +inline bool operator==(const std::shared_ptr &lo, T whatever) { return *lo == whatever; // XXX use std::forward if we have 0x } @@ -816,7 +816,7 @@ public: typedef std::pair RefMapRange; private: - DB(std::shared_ptr reader) : + DB(const std::shared_ptr &reader) : reader(reader), splitter(*reader, true, true), evaluated_count(), schema(nullptr) {} public: diff --git a/code/AssetLib/Step/StepExporter.cpp b/code/AssetLib/Step/StepExporter.cpp index dfe5bab67..1228c72ea 100644 --- a/code/AssetLib/Step/StepExporter.cpp +++ b/code/AssetLib/Step/StepExporter.cpp @@ -175,12 +175,11 @@ void StepExporter::WriteFile() fColor.b = 0.8f; int ind = 100; // the start index to be used - int faceEntryLen = 30; // number of entries for a triangle/face + std::vector faceEntryLen; // numbers of entries for a triangle/face // prepare unique (count triangles and vertices) VectorIndexUMap uniqueVerts; // use a map to reduce find complexity to log(n) VectorIndexUMap::iterator it; - int countFace = 0; for (unsigned int i=0; imNumMeshes; ++i) { @@ -189,7 +188,7 @@ void StepExporter::WriteFile() { aiFace* face = &(mesh->mFaces[j]); - if (face->mNumIndices == 3) countFace++; + if (face->mNumIndices >= 3) faceEntryLen.push_back(15 + 5 * face->mNumIndices); } for (unsigned int j=0; jmNumVertices; ++j) { @@ -218,10 +217,13 @@ void StepExporter::WriteFile() // write the top of data mOutput << "DATA" << endstr; mOutput << "#1=MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION(' ',("; - for (int i=0; imFaces[j]); - if (face->mNumIndices != 3) continue; + const int numIndices = face->mNumIndices; + if (numIndices < 3) continue; - aiVector3D* v1 = &(mesh->mVertices[face->mIndices[0]]); - aiVector3D* v2 = &(mesh->mVertices[face->mIndices[1]]); - aiVector3D* v3 = &(mesh->mVertices[face->mIndices[2]]); - aiVector3D dv12 = *v2 - *v1; - aiVector3D dv23 = *v3 - *v2; - aiVector3D dv31 = *v1 - *v3; - aiVector3D dv13 = *v3 - *v1; - dv12.Normalize(); - dv23.Normalize(); - dv31.Normalize(); - dv13.Normalize(); + std::vector pidArray(numIndices, -1); // vertex id + std::vector dvArray(numIndices); // edge dir + for (int k = 0; k < numIndices; ++k) + { + aiVector3D *v1 = &(mesh->mVertices[face->mIndices[k]]); + pidArray[k] = uniqueVerts.find(v1)->second; - aiVector3D dvY = dv12; - aiVector3D dvX = dvY ^ dv13; + aiVector3D *v2 = nullptr; + if (k + 1 == numIndices) + v2 = &(mesh->mVertices[face->mIndices[0]]); + else + v2 = &(mesh->mVertices[face->mIndices[k + 1]]); + dvArray[k] = *v2 - *v1; + dvArray[k].Normalize(); + } + + aiVector3D dvY = dvArray[1]; + aiVector3D dvX = dvY ^ dvArray[0]; dvX.Normalize(); - int pid1 = uniqueVerts.find(v1)->second; - int pid2 = uniqueVerts.find(v2)->second; - int pid3 = uniqueVerts.find(v3)->second; - // mean vertex color for the face if available if (mesh->HasVertexColors(0)) { @@ -339,35 +344,62 @@ void StepExporter::WriteFile() /* 2 directions of the plane */ mOutput << "#" << sid+9 << "=PLANE('',#" << sid+10 << ")" << endstr; - mOutput << "#" << sid+10 << "=AXIS2_PLACEMENT_3D('',#" << pid1 << ", #" << sid+11 << ",#" << sid+12 << ")" << endstr; + mOutput << "#" << sid+10 << "=AXIS2_PLACEMENT_3D('',#" << pidArray[0] << ",#" << sid+11 << ",#" << sid+12 << ")" << endstr; mOutput << "#" << sid + 11 << "=DIRECTION('',(" << dvX.x << "," << dvX.y << "," << dvX.z << "))" << endstr; mOutput << "#" << sid + 12 << "=DIRECTION('',(" << dvY.x << "," << dvY.y << "," << dvY.z << "))" << endstr; mOutput << "#" << sid+13 << "=FACE_BOUND('',#" << sid+14 << ",.T.)" << endstr; - mOutput << "#" << sid+14 << "=EDGE_LOOP('',(#" << sid+15 << ",#" << sid+16 << ",#" << sid+17 << "))" << endstr; + mOutput << "#" << sid+14 << "=EDGE_LOOP('',("; + int edgeLoopStart = sid + 15; + for (int k = 0; k < numIndices; ++k) + { + if (k == 0) + mOutput << "#"; + else + mOutput << ",#"; + mOutput << edgeLoopStart + k; + } + mOutput << "))" << endstr; /* edge loop */ - mOutput << "#" << sid+15 << "=ORIENTED_EDGE('',*,*,#" << sid+18 << ",.T.)" << endstr; - mOutput << "#" << sid+16 << "=ORIENTED_EDGE('',*,*,#" << sid+19 << ",.T.)" << endstr; - mOutput << "#" << sid+17 << "=ORIENTED_EDGE('',*,*,#" << sid+20 << ",.T.)" << endstr; + int orientedEdgesStart = edgeLoopStart + numIndices; + for (int k=0; k < numIndices; k++) + { + mOutput << "#" << edgeLoopStart+k << "=ORIENTED_EDGE('',*,*,#" << orientedEdgesStart + k << ",.T.)" << endstr; + } /* oriented edges */ - mOutput << "#" << sid+18 << "=EDGE_CURVE('',#" << pid1+1 << ",#" << pid2+1 << ",#" << sid+21 << ",.F.)" << endstr; - mOutput << "#" << sid+19 << "=EDGE_CURVE('',#" << pid2+1 << ",#" << pid3+1 << ",#" << sid+22 << ",.T.)" << endstr; - mOutput << "#" << sid+20 << "=EDGE_CURVE('',#" << pid3+1 << ",#" << pid1+1 << ",#" << sid+23 << ",.T.)" << endstr; + int lineStart = orientedEdgesStart + numIndices; + for (int k=0; k < numIndices; ++k) + { + if (k == 0) + mOutput << "#" << orientedEdgesStart+k << "=EDGE_CURVE('',#" << pidArray[k]+1 << ",#" << pidArray[k+1]+1 << ",#" << lineStart+k << ",.F.)" << endstr; + else if (k+1 == numIndices) + mOutput << "#" << orientedEdgesStart+k << "=EDGE_CURVE('',#" << pidArray[k]+1 << ",#" << pidArray[0]+1 << ",#" << lineStart+k << ",.T.)" << endstr; + else + mOutput << "#" << orientedEdgesStart+k << "=EDGE_CURVE('',#" << pidArray[k]+1 << ",#" << pidArray[k+1]+1 << ",#" << lineStart+k << ",.T.)" << endstr; + } - /* 3 lines and 3 vectors for the lines for the 3 edge curves */ - mOutput << "#" << sid+21 << "=LINE('',#" << pid1 << ",#" << sid+24 << ")" << endstr; - mOutput << "#" << sid+22 << "=LINE('',#" << pid2 << ",#" << sid+25 << ")" << endstr; - mOutput << "#" << sid+23 << "=LINE('',#" << pid3 << ",#" << sid+26 << ")" << endstr; - mOutput << "#" << sid+24 << "=VECTOR('',#" << sid+27 << ",1.0)" << endstr; - mOutput << "#" << sid+25 << "=VECTOR('',#" << sid+28 << ",1.0)" << endstr; - mOutput << "#" << sid+26 << "=VECTOR('',#" << sid+29 << ",1.0)" << endstr; - mOutput << "#" << sid+27 << "=DIRECTION('',(" << dv12.x << "," << dv12.y << "," << dv12.z << "))" << endstr; - mOutput << "#" << sid+28 << "=DIRECTION('',(" << dv23.x << "," << dv23.y << "," << dv23.z << "))" << endstr; - mOutput << "#" << sid+29 << "=DIRECTION('',(" << dv31.x << "," << dv31.y << "," << dv31.z << "))" << endstr; - ind += faceEntryLen; // increase counter + /* n lines and n vectors for the lines for the n edge curves */ + int vectorStart = lineStart + numIndices; + for (int k=0; k < numIndices; ++k) + { + mOutput << "#" << lineStart+k << "=LINE('',#" << pidArray[k] << ",#" << vectorStart+k << ")" << endstr; + } + + int directionStart = vectorStart + numIndices; + for (int k=0; k < numIndices; ++k) + { + mOutput << "#" << vectorStart+k << "=VECTOR('',#" << directionStart+k << ",1.0)" << endstr; + } + + for (int k=0; k < numIndices; ++k) + { + const aiVector3D &dv = dvArray[k]; + mOutput << "#" << directionStart + k << "=DIRECTION('',(" << dv.x << "," << dv.y << "," << dv.z << "))" << endstr; + } + ind += 15 + 5*numIndices; // increase counter } } diff --git a/code/AssetLib/X/XFileExporter.cpp b/code/AssetLib/X/XFileExporter.cpp index da20b935a..b95cb7abf 100644 --- a/code/AssetLib/X/XFileExporter.cpp +++ b/code/AssetLib/X/XFileExporter.cpp @@ -86,7 +86,7 @@ void ExportSceneXFile(const char* pFile,IOSystem* pIOSystem, const aiScene* pSce if (iDoTheExportThing.mOutput.fail()) { throw DeadlyExportError("output data creation failed. Most likely the file became too large: " + std::string(pFile)); } - + // we're still here - export successfully completed. Write result to the given IOSYstem std::unique_ptr outfile (pIOSystem->Open(pFile,"wt")); if (outfile == nullptr) { @@ -530,8 +530,8 @@ void XFileExporter::writePath(const aiString &path) while( str.find( "\\\\") != std::string::npos) str.replace( str.find( "\\\\"), 2, "\\"); - while( str.find( "\\") != std::string::npos) - str.replace( str.find( "\\"), 1, "/"); + while (str.find('\\') != std::string::npos) + str.replace(str.find('\\'), 1, "/"); mOutput << str; diff --git a/code/AssetLib/X/XFileExporter.h b/code/AssetLib/X/XFileExporter.h index 32b75b3d1..620d282b6 100644 --- a/code/AssetLib/X/XFileExporter.h +++ b/code/AssetLib/X/XFileExporter.h @@ -94,9 +94,9 @@ protected: void PushTag() { startstr.append( " "); } /// Leaves an element, decreasing the indentation - void PopTag() { - ai_assert( startstr.length() > 1); - startstr.erase( startstr.length() - 2); + void PopTag() { + ai_assert( startstr.length() > 1); + startstr.erase( startstr.length() - 2); } public: diff --git a/code/AssetLib/X/XFileImporter.cpp b/code/AssetLib/X/XFileImporter.cpp index df1aba331..4c8c54551 100644 --- a/code/AssetLib/X/XFileImporter.cpp +++ b/code/AssetLib/X/XFileImporter.cpp @@ -667,9 +667,7 @@ void XFileImporter::ConvertMaterials( aiScene* pScene, std::vector if it comes after // or if (s == "lighting") { @@ -250,7 +250,7 @@ void XGLImporter::ReadWorld(XmlNode &node, TempScope &scope) { } } - aiNode *const nd = ReadObject(node, scope, true); + aiNode *const nd = ReadObject(node, scope); if (!nd) { ThrowException("failure reading "); } @@ -296,16 +296,13 @@ aiLight *XGLImporter::ReadDirectionalLight(XmlNode &node) { } // ------------------------------------------------------------------------------------------------ -aiNode *XGLImporter::ReadObject(XmlNode &node, TempScope &scope, bool skipFirst/*, const char *closetag */) { +aiNode *XGLImporter::ReadObject(XmlNode &node, TempScope &scope) { aiNode *nd = new aiNode; std::vector children; std::vector meshes; try { for (XmlNode &child : node.children()) { - - skipFirst = false; - const std::string &s = ai_stdStrToLower(child.name()); if (s == "mesh") { const size_t prev = scope.meshes_linear.size(); diff --git a/code/AssetLib/XGL/XGLLoader.h b/code/AssetLib/XGL/XGLLoader.h index f7da4e0a7..a2b224ac9 100644 --- a/code/AssetLib/XGL/XGLLoader.h +++ b/code/AssetLib/XGL/XGLLoader.h @@ -185,7 +185,7 @@ private: void ReadWorld(XmlNode &node, TempScope &scope); void ReadLighting(XmlNode &node, TempScope &scope); aiLight *ReadDirectionalLight(XmlNode &node); - aiNode *ReadObject(XmlNode &node, TempScope &scope, bool skipFirst = false/*, const char *closetag = "object"*/); + aiNode *ReadObject(XmlNode &node, TempScope &scope); bool ReadMesh(XmlNode &node, TempScope &scope); void ReadMaterial(XmlNode &node, TempScope &scope); aiVector2D ReadVec2(XmlNode &node); diff --git a/code/AssetLib/glTF/glTFAsset.h b/code/AssetLib/glTF/glTFAsset.h index da49a1737..4cef646d2 100644 --- a/code/AssetLib/glTF/glTFAsset.h +++ b/code/AssetLib/glTF/glTFAsset.h @@ -456,11 +456,10 @@ namespace glTF /// \param [in] pDecodedData - pointer to decoded data array. /// \param [in] pDecodedData_Length - size of encoded region, in bytes. /// \param [in] pID - ID of the region. - SEncodedRegion(const size_t pOffset, const size_t pEncodedData_Length, uint8_t* pDecodedData, const size_t pDecodedData_Length, const std::string pID) - : Offset(pOffset), EncodedData_Length(pEncodedData_Length), DecodedData(pDecodedData), DecodedData_Length(pDecodedData_Length), ID(pID) - {} + SEncodedRegion(const size_t pOffset, const size_t pEncodedData_Length, uint8_t *pDecodedData, const size_t pDecodedData_Length, const std::string &pID) : + Offset(pOffset), EncodedData_Length(pEncodedData_Length), DecodedData(pDecodedData), DecodedData_Length(pDecodedData_Length), ID(pID) {} - /// \fn ~SEncodedRegion() + /// \fn ~SEncodedRegion() /// Destructor. ~SEncodedRegion() { delete [] DecodedData; } }; @@ -1149,8 +1148,7 @@ namespace glTF void ReadExtensionsUsed(Document& doc); - - IOStream* OpenFile(std::string path, const char* mode, bool absolute = false); + IOStream *OpenFile(const std::string &path, const char *mode, bool absolute = false); }; } diff --git a/code/AssetLib/glTF/glTFAsset.inl b/code/AssetLib/glTF/glTFAsset.inl index 6e1e60846..e915a3aee 100644 --- a/code/AssetLib/glTF/glTFAsset.inl +++ b/code/AssetLib/glTF/glTFAsset.inl @@ -1377,7 +1377,7 @@ inline void Asset::ReadExtensionsUsed(Document &doc) { #undef CHECK_EXT } -inline IOStream *Asset::OpenFile(std::string path, const char *mode, bool absolute) { +inline IOStream *Asset::OpenFile(const std::string& path, const char *mode, bool absolute) { #ifdef ASSIMP_API (void)absolute; return mIOSystem->Open(path, mode); diff --git a/code/AssetLib/glTF/glTFCommon.h b/code/AssetLib/glTF/glTFCommon.h index d99ffbe86..6f35e7881 100644 --- a/code/AssetLib/glTF/glTFCommon.h +++ b/code/AssetLib/glTF/glTFCommon.h @@ -195,11 +195,11 @@ inline void CopyValue(const glTFCommon::mat4 &v, aiMatrix4x4 &o) { inline std::string getCurrentAssetDir(const std::string &pFile) { std::string path = pFile; int pos = std::max(int(pFile.rfind('/')), int(pFile.rfind('\\'))); - if (pos != int(std::string::npos)) { - path = pFile.substr(0, pos + 1); + if (pos == int(std::string::npos)) { + return std::string(); } - return path; + return pFile.substr(0, pos + 1); } #if _MSC_VER # pragma warning(pop) diff --git a/code/AssetLib/glTF/glTFExporter.cpp b/code/AssetLib/glTF/glTFExporter.cpp index c2f96973b..acc195bd9 100644 --- a/code/AssetLib/glTF/glTFExporter.cpp +++ b/code/AssetLib/glTF/glTFExporter.cpp @@ -408,8 +408,7 @@ void glTFExporter::ExportMaterials() * Search through node hierarchy and find the node containing the given meshID. * Returns true on success, and false otherwise. */ -bool FindMeshNode(Ref& nodeIn, Ref& meshNode, std::string meshID) -{ +bool FindMeshNode(Ref &nodeIn, Ref &meshNode, const std::string &meshID) { for (unsigned int i = 0; i < nodeIn->meshes.size(); ++i) { if (meshID.compare(nodeIn->meshes[i]->id) == 0) { meshNode = nodeIn; @@ -530,6 +529,7 @@ void ExportSkin(Asset& mAsset, const aiMesh* aimesh, Ref& meshRef, Ref mStringValue; + Nullable mDoubleValue; + Nullable mUint64Value; + Nullable mInt64Value; + Nullable mBoolValue; + + // std::vector handles both Object and Array + Nullable> mValues; + + operator bool() const { + return Size() != 0; + } + + size_t Size() const { + if (mValues.isPresent) { + return mValues.value.size(); + } else if (mStringValue.isPresent || mDoubleValue.isPresent || mUint64Value.isPresent || mInt64Value.isPresent || mBoolValue.isPresent) { + return 1; + } + return 0; + } + + CustomExtension() = default; + + ~CustomExtension() = default; + + CustomExtension(const CustomExtension &other) : + name(other.name), + mStringValue(other.mStringValue), + mDoubleValue(other.mDoubleValue), + mUint64Value(other.mUint64Value), + mInt64Value(other.mInt64Value), + mBoolValue(other.mBoolValue), + mValues(other.mValues) { + // empty + } +}; + //! Base class for all glTF top-level objects struct Object { int index; //!< The index of this object within its property container @@ -363,6 +410,9 @@ struct Object { std::string id; //!< The globally unique ID used to reference this object std::string name; //!< The user-defined name of this object + CustomExtension customExtensions; + CustomExtension extras; + //! Objects marked as special are not exported (used to emulate the binary body buffer) virtual bool IsSpecial() const { return false; } @@ -377,6 +427,9 @@ struct Object { inline Value *FindArray(Value &val, const char *id); inline Value *FindObject(Value &val, const char *id); inline Value *FindExtension(Value &val, const char *extensionId); + + inline void ReadExtensions(Value &val); + inline void ReadExtras(Value &val); }; // @@ -408,7 +461,7 @@ public: /// \param [in] pDecodedData - pointer to decoded data array. /// \param [in] pDecodedData_Length - size of encoded region, in bytes. /// \param [in] pID - ID of the region. - SEncodedRegion(const size_t pOffset, const size_t pEncodedData_Length, uint8_t *pDecodedData, const size_t pDecodedData_Length, const std::string pID) : + SEncodedRegion(const size_t pOffset, const size_t pEncodedData_Length, uint8_t *pDecodedData, const size_t pDecodedData_Length, const std::string &pID) : Offset(pOffset), EncodedData_Length(pEncodedData_Length), DecodedData(pDecodedData), @@ -834,50 +887,6 @@ struct Mesh : public Object { void Read(Value &pJSON_Object, Asset &pAsset_Root); }; -struct CustomExtension : public Object { - // - // A struct containing custom extension data added to a glTF2 file - // Has to contain Object, Array, String, Double, Uint64, and Int64 at a minimum - // String, Double, Uint64, and Int64 are stored in the Nullables - // Object and Array are stored in the std::vector - // - - Nullable mStringValue; - Nullable mDoubleValue; - Nullable mUint64Value; - Nullable mInt64Value; - Nullable mBoolValue; - - // std::vector handles both Object and Array - Nullable> mValues; - - operator bool() const { - return Size() != 0; - } - - size_t Size() const { - if (mValues.isPresent) { - return mValues.value.size(); - } else if (mStringValue.isPresent || mDoubleValue.isPresent || mUint64Value.isPresent || mInt64Value.isPresent || mBoolValue.isPresent) { - return 1; - } - return 0; - } - - CustomExtension() = default; - - CustomExtension(const CustomExtension &other) - : Object(other) - , mStringValue(other.mStringValue) - , mDoubleValue(other.mDoubleValue) - , mUint64Value(other.mUint64Value) - , mInt64Value(other.mInt64Value) - , mBoolValue(other.mBoolValue) - , mValues(other.mValues) - { - } -}; - struct Node : public Object { std::vector> children; std::vector> meshes; @@ -896,8 +905,6 @@ struct Node : public Object { Ref parent; //!< This is not part of the glTF specification. Used as a helper. - CustomExtension extensions; - Node() {} void Read(Value &obj, Asset &r); }; @@ -1188,7 +1195,7 @@ private: void ReadExtensionsUsed(Document &doc); void ReadExtensionsRequired(Document &doc); - IOStream *OpenFile(std::string path, const char *mode, bool absolute = false); + IOStream *OpenFile(const std::string &path, const char *mode, bool absolute = false); }; inline std::string getContextForErrorMessages(const std::string &id, const std::string &name) { diff --git a/code/AssetLib/glTF2/glTF2Asset.inl b/code/AssetLib/glTF2/glTF2Asset.inl index b51ac20c2..9f793d7c0 100644 --- a/code/AssetLib/glTF2/glTF2Asset.inl +++ b/code/AssetLib/glTF2/glTF2Asset.inl @@ -4,7 +4,6 @@ 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, @@ -304,6 +303,43 @@ inline Value *FindObject(Document &doc, const char *memberId) { inline Value *FindExtension(Value &val, const char *extensionId) { return FindExtensionInContext(val, extensionId, "the document"); } + +inline CustomExtension ReadExtensions(const char *name, Value &obj) { + CustomExtension ret; + ret.name = name; + if (obj.IsObject()) { + ret.mValues.isPresent = true; + for (auto it = obj.MemberBegin(); it != obj.MemberEnd(); ++it) { + auto &val = it->value; + ret.mValues.value.push_back(ReadExtensions(it->name.GetString(), val)); + } + } else if (obj.IsArray()) { + ret.mValues.value.reserve(obj.Size()); + ret.mValues.isPresent = true; + for (unsigned int i = 0; i < obj.Size(); ++i) { + ret.mValues.value.push_back(ReadExtensions(name, obj[i])); + } + } else if (obj.IsNumber()) { + if (obj.IsUint64()) { + ret.mUint64Value.value = obj.GetUint64(); + ret.mUint64Value.isPresent = true; + } else if (obj.IsInt64()) { + ret.mInt64Value.value = obj.GetInt64(); + ret.mInt64Value.isPresent = true; + } else if (obj.IsDouble()) { + ret.mDoubleValue.value = obj.GetDouble(); + ret.mDoubleValue.isPresent = true; + } + } else if (obj.IsString()) { + ReadValue(obj, ret.mStringValue); + ret.mStringValue.isPresent = true; + } else if (obj.IsBool()) { + ret.mBoolValue.value = obj.GetBool(); + ret.mBoolValue.isPresent = true; + } + return ret; +} + } // namespace inline Value *Object::FindString(Value &val, const char *memberId) { @@ -330,6 +366,18 @@ inline Value *Object::FindExtension(Value &val, const char *extensionId) { return FindExtensionInContext(val, extensionId, id.c_str(), name.c_str()); } +inline void Object::ReadExtensions(Value &val) { + if (Value *curExtensions = FindObject(val, "extensions")) { + this->customExtensions = glTF2::ReadExtensions("extensions", *curExtensions); + } +} + +inline void Object::ReadExtras(Value &val) { + if (Value *curExtras = FindObject(val, "extras")) { + this->extras = glTF2::ReadExtensions("extras", *curExtras); + } +} + #ifdef ASSIMP_ENABLE_DRACO template @@ -362,14 +410,14 @@ inline void SetDecodedIndexBuffer_Draco(const draco::Mesh &dracoMesh, Mesh::Prim // Not same size, convert switch (componentBytes) { - case sizeof(uint32_t): - CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); + case sizeof(uint32_t): + CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); break; - case sizeof(uint16_t): - CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); + case sizeof(uint16_t): + CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); break; - case sizeof(uint8_t): - CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); + case sizeof(uint8_t): + CopyFaceIndex_Draco(*decodedIndexBuffer, dracoMesh); break; default: ai_assert(false); @@ -412,23 +460,23 @@ inline void SetDecodedAttributeBuffer_Draco(const draco::Mesh &dracoMesh, uint32 decodedAttribBuffer->Grow(dracoMesh.num_points() * pDracoAttribute->num_components() * componentBytes); switch (accessor.componentType) { - case ComponentType_BYTE: - GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); + case ComponentType_BYTE: + GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break; case ComponentType_UNSIGNED_BYTE: - GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); + GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break; case ComponentType_SHORT: GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break; - case ComponentType_UNSIGNED_SHORT: - GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); + case ComponentType_UNSIGNED_SHORT: + GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break; - case ComponentType_UNSIGNED_INT: - GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); + case ComponentType_UNSIGNED_INT: + GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break; - case ComponentType_FLOAT: - GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); + case ComponentType_FLOAT: + GetAttributeForAllPoints_Draco(dracoMesh, *pDracoAttribute, *decodedAttribBuffer); break; default: ai_assert(false); @@ -569,6 +617,8 @@ Ref LazyDict::Retrieve(unsigned int i) { inst->oIndex = i; ReadMember(obj, "name", inst->name); inst->Read(obj, mAsset); + inst->ReadExtensions(obj); + inst->ReadExtras(obj); Ref result = Add(inst.release()); mRecursiveReferenceCheck.erase(i); @@ -733,12 +783,13 @@ inline void Buffer::EncodedRegion_Mark(const size_t pOffset, const size_t pEncod } inline void Buffer::EncodedRegion_SetCurrent(const std::string &pID) { - if ((EncodedRegion_Current != nullptr) && (EncodedRegion_Current->ID == pID)) return; + if ((EncodedRegion_Current != nullptr) && (EncodedRegion_Current->ID == pID)) { + return; + } for (SEncodedRegion *reg : EncodedRegion_List) { if (reg->ID == pID) { EncodedRegion_Current = reg; - return; } } @@ -788,10 +839,13 @@ inline bool Buffer::ReplaceData_joint(const size_t pBufferData_Offset, const siz } inline size_t Buffer::AppendData(uint8_t *data, size_t length) { - size_t offset = this->byteLength; + const size_t offset = this->byteLength; + // Force alignment to 4 bits - Grow((length + 3) & ~3); + const size_t paddedLength = (length + 3) & ~3; + Grow(paddedLength); memcpy(mData.get() + offset, data, length); + memset(mData.get() + offset + length, 0, paddedLength - length); return offset; } @@ -820,9 +874,7 @@ inline void Buffer::Grow(size_t amount) { // // struct BufferView // - inline void BufferView::Read(Value &obj, Asset &r) { - if (Value *bufferVal = FindUInt(obj, "buffer")) { buffer = r.buffers.Retrieve(bufferVal->GetUint()); } @@ -842,16 +894,21 @@ inline void BufferView::Read(Value &obj, Asset &r) { } inline uint8_t *BufferView::GetPointer(size_t accOffset) { - if (!buffer) return nullptr; + if (!buffer) { + return nullptr; + } uint8_t *basePtr = buffer->GetPointer(); - if (!basePtr) return nullptr; + if (!basePtr) { + return nullptr; + } size_t offset = accOffset + byteOffset; if (buffer->EncodedRegion_Current != nullptr) { const size_t begin = buffer->EncodedRegion_Current->Offset; const size_t end = begin + buffer->EncodedRegion_Current->DecodedData_Length; - if ((offset >= begin) && (offset < end)) + if ((offset >= begin) && (offset < end)) { return &buffer->EncodedRegion_Current->DecodedData[offset - begin]; + } } return basePtr + offset; @@ -877,18 +934,18 @@ inline void Accessor::Sparse::PatchData(unsigned int elementSize) { while (pIndices != indicesEnd) { size_t offset; switch (indicesType) { - case ComponentType_UNSIGNED_BYTE: - offset = *pIndices; - break; - case ComponentType_UNSIGNED_SHORT: - offset = *reinterpret_cast(pIndices); - break; - case ComponentType_UNSIGNED_INT: - offset = *reinterpret_cast(pIndices); - break; - default: - // have fun with float and negative values from signed types as indices. - throw DeadlyImportError("Unsupported component type in index."); + case ComponentType_UNSIGNED_BYTE: + offset = *pIndices; + break; + case ComponentType_UNSIGNED_SHORT: + offset = *reinterpret_cast(pIndices); + break; + case ComponentType_UNSIGNED_INT: + offset = *reinterpret_cast(pIndices); + break; + default: + // have fun with float and negative values from signed types as indices. + throw DeadlyImportError("Unsupported component type in index."); } offset *= elementSize; @@ -900,7 +957,6 @@ inline void Accessor::Sparse::PatchData(unsigned int elementSize) { } inline void Accessor::Read(Value &obj, Asset &r) { - if (Value *bufferViewVal = FindUInt(obj, "bufferView")) { bufferView = r.bufferViews.Retrieve(bufferViewVal->GetUint()); } @@ -909,9 +965,9 @@ inline void Accessor::Read(Value &obj, Asset &r) { componentType = MemberOrDefault(obj, "componentType", ComponentType_BYTE); { const Value* countValue = FindUInt(obj, "count"); - if (!countValue || countValue->GetUint() < 1) + if (!countValue) { - throw DeadlyImportError("A strictly positive count value is required, when reading ", id.c_str(), name.empty() ? "" : " (" + name + ")"); + throw DeadlyImportError("A count value is required, when reading ", id.c_str(), name.empty() ? "" : " (" + name + ")"); } count = countValue->GetUint(); } @@ -1466,7 +1522,7 @@ inline bool GetAttribTargetVector(Mesh::Primitive &p, const int targetIndex, con inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) { Value *curName = FindMember(pJSON_Object, "name"); - if (nullptr != curName) { + if (nullptr != curName && curName->IsString()) { name = curName->GetString(); } @@ -1612,9 +1668,9 @@ inline void Mesh::Read(Value &pJSON_Object, Asset &pAsset_Root) { } } - Value *extras = FindObject(pJSON_Object, "extras"); - if (nullptr != extras) { - if (Value *curTargetNames = FindArray(*extras, "targetNames")) { + Value *curExtras = FindObject(pJSON_Object, "extras"); + if (nullptr != curExtras) { + if (Value *curTargetNames = FindArray(*curExtras, "targetNames")) { this->targetNames.resize(curTargetNames->Size()); for (unsigned int i = 0; i < curTargetNames->Size(); ++i) { Value &targetNameValue = (*curTargetNames)[i]; @@ -1683,42 +1739,6 @@ inline void Light::Read(Value &obj, Asset & /*r*/) { } } -inline CustomExtension ReadExtensions(const char *name, Value &obj) { - CustomExtension ret; - ret.name = name; - if (obj.IsObject()) { - ret.mValues.isPresent = true; - for (auto it = obj.MemberBegin(); it != obj.MemberEnd(); ++it) { - auto &val = it->value; - ret.mValues.value.push_back(ReadExtensions(it->name.GetString(), val)); - } - } else if (obj.IsArray()) { - ret.mValues.value.reserve(obj.Size()); - ret.mValues.isPresent = true; - for (unsigned int i = 0; i < obj.Size(); ++i) { - ret.mValues.value.push_back(ReadExtensions(name, obj[i])); - } - } else if (obj.IsNumber()) { - if (obj.IsUint64()) { - ret.mUint64Value.value = obj.GetUint64(); - ret.mUint64Value.isPresent = true; - } else if (obj.IsInt64()) { - ret.mInt64Value.value = obj.GetInt64(); - ret.mInt64Value.isPresent = true; - } else if (obj.IsDouble()) { - ret.mDoubleValue.value = obj.GetDouble(); - ret.mDoubleValue.isPresent = true; - } - } else if (obj.IsString()) { - ReadValue(obj, ret.mStringValue); - ret.mStringValue.isPresent = true; - } else if (obj.IsBool()) { - ret.mBoolValue.value = obj.GetBool(); - ret.mBoolValue.isPresent = true; - } - return ret; -} - inline void Node::Read(Value &obj, Asset &r) { if (name.empty()) { name = id; @@ -1775,8 +1795,6 @@ inline void Node::Read(Value &obj, Asset &r) { Value *curExtensions = FindObject(obj, "extensions"); if (nullptr != curExtensions) { - this->extensions = ReadExtensions("extensions", *curExtensions); - if (r.extensionsUsed.KHR_lights_punctual) { if (Value *ext = FindObject(*curExtensions, "KHR_lights_punctual")) { Value *curLight = FindUInt(*ext, "light"); @@ -2132,7 +2150,7 @@ inline void Asset::ReadExtensionsUsed(Document &doc) { #undef CHECK_EXT } -inline IOStream *Asset::OpenFile(std::string path, const char *mode, bool /*absolute*/) { +inline IOStream *Asset::OpenFile(const std::string& path, const char *mode, bool /*absolute*/) { #ifdef ASSIMP_API return mIOSystem->Open(path, mode); #else diff --git a/code/AssetLib/glTF2/glTF2AssetWriter.inl b/code/AssetLib/glTF2/glTF2AssetWriter.inl index bf7dbbb2e..115cdf903 100644 --- a/code/AssetLib/glTF2/glTF2AssetWriter.inl +++ b/code/AssetLib/glTF2/glTF2AssetWriter.inl @@ -452,7 +452,7 @@ namespace glTF2 { WriteTex(materialClearcoat, clearcoat.clearcoatTexture, "clearcoatTexture", w.mAl); WriteTex(materialClearcoat, clearcoat.clearcoatRoughnessTexture, "clearcoatRoughnessTexture", w.mAl); WriteTex(materialClearcoat, clearcoat.clearcoatNormalTexture, "clearcoatNormalTexture", w.mAl); - + if (!materialClearcoat.ObjectEmpty()) { exts.AddMember("KHR_materials_clearcoat", materialClearcoat, w.mAl); } @@ -468,7 +468,7 @@ namespace glTF2 { } WriteTex(materialTransmission, transmission.transmissionTexture, "transmissionTexture", w.mAl); - + if (!materialTransmission.ObjectEmpty()) { exts.AddMember("KHR_materials_transmission", materialTransmission, w.mAl); } @@ -613,7 +613,7 @@ namespace glTF2 { if (n.skin) { obj.AddMember("skin", n.skin->index, w.mAl); } - + //gltf2 spec does not support "skeletons" under node if(n.skeletons.size()) { AddRefsVector(obj, "skeletons", n.skeletons, w.mAl); @@ -711,7 +711,7 @@ namespace glTF2 { if (mAsset.scene) { mDoc.AddMember("scene", mAsset.scene->index, mAl); } - + if(mAsset.extras) { mDoc.AddMember("extras", *mAsset.extras, mAl); } @@ -812,7 +812,7 @@ namespace glTF2 { uint32_t binaryChunkLength = 0; if (bodyBuffer->byteLength > 0) { binaryChunkLength = (bodyBuffer->byteLength + 3) & ~3; // Round up to next multiple of 4 - + auto curPaddingLength = binaryChunkLength - bodyBuffer->byteLength; ++GLB_Chunk_count; @@ -881,7 +881,7 @@ namespace glTF2 { if (this->mAsset.extensionsUsed.KHR_materials_sheen) { exts.PushBack(StringRef("KHR_materials_sheen"), mAl); } - + if (this->mAsset.extensionsUsed.KHR_materials_clearcoat) { exts.PushBack(StringRef("KHR_materials_clearcoat"), mAl); } @@ -893,7 +893,7 @@ namespace glTF2 { if (this->mAsset.extensionsUsed.FB_ngon_encoding) { exts.PushBack(StringRef("FB_ngon_encoding"), mAl); } - + if (this->mAsset.extensionsUsed.KHR_texture_basisu) { exts.PushBack(StringRef("KHR_texture_basisu"), mAl); } @@ -901,7 +901,7 @@ namespace glTF2 { if (!exts.Empty()) mDoc.AddMember("extensionsUsed", exts, mAl); - + //basisu extensionRequired Value extsReq; extsReq.SetArray(); diff --git a/code/AssetLib/glTF2/glTF2Exporter.cpp b/code/AssetLib/glTF2/glTF2Exporter.cpp index fe592f342..bbb8a10ff 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.cpp +++ b/code/AssetLib/glTF2/glTF2Exporter.cpp @@ -88,15 +88,13 @@ namespace Assimp { } // end of namespace Assimp glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const aiScene* pScene, - const ExportProperties* pProperties, bool isBinary) + const ExportProperties* pProperties, bool isBinary) : mFilename(filename) , mIOSystem(pIOSystem) + , mScene(pScene) , mProperties(pProperties) + , mAsset(new Asset(pIOSystem)) { - mScene = pScene; - - mAsset.reset( new Asset( pIOSystem ) ); - // Always on as our triangulation process is aware of this type of encoding mAsset->extensionsUsed.FB_ngon_encoding = true; @@ -118,14 +116,14 @@ glTF2Exporter::glTF2Exporter(const char* filename, IOSystem* pIOSystem, const ai ExportScene(); ExportAnimations(); - + // export extras if(mProperties->HasPropertyCallback("extras")) { std::function ExportExtras = mProperties->GetPropertyCallback("extras"); mAsset->extras = (rapidjson::Value*)ExportExtras(0); } - + AssetWriter writer(*mAsset); if (isBinary) { @@ -436,11 +434,11 @@ inline void SetSamplerWrap(SamplerWrap& wrap, aiTextureMapMode map) }; } -void glTF2Exporter::GetTexSampler(const aiMaterial* mat, Ref texture, aiTextureType tt, unsigned int slot) +void glTF2Exporter::GetTexSampler(const aiMaterial& mat, Ref texture, aiTextureType tt, unsigned int slot) { aiString aId; std::string id; - if (aiGetMaterialString(mat, AI_MATKEY_GLTF_MAPPINGID(tt, slot), &aId) == AI_SUCCESS) { + if (aiGetMaterialString(&mat, AI_MATKEY_GLTF_MAPPINGID(tt, slot), &aId) == AI_SUCCESS) { id = aId.C_Str(); } @@ -455,49 +453,52 @@ void glTF2Exporter::GetTexSampler(const aiMaterial* mat, Ref texture, a SamplerMagFilter filterMag; SamplerMinFilter filterMin; - if (aiGetMaterialInteger(mat, AI_MATKEY_MAPPINGMODE_U(tt, slot), (int*)&mapU) == AI_SUCCESS) { + if (aiGetMaterialInteger(&mat, AI_MATKEY_MAPPINGMODE_U(tt, slot), (int*)&mapU) == AI_SUCCESS) { SetSamplerWrap(texture->sampler->wrapS, mapU); } - if (aiGetMaterialInteger(mat, AI_MATKEY_MAPPINGMODE_V(tt, slot), (int*)&mapV) == AI_SUCCESS) { + if (aiGetMaterialInteger(&mat, AI_MATKEY_MAPPINGMODE_V(tt, slot), (int*)&mapV) == AI_SUCCESS) { SetSamplerWrap(texture->sampler->wrapT, mapV); } - if (aiGetMaterialInteger(mat, AI_MATKEY_GLTF_MAPPINGFILTER_MAG(tt, slot), (int*)&filterMag) == AI_SUCCESS) { + if (aiGetMaterialInteger(&mat, AI_MATKEY_GLTF_MAPPINGFILTER_MAG(tt, slot), (int*)&filterMag) == AI_SUCCESS) { texture->sampler->magFilter = filterMag; } - if (aiGetMaterialInteger(mat, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(tt, slot), (int*)&filterMin) == AI_SUCCESS) { + if (aiGetMaterialInteger(&mat, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(tt, slot), (int*)&filterMin) == AI_SUCCESS) { texture->sampler->minFilter = filterMin; } aiString name; - if (aiGetMaterialString(mat, AI_MATKEY_GLTF_MAPPINGNAME(tt, slot), &name) == AI_SUCCESS) { + if (aiGetMaterialString(&mat, AI_MATKEY_GLTF_MAPPINGNAME(tt, slot), &name) == AI_SUCCESS) { texture->sampler->name = name.C_Str(); } } } -void glTF2Exporter::GetMatTexProp(const aiMaterial* mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int slot) +void glTF2Exporter::GetMatTexProp(const aiMaterial& mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int slot) { std::string textureKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName; - mat->Get(textureKey.c_str(), tt, slot, prop); + mat.Get(textureKey.c_str(), tt, slot, prop); } -void glTF2Exporter::GetMatTexProp(const aiMaterial* mat, float& prop, const char* propName, aiTextureType tt, unsigned int slot) +void glTF2Exporter::GetMatTexProp(const aiMaterial& mat, float& prop, const char* propName, aiTextureType tt, unsigned int slot) { std::string textureKey = std::string(_AI_MATKEY_TEXTURE_BASE) + "." + propName; - mat->Get(textureKey.c_str(), tt, slot, prop); + mat.Get(textureKey.c_str(), tt, slot, prop); } -void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, Ref& texture, unsigned int &texCoord, aiTextureType tt, unsigned int slot = 0) { - if (mat->GetTextureCount(tt) > 0) { + if (mat.GetTextureCount(tt) > 0) { aiString tex; - if (mat->Get(AI_MATKEY_TEXTURE(tt, slot), tex) == AI_SUCCESS) { + // Read texcoord (UV map index) + mat.Get(AI_MATKEY_UVWSRC(tt, slot), texCoord); + + if (mat.Get(AI_MATKEY_TEXTURE(tt, slot), tex) == AI_SUCCESS) { std::string path = tex.C_Str(); if (path.size() > 0) { @@ -515,11 +516,10 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe std::string imgId = mAsset->FindUniqueID("", "image"); texture->source = mAsset->images.Create(imgId); - if (path[0] == '*') { // embedded - aiTexture* curTex = mScene->mTextures[atoi(&path[1])]; - + const aiTexture* curTex = mScene->GetEmbeddedTexture(path.c_str()); + if (curTex != nullptr) { // embedded texture->source->name = curTex->mFilename.C_Str(); - + //basisu: embedded ktx2, bu if (curTex->achFormatHint[0]) { std::string mimeType = "image/"; @@ -541,7 +541,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe mimeType += curTex->achFormatHint; texture->source->mimeType = mimeType; } - + // The asset has its own buffer, see Image::SetData //basisu: "image/ktx2", "image/basis" as is texture->source->SetData(reinterpret_cast(curTex->pcData), curTex->mWidth, *mAsset); @@ -554,7 +554,7 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe useBasisUniversal = true; } } - + //basisu if(useBasisUniversal) { mAsset->extensionsUsed.KHR_texture_basisu = true; @@ -568,45 +568,45 @@ void glTF2Exporter::GetMatTex(const aiMaterial* mat, Ref& texture, aiTe } } -void glTF2Exporter::GetMatTex(const aiMaterial* mat, TextureInfo& prop, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, TextureInfo& prop, aiTextureType tt, unsigned int slot = 0) { Ref& texture = prop.texture; - GetMatTex(mat, texture, tt, slot); + GetMatTex(mat, texture, prop.texCoord, tt, slot); - if (texture) { - GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); - } + //if (texture) { + // GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); + //} } -void glTF2Exporter::GetMatTex(const aiMaterial* mat, NormalTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, NormalTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) { Ref& texture = prop.texture; - GetMatTex(mat, texture, tt, slot); + GetMatTex(mat, texture, prop.texCoord, tt, slot); if (texture) { - GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); + //GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); GetMatTexProp(mat, prop.scale, "scale", tt, slot); } } -void glTF2Exporter::GetMatTex(const aiMaterial* mat, OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) +void glTF2Exporter::GetMatTex(const aiMaterial& mat, OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot = 0) { Ref& texture = prop.texture; - GetMatTex(mat, texture, tt, slot); + GetMatTex(mat, texture, prop.texCoord, tt, slot); if (texture) { - GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); + //GetMatTexProp(mat, prop.texCoord, "texCoord", tt, slot); GetMatTexProp(mat, prop.strength, "strength", tt, slot); } } -aiReturn glTF2Exporter::GetMatColor(const aiMaterial* mat, vec4& prop, const char* propName, int type, int idx) +aiReturn glTF2Exporter::GetMatColor(const aiMaterial& mat, vec4& prop, const char* propName, int type, int idx) const { aiColor4D col; - aiReturn result = mat->Get(propName, type, idx, col); + aiReturn result = mat.Get(propName, type, idx, col); if (result == AI_SUCCESS) { prop[0] = col.r; prop[1] = col.g; prop[2] = col.b; prop[3] = col.a; @@ -615,37 +615,116 @@ aiReturn glTF2Exporter::GetMatColor(const aiMaterial* mat, vec4& prop, const cha return result; } -aiReturn glTF2Exporter::GetMatColor(const aiMaterial* mat, vec3& prop, const char* propName, int type, int idx) +aiReturn glTF2Exporter::GetMatColor(const aiMaterial& mat, vec3& prop, const char* propName, int type, int idx) const { aiColor3D col; - aiReturn result = mat->Get(propName, type, idx, col); + aiReturn result = mat.Get(propName, type, idx, col); if (result == AI_SUCCESS) { - prop[0] = col.r; prop[1] = col.g; prop[2] = col.b; + prop[0] = col.r; + prop[1] = col.g; + prop[2] = col.b; } return result; } +bool glTF2Exporter::GetMatSpecGloss(const aiMaterial &mat, glTF2::PbrSpecularGlossiness &pbrSG) { + bool result = false; + // If has Glossiness, a Specular Color or Specular Texture, use the KHR_materials_pbrSpecularGlossiness extension + // NOTE: This extension is being considered for deprecation (Dec 2020), may be replaced by KHR_material_specular + + if (mat.Get(AI_MATKEY_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) == AI_SUCCESS) { + result = true; + } else { + // Don't have explicit glossiness, convert from pbr roughness or legacy shininess + float shininess; + if (mat.Get(AI_MATKEY_ROUGHNESS_FACTOR, shininess) == AI_SUCCESS) { + pbrSG.glossinessFactor = 1.0f - shininess; // Extension defines this way + } else if (mat.Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) { + pbrSG.glossinessFactor = shininess / 1000; + } + } + + if (GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR) == AI_SUCCESS) { + result = true; + } + // Add any appropriate textures + GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); + + result = result || pbrSG.specularGlossinessTexture.texture; + + if (result) { + // Likely to always have diffuse + GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE); + GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE); + } + + return result; +} + +bool glTF2Exporter::GetMatSheen(const aiMaterial &mat, glTF2::MaterialSheen &sheen) { + // Return true if got any valid Sheen properties or textures + if (GetMatColor(mat, sheen.sheenColorFactor, AI_MATKEY_SHEEN_COLOR_FACTOR) != aiReturn_SUCCESS) + return false; + + // Default Sheen color factor {0,0,0} disables Sheen, so do not export + if (sheen.sheenColorFactor == defaultSheenFactor) + return false; + + mat.Get(AI_MATKEY_SHEEN_ROUGHNESS_FACTOR, sheen.sheenRoughnessFactor); + + GetMatTex(mat, sheen.sheenColorTexture, AI_MATKEY_SHEEN_COLOR_TEXTURE); + GetMatTex(mat, sheen.sheenRoughnessTexture, AI_MATKEY_SHEEN_ROUGHNESS_TEXTURE); + + return true; +} + +bool glTF2Exporter::GetMatClearcoat(const aiMaterial &mat, glTF2::MaterialClearcoat &clearcoat) { + if (mat.Get(AI_MATKEY_CLEARCOAT_FACTOR, clearcoat.clearcoatFactor) != aiReturn_SUCCESS) { + return false; + } + + // Clearcoat factor of zero disables Clearcoat, so do not export + if (clearcoat.clearcoatFactor == 0.0f) + return false; + + mat.Get(AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR, clearcoat.clearcoatRoughnessFactor); + + GetMatTex(mat, clearcoat.clearcoatTexture, AI_MATKEY_CLEARCOAT_TEXTURE); + GetMatTex(mat, clearcoat.clearcoatRoughnessTexture, AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE); + GetMatTex(mat, clearcoat.clearcoatNormalTexture, AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE); + + return true; +} + +bool glTF2Exporter::GetMatTransmission(const aiMaterial &mat, glTF2::MaterialTransmission &transmission) { + bool result = mat.Get(AI_MATKEY_TRANSMISSION_FACTOR, transmission.transmissionFactor) == aiReturn_SUCCESS; + GetMatTex(mat, transmission.transmissionTexture, AI_MATKEY_TRANSMISSION_TEXTURE); + return result || transmission.transmissionTexture.texture; +} + void glTF2Exporter::ExportMaterials() { aiString aiName; for (unsigned int i = 0; i < mScene->mNumMaterials; ++i) { - const aiMaterial* mat = mScene->mMaterials[i]; + ai_assert(mScene->mMaterials[i] != nullptr); + + const aiMaterial & mat = *(mScene->mMaterials[i]); std::string id = "material_" + ai_to_string(i); Ref m = mAsset->materials.Create(id); std::string name; - if (mat->Get(AI_MATKEY_NAME, aiName) == AI_SUCCESS) { + if (mat.Get(AI_MATKEY_NAME, aiName) == AI_SUCCESS) { name = aiName.C_Str(); } name = mAsset->FindUniqueID(name, "material"); m->name = name; - GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE); + GetMatTex(mat, m->pbrMetallicRoughness.baseColorTexture, aiTextureType_BASE_COLOR); if (!m->pbrMetallicRoughness.baseColorTexture.texture) { //if there wasn't a baseColorTexture defined in the source, fallback to any diffuse texture @@ -654,26 +733,26 @@ void glTF2Exporter::ExportMaterials() GetMatTex(mat, m->pbrMetallicRoughness.metallicRoughnessTexture, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE); - if (GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR) != AI_SUCCESS) { + if (GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_BASE_COLOR) != AI_SUCCESS) { // if baseColorFactor wasn't defined, then the source is likely not a metallic roughness material. //a fallback to any diffuse color should be used instead GetMatColor(mat, m->pbrMetallicRoughness.baseColorFactor, AI_MATKEY_COLOR_DIFFUSE); } - if (mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) { + if (mat.Get(AI_MATKEY_METALLIC_FACTOR, m->pbrMetallicRoughness.metallicFactor) != AI_SUCCESS) { //if metallicFactor wasn't defined, then the source is likely not a PBR file, and the metallicFactor should be 0 m->pbrMetallicRoughness.metallicFactor = 0; } // get roughness if source is gltf2 file - if (mat->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor) != AI_SUCCESS) { + if (mat.Get(AI_MATKEY_ROUGHNESS_FACTOR, m->pbrMetallicRoughness.roughnessFactor) != AI_SUCCESS) { // otherwise, try to derive and convert from specular + shininess values aiColor4D specularColor; ai_real shininess; if ( - mat->Get(AI_MATKEY_COLOR_SPECULAR, specularColor) == AI_SUCCESS && - mat->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS + mat.Get(AI_MATKEY_COLOR_SPECULAR, specularColor) == AI_SUCCESS && + mat.Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS ) { // convert specular color to luminance float specularIntensity = specularColor[0] * 0.2125f + specularColor[1] * 0.7154f + specularColor[2] * 0.0721f; @@ -694,103 +773,60 @@ void glTF2Exporter::ExportMaterials() GetMatTex(mat, m->emissiveTexture, aiTextureType_EMISSIVE); GetMatColor(mat, m->emissiveFactor, AI_MATKEY_COLOR_EMISSIVE); - mat->Get(AI_MATKEY_TWOSIDED, m->doubleSided); - mat->Get(AI_MATKEY_GLTF_ALPHACUTOFF, m->alphaCutoff); + mat.Get(AI_MATKEY_TWOSIDED, m->doubleSided); + mat.Get(AI_MATKEY_GLTF_ALPHACUTOFF, m->alphaCutoff); + float opacity; aiString alphaMode; - if (mat->Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode) == AI_SUCCESS) { + if (mat.Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) { + if (opacity < 1) { + m->alphaMode = "BLEND"; + m->pbrMetallicRoughness.baseColorFactor[3] *= opacity; + } + } + if (mat.Get(AI_MATKEY_GLTF_ALPHAMODE, alphaMode) == AI_SUCCESS) { m->alphaMode = alphaMode.C_Str(); - } else { - float opacity; - - if (mat->Get(AI_MATKEY_OPACITY, opacity) == AI_SUCCESS) { - if (opacity < 1) { - m->alphaMode = "BLEND"; - m->pbrMetallicRoughness.baseColorFactor[3] *= opacity; - } - } } - bool hasPbrSpecularGlossiness = false; - mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS, hasPbrSpecularGlossiness); - - if (hasPbrSpecularGlossiness) { - - if (!mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness) { - mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true; - } - + { + // KHR_materials_pbrSpecularGlossiness extension + // NOTE: This extension is being considered for deprecation (Dec 2020) PbrSpecularGlossiness pbrSG; - - GetMatColor(mat, pbrSG.diffuseFactor, AI_MATKEY_COLOR_DIFFUSE); - GetMatColor(mat, pbrSG.specularFactor, AI_MATKEY_COLOR_SPECULAR); - - if (mat->Get(AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR, pbrSG.glossinessFactor) != AI_SUCCESS) { - float shininess; - - if (mat->Get(AI_MATKEY_SHININESS, shininess) == AI_SUCCESS) { - pbrSG.glossinessFactor = shininess / 1000; - } + if (GetMatSpecGloss(mat, pbrSG)) { + mAsset->extensionsUsed.KHR_materials_pbrSpecularGlossiness = true; + m->pbrSpecularGlossiness = Nullable(pbrSG); } - - GetMatTex(mat, pbrSG.diffuseTexture, aiTextureType_DIFFUSE); - GetMatTex(mat, pbrSG.specularGlossinessTexture, aiTextureType_SPECULAR); - - m->pbrSpecularGlossiness = Nullable(pbrSG); } - bool unlit; - if (mat->Get(AI_MATKEY_GLTF_UNLIT, unlit) == AI_SUCCESS && unlit) { + // glTFv2 is either PBR or Unlit + aiShadingMode shadingMode = aiShadingMode_PBR_BRDF; + mat.Get(AI_MATKEY_SHADING_MODEL, shadingMode); + if (shadingMode == aiShadingMode_Unlit) { mAsset->extensionsUsed.KHR_materials_unlit = true; m->unlit = true; - } + } else { + // These extensions are not compatible with KHR_materials_unlit or KHR_materials_pbrSpecularGlossiness + if (!m->pbrSpecularGlossiness.isPresent) { + // Sheen + MaterialSheen sheen; + if (GetMatSheen(mat, sheen)) { + mAsset->extensionsUsed.KHR_materials_sheen = true; + m->materialSheen = Nullable(sheen); + } - bool hasMaterialSheen = false; - mat->Get(AI_MATKEY_GLTF_MATERIAL_SHEEN, hasMaterialSheen); + MaterialClearcoat clearcoat; + if (GetMatClearcoat(mat, clearcoat)) { + mAsset->extensionsUsed.KHR_materials_clearcoat = true; + m->materialClearcoat = Nullable(clearcoat); + } - if (hasMaterialSheen) { - mAsset->extensionsUsed.KHR_materials_sheen = true; - - MaterialSheen sheen; - - GetMatColor(mat, sheen.sheenColorFactor, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR); - mat->Get(AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR, sheen.sheenRoughnessFactor); - GetMatTex(mat, sheen.sheenColorTexture, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_TEXTURE); - GetMatTex(mat, sheen.sheenRoughnessTexture, AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_TEXTURE); - - m->materialSheen = Nullable(sheen); - } - - bool hasMaterialClearcoat = false; - mat->Get(AI_MATKEY_GLTF_MATERIAL_CLEARCOAT, hasMaterialClearcoat); - - if (hasMaterialClearcoat) { - mAsset->extensionsUsed.KHR_materials_clearcoat= true; - - MaterialClearcoat clearcoat; - - mat->Get(AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_FACTOR, clearcoat.clearcoatFactor); - mat->Get(AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_FACTOR, clearcoat.clearcoatRoughnessFactor); - GetMatTex(mat, clearcoat.clearcoatTexture, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_TEXTURE); - GetMatTex(mat, clearcoat.clearcoatRoughnessTexture, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_TEXTURE); - GetMatTex(mat, clearcoat.clearcoatNormalTexture, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_NORMAL_TEXTURE); - - m->materialClearcoat = Nullable(clearcoat); - } - - bool hasMaterialTransmission = false; - mat->Get(AI_MATKEY_GLTF_MATERIAL_TRANSMISSION, hasMaterialTransmission); - - if (hasMaterialTransmission) { - mAsset->extensionsUsed.KHR_materials_transmission = true; - - MaterialTransmission transmission; - - mat->Get(AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR, transmission.transmissionFactor); - GetMatTex(mat, transmission.transmissionTexture, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE); - - m->materialTransmission = Nullable(transmission); + MaterialTransmission transmission; + if (GetMatTransmission(mat, transmission)) { + mAsset->extensionsUsed.KHR_materials_transmission = true; + m->materialTransmission = Nullable(transmission); + } + } } } } @@ -799,8 +835,7 @@ void glTF2Exporter::ExportMaterials() * Search through node hierarchy and find the node containing the given meshID. * Returns true on success, and false otherwise. */ -bool FindMeshNode(Ref& nodeIn, Ref& meshNode, std::string meshID) -{ +bool FindMeshNode(Ref &nodeIn, Ref &meshNode, const std::string &meshID) { for (unsigned int i = 0; i < nodeIn->meshes.size(); ++i) { if (meshID.compare(nodeIn->meshes[i]->id) == 0) { meshNode = nodeIn; @@ -1301,8 +1336,11 @@ unsigned int glTF2Exporter::ExportNode(const aiNode* n, Ref& parent) void glTF2Exporter::ExportScene() { - const char* sceneName = "defaultScene"; - Ref scene = mAsset->scenes.Create(sceneName); + // Use the name of the scene if specified + const std::string sceneName = (mScene->mName.length > 0) ? mScene->mName.C_Str() : "defaultScene"; + + // Ensure unique + Ref scene = mAsset->scenes.Create(mAsset->FindUniqueID(sceneName, "")); // root node will be the first one exported (idx 0) if (mAsset->nodes.Size() > 0) { diff --git a/code/AssetLib/glTF2/glTF2Exporter.h b/code/AssetLib/glTF2/glTF2Exporter.h index 86497516a..f5238297f 100644 --- a/code/AssetLib/glTF2/glTF2Exporter.h +++ b/code/AssetLib/glTF2/glTF2Exporter.h @@ -72,6 +72,10 @@ namespace glTF2 struct OcclusionTextureInfo; struct Node; struct Texture; + struct PbrSpecularGlossiness; + struct MaterialSheen; + struct MaterialClearcoat; + struct MaterialTransmission; // Vec/matrix types, as raw float arrays typedef float (vec2)[2]; @@ -97,15 +101,19 @@ namespace Assimp protected: void WriteBinaryData(IOStream* outfile, std::size_t sceneLength); - void GetTexSampler(const aiMaterial* mat, glTF2::Ref texture, aiTextureType tt, unsigned int slot); - void GetMatTexProp(const aiMaterial* mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int idx); - void GetMatTexProp(const aiMaterial* mat, float& prop, const char* propName, aiTextureType tt, unsigned int idx); - void GetMatTex(const aiMaterial* mat, glTF2::Ref& texture, aiTextureType tt, unsigned int slot); - void GetMatTex(const aiMaterial* mat, glTF2::TextureInfo& prop, aiTextureType tt, unsigned int slot); - void GetMatTex(const aiMaterial* mat, glTF2::NormalTextureInfo& prop, aiTextureType tt, unsigned int slot); - void GetMatTex(const aiMaterial* mat, glTF2::OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot); - aiReturn GetMatColor(const aiMaterial* mat, glTF2::vec4& prop, const char* propName, int type, int idx); - aiReturn GetMatColor(const aiMaterial* mat, glTF2::vec3& prop, const char* propName, int type, int idx); + void GetTexSampler(const aiMaterial& mat, glTF2::Ref texture, aiTextureType tt, unsigned int slot); + void GetMatTexProp(const aiMaterial& mat, unsigned int& prop, const char* propName, aiTextureType tt, unsigned int idx); + void GetMatTexProp(const aiMaterial& mat, float& prop, const char* propName, aiTextureType tt, unsigned int idx); + void GetMatTex(const aiMaterial& mat, glTF2::Ref& texture, unsigned int &texCoord, aiTextureType tt, unsigned int slot); + void GetMatTex(const aiMaterial& mat, glTF2::TextureInfo& prop, aiTextureType tt, unsigned int slot); + void GetMatTex(const aiMaterial& mat, glTF2::NormalTextureInfo& prop, aiTextureType tt, unsigned int slot); + void GetMatTex(const aiMaterial& mat, glTF2::OcclusionTextureInfo& prop, aiTextureType tt, unsigned int slot); + aiReturn GetMatColor(const aiMaterial& mat, glTF2::vec4& prop, const char* propName, int type, int idx) const; + aiReturn GetMatColor(const aiMaterial& mat, glTF2::vec3& prop, const char* propName, int type, int idx) const; + bool GetMatSpecGloss(const aiMaterial& mat, glTF2::PbrSpecularGlossiness& pbrSG); + bool GetMatSheen(const aiMaterial& mat, glTF2::MaterialSheen& sheen); + bool GetMatClearcoat(const aiMaterial& mat, glTF2::MaterialClearcoat& clearcoat); + bool GetMatTransmission(const aiMaterial& mat, glTF2::MaterialTransmission& transmission); void ExportMetadata(); void ExportMaterials(); void ExportMeshes(); diff --git a/code/AssetLib/glTF2/glTF2Importer.cpp b/code/AssetLib/glTF2/glTF2Importer.cpp index c62989c3b..ada7aa046 100644 --- a/code/AssetLib/glTF2/glTF2Importer.cpp +++ b/code/AssetLib/glTF2/glTF2Importer.cpp @@ -165,7 +165,8 @@ inline void SetMaterialTextureProperty(std::vector &embeddedTexIdxs, Asset } mat->AddProperty(&uri, AI_MATKEY_TEXTURE(texType, texSlot)); - mat->AddProperty(&prop.texCoord, 1, AI_MATKEY_GLTF_TEXTURE_TEXCOORD(texType, texSlot)); + const int uvIndex = static_cast(prop.texCoord); + mat->AddProperty(&uvIndex, 1, AI_MATKEY_UVWSRC(texType, texSlot)); if (prop.textureTransformSupported) { aiUVTransform transform; @@ -208,6 +209,11 @@ inline void SetMaterialTextureProperty(std::vector &embeddedTexIdxs, Asset if (sampler->minFilter != SamplerMinFilter::UNSET) { mat->AddProperty(&sampler->minFilter, 1, AI_MATKEY_GLTF_MAPPINGFILTER_MIN(texType, texSlot)); } + } else { + // Use glTFv2 default sampler + const aiTextureMapMode default_wrap = aiTextureMapMode_Wrap; + mat->AddProperty(&default_wrap, 1, AI_MATKEY_MAPPINGMODE_U(texType, texSlot)); + mat->AddProperty(&default_wrap, 1, AI_MATKEY_MAPPINGMODE_V(texType, texSlot)); } } } @@ -238,16 +244,18 @@ static aiMaterial *ImportMaterial(std::vector &embeddedTexIdxs, Asset &r, M aimat->AddProperty(&str, AI_MATKEY_NAME); } + // Set Assimp DIFFUSE and BASE COLOR to the pbrMetallicRoughness base color and texture for backwards compatibility + // Technically should not load any pbrMetallicRoughness if extensionsRequired contains KHR_materials_pbrSpecularGlossiness SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); - SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR); + SetMaterialColorProperty(r, mat.pbrMetallicRoughness.baseColorFactor, aimat, AI_MATKEY_BASE_COLOR); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_DIFFUSE); - SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.baseColorTexture, aimat, aiTextureType_BASE_COLOR); SetMaterialTextureProperty(embeddedTexIdxs, r, mat.pbrMetallicRoughness.metallicRoughnessTexture, aimat, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE); - aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR); - aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR); + aimat->AddProperty(&mat.pbrMetallicRoughness.metallicFactor, 1, AI_MATKEY_METALLIC_FACTOR); + aimat->AddProperty(&mat.pbrMetallicRoughness.roughnessFactor, 1, AI_MATKEY_ROUGHNESS_FACTOR); float roughnessAsShininess = 1 - mat.pbrMetallicRoughness.roughnessFactor; roughnessAsShininess *= roughnessAsShininess * 1000; @@ -259,6 +267,7 @@ static aiMaterial *ImportMaterial(std::vector &embeddedTexIdxs, Asset &r, M SetMaterialColorProperty(r, mat.emissiveFactor, aimat, AI_MATKEY_COLOR_EMISSIVE); aimat->AddProperty(&mat.doubleSided, 1, AI_MATKEY_TWOSIDED); + aimat->AddProperty(&mat.pbrMetallicRoughness.baseColorFactor[3], 1, AI_MATKEY_OPACITY); aiString alphaMode(mat.alphaMode); aimat->AddProperty(&alphaMode, AI_MATKEY_GLTF_ALPHAMODE); @@ -268,52 +277,58 @@ static aiMaterial *ImportMaterial(std::vector &embeddedTexIdxs, Asset &r, M if (mat.pbrSpecularGlossiness.isPresent) { PbrSpecularGlossiness &pbrSG = mat.pbrSpecularGlossiness.value; - aimat->AddProperty(&mat.pbrSpecularGlossiness.isPresent, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS); SetMaterialColorProperty(r, pbrSG.diffuseFactor, aimat, AI_MATKEY_COLOR_DIFFUSE); SetMaterialColorProperty(r, pbrSG.specularFactor, aimat, AI_MATKEY_COLOR_SPECULAR); float glossinessAsShininess = pbrSG.glossinessFactor * 1000.0f; aimat->AddProperty(&glossinessAsShininess, 1, AI_MATKEY_SHININESS); - aimat->AddProperty(&pbrSG.glossinessFactor, 1, AI_MATKEY_GLTF_PBRSPECULARGLOSSINESS_GLOSSINESS_FACTOR); + aimat->AddProperty(&pbrSG.glossinessFactor, 1, AI_MATKEY_GLOSSINESS_FACTOR); SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.diffuseTexture, aimat, aiTextureType_DIFFUSE); SetMaterialTextureProperty(embeddedTexIdxs, r, pbrSG.specularGlossinessTexture, aimat, aiTextureType_SPECULAR); } + + // glTFv2 is either PBR or Unlit + aiShadingMode shadingMode = aiShadingMode_PBR_BRDF; if (mat.unlit) { - aimat->AddProperty(&mat.unlit, 1, AI_MATKEY_GLTF_UNLIT); + shadingMode = aiShadingMode_Unlit; } - //KHR_materials_sheen + aimat->AddProperty(&shadingMode, 1, AI_MATKEY_SHADING_MODEL); + + + // KHR_materials_sheen if (mat.materialSheen.isPresent) { MaterialSheen &sheen = mat.materialSheen.value; - - aimat->AddProperty(&mat.materialSheen.isPresent, 1, AI_MATKEY_GLTF_MATERIAL_SHEEN); - SetMaterialColorProperty(r, sheen.sheenColorFactor, aimat, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_FACTOR); - aimat->AddProperty(&sheen.sheenRoughnessFactor, 1, AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_FACTOR); - SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenColorTexture, aimat, AI_MATKEY_GLTF_MATERIAL_SHEEN_COLOR_TEXTURE); - SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenRoughnessTexture, aimat, AI_MATKEY_GLTF_MATERIAL_SHEEN_ROUGHNESS_TEXTURE); + // Default value {0,0,0} disables Sheen + if (sheen.sheenColorFactor != defaultSheenFactor) { + SetMaterialColorProperty(r, sheen.sheenColorFactor, aimat, AI_MATKEY_SHEEN_COLOR_FACTOR); + aimat->AddProperty(&sheen.sheenRoughnessFactor, 1, AI_MATKEY_SHEEN_ROUGHNESS_FACTOR); + SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenColorTexture, aimat, AI_MATKEY_SHEEN_COLOR_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, sheen.sheenRoughnessTexture, aimat, AI_MATKEY_SHEEN_ROUGHNESS_TEXTURE); + } } - //KHR_materials_clearcoat + // KHR_materials_clearcoat if (mat.materialClearcoat.isPresent) { MaterialClearcoat &clearcoat = mat.materialClearcoat.value; - - aimat->AddProperty(&mat.materialClearcoat.isPresent, 1, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT); - aimat->AddProperty(&clearcoat.clearcoatFactor, 1, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_FACTOR); - aimat->AddProperty(&clearcoat.clearcoatRoughnessFactor, 1, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_FACTOR); - SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatTexture, aimat, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_TEXTURE); - SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatRoughnessTexture, aimat, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_ROUGHNESS_TEXTURE); - SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatNormalTexture, aimat, AI_MATKEY_GLTF_MATERIAL_CLEARCOAT_NORMAL_TEXTURE); + // Default value 0.0 disables clearcoat + if (clearcoat.clearcoatFactor != 0.0f) { + aimat->AddProperty(&clearcoat.clearcoatFactor, 1, AI_MATKEY_CLEARCOAT_FACTOR); + aimat->AddProperty(&clearcoat.clearcoatRoughnessFactor, 1, AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR); + SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatTexture, aimat, AI_MATKEY_CLEARCOAT_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatRoughnessTexture, aimat, AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE); + SetMaterialTextureProperty(embeddedTexIdxs, r, clearcoat.clearcoatNormalTexture, aimat, AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE); + } } - //KHR_materials_transmission + // KHR_materials_transmission if (mat.materialTransmission.isPresent) { MaterialTransmission &transmission = mat.materialTransmission.value; - aimat->AddProperty(&mat.materialTransmission.isPresent, 1, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION); - aimat->AddProperty(&transmission.transmissionFactor, 1, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_FACTOR); - SetMaterialTextureProperty(embeddedTexIdxs, r, transmission.transmissionTexture, aimat, AI_MATKEY_GLTF_MATERIAL_TRANSMISSION_TEXTURE); + aimat->AddProperty(&transmission.transmissionFactor, 1, AI_MATKEY_TRANSMISSION_FACTOR); + SetMaterialTextureProperty(embeddedTexIdxs, r, transmission.transmissionTexture, aimat, AI_MATKEY_TRANSMISSION_TEXTURE); } return aimat; @@ -489,7 +504,7 @@ void glTF2Importer::ImportMeshes(glTF2::Asset &r) { "\" does not match the vertex count"); continue; } - + auto componentType = attr.color[c]->componentType; if (componentType == glTF2::ComponentType_FLOAT) { attr.color[c]->ExtractData(aim->mColors[c]); @@ -977,13 +992,21 @@ void ParseExtensions(aiMetadata *metadata, const CustomExtension &extension) { metadata->Add(extension.name.c_str(), extension.mBoolValue.value); } else if (extension.mValues.isPresent) { aiMetadata val; - for (size_t i = 0; i < extension.mValues.value.size(); ++i) { - ParseExtensions(&val, extension.mValues.value[i]); + for (auto const & subExtension : extension.mValues.value) { + ParseExtensions(&val, subExtension); } metadata->Add(extension.name.c_str(), val); } } +void ParseExtras(aiMetadata *metadata, const CustomExtension &extension) { + if (extension.mValues.isPresent) { + for (auto const & subExtension : extension.mValues.value) { + ParseExtensions(metadata, subExtension); + } + } +} + aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector &meshOffsets, glTF2::Ref &ptr) { Node &node = *ptr; @@ -1002,9 +1025,14 @@ aiNode *ImportNode(aiScene *pScene, glTF2::Asset &r, std::vector & } } - if (node.extensions) { + if (node.customExtensions || node.extras) { ainode->mMetaData = new aiMetadata; - ParseExtensions(ainode->mMetaData, node.extensions); + if (node.customExtensions) { + ParseExtensions(ainode->mMetaData, node.customExtensions); + } + if (node.extras) { + ParseExtras(ainode->mMetaData, node.extras); + } } GetNodeTransform(ainode->mTransformation, node); @@ -1308,6 +1336,23 @@ std::unordered_map GatherSamplers(Animation &an continue; } + auto& animsampler = anim.samplers[channel.sampler]; + + if (!animsampler.input) { + ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler input. Skipping."); + continue; + } + + if (!animsampler.output) { + ASSIMP_LOG_WARN("Animation ", anim.name, ": Missing sampler output. Skipping."); + continue; + } + + if (animsampler.input->count > animsampler.output->count) { + ASSIMP_LOG_WARN("Animation ", anim.name, ": Number of keyframes in sampler input ", animsampler.input->count, " exceeds number of keyframes in sampler output ", animsampler.output->count); + continue; + } + const unsigned int node_index = channel.target.node.GetIndex(); AnimationSamplers &sampler = samplers[node_index]; @@ -1442,10 +1487,11 @@ void glTF2Importer::ImportEmbeddedTextures(glTF2::Asset &r) { } } - if (numEmbeddedTexs == 0) + if (numEmbeddedTexs == 0) { return; + } - ASSIMP_LOG_DEBUG("Importing ", numEmbeddedTexs, " embedded textures"); + ASSIMP_LOG_DEBUG("Importing ", numEmbeddedTexs, " embedded textures"); mScene->mTextures = new aiTexture *[numEmbeddedTexs]; std::fill(mScene->mTextures, mScene->mTextures + numEmbeddedTexs, nullptr); @@ -1498,7 +1544,8 @@ void glTF2Importer::ImportCommonMetadata(glTF2::Asset& a) { const bool hasVersion = !a.asset.version.empty(); const bool hasGenerator = !a.asset.generator.empty(); const bool hasCopyright = !a.asset.copyright.empty(); - if (hasVersion || hasGenerator || hasCopyright) { + const bool hasSceneMetadata = a.scene->customExtensions; + if (hasVersion || hasGenerator || hasCopyright || hasSceneMetadata) { mScene->mMetaData = new aiMetadata; if (hasVersion) { mScene->mMetaData->Add(AI_METADATA_SOURCE_FORMAT_VERSION, aiString(a.asset.version)); @@ -1509,6 +1556,9 @@ void glTF2Importer::ImportCommonMetadata(glTF2::Asset& a) { if (hasCopyright) { mScene->mMetaData->Add(AI_METADATA_SOURCE_COPYRIGHT, aiString(a.asset.copyright)); } + if (hasSceneMetadata) { + ParseExtensions(mScene->mMetaData, a.scene->customExtensions); + } } } diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index be1619e81..933b5488c 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -822,7 +822,10 @@ ADD_ASSIMP_IMPORTER( GLTF AssetLib/glTF2/glTF2Importer.h ) -ADD_ASSIMP_IMPORTER( 3MF +ADD_ASSIMP_IMPORTER(3MF + AssetLib/3MF/3MFTypes.h + AssetLib/3MF/XmlSerializer.h + AssetLib/3MF/XmlSerializer.cpp AssetLib/3MF/D3MFImporter.h AssetLib/3MF/D3MFImporter.cpp AssetLib/3MF/D3MFOpcPackage.h @@ -875,6 +878,7 @@ ELSE() ../contrib/pugixml/src/pugiconfig.hpp ../contrib/pugixml/src/pugixml.hpp ) + INCLUDE_DIRECTORIES("../contrib/pugixml/src") SOURCE_GROUP( Contrib\\Pugixml FILES ${Pugixml_SRCS}) ENDIF() @@ -1035,16 +1039,26 @@ IF(ASSIMP_HUNTER_ENABLED) hunter_add_package(RapidJSON) find_package(RapidJSON CONFIG REQUIRED) ELSE() - INCLUDE_DIRECTORIES( "../contrib/rapidjson/include" ) - INCLUDE_DIRECTORIES( "../contrib" ) - INCLUDE_DIRECTORIES( "../contrib/pugixml/src" ) - ADD_DEFINITIONS( -DRAPIDJSON_HAS_STDSTRING=1 ) + INCLUDE_DIRECTORIES("../contrib/rapidjson/include") + ADD_DEFINITIONS( -DRAPIDJSON_HAS_STDSTRING=1) option( ASSIMP_RAPIDJSON_NO_MEMBER_ITERATOR "Suppress rapidjson warning on MSVC (NOTE: breaks android build)" ON ) if(ASSIMP_RAPIDJSON_NO_MEMBER_ITERATOR) ADD_DEFINITIONS( -DRAPIDJSON_NOMEMBERITERATORCLASS ) endif() ENDIF() +# stb +IF(ASSIMP_HUNTER_ENABLED) + hunter_add_package(stb) + find_package(stb CONFIG REQUIRED) +ELSE() + SET( stb_SRCS + ../contrib/stb/stb_image.h + ) + INCLUDE_DIRECTORIES("../contrib") + SOURCE_GROUP( Contrib\\stb FILES ${stb_SRCS}) +ENDIF() + # VC2010 fixes if(MSVC10) option( VC10_STDINT_FIX "Fix for VC10 Compiler regarding pstdint.h redefinition errors" OFF ) @@ -1103,6 +1117,7 @@ SET( assimp_src ${open3dgc_SRCS} ${ziplib_SRCS} ${Pugixml_SRCS} + ${stb_SRCS} # Necessary to show the headers in the project when using the VC++ generator: ${PUBLIC_HEADERS} @@ -1160,8 +1175,9 @@ IF(ASSIMP_HUNTER_ENABLED) utf8cpp zip::zip pugixml + stb::stb ) - + if (ASSIMP_BUILD_DRACO) target_link_libraries(assimp PUBLIC ${draco_LIBRARIES}) endif() diff --git a/code/Common/Assimp.cpp b/code/Common/Assimp.cpp index 4e2c7117c..3a0ec7d60 100644 --- a/code/Common/Assimp.cpp +++ b/code/Common/Assimp.cpp @@ -72,12 +72,25 @@ namespace Assimp { // underlying structure for aiPropertyStore typedef BatchLoader::PropertyMap PropertyMap; +#if defined(__has_warning) +#if __has_warning("-Wordered-compare-function-pointers") +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wordered-compare-function-pointers" +#endif +#endif + /** Stores the LogStream objects for all active C log streams */ struct mpred { bool operator()(const aiLogStream &s0, const aiLogStream &s1) const { return s0.callback < s1.callback && s0.user < s1.user; } }; + +#if defined(__has_warning) +#if __has_warning("-Wordered-compare-function-pointers") +#pragma GCC diagnostic pop +#endif +#endif typedef std::map LogStreamMap; /** Stores the LogStream objects allocated by #aiGetPredefinedLogStream */ @@ -1251,3 +1264,36 @@ ASSIMP_API void aiQuaternionInterpolate( ai_assert(nullptr != end); aiQuaternion::Interpolate(*dst, *start, *end, factor); } + + +// stb_image is a lightweight image loader. It is shared by: +// - M3D import +// - PBRT export +// Since it's a header-only library, its implementation must be instantiated in some cpp file. +// Don't scatter this task over multiple importers/exporters. Maintain it in a central place (here!). + +#define ASSIMP_HAS_PBRT_EXPORT (!ASSIMP_BUILD_NO_EXPORT && !ASSIMP_BUILD_NO_PBRT_EXPORTER) +#define ASSIMP_HAS_M3D ((!ASSIMP_BUILD_NO_EXPORT && !ASSIMP_BUILD_NO_M3D_EXPORTER) || !ASSIMP_BUILD_NO_M3D_IMPORTER) + +#if ASSIMP_HAS_PBRT_EXPORT +# define ASSIMP_NEEDS_STB_IMAGE 1 +#elif ASSIMP_HAS_M3D +# define ASSIMP_NEEDS_STB_IMAGE 1 +# define STBI_ONLY_PNG +#endif + +#if ASSIMP_NEEDS_STB_IMAGE + +# if _MSC_VER // "unreferenced function has been removed" (SSE2 detection routine in x64 builds) +# pragma warning(push) +# pragma warning(disable: 4505) +# endif + +# define STB_IMAGE_IMPLEMENTATION +# include "stb/stb_image.h" + +# if _MSC_VER +# pragma warning(pop) +# endif + +#endif diff --git a/code/Common/DefaultIOStream.cpp b/code/Common/DefaultIOStream.cpp index ba9b9d625..557fbc78f 100644 --- a/code/Common/DefaultIOStream.cpp +++ b/code/Common/DefaultIOStream.cpp @@ -62,7 +62,7 @@ inline int select_fseek(FILE *file, int64_t offset, int origin) { } - + #if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601) template <> inline size_t select_ftell<8>(FILE *file) { @@ -75,7 +75,7 @@ inline int select_fseek<8>(FILE *file, int64_t offset, int origin) { } #endif // #if defined _WIN32 && (!defined __GNUC__ || __MSVCRT_VERSION__ >= 0x0601) - + } // namespace // ---------------------------------------------------------------------------------- @@ -95,7 +95,7 @@ size_t DefaultIOStream::Read(void *pvBuffer, } ai_assert(nullptr != pvBuffer); ai_assert(0 != pSize); - + return (mFile ? ::fread(pvBuffer, pSize, pCount, mFile) : 0); } diff --git a/code/Common/DefaultIOSystem.cpp b/code/Common/DefaultIOSystem.cpp index 98d51a17d..de93909de 100644 --- a/code/Common/DefaultIOSystem.cpp +++ b/code/Common/DefaultIOSystem.cpp @@ -71,7 +71,7 @@ static std::wstring Utf8ToWide(const char *in) { // size includes terminating null; std::wstring adds null automatically std::wstring out(static_cast(size) - 1, L'\0'); MultiByteToWideChar(CP_UTF8, 0, in, -1, &out[0], size); - + return out; } @@ -85,7 +85,7 @@ static std::string WideToUtf8(const wchar_t *in) { // size includes terminating null; std::string adds null automatically std::string out(static_cast(size) - 1, '\0'); WideCharToMultiByte(CP_UTF8, 0, in, -1, &out[0], size, nullptr, nullptr); - + return out; } #endif @@ -121,7 +121,7 @@ IOStream *DefaultIOSystem::Open(const char *strFile, const char *strMode) { if (name.empty()) { return nullptr; } - + file = ::_wfopen(name.c_str(), Utf8ToWide(strMode).c_str()); #else file = ::fopen(strFile, strMode); @@ -173,7 +173,7 @@ inline static std::string MakeAbsolutePath(const char *in) { free(ret); } #endif - if (!ret) { + else { // preserve the input path, maybe someone else is able to fix // the path before it is accessed (e.g. our file system filter) ASSIMP_LOG_WARN("Invalid path: ", std::string(in)); diff --git a/code/Common/Exporter.cpp b/code/Common/Exporter.cpp index ebcc955df..512bbf447 100644 --- a/code/Common/Exporter.cpp +++ b/code/Common/Exporter.cpp @@ -83,7 +83,7 @@ namespace Assimp { void GetPostProcessingStepInstanceList(std::vector< BaseProcess* >& out); // ------------------------------------------------------------------------------------------------ -// Exporter worker function prototypes. Do not use const, because some exporter need to convert +// Exporter worker function prototypes. Do not use const, because some exporter need to convert // the scene temporary #ifndef ASSIMP_BUILD_NO_COLLADA_EXPORTER void ExportSceneCollada(const char*,IOSystem*, const aiScene*, const ExportProperties*); @@ -343,7 +343,7 @@ const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const cha delete pimpl->blob; pimpl->blob = nullptr; } - + auto baseName = pProperties ? pProperties->GetPropertyString(AI_CONFIG_EXPORT_BLOB_NAME, AI_BLOBIO_MAGIC) : AI_BLOBIO_MAGIC; std::shared_ptr old = pimpl->mIOSystem; diff --git a/code/Common/FileSystemFilter.h b/code/Common/FileSystemFilter.h index 6585f9df6..6782dd9e5 100644 --- a/code/Common/FileSystemFilter.h +++ b/code/Common/FileSystemFilter.h @@ -101,7 +101,7 @@ public: /** Tests for the existence of a file at the given path. */ bool Exists( const char* pFile) const { ai_assert( nullptr != mWrapped ); - + std::string tmp = pFile; // Currently this IOSystem is also used to open THE ONE FILE. @@ -126,7 +126,7 @@ public: if ( nullptr == pFile || nullptr == pMode ) { return nullptr; } - + ai_assert( nullptr != pFile ); ai_assert( nullptr != pMode ); diff --git a/code/Common/Importer.cpp b/code/Common/Importer.cpp index a2ad041fb..d0ed3c788 100644 --- a/code/Common/Importer.cpp +++ b/code/Common/Importer.cpp @@ -201,7 +201,7 @@ Importer::~Importer() { // Register a custom post-processing step aiReturn Importer::RegisterPPStep(BaseProcess* pImp) { ai_assert( nullptr != pImp ); - + ASSIMP_BEGIN_EXCEPTION_REGION(); pimpl->mPostProcessingSteps.push_back(pImp); @@ -215,7 +215,7 @@ aiReturn Importer::RegisterPPStep(BaseProcess* pImp) { // Register a custom loader plugin aiReturn Importer::RegisterLoader(BaseImporter* pImp) { ai_assert(nullptr != pImp); - + ASSIMP_BEGIN_EXCEPTION_REGION(); // -------------------------------------------------------------------- @@ -242,7 +242,7 @@ aiReturn Importer::RegisterLoader(BaseImporter* pImp) { pimpl->mImporter.push_back(pImp); ASSIMP_LOG_INFO("Registering custom importer for these file extensions: ", baked); ASSIMP_END_EXCEPTION_REGION(aiReturn); - + return AI_SUCCESS; } @@ -296,7 +296,7 @@ aiReturn Importer::UnregisterPPStep(BaseProcess* pImp) { // Supplies a custom IO handler to the importer to open and access files. void Importer::SetIOHandler( IOSystem* pIOHandler) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); // If the new handler is zero, allocate a default IO implementation. if (!pIOHandler) { @@ -315,7 +315,7 @@ void Importer::SetIOHandler( IOSystem* pIOHandler) { // Get the currently set IO handler IOSystem* Importer::GetIOHandler() const { ai_assert(nullptr != pimpl); - + return pimpl->mIOHandler; } @@ -323,7 +323,7 @@ IOSystem* Importer::GetIOHandler() const { // Check whether a custom IO handler is currently set bool Importer::IsDefaultIOHandler() const { ai_assert(nullptr != pimpl); - + return pimpl->mIsDefaultHandler; } @@ -331,9 +331,9 @@ bool Importer::IsDefaultIOHandler() const { // Supplies a custom progress handler to get regular callbacks during importing void Importer::SetProgressHandler ( ProgressHandler* pHandler ) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); - + // If the new handler is zero, allocate a default implementation. if (!pHandler) { // Release pointer in the possession of the caller @@ -351,7 +351,7 @@ void Importer::SetProgressHandler ( ProgressHandler* pHandler ) { // Get the currently set progress handler ProgressHandler* Importer::GetProgressHandler() const { ai_assert(nullptr != pimpl); - + return pimpl->mProgressHandler; } @@ -359,7 +359,7 @@ ProgressHandler* Importer::GetProgressHandler() const { // Check whether a custom progress handler is currently set bool Importer::IsDefaultProgressHandler() const { ai_assert(nullptr != pimpl); - + return pimpl->mIsDefaultProgressHandler; } @@ -381,7 +381,7 @@ bool _ValidateFlags(unsigned int pFlags) { // Free the current scene void Importer::FreeScene( ) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); delete pimpl->mScene; @@ -396,14 +396,14 @@ void Importer::FreeScene( ) { // Get the current error string, if any const char* Importer::GetErrorString() const { ai_assert(nullptr != pimpl); - + // Must remain valid as long as ReadFile() or FreeFile() are not called return pimpl->mErrorString.c_str(); } const std::exception_ptr& Importer::GetException() const { ai_assert(nullptr != pimpl); - + // Must remain valid as long as ReadFile() or FreeFile() are not called return pimpl->mException; } @@ -412,7 +412,7 @@ const std::exception_ptr& Importer::GetException() const { // Enable extra-verbose mode void Importer::SetExtraVerbose(bool bDo) { ai_assert(nullptr != pimpl); - + pimpl->bExtraVerbose = bDo; } @@ -420,7 +420,7 @@ void Importer::SetExtraVerbose(bool bDo) { // Get the current scene const aiScene* Importer::GetScene() const { ai_assert(nullptr != pimpl); - + return pimpl->mScene; } @@ -428,7 +428,7 @@ const aiScene* Importer::GetScene() const { // Orphan the current scene and return it. aiScene* Importer::GetOrphanedScene() { ai_assert(nullptr != pimpl); - + aiScene* s = pimpl->mScene; ASSIMP_BEGIN_EXCEPTION_REGION(); @@ -437,7 +437,7 @@ aiScene* Importer::GetOrphanedScene() { pimpl->mErrorString = std::string(); pimpl->mException = std::exception_ptr(); ASSIMP_END_EXCEPTION_REGION(aiScene*); - + return s; } @@ -487,7 +487,7 @@ const aiScene* Importer::ReadFileFromMemory( const void* pBuffer, unsigned int pFlags, const char* pHint /*= ""*/) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); if (!pHint) { pHint = ""; @@ -518,7 +518,7 @@ const aiScene* Importer::ReadFileFromMemory( const void* pBuffer, // ------------------------------------------------------------------------------------------------ void WriteLogOpening(const std::string& file) { - + ASSIMP_LOG_INFO("Load ", file); // print a full version dump. This is nice because we don't @@ -580,7 +580,7 @@ void WriteLogOpening(const std::string& file) { // Reads the given file and returns its contents if successful. const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); const std::string pFile(_pFile); @@ -745,7 +745,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) { // either successful or failure - the pointer expresses it anyways ASSIMP_END_EXCEPTION_REGION_WITH_ERROR_STRING(const aiScene*, pimpl->mErrorString, pimpl->mException); - + return pimpl->mScene; } @@ -754,7 +754,7 @@ const aiScene* Importer::ReadFile( const char* _pFile, unsigned int pFlags) { // Apply post-processing to the currently bound scene const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); // Return immediately if no scene is active if (!pimpl->mScene) { @@ -832,7 +832,7 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) { } #endif // ! DEBUG } - pimpl->mProgressHandler->UpdatePostProcess( static_cast(pimpl->mPostProcessingSteps.size()), + pimpl->mProgressHandler->UpdatePostProcess( static_cast(pimpl->mPostProcessingSteps.size()), static_cast(pimpl->mPostProcessingSteps.size()) ); // update private scene flags @@ -845,14 +845,14 @@ const aiScene* Importer::ApplyPostProcessing(unsigned int pFlags) { ASSIMP_LOG_INFO("Leaving post processing pipeline"); ASSIMP_END_EXCEPTION_REGION(const aiScene*); - + return pimpl->mScene; } // ------------------------------------------------------------------------------------------------ const aiScene* Importer::ApplyCustomizedPostProcessing( BaseProcess *rootProcess, bool requestValidation ) { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); // Return immediately if no scene is active @@ -934,14 +934,14 @@ bool Importer::IsExtensionSupported(const char* szExtension) const { // ------------------------------------------------------------------------------------------------ size_t Importer::GetImporterCount() const { ai_assert(nullptr != pimpl); - + return pimpl->mImporter.size(); } // ------------------------------------------------------------------------------------------------ const aiImporterDesc* Importer::GetImporterInfo(size_t index) const { ai_assert(nullptr != pimpl); - + if (index >= pimpl->mImporter.size()) { return nullptr; } @@ -952,7 +952,7 @@ const aiImporterDesc* Importer::GetImporterInfo(size_t index) const { // ------------------------------------------------------------------------------------------------ BaseImporter* Importer::GetImporter (size_t index) const { ai_assert(nullptr != pimpl); - + if (index >= pimpl->mImporter.size()) { return nullptr; } @@ -963,7 +963,7 @@ BaseImporter* Importer::GetImporter (size_t index) const { // Find a loader plugin for a given file extension BaseImporter* Importer::GetImporter (const char* szExtension) const { ai_assert(nullptr != pimpl); - + return GetImporter(GetImporterIndex(szExtension)); } @@ -1002,7 +1002,7 @@ size_t Importer::GetImporterIndex (const char* szExtension) const { // Helper function to build a list of all file extensions supported by ASSIMP void Importer::GetExtensionList(aiString& szOut) const { ai_assert(nullptr != pimpl); - + ASSIMP_BEGIN_EXCEPTION_REGION(); std::set str; for (std::vector::const_iterator i = pimpl->mImporter.begin();i != pimpl->mImporter.end();++i) { @@ -1028,7 +1028,7 @@ void Importer::GetExtensionList(aiString& szOut) const { // Set a configuration property bool Importer::SetPropertyInteger(const char* szName, int iValue) { ai_assert(nullptr != pimpl); - + bool existing; ASSIMP_BEGIN_EXCEPTION_REGION(); existing = SetGenericProperty(pimpl->mIntProperties, szName,iValue); @@ -1040,7 +1040,7 @@ bool Importer::SetPropertyInteger(const char* szName, int iValue) { // Set a configuration property bool Importer::SetPropertyFloat(const char* szName, ai_real iValue) { ai_assert(nullptr != pimpl); - + bool existing; ASSIMP_BEGIN_EXCEPTION_REGION(); existing = SetGenericProperty(pimpl->mFloatProperties, szName,iValue); @@ -1052,7 +1052,7 @@ bool Importer::SetPropertyFloat(const char* szName, ai_real iValue) { // Set a configuration property bool Importer::SetPropertyString(const char* szName, const std::string& value) { ai_assert(nullptr != pimpl); - + bool existing; ASSIMP_BEGIN_EXCEPTION_REGION(); existing = SetGenericProperty(pimpl->mStringProperties, szName,value); @@ -1064,7 +1064,7 @@ bool Importer::SetPropertyString(const char* szName, const std::string& value) { // Set a configuration property bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) { ai_assert(nullptr != pimpl); - + bool existing; ASSIMP_BEGIN_EXCEPTION_REGION(); existing = SetGenericProperty(pimpl->mMatrixProperties, szName,value); @@ -1076,7 +1076,7 @@ bool Importer::SetPropertyMatrix(const char* szName, const aiMatrix4x4& value) { // Get a configuration property int Importer::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffffffff*/) const { ai_assert(nullptr != pimpl); - + return GetGenericProperty(pimpl->mIntProperties,szName,iErrorReturn); } @@ -1084,7 +1084,7 @@ int Importer::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffff // Get a configuration property ai_real Importer::GetPropertyFloat(const char* szName, ai_real iErrorReturn /*= 10e10*/) const { ai_assert(nullptr != pimpl); - + return GetGenericProperty(pimpl->mFloatProperties,szName,iErrorReturn); } @@ -1092,7 +1092,7 @@ ai_real Importer::GetPropertyFloat(const char* szName, ai_real iErrorReturn /*= // Get a configuration property std::string Importer::GetPropertyString(const char* szName, const std::string& iErrorReturn /*= ""*/) const { ai_assert(nullptr != pimpl); - + return GetGenericProperty(pimpl->mStringProperties,szName,iErrorReturn); } @@ -1100,13 +1100,13 @@ std::string Importer::GetPropertyString(const char* szName, const std::string& i // Get a configuration property aiMatrix4x4 Importer::GetPropertyMatrix(const char* szName, const aiMatrix4x4& iErrorReturn /*= aiMatrix4x4()*/) const { ai_assert(nullptr != pimpl); - + return GetGenericProperty(pimpl->mMatrixProperties,szName,iErrorReturn); } // ------------------------------------------------------------------------------------------------ // Get the memory requirements of a single node -inline +inline void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode) { if ( nullptr == pcNode ) { return; @@ -1124,7 +1124,7 @@ void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode) { // Get the memory requirements of the scene void Importer::GetMemoryRequirements(aiMemoryInfo& in) const { ai_assert(nullptr != pimpl); - + in = aiMemoryInfo(); aiScene* mScene = pimpl->mScene; diff --git a/code/Common/Importer.h b/code/Common/Importer.h index e7da7f439..0e04f9452 100644 --- a/code/Common/Importer.h +++ b/code/Common/Importer.h @@ -183,7 +183,7 @@ public: // ------------------------------------------------------------------- /** Construct a batch loader from a given IO system to be used - * to access external files + * to access external files */ explicit BatchLoader(IOSystem* pIO, bool validate = false ); @@ -197,13 +197,13 @@ public: * @param enable True for validation. */ void setValidation( bool enabled ); - + // ------------------------------------------------------------------- /** Returns the current validation step. * @return The current validation step. */ bool getValidation() const; - + // ------------------------------------------------------------------- /** Add a new file to the list of files to be loaded. * @param file File to be loaded diff --git a/code/Common/ImporterRegistry.cpp b/code/Common/ImporterRegistry.cpp index ddfcf6798..5df096166 100644 --- a/code/Common/ImporterRegistry.cpp +++ b/code/Common/ImporterRegistry.cpp @@ -205,8 +205,8 @@ corresponding preprocessor flag to selectively disable formats. namespace Assimp { // ------------------------------------------------------------------------------------------------ -void GetImporterInstanceList(std::vector &out) { - +void GetImporterInstanceList(std::vector &out) { + // Some importers may be unimplemented or otherwise unsuitable for general use // in their current state. Devs can set ASSIMP_ENABLE_DEV_IMPORTERS in their // local environment to enable them, otherwise they're left out of the registry. diff --git a/code/Common/RemoveComments.cpp b/code/Common/RemoveComments.cpp index d1c2ac391..e1ba99761 100644 --- a/code/Common/RemoveComments.cpp +++ b/code/Common/RemoveComments.cpp @@ -59,13 +59,16 @@ void CommentRemover::RemoveLineComments(const char* szComment, ai_assert(nullptr != szBuffer); ai_assert(*szComment); - const size_t len = strlen(szComment); + size_t len = strlen(szComment); + const size_t lenBuffer = strlen(szBuffer); + if (len > lenBuffer) { + len = lenBuffer; + } while (*szBuffer) { // skip over quotes if (*szBuffer == '\"' || *szBuffer == '\'') while (*szBuffer++ && *szBuffer != '\"' && *szBuffer != '\''); - if (!strncmp(szBuffer,szComment,len)) { while (!IsLineEnd(*szBuffer)) *szBuffer++ = chReplacement; diff --git a/code/Common/SceneCombiner.cpp b/code/Common/SceneCombiner.cpp index 555d46b6a..8f10d6308 100644 --- a/code/Common/SceneCombiner.cpp +++ b/code/Common/SceneCombiner.cpp @@ -406,11 +406,25 @@ void SceneCombiner::MergeScenes(aiScene **_dest, aiScene *master, std::vector, // where n is the index of the texture. - aiString &s = *((aiString *)prop->mData); + // Copy here because we overwrite the string data in-place and the buffer inside of aiString + // will be a lie if we just reinterpret from prop->mData. The size of mData is not guaranteed to be + // MAXLEN in size. + aiString s(*(aiString *)prop->mData); if ('*' == s.data[0]) { // Offset the index and write it back .. const unsigned int idx = strtoul10(&s.data[1]) + offset[n]; - ASSIMP_itoa10(&s.data[1], sizeof(s.data) - 1, idx); + const unsigned int oldLen = s.length; + + s.length = 1 + ASSIMP_itoa10(&s.data[1], sizeof(s.data) - 1, idx); + + // The string changed in size so we need to reallocate the buffer for the property. + if (oldLen < s.length) { + prop->mDataLength += s.length - oldLen; + delete[] prop->mData; + prop->mData = new char[prop->mDataLength]; + } + + memcpy(prop->mData, static_cast(&s), prop->mDataLength); } } diff --git a/code/Common/ScenePreprocessor.cpp b/code/Common/ScenePreprocessor.cpp index 132b32df7..2ea17c643 100644 --- a/code/Common/ScenePreprocessor.cpp +++ b/code/Common/ScenePreprocessor.cpp @@ -89,6 +89,9 @@ void ScenePreprocessor::ProcessScene() { ASSIMP_LOG_DEBUG("ScenePreprocessor: Adding default material \'" AI_DEFAULT_MATERIAL_NAME "\'"); for (unsigned int i = 0; i < scene->mNumMeshes; ++i) { + if (nullptr == scene->mMeshes[i]) { + continue; + } scene->mMeshes[i]->mMaterialIndex = scene->mNumMaterials; } diff --git a/code/Common/Win32DebugLogStream.h b/code/Common/Win32DebugLogStream.h index 3385aa8ce..51f1ab178 100644 --- a/code/Common/Win32DebugLogStream.h +++ b/code/Common/Win32DebugLogStream.h @@ -71,19 +71,19 @@ public: }; // --------------------------------------------------------------------------- -inline -Win32DebugLogStream::Win32DebugLogStream(){ +inline +Win32DebugLogStream::Win32DebugLogStream(){ // empty } // --------------------------------------------------------------------------- -inline +inline Win32DebugLogStream::~Win32DebugLogStream(){ // empty } // --------------------------------------------------------------------------- -inline +inline void Win32DebugLogStream::write(const char* message) { ::OutputDebugStringA( message); } diff --git a/code/Common/material.cpp b/code/Common/material.cpp index 5230c8b43..6c90e66f0 100644 --- a/code/Common/material.cpp +++ b/code/Common/material.cpp @@ -47,10 +47,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include // ------------------------------------------------------------------------------- -const char* TextureTypeToString(aiTextureType in) -{ - switch (in) - { +const char *TextureTypeToString(aiTextureType in) { + switch (in) { case aiTextureType_NONE: return "n/a"; case aiTextureType_DIFFUSE: @@ -87,6 +85,12 @@ const char* TextureTypeToString(aiTextureType in) return "DiffuseRoughness"; case aiTextureType_AMBIENT_OCCLUSION: return "AmbientOcclusion"; + case aiTextureType_SHEEN: + return "Sheen"; + case aiTextureType_CLEARCOAT: + return "Clearcoat"; + case aiTextureType_TRANSMISSION: + return "Transmission"; case aiTextureType_UNKNOWN: return "Unknown"; default: diff --git a/code/Material/MaterialSystem.cpp b/code/Material/MaterialSystem.cpp index c35a1aa93..23d198953 100644 --- a/code/Material/MaterialSystem.cpp +++ b/code/Material/MaterialSystem.cpp @@ -555,17 +555,23 @@ uint32_t Assimp::ComputeMaterialHash(const aiMaterial *mat, bool includeMatName } // ------------------------------------------------------------------------------------------------ -void aiMaterial::CopyPropertyList(aiMaterial *pcDest, +void aiMaterial::CopyPropertyList(aiMaterial *const pcDest, const aiMaterial *pcSrc) { ai_assert(nullptr != pcDest); ai_assert(nullptr != pcSrc); + ai_assert(pcDest->mNumProperties <= pcDest->mNumAllocated); + ai_assert(pcSrc->mNumProperties <= pcSrc->mNumAllocated); - unsigned int iOldNum = pcDest->mNumProperties; + const unsigned int iOldNum = pcDest->mNumProperties; pcDest->mNumAllocated += pcSrc->mNumAllocated; pcDest->mNumProperties += pcSrc->mNumProperties; + const unsigned int numAllocated = pcDest->mNumAllocated; aiMaterialProperty **pcOld = pcDest->mProperties; - pcDest->mProperties = new aiMaterialProperty *[pcDest->mNumAllocated]; + pcDest->mProperties = new aiMaterialProperty *[numAllocated]; + + ai_assert(!iOldNum || pcOld); + ai_assert(iOldNum < numAllocated); if (iOldNum && pcOld) { for (unsigned int i = 0; i < iOldNum; ++i) { diff --git a/code/Pbrt/PbrtExporter.cpp b/code/Pbrt/PbrtExporter.cpp index b793c37f9..1c7024c28 100644 --- a/code/Pbrt/PbrtExporter.cpp +++ b/code/Pbrt/PbrtExporter.cpp @@ -83,8 +83,7 @@ Other: #include #include -#define STB_IMAGE_IMPLEMENTATION -#include "stb_image.h" +#include "stb/stb_image.h" using namespace Assimp; @@ -106,14 +105,13 @@ void ExportScenePbrt ( } // end of namespace Assimp // Constructor -PbrtExporter::PbrtExporter ( - const aiScene* pScene, IOSystem* pIOSystem, - const std::string path, const std::string file) -: mScene(pScene), - mIOSystem(pIOSystem), - mPath(path), - mFile(file) -{ +PbrtExporter::PbrtExporter( + const aiScene *pScene, IOSystem *pIOSystem, + const std::string &path, const std::string &file) : + mScene(pScene), + mIOSystem(pIOSystem), + mPath(path), + mFile(file) { // Export embedded textures. if (mScene->mNumTextures > 0) if (!mIOSystem->CreateDirectory("textures")) @@ -210,12 +208,12 @@ void PbrtExporter::WriteMetaData() { aiString* value = static_cast(pMetaData->mValues[i].mData); std::string svalue = value->C_Str(); - std::size_t found = svalue.find_first_of("\n"); + std::size_t found = svalue.find_first_of('\n'); mOutput << "\n"; while (found != std::string::npos) { mOutput << "# " << svalue.substr(0, found) << "\n"; svalue = svalue.substr(found + 1); - found = svalue.find_first_of("\n"); + found = svalue.find_first_of('\n'); } mOutput << "# " << svalue << "\n"; break; @@ -596,8 +594,8 @@ void PbrtExporter::WriteMaterial(int m) { } mOutput << "\n"; - auto White = [](aiColor3D c) { return c.r == 1 && c.g == 1 && c.b == 1; }; - auto Black = [](aiColor3D c) { return c.r == 0 && c.g == 0 && c.b == 0; }; + auto White = [](const aiColor3D &c) { return c.r == 1 && c.g == 1 && c.b == 1; }; + auto Black = [](const aiColor3D &c) { return c.r == 0 && c.g == 0 && c.b == 0; }; aiColor3D diffuse, specular, transparency; bool constantDiffuse = (material->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse) == AI_SUCCESS && diff --git a/code/Pbrt/PbrtExporter.h b/code/Pbrt/PbrtExporter.h index 167f318fc..e8ff03ccb 100644 --- a/code/Pbrt/PbrtExporter.h +++ b/code/Pbrt/PbrtExporter.h @@ -74,8 +74,8 @@ class PbrtExporter { public: /// Constructor for a specific scene to export - PbrtExporter(const aiScene* pScene, IOSystem* pIOSystem, - const std::string path, const std::string file); + PbrtExporter(const aiScene *pScene, IOSystem *pIOSystem, + const std::string &path, const std::string &file); /// Destructor virtual ~PbrtExporter(); diff --git a/code/PostProcessing/ArmaturePopulate.h b/code/PostProcessing/ArmaturePopulate.h index 08a72bd66..ded8b4886 100644 --- a/code/PostProcessing/ArmaturePopulate.h +++ b/code/PostProcessing/ArmaturePopulate.h @@ -97,7 +97,7 @@ public: static void BuildBoneList(aiNode *current_node, const aiNode *root_node, const aiScene *scene, - std::vector &bones); + std::vector &bones); static void BuildBoneStack(aiNode *current_node, const aiNode *root_node, const aiScene *scene, diff --git a/code/PostProcessing/DropFaceNormalsProcess.cpp b/code/PostProcessing/DropFaceNormalsProcess.cpp index c01ac35e5..21abf9693 100644 --- a/code/PostProcessing/DropFaceNormalsProcess.cpp +++ b/code/PostProcessing/DropFaceNormalsProcess.cpp @@ -104,7 +104,7 @@ bool DropFaceNormalsProcess::DropMeshFaceNormals (aiMesh* mesh) { if (nullptr == mesh->mNormals) { return false; } - + delete[] mesh->mNormals; mesh->mNormals = nullptr; return true; diff --git a/code/PostProcessing/EmbedTexturesProcess.cpp b/code/PostProcessing/EmbedTexturesProcess.cpp index 7e435e556..d7720de98 100644 --- a/code/PostProcessing/EmbedTexturesProcess.cpp +++ b/code/PostProcessing/EmbedTexturesProcess.cpp @@ -4,7 +4,6 @@ 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, @@ -41,6 +40,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "EmbedTexturesProcess.h" +#include +#include #include #include "ProcessHelper.h" @@ -48,11 +49,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace Assimp; -EmbedTexturesProcess::EmbedTexturesProcess() -: BaseProcess() { +EmbedTexturesProcess::EmbedTexturesProcess() : + BaseProcess() { + // empty } EmbedTexturesProcess::~EmbedTexturesProcess() { + // empty } bool EmbedTexturesProcess::IsActive(unsigned int pFlags) const { @@ -62,15 +65,16 @@ bool EmbedTexturesProcess::IsActive(unsigned int pFlags) const { void EmbedTexturesProcess::SetupProperties(const Importer* pImp) { mRootPath = pImp->GetPropertyString("sourceFilePath"); mRootPath = mRootPath.substr(0, mRootPath.find_last_of("\\/") + 1u); + mIOHandler = pImp->GetIOHandler(); } void EmbedTexturesProcess::Execute(aiScene* pScene) { - if (pScene == nullptr || pScene->mRootNode == nullptr) return; + if (pScene == nullptr || pScene->mRootNode == nullptr || mIOHandler == nullptr){ + return; + } aiString path; - uint32_t embeddedTexturesCount = 0u; - for (auto matId = 0u; matId < pScene->mNumMaterials; ++matId) { auto material = pScene->mMaterials[matId]; @@ -96,32 +100,36 @@ void EmbedTexturesProcess::Execute(aiScene* pScene) { ASSIMP_LOG_INFO("EmbedTexturesProcess finished. Embedded ", embeddedTexturesCount, " textures." ); } -bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const { +bool EmbedTexturesProcess::addTexture(aiScene *pScene, const std::string &path) const { std::streampos imageSize = 0; std::string imagePath = path; // Test path directly - std::ifstream file(imagePath, std::ios::binary | std::ios::ate); - if ((imageSize = file.tellg()) == std::streampos(-1)) { + if (!mIOHandler->Exists(imagePath)) { ASSIMP_LOG_WARN("EmbedTexturesProcess: Cannot find image: ", imagePath, ". Will try to find it in root folder."); // Test path in root path imagePath = mRootPath + path; - file.open(imagePath, std::ios::binary | std::ios::ate); - if ((imageSize = file.tellg()) == std::streampos(-1)) { + if (!mIOHandler->Exists(imagePath)) { // Test path basename in root path imagePath = mRootPath + path.substr(path.find_last_of("\\/") + 1u); - file.open(imagePath, std::ios::binary | std::ios::ate); - if ((imageSize = file.tellg()) == std::streampos(-1)) { + if (!mIOHandler->Exists(imagePath)) { ASSIMP_LOG_ERROR("EmbedTexturesProcess: Unable to embed texture: ", path, "."); return false; } } } + IOStream* pFile = mIOHandler->Open(imagePath); + if (pFile == nullptr) { + ASSIMP_LOG_ERROR("EmbedTexturesProcess: Unable to embed texture: ", path, "."); + return false; + } + imageSize = pFile->FileSize(); aiTexel* imageContent = new aiTexel[ 1ul + static_cast( imageSize ) / sizeof(aiTexel)]; - file.seekg(0, std::ios::beg); - file.read(reinterpret_cast(imageContent), imageSize); + pFile->Seek(0, aiOrigin_SET); + pFile->Read(reinterpret_cast(imageContent), imageSize, 1); + mIOHandler->Close(pFile); // Enlarging the textures table unsigned int textureId = pScene->mNumTextures++; @@ -129,7 +137,7 @@ bool EmbedTexturesProcess::addTexture(aiScene* pScene, std::string path) const { pScene->mTextures = new aiTexture*[pScene->mNumTextures]; ::memmove(pScene->mTextures, oldTextures, sizeof(aiTexture*) * (pScene->mNumTextures - 1u)); delete [] oldTextures; - + // Add the new texture auto pTexture = new aiTexture; pTexture->mHeight = 0; // Means that this is still compressed diff --git a/code/PostProcessing/EmbedTexturesProcess.h b/code/PostProcessing/EmbedTexturesProcess.h index 90970937a..b33968850 100644 --- a/code/PostProcessing/EmbedTexturesProcess.h +++ b/code/PostProcessing/EmbedTexturesProcess.h @@ -48,6 +48,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. struct aiNode; +class IOSystem; + namespace Assimp { /** @@ -76,10 +78,11 @@ public: private: // Resolve the path and add the file content to the scene as a texture. - bool addTexture(aiScene* pScene, std::string path) const; + bool addTexture(aiScene *pScene, const std::string &path) const; private: std::string mRootPath; + IOSystem* mIOHandler = nullptr; }; } // namespace Assimp diff --git a/code/PostProcessing/FindDegenerates.cpp b/code/PostProcessing/FindDegenerates.cpp index eae91ff02..3809abf50 100644 --- a/code/PostProcessing/FindDegenerates.cpp +++ b/code/PostProcessing/FindDegenerates.cpp @@ -90,7 +90,7 @@ void FindDegeneratesProcess::Execute( aiScene* pScene) { if ( nullptr == pScene) { return; } - + std::unordered_map meshMap; meshMap.reserve(pScene->mNumMeshes); diff --git a/code/PostProcessing/FindInstancesProcess.cpp b/code/PostProcessing/FindInstancesProcess.cpp index ab5f52b78..d46afc201 100644 --- a/code/PostProcessing/FindInstancesProcess.cpp +++ b/code/PostProcessing/FindInstancesProcess.cpp @@ -137,7 +137,7 @@ void FindInstancesProcess::Execute( aiScene* pScene) aiMesh* inst = pScene->mMeshes[i]; hashes[i] = GetMeshHash(inst); - // Find an appropriate epsilon + // Find an appropriate epsilon // to compare position differences against float epsilon = ComputePositionEpsilon(inst); epsilon *= epsilon; diff --git a/code/PostProcessing/MakeVerboseFormat.h b/code/PostProcessing/MakeVerboseFormat.h index c1db76dca..4304a3afa 100644 --- a/code/PostProcessing/MakeVerboseFormat.h +++ b/code/PostProcessing/MakeVerboseFormat.h @@ -98,7 +98,7 @@ public: // ------------------------------------------------------------------- /** Checks whether the scene is already in verbose format. - * @param pScene The data to check. + * @param pScene The data to check. * @return true if the scene is already in verbose format. */ static bool IsVerboseFormat(const aiScene* pScene); diff --git a/code/PostProcessing/OptimizeGraph.cpp b/code/PostProcessing/OptimizeGraph.cpp index e33c2ab18..d7bcf3fec 100644 --- a/code/PostProcessing/OptimizeGraph.cpp +++ b/code/PostProcessing/OptimizeGraph.cpp @@ -170,7 +170,7 @@ void OptimizeGraphProcess::CollectNewChildren(aiNode *nd, std::list &n ++it; } if (join_master && !join.empty()) { - join_master->mName.length = ::ai_snprintf(join_master->mName.data, MAXLEN, "$MergedNode_%i", count_merged++); + join_master->mName.length = ::ai_snprintf(join_master->mName.data, MAXLEN, "$MergedNode_%u", count_merged++); unsigned int out_meshes = 0; for (std::list::const_iterator it = join.cbegin(); it != join.cend(); ++it) { diff --git a/code/PostProcessing/PretransformVertices.cpp b/code/PostProcessing/PretransformVertices.cpp index e9a3af0d2..fa95319ff 100644 --- a/code/PostProcessing/PretransformVertices.cpp +++ b/code/PostProcessing/PretransformVertices.cpp @@ -481,7 +481,7 @@ void PretransformVertices::Execute(aiScene *pScene) { pScene->mMeshes[i]->mNumBones = 0; } } else { - apcOutMeshes.reserve(pScene->mNumMaterials << 1u); + apcOutMeshes.reserve(static_cast(pScene->mNumMaterials) << 1u); std::list aiVFormats; std::vector s(pScene->mNumMeshes, 0); diff --git a/code/PostProcessing/RemoveRedundantMaterials.cpp b/code/PostProcessing/RemoveRedundantMaterials.cpp index c252f37a5..36745fb1d 100644 --- a/code/PostProcessing/RemoveRedundantMaterials.cpp +++ b/code/PostProcessing/RemoveRedundantMaterials.cpp @@ -215,7 +215,7 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene) } else { - ASSIMP_LOG_INFO("RemoveRedundantMatsProcess finished. Removed ", redundantRemoved, " redundant and ", + ASSIMP_LOG_INFO("RemoveRedundantMatsProcess finished. Removed ", redundantRemoved, " redundant and ", unreferencedRemoved, " unused materials."); } } diff --git a/code/PostProcessing/ScaleProcess.cpp b/code/PostProcessing/ScaleProcess.cpp index 0a3e29c42..63dd0443d 100644 --- a/code/PostProcessing/ScaleProcess.cpp +++ b/code/PostProcessing/ScaleProcess.cpp @@ -75,7 +75,7 @@ void ScaleProcess::SetupProperties( const Importer* pImp ) { // File scaling * Application Scaling float importerScale = pImp->GetPropertyFloat( AI_CONFIG_APP_SCALE_KEY, 1.0f ); - // apply scale to the scale + // apply scale to the scale // helps prevent bugs with backward compatibility for anyone using normal scaling. mScale *= importerScale; } @@ -84,7 +84,7 @@ void ScaleProcess::Execute( aiScene* pScene ) { if(mScale == 1.0f) { return; // nothing to scale } - + ai_assert( mScale != 0 ); ai_assert( nullptr != pScene ); ai_assert( nullptr != pScene->mRootNode ); @@ -96,7 +96,7 @@ void ScaleProcess::Execute( aiScene* pScene ) { if ( nullptr == pScene->mRootNode ) { return; } - + // Process animations and update position transform to new unit system for( unsigned int animationID = 0; animationID < pScene->mNumAnimations; animationID++ ) { @@ -105,7 +105,7 @@ void ScaleProcess::Execute( aiScene* pScene ) { for( unsigned int animationChannel = 0; animationChannel < animation->mNumChannels; animationChannel++) { aiNodeAnim* anim = animation->mChannels[animationChannel]; - + for( unsigned int posKey = 0; posKey < anim->mNumPositionKeys; posKey++) { aiVectorKey& vectorKey = anim->mPositionKeys[posKey]; @@ -116,8 +116,8 @@ void ScaleProcess::Execute( aiScene* pScene ) { for( unsigned int meshID = 0; meshID < pScene->mNumMeshes; meshID++) { - aiMesh *mesh = pScene->mMeshes[meshID]; - + aiMesh *mesh = pScene->mMeshes[meshID]; + // Reconstruct mesh vertexes to the new unit system for( unsigned int vertexID = 0; vertexID < mesh->mNumVertices; vertexID++) { @@ -129,9 +129,9 @@ void ScaleProcess::Execute( aiScene* pScene ) { // bone placement / scaling for( unsigned int boneID = 0; boneID < mesh->mNumBones; boneID++) { - // Reconstruct matrix by transform rather than by scale + // Reconstruct matrix by transform rather than by scale // This prevent scale values being changed which can - // be meaningful in some cases + // be meaningful in some cases // like when you want the modeller to see 1:1 compatibility. aiBone* bone = mesh->mBones[boneID]; @@ -139,10 +139,10 @@ void ScaleProcess::Execute( aiScene* pScene ) { aiQuaternion rotation; bone->mOffsetMatrix.Decompose( scale, rotation, pos); - + aiMatrix4x4 translation; aiMatrix4x4::Translation( pos * mScale, translation ); - + aiMatrix4x4 scaling; aiMatrix4x4::Scaling( aiVector3D(scale), scaling ); @@ -157,7 +157,7 @@ void ScaleProcess::Execute( aiScene* pScene ) { for( unsigned int animMeshID = 0; animMeshID < mesh->mNumAnimMeshes; animMeshID++) { aiAnimMesh * animMesh = mesh->mAnimMeshes[animMeshID]; - + for( unsigned int vertexID = 0; vertexID < animMesh->mNumVertices; vertexID++) { aiVector3D& vertex = animMesh->mVertices[vertexID]; @@ -169,31 +169,31 @@ void ScaleProcess::Execute( aiScene* pScene ) { traverseNodes( pScene->mRootNode ); } -void ScaleProcess::traverseNodes( aiNode *node, unsigned int nested_node_id ) { +void ScaleProcess::traverseNodes( aiNode *node, unsigned int nested_node_id ) { applyScaling( node ); for( size_t i = 0; i < node->mNumChildren; i++) { // recurse into the tree until we are done! - traverseNodes( node->mChildren[i], nested_node_id+1 ); + traverseNodes( node->mChildren[i], nested_node_id+1 ); } } void ScaleProcess::applyScaling( aiNode *currentNode ) { if ( nullptr != currentNode ) { - // Reconstruct matrix by transform rather than by scale + // Reconstruct matrix by transform rather than by scale // This prevent scale values being changed which can - // be meaningful in some cases - // like when you want the modeller to + // be meaningful in some cases + // like when you want the modeller to // see 1:1 compatibility. - + aiVector3D pos, scale; aiQuaternion rotation; currentNode->mTransformation.Decompose( scale, rotation, pos); - + aiMatrix4x4 translation; aiMatrix4x4::Translation( pos * mScale, translation ); - + aiMatrix4x4 scaling; // note: we do not use mScale here, this is on purpose. diff --git a/code/PostProcessing/ScaleProcess.h b/code/PostProcessing/ScaleProcess.h index d88490b1c..4dc7e3559 100644 --- a/code/PostProcessing/ScaleProcess.h +++ b/code/PostProcessing/ScaleProcess.h @@ -55,8 +55,8 @@ namespace Assimp { // --------------------------------------------------------------------------- /** ScaleProcess: Class to rescale the whole model. * Now rescales animations, bones, and blend shapes properly. - * Please note this will not write to 'scale' transform it will rewrite mesh - * and matrixes so that your scale values + * Please note this will not write to 'scale' transform it will rewrite mesh + * and matrixes so that your scale values * from your model package are preserved, so this is completely intentional * bugs should be reported as soon as they are found. */ diff --git a/code/PostProcessing/SortByPTypeProcess.cpp b/code/PostProcessing/SortByPTypeProcess.cpp index 20ab63249..3787be51e 100644 --- a/code/PostProcessing/SortByPTypeProcess.cpp +++ b/code/PostProcessing/SortByPTypeProcess.cpp @@ -127,7 +127,7 @@ void SortByPTypeProcess::Execute(aiScene *pScene) { unsigned int aiNumMeshesPerPType[4] = { 0, 0, 0, 0 }; std::vector outMeshes; - outMeshes.reserve(pScene->mNumMeshes << 1u); + outMeshes.reserve(static_cast(pScene->mNumMeshes) << 1u); bool bAnyChanges = false; diff --git a/code/PostProcessing/SplitByBoneCountProcess.cpp b/code/PostProcessing/SplitByBoneCountProcess.cpp index 2613d8561..ed5b9411e 100644 --- a/code/PostProcessing/SplitByBoneCountProcess.cpp +++ b/code/PostProcessing/SplitByBoneCountProcess.cpp @@ -209,7 +209,7 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vector newBonesAtCurrentFace; - + const aiFace& face = pMesh->mFaces[a]; // check every vertex if its bones would still fit into the current submesh for( unsigned int b = 0; b < face.mNumIndices; ++b ) @@ -221,7 +221,7 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vectormNumAnimMeshes > 0) { newMesh->mNumAnimMeshes = pMesh->mNumAnimMeshes; newMesh->mAnimMeshes = new aiAnimMesh*[newMesh->mNumAnimMeshes]; - + for (unsigned int morphIdx = 0; morphIdx < newMesh->mNumAnimMeshes; ++morphIdx) { aiAnimMesh* origTarget = pMesh->mAnimMeshes[morphIdx]; aiAnimMesh* newTarget = new aiAnimMesh; @@ -421,16 +421,16 @@ void SplitByBoneCountProcess::SplitMesh( const aiMesh* pMesh, std::vectormNumVertices = numSubMeshVertices; newTarget->mVertices = new aiVector3D[numSubMeshVertices]; newMesh->mAnimMeshes[morphIdx] = newTarget; - + if (origTarget->HasNormals()) { newTarget->mNormals = new aiVector3D[numSubMeshVertices]; } - + if (origTarget->HasTangentsAndBitangents()) { newTarget->mTangents = new aiVector3D[numSubMeshVertices]; newTarget->mBitangents = new aiVector3D[numSubMeshVertices]; } - + for( unsigned int vi = 0; vi < numSubMeshVertices; ++vi) { // find the source vertex for it in the source mesh unsigned int previousIndex = previousVertexIndices[vi]; diff --git a/code/PostProcessing/SplitLargeMeshes.cpp b/code/PostProcessing/SplitLargeMeshes.cpp index ed680cf7f..cb614edaa 100644 --- a/code/PostProcessing/SplitLargeMeshes.cpp +++ b/code/PostProcessing/SplitLargeMeshes.cpp @@ -40,7 +40,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ -/** +/** * @file Implementation of the SplitLargeMeshes postprocessing step */ @@ -353,7 +353,7 @@ void SplitLargeMeshesProcess_Vertex::Execute( aiScene* pScene) { std::vector > avList; - //Check for point cloud first, + //Check for point cloud first, //Do not process point cloud, splitMesh works only with faces data for (unsigned int a = 0; a < pScene->mNumMeshes; a++) { if ( pScene->mMeshes[a]->mPrimitiveTypes == aiPrimitiveType_POINT ) { diff --git a/code/PostProcessing/TextureTransform.cpp b/code/PostProcessing/TextureTransform.cpp index 681b047c0..74b00d92c 100644 --- a/code/PostProcessing/TextureTransform.cpp +++ b/code/PostProcessing/TextureTransform.cpp @@ -448,7 +448,7 @@ void TextureTransformStep::Execute( aiScene* pScene) if (size > AI_MAX_NUMBER_OF_TEXTURECOORDS) { if (!DefaultLogger::isNullLogger()) { - ASSIMP_LOG_ERROR(static_cast(trafo.size()), " UV channels required but just ", + ASSIMP_LOG_ERROR(static_cast(trafo.size()), " UV channels required but just ", AI_MAX_NUMBER_OF_TEXTURECOORDS, " available"); } size = AI_MAX_NUMBER_OF_TEXTURECOORDS; diff --git a/code/PostProcessing/TriangulateProcess.cpp b/code/PostProcessing/TriangulateProcess.cpp index 0f71320b8..b7928ee59 100644 --- a/code/PostProcessing/TriangulateProcess.cpp +++ b/code/PostProcessing/TriangulateProcess.cpp @@ -86,11 +86,11 @@ namespace { /** * @brief Encode the current triangle, and make sure it is recognized as a triangle. - * + * * This method will rotate indices in tri if needed in order to avoid tri to be considered * part of the previous ngon. This method is to be used whenever you want to emit a real triangle, * and make sure it is seen as a triangle. - * + * * @param tri Triangle to encode. */ void ngonEncodeTriangle(aiFace * tri) { @@ -108,10 +108,10 @@ namespace { /** * @brief Encode a quad (2 triangles) in ngon encoding, and make sure they are seen as a single ngon. - * + * * @param tri1 First quad triangle * @param tri2 Second quad triangle - * + * * @pre Triangles must be properly fanned from the most appropriate vertex. */ void ngonEncodeQuad(aiFace *tri1, aiFace *tri2) { @@ -140,7 +140,7 @@ namespace { /** * @brief Check whether this triangle would be considered part of the lastly emitted ngon or not. - * + * * @param tri Current triangle. * @return true If used as is, this triangle will be part of last ngon. * @return false If used as is, this triangle is not considered part of the last ngon. @@ -512,7 +512,7 @@ bool TriangulateProcess::TriangulateMesh( aiMesh* pMesh) num = 0; break; - /*curOut -= (max-num); // undo all previous work + /*curOut -= (max-num); // undo all previous work for (tmp = 0; tmp < max-2; ++tmp) { aiFace& nface = *curOut++; diff --git a/contrib/draco/.ruby-version b/contrib/draco/.ruby-version deleted file mode 100644 index 276cbf9e2..000000000 --- a/contrib/draco/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -2.3.0 diff --git a/contrib/draco/.travis.yml b/contrib/draco/.travis.yml deleted file mode 100644 index e9ef7123f..000000000 --- a/contrib/draco/.travis.yml +++ /dev/null @@ -1,31 +0,0 @@ -cache: ccache -language: cpp -matrix: - include: - - os: linux - dist: xenial - compiler: clang - - os: linux - dist: xenial - compiler: gcc - - os: osx - compiler: clang - -addons: - apt: - packages: - - cmake - -script: - # Output version info for compilers, cmake, and make - - ${CC} -v - - ${CXX} -v - - cmake --version - - make --version - # Clone googletest - - pushd .. && git clone https://github.com/google/googletest.git && popd - # Configure and build - - mkdir _travis_build && cd _travis_build - - cmake -G "Unix Makefiles" -DENABLE_TESTS=ON .. - - make -j10 - - ./draco_tests diff --git a/contrib/draco/CMakeLists.txt b/contrib/draco/CMakeLists.txt index 3da2c664a..5526e7f60 100644 --- a/contrib/draco/CMakeLists.txt +++ b/contrib/draco/CMakeLists.txt @@ -804,7 +804,7 @@ else() draco_points_enc) # Library targets that consume the object collections. - if(MSVC OR WIN32) + if(MSVC) # In order to produce a DLL and import library the Windows tools require # that the exported symbols are part of the DLL target. The unfortunate side # effect of this is that a single configuration cannot output both the @@ -889,9 +889,6 @@ else() # For Mac, we need to build a .bundle for the unity plugin. if(APPLE) set_target_properties(dracodec_unity PROPERTIES BUNDLE true) - elseif(NOT unity_decoder_lib_type STREQUAL STATIC) - set_target_properties(dracodec_unity - PROPERTIES SOVERSION ${DRACO_SOVERSION}) endif() endif() @@ -916,9 +913,6 @@ else() # For Mac, we need to build a .bundle for the plugin. if(APPLE) set_target_properties(draco_maya_wrapper PROPERTIES BUNDLE true) - else() - set_target_properties(draco_maya_wrapper - PROPERTIES SOVERSION ${DRACO_SOVERSION}) endif() endif() diff --git a/contrib/draco/README.md b/contrib/draco/README.md index add66edcb..0d980b387 100644 --- a/contrib/draco/README.md +++ b/contrib/draco/README.md @@ -2,16 +2,16 @@

    -![Build Status: master](https://travis-ci.org/google/draco.svg?branch=master) +[![Build Status](https://github.com/google/draco/workflows/Build/badge.svg)](https://github.com/google/draco/actions?query=workflow%3ABuild) News ======= ### Version 1.4.1 release -* Using the versioned gstatic.com WASM and Javascript decoders is now +* Using the versioned www.gstatic.com WASM and Javascript decoders is now recommended. To use v1.4.1, use this URL: * https://www.gstatic.com/draco/versioned/decoders/1.4.1/* * Replace the * with the files to load. E.g. - * https://gstatic.com/draco/versioned/decoders/1.4.1/draco_decoder.js + * https://www.gstatic.com/draco/versioned/decoders/1.4.1/draco_decoder.js * This works with the v1.3.6 and v1.4.0 releases, and will work with future Draco releases. * Bug fixes diff --git a/contrib/draco/cmake/draco_build_definitions.cmake b/contrib/draco/cmake/draco_build_definitions.cmake index c1ada6206..f7354c15f 100644 --- a/contrib/draco/cmake/draco_build_definitions.cmake +++ b/contrib/draco/cmake/draco_build_definitions.cmake @@ -6,7 +6,7 @@ set(DRACO_CMAKE_DRACO_BUILD_DEFINITIONS_CMAKE_ 1) # Utility for controlling the main draco library dependency. This changes in # shared builds, and when an optional target requires a shared library build. macro(set_draco_target) - if(MSVC OR WIN32) + if(MSVC) set(draco_dependency draco) set(draco_plugin_dependency ${draco_dependency}) else() @@ -63,6 +63,11 @@ macro(draco_set_build_definitions) if(BUILD_SHARED_LIBS) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS TRUE) endif() + else() + if(${CMAKE_SIZEOF_VOID_P} EQUAL 8) + # Ensure 64-bit platforms can support large files. + list(APPEND draco_defines "_LARGEFILE_SOURCE" "_FILE_OFFSET_BITS=64") + endif() endif() if(ANDROID) @@ -114,4 +119,6 @@ macro(draco_set_build_definitions) draco_check_emscripten_environment() draco_get_required_emscripten_flags(FLAG_LIST_VAR draco_base_cxx_flags) endif() + + draco_configure_sanitizer() endmacro() diff --git a/contrib/draco/cmake/draco_features.cmake b/contrib/draco/cmake/draco_features.cmake deleted file mode 100644 index be444bf24..000000000 --- a/contrib/draco/cmake/draco_features.cmake +++ /dev/null @@ -1,63 +0,0 @@ -if(DRACO_CMAKE_DRACO_FEATURES_CMAKE_) - return() -endif() -set(DRACO_CMAKE_DRACO_FEATURES_CMAKE_ 1) - -set(draco_features_file_name "${draco_build_dir}/draco/draco_features.h") -set(draco_features_list) - -# Macro that handles tracking of Draco preprocessor symbols for the purpose of -# producing draco_features.h. -# -# draco_enable_feature(FEATURE [TARGETS ]) FEATURE -# is required. It should be a Draco preprocessor symbol. TARGETS is optional. It -# can be one or more draco targets. -# -# When the TARGETS argument is not present the preproc symbol is added to -# draco_features.h. When it is draco_features.h is unchanged, and -# target_compile_options() is called for each target specified. -macro(draco_enable_feature) - set(def_flags) - set(def_single_arg_opts FEATURE) - set(def_multi_arg_opts TARGETS) - cmake_parse_arguments(DEF "${def_flags}" "${def_single_arg_opts}" - "${def_multi_arg_opts}" ${ARGN}) - if("${DEF_FEATURE}" STREQUAL "") - message(FATAL_ERROR "Empty FEATURE passed to draco_enable_feature().") - endif() - - # Do nothing/return early if $DEF_FEATURE is already in the list. - list(FIND draco_features_list ${DEF_FEATURE} df_index) - if(NOT df_index EQUAL -1) - return() - endif() - - list(LENGTH DEF_TARGETS df_targets_list_length) - if(${df_targets_list_length} EQUAL 0) - list(APPEND draco_features_list ${DEF_FEATURE}) - else() - foreach(target ${DEF_TARGETS}) - target_compile_definitions(${target} PRIVATE ${DEF_FEATURE}) - endforeach() - endif() -endmacro() - -# Function for generating draco_features.h. -function(draco_generate_features_h) - file(WRITE "${draco_features_file_name}.new" - "// GENERATED FILE -- DO NOT EDIT\n\n" "#ifndef DRACO_FEATURES_H_\n" - "#define DRACO_FEATURES_H_\n\n") - - foreach(feature ${draco_features_list}) - file(APPEND "${draco_features_file_name}.new" "#define ${feature}\n") - endforeach() - - file(APPEND "${draco_features_file_name}.new" - "\n#endif // DRACO_FEATURES_H_") - - # Will replace ${draco_features_file_name} only if the file content has - # changed. This prevents forced Draco rebuilds after CMake runs. - configure_file("${draco_features_file_name}.new" - "${draco_features_file_name}") - file(REMOVE "${draco_features_file_name}.new") -endfunction() diff --git a/contrib/draco/cmake/draco_flags.cmake b/contrib/draco/cmake/draco_flags.cmake index cb9d489e6..0397859a4 100644 --- a/contrib/draco/cmake/draco_flags.cmake +++ b/contrib/draco/cmake/draco_flags.cmake @@ -80,6 +80,12 @@ macro(draco_test_cxx_flag) # Run the actual compile test. unset(draco_all_cxx_flags_pass CACHE) message("--- Running combined CXX flags test, flags: ${all_cxx_flags}") + + # check_cxx_compiler_flag() requires that the flags are a string. When flags + # are passed as a list it will remove the list separators, and attempt to run + # a compile command using list entries concatenated together as a single + # argument. Avoid the problem by forcing the argument to be a string. + draco_set_and_stringify(SOURCE_VARS all_cxx_flags DEST all_cxx_flags) check_cxx_compiler_flag("${all_cxx_flags}" draco_all_cxx_flags_pass) if(cxx_test_FLAG_REQUIRED AND NOT draco_all_cxx_flags_pass) @@ -194,6 +200,9 @@ macro(draco_test_exe_linker_flag) else() unset(CMAKE_EXE_LINKER_FLAGS) endif() + + list(APPEND DRACO_EXE_LINKER_FLAGS ${${link_FLAG_LIST_VAR_NAME}}) + list(REMOVE_DUPLICATES DRACO_EXE_LINKER_FLAGS) endmacro() # Runs the draco compiler tests. This macro builds up the list of list var(s) diff --git a/contrib/draco/cmake/draco_install.cmake b/contrib/draco/cmake/draco_install.cmake index 5c63ecb4a..09bfb591d 100644 --- a/contrib/draco/cmake/draco_install.cmake +++ b/contrib/draco/cmake/draco_install.cmake @@ -55,7 +55,7 @@ macro(draco_setup_install_target) install(TARGETS draco_encoder DESTINATION "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_BINDIR}") - if(WIN32) + if(MSVC) install(TARGETS draco DESTINATION "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") else() diff --git a/contrib/draco/cmake/draco_sanitizer.cmake b/contrib/draco/cmake/draco_sanitizer.cmake index ca8e23176..d2e41a6cb 100644 --- a/contrib/draco/cmake/draco_sanitizer.cmake +++ b/contrib/draco/cmake/draco_sanitizer.cmake @@ -5,28 +5,28 @@ set(DRACO_CMAKE_DRACO_SANITIZER_CMAKE_ 1) # Handles the details of enabling sanitizers. macro(draco_configure_sanitizer) - if(DRACO_SANITIZE AND NOT MSVC) + if(DRACO_SANITIZE AND NOT EMSCRIPTEN AND NOT MSVC) if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") if(DRACO_SANITIZE MATCHES "cfi") - list(APPEND DRACO_CXX_FLAGS "-flto" "-fno-sanitize-trap=cfi") - list(APPEND DRACO_EXE_LINKER_FLAGS "-flto" "-fno-sanitize-trap=cfi" + list(APPEND SAN_CXX_FLAGS "-flto" "-fno-sanitize-trap=cfi") + list(APPEND SAN_LINKER_FLAGS "-flto" "-fno-sanitize-trap=cfi" "-fuse-ld=gold") endif() if(${CMAKE_SIZEOF_VOID_P} EQUAL 4 AND DRACO_SANITIZE MATCHES "integer|undefined") - list(APPEND DRACO_EXE_LINKER_FLAGS "--rtlib=compiler-rt" "-lgcc_s") + list(APPEND SAN_LINKER_FLAGS "--rtlib=compiler-rt" "-lgcc_s") endif() endif() - list(APPEND DRACO_CXX_FLAGS "-fsanitize=${DRACO_SANITIZE}") - list(APPEND DRACO_EXE_LINKER_FLAGS "-fsanitize=${DRACO_SANITIZE}") + list(APPEND SAN_CXX_FLAGS "-fsanitize=${DRACO_SANITIZE}") + list(APPEND SAN_LINKER_FLAGS "-fsanitize=${DRACO_SANITIZE}") # Make sanitizer callstacks accurate. - list(APPEND DRACO_CXX_FLAGS "-fno-omit-frame-pointer" - "-fno-optimize-sibling-calls") + list(APPEND SAN_CXX_FLAGS "-fno-omit-frame-pointer") + list(APPEND SAN_CXX_FLAGS "-fno-optimize-sibling-calls") - draco_test_cxx_flag(FLAG_LIST_VAR_NAMES DRACO_CXX_FLAGS FLAG_REQUIRED) - draco_test_exe_linker_flag(FLAG_LIST_VAR_NAME DRACO_EXE_LINKER_FLAGS) + draco_test_cxx_flag(FLAG_LIST_VAR_NAMES SAN_CXX_FLAGS FLAG_REQUIRED) + draco_test_exe_linker_flag(FLAG_LIST_VAR_NAME SAN_LINKER_FLAGS) endif() endmacro() diff --git a/contrib/draco/cmake/draco_targets.cmake b/contrib/draco/cmake/draco_targets.cmake index 6dfa6a0c4..0456c4d7b 100644 --- a/contrib/draco/cmake/draco_targets.cmake +++ b/contrib/draco/cmake/draco_targets.cmake @@ -87,6 +87,7 @@ macro(draco_add_executable) endif() add_executable(${exe_NAME} ${exe_SOURCES}) + set_target_properties(${exe_NAME} PROPERTIES VERSION ${DRACO_VERSION}) if(exe_OUTPUT_NAME) set_target_properties(${exe_NAME} PROPERTIES OUTPUT_NAME ${exe_OUTPUT_NAME}) @@ -109,10 +110,11 @@ macro(draco_add_executable) if(exe_LINK_FLAGS OR DRACO_EXE_LINKER_FLAGS) if(${CMAKE_VERSION} VERSION_LESS "3.13") - set(link_flags ${exe_LINK_FLAGS} ${DRACO_EXE_LINKER_FLAGS}) + list(APPEND exe_LINK_FLAGS "${DRACO_EXE_LINKER_FLAGS}") + # LINK_FLAGS is managed as a string. + draco_set_and_stringify(SOURCE "${exe_LINK_FLAGS}" DEST exe_LINK_FLAGS) set_target_properties(${exe_NAME} - PROPERTIES LINK_FLAGS ${exe_LINK_FLAGS} - ${DRACO_EXE_LINKER_FLAGS}) + PROPERTIES LINK_FLAGS "${exe_LINK_FLAGS}") else() target_link_options(${exe_NAME} PRIVATE ${exe_LINK_FLAGS} ${DRACO_EXE_LINKER_FLAGS}) @@ -130,7 +132,7 @@ macro(draco_add_executable) endif() if(BUILD_SHARED_LIBS AND (MSVC OR WIN32)) - target_compile_definitions(${lib_NAME} PRIVATE "DRACO_BUILDING_DLL=0") + target_compile_definitions(${exe_NAME} PRIVATE "DRACO_BUILDING_DLL=0") endif() if(exe_LIB_DEPS) @@ -163,8 +165,8 @@ endmacro() # cmake-format: off # - OUTPUT_NAME: Override output file basename. Target basename defaults to # NAME. OUTPUT_NAME is ignored when BUILD_SHARED_LIBS is enabled and CMake -# is generating a build for which MSVC or WIN32 are true. This is to avoid -# output basename collisions with DLL import libraries. +# is generating a build for which MSVC is true. This is to avoid output +# basename collisions with DLL import libraries. # - TEST: Flag. Presence means treat library as a test. # - DEFINES: List of preprocessor macro definitions. # - INCLUDES: list of include directories for the target. @@ -259,7 +261,7 @@ macro(draco_add_library) endif() if(lib_OUTPUT_NAME) - if(NOT (BUILD_SHARED_LIBS AND (MSVC OR WIN32))) + if(NOT (BUILD_SHARED_LIBS AND MSVC)) set_target_properties(${lib_NAME} PROPERTIES OUTPUT_NAME ${lib_OUTPUT_NAME}) endif() @@ -318,8 +320,12 @@ macro(draco_add_library) set_target_properties(${lib_NAME} PROPERTIES PREFIX "") endif() - if(lib_TYPE STREQUAL SHARED AND NOT MSVC) - set_target_properties(${lib_NAME} PROPERTIES SOVERSION ${DRACO_SOVERSION}) + # VERSION and SOVERSION as necessary + if(NOT lib_TYPE STREQUAL STATIC AND NOT lib_TYPE STREQUAL MODULE) + set_target_properties(${lib_NAME} PROPERTIES VERSION ${DRACO_VERSION}) + if(NOT MSVC) + set_target_properties(${lib_NAME} PROPERTIES SOVERSION ${DRACO_SOVERSION}) + endif() endif() if(BUILD_SHARED_LIBS AND (MSVC OR WIN32)) diff --git a/contrib/draco/src/draco/core/cycle_timer.cc b/contrib/draco/src/draco/core/cycle_timer.cc index 94b4b28b2..58df4df77 100644 --- a/contrib/draco/src/draco/core/cycle_timer.cc +++ b/contrib/draco/src/draco/core/cycle_timer.cc @@ -17,31 +17,31 @@ namespace draco { void DracoTimer::Start() { #ifdef _WIN32 - QueryPerformanceCounter(&tv_start); + QueryPerformanceCounter(&tv_start_); #else - gettimeofday(&tv_start, nullptr); + gettimeofday(&tv_start_, nullptr); #endif } void DracoTimer::Stop() { #ifdef _WIN32 - QueryPerformanceCounter(&tv_end); + QueryPerformanceCounter(&tv_end_); #else - gettimeofday(&tv_end, nullptr); + gettimeofday(&tv_end_, nullptr); #endif } int64_t DracoTimer::GetInMs() { #ifdef _WIN32 LARGE_INTEGER elapsed = {0}; - elapsed.QuadPart = tv_end.QuadPart - tv_start.QuadPart; + elapsed.QuadPart = tv_end_.QuadPart - tv_start_.QuadPart; LARGE_INTEGER frequency = {0}; QueryPerformanceFrequency(&frequency); return elapsed.QuadPart * 1000 / frequency.QuadPart; #else - const int64_t seconds = (tv_end.tv_sec - tv_start.tv_sec) * 1000; - const int64_t milliseconds = (tv_end.tv_usec - tv_start.tv_usec) / 1000; + const int64_t seconds = (tv_end_.tv_sec - tv_start_.tv_sec) * 1000; + const int64_t milliseconds = (tv_end_.tv_usec - tv_start_.tv_usec) / 1000; return seconds + milliseconds; #endif } diff --git a/contrib/draco/src/draco/core/cycle_timer.h b/contrib/draco/src/draco/core/cycle_timer.h index 172f1c2e9..f480cc9d3 100644 --- a/contrib/draco/src/draco/core/cycle_timer.h +++ b/contrib/draco/src/draco/core/cycle_timer.h @@ -20,9 +20,10 @@ #define WIN32_LEAN_AND_MEAN #endif #include -typedef LARGE_INTEGER timeval; +typedef LARGE_INTEGER DracoTimeVal; #else #include +typedef timeval DracoTimeVal; #endif #include @@ -39,8 +40,8 @@ class DracoTimer { int64_t GetInMs(); private: - timeval tv_start; - timeval tv_end; + DracoTimeVal tv_start_; + DracoTimeVal tv_end_; }; typedef DracoTimer CycleTimer; diff --git a/contrib/draco/src/draco/io/parser_utils.cc b/contrib/draco/src/draco/io/parser_utils.cc index 4f95f6f84..12afacff6 100644 --- a/contrib/draco/src/draco/io/parser_utils.cc +++ b/contrib/draco/src/draco/io/parser_utils.cc @@ -18,6 +18,7 @@ #include #include #include +#include namespace draco { namespace parser { @@ -252,7 +253,7 @@ DecoderBuffer ParseLineIntoDecoderBuffer(DecoderBuffer *buffer) { std::string ToLower(const std::string &str) { std::string out; - std::transform(str.begin(), str.end(), std::back_inserter(out), [](unsigned char c){return tolower(c);}); + std::transform(str.begin(), str.end(), std::back_inserter(out), tolower); return out; } diff --git a/contrib/draco/src/draco/io/ply_reader.cc b/contrib/draco/src/draco/io/ply_reader.cc index cb32df225..ea7f2689a 100644 --- a/contrib/draco/src/draco/io/ply_reader.cc +++ b/contrib/draco/src/draco/io/ply_reader.cc @@ -268,14 +268,14 @@ std::vector PlyReader::SplitWords(const std::string &line) { while ((end = line.find_first_of(" \t\n\v\f\r", start)) != std::string::npos) { const std::string word(line.substr(start, end - start)); - if (!std::all_of(word.begin(), word.end(), [](unsigned char c){return isspace(c);})) { + if (!std::all_of(word.begin(), word.end(), isspace)) { output.push_back(word); } start = end + 1; } const std::string last_word(line.substr(start)); - if (!std::all_of(last_word.begin(), last_word.end(), [](unsigned char c){return isspace(c);})) { + if (!std::all_of(last_word.begin(), last_word.end(), isspace)) { output.push_back(last_word); } return output; diff --git a/contrib/draco/src/draco/io/stdio_file_reader.cc b/contrib/draco/src/draco/io/stdio_file_reader.cc index 560c3e9e8..a99c96f8f 100644 --- a/contrib/draco/src/draco/io/stdio_file_reader.cc +++ b/contrib/draco/src/draco/io/stdio_file_reader.cc @@ -87,7 +87,14 @@ size_t StdioFileReader::GetFileSize() { return false; } +#if _FILE_OFFSET_BITS == 64 + const size_t file_size = static_cast(ftello(file_)); +#elif defined _WIN64 + const size_t file_size = static_cast(_ftelli64(file_)); +#else const size_t file_size = static_cast(ftell(file_)); +#endif + rewind(file_); return file_size; diff --git a/contrib/openddlparser/code/OpenDDLExport.cpp b/contrib/openddlparser/code/OpenDDLExport.cpp index 6d6f2458d..d235b553b 100644 --- a/contrib/openddlparser/code/OpenDDLExport.cpp +++ b/contrib/openddlparser/code/OpenDDLExport.cpp @@ -134,10 +134,9 @@ bool OpenDDLExport::writeToStream(const std::string &statement) { } bool OpenDDLExport::writeNode(DDLNode *node, std::string &statement) { - bool success(true); writeNodeHeader(node, statement); if (node->hasProperties()) { - success |= writeProperties(node, statement); + writeProperties(node, statement); } writeLineEnd(statement); diff --git a/contrib/openddlparser/code/OpenDDLParser.cpp b/contrib/openddlparser/code/OpenDDLParser.cpp index 6a9f802ec..0c9e0bd98 100644 --- a/contrib/openddlparser/code/OpenDDLParser.cpp +++ b/contrib/openddlparser/code/OpenDDLParser.cpp @@ -132,6 +132,24 @@ OpenDDLParser::~OpenDDLParser() { clear(); } +void OpenDDLParser::logToStream(FILE *f, LogSeverity severity, const std::string &message) { + if (f) { + const char *tag = "none"; + switch (severity) { + case ddl_debug_msg: tag = "debug"; break; + case ddl_info_msg: tag = "info"; break; + case ddl_warn_msg: tag = "warn"; break; + case ddl_error_msg: tag = "error"; break; + } + fprintf(f, "OpenDDLParser: (%5s) %s\n", tag, message.c_str()); + } +} + +OpenDDLParser::logCallback OpenDDLParser::StdLogCallback (FILE *destination) { + using namespace std::placeholders; + return std::bind(logToStream, destination ? destination : stderr, _1, _2); +} + void OpenDDLParser::setLogCallback(logCallback callback) { // install user-specific log callback; null = no log callback m_logCallback = callback; @@ -171,12 +189,8 @@ size_t OpenDDLParser::getBufferSize() const { void OpenDDLParser::clear() { m_buffer.resize(0); - if (nullptr != m_context) { - delete m_context; - m_context = nullptr; - } - - // DDLNode::releaseNodes(); + delete m_context; + m_context = nullptr; } bool OpenDDLParser::validate() { @@ -506,7 +520,9 @@ char *OpenDDLParser::parseName(char *in, char *end, Name **name) { in = parseIdentifier(in, end, &id); if (id) { currentName = new Name(ntype, id); - *name = currentName; + if (currentName) { + *name = currentName; + } } return in; diff --git a/contrib/openddlparser/code/Value.cpp b/contrib/openddlparser/code/Value.cpp index 708a6878f..5a8aa39be 100644 --- a/contrib/openddlparser/code/Value.cpp +++ b/contrib/openddlparser/code/Value.cpp @@ -113,13 +113,14 @@ Value::~Value() { if (m_data != nullptr) { if (m_type == ValueType::ddl_ref) { Reference *tmp = (Reference *)m_data; - if (tmp != nullptr) + if (tmp != nullptr) { delete tmp; - } else + } + } else { delete[] m_data; + } } - if (m_next != nullptr) - delete m_next; + delete m_next; } void Value::setBool(bool value) { diff --git a/contrib/openddlparser/include/openddlparser/OpenDDLParser.h b/contrib/openddlparser/include/openddlparser/OpenDDLParser.h index 5794add90..3fbb4b6af 100644 --- a/contrib/openddlparser/include/openddlparser/OpenDDLParser.h +++ b/contrib/openddlparser/include/openddlparser/OpenDDLParser.h @@ -29,6 +29,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +#include BEGIN_ODDLPARSER_NS @@ -97,8 +98,8 @@ DLL_ODDLPARSER_EXPORT const char *getTypeToken(Value::ValueType type); //------------------------------------------------------------------------------------------------- class DLL_ODDLPARSER_EXPORT OpenDDLParser { public: - /// @brief The log callback function pointer. - typedef void (*logCallback)(LogSeverity severity, const std::string &msg); + /// @brief The log callback function. + typedef std::function logCallback; public: /// @brief The default class constructor. @@ -120,6 +121,11 @@ public: /// @return The current log callback. logCallback getLogCallback() const; + /// @brief A default log callback that writes to a FILE. + /// @param destination [in] Output stream. NULL will use stderr. + /// @return A callback that you can pass to setLogCallback. + static logCallback StdLogCallback(FILE *destination = nullptr); + /// @brief Assigns a new buffer to parse. /// @param buffer [in] The buffer /// @param len [in] Size of the buffer @@ -192,6 +198,9 @@ private: typedef std::vector DDLNodeStack; DDLNodeStack m_stack; Context *m_context; + + /// @brief Callback for StdLogCallback(). Not meant to be called directly. + static void logToStream (FILE *, LogSeverity, const std::string &); }; END_ODDLPARSER_NS diff --git a/contrib/poly2tri/poly2tri/sweep/sweep.cc b/contrib/poly2tri/poly2tri/sweep/sweep.cc index 23aeb6b57..8e3d794c0 100644 --- a/contrib/poly2tri/poly2tri/sweep/sweep.cc +++ b/contrib/poly2tri/poly2tri/sweep/sweep.cc @@ -129,7 +129,7 @@ void Sweep::EdgeEvent(SweepContext& tcx, Point& ep, Point& eq, Triangle* triangl EdgeEvent( tcx, ep, *p1, triangle, *p1 ); } else { // ASSIMP_CHANGE (aramis_acg) - std::runtime_error("EdgeEvent - collinear points not supported"); + throw std::runtime_error("EdgeEvent - collinear points not supported"); } return; } diff --git a/code/Pbrt/stb_image.h b/contrib/stb/stb_image.h similarity index 100% rename from code/Pbrt/stb_image.h rename to contrib/stb/stb_image.h diff --git a/contrib/stb_image/stb_image.h b/contrib/stb_image/stb_image.h deleted file mode 100644 index d9c21bc81..000000000 --- a/contrib/stb_image/stb_image.h +++ /dev/null @@ -1,7462 +0,0 @@ -/* stb_image - v2.19 - public domain image loader - http://nothings.org/stb - no warranty implied; use at your own risk - - Do this: - #define STB_IMAGE_IMPLEMENTATION - before you include this file in *one* C or C++ file to create the implementation. - - // i.e. it should look like this: - #include ... - #include ... - #include ... - #define STB_IMAGE_IMPLEMENTATION - #include "stb_image.h" - - You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. - And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free - - - QUICK NOTES: - Primarily of interest to game developers and other people who can - avoid problematic images and only need the trivial interface - - JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) - PNG 1/2/4/8/16-bit-per-channel - - TGA (not sure what subset, if a subset) - BMP non-1bpp, non-RLE - PSD (composited view only, no extra channels, 8/16 bit-per-channel) - - GIF (*comp always reports as 4-channel) - HDR (radiance rgbE format) - PIC (Softimage PIC) - PNM (PPM and PGM binary only) - - Animated GIF still needs a proper API, but here's one way to do it: - http://gist.github.com/urraka/685d9a6340b26b830d49 - - - decode from memory or through FILE (define STBI_NO_STDIO to remove code) - - decode from arbitrary I/O callbacks - - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) - - Full documentation under "DOCUMENTATION" below. - - -LICENSE - - See end of file for license information. - -RECENT REVISION HISTORY: - - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings - 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes - 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 - RGB-format JPEG; remove white matting in PSD; - allocate large structures on the stack; - correct channel count for PNG & BMP - 2.10 (2016-01-22) avoid warning introduced in 2.09 - 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED - - See end of file for full revision history. - - - ============================ Contributors ========================= - - Image formats Extensions, features - Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) - Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) - Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) - Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) - Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) - Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) - Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) - github:urraka (animated gif) Junggon Kim (PNM comments) - Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) - socks-the-fox (16-bit PNG) - Jeremy Sawicki (handle all ImageNet JPGs) - Optimizations & bugfixes Mikhail Morozov (1-bit BMP) - Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) - Arseny Kapoulkine - John-Mark Allen - - Bug & warning fixes - Marc LeBlanc David Woo Guillaume George Martins Mozeiko - Christpher Lloyd Jerry Jansson Joseph Thomson Phil Jordan - Dave Moore Roy Eltham Hayaki Saito Nathan Reed - Won Chun Luke Graham Johan Duparc Nick Verigakis - the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh - Janez Zemva John Bartholomew Michal Cichon github:romigrou - Jonathan Blow Ken Hamada Tero Hanninen github:svdijk - Laurent Gomila Cort Stratton Sergio Gonzalez github:snagar - Aruelien Pocheville Thibault Reuille Cass Everitt github:Zelex - Ryamond Barbiero Paul Du Bois Engin Manap github:grim210 - Aldo Culquicondor Philipp Wiesemann Dale Weiler github:sammyhw - Oriol Ferrer Mesia Josh Tobin Matthew Gregan github:phprus - Julian Raschke Gregory Mullen Baldur Karlsson github:poppolopoppo - Christian Floisand Kevin Schmidt github:darealshinji - Blazej Dariusz Roszkowski github:Michaelangel007 -*/ - -#ifndef STBI_INCLUDE_STB_IMAGE_H -#define STBI_INCLUDE_STB_IMAGE_H - -// DOCUMENTATION -// -// Limitations: -// - no 12-bit-per-channel JPEG -// - no JPEGs with arithmetic coding -// - GIF always returns *comp=4 -// -// Basic usage (see HDR discussion below for HDR usage): -// int x,y,n; -// unsigned char *data = stbi_load(filename, &x, &y, &n, 0); -// // ... process data if not NULL ... -// // ... x = width, y = height, n = # 8-bit components per pixel ... -// // ... replace '0' with '1'..'4' to force that many components per pixel -// // ... but 'n' will always be the number that it would have been if you said 0 -// stbi_image_free(data) -// -// Standard parameters: -// int *x -- outputs image width in pixels -// int *y -- outputs image height in pixels -// int *channels_in_file -- outputs # of image components in image file -// int desired_channels -- if non-zero, # of image components requested in result -// -// The return value from an image loader is an 'unsigned char *' which points -// to the pixel data, or NULL on an allocation failure or if the image is -// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels, -// with each pixel consisting of N interleaved 8-bit components; the first -// pixel pointed to is top-left-most in the image. There is no padding between -// image scanlines or between pixels, regardless of format. The number of -// components N is 'desired_channels' if desired_channels is non-zero, or -// *channels_in_file otherwise. If desired_channels is non-zero, -// *channels_in_file has the number of components that _would_ have been -// output otherwise. E.g. if you set desired_channels to 4, you will always -// get RGBA output, but you can check *channels_in_file to see if it's trivially -// opaque because e.g. there were only 3 channels in the source image. -// -// An output image with N components has the following components interleaved -// in this order in each pixel: -// -// N=#comp components -// 1 grey -// 2 grey, alpha -// 3 red, green, blue -// 4 red, green, blue, alpha -// -// If image loading fails for any reason, the return value will be NULL, -// and *x, *y, *channels_in_file will be unchanged. The function -// stbi_failure_reason() can be queried for an extremely brief, end-user -// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS -// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly -// more user-friendly ones. -// -// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized. -// -// =========================================================================== -// -// Philosophy -// -// stb libraries are designed with the following priorities: -// -// 1. easy to use -// 2. easy to maintain -// 3. good performance -// -// Sometimes I let "good performance" creep up in priority over "easy to maintain", -// and for best performance I may provide less-easy-to-use APIs that give higher -// performance, in addition to the easy to use ones. Nevertheless, it's important -// to keep in mind that from the standpoint of you, a client of this library, -// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all. -// -// Some secondary priorities arise directly from the first two, some of which -// make more explicit reasons why performance can't be emphasized. -// -// - Portable ("ease of use") -// - Small source code footprint ("easy to maintain") -// - No dependencies ("ease of use") -// -// =========================================================================== -// -// I/O callbacks -// -// I/O callbacks allow you to read from arbitrary sources, like packaged -// files or some other source. Data read from callbacks are processed -// through a small internal buffer (currently 128 bytes) to try to reduce -// overhead. -// -// The three functions you must define are "read" (reads some bytes of data), -// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end). -// -// =========================================================================== -// -// SIMD support -// -// The JPEG decoder will try to automatically use SIMD kernels on x86 when -// supported by the compiler. For ARM Neon support, you must explicitly -// request it. -// -// (The old do-it-yourself SIMD API is no longer supported in the current -// code.) -// -// On x86, SSE2 will automatically be used when available based on a run-time -// test; if not, the generic C versions are used as a fall-back. On ARM targets, -// the typical path is to have separate builds for NEON and non-NEON devices -// (at least this is true for iOS and Android). Therefore, the NEON support is -// toggled by a build flag: define STBI_NEON to get NEON loops. -// -// If for some reason you do not want to use any of SIMD code, or if -// you have issues compiling it, you can disable it entirely by -// defining STBI_NO_SIMD. -// -// =========================================================================== -// -// HDR image support (disable by defining STBI_NO_HDR) -// -// stb_image now supports loading HDR images in general, and currently -// the Radiance .HDR file format, although the support is provided -// generically. You can still load any file through the existing interface; -// if you attempt to load an HDR file, it will be automatically remapped to -// LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1; -// both of these constants can be reconfigured through this interface: -// -// stbi_hdr_to_ldr_gamma(2.2f); -// stbi_hdr_to_ldr_scale(1.0f); -// -// (note, do not use _inverse_ constants; stbi_image will invert them -// appropriately). -// -// Additionally, there is a new, parallel interface for loading files as -// (linear) floats to preserve the full dynamic range: -// -// float *data = stbi_loadf(filename, &x, &y, &n, 0); -// -// If you load LDR images through this interface, those images will -// be promoted to floating point values, run through the inverse of -// constants corresponding to the above: -// -// stbi_ldr_to_hdr_scale(1.0f); -// stbi_ldr_to_hdr_gamma(2.2f); -// -// Finally, given a filename (or an open file or memory block--see header -// file for details) containing image data, you can query for the "most -// appropriate" interface to use (that is, whether the image is HDR or -// not), using: -// -// stbi_is_hdr(char *filename); -// -// =========================================================================== -// -// iPhone PNG support: -// -// By default we convert iphone-formatted PNGs back to RGB, even though -// they are internally encoded differently. You can disable this conversion -// by by calling stbi_convert_iphone_png_to_rgb(0), in which case -// you will always just get the native iphone "format" through (which -// is BGR stored in RGB). -// -// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per -// pixel to remove any premultiplied alpha *only* if the image file explicitly -// says there's premultiplied data (currently only happens in iPhone images, -// and only if iPhone convert-to-rgb processing is on). -// -// =========================================================================== -// -// ADDITIONAL CONFIGURATION -// -// - You can suppress implementation of any of the decoders to reduce -// your code footprint by #defining one or more of the following -// symbols before creating the implementation. -// -// STBI_NO_JPEG -// STBI_NO_PNG -// STBI_NO_BMP -// STBI_NO_PSD -// STBI_NO_TGA -// STBI_NO_GIF -// STBI_NO_HDR -// STBI_NO_PIC -// STBI_NO_PNM (.ppm and .pgm) -// -// - You can request *only* certain decoders and suppress all other ones -// (this will be more forward-compatible, as addition of new decoders -// doesn't require you to disable them explicitly): -// -// STBI_ONLY_JPEG -// STBI_ONLY_PNG -// STBI_ONLY_BMP -// STBI_ONLY_PSD -// STBI_ONLY_TGA -// STBI_ONLY_GIF -// STBI_ONLY_HDR -// STBI_ONLY_PIC -// STBI_ONLY_PNM (.ppm and .pgm) -// -// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still -// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB -// - - -#ifndef STBI_NO_STDIO -#include -#endif // STBI_NO_STDIO - -#define STBI_VERSION 1 - -enum -{ - STBI_default = 0, // only used for desired_channels - - STBI_grey = 1, - STBI_grey_alpha = 2, - STBI_rgb = 3, - STBI_rgb_alpha = 4 -}; - -typedef unsigned char stbi_uc; -typedef unsigned short stbi_us; - -#ifdef __cplusplus -extern "C" { -#endif - -#ifdef STB_IMAGE_STATIC -#define STBIDEF static -#else -#define STBIDEF extern -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// PRIMARY API - works on images of any type -// - -// -// load image by filename, open file, or memory buffer -// - -typedef struct -{ - int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read - void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative - int (*eof) (void *user); // returns nonzero if we are at end of file/data -} stbi_io_callbacks; - -//////////////////////////////////// -// -// 8-bits-per-channel interface -// - -STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels); -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -#endif - - -#ifndef STBI_NO_STDIO -STBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); -// for stbi_load_from_file, file pointer is left pointing immediately after image -#endif - -//////////////////////////////////// -// -// 16-bits-per-channel interface -// - -STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); - -#ifndef STBI_NO_STDIO -STBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); -STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); -#endif - -//////////////////////////////////// -// -// float-per-channel interface -// -#ifndef STBI_NO_LINEAR - STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); - - #ifndef STBI_NO_STDIO - STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); - STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); - #endif -#endif - -#ifndef STBI_NO_HDR - STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); - STBIDEF void stbi_hdr_to_ldr_scale(float scale); -#endif // STBI_NO_HDR - -#ifndef STBI_NO_LINEAR - STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); - STBIDEF void stbi_ldr_to_hdr_scale(float scale); -#endif // STBI_NO_LINEAR - -// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDR -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user); -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len); -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename); -STBIDEF int stbi_is_hdr_from_file(FILE *f); -#endif // STBI_NO_STDIO - - -// get a VERY brief reason for failure -// NOT THREADSAFE -STBIDEF const char *stbi_failure_reason (void); - -// free the loaded image -- this is just free() -STBIDEF void stbi_image_free (void *retval_from_stbi_load); - -// get image dimensions & components without fully decoding -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp); -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len); -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user); - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info (char const *filename, int *x, int *y, int *comp); -STBIDEF int stbi_info_from_file (FILE *f, int *x, int *y, int *comp); -STBIDEF int stbi_is_16_bit (char const *filename); -STBIDEF int stbi_is_16_bit_from_file(FILE *f); -#endif - - - -// for image formats that explicitly notate that they have premultiplied alpha, -// we just return the colors as stored in the file. set this flag to force -// unpremultiplication. results are undefined if the unpremultiply overflow. -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply); - -// indicate whether we should process iphone images back to canonical format, -// or just pass them through "as-is" -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert); - -// flip the image vertically, so the first pixel in the output array is the bottom left -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip); - -// ZLIB client - used by PNG, available for other purposes - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen); -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header); -STBIDEF char *stbi_zlib_decode_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - -STBIDEF char *stbi_zlib_decode_noheader_malloc(const char *buffer, int len, int *outlen); -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen); - - -#ifdef __cplusplus -} -#endif - -// -// -//// end header file ///////////////////////////////////////////////////// -#endif // STBI_INCLUDE_STB_IMAGE_H - -#ifdef STB_IMAGE_IMPLEMENTATION - -#if defined(STBI_ONLY_JPEG) || defined(STBI_ONLY_PNG) || defined(STBI_ONLY_BMP) \ - || defined(STBI_ONLY_TGA) || defined(STBI_ONLY_GIF) || defined(STBI_ONLY_PSD) \ - || defined(STBI_ONLY_HDR) || defined(STBI_ONLY_PIC) || defined(STBI_ONLY_PNM) \ - || defined(STBI_ONLY_ZLIB) - #ifndef STBI_ONLY_JPEG - #define STBI_NO_JPEG - #endif - #ifndef STBI_ONLY_PNG - #define STBI_NO_PNG - #endif - #ifndef STBI_ONLY_BMP - #define STBI_NO_BMP - #endif - #ifndef STBI_ONLY_PSD - #define STBI_NO_PSD - #endif - #ifndef STBI_ONLY_TGA - #define STBI_NO_TGA - #endif - #ifndef STBI_ONLY_GIF - #define STBI_NO_GIF - #endif - #ifndef STBI_ONLY_HDR - #define STBI_NO_HDR - #endif - #ifndef STBI_ONLY_PIC - #define STBI_NO_PIC - #endif - #ifndef STBI_ONLY_PNM - #define STBI_NO_PNM - #endif -#endif - -#if defined(STBI_NO_PNG) && !defined(STBI_SUPPORT_ZLIB) && !defined(STBI_NO_ZLIB) -#define STBI_NO_ZLIB -#endif - - -#include -#include // ptrdiff_t on osx -#include -#include -#include - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -#include // ldexp, pow -#endif - -#ifndef STBI_NO_STDIO -#include -#endif - -#ifndef STBI_ASSERT -#include -#define STBI_ASSERT(x) assert(x) -#endif - - -#ifndef _MSC_VER - #ifdef __cplusplus - #define stbi_inline inline - #else - #define stbi_inline - #endif -#else - #define stbi_inline __forceinline -#endif - - -#ifdef _MSC_VER -typedef unsigned short stbi__uint16; -typedef signed short stbi__int16; -typedef unsigned int stbi__uint32; -typedef signed int stbi__int32; -#else -#include -typedef uint16_t stbi__uint16; -typedef int16_t stbi__int16; -typedef uint32_t stbi__uint32; -typedef int32_t stbi__int32; -#endif - -// should produce compiler error if size is wrong -typedef unsigned char validate_uint32[sizeof(stbi__uint32)==4 ? 1 : -1]; - -#ifdef _MSC_VER -#define STBI_NOTUSED(v) (void)(v) -#else -#define STBI_NOTUSED(v) (void)sizeof(v) -#endif - -#ifdef _MSC_VER -#define STBI_HAS_LROTL -#endif - -#ifdef STBI_HAS_LROTL - #define stbi_lrot(x,y) _lrotl(x,y) -#else - #define stbi_lrot(x,y) (((x) << (y)) | ((x) >> (32 - (y)))) -#endif - -#if defined(STBI_MALLOC) && defined(STBI_FREE) && (defined(STBI_REALLOC) || defined(STBI_REALLOC_SIZED)) -// ok -#elif !defined(STBI_MALLOC) && !defined(STBI_FREE) && !defined(STBI_REALLOC) && !defined(STBI_REALLOC_SIZED) -// ok -#else -#error "Must define all or none of STBI_MALLOC, STBI_FREE, and STBI_REALLOC (or STBI_REALLOC_SIZED)." -#endif - -#ifndef STBI_MALLOC -#define STBI_MALLOC(sz) malloc(sz) -#define STBI_REALLOC(p,newsz) realloc(p,newsz) -#define STBI_FREE(p) free(p) -#endif - -#ifndef STBI_REALLOC_SIZED -#define STBI_REALLOC_SIZED(p,oldsz,newsz) STBI_REALLOC(p,newsz) -#endif - -// x86/x64 detection -#if defined(__x86_64__) || defined(_M_X64) -#define STBI__X64_TARGET -#elif defined(__i386) || defined(_M_IX86) -#define STBI__X86_TARGET -#endif - -#if defined(__GNUC__) && defined(STBI__X86_TARGET) && !defined(__SSE2__) && !defined(STBI_NO_SIMD) -// gcc doesn't support sse2 intrinsics unless you compile with -msse2, -// which in turn means it gets to use SSE2 everywhere. This is unfortunate, -// but previous attempts to provide the SSE2 functions with runtime -// detection caused numerous issues. The way architecture extensions are -// exposed in GCC/Clang is, sadly, not really suited for one-file libs. -// New behavior: if compiled with -msse2, we use SSE2 without any -// detection; if not, we don't use it at all. -#define STBI_NO_SIMD -#endif - -#if defined(__MINGW32__) && defined(STBI__X86_TARGET) && !defined(STBI_MINGW_ENABLE_SSE2) && !defined(STBI_NO_SIMD) -// Note that __MINGW32__ doesn't actually mean 32-bit, so we have to avoid STBI__X64_TARGET -// -// 32-bit MinGW wants ESP to be 16-byte aligned, but this is not in the -// Windows ABI and VC++ as well as Windows DLLs don't maintain that invariant. -// As a result, enabling SSE2 on 32-bit MinGW is dangerous when not -// simultaneously enabling "-mstackrealign". -// -// See https://github.com/nothings/stb/issues/81 for more information. -// -// So default to no SSE2 on 32-bit MinGW. If you've read this far and added -// -mstackrealign to your build settings, feel free to #define STBI_MINGW_ENABLE_SSE2. -#define STBI_NO_SIMD -#endif - -#if !defined(STBI_NO_SIMD) && (defined(STBI__X86_TARGET) || defined(STBI__X64_TARGET)) -#define STBI_SSE2 -#include - -#ifdef _MSC_VER - -#if _MSC_VER >= 1400 // not VC6 -#include // __cpuid -static int stbi__cpuid3(void) -{ - int info[4]; - __cpuid(info,1); - return info[3]; -} -#else -static int stbi__cpuid3(void) -{ - int res; - __asm { - mov eax,1 - cpuid - mov res,edx - } - return res; -} -#endif - -#define STBI_SIMD_ALIGN(type, name) __declspec(align(16)) type name - -static int stbi__sse2_available(void) -{ - int info3 = stbi__cpuid3(); - return ((info3 >> 26) & 1) != 0; -} -#else // assume GCC-style if not VC++ -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) - -static int stbi__sse2_available(void) -{ - // If we're even attempting to compile this on GCC/Clang, that means - // -msse2 is on, which means the compiler is allowed to use SSE2 - // instructions at will, and so are we. - return 1; -} -#endif -#endif - -// ARM NEON -#if defined(STBI_NO_SIMD) && defined(STBI_NEON) -#undef STBI_NEON -#endif - -#ifdef STBI_NEON -#include -// assume GCC or Clang on ARM targets -#define STBI_SIMD_ALIGN(type, name) type name __attribute__((aligned(16))) -#endif - -#ifndef STBI_SIMD_ALIGN -#define STBI_SIMD_ALIGN(type, name) type name -#endif - -/////////////////////////////////////////////// -// -// stbi__context struct and start_xxx functions - -// stbi__context structure is our basic context used by all images, so it -// contains all the IO context, plus some basic image information -typedef struct -{ - stbi__uint32 img_x, img_y; - int img_n, img_out_n; - - stbi_io_callbacks io; - void *io_user_data; - - int read_from_callbacks; - int buflen; - stbi_uc buffer_start[128]; - - stbi_uc *img_buffer, *img_buffer_end; - stbi_uc *img_buffer_original, *img_buffer_original_end; -} stbi__context; - - -static void stbi__refill_buffer(stbi__context *s); - -// initialize a memory-decode context -static void stbi__start_mem(stbi__context *s, stbi_uc const *buffer, int len) -{ - s->io.read = NULL; - s->read_from_callbacks = 0; - s->img_buffer = s->img_buffer_original = (stbi_uc *) buffer; - s->img_buffer_end = s->img_buffer_original_end = (stbi_uc *) buffer+len; -} - -// initialize a callback-based context -static void stbi__start_callbacks(stbi__context *s, stbi_io_callbacks *c, void *user) -{ - s->io = *c; - s->io_user_data = user; - s->buflen = sizeof(s->buffer_start); - s->read_from_callbacks = 1; - s->img_buffer_original = s->buffer_start; - stbi__refill_buffer(s); - s->img_buffer_original_end = s->img_buffer_end; -} - -#ifndef STBI_NO_STDIO - -static int stbi__stdio_read(void *user, char *data, int size) -{ - return (int) fread(data,1,size,(FILE*) user); -} - -static void stbi__stdio_skip(void *user, int n) -{ - fseek((FILE*) user, n, SEEK_CUR); -} - -static int stbi__stdio_eof(void *user) -{ - return feof((FILE*) user); -} - -static stbi_io_callbacks stbi__stdio_callbacks = -{ - stbi__stdio_read, - stbi__stdio_skip, - stbi__stdio_eof, -}; - -static void stbi__start_file(stbi__context *s, FILE *f) -{ - stbi__start_callbacks(s, &stbi__stdio_callbacks, (void *) f); -} - -//static void stop_file(stbi__context *s) { } - -#endif // !STBI_NO_STDIO - -static void stbi__rewind(stbi__context *s) -{ - // conceptually rewind SHOULD rewind to the beginning of the stream, - // but we just rewind to the beginning of the initial buffer, because - // we only use it after doing 'test', which only ever looks at at most 92 bytes - s->img_buffer = s->img_buffer_original; - s->img_buffer_end = s->img_buffer_original_end; -} - -enum -{ - STBI_ORDER_RGB, - STBI_ORDER_BGR -}; - -typedef struct -{ - int bits_per_channel; - int num_channels; - int channel_order; -} stbi__result_info; - -#ifndef STBI_NO_JPEG -static int stbi__jpeg_test(stbi__context *s); -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNG -static int stbi__png_test(stbi__context *s); -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__png_is16(stbi__context *s); -#endif - -#ifndef STBI_NO_BMP -static int stbi__bmp_test(stbi__context *s); -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_TGA -static int stbi__tga_test(stbi__context *s); -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s); -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc); -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp); -static int stbi__psd_is16(stbi__context *s); -#endif - -#ifndef STBI_NO_HDR -static int stbi__hdr_test(stbi__context *s); -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_test(stbi__context *s); -static void *stbi__pic_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_GIF -static int stbi__gif_test(stbi__context *s); -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp); -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -#ifndef STBI_NO_PNM -static int stbi__pnm_test(stbi__context *s); -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri); -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp); -#endif - -// this is not threadsafe -static const char *stbi__g_failure_reason; - -STBIDEF const char *stbi_failure_reason(void) -{ - return stbi__g_failure_reason; -} - -static int stbi__err(const char *str) -{ - stbi__g_failure_reason = str; - return 0; -} - -static void *stbi__malloc(size_t size) -{ - return STBI_MALLOC(size); -} - -// stb_image uses ints pervasively, including for offset calculations. -// therefore the largest decoded image size we can support with the -// current code, even on 64-bit targets, is INT_MAX. this is not a -// significant limitation for the intended use case. -// -// we do, however, need to make sure our size calculations don't -// overflow. hence a few helper functions for size calculations that -// multiply integers together, making sure that they're non-negative -// and no overflow occurs. - -// return 1 if the sum is valid, 0 on overflow. -// negative terms are considered invalid. -static int stbi__addsizes_valid(int a, int b) -{ - if (b < 0) return 0; - // now 0 <= b <= INT_MAX, hence also - // 0 <= INT_MAX - b <= INTMAX. - // And "a + b <= INT_MAX" (which might overflow) is the - // same as a <= INT_MAX - b (no overflow) - return a <= INT_MAX - b; -} - -// returns 1 if the product is valid, 0 on overflow. -// negative factors are considered invalid. -static int stbi__mul2sizes_valid(int a, int b) -{ - if (a < 0 || b < 0) return 0; - if (b == 0) return 1; // mul-by-0 is always safe - // portable way to check for no overflows in a*b - return a <= INT_MAX/b; -} - -// returns 1 if "a*b + add" has no negative terms/factors and doesn't overflow -static int stbi__mad2sizes_valid(int a, int b, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__addsizes_valid(a*b, add); -} - -// returns 1 if "a*b*c + add" has no negative terms/factors and doesn't overflow -static int stbi__mad3sizes_valid(int a, int b, int c, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__addsizes_valid(a*b*c, add); -} - -// returns 1 if "a*b*c*d + add" has no negative terms/factors and doesn't overflow -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -static int stbi__mad4sizes_valid(int a, int b, int c, int d, int add) -{ - return stbi__mul2sizes_valid(a, b) && stbi__mul2sizes_valid(a*b, c) && - stbi__mul2sizes_valid(a*b*c, d) && stbi__addsizes_valid(a*b*c*d, add); -} -#endif - -// mallocs with size overflow checking -static void *stbi__malloc_mad2(int a, int b, int add) -{ - if (!stbi__mad2sizes_valid(a, b, add)) return NULL; - return stbi__malloc(a*b + add); -} - -static void *stbi__malloc_mad3(int a, int b, int c, int add) -{ - if (!stbi__mad3sizes_valid(a, b, c, add)) return NULL; - return stbi__malloc(a*b*c + add); -} - -#if !defined(STBI_NO_LINEAR) || !defined(STBI_NO_HDR) -static void *stbi__malloc_mad4(int a, int b, int c, int d, int add) -{ - if (!stbi__mad4sizes_valid(a, b, c, d, add)) return NULL; - return stbi__malloc(a*b*c*d + add); -} -#endif - -// stbi__err - error -// stbi__errpf - error returning pointer to float -// stbi__errpuc - error returning pointer to unsigned char - -#ifdef STBI_NO_FAILURE_STRINGS - #define stbi__err(x,y) 0 -#elif defined(STBI_FAILURE_USERMSG) - #define stbi__err(x,y) stbi__err(y) -#else - #define stbi__err(x,y) stbi__err(x) -#endif - -#define stbi__errpf(x,y) ((float *)(size_t) (stbi__err(x,y)?NULL:NULL)) -#define stbi__errpuc(x,y) ((unsigned char *)(size_t) (stbi__err(x,y)?NULL:NULL)) - -STBIDEF void stbi_image_free(void *retval_from_stbi_load) -{ - STBI_FREE(retval_from_stbi_load); -} - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp); -#endif - -#ifndef STBI_NO_HDR -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp); -#endif - -static int stbi__vertically_flip_on_load = 0; - -STBIDEF void stbi_set_flip_vertically_on_load(int flag_true_if_should_flip) -{ - stbi__vertically_flip_on_load = flag_true_if_should_flip; -} - -static void *stbi__load_main(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - memset(ri, 0, sizeof(*ri)); // make sure it's initialized if we add new fields - ri->bits_per_channel = 8; // default is 8 so most paths don't have to be changed - ri->channel_order = STBI_ORDER_RGB; // all current input & output are this, but this is here so we can add BGR order - ri->num_channels = 0; - - #ifndef STBI_NO_JPEG - if (stbi__jpeg_test(s)) return stbi__jpeg_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PNG - if (stbi__png_test(s)) return stbi__png_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_BMP - if (stbi__bmp_test(s)) return stbi__bmp_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_GIF - if (stbi__gif_test(s)) return stbi__gif_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PSD - if (stbi__psd_test(s)) return stbi__psd_load(s,x,y,comp,req_comp, ri, bpc); - #endif - #ifndef STBI_NO_PIC - if (stbi__pic_test(s)) return stbi__pic_load(s,x,y,comp,req_comp, ri); - #endif - #ifndef STBI_NO_PNM - if (stbi__pnm_test(s)) return stbi__pnm_load(s,x,y,comp,req_comp, ri); - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - float *hdr = stbi__hdr_load(s, x,y,comp,req_comp, ri); - return stbi__hdr_to_ldr(hdr, *x, *y, req_comp ? req_comp : *comp); - } - #endif - - #ifndef STBI_NO_TGA - // test tga last because it's a crappy test! - if (stbi__tga_test(s)) - return stbi__tga_load(s,x,y,comp,req_comp, ri); - #endif - - return stbi__errpuc("unknown image type", "Image not of any known type, or corrupt"); -} - -static stbi_uc *stbi__convert_16_to_8(stbi__uint16 *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi_uc *reduced; - - reduced = (stbi_uc *) stbi__malloc(img_len); - if (reduced == NULL) return stbi__errpuc("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) - reduced[i] = (stbi_uc)((orig[i] >> 8) & 0xFF); // top half of each byte is sufficient approx of 16->8 bit scaling - - STBI_FREE(orig); - return reduced; -} - -static stbi__uint16 *stbi__convert_8_to_16(stbi_uc *orig, int w, int h, int channels) -{ - int i; - int img_len = w * h * channels; - stbi__uint16 *enlarged; - - enlarged = (stbi__uint16 *) stbi__malloc(img_len*2); - if (enlarged == NULL) return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - - for (i = 0; i < img_len; ++i) - enlarged[i] = (stbi__uint16)((orig[i] << 8) + orig[i]); // replicate to high and low byte, maps 0->0, 255->0xffff - - STBI_FREE(orig); - return enlarged; -} - -static void stbi__vertical_flip(void *image, int w, int h, int bytes_per_pixel) -{ - int row; - size_t bytes_per_row = (size_t)w * bytes_per_pixel; - stbi_uc temp[2048]; - stbi_uc *bytes = (stbi_uc *)image; - - for (row = 0; row < (h>>1); row++) { - stbi_uc *row0 = bytes + row*bytes_per_row; - stbi_uc *row1 = bytes + (h - row - 1)*bytes_per_row; - // swap row0 with row1 - size_t bytes_left = bytes_per_row; - while (bytes_left) { - size_t bytes_copy = (bytes_left < sizeof(temp)) ? bytes_left : sizeof(temp); - memcpy(temp, row0, bytes_copy); - memcpy(row0, row1, bytes_copy); - memcpy(row1, temp, bytes_copy); - row0 += bytes_copy; - row1 += bytes_copy; - bytes_left -= bytes_copy; - } - } -} - -static void stbi__vertical_flip_slices(void *image, int w, int h, int z, int bytes_per_pixel) -{ - int slice; - int slice_size = w * h * bytes_per_pixel; - - stbi_uc *bytes = (stbi_uc *)image; - for (slice = 0; slice < z; ++slice) { - stbi__vertical_flip(bytes, w, h, bytes_per_pixel); - bytes += slice_size; - } -} - -static unsigned char *stbi__load_and_postprocess_8bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 8); - - if (result == NULL) - return NULL; - - if (ri.bits_per_channel != 8) { - STBI_ASSERT(ri.bits_per_channel == 16); - result = stbi__convert_16_to_8((stbi__uint16 *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 8; - } - - // @TODO: move stbi__convert_format to here - - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi_uc)); - } - - return (unsigned char *) result; -} - -static stbi__uint16 *stbi__load_and_postprocess_16bit(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - stbi__result_info ri; - void *result = stbi__load_main(s, x, y, comp, req_comp, &ri, 16); - - if (result == NULL) - return NULL; - - if (ri.bits_per_channel != 16) { - STBI_ASSERT(ri.bits_per_channel == 8); - result = stbi__convert_8_to_16((stbi_uc *) result, *x, *y, req_comp == 0 ? *comp : req_comp); - ri.bits_per_channel = 16; - } - - // @TODO: move stbi__convert_format16 to here - // @TODO: special case RGB-to-Y (and RGBA-to-YA) for 8-bit-to-16-bit case to keep more precision - - if (stbi__vertically_flip_on_load) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(stbi__uint16)); - } - - return (stbi__uint16 *) result; -} - -#if !defined(STBI_NO_HDR) || !defined(STBI_NO_LINEAR) -static void stbi__float_postprocess(float *result, int *x, int *y, int *comp, int req_comp) -{ - if (stbi__vertically_flip_on_load && result != NULL) { - int channels = req_comp ? req_comp : *comp; - stbi__vertical_flip(result, *x, *y, channels * sizeof(float)); - } -} -#endif - -#ifndef STBI_NO_STDIO - -static FILE *stbi__fopen(char const *filename, char const *mode) -{ - FILE *f; -#if defined(_MSC_VER) && _MSC_VER >= 1400 - if (0 != fopen_s(&f, filename, mode)) - f=0; -#else - f = fopen(filename, mode); -#endif - return f; -} - - -STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - unsigned char *result; - if (!f) return stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF stbi_uc *stbi_load_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} - -STBIDEF stbi__uint16 *stbi_load_from_file_16(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__uint16 *result; - stbi__context s; - stbi__start_file(&s,f); - result = stbi__load_and_postprocess_16bit(&s,x,y,comp,req_comp); - if (result) { - // need to 'unget' all the characters in the IO buffer - fseek(f, - (int) (s.img_buffer_end - s.img_buffer), SEEK_CUR); - } - return result; -} - -STBIDEF stbi_us *stbi_load_16(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - stbi__uint16 *result; - if (!f) return (stbi_us *) stbi__errpuc("can't fopen", "Unable to open file"); - result = stbi_load_from_file_16(f,x,y,comp,req_comp); - fclose(f); - return result; -} - - -#endif //!STBI_NO_STDIO - -STBIDEF stbi_us *stbi_load_16_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); -} - -STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *)clbk, user); - return stbi__load_and_postprocess_16bit(&s,x,y,channels_in_file,desired_channels); -} - -STBIDEF stbi_uc *stbi_load_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); -} - -STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__load_and_postprocess_8bit(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_GIF -STBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - unsigned char *result; - stbi__context s; - stbi__start_mem(&s,buffer,len); - - result = (unsigned char*) stbi__load_gif_main(&s, delays, x, y, z, comp, req_comp); - if (stbi__vertically_flip_on_load) { - stbi__vertical_flip_slices( result, *x, *y, *z, *comp ); - } - - return result; -} -#endif - -#ifndef STBI_NO_LINEAR -static float *stbi__loadf_main(stbi__context *s, int *x, int *y, int *comp, int req_comp) -{ - unsigned char *data; - #ifndef STBI_NO_HDR - if (stbi__hdr_test(s)) { - stbi__result_info ri; - float *hdr_data = stbi__hdr_load(s,x,y,comp,req_comp, &ri); - if (hdr_data) - stbi__float_postprocess(hdr_data,x,y,comp,req_comp); - return hdr_data; - } - #endif - data = stbi__load_and_postprocess_8bit(s, x, y, comp, req_comp); - if (data) - return stbi__ldr_to_hdr(data, *x, *y, req_comp ? req_comp : *comp); - return stbi__errpf("unknown image type", "Image not of any known type, or corrupt"); -} - -STBIDEF float *stbi_loadf_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -STBIDEF float *stbi_loadf_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} - -#ifndef STBI_NO_STDIO -STBIDEF float *stbi_loadf(char const *filename, int *x, int *y, int *comp, int req_comp) -{ - float *result; - FILE *f = stbi__fopen(filename, "rb"); - if (!f) return stbi__errpf("can't fopen", "Unable to open file"); - result = stbi_loadf_from_file(f,x,y,comp,req_comp); - fclose(f); - return result; -} - -STBIDEF float *stbi_loadf_from_file(FILE *f, int *x, int *y, int *comp, int req_comp) -{ - stbi__context s; - stbi__start_file(&s,f); - return stbi__loadf_main(&s,x,y,comp,req_comp); -} -#endif // !STBI_NO_STDIO - -#endif // !STBI_NO_LINEAR - -// these is-hdr-or-not is defined independent of whether STBI_NO_LINEAR is -// defined, for API simplicity; if STBI_NO_LINEAR is defined, it always -// reports false! - -STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(buffer); - STBI_NOTUSED(len); - return 0; - #endif -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_is_hdr (char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result=0; - if (f) { - result = stbi_is_hdr_from_file(f); - fclose(f); - } - return result; -} - -STBIDEF int stbi_is_hdr_from_file(FILE *f) -{ - #ifndef STBI_NO_HDR - long pos = ftell(f); - int res; - stbi__context s; - stbi__start_file(&s,f); - res = stbi__hdr_test(&s); - fseek(f, pos, SEEK_SET); - return res; - #else - STBI_NOTUSED(f); - return 0; - #endif -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user) -{ - #ifndef STBI_NO_HDR - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) clbk, user); - return stbi__hdr_test(&s); - #else - STBI_NOTUSED(clbk); - STBI_NOTUSED(user); - return 0; - #endif -} - -#ifndef STBI_NO_LINEAR -static float stbi__l2h_gamma=2.2f, stbi__l2h_scale=1.0f; - -STBIDEF void stbi_ldr_to_hdr_gamma(float gamma) { stbi__l2h_gamma = gamma; } -STBIDEF void stbi_ldr_to_hdr_scale(float scale) { stbi__l2h_scale = scale; } -#endif - -static float stbi__h2l_gamma_i=1.0f/2.2f, stbi__h2l_scale_i=1.0f; - -STBIDEF void stbi_hdr_to_ldr_gamma(float gamma) { stbi__h2l_gamma_i = 1/gamma; } -STBIDEF void stbi_hdr_to_ldr_scale(float scale) { stbi__h2l_scale_i = 1/scale; } - - -////////////////////////////////////////////////////////////////////////////// -// -// Common code used by all image loaders -// - -enum -{ - STBI__SCAN_load=0, - STBI__SCAN_type, - STBI__SCAN_header -}; - -static void stbi__refill_buffer(stbi__context *s) -{ - int n = (s->io.read)(s->io_user_data,(char*)s->buffer_start,s->buflen); - if (n == 0) { - // at end of file, treat same as if from memory, but need to handle case - // where s->img_buffer isn't pointing to safe memory, e.g. 0-byte file - s->read_from_callbacks = 0; - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start+1; - *s->img_buffer = 0; - } else { - s->img_buffer = s->buffer_start; - s->img_buffer_end = s->buffer_start + n; - } -} - -stbi_inline static stbi_uc stbi__get8(stbi__context *s) -{ - if (s->img_buffer < s->img_buffer_end) - return *s->img_buffer++; - if (s->read_from_callbacks) { - stbi__refill_buffer(s); - return *s->img_buffer++; - } - return 0; -} - -stbi_inline static int stbi__at_eof(stbi__context *s) -{ - if (s->io.read) { - if (!(s->io.eof)(s->io_user_data)) return 0; - // if feof() is true, check if buffer = end - // special case: we've only got the special 0 character at the end - if (s->read_from_callbacks == 0) return 1; - } - - return s->img_buffer >= s->img_buffer_end; -} - -static void stbi__skip(stbi__context *s, int n) -{ - if (n < 0) { - s->img_buffer = s->img_buffer_end; - return; - } - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - s->img_buffer = s->img_buffer_end; - (s->io.skip)(s->io_user_data, n - blen); - return; - } - } - s->img_buffer += n; -} - -static int stbi__getn(stbi__context *s, stbi_uc *buffer, int n) -{ - if (s->io.read) { - int blen = (int) (s->img_buffer_end - s->img_buffer); - if (blen < n) { - int res, count; - - memcpy(buffer, s->img_buffer, blen); - - count = (s->io.read)(s->io_user_data, (char*) buffer + blen, n - blen); - res = (count == (n-blen)); - s->img_buffer = s->img_buffer_end; - return res; - } - } - - if (s->img_buffer+n <= s->img_buffer_end) { - memcpy(buffer, s->img_buffer, n); - s->img_buffer += n; - return 1; - } else - return 0; -} - -static int stbi__get16be(stbi__context *s) -{ - int z = stbi__get8(s); - return (z << 8) + stbi__get8(s); -} - -static stbi__uint32 stbi__get32be(stbi__context *s) -{ - stbi__uint32 z = stbi__get16be(s); - return (z << 16) + stbi__get16be(s); -} - -#if defined(STBI_NO_BMP) && defined(STBI_NO_TGA) && defined(STBI_NO_GIF) -// nothing -#else -static int stbi__get16le(stbi__context *s) -{ - int z = stbi__get8(s); - return z + (stbi__get8(s) << 8); -} -#endif - -#ifndef STBI_NO_BMP -static stbi__uint32 stbi__get32le(stbi__context *s) -{ - stbi__uint32 z = stbi__get16le(s); - return z + (stbi__get16le(s) << 16); -} -#endif - -#define STBI__BYTECAST(x) ((stbi_uc) ((x) & 255)) // truncate int to byte without warnings - - -////////////////////////////////////////////////////////////////////////////// -// -// generic converter from built-in img_n to req_comp -// individual types do this automatically as much as possible (e.g. jpeg -// does all cases internally since it needs to colorspace convert anyway, -// and it never has alpha, so very few cases ). png can automatically -// interleave an alpha=255 channel, but falls back to this for other cases -// -// assume data buffer is malloced, so malloc a new one and free that one -// only failure mode is malloc failing - -static stbi_uc stbi__compute_y(int r, int g, int b) -{ - return (stbi_uc) (((r*77) + (g*150) + (29*b)) >> 8); -} - -static unsigned char *stbi__convert_format(unsigned char *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - unsigned char *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (unsigned char *) stbi__malloc_mad3(req_comp, x, y, 0); - if (good == NULL) { - STBI_FREE(data); - return stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - unsigned char *src = data + j * x * img_n ; - unsigned char *dest = good + j * x * req_comp; - - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0], dest[1]=255; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=255; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=255; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = 255; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y(src[0],src[1],src[2]), dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; - default: STBI_ASSERT(0); - } - #undef STBI__CASE - } - - STBI_FREE(data); - return good; -} - -static stbi__uint16 stbi__compute_y_16(int r, int g, int b) -{ - return (stbi__uint16) (((r*77) + (g*150) + (29*b)) >> 8); -} - -static stbi__uint16 *stbi__convert_format16(stbi__uint16 *data, int img_n, int req_comp, unsigned int x, unsigned int y) -{ - int i,j; - stbi__uint16 *good; - - if (req_comp == img_n) return data; - STBI_ASSERT(req_comp >= 1 && req_comp <= 4); - - good = (stbi__uint16 *) stbi__malloc(req_comp * x * y * 2); - if (good == NULL) { - STBI_FREE(data); - return (stbi__uint16 *) stbi__errpuc("outofmem", "Out of memory"); - } - - for (j=0; j < (int) y; ++j) { - stbi__uint16 *src = data + j * x * img_n ; - stbi__uint16 *dest = good + j * x * req_comp; - - #define STBI__COMBO(a,b) ((a)*8+(b)) - #define STBI__CASE(a,b) case STBI__COMBO(a,b): for(i=x-1; i >= 0; --i, src += a, dest += b) - // convert source image with img_n components to one with req_comp components; - // avoid switch per pixel, so use switch per scanline and massive macros - switch (STBI__COMBO(img_n, req_comp)) { - STBI__CASE(1,2) { dest[0]=src[0], dest[1]=0xffff; } break; - STBI__CASE(1,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(1,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=0xffff; } break; - STBI__CASE(2,1) { dest[0]=src[0]; } break; - STBI__CASE(2,3) { dest[0]=dest[1]=dest[2]=src[0]; } break; - STBI__CASE(2,4) { dest[0]=dest[1]=dest[2]=src[0], dest[3]=src[1]; } break; - STBI__CASE(3,4) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2],dest[3]=0xffff; } break; - STBI__CASE(3,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(3,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = 0xffff; } break; - STBI__CASE(4,1) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]); } break; - STBI__CASE(4,2) { dest[0]=stbi__compute_y_16(src[0],src[1],src[2]), dest[1] = src[3]; } break; - STBI__CASE(4,3) { dest[0]=src[0],dest[1]=src[1],dest[2]=src[2]; } break; - default: STBI_ASSERT(0); - } - #undef STBI__CASE - } - - STBI_FREE(data); - return good; -} - -#ifndef STBI_NO_LINEAR -static float *stbi__ldr_to_hdr(stbi_uc *data, int x, int y, int comp) -{ - int i,k,n; - float *output; - if (!data) return NULL; - output = (float *) stbi__malloc_mad4(x, y, comp, sizeof(float), 0); - if (output == NULL) { STBI_FREE(data); return stbi__errpf("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - output[i*comp + k] = (float) (pow(data[i*comp+k]/255.0f, stbi__l2h_gamma) * stbi__l2h_scale); - } - if (k < comp) output[i*comp + k] = data[i*comp+k]/255.0f; - } - STBI_FREE(data); - return output; -} -#endif - -#ifndef STBI_NO_HDR -#define stbi__float2int(x) ((int) (x)) -static stbi_uc *stbi__hdr_to_ldr(float *data, int x, int y, int comp) -{ - int i,k,n; - stbi_uc *output; - if (!data) return NULL; - output = (stbi_uc *) stbi__malloc_mad3(x, y, comp, 0); - if (output == NULL) { STBI_FREE(data); return stbi__errpuc("outofmem", "Out of memory"); } - // compute number of non-alpha components - if (comp & 1) n = comp; else n = comp-1; - for (i=0; i < x*y; ++i) { - for (k=0; k < n; ++k) { - float z = (float) pow(data[i*comp+k]*stbi__h2l_scale_i, stbi__h2l_gamma_i) * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - if (k < comp) { - float z = data[i*comp+k] * 255 + 0.5f; - if (z < 0) z = 0; - if (z > 255) z = 255; - output[i*comp + k] = (stbi_uc) stbi__float2int(z); - } - } - STBI_FREE(data); - return output; -} -#endif - -////////////////////////////////////////////////////////////////////////////// -// -// "baseline" JPEG/JFIF decoder -// -// simple implementation -// - doesn't support delayed output of y-dimension -// - simple interface (only one output format: 8-bit interleaved RGB) -// - doesn't try to recover corrupt jpegs -// - doesn't allow partial loading, loading multiple at once -// - still fast on x86 (copying globals into locals doesn't help x86) -// - allocates lots of intermediate memory (full size of all components) -// - non-interleaved case requires this anyway -// - allows good upsampling (see next) -// high-quality -// - upsampled channels are bilinearly interpolated, even across blocks -// - quality integer IDCT derived from IJG's 'slow' -// performance -// - fast huffman; reasonable integer IDCT -// - some SIMD kernels for common paths on targets with SSE2/NEON -// - uses a lot of intermediate memory, could cache poorly - -#ifndef STBI_NO_JPEG - -// huffman decoding acceleration -#define FAST_BITS 9 // larger handles more cases; smaller stomps less cache - -typedef struct -{ - stbi_uc fast[1 << FAST_BITS]; - // weirdly, repacking this into AoS is a 10% speed loss, instead of a win - stbi__uint16 code[256]; - stbi_uc values[256]; - stbi_uc size[257]; - unsigned int maxcode[18]; - int delta[17]; // old 'firstsymbol' - old 'firstcode' -} stbi__huffman; - -typedef struct -{ - stbi__context *s; - stbi__huffman huff_dc[4]; - stbi__huffman huff_ac[4]; - stbi__uint16 dequant[4][64]; - stbi__int16 fast_ac[4][1 << FAST_BITS]; - -// sizes for components, interleaved MCUs - int img_h_max, img_v_max; - int img_mcu_x, img_mcu_y; - int img_mcu_w, img_mcu_h; - -// definition of jpeg image component - struct - { - int id; - int h,v; - int tq; - int hd,ha; - int dc_pred; - - int x,y,w2,h2; - stbi_uc *data; - void *raw_data, *raw_coeff; - stbi_uc *linebuf; - short *coeff; // progressive only - int coeff_w, coeff_h; // number of 8x8 coefficient blocks - } img_comp[4]; - - stbi__uint32 code_buffer; // jpeg entropy-coded buffer - int code_bits; // number of valid bits - unsigned char marker; // marker seen while filling entropy buffer - int nomore; // flag if we saw a marker so must stop - - int progressive; - int spec_start; - int spec_end; - int succ_high; - int succ_low; - int eob_run; - int jfif; - int app14_color_transform; // Adobe APP14 tag - int rgb; - - int scan_n, order[4]; - int restart_interval, todo; - -// kernels - void (*idct_block_kernel)(stbi_uc *out, int out_stride, short data[64]); - void (*YCbCr_to_RGB_kernel)(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step); - stbi_uc *(*resample_row_hv_2_kernel)(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs); -} stbi__jpeg; - -static int stbi__build_huffman(stbi__huffman *h, int *count) -{ - int i,j,k=0; - unsigned int code; - // build size list for each symbol (from JPEG spec) - for (i=0; i < 16; ++i) - for (j=0; j < count[i]; ++j) - h->size[k++] = (stbi_uc) (i+1); - h->size[k] = 0; - - // compute actual symbols (from jpeg spec) - code = 0; - k = 0; - for(j=1; j <= 16; ++j) { - // compute delta to add to code to compute symbol id - h->delta[j] = k - code; - if (h->size[k] == j) { - while (h->size[k] == j) - h->code[k++] = (stbi__uint16) (code++); - if (code-1 >= (1u << j)) return stbi__err("bad code lengths","Corrupt JPEG"); - } - // compute largest code + 1 for this size, preshifted as needed later - h->maxcode[j] = code << (16-j); - code <<= 1; - } - h->maxcode[j] = 0xffffffff; - - // build non-spec acceleration table; 255 is flag for not-accelerated - memset(h->fast, 255, 1 << FAST_BITS); - for (i=0; i < k; ++i) { - int s = h->size[i]; - if (s <= FAST_BITS) { - int c = h->code[i] << (FAST_BITS-s); - int m = 1 << (FAST_BITS-s); - for (j=0; j < m; ++j) { - h->fast[c+j] = (stbi_uc) i; - } - } - } - return 1; -} - -// build a table that decodes both magnitude and value of small ACs in -// one go. -static void stbi__build_fast_ac(stbi__int16 *fast_ac, stbi__huffman *h) -{ - int i; - for (i=0; i < (1 << FAST_BITS); ++i) { - stbi_uc fast = h->fast[i]; - fast_ac[i] = 0; - if (fast < 255) { - int rs = h->values[fast]; - int run = (rs >> 4) & 15; - int magbits = rs & 15; - int len = h->size[fast]; - - if (magbits && len + magbits <= FAST_BITS) { - // magnitude code followed by receive_extend code - int k = ((i << len) & ((1 << FAST_BITS) - 1)) >> (FAST_BITS - magbits); - int m = 1 << (magbits - 1); - if (k < m) k += (~0U << magbits) + 1; - // if the result is small enough, we can fit it in fast_ac table - if (k >= -128 && k <= 127) - fast_ac[i] = (stbi__int16) ((k * 256) + (run * 16) + (len + magbits)); - } - } - } -} - -static void stbi__grow_buffer_unsafe(stbi__jpeg *j) -{ - do { - unsigned int b = j->nomore ? 0 : stbi__get8(j->s); - if (b == 0xff) { - int c = stbi__get8(j->s); - while (c == 0xff) c = stbi__get8(j->s); // consume fill bytes - if (c != 0) { - j->marker = (unsigned char) c; - j->nomore = 1; - return; - } - } - j->code_buffer |= b << (24 - j->code_bits); - j->code_bits += 8; - } while (j->code_bits <= 24); -} - -// (1 << n) - 1 -static const stbi__uint32 stbi__bmask[17]={0,1,3,7,15,31,63,127,255,511,1023,2047,4095,8191,16383,32767,65535}; - -// decode a jpeg huffman value from the bitstream -stbi_inline static int stbi__jpeg_huff_decode(stbi__jpeg *j, stbi__huffman *h) -{ - unsigned int temp; - int c,k; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - // look at the top FAST_BITS and determine what symbol ID it is, - // if the code is <= FAST_BITS - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - k = h->fast[c]; - if (k < 255) { - int s = h->size[k]; - if (s > j->code_bits) - return -1; - j->code_buffer <<= s; - j->code_bits -= s; - return h->values[k]; - } - - // naive test is to shift the code_buffer down so k bits are - // valid, then test against maxcode. To speed this up, we've - // preshifted maxcode left so that it has (16-k) 0s at the - // end; in other words, regardless of the number of bits, it - // wants to be compared against something shifted to have 16; - // that way we don't need to shift inside the loop. - temp = j->code_buffer >> 16; - for (k=FAST_BITS+1 ; ; ++k) - if (temp < h->maxcode[k]) - break; - if (k == 17) { - // error! code not found - j->code_bits -= 16; - return -1; - } - - if (k > j->code_bits) - return -1; - - // convert the huffman code to the symbol id - c = ((j->code_buffer >> (32 - k)) & stbi__bmask[k]) + h->delta[k]; - STBI_ASSERT((((j->code_buffer) >> (32 - h->size[c])) & stbi__bmask[h->size[c]]) == h->code[c]); - - // convert the id to a symbol - j->code_bits -= k; - j->code_buffer <<= k; - return h->values[c]; -} - -// bias[n] = (-1<code_bits < n) stbi__grow_buffer_unsafe(j); - - sgn = (stbi__int32)j->code_buffer >> 31; // sign bit is always in MSB - k = stbi_lrot(j->code_buffer, n); - STBI_ASSERT(n >= 0 && n < (int) (sizeof(stbi__bmask)/sizeof(*stbi__bmask))); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k + (stbi__jbias[n] & ~sgn); -} - -// get some unsigned bits -stbi_inline static int stbi__jpeg_get_bits(stbi__jpeg *j, int n) -{ - unsigned int k; - if (j->code_bits < n) stbi__grow_buffer_unsafe(j); - k = stbi_lrot(j->code_buffer, n); - j->code_buffer = k & ~stbi__bmask[n]; - k &= stbi__bmask[n]; - j->code_bits -= n; - return k; -} - -stbi_inline static int stbi__jpeg_get_bit(stbi__jpeg *j) -{ - unsigned int k; - if (j->code_bits < 1) stbi__grow_buffer_unsafe(j); - k = j->code_buffer; - j->code_buffer <<= 1; - --j->code_bits; - return k & 0x80000000; -} - -// given a value that's at position X in the zigzag stream, -// where does it appear in the 8x8 matrix coded as row-major? -static const stbi_uc stbi__jpeg_dezigzag[64+15] = -{ - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63, - // let corrupt input sample past end - 63, 63, 63, 63, 63, 63, 63, 63, - 63, 63, 63, 63, 63, 63, 63 -}; - -// decode one 64-entry block-- -static int stbi__jpeg_decode_block(stbi__jpeg *j, short data[64], stbi__huffman *hdc, stbi__huffman *hac, stbi__int16 *fac, int b, stbi__uint16 *dequant) -{ - int diff,dc,k; - int t; - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - t = stbi__jpeg_huff_decode(j, hdc); - if (t < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - - // 0 all the ac values now so we can do it 32-bits at a time - memset(data,0,64*sizeof(data[0])); - - diff = t ? stbi__extend_receive(j, t) : 0; - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc * dequant[0]); - - // decode AC components, see JPEG spec - k = 1; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) * dequant[zig]); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (rs != 0xf0) break; // end block - k += 16; - } else { - k += r; - // decode into unzigzag'd location - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) * dequant[zig]); - } - } - } while (k < 64); - return 1; -} - -static int stbi__jpeg_decode_block_prog_dc(stbi__jpeg *j, short data[64], stbi__huffman *hdc, int b) -{ - int diff,dc; - int t; - if (j->spec_end != 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - - if (j->succ_high == 0) { - // first scan for DC coefficient, must be first - memset(data,0,64*sizeof(data[0])); // 0 all the ac values now - t = stbi__jpeg_huff_decode(j, hdc); - diff = t ? stbi__extend_receive(j, t) : 0; - - dc = j->img_comp[b].dc_pred + diff; - j->img_comp[b].dc_pred = dc; - data[0] = (short) (dc << j->succ_low); - } else { - // refinement scan for DC coefficient - if (stbi__jpeg_get_bit(j)) - data[0] += (short) (1 << j->succ_low); - } - return 1; -} - -// @OPTIMIZE: store non-zigzagged during the decode passes, -// and only de-zigzag when dequantizing -static int stbi__jpeg_decode_block_prog_ac(stbi__jpeg *j, short data[64], stbi__huffman *hac, stbi__int16 *fac) -{ - int k; - if (j->spec_start == 0) return stbi__err("can't merge dc and ac", "Corrupt JPEG"); - - if (j->succ_high == 0) { - int shift = j->succ_low; - - if (j->eob_run) { - --j->eob_run; - return 1; - } - - k = j->spec_start; - do { - unsigned int zig; - int c,r,s; - if (j->code_bits < 16) stbi__grow_buffer_unsafe(j); - c = (j->code_buffer >> (32 - FAST_BITS)) & ((1 << FAST_BITS)-1); - r = fac[c]; - if (r) { // fast-AC path - k += (r >> 4) & 15; // run - s = r & 15; // combined length - j->code_buffer <<= s; - j->code_bits -= s; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) ((r >> 8) << shift); - } else { - int rs = stbi__jpeg_huff_decode(j, hac); - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r); - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - --j->eob_run; - break; - } - k += 16; - } else { - k += r; - zig = stbi__jpeg_dezigzag[k++]; - data[zig] = (short) (stbi__extend_receive(j,s) << shift); - } - } - } while (k <= j->spec_end); - } else { - // refinement scan for these AC coefficients - - short bit = (short) (1 << j->succ_low); - - if (j->eob_run) { - --j->eob_run; - for (k = j->spec_start; k <= j->spec_end; ++k) { - short *p = &data[stbi__jpeg_dezigzag[k]]; - if (*p != 0) - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } - } else { - k = j->spec_start; - do { - int r,s; - int rs = stbi__jpeg_huff_decode(j, hac); // @OPTIMIZE see if we can use the fast path here, advance-by-r is so slow, eh - if (rs < 0) return stbi__err("bad huffman code","Corrupt JPEG"); - s = rs & 15; - r = rs >> 4; - if (s == 0) { - if (r < 15) { - j->eob_run = (1 << r) - 1; - if (r) - j->eob_run += stbi__jpeg_get_bits(j, r); - r = 64; // force end of block - } else { - // r=15 s=0 should write 16 0s, so we just do - // a run of 15 0s and then write s (which is 0), - // so we don't have to do anything special here - } - } else { - if (s != 1) return stbi__err("bad huffman code", "Corrupt JPEG"); - // sign bit - if (stbi__jpeg_get_bit(j)) - s = bit; - else - s = -bit; - } - - // advance by r - while (k <= j->spec_end) { - short *p = &data[stbi__jpeg_dezigzag[k++]]; - if (*p != 0) { - if (stbi__jpeg_get_bit(j)) - if ((*p & bit)==0) { - if (*p > 0) - *p += bit; - else - *p -= bit; - } - } else { - if (r == 0) { - *p = (short) s; - break; - } - --r; - } - } - } while (k <= j->spec_end); - } - } - return 1; -} - -// take a -128..127 value and stbi__clamp it and convert to 0..255 -stbi_inline static stbi_uc stbi__clamp(int x) -{ - // trick to use a single test to catch both cases - if ((unsigned int) x > 255) { - if (x < 0) return 0; - if (x > 255) return 255; - } - return (stbi_uc) x; -} - -#define stbi__f2f(x) ((int) (((x) * 4096 + 0.5))) -#define stbi__fsh(x) ((x) * 4096) - -// derived from jidctint -- DCT_ISLOW -#define STBI__IDCT_1D(s0,s1,s2,s3,s4,s5,s6,s7) \ - int t0,t1,t2,t3,p1,p2,p3,p4,p5,x0,x1,x2,x3; \ - p2 = s2; \ - p3 = s6; \ - p1 = (p2+p3) * stbi__f2f(0.5411961f); \ - t2 = p1 + p3*stbi__f2f(-1.847759065f); \ - t3 = p1 + p2*stbi__f2f( 0.765366865f); \ - p2 = s0; \ - p3 = s4; \ - t0 = stbi__fsh(p2+p3); \ - t1 = stbi__fsh(p2-p3); \ - x0 = t0+t3; \ - x3 = t0-t3; \ - x1 = t1+t2; \ - x2 = t1-t2; \ - t0 = s7; \ - t1 = s5; \ - t2 = s3; \ - t3 = s1; \ - p3 = t0+t2; \ - p4 = t1+t3; \ - p1 = t0+t3; \ - p2 = t1+t2; \ - p5 = (p3+p4)*stbi__f2f( 1.175875602f); \ - t0 = t0*stbi__f2f( 0.298631336f); \ - t1 = t1*stbi__f2f( 2.053119869f); \ - t2 = t2*stbi__f2f( 3.072711026f); \ - t3 = t3*stbi__f2f( 1.501321110f); \ - p1 = p5 + p1*stbi__f2f(-0.899976223f); \ - p2 = p5 + p2*stbi__f2f(-2.562915447f); \ - p3 = p3*stbi__f2f(-1.961570560f); \ - p4 = p4*stbi__f2f(-0.390180644f); \ - t3 += p1+p4; \ - t2 += p2+p3; \ - t1 += p2+p4; \ - t0 += p1+p3; - -static void stbi__idct_block(stbi_uc *out, int out_stride, short data[64]) -{ - int i,val[64],*v=val; - stbi_uc *o; - short *d = data; - - // columns - for (i=0; i < 8; ++i,++d, ++v) { - // if all zeroes, shortcut -- this avoids dequantizing 0s and IDCTing - if (d[ 8]==0 && d[16]==0 && d[24]==0 && d[32]==0 - && d[40]==0 && d[48]==0 && d[56]==0) { - // no shortcut 0 seconds - // (1|2|3|4|5|6|7)==0 0 seconds - // all separate -0.047 seconds - // 1 && 2|3 && 4|5 && 6|7: -0.047 seconds - int dcterm = d[0]*4; - v[0] = v[8] = v[16] = v[24] = v[32] = v[40] = v[48] = v[56] = dcterm; - } else { - STBI__IDCT_1D(d[ 0],d[ 8],d[16],d[24],d[32],d[40],d[48],d[56]) - // constants scaled things up by 1<<12; let's bring them back - // down, but keep 2 extra bits of precision - x0 += 512; x1 += 512; x2 += 512; x3 += 512; - v[ 0] = (x0+t3) >> 10; - v[56] = (x0-t3) >> 10; - v[ 8] = (x1+t2) >> 10; - v[48] = (x1-t2) >> 10; - v[16] = (x2+t1) >> 10; - v[40] = (x2-t1) >> 10; - v[24] = (x3+t0) >> 10; - v[32] = (x3-t0) >> 10; - } - } - - for (i=0, v=val, o=out; i < 8; ++i,v+=8,o+=out_stride) { - // no fast case since the first 1D IDCT spread components out - STBI__IDCT_1D(v[0],v[1],v[2],v[3],v[4],v[5],v[6],v[7]) - // constants scaled things up by 1<<12, plus we had 1<<2 from first - // loop, plus horizontal and vertical each scale by sqrt(8) so together - // we've got an extra 1<<3, so 1<<17 total we need to remove. - // so we want to round that, which means adding 0.5 * 1<<17, - // aka 65536. Also, we'll end up with -128 to 127 that we want - // to encode as 0..255 by adding 128, so we'll add that before the shift - x0 += 65536 + (128<<17); - x1 += 65536 + (128<<17); - x2 += 65536 + (128<<17); - x3 += 65536 + (128<<17); - // tried computing the shifts into temps, or'ing the temps to see - // if any were out of range, but that was slower - o[0] = stbi__clamp((x0+t3) >> 17); - o[7] = stbi__clamp((x0-t3) >> 17); - o[1] = stbi__clamp((x1+t2) >> 17); - o[6] = stbi__clamp((x1-t2) >> 17); - o[2] = stbi__clamp((x2+t1) >> 17); - o[5] = stbi__clamp((x2-t1) >> 17); - o[3] = stbi__clamp((x3+t0) >> 17); - o[4] = stbi__clamp((x3-t0) >> 17); - } -} - -#ifdef STBI_SSE2 -// sse2 integer IDCT. not the fastest possible implementation but it -// produces bit-identical results to the generic C version so it's -// fully "transparent". -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - // This is constructed to match our regular (generic) integer IDCT exactly. - __m128i row0, row1, row2, row3, row4, row5, row6, row7; - __m128i tmp; - - // dot product constant: even elems=x, odd elems=y - #define dct_const(x,y) _mm_setr_epi16((x),(y),(x),(y),(x),(y),(x),(y)) - - // out(0) = c0[even]*x + c0[odd]*y (c0, x, y 16-bit, out 32-bit) - // out(1) = c1[even]*x + c1[odd]*y - #define dct_rot(out0,out1, x,y,c0,c1) \ - __m128i c0##lo = _mm_unpacklo_epi16((x),(y)); \ - __m128i c0##hi = _mm_unpackhi_epi16((x),(y)); \ - __m128i out0##_l = _mm_madd_epi16(c0##lo, c0); \ - __m128i out0##_h = _mm_madd_epi16(c0##hi, c0); \ - __m128i out1##_l = _mm_madd_epi16(c0##lo, c1); \ - __m128i out1##_h = _mm_madd_epi16(c0##hi, c1) - - // out = in << 12 (in 16-bit, out 32-bit) - #define dct_widen(out, in) \ - __m128i out##_l = _mm_srai_epi32(_mm_unpacklo_epi16(_mm_setzero_si128(), (in)), 4); \ - __m128i out##_h = _mm_srai_epi32(_mm_unpackhi_epi16(_mm_setzero_si128(), (in)), 4) - - // wide add - #define dct_wadd(out, a, b) \ - __m128i out##_l = _mm_add_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_add_epi32(a##_h, b##_h) - - // wide sub - #define dct_wsub(out, a, b) \ - __m128i out##_l = _mm_sub_epi32(a##_l, b##_l); \ - __m128i out##_h = _mm_sub_epi32(a##_h, b##_h) - - // butterfly a/b, add bias, then shift by "s" and pack - #define dct_bfly32o(out0, out1, a,b,bias,s) \ - { \ - __m128i abiased_l = _mm_add_epi32(a##_l, bias); \ - __m128i abiased_h = _mm_add_epi32(a##_h, bias); \ - dct_wadd(sum, abiased, b); \ - dct_wsub(dif, abiased, b); \ - out0 = _mm_packs_epi32(_mm_srai_epi32(sum_l, s), _mm_srai_epi32(sum_h, s)); \ - out1 = _mm_packs_epi32(_mm_srai_epi32(dif_l, s), _mm_srai_epi32(dif_h, s)); \ - } - - // 8-bit interleave step (for transposes) - #define dct_interleave8(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi8(a, b); \ - b = _mm_unpackhi_epi8(tmp, b) - - // 16-bit interleave step (for transposes) - #define dct_interleave16(a, b) \ - tmp = a; \ - a = _mm_unpacklo_epi16(a, b); \ - b = _mm_unpackhi_epi16(tmp, b) - - #define dct_pass(bias,shift) \ - { \ - /* even part */ \ - dct_rot(t2e,t3e, row2,row6, rot0_0,rot0_1); \ - __m128i sum04 = _mm_add_epi16(row0, row4); \ - __m128i dif04 = _mm_sub_epi16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - dct_rot(y0o,y2o, row7,row3, rot2_0,rot2_1); \ - dct_rot(y1o,y3o, row5,row1, rot3_0,rot3_1); \ - __m128i sum17 = _mm_add_epi16(row1, row7); \ - __m128i sum35 = _mm_add_epi16(row3, row5); \ - dct_rot(y4o,y5o, sum17,sum35, rot1_0,rot1_1); \ - dct_wadd(x4, y0o, y4o); \ - dct_wadd(x5, y1o, y5o); \ - dct_wadd(x6, y2o, y5o); \ - dct_wadd(x7, y3o, y4o); \ - dct_bfly32o(row0,row7, x0,x7,bias,shift); \ - dct_bfly32o(row1,row6, x1,x6,bias,shift); \ - dct_bfly32o(row2,row5, x2,x5,bias,shift); \ - dct_bfly32o(row3,row4, x3,x4,bias,shift); \ - } - - __m128i rot0_0 = dct_const(stbi__f2f(0.5411961f), stbi__f2f(0.5411961f) + stbi__f2f(-1.847759065f)); - __m128i rot0_1 = dct_const(stbi__f2f(0.5411961f) + stbi__f2f( 0.765366865f), stbi__f2f(0.5411961f)); - __m128i rot1_0 = dct_const(stbi__f2f(1.175875602f) + stbi__f2f(-0.899976223f), stbi__f2f(1.175875602f)); - __m128i rot1_1 = dct_const(stbi__f2f(1.175875602f), stbi__f2f(1.175875602f) + stbi__f2f(-2.562915447f)); - __m128i rot2_0 = dct_const(stbi__f2f(-1.961570560f) + stbi__f2f( 0.298631336f), stbi__f2f(-1.961570560f)); - __m128i rot2_1 = dct_const(stbi__f2f(-1.961570560f), stbi__f2f(-1.961570560f) + stbi__f2f( 3.072711026f)); - __m128i rot3_0 = dct_const(stbi__f2f(-0.390180644f) + stbi__f2f( 2.053119869f), stbi__f2f(-0.390180644f)); - __m128i rot3_1 = dct_const(stbi__f2f(-0.390180644f), stbi__f2f(-0.390180644f) + stbi__f2f( 1.501321110f)); - - // rounding biases in column/row passes, see stbi__idct_block for explanation. - __m128i bias_0 = _mm_set1_epi32(512); - __m128i bias_1 = _mm_set1_epi32(65536 + (128<<17)); - - // load - row0 = _mm_load_si128((const __m128i *) (data + 0*8)); - row1 = _mm_load_si128((const __m128i *) (data + 1*8)); - row2 = _mm_load_si128((const __m128i *) (data + 2*8)); - row3 = _mm_load_si128((const __m128i *) (data + 3*8)); - row4 = _mm_load_si128((const __m128i *) (data + 4*8)); - row5 = _mm_load_si128((const __m128i *) (data + 5*8)); - row6 = _mm_load_si128((const __m128i *) (data + 6*8)); - row7 = _mm_load_si128((const __m128i *) (data + 7*8)); - - // column pass - dct_pass(bias_0, 10); - - { - // 16bit 8x8 transpose pass 1 - dct_interleave16(row0, row4); - dct_interleave16(row1, row5); - dct_interleave16(row2, row6); - dct_interleave16(row3, row7); - - // transpose pass 2 - dct_interleave16(row0, row2); - dct_interleave16(row1, row3); - dct_interleave16(row4, row6); - dct_interleave16(row5, row7); - - // transpose pass 3 - dct_interleave16(row0, row1); - dct_interleave16(row2, row3); - dct_interleave16(row4, row5); - dct_interleave16(row6, row7); - } - - // row pass - dct_pass(bias_1, 17); - - { - // pack - __m128i p0 = _mm_packus_epi16(row0, row1); // a0a1a2a3...a7b0b1b2b3...b7 - __m128i p1 = _mm_packus_epi16(row2, row3); - __m128i p2 = _mm_packus_epi16(row4, row5); - __m128i p3 = _mm_packus_epi16(row6, row7); - - // 8bit 8x8 transpose pass 1 - dct_interleave8(p0, p2); // a0e0a1e1... - dct_interleave8(p1, p3); // c0g0c1g1... - - // transpose pass 2 - dct_interleave8(p0, p1); // a0c0e0g0... - dct_interleave8(p2, p3); // b0d0f0h0... - - // transpose pass 3 - dct_interleave8(p0, p2); // a0b0c0d0... - dct_interleave8(p1, p3); // a4b4c4d4... - - // store - _mm_storel_epi64((__m128i *) out, p0); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p0, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p2); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p2, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p1); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p1, 0x4e)); out += out_stride; - _mm_storel_epi64((__m128i *) out, p3); out += out_stride; - _mm_storel_epi64((__m128i *) out, _mm_shuffle_epi32(p3, 0x4e)); - } - -#undef dct_const -#undef dct_rot -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_interleave8 -#undef dct_interleave16 -#undef dct_pass -} - -#endif // STBI_SSE2 - -#ifdef STBI_NEON - -// NEON integer IDCT. should produce bit-identical -// results to the generic C version. -static void stbi__idct_simd(stbi_uc *out, int out_stride, short data[64]) -{ - int16x8_t row0, row1, row2, row3, row4, row5, row6, row7; - - int16x4_t rot0_0 = vdup_n_s16(stbi__f2f(0.5411961f)); - int16x4_t rot0_1 = vdup_n_s16(stbi__f2f(-1.847759065f)); - int16x4_t rot0_2 = vdup_n_s16(stbi__f2f( 0.765366865f)); - int16x4_t rot1_0 = vdup_n_s16(stbi__f2f( 1.175875602f)); - int16x4_t rot1_1 = vdup_n_s16(stbi__f2f(-0.899976223f)); - int16x4_t rot1_2 = vdup_n_s16(stbi__f2f(-2.562915447f)); - int16x4_t rot2_0 = vdup_n_s16(stbi__f2f(-1.961570560f)); - int16x4_t rot2_1 = vdup_n_s16(stbi__f2f(-0.390180644f)); - int16x4_t rot3_0 = vdup_n_s16(stbi__f2f( 0.298631336f)); - int16x4_t rot3_1 = vdup_n_s16(stbi__f2f( 2.053119869f)); - int16x4_t rot3_2 = vdup_n_s16(stbi__f2f( 3.072711026f)); - int16x4_t rot3_3 = vdup_n_s16(stbi__f2f( 1.501321110f)); - -#define dct_long_mul(out, inq, coeff) \ - int32x4_t out##_l = vmull_s16(vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmull_s16(vget_high_s16(inq), coeff) - -#define dct_long_mac(out, acc, inq, coeff) \ - int32x4_t out##_l = vmlal_s16(acc##_l, vget_low_s16(inq), coeff); \ - int32x4_t out##_h = vmlal_s16(acc##_h, vget_high_s16(inq), coeff) - -#define dct_widen(out, inq) \ - int32x4_t out##_l = vshll_n_s16(vget_low_s16(inq), 12); \ - int32x4_t out##_h = vshll_n_s16(vget_high_s16(inq), 12) - -// wide add -#define dct_wadd(out, a, b) \ - int32x4_t out##_l = vaddq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vaddq_s32(a##_h, b##_h) - -// wide sub -#define dct_wsub(out, a, b) \ - int32x4_t out##_l = vsubq_s32(a##_l, b##_l); \ - int32x4_t out##_h = vsubq_s32(a##_h, b##_h) - -// butterfly a/b, then shift using "shiftop" by "s" and pack -#define dct_bfly32o(out0,out1, a,b,shiftop,s) \ - { \ - dct_wadd(sum, a, b); \ - dct_wsub(dif, a, b); \ - out0 = vcombine_s16(shiftop(sum_l, s), shiftop(sum_h, s)); \ - out1 = vcombine_s16(shiftop(dif_l, s), shiftop(dif_h, s)); \ - } - -#define dct_pass(shiftop, shift) \ - { \ - /* even part */ \ - int16x8_t sum26 = vaddq_s16(row2, row6); \ - dct_long_mul(p1e, sum26, rot0_0); \ - dct_long_mac(t2e, p1e, row6, rot0_1); \ - dct_long_mac(t3e, p1e, row2, rot0_2); \ - int16x8_t sum04 = vaddq_s16(row0, row4); \ - int16x8_t dif04 = vsubq_s16(row0, row4); \ - dct_widen(t0e, sum04); \ - dct_widen(t1e, dif04); \ - dct_wadd(x0, t0e, t3e); \ - dct_wsub(x3, t0e, t3e); \ - dct_wadd(x1, t1e, t2e); \ - dct_wsub(x2, t1e, t2e); \ - /* odd part */ \ - int16x8_t sum15 = vaddq_s16(row1, row5); \ - int16x8_t sum17 = vaddq_s16(row1, row7); \ - int16x8_t sum35 = vaddq_s16(row3, row5); \ - int16x8_t sum37 = vaddq_s16(row3, row7); \ - int16x8_t sumodd = vaddq_s16(sum17, sum35); \ - dct_long_mul(p5o, sumodd, rot1_0); \ - dct_long_mac(p1o, p5o, sum17, rot1_1); \ - dct_long_mac(p2o, p5o, sum35, rot1_2); \ - dct_long_mul(p3o, sum37, rot2_0); \ - dct_long_mul(p4o, sum15, rot2_1); \ - dct_wadd(sump13o, p1o, p3o); \ - dct_wadd(sump24o, p2o, p4o); \ - dct_wadd(sump23o, p2o, p3o); \ - dct_wadd(sump14o, p1o, p4o); \ - dct_long_mac(x4, sump13o, row7, rot3_0); \ - dct_long_mac(x5, sump24o, row5, rot3_1); \ - dct_long_mac(x6, sump23o, row3, rot3_2); \ - dct_long_mac(x7, sump14o, row1, rot3_3); \ - dct_bfly32o(row0,row7, x0,x7,shiftop,shift); \ - dct_bfly32o(row1,row6, x1,x6,shiftop,shift); \ - dct_bfly32o(row2,row5, x2,x5,shiftop,shift); \ - dct_bfly32o(row3,row4, x3,x4,shiftop,shift); \ - } - - // load - row0 = vld1q_s16(data + 0*8); - row1 = vld1q_s16(data + 1*8); - row2 = vld1q_s16(data + 2*8); - row3 = vld1q_s16(data + 3*8); - row4 = vld1q_s16(data + 4*8); - row5 = vld1q_s16(data + 5*8); - row6 = vld1q_s16(data + 6*8); - row7 = vld1q_s16(data + 7*8); - - // add DC bias - row0 = vaddq_s16(row0, vsetq_lane_s16(1024, vdupq_n_s16(0), 0)); - - // column pass - dct_pass(vrshrn_n_s32, 10); - - // 16bit 8x8 transpose - { -// these three map to a single VTRN.16, VTRN.32, and VSWP, respectively. -// whether compilers actually get this is another story, sadly. -#define dct_trn16(x, y) { int16x8x2_t t = vtrnq_s16(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn32(x, y) { int32x4x2_t t = vtrnq_s32(vreinterpretq_s32_s16(x), vreinterpretq_s32_s16(y)); x = vreinterpretq_s16_s32(t.val[0]); y = vreinterpretq_s16_s32(t.val[1]); } -#define dct_trn64(x, y) { int16x8_t x0 = x; int16x8_t y0 = y; x = vcombine_s16(vget_low_s16(x0), vget_low_s16(y0)); y = vcombine_s16(vget_high_s16(x0), vget_high_s16(y0)); } - - // pass 1 - dct_trn16(row0, row1); // a0b0a2b2a4b4a6b6 - dct_trn16(row2, row3); - dct_trn16(row4, row5); - dct_trn16(row6, row7); - - // pass 2 - dct_trn32(row0, row2); // a0b0c0d0a4b4c4d4 - dct_trn32(row1, row3); - dct_trn32(row4, row6); - dct_trn32(row5, row7); - - // pass 3 - dct_trn64(row0, row4); // a0b0c0d0e0f0g0h0 - dct_trn64(row1, row5); - dct_trn64(row2, row6); - dct_trn64(row3, row7); - -#undef dct_trn16 -#undef dct_trn32 -#undef dct_trn64 - } - - // row pass - // vrshrn_n_s32 only supports shifts up to 16, we need - // 17. so do a non-rounding shift of 16 first then follow - // up with a rounding shift by 1. - dct_pass(vshrn_n_s32, 16); - - { - // pack and round - uint8x8_t p0 = vqrshrun_n_s16(row0, 1); - uint8x8_t p1 = vqrshrun_n_s16(row1, 1); - uint8x8_t p2 = vqrshrun_n_s16(row2, 1); - uint8x8_t p3 = vqrshrun_n_s16(row3, 1); - uint8x8_t p4 = vqrshrun_n_s16(row4, 1); - uint8x8_t p5 = vqrshrun_n_s16(row5, 1); - uint8x8_t p6 = vqrshrun_n_s16(row6, 1); - uint8x8_t p7 = vqrshrun_n_s16(row7, 1); - - // again, these can translate into one instruction, but often don't. -#define dct_trn8_8(x, y) { uint8x8x2_t t = vtrn_u8(x, y); x = t.val[0]; y = t.val[1]; } -#define dct_trn8_16(x, y) { uint16x4x2_t t = vtrn_u16(vreinterpret_u16_u8(x), vreinterpret_u16_u8(y)); x = vreinterpret_u8_u16(t.val[0]); y = vreinterpret_u8_u16(t.val[1]); } -#define dct_trn8_32(x, y) { uint32x2x2_t t = vtrn_u32(vreinterpret_u32_u8(x), vreinterpret_u32_u8(y)); x = vreinterpret_u8_u32(t.val[0]); y = vreinterpret_u8_u32(t.val[1]); } - - // sadly can't use interleaved stores here since we only write - // 8 bytes to each scan line! - - // 8x8 8-bit transpose pass 1 - dct_trn8_8(p0, p1); - dct_trn8_8(p2, p3); - dct_trn8_8(p4, p5); - dct_trn8_8(p6, p7); - - // pass 2 - dct_trn8_16(p0, p2); - dct_trn8_16(p1, p3); - dct_trn8_16(p4, p6); - dct_trn8_16(p5, p7); - - // pass 3 - dct_trn8_32(p0, p4); - dct_trn8_32(p1, p5); - dct_trn8_32(p2, p6); - dct_trn8_32(p3, p7); - - // store - vst1_u8(out, p0); out += out_stride; - vst1_u8(out, p1); out += out_stride; - vst1_u8(out, p2); out += out_stride; - vst1_u8(out, p3); out += out_stride; - vst1_u8(out, p4); out += out_stride; - vst1_u8(out, p5); out += out_stride; - vst1_u8(out, p6); out += out_stride; - vst1_u8(out, p7); - -#undef dct_trn8_8 -#undef dct_trn8_16 -#undef dct_trn8_32 - } - -#undef dct_long_mul -#undef dct_long_mac -#undef dct_widen -#undef dct_wadd -#undef dct_wsub -#undef dct_bfly32o -#undef dct_pass -} - -#endif // STBI_NEON - -#define STBI__MARKER_none 0xff -// if there's a pending marker from the entropy stream, return that -// otherwise, fetch from the stream and get a marker. if there's no -// marker, return 0xff, which is never a valid marker value -static stbi_uc stbi__get_marker(stbi__jpeg *j) -{ - stbi_uc x; - if (j->marker != STBI__MARKER_none) { x = j->marker; j->marker = STBI__MARKER_none; return x; } - x = stbi__get8(j->s); - if (x != 0xff) return STBI__MARKER_none; - while (x == 0xff) - x = stbi__get8(j->s); // consume repeated 0xff fill bytes - return x; -} - -// in each scan, we'll have scan_n components, and the order -// of the components is specified by order[] -#define STBI__RESTART(x) ((x) >= 0xd0 && (x) <= 0xd7) - -// after a restart interval, stbi__jpeg_reset the entropy decoder and -// the dc prediction -static void stbi__jpeg_reset(stbi__jpeg *j) -{ - j->code_bits = 0; - j->code_buffer = 0; - j->nomore = 0; - j->img_comp[0].dc_pred = j->img_comp[1].dc_pred = j->img_comp[2].dc_pred = j->img_comp[3].dc_pred = 0; - j->marker = STBI__MARKER_none; - j->todo = j->restart_interval ? j->restart_interval : 0x7fffffff; - j->eob_run = 0; - // no more than 1<<31 MCUs if no restart_interal? that's plenty safe, - // since we don't even allow 1<<30 pixels -} - -static int stbi__parse_entropy_coded_data(stbi__jpeg *z) -{ - stbi__jpeg_reset(z); - if (!z->progressive) { - if (z->scan_n == 1) { - int i,j; - STBI_SIMD_ALIGN(short, data[64]); - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - // if it's NOT a restart, then just bail, so we get corrupt data - // rather than no data - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - STBI_SIMD_ALIGN(short, data[64]); - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x)*8; - int y2 = (j*z->img_comp[n].v + y)*8; - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block(z, data, z->huff_dc+z->img_comp[n].hd, z->huff_ac+ha, z->fast_ac[ha], n, z->dequant[z->img_comp[n].tq])) return 0; - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*y2+x2, z->img_comp[n].w2, data); - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } else { - if (z->scan_n == 1) { - int i,j; - int n = z->order[0]; - // non-interleaved data, we just need to process one block at a time, - // in trivial scanline order - // number of blocks to do just depends on how many actual "pixels" this - // component has, independent of interleaved MCU blocking and such - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - if (z->spec_start == 0) { - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } else { - int ha = z->img_comp[n].ha; - if (!stbi__jpeg_decode_block_prog_ac(z, data, &z->huff_ac[ha], z->fast_ac[ha])) - return 0; - } - // every data block is an MCU, so countdown the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } else { // interleaved - int i,j,k,x,y; - for (j=0; j < z->img_mcu_y; ++j) { - for (i=0; i < z->img_mcu_x; ++i) { - // scan an interleaved mcu... process scan_n components in order - for (k=0; k < z->scan_n; ++k) { - int n = z->order[k]; - // scan out an mcu's worth of this component; that's just determined - // by the basic H and V specified for the component - for (y=0; y < z->img_comp[n].v; ++y) { - for (x=0; x < z->img_comp[n].h; ++x) { - int x2 = (i*z->img_comp[n].h + x); - int y2 = (j*z->img_comp[n].v + y); - short *data = z->img_comp[n].coeff + 64 * (x2 + y2 * z->img_comp[n].coeff_w); - if (!stbi__jpeg_decode_block_prog_dc(z, data, &z->huff_dc[z->img_comp[n].hd], n)) - return 0; - } - } - } - // after all interleaved components, that's an interleaved MCU, - // so now count down the restart interval - if (--z->todo <= 0) { - if (z->code_bits < 24) stbi__grow_buffer_unsafe(z); - if (!STBI__RESTART(z->marker)) return 1; - stbi__jpeg_reset(z); - } - } - } - return 1; - } - } -} - -static void stbi__jpeg_dequantize(short *data, stbi__uint16 *dequant) -{ - int i; - for (i=0; i < 64; ++i) - data[i] *= dequant[i]; -} - -static void stbi__jpeg_finish(stbi__jpeg *z) -{ - if (z->progressive) { - // dequantize and idct the data - int i,j,n; - for (n=0; n < z->s->img_n; ++n) { - int w = (z->img_comp[n].x+7) >> 3; - int h = (z->img_comp[n].y+7) >> 3; - for (j=0; j < h; ++j) { - for (i=0; i < w; ++i) { - short *data = z->img_comp[n].coeff + 64 * (i + j * z->img_comp[n].coeff_w); - stbi__jpeg_dequantize(data, z->dequant[z->img_comp[n].tq]); - z->idct_block_kernel(z->img_comp[n].data+z->img_comp[n].w2*j*8+i*8, z->img_comp[n].w2, data); - } - } - } - } -} - -static int stbi__process_marker(stbi__jpeg *z, int m) -{ - int L; - switch (m) { - case STBI__MARKER_none: // no marker found - return stbi__err("expected marker","Corrupt JPEG"); - - case 0xDD: // DRI - specify restart interval - if (stbi__get16be(z->s) != 4) return stbi__err("bad DRI len","Corrupt JPEG"); - z->restart_interval = stbi__get16be(z->s); - return 1; - - case 0xDB: // DQT - define quantization table - L = stbi__get16be(z->s)-2; - while (L > 0) { - int q = stbi__get8(z->s); - int p = q >> 4, sixteen = (p != 0); - int t = q & 15,i; - if (p != 0 && p != 1) return stbi__err("bad DQT type","Corrupt JPEG"); - if (t > 3) return stbi__err("bad DQT table","Corrupt JPEG"); - - for (i=0; i < 64; ++i) - z->dequant[t][stbi__jpeg_dezigzag[i]] = (stbi__uint16)(sixteen ? stbi__get16be(z->s) : stbi__get8(z->s)); - L -= (sixteen ? 129 : 65); - } - return L==0; - - case 0xC4: // DHT - define huffman table - L = stbi__get16be(z->s)-2; - while (L > 0) { - stbi_uc *v; - int sizes[16],i,n=0; - int q = stbi__get8(z->s); - int tc = q >> 4; - int th = q & 15; - if (tc > 1 || th > 3) return stbi__err("bad DHT header","Corrupt JPEG"); - for (i=0; i < 16; ++i) { - sizes[i] = stbi__get8(z->s); - n += sizes[i]; - } - L -= 17; - if (tc == 0) { - if (!stbi__build_huffman(z->huff_dc+th, sizes)) return 0; - v = z->huff_dc[th].values; - } else { - if (!stbi__build_huffman(z->huff_ac+th, sizes)) return 0; - v = z->huff_ac[th].values; - } - for (i=0; i < n; ++i) - v[i] = stbi__get8(z->s); - if (tc != 0) - stbi__build_fast_ac(z->fast_ac[th], z->huff_ac + th); - L -= n; - } - return L==0; - } - - // check for comment block or APP blocks - if ((m >= 0xE0 && m <= 0xEF) || m == 0xFE) { - L = stbi__get16be(z->s); - if (L < 2) { - if (m == 0xFE) - return stbi__err("bad COM len","Corrupt JPEG"); - else - return stbi__err("bad APP len","Corrupt JPEG"); - } - L -= 2; - - if (m == 0xE0 && L >= 5) { // JFIF APP0 segment - static const unsigned char tag[5] = {'J','F','I','F','\0'}; - int ok = 1; - int i; - for (i=0; i < 5; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 5; - if (ok) - z->jfif = 1; - } else if (m == 0xEE && L >= 12) { // Adobe APP14 segment - static const unsigned char tag[6] = {'A','d','o','b','e','\0'}; - int ok = 1; - int i; - for (i=0; i < 6; ++i) - if (stbi__get8(z->s) != tag[i]) - ok = 0; - L -= 6; - if (ok) { - stbi__get8(z->s); // version - stbi__get16be(z->s); // flags0 - stbi__get16be(z->s); // flags1 - z->app14_color_transform = stbi__get8(z->s); // color transform - L -= 6; - } - } - - stbi__skip(z->s, L); - return 1; - } - - return stbi__err("unknown marker","Corrupt JPEG"); -} - -// after we see SOS -static int stbi__process_scan_header(stbi__jpeg *z) -{ - int i; - int Ls = stbi__get16be(z->s); - z->scan_n = stbi__get8(z->s); - if (z->scan_n < 1 || z->scan_n > 4 || z->scan_n > (int) z->s->img_n) return stbi__err("bad SOS component count","Corrupt JPEG"); - if (Ls != 6+2*z->scan_n) return stbi__err("bad SOS len","Corrupt JPEG"); - for (i=0; i < z->scan_n; ++i) { - int id = stbi__get8(z->s), which; - int q = stbi__get8(z->s); - for (which = 0; which < z->s->img_n; ++which) - if (z->img_comp[which].id == id) - break; - if (which == z->s->img_n) return 0; // no match - z->img_comp[which].hd = q >> 4; if (z->img_comp[which].hd > 3) return stbi__err("bad DC huff","Corrupt JPEG"); - z->img_comp[which].ha = q & 15; if (z->img_comp[which].ha > 3) return stbi__err("bad AC huff","Corrupt JPEG"); - z->order[i] = which; - } - - { - int aa; - z->spec_start = stbi__get8(z->s); - z->spec_end = stbi__get8(z->s); // should be 63, but might be 0 - aa = stbi__get8(z->s); - z->succ_high = (aa >> 4); - z->succ_low = (aa & 15); - if (z->progressive) { - if (z->spec_start > 63 || z->spec_end > 63 || z->spec_start > z->spec_end || z->succ_high > 13 || z->succ_low > 13) - return stbi__err("bad SOS", "Corrupt JPEG"); - } else { - if (z->spec_start != 0) return stbi__err("bad SOS","Corrupt JPEG"); - if (z->succ_high != 0 || z->succ_low != 0) return stbi__err("bad SOS","Corrupt JPEG"); - z->spec_end = 63; - } - } - - return 1; -} - -static int stbi__free_jpeg_components(stbi__jpeg *z, int ncomp, int why) -{ - int i; - for (i=0; i < ncomp; ++i) { - if (z->img_comp[i].raw_data) { - STBI_FREE(z->img_comp[i].raw_data); - z->img_comp[i].raw_data = NULL; - z->img_comp[i].data = NULL; - } - if (z->img_comp[i].raw_coeff) { - STBI_FREE(z->img_comp[i].raw_coeff); - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].coeff = 0; - } - if (z->img_comp[i].linebuf) { - STBI_FREE(z->img_comp[i].linebuf); - z->img_comp[i].linebuf = NULL; - } - } - return why; -} - -static int stbi__process_frame_header(stbi__jpeg *z, int scan) -{ - stbi__context *s = z->s; - int Lf,p,i,q, h_max=1,v_max=1,c; - Lf = stbi__get16be(s); if (Lf < 11) return stbi__err("bad SOF len","Corrupt JPEG"); // JPEG - p = stbi__get8(s); if (p != 8) return stbi__err("only 8-bit","JPEG format not supported: 8-bit only"); // JPEG baseline - s->img_y = stbi__get16be(s); if (s->img_y == 0) return stbi__err("no header height", "JPEG format not supported: delayed height"); // Legal, but we don't handle it--but neither does IJG - s->img_x = stbi__get16be(s); if (s->img_x == 0) return stbi__err("0 width","Corrupt JPEG"); // JPEG requires - c = stbi__get8(s); - if (c != 3 && c != 1 && c != 4) return stbi__err("bad component count","Corrupt JPEG"); - s->img_n = c; - for (i=0; i < c; ++i) { - z->img_comp[i].data = NULL; - z->img_comp[i].linebuf = NULL; - } - - if (Lf != 8+3*s->img_n) return stbi__err("bad SOF len","Corrupt JPEG"); - - z->rgb = 0; - for (i=0; i < s->img_n; ++i) { - static const unsigned char rgb[3] = { 'R', 'G', 'B' }; - z->img_comp[i].id = stbi__get8(s); - if (s->img_n == 3 && z->img_comp[i].id == rgb[i]) - ++z->rgb; - q = stbi__get8(s); - z->img_comp[i].h = (q >> 4); if (!z->img_comp[i].h || z->img_comp[i].h > 4) return stbi__err("bad H","Corrupt JPEG"); - z->img_comp[i].v = q & 15; if (!z->img_comp[i].v || z->img_comp[i].v > 4) return stbi__err("bad V","Corrupt JPEG"); - z->img_comp[i].tq = stbi__get8(s); if (z->img_comp[i].tq > 3) return stbi__err("bad TQ","Corrupt JPEG"); - } - - if (scan != STBI__SCAN_load) return 1; - - if (!stbi__mad3sizes_valid(s->img_x, s->img_y, s->img_n, 0)) return stbi__err("too large", "Image too large to decode"); - - for (i=0; i < s->img_n; ++i) { - if (z->img_comp[i].h > h_max) h_max = z->img_comp[i].h; - if (z->img_comp[i].v > v_max) v_max = z->img_comp[i].v; - } - - // compute interleaved mcu info - z->img_h_max = h_max; - z->img_v_max = v_max; - z->img_mcu_w = h_max * 8; - z->img_mcu_h = v_max * 8; - // these sizes can't be more than 17 bits - z->img_mcu_x = (s->img_x + z->img_mcu_w-1) / z->img_mcu_w; - z->img_mcu_y = (s->img_y + z->img_mcu_h-1) / z->img_mcu_h; - - for (i=0; i < s->img_n; ++i) { - // number of effective pixels (e.g. for non-interleaved MCU) - z->img_comp[i].x = (s->img_x * z->img_comp[i].h + h_max-1) / h_max; - z->img_comp[i].y = (s->img_y * z->img_comp[i].v + v_max-1) / v_max; - // to simplify generation, we'll allocate enough memory to decode - // the bogus oversized data from using interleaved MCUs and their - // big blocks (e.g. a 16x16 iMCU on an image of width 33); we won't - // discard the extra data until colorspace conversion - // - // img_mcu_x, img_mcu_y: <=17 bits; comp[i].h and .v are <=4 (checked earlier) - // so these muls can't overflow with 32-bit ints (which we require) - z->img_comp[i].w2 = z->img_mcu_x * z->img_comp[i].h * 8; - z->img_comp[i].h2 = z->img_mcu_y * z->img_comp[i].v * 8; - z->img_comp[i].coeff = 0; - z->img_comp[i].raw_coeff = 0; - z->img_comp[i].linebuf = NULL; - z->img_comp[i].raw_data = stbi__malloc_mad2(z->img_comp[i].w2, z->img_comp[i].h2, 15); - if (z->img_comp[i].raw_data == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - // align blocks for idct using mmx/sse - z->img_comp[i].data = (stbi_uc*) (((size_t) z->img_comp[i].raw_data + 15) & ~15); - if (z->progressive) { - // w2, h2 are multiples of 8 (see above) - z->img_comp[i].coeff_w = z->img_comp[i].w2 / 8; - z->img_comp[i].coeff_h = z->img_comp[i].h2 / 8; - z->img_comp[i].raw_coeff = stbi__malloc_mad3(z->img_comp[i].w2, z->img_comp[i].h2, sizeof(short), 15); - if (z->img_comp[i].raw_coeff == NULL) - return stbi__free_jpeg_components(z, i+1, stbi__err("outofmem", "Out of memory")); - z->img_comp[i].coeff = (short*) (((size_t) z->img_comp[i].raw_coeff + 15) & ~15); - } - } - - return 1; -} - -// use comparisons since in some cases we handle more than one case (e.g. SOF) -#define stbi__DNL(x) ((x) == 0xdc) -#define stbi__SOI(x) ((x) == 0xd8) -#define stbi__EOI(x) ((x) == 0xd9) -#define stbi__SOF(x) ((x) == 0xc0 || (x) == 0xc1 || (x) == 0xc2) -#define stbi__SOS(x) ((x) == 0xda) - -#define stbi__SOF_progressive(x) ((x) == 0xc2) - -static int stbi__decode_jpeg_header(stbi__jpeg *z, int scan) -{ - int m; - z->jfif = 0; - z->app14_color_transform = -1; // valid values are 0,1,2 - z->marker = STBI__MARKER_none; // initialize cached marker to empty - m = stbi__get_marker(z); - if (!stbi__SOI(m)) return stbi__err("no SOI","Corrupt JPEG"); - if (scan == STBI__SCAN_type) return 1; - m = stbi__get_marker(z); - while (!stbi__SOF(m)) { - if (!stbi__process_marker(z,m)) return 0; - m = stbi__get_marker(z); - while (m == STBI__MARKER_none) { - // some files have extra padding after their blocks, so ok, we'll scan - if (stbi__at_eof(z->s)) return stbi__err("no SOF", "Corrupt JPEG"); - m = stbi__get_marker(z); - } - } - z->progressive = stbi__SOF_progressive(m); - if (!stbi__process_frame_header(z, scan)) return 0; - return 1; -} - -// decode image to YCbCr format -static int stbi__decode_jpeg_image(stbi__jpeg *j) -{ - int m; - for (m = 0; m < 4; m++) { - j->img_comp[m].raw_data = NULL; - j->img_comp[m].raw_coeff = NULL; - } - j->restart_interval = 0; - if (!stbi__decode_jpeg_header(j, STBI__SCAN_load)) return 0; - m = stbi__get_marker(j); - while (!stbi__EOI(m)) { - if (stbi__SOS(m)) { - if (!stbi__process_scan_header(j)) return 0; - if (!stbi__parse_entropy_coded_data(j)) return 0; - if (j->marker == STBI__MARKER_none ) { - // handle 0s at the end of image data from IP Kamera 9060 - while (!stbi__at_eof(j->s)) { - int x = stbi__get8(j->s); - if (x == 255) { - j->marker = stbi__get8(j->s); - break; - } - } - // if we reach eof without hitting a marker, stbi__get_marker() below will fail and we'll eventually return 0 - } - } else if (stbi__DNL(m)) { - int Ld = stbi__get16be(j->s); - stbi__uint32 NL = stbi__get16be(j->s); - if (Ld != 4) return stbi__err("bad DNL len", "Corrupt JPEG"); - if (NL != j->s->img_y) return stbi__err("bad DNL height", "Corrupt JPEG"); - } else { - if (!stbi__process_marker(j, m)) return 0; - } - m = stbi__get_marker(j); - } - if (j->progressive) - stbi__jpeg_finish(j); - return 1; -} - -// static jfif-centered resampling (across block boundaries) - -typedef stbi_uc *(*resample_row_func)(stbi_uc *out, stbi_uc *in0, stbi_uc *in1, - int w, int hs); - -#define stbi__div4(x) ((stbi_uc) ((x) >> 2)) - -static stbi_uc *resample_row_1(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - STBI_NOTUSED(out); - STBI_NOTUSED(in_far); - STBI_NOTUSED(w); - STBI_NOTUSED(hs); - return in_near; -} - -static stbi_uc* stbi__resample_row_v_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples vertically for every one in input - int i; - STBI_NOTUSED(hs); - for (i=0; i < w; ++i) - out[i] = stbi__div4(3*in_near[i] + in_far[i] + 2); - return out; -} - -static stbi_uc* stbi__resample_row_h_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate two samples horizontally for every one in input - int i; - stbi_uc *input = in_near; - - if (w == 1) { - // if only one sample, can't do any interpolation - out[0] = out[1] = input[0]; - return out; - } - - out[0] = input[0]; - out[1] = stbi__div4(input[0]*3 + input[1] + 2); - for (i=1; i < w-1; ++i) { - int n = 3*input[i]+2; - out[i*2+0] = stbi__div4(n+input[i-1]); - out[i*2+1] = stbi__div4(n+input[i+1]); - } - out[i*2+0] = stbi__div4(input[w-2]*3 + input[w-1] + 2); - out[i*2+1] = input[w-1]; - - STBI_NOTUSED(in_far); - STBI_NOTUSED(hs); - - return out; -} - -#define stbi__div16(x) ((stbi_uc) ((x) >> 4)) - -static stbi_uc *stbi__resample_row_hv_2(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i,t0,t1; - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - out[0] = stbi__div4(t1+2); - for (i=1; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static stbi_uc *stbi__resample_row_hv_2_simd(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // need to generate 2x2 samples for every one in input - int i=0,t0,t1; - - if (w == 1) { - out[0] = out[1] = stbi__div4(3*in_near[0] + in_far[0] + 2); - return out; - } - - t1 = 3*in_near[0] + in_far[0]; - // process groups of 8 pixels for as long as we can. - // note we can't handle the last pixel in a row in this loop - // because we need to handle the filter boundary conditions. - for (; i < ((w-1) & ~7); i += 8) { -#if defined(STBI_SSE2) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - __m128i zero = _mm_setzero_si128(); - __m128i farb = _mm_loadl_epi64((__m128i *) (in_far + i)); - __m128i nearb = _mm_loadl_epi64((__m128i *) (in_near + i)); - __m128i farw = _mm_unpacklo_epi8(farb, zero); - __m128i nearw = _mm_unpacklo_epi8(nearb, zero); - __m128i diff = _mm_sub_epi16(farw, nearw); - __m128i nears = _mm_slli_epi16(nearw, 2); - __m128i curr = _mm_add_epi16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - __m128i prv0 = _mm_slli_si128(curr, 2); - __m128i nxt0 = _mm_srli_si128(curr, 2); - __m128i prev = _mm_insert_epi16(prv0, t1, 0); - __m128i next = _mm_insert_epi16(nxt0, 3*in_near[i+8] + in_far[i+8], 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - __m128i bias = _mm_set1_epi16(8); - __m128i curs = _mm_slli_epi16(curr, 2); - __m128i prvd = _mm_sub_epi16(prev, curr); - __m128i nxtd = _mm_sub_epi16(next, curr); - __m128i curb = _mm_add_epi16(curs, bias); - __m128i even = _mm_add_epi16(prvd, curb); - __m128i odd = _mm_add_epi16(nxtd, curb); - - // interleave even and odd pixels, then undo scaling. - __m128i int0 = _mm_unpacklo_epi16(even, odd); - __m128i int1 = _mm_unpackhi_epi16(even, odd); - __m128i de0 = _mm_srli_epi16(int0, 4); - __m128i de1 = _mm_srli_epi16(int1, 4); - - // pack and write output - __m128i outv = _mm_packus_epi16(de0, de1); - _mm_storeu_si128((__m128i *) (out + i*2), outv); -#elif defined(STBI_NEON) - // load and perform the vertical filtering pass - // this uses 3*x + y = 4*x + (y - x) - uint8x8_t farb = vld1_u8(in_far + i); - uint8x8_t nearb = vld1_u8(in_near + i); - int16x8_t diff = vreinterpretq_s16_u16(vsubl_u8(farb, nearb)); - int16x8_t nears = vreinterpretq_s16_u16(vshll_n_u8(nearb, 2)); - int16x8_t curr = vaddq_s16(nears, diff); // current row - - // horizontal filter works the same based on shifted vers of current - // row. "prev" is current row shifted right by 1 pixel; we need to - // insert the previous pixel value (from t1). - // "next" is current row shifted left by 1 pixel, with first pixel - // of next block of 8 pixels added in. - int16x8_t prv0 = vextq_s16(curr, curr, 7); - int16x8_t nxt0 = vextq_s16(curr, curr, 1); - int16x8_t prev = vsetq_lane_s16(t1, prv0, 0); - int16x8_t next = vsetq_lane_s16(3*in_near[i+8] + in_far[i+8], nxt0, 7); - - // horizontal filter, polyphase implementation since it's convenient: - // even pixels = 3*cur + prev = cur*4 + (prev - cur) - // odd pixels = 3*cur + next = cur*4 + (next - cur) - // note the shared term. - int16x8_t curs = vshlq_n_s16(curr, 2); - int16x8_t prvd = vsubq_s16(prev, curr); - int16x8_t nxtd = vsubq_s16(next, curr); - int16x8_t even = vaddq_s16(curs, prvd); - int16x8_t odd = vaddq_s16(curs, nxtd); - - // undo scaling and round, then store with even/odd phases interleaved - uint8x8x2_t o; - o.val[0] = vqrshrun_n_s16(even, 4); - o.val[1] = vqrshrun_n_s16(odd, 4); - vst2_u8(out + i*2, o); -#endif - - // "previous" value for next iter - t1 = 3*in_near[i+7] + in_far[i+7]; - } - - t0 = t1; - t1 = 3*in_near[i] + in_far[i]; - out[i*2] = stbi__div16(3*t1 + t0 + 8); - - for (++i; i < w; ++i) { - t0 = t1; - t1 = 3*in_near[i]+in_far[i]; - out[i*2-1] = stbi__div16(3*t0 + t1 + 8); - out[i*2 ] = stbi__div16(3*t1 + t0 + 8); - } - out[w*2-1] = stbi__div4(t1+2); - - STBI_NOTUSED(hs); - - return out; -} -#endif - -static stbi_uc *stbi__resample_row_generic(stbi_uc *out, stbi_uc *in_near, stbi_uc *in_far, int w, int hs) -{ - // resample with nearest-neighbor - int i,j; - STBI_NOTUSED(in_far); - for (i=0; i < w; ++i) - for (j=0; j < hs; ++j) - out[i*hs+j] = in_near[i]; - return out; -} - -// this is a reduced-precision calculation of YCbCr-to-RGB introduced -// to make sure the code produces the same results in both SIMD and scalar -#define stbi__float2fixed(x) (((int) ((x) * 4096.0f + 0.5f)) << 8) -static void stbi__YCbCr_to_RGB_row(stbi_uc *out, const stbi_uc *y, const stbi_uc *pcb, const stbi_uc *pcr, int count, int step) -{ - int i; - for (i=0; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + (cr*-stbi__float2fixed(0.71414f)) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} - -#if defined(STBI_SSE2) || defined(STBI_NEON) -static void stbi__YCbCr_to_RGB_simd(stbi_uc *out, stbi_uc const *y, stbi_uc const *pcb, stbi_uc const *pcr, int count, int step) -{ - int i = 0; - -#ifdef STBI_SSE2 - // step == 3 is pretty ugly on the final interleave, and i'm not convinced - // it's useful in practice (you wouldn't use it for textures, for example). - // so just accelerate step == 4 case. - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - __m128i signflip = _mm_set1_epi8(-0x80); - __m128i cr_const0 = _mm_set1_epi16( (short) ( 1.40200f*4096.0f+0.5f)); - __m128i cr_const1 = _mm_set1_epi16( - (short) ( 0.71414f*4096.0f+0.5f)); - __m128i cb_const0 = _mm_set1_epi16( - (short) ( 0.34414f*4096.0f+0.5f)); - __m128i cb_const1 = _mm_set1_epi16( (short) ( 1.77200f*4096.0f+0.5f)); - __m128i y_bias = _mm_set1_epi8((char) (unsigned char) 128); - __m128i xw = _mm_set1_epi16(255); // alpha channel - - for (; i+7 < count; i += 8) { - // load - __m128i y_bytes = _mm_loadl_epi64((__m128i *) (y+i)); - __m128i cr_bytes = _mm_loadl_epi64((__m128i *) (pcr+i)); - __m128i cb_bytes = _mm_loadl_epi64((__m128i *) (pcb+i)); - __m128i cr_biased = _mm_xor_si128(cr_bytes, signflip); // -128 - __m128i cb_biased = _mm_xor_si128(cb_bytes, signflip); // -128 - - // unpack to short (and left-shift cr, cb by 8) - __m128i yw = _mm_unpacklo_epi8(y_bias, y_bytes); - __m128i crw = _mm_unpacklo_epi8(_mm_setzero_si128(), cr_biased); - __m128i cbw = _mm_unpacklo_epi8(_mm_setzero_si128(), cb_biased); - - // color transform - __m128i yws = _mm_srli_epi16(yw, 4); - __m128i cr0 = _mm_mulhi_epi16(cr_const0, crw); - __m128i cb0 = _mm_mulhi_epi16(cb_const0, cbw); - __m128i cb1 = _mm_mulhi_epi16(cbw, cb_const1); - __m128i cr1 = _mm_mulhi_epi16(crw, cr_const1); - __m128i rws = _mm_add_epi16(cr0, yws); - __m128i gwt = _mm_add_epi16(cb0, yws); - __m128i bws = _mm_add_epi16(yws, cb1); - __m128i gws = _mm_add_epi16(gwt, cr1); - - // descale - __m128i rw = _mm_srai_epi16(rws, 4); - __m128i bw = _mm_srai_epi16(bws, 4); - __m128i gw = _mm_srai_epi16(gws, 4); - - // back to byte, set up for transpose - __m128i brb = _mm_packus_epi16(rw, bw); - __m128i gxb = _mm_packus_epi16(gw, xw); - - // transpose to interleave channels - __m128i t0 = _mm_unpacklo_epi8(brb, gxb); - __m128i t1 = _mm_unpackhi_epi8(brb, gxb); - __m128i o0 = _mm_unpacklo_epi16(t0, t1); - __m128i o1 = _mm_unpackhi_epi16(t0, t1); - - // store - _mm_storeu_si128((__m128i *) (out + 0), o0); - _mm_storeu_si128((__m128i *) (out + 16), o1); - out += 32; - } - } -#endif - -#ifdef STBI_NEON - // in this version, step=3 support would be easy to add. but is there demand? - if (step == 4) { - // this is a fairly straightforward implementation and not super-optimized. - uint8x8_t signflip = vdup_n_u8(0x80); - int16x8_t cr_const0 = vdupq_n_s16( (short) ( 1.40200f*4096.0f+0.5f)); - int16x8_t cr_const1 = vdupq_n_s16( - (short) ( 0.71414f*4096.0f+0.5f)); - int16x8_t cb_const0 = vdupq_n_s16( - (short) ( 0.34414f*4096.0f+0.5f)); - int16x8_t cb_const1 = vdupq_n_s16( (short) ( 1.77200f*4096.0f+0.5f)); - - for (; i+7 < count; i += 8) { - // load - uint8x8_t y_bytes = vld1_u8(y + i); - uint8x8_t cr_bytes = vld1_u8(pcr + i); - uint8x8_t cb_bytes = vld1_u8(pcb + i); - int8x8_t cr_biased = vreinterpret_s8_u8(vsub_u8(cr_bytes, signflip)); - int8x8_t cb_biased = vreinterpret_s8_u8(vsub_u8(cb_bytes, signflip)); - - // expand to s16 - int16x8_t yws = vreinterpretq_s16_u16(vshll_n_u8(y_bytes, 4)); - int16x8_t crw = vshll_n_s8(cr_biased, 7); - int16x8_t cbw = vshll_n_s8(cb_biased, 7); - - // color transform - int16x8_t cr0 = vqdmulhq_s16(crw, cr_const0); - int16x8_t cb0 = vqdmulhq_s16(cbw, cb_const0); - int16x8_t cr1 = vqdmulhq_s16(crw, cr_const1); - int16x8_t cb1 = vqdmulhq_s16(cbw, cb_const1); - int16x8_t rws = vaddq_s16(yws, cr0); - int16x8_t gws = vaddq_s16(vaddq_s16(yws, cb0), cr1); - int16x8_t bws = vaddq_s16(yws, cb1); - - // undo scaling, round, convert to byte - uint8x8x4_t o; - o.val[0] = vqrshrun_n_s16(rws, 4); - o.val[1] = vqrshrun_n_s16(gws, 4); - o.val[2] = vqrshrun_n_s16(bws, 4); - o.val[3] = vdup_n_u8(255); - - // store, interleaving r/g/b/a - vst4_u8(out, o); - out += 8*4; - } - } -#endif - - for (; i < count; ++i) { - int y_fixed = (y[i] << 20) + (1<<19); // rounding - int r,g,b; - int cr = pcr[i] - 128; - int cb = pcb[i] - 128; - r = y_fixed + cr* stbi__float2fixed(1.40200f); - g = y_fixed + cr*-stbi__float2fixed(0.71414f) + ((cb*-stbi__float2fixed(0.34414f)) & 0xffff0000); - b = y_fixed + cb* stbi__float2fixed(1.77200f); - r >>= 20; - g >>= 20; - b >>= 20; - if ((unsigned) r > 255) { if (r < 0) r = 0; else r = 255; } - if ((unsigned) g > 255) { if (g < 0) g = 0; else g = 255; } - if ((unsigned) b > 255) { if (b < 0) b = 0; else b = 255; } - out[0] = (stbi_uc)r; - out[1] = (stbi_uc)g; - out[2] = (stbi_uc)b; - out[3] = 255; - out += step; - } -} -#endif - -// set up the kernels -static void stbi__setup_jpeg(stbi__jpeg *j) -{ - j->idct_block_kernel = stbi__idct_block; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_row; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2; - -#ifdef STBI_SSE2 - if (stbi__sse2_available()) { - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; - } -#endif - -#ifdef STBI_NEON - j->idct_block_kernel = stbi__idct_simd; - j->YCbCr_to_RGB_kernel = stbi__YCbCr_to_RGB_simd; - j->resample_row_hv_2_kernel = stbi__resample_row_hv_2_simd; -#endif -} - -// clean up the temporary component buffers -static void stbi__cleanup_jpeg(stbi__jpeg *j) -{ - stbi__free_jpeg_components(j, j->s->img_n, 0); -} - -typedef struct -{ - resample_row_func resample; - stbi_uc *line0,*line1; - int hs,vs; // expansion factor in each axis - int w_lores; // horizontal pixels pre-expansion - int ystep; // how far through vertical expansion we are - int ypos; // which pre-expansion row we're on -} stbi__resample; - -// fast 0..255 * 0..255 => 0..255 rounded multiplication -static stbi_uc stbi__blinn_8x8(stbi_uc x, stbi_uc y) -{ - unsigned int t = x*y + 128; - return (stbi_uc) ((t + (t >>8)) >> 8); -} - -static stbi_uc *load_jpeg_image(stbi__jpeg *z, int *out_x, int *out_y, int *comp, int req_comp) -{ - int n, decode_n, is_rgb; - z->s->img_n = 0; // make stbi__cleanup_jpeg safe - - // validate req_comp - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - - // load a jpeg image from whichever source, but leave in YCbCr format - if (!stbi__decode_jpeg_image(z)) { stbi__cleanup_jpeg(z); return NULL; } - - // determine actual number of components to generate - n = req_comp ? req_comp : z->s->img_n >= 3 ? 3 : 1; - - is_rgb = z->s->img_n == 3 && (z->rgb == 3 || (z->app14_color_transform == 0 && !z->jfif)); - - if (z->s->img_n == 3 && n < 3 && !is_rgb) - decode_n = 1; - else - decode_n = z->s->img_n; - - // resample and color-convert - { - int k; - unsigned int i,j; - stbi_uc *output; - stbi_uc *coutput[4]; - - stbi__resample res_comp[4]; - - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - - // allocate line buffer big enough for upsampling off the edges - // with upsample factor of 4 - z->img_comp[k].linebuf = (stbi_uc *) stbi__malloc(z->s->img_x + 3); - if (!z->img_comp[k].linebuf) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - r->hs = z->img_h_max / z->img_comp[k].h; - r->vs = z->img_v_max / z->img_comp[k].v; - r->ystep = r->vs >> 1; - r->w_lores = (z->s->img_x + r->hs-1) / r->hs; - r->ypos = 0; - r->line0 = r->line1 = z->img_comp[k].data; - - if (r->hs == 1 && r->vs == 1) r->resample = resample_row_1; - else if (r->hs == 1 && r->vs == 2) r->resample = stbi__resample_row_v_2; - else if (r->hs == 2 && r->vs == 1) r->resample = stbi__resample_row_h_2; - else if (r->hs == 2 && r->vs == 2) r->resample = z->resample_row_hv_2_kernel; - else r->resample = stbi__resample_row_generic; - } - - // can't error after this so, this is safe - output = (stbi_uc *) stbi__malloc_mad3(n, z->s->img_x, z->s->img_y, 1); - if (!output) { stbi__cleanup_jpeg(z); return stbi__errpuc("outofmem", "Out of memory"); } - - // now go ahead and resample - for (j=0; j < z->s->img_y; ++j) { - stbi_uc *out = output + n * z->s->img_x * j; - for (k=0; k < decode_n; ++k) { - stbi__resample *r = &res_comp[k]; - int y_bot = r->ystep >= (r->vs >> 1); - coutput[k] = r->resample(z->img_comp[k].linebuf, - y_bot ? r->line1 : r->line0, - y_bot ? r->line0 : r->line1, - r->w_lores, r->hs); - if (++r->ystep >= r->vs) { - r->ystep = 0; - r->line0 = r->line1; - if (++r->ypos < z->img_comp[k].y) - r->line1 += z->img_comp[k].w2; - } - } - if (n >= 3) { - stbi_uc *y = coutput[0]; - if (z->s->img_n == 3) { - if (is_rgb) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = y[i]; - out[1] = coutput[1][i]; - out[2] = coutput[2][i]; - out[3] = 255; - out += n; - } - } else { - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else if (z->s->img_n == 4) { - if (z->app14_color_transform == 0) { // CMYK - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(coutput[0][i], m); - out[1] = stbi__blinn_8x8(coutput[1][i], m); - out[2] = stbi__blinn_8x8(coutput[2][i], m); - out[3] = 255; - out += n; - } - } else if (z->app14_color_transform == 2) { // YCCK - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - out[0] = stbi__blinn_8x8(255 - out[0], m); - out[1] = stbi__blinn_8x8(255 - out[1], m); - out[2] = stbi__blinn_8x8(255 - out[2], m); - out += n; - } - } else { // YCbCr + alpha? Ignore the fourth channel for now - z->YCbCr_to_RGB_kernel(out, y, coutput[1], coutput[2], z->s->img_x, n); - } - } else - for (i=0; i < z->s->img_x; ++i) { - out[0] = out[1] = out[2] = y[i]; - out[3] = 255; // not used if n==3 - out += n; - } - } else { - if (is_rgb) { - if (n == 1) - for (i=0; i < z->s->img_x; ++i) - *out++ = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - else { - for (i=0; i < z->s->img_x; ++i, out += 2) { - out[0] = stbi__compute_y(coutput[0][i], coutput[1][i], coutput[2][i]); - out[1] = 255; - } - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 0) { - for (i=0; i < z->s->img_x; ++i) { - stbi_uc m = coutput[3][i]; - stbi_uc r = stbi__blinn_8x8(coutput[0][i], m); - stbi_uc g = stbi__blinn_8x8(coutput[1][i], m); - stbi_uc b = stbi__blinn_8x8(coutput[2][i], m); - out[0] = stbi__compute_y(r, g, b); - out[1] = 255; - out += n; - } - } else if (z->s->img_n == 4 && z->app14_color_transform == 2) { - for (i=0; i < z->s->img_x; ++i) { - out[0] = stbi__blinn_8x8(255 - coutput[0][i], coutput[3][i]); - out[1] = 255; - out += n; - } - } else { - stbi_uc *y = coutput[0]; - if (n == 1) - for (i=0; i < z->s->img_x; ++i) out[i] = y[i]; - else - for (i=0; i < z->s->img_x; ++i) *out++ = y[i], *out++ = 255; - } - } - } - stbi__cleanup_jpeg(z); - *out_x = z->s->img_x; - *out_y = z->s->img_y; - if (comp) *comp = z->s->img_n >= 3 ? 3 : 1; // report original components, not output - return output; - } -} - -static void *stbi__jpeg_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - unsigned char* result; - stbi__jpeg* j = (stbi__jpeg*) stbi__malloc(sizeof(stbi__jpeg)); - STBI_NOTUSED(ri); - j->s = s; - stbi__setup_jpeg(j); - result = load_jpeg_image(j, x,y,comp,req_comp); - STBI_FREE(j); - return result; -} - -static int stbi__jpeg_test(stbi__context *s) -{ - int r; - stbi__jpeg* j = (stbi__jpeg*)stbi__malloc(sizeof(stbi__jpeg)); - j->s = s; - stbi__setup_jpeg(j); - r = stbi__decode_jpeg_header(j, STBI__SCAN_type); - stbi__rewind(s); - STBI_FREE(j); - return r; -} - -static int stbi__jpeg_info_raw(stbi__jpeg *j, int *x, int *y, int *comp) -{ - if (!stbi__decode_jpeg_header(j, STBI__SCAN_header)) { - stbi__rewind( j->s ); - return 0; - } - if (x) *x = j->s->img_x; - if (y) *y = j->s->img_y; - if (comp) *comp = j->s->img_n >= 3 ? 3 : 1; - return 1; -} - -static int stbi__jpeg_info(stbi__context *s, int *x, int *y, int *comp) -{ - int result; - stbi__jpeg* j = (stbi__jpeg*) (stbi__malloc(sizeof(stbi__jpeg))); - j->s = s; - result = stbi__jpeg_info_raw(j, x, y, comp); - STBI_FREE(j); - return result; -} -#endif - -// public domain zlib decode v0.2 Sean Barrett 2006-11-18 -// simple implementation -// - all input must be provided in an upfront buffer -// - all output is written to a single output buffer (can malloc/realloc) -// performance -// - fast huffman - -#ifndef STBI_NO_ZLIB - -// fast-way is faster to check than jpeg huffman, but slow way is slower -#define STBI__ZFAST_BITS 9 // accelerate all cases in default tables -#define STBI__ZFAST_MASK ((1 << STBI__ZFAST_BITS) - 1) - -// zlib-style huffman encoding -// (jpegs packs from left, zlib from right, so can't share code) -typedef struct -{ - stbi__uint16 fast[1 << STBI__ZFAST_BITS]; - stbi__uint16 firstcode[16]; - int maxcode[17]; - stbi__uint16 firstsymbol[16]; - stbi_uc size[288]; - stbi__uint16 value[288]; -} stbi__zhuffman; - -stbi_inline static int stbi__bitreverse16(int n) -{ - n = ((n & 0xAAAA) >> 1) | ((n & 0x5555) << 1); - n = ((n & 0xCCCC) >> 2) | ((n & 0x3333) << 2); - n = ((n & 0xF0F0) >> 4) | ((n & 0x0F0F) << 4); - n = ((n & 0xFF00) >> 8) | ((n & 0x00FF) << 8); - return n; -} - -stbi_inline static int stbi__bit_reverse(int v, int bits) -{ - STBI_ASSERT(bits <= 16); - // to bit reverse n bits, reverse 16 and shift - // e.g. 11 bits, bit reverse and shift away 5 - return stbi__bitreverse16(v) >> (16-bits); -} - -static int stbi__zbuild_huffman(stbi__zhuffman *z, const stbi_uc *sizelist, int num) -{ - int i,k=0; - int code, next_code[16], sizes[17]; - - // DEFLATE spec for generating codes - memset(sizes, 0, sizeof(sizes)); - memset(z->fast, 0, sizeof(z->fast)); - for (i=0; i < num; ++i) - ++sizes[sizelist[i]]; - sizes[0] = 0; - for (i=1; i < 16; ++i) - if (sizes[i] > (1 << i)) - return stbi__err("bad sizes", "Corrupt PNG"); - code = 0; - for (i=1; i < 16; ++i) { - next_code[i] = code; - z->firstcode[i] = (stbi__uint16) code; - z->firstsymbol[i] = (stbi__uint16) k; - code = (code + sizes[i]); - if (sizes[i]) - if (code-1 >= (1 << i)) return stbi__err("bad codelengths","Corrupt PNG"); - z->maxcode[i] = code << (16-i); // preshift for inner loop - code <<= 1; - k += sizes[i]; - } - z->maxcode[16] = 0x10000; // sentinel - for (i=0; i < num; ++i) { - int s = sizelist[i]; - if (s) { - int c = next_code[s] - z->firstcode[s] + z->firstsymbol[s]; - stbi__uint16 fastv = (stbi__uint16) ((s << 9) | i); - z->size [c] = (stbi_uc ) s; - z->value[c] = (stbi__uint16) i; - if (s <= STBI__ZFAST_BITS) { - int j = stbi__bit_reverse(next_code[s],s); - while (j < (1 << STBI__ZFAST_BITS)) { - z->fast[j] = fastv; - j += (1 << s); - } - } - ++next_code[s]; - } - } - return 1; -} - -// zlib-from-memory implementation for PNG reading -// because PNG allows splitting the zlib stream arbitrarily, -// and it's annoying structurally to have PNG call ZLIB call PNG, -// we require PNG read all the IDATs and combine them into a single -// memory buffer - -typedef struct -{ - stbi_uc *zbuffer, *zbuffer_end; - int num_bits; - stbi__uint32 code_buffer; - - char *zout; - char *zout_start; - char *zout_end; - int z_expandable; - - stbi__zhuffman z_length, z_distance; -} stbi__zbuf; - -stbi_inline static stbi_uc stbi__zget8(stbi__zbuf *z) -{ - if (z->zbuffer >= z->zbuffer_end) return 0; - return *z->zbuffer++; -} - -static void stbi__fill_bits(stbi__zbuf *z) -{ - do { - STBI_ASSERT(z->code_buffer < (1U << z->num_bits)); - z->code_buffer |= (unsigned int) stbi__zget8(z) << z->num_bits; - z->num_bits += 8; - } while (z->num_bits <= 24); -} - -stbi_inline static unsigned int stbi__zreceive(stbi__zbuf *z, int n) -{ - unsigned int k; - if (z->num_bits < n) stbi__fill_bits(z); - k = z->code_buffer & ((1 << n) - 1); - z->code_buffer >>= n; - z->num_bits -= n; - return k; -} - -static int stbi__zhuffman_decode_slowpath(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s,k; - // not resolved by fast table, so compute it the slow way - // use jpeg approach, which requires MSbits at top - k = stbi__bit_reverse(a->code_buffer, 16); - for (s=STBI__ZFAST_BITS+1; ; ++s) - if (k < z->maxcode[s]) - break; - if (s == 16) return -1; // invalid code! - // code size is s, so: - b = (k >> (16-s)) - z->firstcode[s] + z->firstsymbol[s]; - STBI_ASSERT(z->size[b] == s); - a->code_buffer >>= s; - a->num_bits -= s; - return z->value[b]; -} - -stbi_inline static int stbi__zhuffman_decode(stbi__zbuf *a, stbi__zhuffman *z) -{ - int b,s; - if (a->num_bits < 16) stbi__fill_bits(a); - b = z->fast[a->code_buffer & STBI__ZFAST_MASK]; - if (b) { - s = b >> 9; - a->code_buffer >>= s; - a->num_bits -= s; - return b & 511; - } - return stbi__zhuffman_decode_slowpath(a, z); -} - -static int stbi__zexpand(stbi__zbuf *z, char *zout, int n) // need to make room for n bytes -{ - char *q; - int cur, limit, old_limit; - z->zout = zout; - if (!z->z_expandable) return stbi__err("output buffer limit","Corrupt PNG"); - cur = (int) (z->zout - z->zout_start); - limit = old_limit = (int) (z->zout_end - z->zout_start); - while (cur + n > limit) - limit *= 2; - q = (char *) STBI_REALLOC_SIZED(z->zout_start, old_limit, limit); - STBI_NOTUSED(old_limit); - if (q == NULL) return stbi__err("outofmem", "Out of memory"); - z->zout_start = q; - z->zout = q + cur; - z->zout_end = q + limit; - return 1; -} - -static const int stbi__zlength_base[31] = { - 3,4,5,6,7,8,9,10,11,13, - 15,17,19,23,27,31,35,43,51,59, - 67,83,99,115,131,163,195,227,258,0,0 }; - -static const int stbi__zlength_extra[31]= -{ 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; - -static const int stbi__zdist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, -257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; - -static const int stbi__zdist_extra[32] = -{ 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; - -static int stbi__parse_huffman_block(stbi__zbuf *a) -{ - char *zout = a->zout; - for(;;) { - int z = stbi__zhuffman_decode(a, &a->z_length); - if (z < 256) { - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); // error in huffman codes - if (zout >= a->zout_end) { - if (!stbi__zexpand(a, zout, 1)) return 0; - zout = a->zout; - } - *zout++ = (char) z; - } else { - stbi_uc *p; - int len,dist; - if (z == 256) { - a->zout = zout; - return 1; - } - z -= 257; - len = stbi__zlength_base[z]; - if (stbi__zlength_extra[z]) len += stbi__zreceive(a, stbi__zlength_extra[z]); - z = stbi__zhuffman_decode(a, &a->z_distance); - if (z < 0) return stbi__err("bad huffman code","Corrupt PNG"); - dist = stbi__zdist_base[z]; - if (stbi__zdist_extra[z]) dist += stbi__zreceive(a, stbi__zdist_extra[z]); - if (zout - a->zout_start < dist) return stbi__err("bad dist","Corrupt PNG"); - if (zout + len > a->zout_end) { - if (!stbi__zexpand(a, zout, len)) return 0; - zout = a->zout; - } - p = (stbi_uc *) (zout - dist); - if (dist == 1) { // run of one byte; common in images. - stbi_uc v = *p; - if (len) { do *zout++ = v; while (--len); } - } else { - if (len) { do *zout++ = *p++; while (--len); } - } - } - } -} - -static int stbi__compute_huffman_codes(stbi__zbuf *a) -{ - static const stbi_uc length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; - stbi__zhuffman z_codelength; - stbi_uc lencodes[286+32+137];//padding for maximum single op - stbi_uc codelength_sizes[19]; - int i,n; - - int hlit = stbi__zreceive(a,5) + 257; - int hdist = stbi__zreceive(a,5) + 1; - int hclen = stbi__zreceive(a,4) + 4; - int ntot = hlit + hdist; - - memset(codelength_sizes, 0, sizeof(codelength_sizes)); - for (i=0; i < hclen; ++i) { - int s = stbi__zreceive(a,3); - codelength_sizes[length_dezigzag[i]] = (stbi_uc) s; - } - if (!stbi__zbuild_huffman(&z_codelength, codelength_sizes, 19)) return 0; - - n = 0; - while (n < ntot) { - int c = stbi__zhuffman_decode(a, &z_codelength); - if (c < 0 || c >= 19) return stbi__err("bad codelengths", "Corrupt PNG"); - if (c < 16) - lencodes[n++] = (stbi_uc) c; - else { - stbi_uc fill = 0; - if (c == 16) { - c = stbi__zreceive(a,2)+3; - if (n == 0) return stbi__err("bad codelengths", "Corrupt PNG"); - fill = lencodes[n-1]; - } else if (c == 17) - c = stbi__zreceive(a,3)+3; - else { - STBI_ASSERT(c == 18); - c = stbi__zreceive(a,7)+11; - } - if (ntot - n < c) return stbi__err("bad codelengths", "Corrupt PNG"); - memset(lencodes+n, fill, c); - n += c; - } - } - if (n != ntot) return stbi__err("bad codelengths","Corrupt PNG"); - if (!stbi__zbuild_huffman(&a->z_length, lencodes, hlit)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, lencodes+hlit, hdist)) return 0; - return 1; -} - -static int stbi__parse_uncompressed_block(stbi__zbuf *a) -{ - stbi_uc header[4]; - int len,nlen,k; - if (a->num_bits & 7) - stbi__zreceive(a, a->num_bits & 7); // discard - // drain the bit-packed data into header - k = 0; - while (a->num_bits > 0) { - header[k++] = (stbi_uc) (a->code_buffer & 255); // suppress MSVC run-time check - a->code_buffer >>= 8; - a->num_bits -= 8; - } - STBI_ASSERT(a->num_bits == 0); - // now fill header the normal way - while (k < 4) - header[k++] = stbi__zget8(a); - len = header[1] * 256 + header[0]; - nlen = header[3] * 256 + header[2]; - if (nlen != (len ^ 0xffff)) return stbi__err("zlib corrupt","Corrupt PNG"); - if (a->zbuffer + len > a->zbuffer_end) return stbi__err("read past buffer","Corrupt PNG"); - if (a->zout + len > a->zout_end) - if (!stbi__zexpand(a, a->zout, len)) return 0; - memcpy(a->zout, a->zbuffer, len); - a->zbuffer += len; - a->zout += len; - return 1; -} - -static int stbi__parse_zlib_header(stbi__zbuf *a) -{ - int cmf = stbi__zget8(a); - int cm = cmf & 15; - /* int cinfo = cmf >> 4; */ - int flg = stbi__zget8(a); - if ((cmf*256+flg) % 31 != 0) return stbi__err("bad zlib header","Corrupt PNG"); // zlib spec - if (flg & 32) return stbi__err("no preset dict","Corrupt PNG"); // preset dictionary not allowed in png - if (cm != 8) return stbi__err("bad compression","Corrupt PNG"); // DEFLATE required for png - // window = 1 << (8 + cinfo)... but who cares, we fully buffer output - return 1; -} - -static const stbi_uc stbi__zdefault_length[288] = -{ - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, - 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,8,8,8,8,8,8,8,8 -}; -static const stbi_uc stbi__zdefault_distance[32] = -{ - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5 -}; -/* -Init algorithm: -{ - int i; // use <= to match clearly with spec - for (i=0; i <= 143; ++i) stbi__zdefault_length[i] = 8; - for ( ; i <= 255; ++i) stbi__zdefault_length[i] = 9; - for ( ; i <= 279; ++i) stbi__zdefault_length[i] = 7; - for ( ; i <= 287; ++i) stbi__zdefault_length[i] = 8; - - for (i=0; i <= 31; ++i) stbi__zdefault_distance[i] = 5; -} -*/ - -static int stbi__parse_zlib(stbi__zbuf *a, int parse_header) -{ - int final, type; - if (parse_header) - if (!stbi__parse_zlib_header(a)) return 0; - a->num_bits = 0; - a->code_buffer = 0; - do { - final = stbi__zreceive(a,1); - type = stbi__zreceive(a,2); - if (type == 0) { - if (!stbi__parse_uncompressed_block(a)) return 0; - } else if (type == 3) { - return 0; - } else { - if (type == 1) { - // use fixed code lengths - if (!stbi__zbuild_huffman(&a->z_length , stbi__zdefault_length , 288)) return 0; - if (!stbi__zbuild_huffman(&a->z_distance, stbi__zdefault_distance, 32)) return 0; - } else { - if (!stbi__compute_huffman_codes(a)) return 0; - } - if (!stbi__parse_huffman_block(a)) return 0; - } - } while (!final); - return 1; -} - -static int stbi__do_zlib(stbi__zbuf *a, char *obuf, int olen, int exp, int parse_header) -{ - a->zout_start = obuf; - a->zout = obuf; - a->zout_end = obuf + olen; - a->z_expandable = exp; - - return stbi__parse_zlib(a, parse_header); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize(const char *buffer, int len, int initial_size, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, 1)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF char *stbi_zlib_decode_malloc(char const *buffer, int len, int *outlen) -{ - return stbi_zlib_decode_malloc_guesssize(buffer, len, 16384, outlen); -} - -STBIDEF char *stbi_zlib_decode_malloc_guesssize_headerflag(const char *buffer, int len, int initial_size, int *outlen, int parse_header) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(initial_size); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer + len; - if (stbi__do_zlib(&a, p, initial_size, 1, parse_header)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_buffer(char *obuffer, int olen, char const *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 1)) - return (int) (a.zout - a.zout_start); - else - return -1; -} - -STBIDEF char *stbi_zlib_decode_noheader_malloc(char const *buffer, int len, int *outlen) -{ - stbi__zbuf a; - char *p = (char *) stbi__malloc(16384); - if (p == NULL) return NULL; - a.zbuffer = (stbi_uc *) buffer; - a.zbuffer_end = (stbi_uc *) buffer+len; - if (stbi__do_zlib(&a, p, 16384, 1, 0)) { - if (outlen) *outlen = (int) (a.zout - a.zout_start); - return a.zout_start; - } else { - STBI_FREE(a.zout_start); - return NULL; - } -} - -STBIDEF int stbi_zlib_decode_noheader_buffer(char *obuffer, int olen, const char *ibuffer, int ilen) -{ - stbi__zbuf a; - a.zbuffer = (stbi_uc *) ibuffer; - a.zbuffer_end = (stbi_uc *) ibuffer + ilen; - if (stbi__do_zlib(&a, obuffer, olen, 0, 0)) - return (int) (a.zout - a.zout_start); - else - return -1; -} -#endif - -// public domain "baseline" PNG decoder v0.10 Sean Barrett 2006-11-18 -// simple implementation -// - only 8-bit samples -// - no CRC checking -// - allocates lots of intermediate memory -// - avoids problem of streaming data between subsystems -// - avoids explicit window management -// performance -// - uses stb_zlib, a PD zlib implementation with fast huffman decoding - -#ifndef STBI_NO_PNG -typedef struct -{ - stbi__uint32 length; - stbi__uint32 type; -} stbi__pngchunk; - -static stbi__pngchunk stbi__get_chunk_header(stbi__context *s) -{ - stbi__pngchunk c; - c.length = stbi__get32be(s); - c.type = stbi__get32be(s); - return c; -} - -static int stbi__check_png_header(stbi__context *s) -{ - static const stbi_uc png_sig[8] = { 137,80,78,71,13,10,26,10 }; - int i; - for (i=0; i < 8; ++i) - if (stbi__get8(s) != png_sig[i]) return stbi__err("bad png sig","Not a PNG"); - return 1; -} - -typedef struct -{ - stbi__context *s; - stbi_uc *idata, *expanded, *out; - int depth; -} stbi__png; - - -enum { - STBI__F_none=0, - STBI__F_sub=1, - STBI__F_up=2, - STBI__F_avg=3, - STBI__F_paeth=4, - // synthetic filters used for first scanline to avoid needing a dummy row of 0s - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static stbi_uc first_row_filter[5] = -{ - STBI__F_none, - STBI__F_sub, - STBI__F_none, - STBI__F_avg_first, - STBI__F_paeth_first -}; - -static int stbi__paeth(int a, int b, int c) -{ - int p = a + b - c; - int pa = abs(p-a); - int pb = abs(p-b); - int pc = abs(p-c); - if (pa <= pb && pa <= pc) return a; - if (pb <= pc) return b; - return c; -} - -static const stbi_uc stbi__depth_scale_table[9] = { 0, 0xff, 0x55, 0, 0x11, 0,0,0, 0x01 }; - -// create the png data from post-deflated data -static int stbi__create_png_image_raw(stbi__png *a, stbi_uc *raw, stbi__uint32 raw_len, int out_n, stbi__uint32 x, stbi__uint32 y, int depth, int color) -{ - int bytes = (depth == 16? 2 : 1); - stbi__context *s = a->s; - stbi__uint32 i,j,stride = x*out_n*bytes; - stbi__uint32 img_len, img_width_bytes; - int k; - int img_n = s->img_n; // copy it into a local for later - - int output_bytes = out_n*bytes; - int filter_bytes = img_n*bytes; - int width = x; - - STBI_ASSERT(out_n == s->img_n || out_n == s->img_n+1); - a->out = (stbi_uc *) stbi__malloc_mad3(x, y, output_bytes, 0); // extra bytes to write off the end into - if (!a->out) return stbi__err("outofmem", "Out of memory"); - - if (!stbi__mad3sizes_valid(img_n, x, depth, 7)) return stbi__err("too large", "Corrupt PNG"); - img_width_bytes = (((img_n * x * depth) + 7) >> 3); - img_len = (img_width_bytes + 1) * y; - - // we used to check for exact match between raw_len and img_len on non-interlaced PNGs, - // but issue #276 reported a PNG in the wild that had extra data at the end (all zeros), - // so just check for raw_len < img_len always. - if (raw_len < img_len) return stbi__err("not enough pixels","Corrupt PNG"); - - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *prior; - int filter = *raw++; - - if (filter > 4) - return stbi__err("invalid filter","Corrupt PNG"); - - if (depth < 8) { - STBI_ASSERT(img_width_bytes <= x); - cur += x*out_n - img_width_bytes; // store output to the rightmost img_len bytes, so we can decode in place - filter_bytes = 1; - width = img_width_bytes; - } - prior = cur - stride; // bugfix: need to compute this after 'cur +=' computation above - - // if first row, use special filter that doesn't sample previous row - if (j == 0) filter = first_row_filter[filter]; - - // handle first byte explicitly - for (k=0; k < filter_bytes; ++k) { - switch (filter) { - case STBI__F_none : cur[k] = raw[k]; break; - case STBI__F_sub : cur[k] = raw[k]; break; - case STBI__F_up : cur[k] = STBI__BYTECAST(raw[k] + prior[k]); break; - case STBI__F_avg : cur[k] = STBI__BYTECAST(raw[k] + (prior[k]>>1)); break; - case STBI__F_paeth : cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(0,prior[k],0)); break; - case STBI__F_avg_first : cur[k] = raw[k]; break; - case STBI__F_paeth_first: cur[k] = raw[k]; break; - } - } - - if (depth == 8) { - if (img_n != out_n) - cur[img_n] = 255; // first pixel - raw += img_n; - cur += out_n; - prior += out_n; - } else if (depth == 16) { - if (img_n != out_n) { - cur[filter_bytes] = 255; // first pixel top byte - cur[filter_bytes+1] = 255; // first pixel bottom byte - } - raw += filter_bytes; - cur += output_bytes; - prior += output_bytes; - } else { - raw += 1; - cur += 1; - prior += 1; - } - - // this is a little gross, so that we don't switch per-pixel or per-component - if (depth < 8 || img_n == out_n) { - int nk = (width - 1)*filter_bytes; - #define STBI__CASE(f) \ - case f: \ - for (k=0; k < nk; ++k) - switch (filter) { - // "none" filter turns into a memcpy here; make that explicit. - case STBI__F_none: memcpy(cur, raw, nk); break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k-filter_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k-filter_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],prior[k],prior[k-filter_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k-filter_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k-filter_bytes],0,0)); } break; - } - #undef STBI__CASE - raw += nk; - } else { - STBI_ASSERT(img_n+1 == out_n); - #define STBI__CASE(f) \ - case f: \ - for (i=x-1; i >= 1; --i, cur[filter_bytes]=255,raw+=filter_bytes,cur+=output_bytes,prior+=output_bytes) \ - for (k=0; k < filter_bytes; ++k) - switch (filter) { - STBI__CASE(STBI__F_none) { cur[k] = raw[k]; } break; - STBI__CASE(STBI__F_sub) { cur[k] = STBI__BYTECAST(raw[k] + cur[k- output_bytes]); } break; - STBI__CASE(STBI__F_up) { cur[k] = STBI__BYTECAST(raw[k] + prior[k]); } break; - STBI__CASE(STBI__F_avg) { cur[k] = STBI__BYTECAST(raw[k] + ((prior[k] + cur[k- output_bytes])>>1)); } break; - STBI__CASE(STBI__F_paeth) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],prior[k],prior[k- output_bytes])); } break; - STBI__CASE(STBI__F_avg_first) { cur[k] = STBI__BYTECAST(raw[k] + (cur[k- output_bytes] >> 1)); } break; - STBI__CASE(STBI__F_paeth_first) { cur[k] = STBI__BYTECAST(raw[k] + stbi__paeth(cur[k- output_bytes],0,0)); } break; - } - #undef STBI__CASE - - // the loop above sets the high byte of the pixels' alpha, but for - // 16 bit png files we also need the low byte set. we'll do that here. - if (depth == 16) { - cur = a->out + stride*j; // start at the beginning of the row again - for (i=0; i < x; ++i,cur+=output_bytes) { - cur[filter_bytes+1] = 255; - } - } - } - } - - // we make a separate pass to expand bits to pixels; for performance, - // this could run two scanlines behind the above code, so it won't - // intefere with filtering but will still be in the cache. - if (depth < 8) { - for (j=0; j < y; ++j) { - stbi_uc *cur = a->out + stride*j; - stbi_uc *in = a->out + stride*j + x*out_n - img_width_bytes; - // unpack 1/2/4-bit into a 8-bit buffer. allows us to keep the common 8-bit path optimal at minimal cost for 1/2/4-bit - // png guarante byte alignment, if width is not multiple of 8/4/2 we'll decode dummy trailing data that will be skipped in the later loop - stbi_uc scale = (color == 0) ? stbi__depth_scale_table[depth] : 1; // scale grayscale values to 0..255 range - - // note that the final byte might overshoot and write more data than desired. - // we can allocate enough data that this never writes out of memory, but it - // could also overwrite the next scanline. can it overwrite non-empty data - // on the next scanline? yes, consider 1-pixel-wide scanlines with 1-bit-per-pixel. - // so we need to explicitly clamp the final ones - - if (depth == 4) { - for (k=x*img_n; k >= 2; k-=2, ++in) { - *cur++ = scale * ((*in >> 4) ); - *cur++ = scale * ((*in ) & 0x0f); - } - if (k > 0) *cur++ = scale * ((*in >> 4) ); - } else if (depth == 2) { - for (k=x*img_n; k >= 4; k-=4, ++in) { - *cur++ = scale * ((*in >> 6) ); - *cur++ = scale * ((*in >> 4) & 0x03); - *cur++ = scale * ((*in >> 2) & 0x03); - *cur++ = scale * ((*in ) & 0x03); - } - if (k > 0) *cur++ = scale * ((*in >> 6) ); - if (k > 1) *cur++ = scale * ((*in >> 4) & 0x03); - if (k > 2) *cur++ = scale * ((*in >> 2) & 0x03); - } else if (depth == 1) { - for (k=x*img_n; k >= 8; k-=8, ++in) { - *cur++ = scale * ((*in >> 7) ); - *cur++ = scale * ((*in >> 6) & 0x01); - *cur++ = scale * ((*in >> 5) & 0x01); - *cur++ = scale * ((*in >> 4) & 0x01); - *cur++ = scale * ((*in >> 3) & 0x01); - *cur++ = scale * ((*in >> 2) & 0x01); - *cur++ = scale * ((*in >> 1) & 0x01); - *cur++ = scale * ((*in ) & 0x01); - } - if (k > 0) *cur++ = scale * ((*in >> 7) ); - if (k > 1) *cur++ = scale * ((*in >> 6) & 0x01); - if (k > 2) *cur++ = scale * ((*in >> 5) & 0x01); - if (k > 3) *cur++ = scale * ((*in >> 4) & 0x01); - if (k > 4) *cur++ = scale * ((*in >> 3) & 0x01); - if (k > 5) *cur++ = scale * ((*in >> 2) & 0x01); - if (k > 6) *cur++ = scale * ((*in >> 1) & 0x01); - } - if (img_n != out_n) { - int q; - // insert alpha = 255 - cur = a->out + stride*j; - if (img_n == 1) { - for (q=x-1; q >= 0; --q) { - cur[q*2+1] = 255; - cur[q*2+0] = cur[q]; - } - } else { - STBI_ASSERT(img_n == 3); - for (q=x-1; q >= 0; --q) { - cur[q*4+3] = 255; - cur[q*4+2] = cur[q*3+2]; - cur[q*4+1] = cur[q*3+1]; - cur[q*4+0] = cur[q*3+0]; - } - } - } - } - } else if (depth == 16) { - // force the image data from big-endian to platform-native. - // this is done in a separate pass due to the decoding relying - // on the data being untouched, but could probably be done - // per-line during decode if care is taken. - stbi_uc *cur = a->out; - stbi__uint16 *cur16 = (stbi__uint16*)cur; - - for(i=0; i < x*y*out_n; ++i,cur16++,cur+=2) { - *cur16 = (cur[0] << 8) | cur[1]; - } - } - - return 1; -} - -static int stbi__create_png_image(stbi__png *a, stbi_uc *image_data, stbi__uint32 image_data_len, int out_n, int depth, int color, int interlaced) -{ - int bytes = (depth == 16 ? 2 : 1); - int out_bytes = out_n * bytes; - stbi_uc *final; - int p; - if (!interlaced) - return stbi__create_png_image_raw(a, image_data, image_data_len, out_n, a->s->img_x, a->s->img_y, depth, color); - - // de-interlacing - final = (stbi_uc *) stbi__malloc_mad3(a->s->img_x, a->s->img_y, out_bytes, 0); - for (p=0; p < 7; ++p) { - int xorig[] = { 0,4,0,2,0,1,0 }; - int yorig[] = { 0,0,4,0,2,0,1 }; - int xspc[] = { 8,8,4,4,2,2,1 }; - int yspc[] = { 8,8,8,4,4,2,2 }; - int i,j,x,y; - // pass1_x[4] = 0, pass1_x[5] = 1, pass1_x[12] = 1 - x = (a->s->img_x - xorig[p] + xspc[p]-1) / xspc[p]; - y = (a->s->img_y - yorig[p] + yspc[p]-1) / yspc[p]; - if (x && y) { - stbi__uint32 img_len = ((((a->s->img_n * x * depth) + 7) >> 3) + 1) * y; - if (!stbi__create_png_image_raw(a, image_data, image_data_len, out_n, x, y, depth, color)) { - STBI_FREE(final); - return 0; - } - for (j=0; j < y; ++j) { - for (i=0; i < x; ++i) { - int out_y = j*yspc[p]+yorig[p]; - int out_x = i*xspc[p]+xorig[p]; - memcpy(final + out_y*a->s->img_x*out_bytes + out_x*out_bytes, - a->out + (j*x+i)*out_bytes, out_bytes); - } - } - STBI_FREE(a->out); - image_data += img_len; - image_data_len -= img_len; - } - } - a->out = final; - - return 1; -} - -static int stbi__compute_transparency(stbi__png *z, stbi_uc tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - // compute color-based transparency, assuming we've - // already got 255 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i=0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 255); - p += 2; - } - } else { - for (i=0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__compute_transparency16(stbi__png *z, stbi__uint16 tc[3], int out_n) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi__uint16 *p = (stbi__uint16*) z->out; - - // compute color-based transparency, assuming we've - // already got 65535 as the alpha value in the output - STBI_ASSERT(out_n == 2 || out_n == 4); - - if (out_n == 2) { - for (i = 0; i < pixel_count; ++i) { - p[1] = (p[0] == tc[0] ? 0 : 65535); - p += 2; - } - } else { - for (i = 0; i < pixel_count; ++i) { - if (p[0] == tc[0] && p[1] == tc[1] && p[2] == tc[2]) - p[3] = 0; - p += 4; - } - } - return 1; -} - -static int stbi__expand_png_palette(stbi__png *a, stbi_uc *palette, int len, int pal_img_n) -{ - stbi__uint32 i, pixel_count = a->s->img_x * a->s->img_y; - stbi_uc *p, *temp_out, *orig = a->out; - - p = (stbi_uc *) stbi__malloc_mad2(pixel_count, pal_img_n, 0); - if (p == NULL) return stbi__err("outofmem", "Out of memory"); - - // between here and free(out) below, exitting would leak - temp_out = p; - - if (pal_img_n == 3) { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p += 3; - } - } else { - for (i=0; i < pixel_count; ++i) { - int n = orig[i]*4; - p[0] = palette[n ]; - p[1] = palette[n+1]; - p[2] = palette[n+2]; - p[3] = palette[n+3]; - p += 4; - } - } - STBI_FREE(a->out); - a->out = temp_out; - - STBI_NOTUSED(len); - - return 1; -} - -static int stbi__unpremultiply_on_load = 0; -static int stbi__de_iphone_flag = 0; - -STBIDEF void stbi_set_unpremultiply_on_load(int flag_true_if_should_unpremultiply) -{ - stbi__unpremultiply_on_load = flag_true_if_should_unpremultiply; -} - -STBIDEF void stbi_convert_iphone_png_to_rgb(int flag_true_if_should_convert) -{ - stbi__de_iphone_flag = flag_true_if_should_convert; -} - -static void stbi__de_iphone(stbi__png *z) -{ - stbi__context *s = z->s; - stbi__uint32 i, pixel_count = s->img_x * s->img_y; - stbi_uc *p = z->out; - - if (s->img_out_n == 3) { // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 3; - } - } else { - STBI_ASSERT(s->img_out_n == 4); - if (stbi__unpremultiply_on_load) { - // convert bgr to rgb and unpremultiply - for (i=0; i < pixel_count; ++i) { - stbi_uc a = p[3]; - stbi_uc t = p[0]; - if (a) { - stbi_uc half = a / 2; - p[0] = (p[2] * 255 + half) / a; - p[1] = (p[1] * 255 + half) / a; - p[2] = ( t * 255 + half) / a; - } else { - p[0] = p[2]; - p[2] = t; - } - p += 4; - } - } else { - // convert bgr to rgb - for (i=0; i < pixel_count; ++i) { - stbi_uc t = p[0]; - p[0] = p[2]; - p[2] = t; - p += 4; - } - } - } -} - -#define STBI__PNG_TYPE(a,b,c,d) (((unsigned) (a) << 24) + ((unsigned) (b) << 16) + ((unsigned) (c) << 8) + (unsigned) (d)) - -static int stbi__parse_png_file(stbi__png *z, int scan, int req_comp) -{ - stbi_uc palette[1024], pal_img_n=0; - stbi_uc has_trans=0, tc[3]; - stbi__uint16 tc16[3]; - stbi__uint32 ioff=0, idata_limit=0, i, pal_len=0; - int first=1,k,interlace=0, color=0, is_iphone=0; - stbi__context *s = z->s; - - z->expanded = NULL; - z->idata = NULL; - z->out = NULL; - - if (!stbi__check_png_header(s)) return 0; - - if (scan == STBI__SCAN_type) return 1; - - for (;;) { - stbi__pngchunk c = stbi__get_chunk_header(s); - switch (c.type) { - case STBI__PNG_TYPE('C','g','B','I'): - is_iphone = 1; - stbi__skip(s, c.length); - break; - case STBI__PNG_TYPE('I','H','D','R'): { - int comp,filter; - if (!first) return stbi__err("multiple IHDR","Corrupt PNG"); - first = 0; - if (c.length != 13) return stbi__err("bad IHDR len","Corrupt PNG"); - s->img_x = stbi__get32be(s); if (s->img_x > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); - s->img_y = stbi__get32be(s); if (s->img_y > (1 << 24)) return stbi__err("too large","Very large image (corrupt?)"); - z->depth = stbi__get8(s); if (z->depth != 1 && z->depth != 2 && z->depth != 4 && z->depth != 8 && z->depth != 16) return stbi__err("1/2/4/8/16-bit only","PNG not supported: 1/2/4/8/16-bit only"); - color = stbi__get8(s); if (color > 6) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3 && z->depth == 16) return stbi__err("bad ctype","Corrupt PNG"); - if (color == 3) pal_img_n = 3; else if (color & 1) return stbi__err("bad ctype","Corrupt PNG"); - comp = stbi__get8(s); if (comp) return stbi__err("bad comp method","Corrupt PNG"); - filter= stbi__get8(s); if (filter) return stbi__err("bad filter method","Corrupt PNG"); - interlace = stbi__get8(s); if (interlace>1) return stbi__err("bad interlace method","Corrupt PNG"); - if (!s->img_x || !s->img_y) return stbi__err("0-pixel image","Corrupt PNG"); - if (!pal_img_n) { - s->img_n = (color & 2 ? 3 : 1) + (color & 4 ? 1 : 0); - if ((1 << 30) / s->img_x / s->img_n < s->img_y) return stbi__err("too large", "Image too large to decode"); - if (scan == STBI__SCAN_header) return 1; - } else { - // if paletted, then pal_n is our final components, and - // img_n is # components to decompress/filter. - s->img_n = 1; - if ((1 << 30) / s->img_x / 4 < s->img_y) return stbi__err("too large","Corrupt PNG"); - // if SCAN_header, have to scan to see if we have a tRNS - } - break; - } - - case STBI__PNG_TYPE('P','L','T','E'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (c.length > 256*3) return stbi__err("invalid PLTE","Corrupt PNG"); - pal_len = c.length / 3; - if (pal_len * 3 != c.length) return stbi__err("invalid PLTE","Corrupt PNG"); - for (i=0; i < pal_len; ++i) { - palette[i*4+0] = stbi__get8(s); - palette[i*4+1] = stbi__get8(s); - palette[i*4+2] = stbi__get8(s); - palette[i*4+3] = 255; - } - break; - } - - case STBI__PNG_TYPE('t','R','N','S'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (z->idata) return stbi__err("tRNS after IDAT","Corrupt PNG"); - if (pal_img_n) { - if (scan == STBI__SCAN_header) { s->img_n = 4; return 1; } - if (pal_len == 0) return stbi__err("tRNS before PLTE","Corrupt PNG"); - if (c.length > pal_len) return stbi__err("bad tRNS len","Corrupt PNG"); - pal_img_n = 4; - for (i=0; i < c.length; ++i) - palette[i*4+3] = stbi__get8(s); - } else { - if (!(s->img_n & 1)) return stbi__err("tRNS with alpha","Corrupt PNG"); - if (c.length != (stbi__uint32) s->img_n*2) return stbi__err("bad tRNS len","Corrupt PNG"); - has_trans = 1; - if (z->depth == 16) { - for (k = 0; k < s->img_n; ++k) tc16[k] = (stbi__uint16)stbi__get16be(s); // copy the values as-is - } else { - for (k = 0; k < s->img_n; ++k) tc[k] = (stbi_uc)(stbi__get16be(s) & 255) * stbi__depth_scale_table[z->depth]; // non 8-bit images will be larger - } - } - break; - } - - case STBI__PNG_TYPE('I','D','A','T'): { - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (pal_img_n && !pal_len) return stbi__err("no PLTE","Corrupt PNG"); - if (scan == STBI__SCAN_header) { s->img_n = pal_img_n; return 1; } - if ((int)(ioff + c.length) < (int)ioff) return 0; - if (ioff + c.length > idata_limit) { - stbi__uint32 idata_limit_old = idata_limit; - stbi_uc *p; - if (idata_limit == 0) idata_limit = c.length > 4096 ? c.length : 4096; - while (ioff + c.length > idata_limit) - idata_limit *= 2; - STBI_NOTUSED(idata_limit_old); - p = (stbi_uc *) STBI_REALLOC_SIZED(z->idata, idata_limit_old, idata_limit); if (p == NULL) return stbi__err("outofmem", "Out of memory"); - z->idata = p; - } - if (!stbi__getn(s, z->idata+ioff,c.length)) return stbi__err("outofdata","Corrupt PNG"); - ioff += c.length; - break; - } - - case STBI__PNG_TYPE('I','E','N','D'): { - stbi__uint32 raw_len, bpl; - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if (scan != STBI__SCAN_load) return 1; - if (z->idata == NULL) return stbi__err("no IDAT","Corrupt PNG"); - // initial guess for decoded data size to avoid unnecessary reallocs - bpl = (s->img_x * z->depth + 7) / 8; // bytes per line, per component - raw_len = bpl * s->img_y * s->img_n /* pixels */ + s->img_y /* filter mode per row */; - z->expanded = (stbi_uc *) stbi_zlib_decode_malloc_guesssize_headerflag((char *) z->idata, ioff, raw_len, (int *) &raw_len, !is_iphone); - if (z->expanded == NULL) return 0; // zlib should set error - STBI_FREE(z->idata); z->idata = NULL; - if ((req_comp == s->img_n+1 && req_comp != 3 && !pal_img_n) || has_trans) - s->img_out_n = s->img_n+1; - else - s->img_out_n = s->img_n; - if (!stbi__create_png_image(z, z->expanded, raw_len, s->img_out_n, z->depth, color, interlace)) return 0; - if (has_trans) { - if (z->depth == 16) { - if (!stbi__compute_transparency16(z, tc16, s->img_out_n)) return 0; - } else { - if (!stbi__compute_transparency(z, tc, s->img_out_n)) return 0; - } - } - if (is_iphone && stbi__de_iphone_flag && s->img_out_n > 2) - stbi__de_iphone(z); - if (pal_img_n) { - // pal_img_n == 3 or 4 - s->img_n = pal_img_n; // record the actual colors we had - s->img_out_n = pal_img_n; - if (req_comp >= 3) s->img_out_n = req_comp; - if (!stbi__expand_png_palette(z, palette, pal_len, s->img_out_n)) - return 0; - } else if (has_trans) { - // non-paletted image with tRNS -> source image has (constant) alpha - ++s->img_n; - } - STBI_FREE(z->expanded); z->expanded = NULL; - return 1; - } - - default: - // if critical, fail - if (first) return stbi__err("first not IHDR", "Corrupt PNG"); - if ((c.type & (1 << 29)) == 0) { - #ifndef STBI_NO_FAILURE_STRINGS - // not threadsafe - static char invalid_chunk[] = "XXXX PNG chunk not known"; - invalid_chunk[0] = STBI__BYTECAST(c.type >> 24); - invalid_chunk[1] = STBI__BYTECAST(c.type >> 16); - invalid_chunk[2] = STBI__BYTECAST(c.type >> 8); - invalid_chunk[3] = STBI__BYTECAST(c.type >> 0); - #endif - return stbi__err(invalid_chunk, "PNG not supported: unknown PNG chunk type"); - } - stbi__skip(s, c.length); - break; - } - // end of PNG chunk, read and skip CRC - stbi__get32be(s); - } -} - -static void *stbi__do_png(stbi__png *p, int *x, int *y, int *n, int req_comp, stbi__result_info *ri) -{ - void *result=NULL; - if (req_comp < 0 || req_comp > 4) return stbi__errpuc("bad req_comp", "Internal error"); - if (stbi__parse_png_file(p, STBI__SCAN_load, req_comp)) { - if (p->depth < 8) - ri->bits_per_channel = 8; - else - ri->bits_per_channel = p->depth; - result = p->out; - p->out = NULL; - if (req_comp && req_comp != p->s->img_out_n) { - if (ri->bits_per_channel == 8) - result = stbi__convert_format((unsigned char *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - else - result = stbi__convert_format16((stbi__uint16 *) result, p->s->img_out_n, req_comp, p->s->img_x, p->s->img_y); - p->s->img_out_n = req_comp; - if (result == NULL) return result; - } - *x = p->s->img_x; - *y = p->s->img_y; - if (n) *n = p->s->img_n; - } - STBI_FREE(p->out); p->out = NULL; - STBI_FREE(p->expanded); p->expanded = NULL; - STBI_FREE(p->idata); p->idata = NULL; - - return result; -} - -static void *stbi__png_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi__png p; - p.s = s; - return stbi__do_png(&p, x,y,comp,req_comp, ri); -} - -static int stbi__png_test(stbi__context *s) -{ - int r; - r = stbi__check_png_header(s); - stbi__rewind(s); - return r; -} - -static int stbi__png_info_raw(stbi__png *p, int *x, int *y, int *comp) -{ - if (!stbi__parse_png_file(p, STBI__SCAN_header, 0)) { - stbi__rewind( p->s ); - return 0; - } - if (x) *x = p->s->img_x; - if (y) *y = p->s->img_y; - if (comp) *comp = p->s->img_n; - return 1; -} - -static int stbi__png_info(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__png p; - p.s = s; - return stbi__png_info_raw(&p, x, y, comp); -} - -static int stbi__png_is16(stbi__context *s) -{ - stbi__png p; - p.s = s; - if (!stbi__png_info_raw(&p, NULL, NULL, NULL)) - return 0; - if (p.depth != 16) { - stbi__rewind(p.s); - return 0; - } - return 1; -} -#endif - -// Microsoft/Windows BMP image - -#ifndef STBI_NO_BMP -static int stbi__bmp_test_raw(stbi__context *s) -{ - int r; - int sz; - if (stbi__get8(s) != 'B') return 0; - if (stbi__get8(s) != 'M') return 0; - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - stbi__get32le(s); // discard data offset - sz = stbi__get32le(s); - r = (sz == 12 || sz == 40 || sz == 56 || sz == 108 || sz == 124); - return r; -} - -static int stbi__bmp_test(stbi__context *s) -{ - int r = stbi__bmp_test_raw(s); - stbi__rewind(s); - return r; -} - - -// returns 0..31 for the highest set bit -static int stbi__high_bit(unsigned int z) -{ - int n=0; - if (z == 0) return -1; - if (z >= 0x10000) n += 16, z >>= 16; - if (z >= 0x00100) n += 8, z >>= 8; - if (z >= 0x00010) n += 4, z >>= 4; - if (z >= 0x00004) n += 2, z >>= 2; - if (z >= 0x00002) n += 1, z >>= 1; - return n; -} - -static int stbi__bitcount(unsigned int a) -{ - a = (a & 0x55555555) + ((a >> 1) & 0x55555555); // max 2 - a = (a & 0x33333333) + ((a >> 2) & 0x33333333); // max 4 - a = (a + (a >> 4)) & 0x0f0f0f0f; // max 8 per 4, now 8 bits - a = (a + (a >> 8)); // max 16 per 8 bits - a = (a + (a >> 16)); // max 32 per 8 bits - return a & 0xff; -} - -// extract an arbitrarily-aligned N-bit value (N=bits) -// from v, and then make it 8-bits long and fractionally -// extend it to full full range. -static int stbi__shiftsigned(int v, int shift, int bits) -{ - static unsigned int mul_table[9] = { - 0, - 0xff/*0b11111111*/, 0x55/*0b01010101*/, 0x49/*0b01001001*/, 0x11/*0b00010001*/, - 0x21/*0b00100001*/, 0x41/*0b01000001*/, 0x81/*0b10000001*/, 0x01/*0b00000001*/, - }; - static unsigned int shift_table[9] = { - 0, 0,0,1,0,2,4,6,0, - }; - if (shift < 0) - v <<= -shift; - else - v >>= shift; - STBI_ASSERT(v >= 0 && v < 256); - v >>= (8-bits); - STBI_ASSERT(bits >= 0 && bits <= 8); - return (int) ((unsigned) v * mul_table[bits]) >> shift_table[bits]; -} - -typedef struct -{ - int bpp, offset, hsz; - unsigned int mr,mg,mb,ma, all_a; -} stbi__bmp_data; - -static void *stbi__bmp_parse_header(stbi__context *s, stbi__bmp_data *info) -{ - int hsz; - if (stbi__get8(s) != 'B' || stbi__get8(s) != 'M') return stbi__errpuc("not BMP", "Corrupt BMP"); - stbi__get32le(s); // discard filesize - stbi__get16le(s); // discard reserved - stbi__get16le(s); // discard reserved - info->offset = stbi__get32le(s); - info->hsz = hsz = stbi__get32le(s); - info->mr = info->mg = info->mb = info->ma = 0; - - if (hsz != 12 && hsz != 40 && hsz != 56 && hsz != 108 && hsz != 124) return stbi__errpuc("unknown BMP", "BMP type not supported: unknown"); - if (hsz == 12) { - s->img_x = stbi__get16le(s); - s->img_y = stbi__get16le(s); - } else { - s->img_x = stbi__get32le(s); - s->img_y = stbi__get32le(s); - } - if (stbi__get16le(s) != 1) return stbi__errpuc("bad BMP", "bad BMP"); - info->bpp = stbi__get16le(s); - if (hsz != 12) { - int compress = stbi__get32le(s); - if (compress == 1 || compress == 2) return stbi__errpuc("BMP RLE", "BMP type not supported: RLE"); - stbi__get32le(s); // discard sizeof - stbi__get32le(s); // discard hres - stbi__get32le(s); // discard vres - stbi__get32le(s); // discard colorsused - stbi__get32le(s); // discard max important - if (hsz == 40 || hsz == 56) { - if (hsz == 56) { - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - stbi__get32le(s); - } - if (info->bpp == 16 || info->bpp == 32) { - if (compress == 0) { - if (info->bpp == 32) { - info->mr = 0xffu << 16; - info->mg = 0xffu << 8; - info->mb = 0xffu << 0; - info->ma = 0xffu << 24; - info->all_a = 0; // if all_a is 0 at end, then we loaded alpha channel but it was all 0 - } else { - info->mr = 31u << 10; - info->mg = 31u << 5; - info->mb = 31u << 0; - } - } else if (compress == 3) { - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - // not documented, but generated by photoshop and handled by mspaint - if (info->mr == info->mg && info->mg == info->mb) { - // ?!?!? - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else - return stbi__errpuc("bad BMP", "bad BMP"); - } - } else { - int i; - if (hsz != 108 && hsz != 124) - return stbi__errpuc("bad BMP", "bad BMP"); - info->mr = stbi__get32le(s); - info->mg = stbi__get32le(s); - info->mb = stbi__get32le(s); - info->ma = stbi__get32le(s); - stbi__get32le(s); // discard color space - for (i=0; i < 12; ++i) - stbi__get32le(s); // discard color space parameters - if (hsz == 124) { - stbi__get32le(s); // discard rendering intent - stbi__get32le(s); // discard offset of profile data - stbi__get32le(s); // discard size of profile data - stbi__get32le(s); // discard reserved - } - } - } - return (void *) 1; -} - - -static void *stbi__bmp_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - unsigned int mr=0,mg=0,mb=0,ma=0, all_a; - stbi_uc pal[256][4]; - int psize=0,i,j,width; - int flip_vertically, pad, target; - stbi__bmp_data info; - STBI_NOTUSED(ri); - - info.all_a = 255; - if (stbi__bmp_parse_header(s, &info) == NULL) - return NULL; // error code already set - - flip_vertically = ((int) s->img_y) > 0; - s->img_y = abs((int) s->img_y); - - mr = info.mr; - mg = info.mg; - mb = info.mb; - ma = info.ma; - all_a = info.all_a; - - if (info.hsz == 12) { - if (info.bpp < 24) - psize = (info.offset - 14 - 24) / 3; - } else { - if (info.bpp < 16) - psize = (info.offset - 14 - info.hsz) >> 2; - } - - s->img_n = ma ? 4 : 3; - if (req_comp && req_comp >= 3) // we can directly decode 3 or 4 - target = req_comp; - else - target = s->img_n; // if they want monochrome, we'll post-convert - - // sanity-check size - if (!stbi__mad3sizes_valid(target, s->img_x, s->img_y, 0)) - return stbi__errpuc("too large", "Corrupt BMP"); - - out = (stbi_uc *) stbi__malloc_mad3(target, s->img_x, s->img_y, 0); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - if (info.bpp < 16) { - int z=0; - if (psize == 0 || psize > 256) { STBI_FREE(out); return stbi__errpuc("invalid", "Corrupt BMP"); } - for (i=0; i < psize; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - if (info.hsz != 12) stbi__get8(s); - pal[i][3] = 255; - } - stbi__skip(s, info.offset - 14 - info.hsz - psize * (info.hsz == 12 ? 3 : 4)); - if (info.bpp == 1) width = (s->img_x + 7) >> 3; - else if (info.bpp == 4) width = (s->img_x + 1) >> 1; - else if (info.bpp == 8) width = s->img_x; - else { STBI_FREE(out); return stbi__errpuc("bad bpp", "Corrupt BMP"); } - pad = (-width)&3; - if (info.bpp == 1) { - for (j=0; j < (int) s->img_y; ++j) { - int bit_offset = 7, v = stbi__get8(s); - for (i=0; i < (int) s->img_x; ++i) { - int color = (v>>bit_offset)&0x1; - out[z++] = pal[color][0]; - out[z++] = pal[color][1]; - out[z++] = pal[color][2]; - if((--bit_offset) < 0) { - bit_offset = 7; - v = stbi__get8(s); - } - } - stbi__skip(s, pad); - } - } else { - for (j=0; j < (int) s->img_y; ++j) { - for (i=0; i < (int) s->img_x; i += 2) { - int v=stbi__get8(s),v2=0; - if (info.bpp == 4) { - v2 = v & 15; - v >>= 4; - } - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - if (i+1 == (int) s->img_x) break; - v = (info.bpp == 8) ? stbi__get8(s) : v2; - out[z++] = pal[v][0]; - out[z++] = pal[v][1]; - out[z++] = pal[v][2]; - if (target == 4) out[z++] = 255; - } - stbi__skip(s, pad); - } - } - } else { - int rshift=0,gshift=0,bshift=0,ashift=0,rcount=0,gcount=0,bcount=0,acount=0; - int z = 0; - int easy=0; - stbi__skip(s, info.offset - 14 - info.hsz); - if (info.bpp == 24) width = 3 * s->img_x; - else if (info.bpp == 16) width = 2*s->img_x; - else /* bpp = 32 and pad = 0 */ width=0; - pad = (-width) & 3; - if (info.bpp == 24) { - easy = 1; - } else if (info.bpp == 32) { - if (mb == 0xff && mg == 0xff00 && mr == 0x00ff0000 && ma == 0xff000000) - easy = 2; - } - if (!easy) { - if (!mr || !mg || !mb) { STBI_FREE(out); return stbi__errpuc("bad masks", "Corrupt BMP"); } - // right shift amt to put high bit in position #7 - rshift = stbi__high_bit(mr)-7; rcount = stbi__bitcount(mr); - gshift = stbi__high_bit(mg)-7; gcount = stbi__bitcount(mg); - bshift = stbi__high_bit(mb)-7; bcount = stbi__bitcount(mb); - ashift = stbi__high_bit(ma)-7; acount = stbi__bitcount(ma); - } - for (j=0; j < (int) s->img_y; ++j) { - if (easy) { - for (i=0; i < (int) s->img_x; ++i) { - unsigned char a; - out[z+2] = stbi__get8(s); - out[z+1] = stbi__get8(s); - out[z+0] = stbi__get8(s); - z += 3; - a = (easy == 2 ? stbi__get8(s) : 255); - all_a |= a; - if (target == 4) out[z++] = a; - } - } else { - int bpp = info.bpp; - for (i=0; i < (int) s->img_x; ++i) { - stbi__uint32 v = (bpp == 16 ? (stbi__uint32) stbi__get16le(s) : stbi__get32le(s)); - unsigned int a; - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mr, rshift, rcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mg, gshift, gcount)); - out[z++] = STBI__BYTECAST(stbi__shiftsigned(v & mb, bshift, bcount)); - a = (ma ? stbi__shiftsigned(v & ma, ashift, acount) : 255); - all_a |= a; - if (target == 4) out[z++] = STBI__BYTECAST(a); - } - } - stbi__skip(s, pad); - } - } - - // if alpha channel is all 0s, replace with all 255s - if (target == 4 && all_a == 0) - for (i=4*s->img_x*s->img_y-1; i >= 0; i -= 4) - out[i] = 255; - - if (flip_vertically) { - stbi_uc t; - for (j=0; j < (int) s->img_y>>1; ++j) { - stbi_uc *p1 = out + j *s->img_x*target; - stbi_uc *p2 = out + (s->img_y-1-j)*s->img_x*target; - for (i=0; i < (int) s->img_x*target; ++i) { - t = p1[i], p1[i] = p2[i], p2[i] = t; - } - } - } - - if (req_comp && req_comp != target) { - out = stbi__convert_format(out, target, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - return out; -} -#endif - -// Targa Truevision - TGA -// by Jonathan Dummer -#ifndef STBI_NO_TGA -// returns STBI_rgb or whatever, 0 on error -static int stbi__tga_get_comp(int bits_per_pixel, int is_grey, int* is_rgb16) -{ - // only RGB or RGBA (incl. 16bit) or grey allowed - if (is_rgb16) *is_rgb16 = 0; - switch(bits_per_pixel) { - case 8: return STBI_grey; - case 16: if(is_grey) return STBI_grey_alpha; - // fallthrough - case 15: if(is_rgb16) *is_rgb16 = 1; - return STBI_rgb; - case 24: // fallthrough - case 32: return bits_per_pixel/8; - default: return 0; - } -} - -static int stbi__tga_info(stbi__context *s, int *x, int *y, int *comp) -{ - int tga_w, tga_h, tga_comp, tga_image_type, tga_bits_per_pixel, tga_colormap_bpp; - int sz, tga_colormap_type; - stbi__get8(s); // discard Offset - tga_colormap_type = stbi__get8(s); // colormap type - if( tga_colormap_type > 1 ) { - stbi__rewind(s); - return 0; // only RGB or indexed allowed - } - tga_image_type = stbi__get8(s); // image type - if ( tga_colormap_type == 1 ) { // colormapped (paletted) image - if (tga_image_type != 1 && tga_image_type != 9) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) { - stbi__rewind(s); - return 0; - } - stbi__skip(s,4); // skip image x and y origin - tga_colormap_bpp = sz; - } else { // "normal" image w/o colormap - only RGB or grey allowed, +/- RLE - if ( (tga_image_type != 2) && (tga_image_type != 3) && (tga_image_type != 10) && (tga_image_type != 11) ) { - stbi__rewind(s); - return 0; // only RGB or grey allowed, +/- RLE - } - stbi__skip(s,9); // skip colormap specification and image x/y origin - tga_colormap_bpp = 0; - } - tga_w = stbi__get16le(s); - if( tga_w < 1 ) { - stbi__rewind(s); - return 0; // test width - } - tga_h = stbi__get16le(s); - if( tga_h < 1 ) { - stbi__rewind(s); - return 0; // test height - } - tga_bits_per_pixel = stbi__get8(s); // bits per pixel - stbi__get8(s); // ignore alpha bits - if (tga_colormap_bpp != 0) { - if((tga_bits_per_pixel != 8) && (tga_bits_per_pixel != 16)) { - // when using a colormap, tga_bits_per_pixel is the size of the indexes - // I don't think anything but 8 or 16bit indexes makes sense - stbi__rewind(s); - return 0; - } - tga_comp = stbi__tga_get_comp(tga_colormap_bpp, 0, NULL); - } else { - tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3) || (tga_image_type == 11), NULL); - } - if(!tga_comp) { - stbi__rewind(s); - return 0; - } - if (x) *x = tga_w; - if (y) *y = tga_h; - if (comp) *comp = tga_comp; - return 1; // seems to have passed everything -} - -static int stbi__tga_test(stbi__context *s) -{ - int res = 0; - int sz, tga_color_type; - stbi__get8(s); // discard Offset - tga_color_type = stbi__get8(s); // color type - if ( tga_color_type > 1 ) goto errorEnd; // only RGB or indexed allowed - sz = stbi__get8(s); // image type - if ( tga_color_type == 1 ) { // colormapped (paletted) image - if (sz != 1 && sz != 9) goto errorEnd; // colortype 1 demands image type 1 or 9 - stbi__skip(s,4); // skip index of first colormap entry and number of entries - sz = stbi__get8(s); // check bits per palette color entry - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - stbi__skip(s,4); // skip image x and y origin - } else { // "normal" image w/o colormap - if ( (sz != 2) && (sz != 3) && (sz != 10) && (sz != 11) ) goto errorEnd; // only RGB or grey allowed, +/- RLE - stbi__skip(s,9); // skip colormap specification and image x/y origin - } - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test width - if ( stbi__get16le(s) < 1 ) goto errorEnd; // test height - sz = stbi__get8(s); // bits per pixel - if ( (tga_color_type == 1) && (sz != 8) && (sz != 16) ) goto errorEnd; // for colormapped images, bpp is size of an index - if ( (sz != 8) && (sz != 15) && (sz != 16) && (sz != 24) && (sz != 32) ) goto errorEnd; - - res = 1; // if we got this far, everything's good and we can return 1 instead of 0 - -errorEnd: - stbi__rewind(s); - return res; -} - -// read 16bit value and convert to 24bit RGB -static void stbi__tga_read_rgb16(stbi__context *s, stbi_uc* out) -{ - stbi__uint16 px = (stbi__uint16)stbi__get16le(s); - stbi__uint16 fiveBitMask = 31; - // we have 3 channels with 5bits each - int r = (px >> 10) & fiveBitMask; - int g = (px >> 5) & fiveBitMask; - int b = px & fiveBitMask; - // Note that this saves the data in RGB(A) order, so it doesn't need to be swapped later - out[0] = (stbi_uc)((r * 255)/31); - out[1] = (stbi_uc)((g * 255)/31); - out[2] = (stbi_uc)((b * 255)/31); - - // some people claim that the most significant bit might be used for alpha - // (possibly if an alpha-bit is set in the "image descriptor byte") - // but that only made 16bit test images completely translucent.. - // so let's treat all 15 and 16bit TGAs as RGB with no alpha. -} - -static void *stbi__tga_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - // read in the TGA header stuff - int tga_offset = stbi__get8(s); - int tga_indexed = stbi__get8(s); - int tga_image_type = stbi__get8(s); - int tga_is_RLE = 0; - int tga_palette_start = stbi__get16le(s); - int tga_palette_len = stbi__get16le(s); - int tga_palette_bits = stbi__get8(s); - int tga_x_origin = stbi__get16le(s); - int tga_y_origin = stbi__get16le(s); - int tga_width = stbi__get16le(s); - int tga_height = stbi__get16le(s); - int tga_bits_per_pixel = stbi__get8(s); - int tga_comp, tga_rgb16=0; - int tga_inverted = stbi__get8(s); - // int tga_alpha_bits = tga_inverted & 15; // the 4 lowest bits - unused (useless?) - // image data - unsigned char *tga_data; - unsigned char *tga_palette = NULL; - int i, j; - unsigned char raw_data[4] = {0}; - int RLE_count = 0; - int RLE_repeating = 0; - int read_next_pixel = 1; - STBI_NOTUSED(ri); - - // do a tiny bit of precessing - if ( tga_image_type >= 8 ) - { - tga_image_type -= 8; - tga_is_RLE = 1; - } - tga_inverted = 1 - ((tga_inverted >> 5) & 1); - - // If I'm paletted, then I'll use the number of bits from the palette - if ( tga_indexed ) tga_comp = stbi__tga_get_comp(tga_palette_bits, 0, &tga_rgb16); - else tga_comp = stbi__tga_get_comp(tga_bits_per_pixel, (tga_image_type == 3), &tga_rgb16); - - if(!tga_comp) // shouldn't really happen, stbi__tga_test() should have ensured basic consistency - return stbi__errpuc("bad format", "Can't find out TGA pixelformat"); - - // tga info - *x = tga_width; - *y = tga_height; - if (comp) *comp = tga_comp; - - if (!stbi__mad3sizes_valid(tga_width, tga_height, tga_comp, 0)) - return stbi__errpuc("too large", "Corrupt TGA"); - - tga_data = (unsigned char*)stbi__malloc_mad3(tga_width, tga_height, tga_comp, 0); - if (!tga_data) return stbi__errpuc("outofmem", "Out of memory"); - - // skip to the data's starting position (offset usually = 0) - stbi__skip(s, tga_offset ); - - if ( !tga_indexed && !tga_is_RLE && !tga_rgb16 ) { - for (i=0; i < tga_height; ++i) { - int row = tga_inverted ? tga_height -i - 1 : i; - stbi_uc *tga_row = tga_data + row*tga_width*tga_comp; - stbi__getn(s, tga_row, tga_width * tga_comp); - } - } else { - // do I need to load a palette? - if ( tga_indexed) - { - // any data to skip? (offset usually = 0) - stbi__skip(s, tga_palette_start ); - // load the palette - tga_palette = (unsigned char*)stbi__malloc_mad2(tga_palette_len, tga_comp, 0); - if (!tga_palette) { - STBI_FREE(tga_data); - return stbi__errpuc("outofmem", "Out of memory"); - } - if (tga_rgb16) { - stbi_uc *pal_entry = tga_palette; - STBI_ASSERT(tga_comp == STBI_rgb); - for (i=0; i < tga_palette_len; ++i) { - stbi__tga_read_rgb16(s, pal_entry); - pal_entry += tga_comp; - } - } else if (!stbi__getn(s, tga_palette, tga_palette_len * tga_comp)) { - STBI_FREE(tga_data); - STBI_FREE(tga_palette); - return stbi__errpuc("bad palette", "Corrupt TGA"); - } - } - // load the data - for (i=0; i < tga_width * tga_height; ++i) - { - // if I'm in RLE mode, do I need to get a RLE stbi__pngchunk? - if ( tga_is_RLE ) - { - if ( RLE_count == 0 ) - { - // yep, get the next byte as a RLE command - int RLE_cmd = stbi__get8(s); - RLE_count = 1 + (RLE_cmd & 127); - RLE_repeating = RLE_cmd >> 7; - read_next_pixel = 1; - } else if ( !RLE_repeating ) - { - read_next_pixel = 1; - } - } else - { - read_next_pixel = 1; - } - // OK, if I need to read a pixel, do it now - if ( read_next_pixel ) - { - // load however much data we did have - if ( tga_indexed ) - { - // read in index, then perform the lookup - int pal_idx = (tga_bits_per_pixel == 8) ? stbi__get8(s) : stbi__get16le(s); - if ( pal_idx >= tga_palette_len ) { - // invalid index - pal_idx = 0; - } - pal_idx *= tga_comp; - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = tga_palette[pal_idx+j]; - } - } else if(tga_rgb16) { - STBI_ASSERT(tga_comp == STBI_rgb); - stbi__tga_read_rgb16(s, raw_data); - } else { - // read in the data raw - for (j = 0; j < tga_comp; ++j) { - raw_data[j] = stbi__get8(s); - } - } - // clear the reading flag for the next pixel - read_next_pixel = 0; - } // end of reading a pixel - - // copy data - for (j = 0; j < tga_comp; ++j) - tga_data[i*tga_comp+j] = raw_data[j]; - - // in case we're in RLE mode, keep counting down - --RLE_count; - } - // do I need to invert the image? - if ( tga_inverted ) - { - for (j = 0; j*2 < tga_height; ++j) - { - int index1 = j * tga_width * tga_comp; - int index2 = (tga_height - 1 - j) * tga_width * tga_comp; - for (i = tga_width * tga_comp; i > 0; --i) - { - unsigned char temp = tga_data[index1]; - tga_data[index1] = tga_data[index2]; - tga_data[index2] = temp; - ++index1; - ++index2; - } - } - } - // clear my palette, if I had one - if ( tga_palette != NULL ) - { - STBI_FREE( tga_palette ); - } - } - - // swap RGB - if the source data was RGB16, it already is in the right order - if (tga_comp >= 3 && !tga_rgb16) - { - unsigned char* tga_pixel = tga_data; - for (i=0; i < tga_width * tga_height; ++i) - { - unsigned char temp = tga_pixel[0]; - tga_pixel[0] = tga_pixel[2]; - tga_pixel[2] = temp; - tga_pixel += tga_comp; - } - } - - // convert to target component count - if (req_comp && req_comp != tga_comp) - tga_data = stbi__convert_format(tga_data, tga_comp, req_comp, tga_width, tga_height); - - // the things I do to get rid of an error message, and yet keep - // Microsoft's C compilers happy... [8^( - tga_palette_start = tga_palette_len = tga_palette_bits = - tga_x_origin = tga_y_origin = 0; - // OK, done - return tga_data; -} -#endif - -// ************************************************************************************************* -// Photoshop PSD loader -- PD by Thatcher Ulrich, integration by Nicolas Schulz, tweaked by STB - -#ifndef STBI_NO_PSD -static int stbi__psd_test(stbi__context *s) -{ - int r = (stbi__get32be(s) == 0x38425053); - stbi__rewind(s); - return r; -} - -static int stbi__psd_decode_rle(stbi__context *s, stbi_uc *p, int pixelCount) -{ - int count, nleft, len; - - count = 0; - while ((nleft = pixelCount - count) > 0) { - len = stbi__get8(s); - if (len == 128) { - // No-op. - } else if (len < 128) { - // Copy next len+1 bytes literally. - len++; - if (len > nleft) return 0; // corrupt data - count += len; - while (len) { - *p = stbi__get8(s); - p += 4; - len--; - } - } else if (len > 128) { - stbi_uc val; - // Next -len+1 bytes in the dest are replicated from next source byte. - // (Interpret len as a negative 8-bit int.) - len = 257 - len; - if (len > nleft) return 0; // corrupt data - val = stbi__get8(s); - count += len; - while (len) { - *p = val; - p += 4; - len--; - } - } - } - - return 1; -} - -static void *stbi__psd_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri, int bpc) -{ - int pixelCount; - int channelCount, compression; - int channel, i; - int bitdepth; - int w,h; - stbi_uc *out; - STBI_NOTUSED(ri); - - // Check identifier - if (stbi__get32be(s) != 0x38425053) // "8BPS" - return stbi__errpuc("not PSD", "Corrupt PSD image"); - - // Check file type version. - if (stbi__get16be(s) != 1) - return stbi__errpuc("wrong version", "Unsupported version of PSD image"); - - // Skip 6 reserved bytes. - stbi__skip(s, 6 ); - - // Read the number of channels (R, G, B, A, etc). - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) - return stbi__errpuc("wrong channel count", "Unsupported number of channels in PSD image"); - - // Read the rows and columns of the image. - h = stbi__get32be(s); - w = stbi__get32be(s); - - // Make sure the depth is 8 bits. - bitdepth = stbi__get16be(s); - if (bitdepth != 8 && bitdepth != 16) - return stbi__errpuc("unsupported bit depth", "PSD bit depth is not 8 or 16 bit"); - - // Make sure the color mode is RGB. - // Valid options are: - // 0: Bitmap - // 1: Grayscale - // 2: Indexed color - // 3: RGB color - // 4: CMYK color - // 7: Multichannel - // 8: Duotone - // 9: Lab color - if (stbi__get16be(s) != 3) - return stbi__errpuc("wrong color format", "PSD is not in RGB color format"); - - // Skip the Mode Data. (It's the palette for indexed color; other info for other modes.) - stbi__skip(s,stbi__get32be(s) ); - - // Skip the image resources. (resolution, pen tool paths, etc) - stbi__skip(s, stbi__get32be(s) ); - - // Skip the reserved data. - stbi__skip(s, stbi__get32be(s) ); - - // Find out if the data is compressed. - // Known values: - // 0: no compression - // 1: RLE compressed - compression = stbi__get16be(s); - if (compression > 1) - return stbi__errpuc("bad compression", "PSD has an unknown compression format"); - - // Check size - if (!stbi__mad3sizes_valid(4, w, h, 0)) - return stbi__errpuc("too large", "Corrupt PSD"); - - // Create the destination image. - - if (!compression && bitdepth == 16 && bpc == 16) { - out = (stbi_uc *) stbi__malloc_mad3(8, w, h, 0); - ri->bits_per_channel = 16; - } else - out = (stbi_uc *) stbi__malloc(4 * w*h); - - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - pixelCount = w*h; - - // Initialize the data to zero. - //memset( out, 0, pixelCount * 4 ); - - // Finally, the image data. - if (compression) { - // RLE as used by .PSD and .TIFF - // Loop until you get the number of unpacked bytes you are expecting: - // Read the next source byte into n. - // If n is between 0 and 127 inclusive, copy the next n+1 bytes literally. - // Else if n is between -127 and -1 inclusive, copy the next byte -n+1 times. - // Else if n is 128, noop. - // Endloop - - // The RLE-compressed data is preceeded by a 2-byte data count for each row in the data, - // which we're going to just skip. - stbi__skip(s, h * channelCount * 2 ); - - // Read the RLE data by channel. - for (channel = 0; channel < 4; channel++) { - stbi_uc *p; - - p = out+channel; - if (channel >= channelCount) { - // Fill this channel with default data. - for (i = 0; i < pixelCount; i++, p += 4) - *p = (channel == 3 ? 255 : 0); - } else { - // Read the RLE data. - if (!stbi__psd_decode_rle(s, p, pixelCount)) { - STBI_FREE(out); - return stbi__errpuc("corrupt", "bad RLE data"); - } - } - } - - } else { - // We're at the raw image data. It's each channel in order (Red, Green, Blue, Alpha, ...) - // where each channel consists of an 8-bit (or 16-bit) value for each pixel in the image. - - // Read the data by channel. - for (channel = 0; channel < 4; channel++) { - if (channel >= channelCount) { - // Fill this channel with default data. - if (bitdepth == 16 && bpc == 16) { - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - stbi__uint16 val = channel == 3 ? 65535 : 0; - for (i = 0; i < pixelCount; i++, q += 4) - *q = val; - } else { - stbi_uc *p = out+channel; - stbi_uc val = channel == 3 ? 255 : 0; - for (i = 0; i < pixelCount; i++, p += 4) - *p = val; - } - } else { - if (ri->bits_per_channel == 16) { // output bpc - stbi__uint16 *q = ((stbi__uint16 *) out) + channel; - for (i = 0; i < pixelCount; i++, q += 4) - *q = (stbi__uint16) stbi__get16be(s); - } else { - stbi_uc *p = out+channel; - if (bitdepth == 16) { // input bpc - for (i = 0; i < pixelCount; i++, p += 4) - *p = (stbi_uc) (stbi__get16be(s) >> 8); - } else { - for (i = 0; i < pixelCount; i++, p += 4) - *p = stbi__get8(s); - } - } - } - } - } - - // remove weird white matte from PSD - if (channelCount >= 4) { - if (ri->bits_per_channel == 16) { - for (i=0; i < w*h; ++i) { - stbi__uint16 *pixel = (stbi__uint16 *) out + 4*i; - if (pixel[3] != 0 && pixel[3] != 65535) { - float a = pixel[3] / 65535.0f; - float ra = 1.0f / a; - float inv_a = 65535.0f * (1 - ra); - pixel[0] = (stbi__uint16) (pixel[0]*ra + inv_a); - pixel[1] = (stbi__uint16) (pixel[1]*ra + inv_a); - pixel[2] = (stbi__uint16) (pixel[2]*ra + inv_a); - } - } - } else { - for (i=0; i < w*h; ++i) { - unsigned char *pixel = out + 4*i; - if (pixel[3] != 0 && pixel[3] != 255) { - float a = pixel[3] / 255.0f; - float ra = 1.0f / a; - float inv_a = 255.0f * (1 - ra); - pixel[0] = (unsigned char) (pixel[0]*ra + inv_a); - pixel[1] = (unsigned char) (pixel[1]*ra + inv_a); - pixel[2] = (unsigned char) (pixel[2]*ra + inv_a); - } - } - } - } - - // convert to desired output format - if (req_comp && req_comp != 4) { - if (ri->bits_per_channel == 16) - out = (stbi_uc *) stbi__convert_format16((stbi__uint16 *) out, 4, req_comp, w, h); - else - out = stbi__convert_format(out, 4, req_comp, w, h); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - - if (comp) *comp = 4; - *y = h; - *x = w; - - return out; -} -#endif - -// ************************************************************************************************* -// Softimage PIC loader -// by Tom Seddon -// -// See http://softimage.wiki.softimage.com/index.php/INFO:_PIC_file_format -// See http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/softimagepic/ - -#ifndef STBI_NO_PIC -static int stbi__pic_is4(stbi__context *s,const char *str) -{ - int i; - for (i=0; i<4; ++i) - if (stbi__get8(s) != (stbi_uc)str[i]) - return 0; - - return 1; -} - -static int stbi__pic_test_core(stbi__context *s) -{ - int i; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) - return 0; - - for(i=0;i<84;++i) - stbi__get8(s); - - if (!stbi__pic_is4(s,"PICT")) - return 0; - - return 1; -} - -typedef struct -{ - stbi_uc size,type,channel; -} stbi__pic_packet; - -static stbi_uc *stbi__readval(stbi__context *s, int channel, stbi_uc *dest) -{ - int mask=0x80, i; - - for (i=0; i<4; ++i, mask>>=1) { - if (channel & mask) { - if (stbi__at_eof(s)) return stbi__errpuc("bad file","PIC file too short"); - dest[i]=stbi__get8(s); - } - } - - return dest; -} - -static void stbi__copyval(int channel,stbi_uc *dest,const stbi_uc *src) -{ - int mask=0x80,i; - - for (i=0;i<4; ++i, mask>>=1) - if (channel&mask) - dest[i]=src[i]; -} - -static stbi_uc *stbi__pic_load_core(stbi__context *s,int width,int height,int *comp, stbi_uc *result) -{ - int act_comp=0,num_packets=0,y,chained; - stbi__pic_packet packets[10]; - - // this will (should...) cater for even some bizarre stuff like having data - // for the same channel in multiple packets. - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return stbi__errpuc("bad format","too many packets"); - - packet = &packets[num_packets++]; - - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - - act_comp |= packet->channel; - - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (reading packets)"); - if (packet->size != 8) return stbi__errpuc("bad format","packet isn't 8bpp"); - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); // has alpha channel? - - for(y=0; ytype) { - default: - return stbi__errpuc("bad format","packet has bad compression type"); - - case 0: {//uncompressed - int x; - - for(x=0;xchannel,dest)) - return 0; - break; - } - - case 1://Pure RLE - { - int left=width, i; - - while (left>0) { - stbi_uc count,value[4]; - - count=stbi__get8(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pure read count)"); - - if (count > left) - count = (stbi_uc) left; - - if (!stbi__readval(s,packet->channel,value)) return 0; - - for(i=0; ichannel,dest,value); - left -= count; - } - } - break; - - case 2: {//Mixed RLE - int left=width; - while (left>0) { - int count = stbi__get8(s), i; - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (mixed read count)"); - - if (count >= 128) { // Repeated - stbi_uc value[4]; - - if (count==128) - count = stbi__get16be(s); - else - count -= 127; - if (count > left) - return stbi__errpuc("bad file","scanline overrun"); - - if (!stbi__readval(s,packet->channel,value)) - return 0; - - for(i=0;ichannel,dest,value); - } else { // Raw - ++count; - if (count>left) return stbi__errpuc("bad file","scanline overrun"); - - for(i=0;ichannel,dest)) - return 0; - } - left-=count; - } - break; - } - } - } - } - - return result; -} - -static void *stbi__pic_load(stbi__context *s,int *px,int *py,int *comp,int req_comp, stbi__result_info *ri) -{ - stbi_uc *result; - int i, x,y, internal_comp; - STBI_NOTUSED(ri); - - if (!comp) comp = &internal_comp; - - for (i=0; i<92; ++i) - stbi__get8(s); - - x = stbi__get16be(s); - y = stbi__get16be(s); - if (stbi__at_eof(s)) return stbi__errpuc("bad file","file too short (pic header)"); - if (!stbi__mad3sizes_valid(x, y, 4, 0)) return stbi__errpuc("too large", "PIC image too large to decode"); - - stbi__get32be(s); //skip `ratio' - stbi__get16be(s); //skip `fields' - stbi__get16be(s); //skip `pad' - - // intermediate buffer is RGBA - result = (stbi_uc *) stbi__malloc_mad3(x, y, 4, 0); - memset(result, 0xff, x*y*4); - - if (!stbi__pic_load_core(s,x,y,comp, result)) { - STBI_FREE(result); - result=0; - } - *px = x; - *py = y; - if (req_comp == 0) req_comp = *comp; - result=stbi__convert_format(result,4,req_comp,x,y); - - return result; -} - -static int stbi__pic_test(stbi__context *s) -{ - int r = stbi__pic_test_core(s); - stbi__rewind(s); - return r; -} -#endif - -// ************************************************************************************************* -// GIF loader -- public domain by Jean-Marc Lienher -- simplified/shrunk by stb - -#ifndef STBI_NO_GIF -typedef struct -{ - stbi__int16 prefix; - stbi_uc first; - stbi_uc suffix; -} stbi__gif_lzw; - -typedef struct -{ - int w,h; - stbi_uc *out; // output buffer (always 4 components) - stbi_uc *background; // The current "background" as far as a gif is concerned - stbi_uc *history; - int flags, bgindex, ratio, transparent, eflags; - stbi_uc pal[256][4]; - stbi_uc lpal[256][4]; - stbi__gif_lzw codes[8192]; - stbi_uc *color_table; - int parse, step; - int lflags; - int start_x, start_y; - int max_x, max_y; - int cur_x, cur_y; - int line_size; - int delay; -} stbi__gif; - -static int stbi__gif_test_raw(stbi__context *s) -{ - int sz; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') return 0; - sz = stbi__get8(s); - if (sz != '9' && sz != '7') return 0; - if (stbi__get8(s) != 'a') return 0; - return 1; -} - -static int stbi__gif_test(stbi__context *s) -{ - int r = stbi__gif_test_raw(s); - stbi__rewind(s); - return r; -} - -static void stbi__gif_parse_colortable(stbi__context *s, stbi_uc pal[256][4], int num_entries, int transp) -{ - int i; - for (i=0; i < num_entries; ++i) { - pal[i][2] = stbi__get8(s); - pal[i][1] = stbi__get8(s); - pal[i][0] = stbi__get8(s); - pal[i][3] = transp == i ? 0 : 255; - } -} - -static int stbi__gif_header(stbi__context *s, stbi__gif *g, int *comp, int is_info) -{ - stbi_uc version; - if (stbi__get8(s) != 'G' || stbi__get8(s) != 'I' || stbi__get8(s) != 'F' || stbi__get8(s) != '8') - return stbi__err("not GIF", "Corrupt GIF"); - - version = stbi__get8(s); - if (version != '7' && version != '9') return stbi__err("not GIF", "Corrupt GIF"); - if (stbi__get8(s) != 'a') return stbi__err("not GIF", "Corrupt GIF"); - - stbi__g_failure_reason = ""; - g->w = stbi__get16le(s); - g->h = stbi__get16le(s); - g->flags = stbi__get8(s); - g->bgindex = stbi__get8(s); - g->ratio = stbi__get8(s); - g->transparent = -1; - - if (comp != 0) *comp = 4; // can't actually tell whether it's 3 or 4 until we parse the comments - - if (is_info) return 1; - - if (g->flags & 0x80) - stbi__gif_parse_colortable(s,g->pal, 2 << (g->flags & 7), -1); - - return 1; -} - -static int stbi__gif_info_raw(stbi__context *s, int *x, int *y, int *comp) -{ - stbi__gif* g = (stbi__gif*) stbi__malloc(sizeof(stbi__gif)); - if (!stbi__gif_header(s, g, comp, 1)) { - STBI_FREE(g); - stbi__rewind( s ); - return 0; - } - if (x) *x = g->w; - if (y) *y = g->h; - STBI_FREE(g); - return 1; -} - -static void stbi__out_gif_code(stbi__gif *g, stbi__uint16 code) -{ - stbi_uc *p, *c; - int idx; - - // recurse to decode the prefixes, since the linked-list is backwards, - // and working backwards through an interleaved image would be nasty - if (g->codes[code].prefix >= 0) - stbi__out_gif_code(g, g->codes[code].prefix); - - if (g->cur_y >= g->max_y) return; - - idx = g->cur_x + g->cur_y; - p = &g->out[idx]; - g->history[idx / 4] = 1; - - c = &g->color_table[g->codes[code].suffix * 4]; - if (c[3] > 128) { // don't render transparent pixels; - p[0] = c[2]; - p[1] = c[1]; - p[2] = c[0]; - p[3] = c[3]; - } - g->cur_x += 4; - - if (g->cur_x >= g->max_x) { - g->cur_x = g->start_x; - g->cur_y += g->step; - - while (g->cur_y >= g->max_y && g->parse > 0) { - g->step = (1 << g->parse) * g->line_size; - g->cur_y = g->start_y + (g->step >> 1); - --g->parse; - } - } -} - -static stbi_uc *stbi__process_gif_raster(stbi__context *s, stbi__gif *g) -{ - stbi_uc lzw_cs; - stbi__int32 len, init_code; - stbi__uint32 first; - stbi__int32 codesize, codemask, avail, oldcode, bits, valid_bits, clear; - stbi__gif_lzw *p; - - lzw_cs = stbi__get8(s); - if (lzw_cs > 12) return NULL; - clear = 1 << lzw_cs; - first = 1; - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - bits = 0; - valid_bits = 0; - for (init_code = 0; init_code < clear; init_code++) { - g->codes[init_code].prefix = -1; - g->codes[init_code].first = (stbi_uc) init_code; - g->codes[init_code].suffix = (stbi_uc) init_code; - } - - // support no starting clear code - avail = clear+2; - oldcode = -1; - - len = 0; - for(;;) { - if (valid_bits < codesize) { - if (len == 0) { - len = stbi__get8(s); // start new block - if (len == 0) - return g->out; - } - --len; - bits |= (stbi__int32) stbi__get8(s) << valid_bits; - valid_bits += 8; - } else { - stbi__int32 code = bits & codemask; - bits >>= codesize; - valid_bits -= codesize; - // @OPTIMIZE: is there some way we can accelerate the non-clear path? - if (code == clear) { // clear code - codesize = lzw_cs + 1; - codemask = (1 << codesize) - 1; - avail = clear + 2; - oldcode = -1; - first = 0; - } else if (code == clear + 1) { // end of stream code - stbi__skip(s, len); - while ((len = stbi__get8(s)) > 0) - stbi__skip(s,len); - return g->out; - } else if (code <= avail) { - if (first) { - return stbi__errpuc("no clear code", "Corrupt GIF"); - } - - if (oldcode >= 0) { - p = &g->codes[avail++]; - if (avail > 8192) { - return stbi__errpuc("too many codes", "Corrupt GIF"); - } - - p->prefix = (stbi__int16) oldcode; - p->first = g->codes[oldcode].first; - p->suffix = (code == avail) ? p->first : g->codes[code].first; - } else if (code == avail) - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - - stbi__out_gif_code(g, (stbi__uint16) code); - - if ((avail & codemask) == 0 && avail <= 0x0FFF) { - codesize++; - codemask = (1 << codesize) - 1; - } - - oldcode = code; - } else { - return stbi__errpuc("illegal code in raster", "Corrupt GIF"); - } - } - } -} - -// this function is designed to support animated gifs, although stb_image doesn't support it -// two back is the image from two frames ago, used for a very specific disposal format -static stbi_uc *stbi__gif_load_next(stbi__context *s, stbi__gif *g, int *comp, int req_comp, stbi_uc *two_back) -{ - int dispose; - int first_frame; - int pi; - int pcount; - - // on first frame, any non-written pixels get the background colour (non-transparent) - first_frame = 0; - if (g->out == 0) { - if (!stbi__gif_header(s, g, comp,0)) return 0; // stbi__g_failure_reason set by stbi__gif_header - g->out = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - g->background = (stbi_uc *) stbi__malloc(4 * g->w * g->h); - g->history = (stbi_uc *) stbi__malloc(g->w * g->h); - if (g->out == 0) return stbi__errpuc("outofmem", "Out of memory"); - - // image is treated as "tranparent" at the start - ie, nothing overwrites the current background; - // background colour is only used for pixels that are not rendered first frame, after that "background" - // color refers to teh color that was there the previous frame. - memset( g->out, 0x00, 4 * g->w * g->h ); - memset( g->background, 0x00, 4 * g->w * g->h ); // state of the background (starts transparent) - memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame - first_frame = 1; - } else { - // second frame - how do we dispoase of the previous one? - dispose = (g->eflags & 0x1C) >> 2; - pcount = g->w * g->h; - - if ((dispose == 3) && (two_back == 0)) { - dispose = 2; // if I don't have an image to revert back to, default to the old background - } - - if (dispose == 3) { // use previous graphic - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &two_back[pi * 4], 4 ); - } - } - } else if (dispose == 2) { - // restore what was changed last frame to background before that frame; - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi]) { - memcpy( &g->out[pi * 4], &g->background[pi * 4], 4 ); - } - } - } else { - // This is a non-disposal case eithe way, so just - // leave the pixels as is, and they will become the new background - // 1: do not dispose - // 0: not specified. - } - - // background is what out is after the undoing of the previou frame; - memcpy( g->background, g->out, 4 * g->w * g->h ); - } - - // clear my history; - memset( g->history, 0x00, g->w * g->h ); // pixels that were affected previous frame - - for (;;) { - int tag = stbi__get8(s); - switch (tag) { - case 0x2C: /* Image Descriptor */ - { - stbi__int32 x, y, w, h; - stbi_uc *o; - - x = stbi__get16le(s); - y = stbi__get16le(s); - w = stbi__get16le(s); - h = stbi__get16le(s); - if (((x + w) > (g->w)) || ((y + h) > (g->h))) - return stbi__errpuc("bad Image Descriptor", "Corrupt GIF"); - - g->line_size = g->w * 4; - g->start_x = x * 4; - g->start_y = y * g->line_size; - g->max_x = g->start_x + w * 4; - g->max_y = g->start_y + h * g->line_size; - g->cur_x = g->start_x; - g->cur_y = g->start_y; - - g->lflags = stbi__get8(s); - - if (g->lflags & 0x40) { - g->step = 8 * g->line_size; // first interlaced spacing - g->parse = 3; - } else { - g->step = g->line_size; - g->parse = 0; - } - - if (g->lflags & 0x80) { - stbi__gif_parse_colortable(s,g->lpal, 2 << (g->lflags & 7), g->eflags & 0x01 ? g->transparent : -1); - g->color_table = (stbi_uc *) g->lpal; - } else if (g->flags & 0x80) { - g->color_table = (stbi_uc *) g->pal; - } else - return stbi__errpuc("missing color table", "Corrupt GIF"); - - o = stbi__process_gif_raster(s, g); - if (o == NULL) return NULL; - - // if this was the first frame, - pcount = g->w * g->h; - if (first_frame && (g->bgindex > 0)) { - // if first frame, any pixel not drawn to gets the background color - for (pi = 0; pi < pcount; ++pi) { - if (g->history[pi] == 0) { - g->pal[g->bgindex][3] = 255; // just in case it was made transparent, undo that; It will be reset next frame if need be; - memcpy( &g->out[pi * 4], &g->pal[g->bgindex], 4 ); - } - } - } - - return o; - } - - case 0x21: // Comment Extension. - { - int len; - int ext = stbi__get8(s); - if (ext == 0xF9) { // Graphic Control Extension. - len = stbi__get8(s); - if (len == 4) { - g->eflags = stbi__get8(s); - g->delay = 10 * stbi__get16le(s); // delay - 1/100th of a second, saving as 1/1000ths. - - // unset old transparent - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 255; - } - if (g->eflags & 0x01) { - g->transparent = stbi__get8(s); - if (g->transparent >= 0) { - g->pal[g->transparent][3] = 0; - } - } else { - // don't need transparent - stbi__skip(s, 1); - g->transparent = -1; - } - } else { - stbi__skip(s, len); - break; - } - } - while ((len = stbi__get8(s)) != 0) { - stbi__skip(s, len); - } - break; - } - - case 0x3B: // gif stream termination code - return (stbi_uc *) s; // using '1' causes warning on some compilers - - default: - return stbi__errpuc("unknown code", "Corrupt GIF"); - } - } -} - -static void *stbi__load_gif_main(stbi__context *s, int **delays, int *x, int *y, int *z, int *comp, int req_comp) -{ - if (stbi__gif_test(s)) { - int layers = 0; - stbi_uc *u = 0; - stbi_uc *out = 0; - stbi_uc *two_back = 0; - stbi__gif g; - int stride; - memset(&g, 0, sizeof(g)); - if (delays) { - *delays = 0; - } - - do { - u = stbi__gif_load_next(s, &g, comp, req_comp, two_back); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - - if (u) { - *x = g.w; - *y = g.h; - ++layers; - stride = g.w * g.h * 4; - - if (out) { - out = (stbi_uc*) STBI_REALLOC( out, layers * stride ); - if (delays) { - *delays = (int*) STBI_REALLOC( *delays, sizeof(int) * layers ); - } - } else { - out = (stbi_uc*)stbi__malloc( layers * stride ); - if (delays) { - *delays = (int*) stbi__malloc( layers * sizeof(int) ); - } - } - memcpy( out + ((layers - 1) * stride), u, stride ); - if (layers >= 2) { - two_back = out - 2 * stride; - } - - if (delays) { - (*delays)[layers - 1U] = g.delay; - } - } - } while (u != 0); - - // free temp buffer; - STBI_FREE(g.out); - STBI_FREE(g.history); - STBI_FREE(g.background); - - // do the final conversion after loading everything; - if (req_comp && req_comp != 4) - out = stbi__convert_format(out, 4, req_comp, layers * g.w, g.h); - - *z = layers; - return out; - } else { - return stbi__errpuc("not GIF", "Image was not as a gif type."); - } -} - -static void *stbi__gif_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *u = 0; - stbi__gif g; - memset(&g, 0, sizeof(g)); - - u = stbi__gif_load_next(s, &g, comp, req_comp, 0); - if (u == (stbi_uc *) s) u = 0; // end of animated gif marker - if (u) { - *x = g.w; - *y = g.h; - - // moved conversion to after successful load so that the same - // can be done for multiple frames. - if (req_comp && req_comp != 4) - u = stbi__convert_format(u, 4, req_comp, g.w, g.h); - } - - // free buffers needed for multiple frame loading; - STBI_FREE(g.history); - STBI_FREE(g.background); - - return u; -} - -static int stbi__gif_info(stbi__context *s, int *x, int *y, int *comp) -{ - return stbi__gif_info_raw(s,x,y,comp); -} -#endif - -// ************************************************************************************************* -// Radiance RGBE HDR loader -// originally by Nicolas Schulz -#ifndef STBI_NO_HDR -static int stbi__hdr_test_core(stbi__context *s, const char *signature) -{ - int i; - for (i=0; signature[i]; ++i) - if (stbi__get8(s) != signature[i]) - return 0; - stbi__rewind(s); - return 1; -} - -static int stbi__hdr_test(stbi__context* s) -{ - int r = stbi__hdr_test_core(s, "#?RADIANCE\n"); - stbi__rewind(s); - if(!r) { - r = stbi__hdr_test_core(s, "#?RGBE\n"); - stbi__rewind(s); - } - return r; -} - -#define STBI__HDR_BUFLEN 1024 -static char *stbi__hdr_gettoken(stbi__context *z, char *buffer) -{ - int len=0; - char c = '\0'; - - c = (char) stbi__get8(z); - - while (!stbi__at_eof(z) && c != '\n') { - buffer[len++] = c; - if (len == STBI__HDR_BUFLEN-1) { - // flush to end of line - while (!stbi__at_eof(z) && stbi__get8(z) != '\n') - ; - break; - } - c = (char) stbi__get8(z); - } - - buffer[len] = 0; - return buffer; -} - -static void stbi__hdr_convert(float *output, stbi_uc *input, int req_comp) -{ - if ( input[3] != 0 ) { - float f1; - // Exponent - f1 = (float) ldexp(1.0f, input[3] - (int)(128 + 8)); - if (req_comp <= 2) - output[0] = (input[0] + input[1] + input[2]) * f1 / 3; - else { - output[0] = input[0] * f1; - output[1] = input[1] * f1; - output[2] = input[2] * f1; - } - if (req_comp == 2) output[1] = 1; - if (req_comp == 4) output[3] = 1; - } else { - switch (req_comp) { - case 4: output[3] = 1; /* fallthrough */ - case 3: output[0] = output[1] = output[2] = 0; - break; - case 2: output[1] = 1; /* fallthrough */ - case 1: output[0] = 0; - break; - } - } -} - -static float *stbi__hdr_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int width, height; - stbi_uc *scanline; - float *hdr_data; - int len; - unsigned char count, value; - int i, j, k, c1,c2, z; - const char *headerToken; - STBI_NOTUSED(ri); - - // Check identifier - headerToken = stbi__hdr_gettoken(s,buffer); - if (strcmp(headerToken, "#?RADIANCE") != 0 && strcmp(headerToken, "#?RGBE") != 0) - return stbi__errpf("not HDR", "Corrupt HDR image"); - - // Parse header - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) return stbi__errpf("unsupported format", "Unsupported HDR format"); - - // Parse width and height - // can't use sscanf() if we're not using stdio! - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - height = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) return stbi__errpf("unsupported data layout", "Unsupported HDR format"); - token += 3; - width = (int) strtol(token, NULL, 10); - - *x = width; - *y = height; - - if (comp) *comp = 3; - if (req_comp == 0) req_comp = 3; - - if (!stbi__mad4sizes_valid(width, height, req_comp, sizeof(float), 0)) - return stbi__errpf("too large", "HDR image is too large"); - - // Read data - hdr_data = (float *) stbi__malloc_mad4(width, height, req_comp, sizeof(float), 0); - if (!hdr_data) - return stbi__errpf("outofmem", "Out of memory"); - - // Load image data - // image data is stored as some number of sca - if ( width < 8 || width >= 32768) { - // Read flat data - for (j=0; j < height; ++j) { - for (i=0; i < width; ++i) { - stbi_uc rgbe[4]; - main_decode_loop: - stbi__getn(s, rgbe, 4); - stbi__hdr_convert(hdr_data + j * width * req_comp + i * req_comp, rgbe, req_comp); - } - } - } else { - // Read RLE-encoded data - scanline = NULL; - - for (j = 0; j < height; ++j) { - c1 = stbi__get8(s); - c2 = stbi__get8(s); - len = stbi__get8(s); - if (c1 != 2 || c2 != 2 || (len & 0x80)) { - // not run-length encoded, so we have to actually use THIS data as a decoded - // pixel (note this can't be a valid pixel--one of RGB must be >= 128) - stbi_uc rgbe[4]; - rgbe[0] = (stbi_uc) c1; - rgbe[1] = (stbi_uc) c2; - rgbe[2] = (stbi_uc) len; - rgbe[3] = (stbi_uc) stbi__get8(s); - stbi__hdr_convert(hdr_data, rgbe, req_comp); - i = 1; - j = 0; - STBI_FREE(scanline); - goto main_decode_loop; // yes, this makes no sense - } - len <<= 8; - len |= stbi__get8(s); - if (len != width) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("invalid decoded scanline length", "corrupt HDR"); } - if (scanline == NULL) { - scanline = (stbi_uc *) stbi__malloc_mad2(width, 4, 0); - if (!scanline) { - STBI_FREE(hdr_data); - return stbi__errpf("outofmem", "Out of memory"); - } - } - - for (k = 0; k < 4; ++k) { - int nleft; - i = 0; - while ((nleft = width - i) > 0) { - count = stbi__get8(s); - if (count > 128) { - // Run - value = stbi__get8(s); - count -= 128; - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = value; - } else { - // Dump - if (count > nleft) { STBI_FREE(hdr_data); STBI_FREE(scanline); return stbi__errpf("corrupt", "bad RLE data in HDR"); } - for (z = 0; z < count; ++z) - scanline[i++ * 4 + k] = stbi__get8(s); - } - } - } - for (i=0; i < width; ++i) - stbi__hdr_convert(hdr_data+(j*width + i)*req_comp, scanline + i*4, req_comp); - } - if (scanline) - STBI_FREE(scanline); - } - - return hdr_data; -} - -static int stbi__hdr_info(stbi__context *s, int *x, int *y, int *comp) -{ - char buffer[STBI__HDR_BUFLEN]; - char *token; - int valid = 0; - int dummy; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - if (stbi__hdr_test(s) == 0) { - stbi__rewind( s ); - return 0; - } - - for(;;) { - token = stbi__hdr_gettoken(s,buffer); - if (token[0] == 0) break; - if (strcmp(token, "FORMAT=32-bit_rle_rgbe") == 0) valid = 1; - } - - if (!valid) { - stbi__rewind( s ); - return 0; - } - token = stbi__hdr_gettoken(s,buffer); - if (strncmp(token, "-Y ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *y = (int) strtol(token, &token, 10); - while (*token == ' ') ++token; - if (strncmp(token, "+X ", 3)) { - stbi__rewind( s ); - return 0; - } - token += 3; - *x = (int) strtol(token, NULL, 10); - *comp = 3; - return 1; -} -#endif // STBI_NO_HDR - -#ifndef STBI_NO_BMP -static int stbi__bmp_info(stbi__context *s, int *x, int *y, int *comp) -{ - void *p; - stbi__bmp_data info; - - info.all_a = 255; - p = stbi__bmp_parse_header(s, &info); - stbi__rewind( s ); - if (p == NULL) - return 0; - if (x) *x = s->img_x; - if (y) *y = s->img_y; - if (comp) *comp = info.ma ? 4 : 3; - return 1; -} -#endif - -#ifndef STBI_NO_PSD -static int stbi__psd_info(stbi__context *s, int *x, int *y, int *comp) -{ - int channelCount, dummy, depth; - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - *y = stbi__get32be(s); - *x = stbi__get32be(s); - depth = stbi__get16be(s); - if (depth != 8 && depth != 16) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 3) { - stbi__rewind( s ); - return 0; - } - *comp = 4; - return 1; -} - -static int stbi__psd_is16(stbi__context *s) -{ - int channelCount, depth; - if (stbi__get32be(s) != 0x38425053) { - stbi__rewind( s ); - return 0; - } - if (stbi__get16be(s) != 1) { - stbi__rewind( s ); - return 0; - } - stbi__skip(s, 6); - channelCount = stbi__get16be(s); - if (channelCount < 0 || channelCount > 16) { - stbi__rewind( s ); - return 0; - } - (void) stbi__get32be(s); - (void) stbi__get32be(s); - depth = stbi__get16be(s); - if (depth != 16) { - stbi__rewind( s ); - return 0; - } - return 1; -} -#endif - -#ifndef STBI_NO_PIC -static int stbi__pic_info(stbi__context *s, int *x, int *y, int *comp) -{ - int act_comp=0,num_packets=0,chained,dummy; - stbi__pic_packet packets[10]; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - if (!stbi__pic_is4(s,"\x53\x80\xF6\x34")) { - stbi__rewind(s); - return 0; - } - - stbi__skip(s, 88); - - *x = stbi__get16be(s); - *y = stbi__get16be(s); - if (stbi__at_eof(s)) { - stbi__rewind( s); - return 0; - } - if ( (*x) != 0 && (1 << 28) / (*x) < (*y)) { - stbi__rewind( s ); - return 0; - } - - stbi__skip(s, 8); - - do { - stbi__pic_packet *packet; - - if (num_packets==sizeof(packets)/sizeof(packets[0])) - return 0; - - packet = &packets[num_packets++]; - chained = stbi__get8(s); - packet->size = stbi__get8(s); - packet->type = stbi__get8(s); - packet->channel = stbi__get8(s); - act_comp |= packet->channel; - - if (stbi__at_eof(s)) { - stbi__rewind( s ); - return 0; - } - if (packet->size != 8) { - stbi__rewind( s ); - return 0; - } - } while (chained); - - *comp = (act_comp & 0x10 ? 4 : 3); - - return 1; -} -#endif - -// ************************************************************************************************* -// Portable Gray Map and Portable Pixel Map loader -// by Ken Miller -// -// PGM: http://netpbm.sourceforge.net/doc/pgm.html -// PPM: http://netpbm.sourceforge.net/doc/ppm.html -// -// Known limitations: -// Does not support comments in the header section -// Does not support ASCII image data (formats P2 and P3) -// Does not support 16-bit-per-channel - -#ifndef STBI_NO_PNM - -static int stbi__pnm_test(stbi__context *s) -{ - char p, t; - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind( s ); - return 0; - } - return 1; -} - -static void *stbi__pnm_load(stbi__context *s, int *x, int *y, int *comp, int req_comp, stbi__result_info *ri) -{ - stbi_uc *out; - STBI_NOTUSED(ri); - - if (!stbi__pnm_info(s, (int *)&s->img_x, (int *)&s->img_y, (int *)&s->img_n)) - return 0; - - *x = s->img_x; - *y = s->img_y; - if (comp) *comp = s->img_n; - - if (!stbi__mad3sizes_valid(s->img_n, s->img_x, s->img_y, 0)) - return stbi__errpuc("too large", "PNM too large"); - - out = (stbi_uc *) stbi__malloc_mad3(s->img_n, s->img_x, s->img_y, 0); - if (!out) return stbi__errpuc("outofmem", "Out of memory"); - stbi__getn(s, out, s->img_n * s->img_x * s->img_y); - - if (req_comp && req_comp != s->img_n) { - out = stbi__convert_format(out, s->img_n, req_comp, s->img_x, s->img_y); - if (out == NULL) return out; // stbi__convert_format frees input on failure - } - return out; -} - -static int stbi__pnm_isspace(char c) -{ - return c == ' ' || c == '\t' || c == '\n' || c == '\v' || c == '\f' || c == '\r'; -} - -static void stbi__pnm_skip_whitespace(stbi__context *s, char *c) -{ - for (;;) { - while (!stbi__at_eof(s) && stbi__pnm_isspace(*c)) - *c = (char) stbi__get8(s); - - if (stbi__at_eof(s) || *c != '#') - break; - - while (!stbi__at_eof(s) && *c != '\n' && *c != '\r' ) - *c = (char) stbi__get8(s); - } -} - -static int stbi__pnm_isdigit(char c) -{ - return c >= '0' && c <= '9'; -} - -static int stbi__pnm_getinteger(stbi__context *s, char *c) -{ - int value = 0; - - while (!stbi__at_eof(s) && stbi__pnm_isdigit(*c)) { - value = value*10 + (*c - '0'); - *c = (char) stbi__get8(s); - } - - return value; -} - -static int stbi__pnm_info(stbi__context *s, int *x, int *y, int *comp) -{ - int maxv, dummy; - char c, p, t; - - if (!x) x = &dummy; - if (!y) y = &dummy; - if (!comp) comp = &dummy; - - stbi__rewind(s); - - // Get identifier - p = (char) stbi__get8(s); - t = (char) stbi__get8(s); - if (p != 'P' || (t != '5' && t != '6')) { - stbi__rewind(s); - return 0; - } - - *comp = (t == '6') ? 3 : 1; // '5' is 1-component .pgm; '6' is 3-component .ppm - - c = (char) stbi__get8(s); - stbi__pnm_skip_whitespace(s, &c); - - *x = stbi__pnm_getinteger(s, &c); // read width - stbi__pnm_skip_whitespace(s, &c); - - *y = stbi__pnm_getinteger(s, &c); // read height - stbi__pnm_skip_whitespace(s, &c); - - maxv = stbi__pnm_getinteger(s, &c); // read max value - - if (maxv > 255) - return stbi__err("max value > 255", "PPM image not 8-bit"); - else - return 1; -} -#endif - -static int stbi__info_main(stbi__context *s, int *x, int *y, int *comp) -{ - #ifndef STBI_NO_JPEG - if (stbi__jpeg_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNG - if (stbi__png_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_GIF - if (stbi__gif_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_BMP - if (stbi__bmp_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PSD - if (stbi__psd_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PIC - if (stbi__pic_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_PNM - if (stbi__pnm_info(s, x, y, comp)) return 1; - #endif - - #ifndef STBI_NO_HDR - if (stbi__hdr_info(s, x, y, comp)) return 1; - #endif - - // test tga last because it's a crappy test! - #ifndef STBI_NO_TGA - if (stbi__tga_info(s, x, y, comp)) - return 1; - #endif - return stbi__err("unknown image type", "Image not of any known type, or corrupt"); -} - -static int stbi__is_16_main(stbi__context *s) -{ - #ifndef STBI_NO_PNG - if (stbi__png_is16(s)) return 1; - #endif - - #ifndef STBI_NO_PSD - if (stbi__psd_is16(s)) return 1; - #endif - - return 0; -} - -#ifndef STBI_NO_STDIO -STBIDEF int stbi_info(char const *filename, int *x, int *y, int *comp) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_info_from_file(f, x, y, comp); - fclose(f); - return result; -} - -STBIDEF int stbi_info_from_file(FILE *f, int *x, int *y, int *comp) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__info_main(&s,x,y,comp); - fseek(f,pos,SEEK_SET); - return r; -} - -STBIDEF int stbi_is_16_bit(char const *filename) -{ - FILE *f = stbi__fopen(filename, "rb"); - int result; - if (!f) return stbi__err("can't fopen", "Unable to open file"); - result = stbi_is_16_bit_from_file(f); - fclose(f); - return result; -} - -STBIDEF int stbi_is_16_bit_from_file(FILE *f) -{ - int r; - stbi__context s; - long pos = ftell(f); - stbi__start_file(&s, f); - r = stbi__is_16_main(&s); - fseek(f,pos,SEEK_SET); - return r; -} -#endif // !STBI_NO_STDIO - -STBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *c, void *user, int *x, int *y, int *comp) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__info_main(&s,x,y,comp); -} - -STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len) -{ - stbi__context s; - stbi__start_mem(&s,buffer,len); - return stbi__is_16_main(&s); -} - -STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *c, void *user) -{ - stbi__context s; - stbi__start_callbacks(&s, (stbi_io_callbacks *) c, user); - return stbi__is_16_main(&s); -} - -#endif // STB_IMAGE_IMPLEMENTATION - -/* - revision history: - 2.19 (2018-02-11) fix warning - 2.18 (2018-01-30) fix warnings - 2.17 (2018-01-29) change sbti__shiftsigned to avoid clang -O2 bug - 1-bit BMP - *_is_16_bit api - avoid warnings - 2.16 (2017-07-23) all functions have 16-bit variants; - STBI_NO_STDIO works again; - compilation fixes; - fix rounding in unpremultiply; - optimize vertical flip; - disable raw_len validation; - documentation fixes - 2.15 (2017-03-18) fix png-1,2,4 bug; now all Imagenet JPGs decode; - warning fixes; disable run-time SSE detection on gcc; - uniform handling of optional "return" values; - thread-safe initialization of zlib tables - 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs - 2.13 (2016-11-29) add 16-bit API, only supported for PNG right now - 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes - 2.11 (2016-04-02) allocate large structures on the stack - remove white matting for transparent PSD - fix reported channel count for PNG & BMP - re-enable SSE2 in non-gcc 64-bit - support RGB-formatted JPEG - read 16-bit PNGs (only as 8-bit) - 2.10 (2016-01-22) avoid warning introduced in 2.09 by STBI_REALLOC_SIZED - 2.09 (2016-01-16) allow comments in PNM files - 16-bit-per-pixel TGA (not bit-per-component) - info() for TGA could break due to .hdr handling - info() for BMP to shares code instead of sloppy parse - can use STBI_REALLOC_SIZED if allocator doesn't support realloc - code cleanup - 2.08 (2015-09-13) fix to 2.07 cleanup, reading RGB PSD as RGBA - 2.07 (2015-09-13) fix compiler warnings - partial animated GIF support - limited 16-bpc PSD support - #ifdef unused functions - bug with < 92 byte PIC,PNM,HDR,TGA - 2.06 (2015-04-19) fix bug where PSD returns wrong '*comp' value - 2.05 (2015-04-19) fix bug in progressive JPEG handling, fix warning - 2.04 (2015-04-15) try to re-enable SIMD on MinGW 64-bit - 2.03 (2015-04-12) extra corruption checking (mmozeiko) - stbi_set_flip_vertically_on_load (nguillemot) - fix NEON support; fix mingw support - 2.02 (2015-01-19) fix incorrect assert, fix warning - 2.01 (2015-01-17) fix various warnings; suppress SIMD on gcc 32-bit without -msse2 - 2.00b (2014-12-25) fix STBI_MALLOC in progressive JPEG - 2.00 (2014-12-25) optimize JPG, including x86 SSE2 & NEON SIMD (ryg) - progressive JPEG (stb) - PGM/PPM support (Ken Miller) - STBI_MALLOC,STBI_REALLOC,STBI_FREE - GIF bugfix -- seemingly never worked - STBI_NO_*, STBI_ONLY_* - 1.48 (2014-12-14) fix incorrectly-named assert() - 1.47 (2014-12-14) 1/2/4-bit PNG support, both direct and paletted (Omar Cornut & stb) - optimize PNG (ryg) - fix bug in interlaced PNG with user-specified channel count (stb) - 1.46 (2014-08-26) - fix broken tRNS chunk (colorkey-style transparency) in non-paletted PNG - 1.45 (2014-08-16) - fix MSVC-ARM internal compiler error by wrapping malloc - 1.44 (2014-08-07) - various warning fixes from Ronny Chevalier - 1.43 (2014-07-15) - fix MSVC-only compiler problem in code changed in 1.42 - 1.42 (2014-07-09) - don't define _CRT_SECURE_NO_WARNINGS (affects user code) - fixes to stbi__cleanup_jpeg path - added STBI_ASSERT to avoid requiring assert.h - 1.41 (2014-06-25) - fix search&replace from 1.36 that messed up comments/error messages - 1.40 (2014-06-22) - fix gcc struct-initialization warning - 1.39 (2014-06-15) - fix to TGA optimization when req_comp != number of components in TGA; - fix to GIF loading because BMP wasn't rewinding (whoops, no GIFs in my test suite) - add support for BMP version 5 (more ignored fields) - 1.38 (2014-06-06) - suppress MSVC warnings on integer casts truncating values - fix accidental rename of 'skip' field of I/O - 1.37 (2014-06-04) - remove duplicate typedef - 1.36 (2014-06-03) - convert to header file single-file library - if de-iphone isn't set, load iphone images color-swapped instead of returning NULL - 1.35 (2014-05-27) - various warnings - fix broken STBI_SIMD path - fix bug where stbi_load_from_file no longer left file pointer in correct place - fix broken non-easy path for 32-bit BMP (possibly never used) - TGA optimization by Arseny Kapoulkine - 1.34 (unknown) - use STBI_NOTUSED in stbi__resample_row_generic(), fix one more leak in tga failure case - 1.33 (2011-07-14) - make stbi_is_hdr work in STBI_NO_HDR (as specified), minor compiler-friendly improvements - 1.32 (2011-07-13) - support for "info" function for all supported filetypes (SpartanJ) - 1.31 (2011-06-20) - a few more leak fixes, bug in PNG handling (SpartanJ) - 1.30 (2011-06-11) - added ability to load files via callbacks to accomidate custom input streams (Ben Wenger) - removed deprecated format-specific test/load functions - removed support for installable file formats (stbi_loader) -- would have been broken for IO callbacks anyway - error cases in bmp and tga give messages and don't leak (Raymond Barbiero, grisha) - fix inefficiency in decoding 32-bit BMP (David Woo) - 1.29 (2010-08-16) - various warning fixes from Aurelien Pocheville - 1.28 (2010-08-01) - fix bug in GIF palette transparency (SpartanJ) - 1.27 (2010-08-01) - cast-to-stbi_uc to fix warnings - 1.26 (2010-07-24) - fix bug in file buffering for PNG reported by SpartanJ - 1.25 (2010-07-17) - refix trans_data warning (Won Chun) - 1.24 (2010-07-12) - perf improvements reading from files on platforms with lock-heavy fgetc() - minor perf improvements for jpeg - deprecated type-specific functions so we'll get feedback if they're needed - attempt to fix trans_data warning (Won Chun) - 1.23 fixed bug in iPhone support - 1.22 (2010-07-10) - removed image *writing* support - stbi_info support from Jetro Lauha - GIF support from Jean-Marc Lienher - iPhone PNG-extensions from James Brown - warning-fixes from Nicolas Schulz and Janez Zemva (i.stbi__err. Janez (U+017D)emva) - 1.21 fix use of 'stbi_uc' in header (reported by jon blow) - 1.20 added support for Softimage PIC, by Tom Seddon - 1.19 bug in interlaced PNG corruption check (found by ryg) - 1.18 (2008-08-02) - fix a threading bug (local mutable static) - 1.17 support interlaced PNG - 1.16 major bugfix - stbi__convert_format converted one too many pixels - 1.15 initialize some fields for thread safety - 1.14 fix threadsafe conversion bug - header-file-only version (#define STBI_HEADER_FILE_ONLY before including) - 1.13 threadsafe - 1.12 const qualifiers in the API - 1.11 Support installable IDCT, colorspace conversion routines - 1.10 Fixes for 64-bit (don't use "unsigned long") - optimized upsampling by Fabian "ryg" Giesen - 1.09 Fix format-conversion for PSD code (bad global variables!) - 1.08 Thatcher Ulrich's PSD code integrated by Nicolas Schulz - 1.07 attempt to fix C++ warning/errors again - 1.06 attempt to fix C++ warning/errors again - 1.05 fix TGA loading to return correct *comp and use good luminance calc - 1.04 default float alpha is 1, not 255; use 'void *' for stbi_image_free - 1.03 bugfixes to STBI_NO_STDIO, STBI_NO_HDR - 1.02 support for (subset of) HDR files, float interface for preferred access to them - 1.01 fix bug: possible bug in handling right-side up bmps... not sure - fix bug: the stbi__bmp_load() and stbi__tga_load() functions didn't work at all - 1.00 interface to zlib that skips zlib header - 0.99 correct handling of alpha in palette - 0.98 TGA loader by lonesock; dynamically add loaders (untested) - 0.97 jpeg errors on too large a file; also catch another malloc failure - 0.96 fix detection of invalid v value - particleman@mollyrocket forum - 0.95 during header scan, seek to markers in case of padding - 0.94 STBI_NO_STDIO to disable stdio usage; rename all #defines the same - 0.93 handle jpegtran output; verbose errors - 0.92 read 4,8,16,24,32-bit BMP files of several formats - 0.91 output 24-bit Windows 3.0 BMP files - 0.90 fix a few more warnings; bump version number to approach 1.0 - 0.61 bugfixes due to Marc LeBlanc, Christopher Lloyd - 0.60 fix compiling as c++ - 0.59 fix warnings: merge Dave Moore's -Wall fixes - 0.58 fix bug: zlib uncompressed mode len/nlen was wrong endian - 0.57 fix bug: jpg last huffman symbol before marker was >9 bits but less than 16 available - 0.56 fix bug: zlib uncompressed mode len vs. nlen - 0.55 fix bug: restart_interval not initialized to 0 - 0.54 allow NULL for 'int *comp' - 0.53 fix bug in png 3->4; speedup png decoding - 0.52 png handles req_comp=3,4 directly; minor cleanup; jpeg comments - 0.51 obey req_comp requests, 1-component jpegs return as 1-component, - on 'test' only check type, not whether we support this variant - 0.50 (2006-11-19) - first released version -*/ - - -/* ------------------------------------------------------------------------------- -This software is available under 2 licenses -- choose whichever you prefer. ------------------------------------------------------------------------------- -ALTERNATIVE A - MIT License -Copyright (c) 2017 Sean Barrett -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. ------------------------------------------------------------------------------- -ALTERNATIVE B - Public Domain (www.unlicense.org) -This is free and unencumbered software released into the public domain. -Anyone is free to copy, modify, publish, use, compile, sell, or distribute this -software, either in source code form or as a compiled binary, for any purpose, -commercial or non-commercial, and by any means. -In jurisdictions that recognize copyright laws, the author or authors of this -software dedicate any and all copyright interest in the software to the public -domain. We make this dedication for the benefit of the public at large and to -the detriment of our heirs and successors. We intend this dedication to be an -overt act of relinquishment in perpetuity of all present and future rights to -this software under copyright law. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- -*/ diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index ffe39f9f7..54273b90c 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -32,7 +32,7 @@ PROJECT_NAME = Assimp # This could be handy for archiving the generated documentation or # if some version control system is used. -PROJECT_NUMBER = "v4.1. (December 2018)" +PROJECT_NUMBER = "v5.0.1. (December 2020)" # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer @@ -197,7 +197,7 @@ SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. # Doxygen uses this value to replace tabs by spaces in code fragments. -TAB_SIZE = 8 +TAB_SIZE = 4 # This tag can be used to specify a number of aliases that acts # as commands in the documentation. An alias has the form "name=value". @@ -636,7 +636,7 @@ WARN_IF_DOC_ERROR = YES # wrong or incomplete parameter documentation, but not about the absence of # documentation. -WARN_NO_PARAMDOC = NO +WARN_NO_PARAMDOC = YES # The WARN_FORMAT tag determines the format of the warning messages that # doxygen can produce. The string should contain the $file, $line, and $text @@ -663,11 +663,7 @@ WARN_LOGFILE = # with spaces. INPUT = @doxy_main_page@ \ - @PROJECT_SOURCE_DIR@ \ - @PROJECT_BINARY_DIR@ \ - @PROJECT_SOURCE_DIR@/include/ \ - @PROJECT_SOURCE_DIR@/doc/dox.h \ - @PROJECT_SOURCE_DIR@/code/BaseImporter.h + @PROJECT_SOURCE_DIR@/include/ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is @@ -690,28 +686,10 @@ FILE_PATTERNS = *.c \ *.cxx \ *.cpp \ *.c++ \ - *.d \ - *.java \ - *.ii \ - *.ixx \ - *.ipp \ - *.i++ \ *.inl \ *.h \ - *.hh \ - *.hxx \ *.hpp \ - *.h++ \ - *.idl \ - *.odl \ - *.cs \ - *.php \ - *.php3 \ - *.inc \ - *.m \ - *.mm \ - *.dox \ - *.py + *.dox # The RECURSIVE tag can be used to turn specify whether or not subdirectories # should be searched for input files as well. Possible values are YES and NO. @@ -725,7 +703,7 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = irrXML.h +EXCLUDE = contrib/* # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded @@ -739,8 +717,7 @@ EXCLUDE_SYMLINKS = NO # against the file with absolute path, so to exclude all test directories # for example use the pattern */test/* -EXCLUDE_PATTERNS = */.svn/* \ - */.svn +EXCLUDE_PATTERNS = */.git/* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the @@ -901,7 +878,7 @@ IGNORE_PREFIX = # If the GENERATE_HTML tag is set to YES (the default) Doxygen will # generate HTML output. -GENERATE_HTML = YES +GENERATE_HTML = NO # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be @@ -1484,7 +1461,7 @@ MAN_LINKS = NO # generate an XML file that captures the structure of # the code including all documentation. -GENERATE_XML = NO +GENERATE_XML = YES # The XML_OUTPUT tag is used to specify where the XML pages will be put. # If a relative path is entered the value of OUTPUT_DIRECTORY will be diff --git a/doc/dox.h b/doc/dox.h index a4516dc7a..409e775d4 100644 --- a/doc/dox.h +++ b/doc/dox.h @@ -90,8 +90,8 @@ but not all of them are *open-source*. If there's an accompagning '\source @section main_install Installation assimp can be used in two ways: linking against the pre-built libraries or building the library on your own. The former -option is the easiest, but the assimp distribution contains pre-built libraries only for Visual C++ 2013, 2015 and 2017. -For other compilers you'll have to build assimp for yourself. Which is hopefully as hassle-free as the other way, but +option is the easiest, but the assimp distribution contains pre-built libraries only for Visual C++ 2013, 2015 and 2017. +For other compilers you'll have to build assimp for yourself. Which is hopefully as hassle-free as the other way, but needs a bit more work. Both ways are described at the @link install Installation page. @endlink If you want to use assimp on Ubuntu you can install it via the following command: @@ -145,7 +145,7 @@ to your include paths (Menu->Extras->Options->Projects and Solutions-&g and the assimp/lib/<Compiler> path to your linker paths (Menu->Extras->Options->Projects and Solutions->VC++ Directories->Library files). This is necessary only once to setup all paths inside you IDE. -To use the library in your C++ project you can simply generate a project file via cmake. One way is to add the assimp-folder +To use the library in your C++ project you can simply generate a project file via cmake. One way is to add the assimp-folder as a subdirectory via the cmake-command @code @@ -158,7 +158,7 @@ Now just add the assimp-dependency to your application: TARGET_LINK_LIBRARIES(my_game assimp) @endcode -If done correctly you should now be able to compile, link, run and use the application. +If done correctly you should now be able to compile, link, run and use the application. @section install_own Building the library from scratch @@ -170,7 +170,7 @@ to build the library just open a command-prompt / bash, navigate into the repo-f cmake CMakeLists.txt @endcode -A project-file of your default make-system ( like gnu-make on linux or Visual-Studio on Windows ) will be generated. +A project-file of your default make-system ( like gnu-make on linux or Visual-Studio on Windows ) will be generated. Run the build and you are done. You can find the libs at assimp/lib and the dll's / so's at bin. @section assimp_dll Windows DLL Build @@ -496,10 +496,10 @@ X3 Y3 Z3 T3 @endcode with (X1, X2, X3) being the local X base vector, (Y1, Y2, Y3) being the local Y base vector, (Z1, Z2, Z3) being the local Z base vector and (T1, T2, T3) being the -offset of the local origin (the translational part). +offset of the local origin (the translational part). All matrices in the library use row-major storage order. That means that the matrix elements are -stored row-by-row, i.e. they end up like this in memory: -[X1, Y1, Z1, T1, X2, Y2, Z2, T2, X3, Y3, Z3, T3, 0, 0, 0, 1]. +stored row-by-row, i.e. they end up like this in memory: +[X1, Y1, Z1, T1, X2, Y2, Z2, T2, X3, Y3, Z3, T3, 0, 0, 0, 1]. Note that this is neither the OpenGL format nor the DirectX format, because both of them specify the matrix layout such that the translational part occupies three consecutive addresses in memory (so those @@ -1498,7 +1498,7 @@ Just copy'n'paste the template from Appendix A and adapt it for your needs. with DefaultLogger::get()->[error, warn, debug, info].
  • -Make sure that your loader compiles against all build configurations on all supported platforms. You can use our CI-build to check several platforms +Make sure that your loader compiles against all build configurations on all supported platforms. You can use our CI-build to check several platforms like Windows and Linux ( 32 bit and 64 bit ).
  • diff --git a/doc/dox_cmd.h b/doc/dox_cmd.h index 78e755d56..79ea3a861 100644 --- a/doc/dox_cmd.h +++ b/doc/dox_cmd.h @@ -12,7 +12,7 @@ @section intro Introduction -This document describes the usage of assimp's command line tools. +This document describes the usage of assimp's command line tools. This is *not* the SDK reference and programming-related stuff is not covered here.

    NOTE: For simplicity, the following sections are written with Windows in mind. However @@ -29,7 +29,7 @@ assimp [command] [parameters] The following commands are available: - + @@ -184,7 +184,7 @@ Generate a text or binary dump of a model. This is the core component of Assimp' regression test suite but it could also be useful for other developers to quickly examine the contents of a model. Note that text dumps are not intended to be used as intermediate format, Assimp is not able to read them again, nor is the file format -stable or well-defined. It may change with every revision without notice. +stable or well-defined. It may change with every revision without notice. Binary dumps (*.assbin) are backwards- and forwards-compatible.

    Syntax:

    @@ -199,7 +199,7 @@ assimp dump [] [-b] [-s] [common parameters]

    model

    -Required. Relative or absolute path to the input model. +Required. Relative or absolute path to the input model.

    @@ -220,7 +220,7 @@ The long form of this parameter is --binary.
    Optional. If this switch is specified, the dump is shortened to include only min/max values for all vertex components and animation channels. The resulting -file is much smaller, but the original model can't be reconstructed from it. This is +file is much smaller, but the original model can't be reconstructed from it. This is used by Assimp's regression test suite, comparing those minidumps provides a fast way to verify whether a loader works correctly or not. The long form of this parameter is --short. @@ -229,7 +229,7 @@ The long form of this parameter is --short.

    common parameters

    -Optional. Import configuration & postprocessing. +Optional. Import configuration & postprocessing. See the @link common common parameters page @endlink for more information.

    @@ -248,7 +248,7 @@ The log output is included with the dump. @code assimp dump files\*.* -assimp dump files\*.* +assimp dump files\*.* @endcode Dumps all loadable model files in the 'files' subdir. The output dumps are named @@ -275,14 +275,14 @@ assimp extract [] [-t] [-f] [-ba] [-s] [common parameters]

    model

    -Required. Relative or absolute path to the input model. +Required. Relative or absolute path to the input model.

    out

    Optional. Relative or absolute path to write the output images to. If the file name is omitted the output images are named
    -The suffix _img<n> is appended to the file name if the -s switch is not specified +The suffix _img<n> is appended to the file name if the -s switch is not specified (where <n> is the zero-based index of the texture in the model file).
    The output file format is determined from the given file extension. Supported @@ -296,7 +296,7 @@ written in their native file format (e.g. jpg).

    -t<n>

    -Optional. Specifies the (zero-based) index of the embedded texture to be extracted from +Optional. Specifies the (zero-based) index of the embedded texture to be extracted from the model. If this option is *not* specified all textures found are exported. The long form of this parameter is --texture=<n>.

    @@ -348,8 +348,8 @@ imported data structure and writes it to test_img0.bmp. @code -assimp extract files\*.mdl *.bmp -assimp extract files\*.mdl *.bmp +assimp extract files\*.mdl *.bmp +assimp extract files\*.mdl *.bmp @endcode Extracts all embedded textures from all loadable .mdl files in the 'files' subdirectory @@ -361,10 +361,10 @@ and writes them to bitmaps which are named _img.bmp /** @page common Common parameters -The parameters described on this page are commonly used by almost every assimp command. They +The parameters described on this page are commonly used by almost every assimp command. They specify how the library will postprocess the imported data. This is done by several configurable pipeline stages, called 'post processing steps'. Below you can find a list -of all supported steps along with short descriptions of what they're doing.
    Programmers: +of all supported steps along with short descriptions of what they're doing.
    Programmers: more information can be found in the aiPostProcess.h header.
    @link version version @endlink Retrieve the current version of assimp
    @@ -376,7 +376,7 @@ more information can be found in the aiPostProcess.h header. - @@ -428,7 +428,7 @@ more information can be found in the aiPostProcess.h header. - @@ -515,7 +515,7 @@ For convenience some default postprocessing configurations are provided. The corresponding command line parameter is -c<name> (or --config=<name>).
    -ptv --pretransform-verticesMove all vertices into worldspace and collapse the scene graph. Animation data is lost. + Move all vertices into worldspace and collapse the scene graph. Animation data is lost. This is intended for applications which don't support scenegraph-oriented rendering.
    -icl --improve-cache-localityImprove the cache locality of the vertex buffer by reordering the index buffer + Improve the cache locality of the vertex buffer by reordering the index buffer to achieve a lower ACMR (average post-transform vertex cache miss ratio)
    - + @@ -543,7 +543,7 @@ The corresponding command line parameter is -c<name> (or --co There are also some common flags to customize Assimp's logging behaviour:
    Name Description
    - + @@ -558,7 +558,7 @@ There are also some common flags to customize Assimp's logging behaviour: -
    Name Description
    -v or --verboseEnables verbose logging. Debug messages will be produced too. This might + Enables verbose logging. Debug messages will be produced too. This might decrease loading performance and result in *very* long logs ... use with caution if you experience strange issues.
    diff --git a/fuzz/assimp_fuzzer.cc b/fuzz/assimp_fuzzer.cc index b65ee0236..33748c10f 100644 --- a/fuzz/assimp_fuzzer.cc +++ b/fuzz/assimp_fuzzer.cc @@ -54,6 +54,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t dataSize) { aiProcessPreset_TargetRealtime_Quality, nullptr ); aiDetachLogStream(&stream); - + return 0; } diff --git a/include/assimp/BaseImporter.h b/include/assimp/BaseImporter.h index 38bec1afd..54b5daac1 100644 --- a/include/assimp/BaseImporter.h +++ b/include/assimp/BaseImporter.h @@ -154,7 +154,7 @@ public: /** Returns the exception of the last exception that occurred. * Note: Exceptions are not the only source of error details, so GetErrorText * should be consulted too. - * @return The last exception that occurred. + * @return The last exception that occurred. */ const std::exception_ptr& GetException() const { return m_Exception; diff --git a/include/assimp/Compiler/poppack1.h b/include/assimp/Compiler/poppack1.h index a8e9a3c7e..ff501bc0c 100644 --- a/include/assimp/Compiler/poppack1.h +++ b/include/assimp/Compiler/poppack1.h @@ -1,7 +1,7 @@ // =============================================================================== -// May be included multiple times - resets structure packing to the defaults -// for all supported compilers. Reverts the changes made by #include +// May be included multiple times - resets structure packing to the defaults +// for all supported compilers. Reverts the changes made by #include // // Currently this works on the following compilers: // MSVC 7,8,9 diff --git a/include/assimp/Compiler/pushpack1.h b/include/assimp/Compiler/pushpack1.h index 2a5e2dfe6..b32ed172c 100644 --- a/include/assimp/Compiler/pushpack1.h +++ b/include/assimp/Compiler/pushpack1.h @@ -1,7 +1,7 @@ // =============================================================================== -// May be included multiple times - sets structure packing to 1 +// May be included multiple times - sets structure packing to 1 // for all supported compilers. #include reverts the changes. // // Currently this works on the following compilers: @@ -37,7 +37,7 @@ #if defined(_MSC_VER) // C4103: Packing was changed after the inclusion of the header, probably missing #pragma pop -# pragma warning (disable : 4103) +# pragma warning (disable : 4103) #endif #define AI_PUSHPACK_IS_DEFINED diff --git a/include/assimp/Exceptional.h b/include/assimp/Exceptional.h index 98e2a3321..1bf399cbc 100644 --- a/include/assimp/Exceptional.h +++ b/include/assimp/Exceptional.h @@ -59,7 +59,7 @@ using std::runtime_error; class ASSIMP_API DeadlyErrorBase : public runtime_error { protected: DeadlyErrorBase(Assimp::Formatter::format f); - + template DeadlyErrorBase(Assimp::Formatter::format f, U&& u, T&&... args) : DeadlyErrorBase(std::move(f << std::forward(u)), std::forward(args)...) {} diff --git a/include/assimp/Exporter.hpp b/include/assimp/Exporter.hpp index 8e78ac954..6ab35a8f0 100644 --- a/include/assimp/Exporter.hpp +++ b/include/assimp/Exporter.hpp @@ -390,7 +390,7 @@ public: * @see SetPropertyInteger() */ bool SetPropertyMatrix(const char *szName, const aiMatrix4x4 &sValue); - + bool SetPropertyCallback(const char *szName, const std::function &f); // ------------------------------------------------------------------- diff --git a/include/assimp/IOStreamBuffer.h b/include/assimp/IOStreamBuffer.h index ee4ac0953..c3b7327ff 100644 --- a/include/assimp/IOStreamBuffer.h +++ b/include/assimp/IOStreamBuffer.h @@ -81,7 +81,7 @@ public: /// @brief Returns the file-size. /// @return The file-size. size_t size() const; - + /// @brief Returns the cache size. /// @return The cache size. size_t cacheSize() const; @@ -278,7 +278,7 @@ bool IOStreamBuffer::getNextDataLine( std::vector &buffer, T continuationT } } } - + buffer[ i ] = '\n'; ++m_cachePos; @@ -334,7 +334,7 @@ template AI_FORCE_INLINE bool IOStreamBuffer::getNextBlock( std::vector &buffer) { // Return the last block-value if getNextLine was used before - if ( 0 != m_cachePos ) { + if ( 0 != m_cachePos ) { buffer = std::vector( m_cache.begin() + m_cachePos, m_cache.end() ); m_cachePos = 0; } else { diff --git a/include/assimp/IOSystem.hpp b/include/assimp/IOSystem.hpp index 76f876440..7be373cf1 100644 --- a/include/assimp/IOSystem.hpp +++ b/include/assimp/IOSystem.hpp @@ -62,9 +62,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "types.h" #ifdef _WIN32 -# include -# include -# include +# include +# include +# include #else # include # include @@ -84,7 +84,7 @@ namespace Assimp { * to the importer library. If you implement this interface, you also want to * supply a custom implementation for IOStream. * - * @see Importer::SetIOHandler() + * @see Importer::SetIOHandler() */ class ASSIMP_API IOSystem #ifndef SWIG diff --git a/include/assimp/Logger.hpp b/include/assimp/Logger.hpp index 3ca4a6cb2..aa7ffba7c 100644 --- a/include/assimp/Logger.hpp +++ b/include/assimp/Logger.hpp @@ -99,8 +99,8 @@ public: virtual ~Logger(); // ---------------------------------------------------------------------- - /** @brief Writes a info message - * @param message Info message*/ + /** @brief Writes a debug message + * @param message Debug message*/ void debug(const char* message); template @@ -109,10 +109,10 @@ public: } // ---------------------------------------------------------------------- - /** @brief Writes a debug message + /** @brief Writes a debug message * @param message Debug message*/ void verboseDebug(const char* message); - + template void verboseDebug(T&&... args) { verboseDebug(formatMessage(std::forward(args)...).c_str()); @@ -140,7 +140,7 @@ public: // ---------------------------------------------------------------------- /** @brief Writes an error message - * @param message Info message*/ + * @param message Error message*/ void error(const char* message); template diff --git a/include/assimp/MemoryIOWrapper.h b/include/assimp/MemoryIOWrapper.h index 86dc5ea60..24f6a85d1 100644 --- a/include/assimp/MemoryIOWrapper.h +++ b/include/assimp/MemoryIOWrapper.h @@ -57,7 +57,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include namespace Assimp { - + #define AI_MEMORYIO_MAGIC_FILENAME "$$$___magic___$$$" #define AI_MEMORYIO_MAGIC_FILENAME_LENGTH 17 @@ -85,7 +85,7 @@ public: size_t Read(void* pvBuffer, size_t pSize, size_t pCount) { ai_assert(nullptr != pvBuffer); ai_assert(0 != pSize); - + const size_t cnt = std::min( pCount, (length-pos) / pSize); const size_t ofs = pSize * cnt; @@ -209,7 +209,7 @@ public: return existing_io ? existing_io->ComparePaths(one, second) : false; } - bool PushDirectory( const std::string &path ) override { + bool PushDirectory( const std::string &path ) override { return existing_io ? existing_io->PushDirectory(path) : false; } diff --git a/include/assimp/SmallVector.h b/include/assimp/SmallVector.h index bcb8482a9..fb78f5a97 100644 --- a/include/assimp/SmallVector.h +++ b/include/assimp/SmallVector.h @@ -53,17 +53,17 @@ Based on CppCon 2016: Chandler Carruth "High Performance Code 201: Hybrid Data S namespace Assimp { // -------------------------------------------------------------------------------------------- -/// @brief Small vector with inplace storage. +/// @brief Small vector with inplace storage. /// /// Reduces heap allocations when list is shorter. It uses a small array for a dedicated size. -/// When the growing gets bigger than this small cache a dynamic growing algorithm will be +/// When the growing gets bigger than this small cache a dynamic growing algorithm will be /// used. // -------------------------------------------------------------------------------------------- template class SmallVector { public: /// @brief The default class constructor. - SmallVector() : + SmallVector() : mStorage(mInplaceStorage), mSize(0), mCapacity(Capacity) { @@ -84,7 +84,7 @@ public: mStorage[mSize++] = item; return; } - + push_back_and_grow(item); } diff --git a/include/assimp/SmoothingGroups.inl b/include/assimp/SmoothingGroups.inl index a32626e00..08c2dfa53 100644 --- a/include/assimp/SmoothingGroups.inl +++ b/include/assimp/SmoothingGroups.inl @@ -7,8 +7,8 @@ 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 +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 @@ -25,16 +25,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ @@ -77,7 +77,7 @@ void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups& sMesh) sMesh.mNormals[face.mIndices[c]] = vNor; } - // calculate the position bounds so we have a reliable epsilon to check position differences against + // calculate the position bounds so we have a reliable epsilon to check position differences against aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f); for( unsigned int a = 0; a < sMesh.mPositions.size(); a++) { @@ -91,7 +91,7 @@ void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups& sMesh) const float posEpsilon = (maxVec - minVec).Length() * 1e-5f; std::vector avNormals; avNormals.resize(sMesh.mNormals.size()); - + // now generate the spatial sort tree SGSpatialSort sSort; for( typename std::vector::iterator i = sMesh.mFaces.begin(); diff --git a/include/assimp/XmlParser.h b/include/assimp/XmlParser.h index 18d48f337..9c2dc419e 100644 --- a/include/assimp/XmlParser.h +++ b/include/assimp/XmlParser.h @@ -94,7 +94,7 @@ using XmlAttribute = pugi::xml_attribute; /// } /// } /// @endcode -/// @tparam TNodeType +/// @tparam TNodeType template class TXmlParser { public: @@ -123,7 +123,7 @@ public: /// @brief Will search for a child-node by its name /// @param name [in] The name of the child-node. - /// @return The node instance or nullptr, if nothing was found. + /// @return The node instance or nullptr, if nothing was found. TNodeType *findNode(const std::string &name) { if (name.empty()) { return nullptr; @@ -162,12 +162,12 @@ public: mData.resize(len + 1); memset(&mData[0], '\0', len + 1); stream->Read(&mData[0], 1, len); - + mDoc = new pugi::xml_document(); pugi::xml_parse_result parse_result = mDoc->load_string(&mData[0], pugi::parse_full); if (parse_result.status == pugi::status_ok) { return true; - } + } ASSIMP_LOG_DEBUG("Error while parse xml.", std::string(parse_result.description()), " @ ", parse_result.offset); @@ -457,7 +457,7 @@ public: } private: - XmlNode &mParent; + XmlNode &mParent; std::vector mNodes; size_t mIndex; }; diff --git a/include/assimp/ai_assert.h b/include/assimp/ai_assert.h index 6736b24c9..b377b6e8b 100644 --- a/include/assimp/ai_assert.h +++ b/include/assimp/ai_assert.h @@ -57,7 +57,7 @@ namespace Assimp #else # define ai_assert(expression) -# define ai_assert_entry() +# define ai_assert_entry() #endif // ASSIMP_BUILD_DEBUG #endif // AI_ASSERT_H_INC diff --git a/include/assimp/anim.h b/include/assimp/anim.h index 79841a537..dcd054d1e 100644 --- a/include/assimp/anim.h +++ b/include/assimp/anim.h @@ -39,7 +39,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** +/** * @file anim.h * @brief Defines the data structures in which the imported animations * are returned. @@ -478,11 +478,11 @@ struct aiAnimation { namespace Assimp { // --------------------------------------------------------------------------- -/** +/** * @brief CPP-API: Utility class to simplify interpolations of various data types. * * The type of interpolation is chosen automatically depending on the - * types of the arguments. + * types of the arguments. */ template struct Interpolator { diff --git a/include/assimp/cimport.h b/include/assimp/cimport.h index b4a172742..d660eebb1 100644 --- a/include/assimp/cimport.h +++ b/include/assimp/cimport.h @@ -894,7 +894,7 @@ ASSIMP_API float aiMatrix3Determinant( // -------------------------------------------------------------------------------- /** Get a 3x3 rotation matrix around the Z axis. - * @param mat Receives the output matrix + * @param mat Receives the output matrix * @param angle Rotation angle, in radians */ ASSIMP_API void aiMatrix3RotationZ( @@ -903,7 +903,7 @@ ASSIMP_API void aiMatrix3RotationZ( // -------------------------------------------------------------------------------- /** Returns a 3x3 rotation matrix for a rotation around an arbitrary axis. - * @param mat Receives the output matrix + * @param mat Receives the output matrix * @param axis Rotation axis, should be a normalized vector * @param angle Rotation angle, in radians */ @@ -914,7 +914,7 @@ ASSIMP_API void aiMatrix3FromRotationAroundAxis( // -------------------------------------------------------------------------------- /** Get a 3x3 translation matrix. - * @param mat Receives the output matrix + * @param mat Receives the output matrix * @param translation The translation vector */ ASSIMP_API void aiMatrix3Translation( @@ -923,7 +923,7 @@ ASSIMP_API void aiMatrix3Translation( // -------------------------------------------------------------------------------- /** Create a 3x3 matrix that rotates one vector to another vector. - * @param mat Receives the output matrix + * @param mat Receives the output matrix * @param from Vector to rotate from * @param to Vector to rotate to */ @@ -1059,7 +1059,7 @@ ASSIMP_API void aiMatrix4DecomposeNoScaling( // -------------------------------------------------------------------------------- /** Creates a 4x4 matrix from a set of euler angles. - * @param mat Receives the output matrix + * @param mat Receives the output matrix * @param x Rotation angle for the x-axis, in radians * @param y Rotation angle for the y-axis, in radians * @param z Rotation angle for the z-axis, in radians @@ -1137,7 +1137,7 @@ ASSIMP_API void aiMatrix4FromTo( // -------------------------------------------------------------------------------- /** Create a Quaternion from euler angles. - * @param q Receives the output quaternion + * @param q Receives the output quaternion * @param x Rotation angle for the x-axis, in radians * @param y Rotation angle for the y-axis, in radians * @param z Rotation angle for the z-axis, in radians diff --git a/include/assimp/defs.h b/include/assimp/defs.h index d61fd7901..8d1011a28 100644 --- a/include/assimp/defs.h +++ b/include/assimp/defs.h @@ -161,7 +161,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #pragma warning(disable : 4251) #endif /* Force the compiler to inline a function, if possible */ - #define AI_FORCE_INLINE inline + #define AI_FORCE_INLINE inline /* Tells the compiler that a function never returns. Used in code analysis * to skip dead paths (e.g. after an assertion evaluated to false). */ diff --git a/include/assimp/fast_atof.h b/include/assimp/fast_atof.h index aea793f35..43bbbff64 100644 --- a/include/assimp/fast_atof.h +++ b/include/assimp/fast_atof.h @@ -194,7 +194,7 @@ uint64_t strtoul10_64( const char* in, const char** out=0, unsigned int* max_ino if ( *in < '0' || *in > '9' ) { // The string is known to be bad, so don't risk printing the whole thing. - throw ExceptionType("The string \"", ai_str_toprintable(in, 30), "\" cannot be converted into a value." ); + throw ExceptionType("The string \"", ai_str_toprintable(in, (int)strlen(in)), "\" cannot be converted into a value." ); } for ( ;; ) { @@ -294,7 +294,7 @@ const char* fast_atoreal_move(const char* c, Real& out, bool check_comma = true) if (!(c[0] >= '0' && c[0] <= '9') && !((c[0] == '.' || (check_comma && c[0] == ',')) && c[1] >= '0' && c[1] <= '9')) { // The string is known to be bad, so don't risk printing the whole thing. - throw ExceptionType("Cannot parse string \"", ai_str_toprintable(c, 30), + throw ExceptionType("Cannot parse string \"", ai_str_toprintable(c, (int)strlen(c)), "\" as a real number: does not start with digit " "or decimal point followed by digit."); } diff --git a/include/assimp/light.h b/include/assimp/light.h index bc37de43a..48efdaebd 100644 --- a/include/assimp/light.h +++ b/include/assimp/light.h @@ -257,7 +257,7 @@ struct aiLight #ifdef __cplusplus } -#endif +#endif #endif // !! AI_LIGHT_H_INC diff --git a/include/assimp/material.h b/include/assimp/material.h index 08c0491c0..250ad90d5 100644 --- a/include/assimp/material.h +++ b/include/assimp/material.h @@ -144,7 +144,7 @@ enum aiTextureMapMode { enum aiTextureMapping { /** The mapping coordinates are taken from an UV channel. * - * The #AI_MATKEY_UVWSRC key specifies from which UV channel + * #AI_MATKEY_UVWSRC property specifies from which UV channel * the texture coordinates are to be taken from (remember, * meshes can have more than one UV channel). */ @@ -194,19 +194,23 @@ enum aiTextureType { */ aiTextureType_NONE = 0, - /** LEGACY API MATERIALS - * Legacy refers to materials which + /** LEGACY API MATERIALS + * Legacy refers to materials which * Were originally implemented in the specifications around 2000. * These must never be removed, as most engines support them. */ /** The texture is combined with the result of the diffuse * lighting equation. + * OR + * PBR Specular/Glossiness */ aiTextureType_DIFFUSE = 1, /** The texture is combined with the result of the specular * lighting equation. + * OR + * PBR Specular/Glossiness */ aiTextureType_SPECULAR = 2, @@ -288,6 +292,32 @@ enum aiTextureType { aiTextureType_DIFFUSE_ROUGHNESS = 16, aiTextureType_AMBIENT_OCCLUSION = 17, + /** PBR Material Modifiers + * Some modern renderers have further PBR modifiers that may be overlaid + * on top of the 'base' PBR materials for additional realism. + * These use multiple texture maps, so only the base type is directly defined + */ + + /** Sheen + * Generally used to simulate textiles that are covered in a layer of microfibers + * eg velvet + * https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_sheen + */ + aiTextureType_SHEEN = 19, + + /** Clearcoat + * Simulates a layer of 'polish' or 'laquer' layered on top of a PBR substrate + * https://autodesk.github.io/standard-surface/#closures/coating + * https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_clearcoat + */ + aiTextureType_CLEARCOAT = 20, + + /** Transmission + * Simulates transmission through the surface + * May include further information such as wall thickness + */ + aiTextureType_TRANSMISSION = 21, + /** Unknown texture * * A texture reference that does not match any of the definitions @@ -309,6 +339,8 @@ ASSIMP_API const char *TextureTypeToString(enum aiTextureType in); // --------------------------------------------------------------------------- /** @brief Defines all shading models supported by the library + * + * Property: #AI_MATKEY_SHADING_MODEL * * The list of shading modes has been taken from Blender. * See Blender documentation for more information. The API does @@ -318,6 +350,7 @@ ASSIMP_API const char *TextureTypeToString(enum aiTextureType in); * Again, this value is just a hint. Assimp tries to select the shader whose * most common implementation matches the original rendering results of the * 3D modeler which wrote a particular model as closely as possible. + * */ enum aiShadingMode { /** Flat shading. Shading is done on per-face base, @@ -364,13 +397,28 @@ enum aiShadingMode { aiShadingMode_CookTorrance = 0x8, /** No shading at all. Constant light influence of 1.0. + * Also known as "Unlit" */ aiShadingMode_NoShading = 0x9, + aiShadingMode_Unlit = aiShadingMode_NoShading, // Alias /** Fresnel shading */ aiShadingMode_Fresnel = 0xa, + /** Physically-Based Rendering (PBR) shading using + * Bidirectional scattering/reflectance distribution function (BSDF/BRDF) + * There are multiple methods under this banner, and model files may provide + * data for more than one PBR-BRDF method. + * Applications should use the set of provided properties to determine which + * of their preferred PBR rendering methods are likely to be available + * eg: + * - If AI_MATKEY_METALLIC_FACTOR is set, then a Metallic/Roughness is available + * - If AI_MATKEY_GLOSSINESS_FACTOR is set, then a Specular/Glossiness is available + * Note that some PBR methods allow layering of techniques + */ + aiShadingMode_PBR_BRDF = 0xb, + #ifndef SWIG _aiShadingMode_Force32Bit = INT_MAX #endif @@ -922,12 +970,66 @@ extern "C" { // --------------------------------------------------------------------------- // PBR material support +// -------------------- +// Properties defining PBR rendering techniques #define AI_MATKEY_USE_COLOR_MAP "$mat.useColorMap", 0, 0 + +// Metallic/Roughness Workflow +// --------------------------- +// Base RGBA color factor. Will be multiplied by final base color texture values if extant +// Note: Importers may choose to copy this into AI_MATKEY_COLOR_DIFFUSE for compatibility +// with renderers and formats that do not support Metallic/Roughness PBR #define AI_MATKEY_BASE_COLOR "$clr.base", 0, 0 +#define AI_MATKEY_BASE_COLOR_TEXTURE aiTextureType_BASE_COLOR, 0 #define AI_MATKEY_USE_METALLIC_MAP "$mat.useMetallicMap", 0, 0 +// Metallic factor. 0.0 = Full Dielectric, 1.0 = Full Metal #define AI_MATKEY_METALLIC_FACTOR "$mat.metallicFactor", 0, 0 +#define AI_MATKEY_METALLIC_TEXTURE aiTextureType_METALNESS, 0 #define AI_MATKEY_USE_ROUGHNESS_MAP "$mat.useRoughnessMap", 0, 0 +// Roughness factor. 0.0 = Perfectly Smooth, 1.0 = Completely Rough #define AI_MATKEY_ROUGHNESS_FACTOR "$mat.roughnessFactor", 0, 0 +#define AI_MATKEY_ROUGHNESS_TEXTURE aiTextureType_DIFFUSE_ROUGHNESS, 0 + +// Specular/Glossiness Workflow +// --------------------------- +// Diffuse/Albedo Color. Note: Pure Metals have a diffuse of {0,0,0} +// AI_MATKEY_COLOR_DIFFUSE +// Specular Color. +// Note: Metallic/Roughness may also have a Specular Color +// AI_MATKEY_COLOR_SPECULAR +#define AI_MATKEY_SPECULAR_FACTOR "$mat.specularFactor", 0, 0 +// Glossiness factor. 0.0 = Completely Rough, 1.0 = Perfectly Smooth +#define AI_MATKEY_GLOSSINESS_FACTOR "$mat.glossinessFactor", 0, 0 + +// Sheen +// ----- +// Sheen base RGB color. Default {0,0,0} +#define AI_MATKEY_SHEEN_COLOR_FACTOR "$clr.sheen.factor", 0, 0 +// Sheen Roughness Factor. +#define AI_MATKEY_SHEEN_ROUGHNESS_FACTOR "$mat.sheen.roughnessFactor", 0, 0 +#define AI_MATKEY_SHEEN_COLOR_TEXTURE aiTextureType_SHEEN, 0 +#define AI_MATKEY_SHEEN_ROUGHNESS_TEXTURE aiTextureType_SHEEN, 1 + +// Clearcoat +// --------- +// Clearcoat layer intensity. 0.0 = none (disabled) +#define AI_MATKEY_CLEARCOAT_FACTOR "$mat.clearcoat.factor", 0, 0 +#define AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR "$mat.clearcoat.roughnessFactor", 0, 0 +#define AI_MATKEY_CLEARCOAT_TEXTURE aiTextureType_CLEARCOAT, 0 +#define AI_MATKEY_CLEARCOAT_ROUGHNESS_TEXTURE aiTextureType_CLEARCOAT, 1 +#define AI_MATKEY_CLEARCOAT_NORMAL_TEXTURE aiTextureType_CLEARCOAT, 2 + +// Transmission +// ------------ +// https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_transmission +// Base percentage of light transmitted through the surface. 0.0 = Opaque, 1.0 = Fully transparent +#define AI_MATKEY_TRANSMISSION_FACTOR "$mat.transmission.factor", 0, 0 +// Texture defining percentage of light transmitted through the surface. +// Multiplied by AI_MATKEY_TRANSMISSION_FACTOR +#define AI_MATKEY_TRANSMISSION_TEXTURE aiTextureType_TRANSMISSION, 0 + +// Emissive +// -------- #define AI_MATKEY_USE_EMISSIVE_MAP "$mat.useEmissiveMap", 0, 0 #define AI_MATKEY_EMISSIVE_INTENSITY "$mat.emissiveIntensity", 0, 0 #define AI_MATKEY_USE_AO_MAP "$mat.useAOMap", 0, 0 @@ -1397,8 +1499,6 @@ ASSIMP_API C_ENUM aiReturn aiGetMaterialFloatArray( ai_real *pOut, unsigned int *pMax); -#ifdef __cplusplus - // --------------------------------------------------------------------------- /** @brief Retrieve a single float property with a specific key from the material. * @@ -1418,7 +1518,7 @@ ASSIMP_API C_ENUM aiReturn aiGetMaterialFloatArray( * @return Specifies whether the key has been found. If not, the output * float remains unmodified.*/ // --------------------------------------------------------------------------- -inline aiReturn aiGetMaterialFloat(const aiMaterial *pMat, +inline aiReturn aiGetMaterialFloat(const C_STRUCT aiMaterial *pMat, const char *pKey, unsigned int type, unsigned int index, @@ -1426,14 +1526,6 @@ inline aiReturn aiGetMaterialFloat(const aiMaterial *pMat, return aiGetMaterialFloatArray(pMat, pKey, type, index, pOut, (unsigned int *)0x0); } -#else - -// Use our friend, the C preprocessor -#define aiGetMaterialFloat (pMat, type, index, pKey, pOut) \ - aiGetMaterialFloatArray(pMat, type, index, pKey, pOut, NULL) - -#endif //!__cplusplus - // --------------------------------------------------------------------------- /** @brief Retrieve an array of integer values with a specific key * from a material @@ -1446,8 +1538,6 @@ ASSIMP_API C_ENUM aiReturn aiGetMaterialIntegerArray(const C_STRUCT aiMaterial * int *pOut, unsigned int *pMax); -#ifdef __cplusplus - // --------------------------------------------------------------------------- /** @brief Retrieve an integer property with a specific key from a material * @@ -1461,14 +1551,6 @@ inline aiReturn aiGetMaterialInteger(const C_STRUCT aiMaterial *pMat, return aiGetMaterialIntegerArray(pMat, pKey, type, index, pOut, (unsigned int *)0x0); } -#else - -// use our friend, the C preprocessor -#define aiGetMaterialInteger (pMat, type, index, pKey, pOut) \ - aiGetMaterialIntegerArray(pMat, type, index, pKey, pOut, NULL) - -#endif //!__cplusplus - // --------------------------------------------------------------------------- /** @brief Retrieve a color value from the material property table * diff --git a/include/assimp/matrix4x4.h b/include/assimp/matrix4x4.h index c929ef2a9..6caf7686c 100644 --- a/include/assimp/matrix4x4.h +++ b/include/assimp/matrix4x4.h @@ -230,7 +230,7 @@ public: * @param out Receives the output matrix * @return Reference to the output matrix */ - static aiMatrix4x4t& Translation( const aiVector3t& v, + static aiMatrix4x4t& Translation( const aiVector3t& v, aiMatrix4x4t& out); // ------------------------------------------------------------------- diff --git a/include/assimp/matrix4x4.inl b/include/assimp/matrix4x4.inl index 88315ac28..c1dd87b65 100644 --- a/include/assimp/matrix4x4.inl +++ b/include/assimp/matrix4x4.inl @@ -421,7 +421,7 @@ void aiMatrix4x4t::Decompose(aiVector3t& pScaling, aiVector3tmArmature and aiBone->mNode generically * The point of these is it saves you later having to calculate these elements * This is useful when handling rest information or skin information - * If you have multiple armatures on your models we strongly recommend enabling this - * Instead of writing your own multi-root, multi-armature lookups we have done the + * If you have multiple armatures on your models we strongly recommend enabling this + * Instead of writing your own multi-root, multi-armature lookups we have done the * hard work for you :) */ aiProcess_PopulateArmatureData = 0x4000, @@ -579,7 +579,7 @@ enum aiPostProcessSteps * of the imported model. And if so, it uses that. */ aiProcess_EmbedTextures = 0x10000000, - + // aiProcess_GenEntityMeshes = 0x100000, // aiProcess_OptimizeAnimations = 0x200000 // aiProcess_FixTexturePaths = 0x200000 diff --git a/include/assimp/quaternion.h b/include/assimp/quaternion.h index 7c096fe00..6941fbbc3 100644 --- a/include/assimp/quaternion.h +++ b/include/assimp/quaternion.h @@ -73,7 +73,7 @@ public: explicit aiQuaterniont( const aiMatrix3x3t& pRotMatrix); /** Construct from euler angles */ - aiQuaterniont( TReal rotx, TReal roty, TReal rotz); + aiQuaterniont( TReal roty, TReal rotz, TReal rotx); /** Construct from an axis-angle pair */ aiQuaterniont( aiVector3t axis, TReal angle); diff --git a/include/assimp/scene.h b/include/assimp/scene.h index 336eb1ebd..dcf803572 100644 --- a/include/assimp/scene.h +++ b/include/assimp/scene.h @@ -70,7 +70,7 @@ extern "C" { #endif // ------------------------------------------------------------------------------- -/** +/** * A node in the imported hierarchy. * * Each node has name, a parent node (except for the root node), @@ -149,12 +149,12 @@ struct ASSIMP_API aiNode * @param name Name to search for * @return nullptr or a valid Node if the search was successful. */ - inline + inline const aiNode* FindNode(const aiString& name) const { return FindNode(name.data); } - inline + inline aiNode* FindNode(const aiString& name) { return FindNode(name.data); } @@ -353,34 +353,34 @@ struct aiScene //! Check whether the scene contains meshes //! Unless no special scene flags are set this will always be true. - inline bool HasMeshes() const { - return mMeshes != nullptr && mNumMeshes > 0; + inline bool HasMeshes() const { + return mMeshes != nullptr && mNumMeshes > 0; } //! Check whether the scene contains materials //! Unless no special scene flags are set this will always be true. - inline bool HasMaterials() const { - return mMaterials != nullptr && mNumMaterials > 0; + inline bool HasMaterials() const { + return mMaterials != nullptr && mNumMaterials > 0; } //! Check whether the scene contains lights - inline bool HasLights() const { - return mLights != nullptr && mNumLights > 0; + inline bool HasLights() const { + return mLights != nullptr && mNumLights > 0; } //! Check whether the scene contains textures inline bool HasTextures() const { - return mTextures != nullptr && mNumTextures > 0; + return mTextures != nullptr && mNumTextures > 0; } //! Check whether the scene contains cameras inline bool HasCameras() const { - return mCameras != nullptr && mNumCameras > 0; + return mCameras != nullptr && mNumCameras > 0; } //! Check whether the scene contains animations - inline bool HasAnimations() const { - return mAnimations != nullptr && mNumAnimations > 0; + inline bool HasAnimations() const { + return mAnimations != nullptr && mNumAnimations > 0; } //! Returns a short filename from a full path @@ -395,22 +395,35 @@ struct aiScene //! Returns an embedded texture const aiTexture* GetEmbeddedTexture(const char* filename) const { + return GetEmbeddedTextureAndIndex(filename).first; + } + + //! Returns an embedded texture and its index + std::pair GetEmbeddedTextureAndIndex(const char* filename) const { + if (nullptr==filename) { + return std::make_pair(nullptr, -1); + } // lookup using texture ID (if referenced like: "*1", "*2", etc.) if ('*' == *filename) { int index = std::atoi(filename + 1); - if (0 > index || mNumTextures <= static_cast(index)) - return nullptr; - return mTextures[index]; + if (0 > index || mNumTextures <= static_cast(index)) { + return std::make_pair(nullptr, -1); + } + return std::make_pair(mTextures[index], index); } // lookup using filename const char* shortFilename = GetShortFilename(filename); + if (nullptr == shortFilename) { + return std::make_pair(nullptr, -1); + } + for (unsigned int i = 0; i < mNumTextures; i++) { const char* shortTextureFilename = GetShortFilename(mTextures[i]->mFilename.C_Str()); if (strcmp(shortTextureFilename, shortFilename) == 0) { - return mTextures[i]; + return std::make_pair(mTextures[i], i); } } - return nullptr; + return std::make_pair(nullptr, -1); } #endif // __cplusplus diff --git a/include/assimp/vector2.inl b/include/assimp/vector2.inl index 0ca440e72..b51dd0ec2 100644 --- a/include/assimp/vector2.inl +++ b/include/assimp/vector2.inl @@ -190,7 +190,7 @@ aiVector2t operator + (const aiVector2t& v1, const aiVector2t -inline +inline aiVector2t operator - (const aiVector2t& v1, const aiVector2t& v2) { return aiVector2t( v1.x - v2.x, v1.y - v2.y); } @@ -198,7 +198,7 @@ aiVector2t operator - (const aiVector2t& v1, const aiVector2t -inline +inline TReal operator * (const aiVector2t& v1, const aiVector2t& v2) { return v1.x*v2.x + v1.y*v2.y; } @@ -206,7 +206,7 @@ TReal operator * (const aiVector2t& v1, const aiVector2t& v2) { // ------------------------------------------------------------------------------------------------ // scalar multiplication template -inline +inline aiVector2t operator * ( TReal f, const aiVector2t& v) { return aiVector2t( f*v.x, f*v.y); } @@ -214,7 +214,7 @@ aiVector2t operator * ( TReal f, const aiVector2t& v) { // ------------------------------------------------------------------------------------------------ // and the other way around template -inline +inline aiVector2t operator * ( const aiVector2t& v, TReal f) { return aiVector2t( f*v.x, f*v.y); } @@ -222,7 +222,7 @@ aiVector2t operator * ( const aiVector2t& v, TReal f) { // ------------------------------------------------------------------------------------------------ // scalar division template -inline +inline aiVector2t operator / ( const aiVector2t& v, TReal f) { return v * (1/f); } @@ -230,7 +230,7 @@ aiVector2t operator / ( const aiVector2t& v, TReal f) { // ------------------------------------------------------------------------------------------------ // vector division template -inline +inline aiVector2t operator / ( const aiVector2t& v, const aiVector2t& v2) { return aiVector2t(v.x / v2.x,v.y / v2.y); } @@ -238,7 +238,7 @@ aiVector2t operator / ( const aiVector2t& v, const aiVector2t -inline +inline aiVector2t operator - ( const aiVector2t& v) { return aiVector2t( -v.x, -v.y); } diff --git a/include/assimp/vector3.h b/include/assimp/vector3.h index b084cf9c3..e3ad0b680 100644 --- a/include/assimp/vector3.h +++ b/include/assimp/vector3.h @@ -5,8 +5,6 @@ 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, @@ -65,27 +63,49 @@ template class aiMatrix3x3t; template class aiMatrix4x4t; // --------------------------------------------------------------------------- -/** Represents a three-dimensional vector. */ +/// @brief Represents a three-dimensional vector. +// --------------------------------------------------------------------------- template class aiVector3t { public: + /// @brief The default class constructor. aiVector3t() AI_NO_EXCEPT : x(), y(), z() {} + + /// @brief The class constructor with the components. + /// @param _x The x-component for the vector. + /// @param _y The y-component for the vector. + /// @param _z The z-component for the vector. aiVector3t(TReal _x, TReal _y, TReal _z) : x(_x), y(_y), z(_z) {} + + /// @brief The class constructor with a default value. + /// @param _xyz The value for x, y and z. explicit aiVector3t (TReal _xyz ) : x(_xyz), y(_xyz), z(_xyz) {} + + /// @brief The copy constructor. + /// @param o The instance to copy from. aiVector3t( const aiVector3t& o ) = default; - // combined operators + /// @brief combined operators + /// @brief The copy constructor. const aiVector3t& operator += (const aiVector3t& o); + + /// @brief The copy constructor. const aiVector3t& operator -= (const aiVector3t& o); + + /// @brief The copy constructor. const aiVector3t& operator *= (TReal f); + + /// @brief The copy constructor. const aiVector3t& operator /= (TReal f); - // transform vector by matrix + /// @brief Transform vector by matrix aiVector3t& operator *= (const aiMatrix3x3t& mat); aiVector3t& operator *= (const aiMatrix4x4t& mat); - // access a single element + /// @brief access a single element, const. TReal operator[](unsigned int i) const; + + /// @brief access a single element, non-const. TReal& operator[](unsigned int i); // comparison @@ -93,6 +113,7 @@ public: bool operator!= (const aiVector3t& other) const; bool operator < (const aiVector3t& other) const; + /// @brief bool Equal(const aiVector3t& other, TReal epsilon = 1e-6) const; template diff --git a/packaging/windows-innosetup/readme_installer.txt b/packaging/windows-innosetup/readme_installer.txt index 6ea969dc1..252c396af 100644 --- a/packaging/windows-innosetup/readme_installer.txt +++ b/packaging/windows-innosetup/readme_installer.txt @@ -10,7 +10,7 @@ http://assimp.sf.net Troubleshooting =============== -1. Missing d3dx9_(some-number).dll? +1. Missing d3dx9_(some-number).dll? Install the latest DirectX runtime or grab the file from somewhere (that's evil but mostly fine). 2. Application configuration not correct / missing msvcr***.dll? diff --git a/packaging/windows-innosetup/readme_installer_vieweronly.txt b/packaging/windows-innosetup/readme_installer_vieweronly.txt index 1e84c577d..0acd6ef52 100644 --- a/packaging/windows-innosetup/readme_installer_vieweronly.txt +++ b/packaging/windows-innosetup/readme_installer_vieweronly.txt @@ -19,7 +19,7 @@ Viewer Troubleshooting =============== -1. Missing d3dx9_(number).dll? +1. Missing d3dx9_(number).dll? Install the latest DirectX runtime or grab the file from somewhere (that's evil but mostly fine). 2. Application configuration not correct / missing msvcr***.dll? diff --git a/packaging/windows-mkzip/bin_readme.txt b/packaging/windows-mkzip/bin_readme.txt index 5cff1f30e..f4402d6bf 100644 --- a/packaging/windows-mkzip/bin_readme.txt +++ b/packaging/windows-mkzip/bin_readme.txt @@ -19,7 +19,7 @@ Viewer Troubleshooting =============== -1. Missing d3dx9_42.dll? +1. Missing d3dx9_42.dll? Install the latest DirectX runtime or grab the file from somewhere (that's evil but mostly fine). 2. Application configuration not correct / missing msv*** DLLs? diff --git a/port/AndroidJNI/CMakeLists.txt b/port/AndroidJNI/CMakeLists.txt index 43e842848..8c821c72a 100644 --- a/port/AndroidJNI/CMakeLists.txt +++ b/port/AndroidJNI/CMakeLists.txt @@ -3,10 +3,10 @@ cmake_minimum_required(VERSION 3.10) include_directories(./) include_directories(./../../) add_library( # Defines the name of the library. - android_jniiosystem + android_jniiosystem # Implements a static library. - STATIC + STATIC # relative path to source file(s). AndroidJNIIOSystem.cpp diff --git a/port/AssimpDelphi/Readme.txt b/port/AssimpDelphi/Readme.txt index 07d6935ae..1ec6d2109 100644 --- a/port/AssimpDelphi/Readme.txt +++ b/port/AssimpDelphi/Readme.txt @@ -1,6 +1,6 @@ This is a set of Delphi units for using the Assimp C DLL. This was created for use with Delphi 7, but should be usable as-is or with minimal modifications with later Delphi versions. -This set of headers is enough to load and display a model with external textures. Since I'm not familiar with animated models and some of the other functionality of the assimp library, I did not convert the headers for those features. +This set of headers is enough to load and display a model with external textures. Since I'm not familiar with animated models and some of the other functionality of the assimp library, I did not convert the headers for those features. See http://sourceforge.net/tracker/?func=detail&aid=3212646&group_id=226462&atid=1067634 for the original patch diff --git a/port/PyAssimp/pyassimp/helper.py b/port/PyAssimp/pyassimp/helper.py index 7a4b2bdcb..7c14f6097 100644 --- a/port/PyAssimp/pyassimp/helper.py +++ b/port/PyAssimp/pyassimp/helper.py @@ -34,7 +34,8 @@ if os.name=='posix': additional_dirs.extend([item for item in os.environ['LD_LIBRARY_PATH'].split(':') if item]) # check if running from anaconda. - if "conda" or "continuum" in sys.version.lower(): + anaconda_keywords = ("conda", "continuum") + if any(k in sys.version.lower() for k in anaconda_keywords): cur_path = get_python_lib() pattern = re.compile('.*\/lib\/') conda_lib = pattern.match(cur_path).group() diff --git a/port/jassimp/jassimp-native/src/jassimp.cpp b/port/jassimp/jassimp-native/src/jassimp.cpp index 0cf01b1e3..6661ce9c4 100644 --- a/port/jassimp/jassimp-native/src/jassimp.cpp +++ b/port/jassimp/jassimp-native/src/jassimp.cpp @@ -15,7 +15,7 @@ #define lprintf(...) printf (__VA_ARGS__) #endif /* ANDROID */ #else -#define lprintf +#define lprintf #endif static std::string gLastErrorString; @@ -63,7 +63,7 @@ static bool createInstance(JNIEnv *env, const char* className, jobject& newInsta newInstance = env->NewObject(clazz, ctr_id); - if (NULL == newInstance) + if (NULL == newInstance) { lprintf("error calling no-arg constructor for class %s\n", className); return false; @@ -94,7 +94,7 @@ static bool createInstance(JNIEnv *env, const char* className, const char* signa newInstance = env->NewObjectA(clazz, ctr_id, params); - if (NULL == newInstance) + if (NULL == newInstance) { lprintf("error calling constructor for class %s, signature %s\n", className, signature); return false; @@ -229,7 +229,7 @@ static bool getStaticField(JNIEnv *env, const char* className, const char* field } -static bool call(JNIEnv *env, jobject object, const char* typeName, const char* methodName, +static bool call(JNIEnv *env, jobject object, const char* typeName, const char* methodName, const char* signature,/* const*/ jvalue* params) { jclass clazz = env->FindClass(typeName); @@ -275,7 +275,7 @@ static bool callv(JNIEnv *env, jobject object, const char* typeName, return true; } -static jobject callo(JNIEnv *env, jobject object, const char* typeName, const char* methodName, +static jobject callo(JNIEnv *env, jobject object, const char* typeName, const char* methodName, const char* signature,/* const*/ jvalue* params) { jclass clazz = env->FindClass(typeName); @@ -300,7 +300,7 @@ static jobject callo(JNIEnv *env, jobject object, const char* typeName, const ch return jReturnValue; } -static int calli(JNIEnv *env, jobject object, const char* typeName, const char* methodName, +static int calli(JNIEnv *env, jobject object, const char* typeName, const char* methodName, const char* signature) { jclass clazz = env->FindClass(typeName); @@ -325,7 +325,7 @@ static int calli(JNIEnv *env, jobject object, const char* typeName, const char* return (int) jReturnValue; } -static int callc(JNIEnv *env, jobject object, const char* typeName, const char* methodName, +static int callc(JNIEnv *env, jobject object, const char* typeName, const char* methodName, const char* signature) { jclass clazz = env->FindClass(typeName); @@ -351,7 +351,7 @@ static int callc(JNIEnv *env, jobject object, const char* typeName, const char* } -static bool callStaticObject(JNIEnv *env, const char* typeName, const char* methodName, +static bool callStaticObject(JNIEnv *env, const char* typeName, const char* methodName, const char* signature,/* const*/ jvalue* params, jobject& returnValue) { jclass clazz = env->FindClass(typeName); @@ -441,13 +441,13 @@ static bool copyBufferArray(JNIEnv *env, jobject jMesh, const char* jBufferName, class JavaIOStream : public Assimp::IOStream { -private: +private: size_t pos; size_t size; char* buffer; jobject jIOStream; - + public: JavaIOStream(size_t size, char* buffer, jobject jIOStream) : pos(0), @@ -455,28 +455,28 @@ public: buffer(buffer), jIOStream(jIOStream) {}; - - - ~JavaIOStream(void) + + + ~JavaIOStream(void) { free(buffer); - }; + }; size_t Read(void* pvBuffer, size_t pSize, size_t pCount) { const size_t cnt = std::min(pCount,(size - pos)/pSize); const size_t ofs = pSize*cnt; - + memcpy(pvBuffer, buffer + pos, ofs); pos += ofs; - + return cnt; }; - size_t Write(const void* pvBuffer, size_t pSize, size_t pCount) + size_t Write(const void* pvBuffer, size_t pSize, size_t pCount) { return 0; }; - + aiReturn Seek(size_t pOffset, aiOrigin pOrigin) { if (aiOrigin_SET == pOrigin) { @@ -499,40 +499,40 @@ public: } return AI_SUCCESS; }; - + size_t Tell(void) const { return pos; }; - + size_t FileSize() const { return size; }; - + void Flush() {}; - - + + jobject javaObject() { return jIOStream; }; - - + + }; - + class JavaIOSystem : public Assimp::IOSystem { private: JNIEnv* mJniEnv; jobject& mJavaIOSystem; - + public: JavaIOSystem(JNIEnv* env, jobject& javaIOSystem) : mJniEnv(env), mJavaIOSystem(javaIOSystem) {}; - + bool Exists( const char* pFile) const { jvalue params[1]; @@ -544,27 +544,27 @@ class JavaIOSystem : public Assimp::IOSystem { { return (char) callc(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "getOsSeparator", "()C"); }; - + Assimp::IOStream* Open(const char* pFile,const char* pMode = "rb") { jvalue params[2]; params[0].l = mJniEnv->NewStringUTF(pFile); params[1].l = mJniEnv->NewStringUTF(pMode); - - + + jobject jStream = callo(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "open", "(Ljava/lang/String;Ljava/lang/String;)Ljassimp/AiIOStream;", params); if(NULL == jStream) { lprintf("NULL object from AiIOSystem.open\n"); return NULL; } - + size_t size = calli(mJniEnv, jStream, "jassimp/AiIOStream", "getFileSize", "()I"); lprintf("Model file size is %d\n", size); - + char* buffer = (char*)malloc(size); jobject javaBuffer = mJniEnv->NewDirectByteBuffer(buffer, size); - + jvalue readParams[1]; readParams[0].l = javaBuffer; if(call(mJniEnv, jStream, "jassimp/AiIOStream", "read", "(Ljava/nio/ByteBuffer;)Z", readParams)) @@ -581,28 +581,28 @@ class JavaIOSystem : public Assimp::IOSystem { }; void Close( Assimp::IOStream* pFile) { - + jvalue params[1]; params[0].l = ((JavaIOStream*) pFile)->javaObject(); callv(mJniEnv, mJavaIOSystem, "jassimp/AiIOSystem", "close", "(Ljassimp/AiIOStream;)V", params); delete pFile; }; - - + + }; class JavaProgressHandler : public Assimp::ProgressHandler { private: JNIEnv* mJniEnv; jobject& mJavaProgressHandler; - + public: JavaProgressHandler(JNIEnv* env, jobject& javaProgressHandler) : mJniEnv(env), mJavaProgressHandler(javaProgressHandler) {}; - + bool Update(float percentage) { jvalue params[1]; @@ -623,12 +623,12 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) jobject jMesh = NULL; SmartLocalRef refMesh(env, jMesh); - if (!createInstance(env, "jassimp/AiMesh", jMesh)) + if (!createInstance(env, "jassimp/AiMesh", jMesh)) { return false; } - + /* add mesh to m_meshes java.util.List */ jobject jMeshes = NULL; SmartLocalRef refMeshes(env, jMeshes); @@ -671,7 +671,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) /* determine face buffer size */ bool isPureTriangle = cMesh->mPrimitiveTypes == aiPrimitiveType_TRIANGLE; size_t faceBufferSize; - if (isPureTriangle) + if (isPureTriangle) { faceBufferSize = cMesh->mNumFaces * 3 * sizeof(unsigned int); } @@ -715,7 +715,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) /* push face data to java */ if (cMesh->mNumFaces > 0) { - if (isPureTriangle) + if (isPureTriangle) { char* faceBuffer = (char*) malloc(faceBufferSize); @@ -729,7 +729,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) free(faceBuffer); - if (!res) + if (!res) { lprintf("could not copy face data\n"); return false; @@ -750,7 +750,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) memcpy(faceBuffer + faceBufferPos, cMesh->mFaces[face].mIndices, faceDataSize); faceBufferPos += faceDataSize; } - + if (faceBufferPos != faceBufferSize) { /* this should really not happen */ @@ -766,7 +766,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) free(faceBuffer); free(offsetBuffer); - if (!res) + if (!res) { lprintf("could not copy face data\n"); return false; @@ -933,7 +933,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) jobject jBone; SmartLocalRef refBone(env, jBone); - if (!createInstance(env, "jassimp/AiBone", jBone)) + if (!createInstance(env, "jassimp/AiBone", jBone)) { return false; } @@ -988,7 +988,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) wrapParams[0].l = jMatrixArr; jobject jMatrix; SmartLocalRef refMatrix(env, jMatrix); - + if (!callStaticObject(env, "jassimp/Jassimp", "wrapMatrix", "([F)Ljava/lang/Object;", wrapParams, jMatrix)) { return false; @@ -1012,7 +1012,7 @@ static bool loadMeshes(JNIEnv *env, const aiScene* cScene, jobject& jScene) { return false; } - + if (!setFloatField(env, jBoneWeight, "m_weight", cBone->mWeights[w].mWeight)) { return false; @@ -1228,7 +1228,7 @@ static bool loadSceneNode(JNIEnv *env, const aiNode *cNode, jobject parent, jobj wrapNodeParams[3].l = jNodeName; jobject jNode; if (!callStaticObject(env, "jassimp/Jassimp", "wrapSceneNode", - "(Ljava/lang/Object;Ljava/lang/Object;[ILjava/lang/String;)Ljava/lang/Object;", wrapNodeParams, jNode)) + "(Ljava/lang/Object;Ljava/lang/Object;[ILjava/lang/String;)Ljava/lang/Object;", wrapNodeParams, jNode)) { return false; } @@ -1287,7 +1287,7 @@ static bool loadSceneGraph(JNIEnv *env, const aiScene* cScene, jobject& jScene) } -static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) +static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) { for (unsigned int m = 0; m < cScene->mNumMaterials; m++) { @@ -1320,7 +1320,7 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) } /* set texture numbers */ - for (int ttInd = aiTextureType_DIFFUSE; ttInd < aiTextureType_UNKNOWN; ttInd++) + for (int ttInd = aiTextureType_DIFFUSE; ttInd < aiTextureType_UNKNOWN; ttInd++) { aiTextureType tt = static_cast(ttInd); @@ -1341,7 +1341,7 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) for (unsigned int p = 0; p < cMaterial->mNumProperties; p++) { - //printf("%s - %u - %u\n", cScene->mMaterials[m]->mProperties[p]->mKey.C_Str(), + //printf("%s - %u - %u\n", cScene->mMaterials[m]->mProperties[p]->mKey.C_Str(), // cScene->mMaterials[m]->mProperties[p]->mSemantic, // cScene->mMaterials[m]->mProperties[p]->mDataLength); @@ -1362,9 +1362,9 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) /* special case conversion for color3 */ - if (NULL != strstr(cProperty->mKey.C_Str(), "clr") && + if (NULL != strstr(cProperty->mKey.C_Str(), "clr") && cProperty->mType == aiPTI_Float && - cProperty->mDataLength == 3 * sizeof(float)) + cProperty->mDataLength == 3 * sizeof(float)) { jobject jData = NULL; SmartLocalRef refData(env, jData); @@ -1380,16 +1380,16 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) } constructorParams[4].l = jData; - if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", + if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", constructorParams, jProperty)) { return false; } } /* special case conversion for color4 */ - else if (NULL != strstr(cProperty->mKey.C_Str(), "clr") && + else if (NULL != strstr(cProperty->mKey.C_Str(), "clr") && cProperty->mType == aiPTI_Float && - cProperty->mDataLength == 4 * sizeof(float)) + cProperty->mDataLength == 4 * sizeof(float)) { jobject jData = NULL; SmartLocalRef refData(env, jData); @@ -1406,13 +1406,13 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) } constructorParams[4].l = jData; - if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", + if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", constructorParams, jProperty)) { return false; } } - else if (cProperty->mType == aiPTI_Float && cProperty->mDataLength == sizeof(float)) + else if (cProperty->mType == aiPTI_Float && cProperty->mDataLength == sizeof(float)) { jobject jData = NULL; SmartLocalRef refData(env, jData); @@ -1425,13 +1425,13 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) } constructorParams[4].l = jData; - if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", + if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", constructorParams, jProperty)) { return false; } } - else if (cProperty->mType == aiPTI_Integer && cProperty->mDataLength == sizeof(int)) + else if (cProperty->mType == aiPTI_Integer && cProperty->mDataLength == sizeof(int)) { jobject jData = NULL; SmartLocalRef refData(env, jData); @@ -1444,26 +1444,26 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) } constructorParams[4].l = jData; - if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", + if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", constructorParams, jProperty)) { return false; } } - else if (cProperty->mType == aiPTI_String) + else if (cProperty->mType == aiPTI_String) { /* skip length prefix */ jobject jData = env->NewStringUTF(cProperty->mData + 4); SmartLocalRef refData(env, jData); constructorParams[4].l = jData; - if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", + if (!createInstance(env, "jassimp/AiMaterial$Property", "(Ljava/lang/String;IIILjava/lang/Object;)V", constructorParams, jProperty)) { return false; } } - else + else { constructorParams[4].i = cProperty->mDataLength; @@ -1521,7 +1521,7 @@ static bool loadMaterials(JNIEnv *env, const aiScene* cScene, jobject& jScene) } -static bool loadAnimations(JNIEnv *env, const aiScene* cScene, jobject& jScene) +static bool loadAnimations(JNIEnv *env, const aiScene* cScene, jobject& jScene) { lprintf("converting %d animations ...\n", cScene->mNumAnimations); @@ -1603,19 +1603,19 @@ static bool loadAnimations(JNIEnv *env, const aiScene* cScene, jobject& jScene) } /* copy keys */ - if (!copyBuffer(env, jNodeAnim, "m_posKeys", cNodeAnim->mPositionKeys, + if (!copyBuffer(env, jNodeAnim, "m_posKeys", cNodeAnim->mPositionKeys, cNodeAnim->mNumPositionKeys * sizeof(aiVectorKey))) { return false; } - if (!copyBuffer(env, jNodeAnim, "m_rotKeys", cNodeAnim->mRotationKeys, + if (!copyBuffer(env, jNodeAnim, "m_rotKeys", cNodeAnim->mRotationKeys, cNodeAnim->mNumRotationKeys * sizeof(aiQuatKey))) { return false; } - if (!copyBuffer(env, jNodeAnim, "m_scaleKeys", cNodeAnim->mScalingKeys, + if (!copyBuffer(env, jNodeAnim, "m_scaleKeys", cNodeAnim->mScalingKeys, cNodeAnim->mNumScalingKeys * sizeof(aiVectorKey))) { return false; @@ -1629,7 +1629,7 @@ static bool loadAnimations(JNIEnv *env, const aiScene* cScene, jobject& jScene) } -static bool loadLights(JNIEnv *env, const aiScene* cScene, jobject& jScene) +static bool loadLights(JNIEnv *env, const aiScene* cScene, jobject& jScene) { lprintf("converting %d lights ...\n", cScene->mNumLights); @@ -1712,7 +1712,7 @@ static bool loadLights(JNIEnv *env, const aiScene* cScene, jobject& jScene) params[9].l = jAmbient; params[10].f = cLight->mAngleInnerCone; params[11].f = cLight->mAngleOuterCone; - + if (!createInstance(env, "jassimp/AiLight", "(Ljava/lang/String;ILjava/lang/Object;Ljava/lang/Object;FFFLjava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;FF)V", params, jLight)) { @@ -1736,13 +1736,13 @@ static bool loadLights(JNIEnv *env, const aiScene* cScene, jobject& jScene) } } - lprintf("converting lights finished ...\n"); + lprintf("converting lights finished ...\n"); return true; } -static bool loadCameras(JNIEnv *env, const aiScene* cScene, jobject& jScene) +static bool loadCameras(JNIEnv *env, const aiScene* cScene, jobject& jScene) { lprintf("converting %d cameras ...\n", cScene->mNumCameras); @@ -1799,7 +1799,7 @@ static bool loadCameras(JNIEnv *env, const aiScene* cScene, jobject& jScene) params[5].f = cCamera->mClipPlaneNear; params[6].f = cCamera->mClipPlaneFar; params[7].f = cCamera->mAspect; - + if (!createInstance(env, "jassimp/AiCamera", "(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;FFFF)V", params, jCamera)) { @@ -1901,25 +1901,25 @@ JNIEXPORT jstring JNICALL Java_jassimp_Jassimp_getErrorString JNIEXPORT jobject JNICALL Java_jassimp_Jassimp_aiImportFile (JNIEnv *env, jclass jClazz, jstring jFilename, jlong postProcess, jobject ioSystem, jobject progressHandler) { - jobject jScene = NULL; + jobject jScene = NULL; /* convert params */ const char* cFilename = env->GetStringUTFChars(jFilename, NULL); - + Assimp::Importer imp; - + if(ioSystem != NULL) { - imp.SetIOHandler(new JavaIOSystem(env, ioSystem)); + imp.SetIOHandler(new JavaIOSystem(env, ioSystem)); lprintf("Created aiFileIO\n"); } - + if(progressHandler != NULL) { imp.SetProgressHandler(new JavaProgressHandler(env, progressHandler)); } - + lprintf("opening file: %s\n", cFilename); /* do import */ @@ -1931,7 +1931,7 @@ JNIEXPORT jobject JNICALL Java_jassimp_Jassimp_aiImportFile goto error; } - if (!createInstance(env, "jassimp/AiScene", jScene)) + if (!createInstance(env, "jassimp/AiScene", jScene)) { goto error; } diff --git a/samples/SimpleAssimpViewX/ModelLoaderHelperClasses.h b/samples/SimpleAssimpViewX/ModelLoaderHelperClasses.h index 0e6dfab3c..1803ad122 100644 --- a/samples/SimpleAssimpViewX/ModelLoaderHelperClasses.h +++ b/samples/SimpleAssimpViewX/ModelLoaderHelperClasses.h @@ -15,7 +15,7 @@ /* workflow: - 1) create a new scene wrapper + 1) create a new scene wrapper 2) populate an array of of meshHelpers for each mesh in the original scene 3) (eventually) create an animator instance 4) scale the asset (needed?) @@ -24,16 +24,16 @@ 5b) create a static vertex buffer 5c) create index buffer 5d) populate the index buffer - 5e) (eventually) gather weights + 5e) (eventually) gather weights */ #define BUFFER_OFFSET(i) ((char *)NULL + (i)) -struct Vertex +struct Vertex { aiVector3D vPosition; aiVector3D vNormal; - + aiColor4D dColorDiffuse; aiVector3D vTangent; aiVector3D vBitangent; @@ -46,33 +46,33 @@ struct Vertex // Helper Class to store GPU related resources from a given aiMesh // Modeled after AssimpView asset helper -@interface MeshHelper : NSObject -{ +@interface MeshHelper : NSObject +{ // Display list ID, this one shots *all drawing* of the mesh. Only ever use this to draw. Booya. GLuint displayList; - + // VAO that encapsulates all VBO drawing state GLuint vao; - + // VBOs GLuint vertexBuffer; GLuint indexBuffer; GLuint normalBuffer; GLuint numIndices; - + // texture GLuint textureID; - - // Material + + // Material aiColor4D diffuseColor; aiColor4D specularColor; aiColor4D ambientColor; aiColor4D emissiveColor; - + GLfloat opacity; GLfloat shininess; GLfloat specularStrength; - + BOOL twoSided; } diff --git a/samples/SimpleAssimpViewX/MyDocument.h b/samples/SimpleAssimpViewX/MyDocument.h index 16745dc3c..67675e306 100644 --- a/samples/SimpleAssimpViewX/MyDocument.h +++ b/samples/SimpleAssimpViewX/MyDocument.h @@ -20,27 +20,27 @@ #import -@interface MyDocument : NSPersistentDocument +@interface MyDocument : NSPersistentDocument { CVDisplayLinkRef _displayLink; NSOpenGLContext* _glContext; NSOpenGLPixelFormat* _glPixelFormat; - + NSView* _view; - + // Assimp Stuff aiScene* _scene; aiVector3D scene_min, scene_max, scene_center; - double normalizedScale; - + double normalizedScale; + // Our array of textures. GLuint *textureIds; - + // only used if we use - NSMutableArray* modelMeshes; + NSMutableArray* modelMeshes; BOOL builtBuffers; - - NSMutableDictionary* textureDictionary; // Array of Dicionaries that map image filenames to textureIds + + NSMutableDictionary* textureDictionary; // Array of Dicionaries that map image filenames to textureIds } @property (retain) IBOutlet NSView* _view; diff --git a/samples/SimpleOpenGL/Sample_SimpleOpenGL.c b/samples/SimpleOpenGL/Sample_SimpleOpenGL.c index bcb109564..7aa306ed4 100644 --- a/samples/SimpleOpenGL/Sample_SimpleOpenGL.c +++ b/samples/SimpleOpenGL/Sample_SimpleOpenGL.c @@ -29,7 +29,7 @@ /* ---------------------------------------------------------------------------- */ inline static void print_run_command(const char* command_name) { - printf("Run '%s %s' for more information.\n", + printf("Run '%s %s' for more information.\n", PROJECT_NAME, command_name); } @@ -43,7 +43,7 @@ inline static void print_error(const char* msg) { /* ---------------------------------------------------------------------------- */ inline static void print_usage() { - static const char* usage_format = + static const char* usage_format = "Usage: " PROJECT_NAME " " DOUBLE_NEW_LINE diff --git a/samples/SimpleTexturedDirectx11/CMakeLists.txt b/samples/SimpleTexturedDirectx11/CMakeLists.txt index 82144caa9..007ada3af 100644 --- a/samples/SimpleTexturedDirectx11/CMakeLists.txt +++ b/samples/SimpleTexturedDirectx11/CMakeLists.txt @@ -24,13 +24,13 @@ LINK_DIRECTORIES( ) ADD_EXECUTABLE( assimp_simpletextureddirectx11 WIN32 - SimpleTexturedDirectx11/Mesh.h + SimpleTexturedDirectx11/Mesh.h SimpleTexturedDirectx11/ModelLoader.cpp SimpleTexturedDirectx11/ModelLoader.h #SimpleTexturedDirectx11/PixelShader.hlsl SimpleTexturedDirectx11/TextureLoader.cpp - SimpleTexturedDirectx11/TextureLoader.h - #SimpleTexturedDirectx11/VertexShader.hlsl + SimpleTexturedDirectx11/TextureLoader.h + #SimpleTexturedDirectx11/VertexShader.hlsl SimpleTexturedDirectx11/main.cpp SimpleTexturedDirectx11/SafeRelease.hpp ${SAMPLES_SHARED_CODE_DIR}/UTFConverter.cpp diff --git a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/ModelLoader.cpp b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/ModelLoader.cpp index 92760d691..18bb10f1e 100644 --- a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/ModelLoader.cpp +++ b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/ModelLoader.cpp @@ -1,6 +1,6 @@ #include "ModelLoader.h" -ModelLoader::ModelLoader() : +ModelLoader::ModelLoader() : dev_(nullptr), devcon_(nullptr), meshes_(), diff --git a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/TextureLoader.cpp b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/TextureLoader.cpp index 01ba343e8..a02c53ca6 100644 --- a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/TextureLoader.cpp +++ b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/TextureLoader.cpp @@ -140,16 +140,16 @@ static WICConvert g_WICConvert[] = { GUID_WICPixelFormatBlackWhite, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM - { GUID_WICPixelFormat1bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat2bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat4bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat8bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat1bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat2bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat4bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat8bppIndexed, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat2bppGray, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM - { GUID_WICPixelFormat4bppGray, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM + { GUID_WICPixelFormat2bppGray, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM + { GUID_WICPixelFormat4bppGray, GUID_WICPixelFormat8bppGray }, // DXGI_FORMAT_R8_UNORM - { GUID_WICPixelFormat16bppGrayFixedPoint, GUID_WICPixelFormat16bppGrayHalf }, // DXGI_FORMAT_R16_FLOAT - { GUID_WICPixelFormat32bppGrayFixedPoint, GUID_WICPixelFormat32bppGrayFloat }, // DXGI_FORMAT_R32_FLOAT + { GUID_WICPixelFormat16bppGrayFixedPoint, GUID_WICPixelFormat16bppGrayHalf }, // DXGI_FORMAT_R16_FLOAT + { GUID_WICPixelFormat32bppGrayFixedPoint, GUID_WICPixelFormat32bppGrayFloat }, // DXGI_FORMAT_R32_FLOAT #ifdef DXGI_1_2_FORMATS @@ -165,10 +165,10 @@ static WICConvert g_WICConvert[] = { GUID_WICPixelFormat32bppBGR101010, GUID_WICPixelFormat32bppRGBA1010102 }, // DXGI_FORMAT_R10G10B10A2_UNORM - { GUID_WICPixelFormat24bppBGR, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat24bppRGB, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat32bppPBGRA, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM - { GUID_WICPixelFormat32bppPRGBA, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat24bppBGR, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat24bppRGB, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat32bppPBGRA, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat32bppPRGBA, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM { GUID_WICPixelFormat48bppRGB, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM { GUID_WICPixelFormat48bppBGR, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM @@ -176,21 +176,21 @@ static WICConvert g_WICConvert[] = { GUID_WICPixelFormat64bppPRGBA, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM { GUID_WICPixelFormat64bppPBGRA, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM - { GUID_WICPixelFormat48bppRGBFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat48bppBGRFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat64bppRGBAFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat64bppBGRAFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat64bppRGBFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat64bppRGBHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat48bppRGBHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat48bppRGBFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat48bppBGRFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppRGBAFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppBGRAFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppRGBFixedPoint, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppRGBHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat48bppRGBHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT - { GUID_WICPixelFormat96bppRGBFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT - { GUID_WICPixelFormat128bppPRGBAFloat, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT - { GUID_WICPixelFormat128bppRGBFloat, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT - { GUID_WICPixelFormat128bppRGBAFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT - { GUID_WICPixelFormat128bppRGBFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat96bppRGBFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppPRGBAFloat, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppRGBFloat, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppRGBAFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT + { GUID_WICPixelFormat128bppRGBFixedPoint, GUID_WICPixelFormat128bppRGBAFloat }, // DXGI_FORMAT_R32G32B32A32_FLOAT - { GUID_WICPixelFormat32bppCMYK, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM + { GUID_WICPixelFormat32bppCMYK, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM { GUID_WICPixelFormat64bppCMYK, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM { GUID_WICPixelFormat40bppCMYKAlpha, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM { GUID_WICPixelFormat80bppCMYKAlpha, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM @@ -198,7 +198,7 @@ static WICConvert g_WICConvert[] = #if (_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/) { GUID_WICPixelFormat32bppRGB, GUID_WICPixelFormat32bppRGBA }, // DXGI_FORMAT_R8G8B8A8_UNORM { GUID_WICPixelFormat64bppRGB, GUID_WICPixelFormat64bppRGBA }, // DXGI_FORMAT_R16G16B16A16_UNORM - { GUID_WICPixelFormat64bppPRGBAHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT + { GUID_WICPixelFormat64bppPRGBAHalf, GUID_WICPixelFormat64bppRGBAHalf }, // DXGI_FORMAT_R16G16B16A16_FLOAT #endif // We don't support n-channel formats diff --git a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/main.cpp b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/main.cpp index 02e2b6088..3f8d15320 100644 --- a/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/main.cpp +++ b/samples/SimpleTexturedDirectx11/SimpleTexturedDirectx11/main.cpp @@ -128,7 +128,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, int argc; LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &argc); if (!argv) { - MessageBox(nullptr, + MessageBox(nullptr, TEXT("An error occured while reading command line arguments."), TEXT("Error!"), MB_ICONERROR | MB_OK); @@ -145,8 +145,8 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, // Ensure that a model file has been specified. if (argc < 2) { - MessageBox(nullptr, - TEXT("No model file specified. The program will now close."), + MessageBox(nullptr, + TEXT("No model file specified. The program will now close."), TEXT("Error!"), MB_ICONERROR | MB_OK); free_command_line_allocated_memory(); @@ -157,7 +157,7 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, g_ModelPath = UTFConverter(argv[1]).str(); free_command_line_allocated_memory(); - + WNDCLASSEX wc; MSG msg; @@ -573,7 +573,7 @@ void InitGraphics() HRESULT CompileShaderFromFile(LPCWSTR pFileName, const D3D_SHADER_MACRO* pDefines, LPCSTR pEntryPoint, LPCSTR pShaderModel, ID3DBlob** ppBytecodeBlob) { UINT compileFlags = D3DCOMPILE_ENABLE_STRICTNESS | D3DCOMPILE_PACK_MATRIX_COLUMN_MAJOR; - + #ifdef _DEBUG compileFlags |= D3DCOMPILE_DEBUG; #endif diff --git a/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp b/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp index aa2344118..be0e651f1 100644 --- a/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp +++ b/samples/SimpleTexturedOpenGL/SimpleTexturedOpenGL/src/model_loading.cpp @@ -23,7 +23,7 @@ #endif // _MSC_VER #define STB_IMAGE_IMPLEMENTATION -#include "contrib/stb_image/stb_image.h" +#include "contrib/stb/stb_image.h" #ifdef _MSC_VER #pragma warning(default: 4100) // Enable warning 'unreferenced formal parameter' @@ -579,7 +579,7 @@ void KillGLWindow() // Properly Kill The Window if (!DestroyWindow(g_hWnd)) // Are We Able To Destroy The Window MessageBox(nullptr, TEXT("Could Not Release hWnd."), TEXT("SHUTDOWN ERROR"), MB_OK | MB_ICONINFORMATION); g_hWnd = nullptr; - } + } if (g_hInstance) { @@ -727,7 +727,7 @@ BOOL CreateGLWindow(const char* title, int width, int height, int bits, bool ful return FALSE; } - if (nullptr == (hRC=wglCreateContext(hDC))) + if (nullptr == (hRC=wglCreateContext(hDC))) { abortGLInit("Can't Create A GL Rendering Context."); return FALSE; diff --git a/test/models-nonbsd/3D/mar_rifle.source.txt b/test/models-nonbsd/3D/mar_rifle.source.txt index c0cd5fe6d..d7183b7b6 100644 --- a/test/models-nonbsd/3D/mar_rifle.source.txt +++ b/test/models-nonbsd/3D/mar_rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/3DS/cart_wheel.source.txt b/test/models-nonbsd/3DS/cart_wheel.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/3DS/cart_wheel.source.txt +++ b/test/models-nonbsd/3DS/cart_wheel.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/3DS/mar_rifle.source.txt b/test/models-nonbsd/3DS/mar_rifle.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/3DS/mar_rifle.source.txt +++ b/test/models-nonbsd/3DS/mar_rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/3DS/mp5_sil.source.txt b/test/models-nonbsd/3DS/mp5_sil.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/3DS/mp5_sil.source.txt +++ b/test/models-nonbsd/3DS/mp5_sil.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/ASE/Rifle.source.txt b/test/models-nonbsd/ASE/Rifle.source.txt index 1b96f8564..802c57045 100644 --- a/test/models-nonbsd/ASE/Rifle.source.txt +++ b/test/models-nonbsd/ASE/Rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,9 +11,9 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/ASE/Rifle2.source.txt b/test/models-nonbsd/ASE/Rifle2.source.txt index 3fa628db4..7b50d1645 100644 --- a/test/models-nonbsd/ASE/Rifle2.source.txt +++ b/test/models-nonbsd/ASE/Rifle2.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,9 +11,9 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/BLEND/fleurOptonl.source.txt b/test/models-nonbsd/BLEND/fleurOptonl.source.txt index b9c58b5d9..d4c583620 100644 --- a/test/models-nonbsd/BLEND/fleurOptonl.source.txt +++ b/test/models-nonbsd/BLEND/fleurOptonl.source.txt @@ -10,7 +10,7 @@ Puoi utilizzarlo liberamente, modificarlo e migliorarlo. ************* Ma attenzione!! ********************* Nel modificare e utilizzare i modelli, ricorda comunque sempre che : -"il diritto morale all'integrità dell'opera (diritto dell'autore originale) non ti consente di apportare all'opera deformazioni o modificazioni, od ogni altro atto a danno dell'opera stessa, che possano essere di pregiudizio all'onore o alla reputazione dell'autore (la valutazione della lesione dell'onore o della reputazione avviene sulla base di elementi psicologici soggettivi)" +"il diritto morale all'integrità dell'opera (diritto dell'autore originale) non ti consente di apportare all'opera deformazioni o modificazioni, od ogni altro atto a danno dell'opera stessa, che possano essere di pregiudizio all'onore o alla reputazione dell'autore (la valutazione della lesione dell'onore o della reputazione avviene sulla base di elementi psicologici soggettivi)" (dalle faq di Creative Commons Italia) http://www.creativecommons.it/node/165#27 @@ -26,7 +26,7 @@ In particolare, sara' da me considerata lesione d'onore l'uso e/o l'adattamento Se lo fate rischiate la denuncia e il risarcimento danni. Per qualsiasi chiarimento in proposito potete comunque scrivermi. -Questo e' un diritto garantito per legge a me come ad ogni altro artista. +Questo e' un diritto garantito per legge a me come ad ogni altro artista. L'utilizzo della Creative Commons non influisce su questo diritto. ************************************************ @@ -45,7 +45,7 @@ work, so you are not allowed to do exactely what you want with my models. The author (that is me) has the right to prevent distortion, mutilation, or other modification of his work which would be prejudicial to his or her honor or reputation. Me, i consider to be prejudicial to my honor, the modification and/or the use of my models in projects that are related to: - + Racism and hatred instigation. War promotion. Cruelty toward animals. @@ -69,15 +69,15 @@ Note Questo e' stato il mio secondo modello completo. Per questo motivo potrebbe contenere errori e imprecisioni. - + Al momento, questi sono i difetti che ho notato: - + - Non e' nell'origine degli assi. - lo scheletro ha il centro spostato di lato - Nel texture sheet c'e' spazio sprecato. - - + + ################### Notes @@ -86,7 +86,7 @@ This was my first complete model, so it probably contains something wrong. At the moment, this is what i noticed: - + - She's not in the origin of axis - Armature's center is not in armature's center. - Texture sheet contains wasted space. diff --git a/test/models-nonbsd/DXF/rifle.source.txt b/test/models-nonbsd/DXF/rifle.source.txt index a2585afad..fa25f10db 100644 --- a/test/models-nonbsd/DXF/rifle.source.txt +++ b/test/models-nonbsd/DXF/rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,9 +11,9 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/FBX/2013_ASCII/cart_wheel.source.txt b/test/models-nonbsd/FBX/2013_ASCII/cart_wheel.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/FBX/2013_ASCII/cart_wheel.source.txt +++ b/test/models-nonbsd/FBX/2013_ASCII/cart_wheel.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/FBX/2013_ASCII/kwxport_test_vcolors.fbx.source.txt b/test/models-nonbsd/FBX/2013_ASCII/kwxport_test_vcolors.fbx.source.txt index 94ee48e4a..7c81e612f 100644 --- a/test/models-nonbsd/FBX/2013_ASCII/kwxport_test_vcolors.fbx.source.txt +++ b/test/models-nonbsd/FBX/2013_ASCII/kwxport_test_vcolors.fbx.source.txt @@ -1,9 +1,9 @@ From kwxport http://www.kwxport.org/ ->> -The kW Xport plug-in source is released under the MIT license. -Basically, it means "feel free to use it; credit the source; don't sue me +>> +The kW Xport plug-in source is released under the MIT license. +Basically, it means "feel free to use it; credit the source; don't sue me if something goes wrong." >> diff --git a/test/models-nonbsd/FBX/2013_ASCII/mar_rifle.source.txt b/test/models-nonbsd/FBX/2013_ASCII/mar_rifle.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/FBX/2013_ASCII/mar_rifle.source.txt +++ b/test/models-nonbsd/FBX/2013_ASCII/mar_rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/FBX/2013_ASCII/mp5_sil.source.txt b/test/models-nonbsd/FBX/2013_ASCII/mp5_sil.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/FBX/2013_ASCII/mp5_sil.source.txt +++ b/test/models-nonbsd/FBX/2013_ASCII/mp5_sil.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/FBX/2013_BINARY/cart_wheel.source.txt b/test/models-nonbsd/FBX/2013_BINARY/cart_wheel.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/FBX/2013_BINARY/cart_wheel.source.txt +++ b/test/models-nonbsd/FBX/2013_BINARY/cart_wheel.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/FBX/2013_BINARY/kwxport_test_vcolors.fbx.source.txt b/test/models-nonbsd/FBX/2013_BINARY/kwxport_test_vcolors.fbx.source.txt index 94ee48e4a..7c81e612f 100644 --- a/test/models-nonbsd/FBX/2013_BINARY/kwxport_test_vcolors.fbx.source.txt +++ b/test/models-nonbsd/FBX/2013_BINARY/kwxport_test_vcolors.fbx.source.txt @@ -1,9 +1,9 @@ From kwxport http://www.kwxport.org/ ->> -The kW Xport plug-in source is released under the MIT license. -Basically, it means "feel free to use it; credit the source; don't sue me +>> +The kW Xport plug-in source is released under the MIT license. +Basically, it means "feel free to use it; credit the source; don't sue me if something goes wrong." >> diff --git a/test/models-nonbsd/FBX/2013_BINARY/mar_rifle.source.txt b/test/models-nonbsd/FBX/2013_BINARY/mar_rifle.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/FBX/2013_BINARY/mar_rifle.source.txt +++ b/test/models-nonbsd/FBX/2013_BINARY/mar_rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/FBX/2013_BINARY/mp5_sil.source.txt b/test/models-nonbsd/FBX/2013_BINARY/mp5_sil.source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/FBX/2013_BINARY/mp5_sil.source.txt +++ b/test/models-nonbsd/FBX/2013_BINARY/mp5_sil.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/LWO/LWO2/LWSReferences/QuickDraw.source.txt b/test/models-nonbsd/LWO/LWO2/LWSReferences/QuickDraw.source.txt index aaa244217..80afd30a7 100644 --- a/test/models-nonbsd/LWO/LWO2/LWSReferences/QuickDraw.source.txt +++ b/test/models-nonbsd/LWO/LWO2/LWSReferences/QuickDraw.source.txt @@ -1,10 +1,10 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas -"These 3d models are contributed by John Hoffman and are based on -characters from a cartoon show called "Jayce and the wheel warriors" +"These 3d models are contributed by John Hoffman and are based on +characters from a cartoon show called "Jayce and the wheel warriors" (except the marauder) John's site: http://www3.sympatico.ca/john.hoffman" ===================================================================== @@ -15,9 +15,9 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/LWO/LWO2/rifle.source.txt b/test/models-nonbsd/LWO/LWO2/rifle.source.txt index 5523bbc0c..5774ecc0e 100644 --- a/test/models-nonbsd/LWO/LWO2/rifle.source.txt +++ b/test/models-nonbsd/LWO/LWO2/rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,9 +11,9 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/MD2/source.txt b/test/models-nonbsd/MD2/source.txt index 6f43a3a22..bf74fa057 100644 --- a/test/models-nonbsd/MD2/source.txt +++ b/test/models-nonbsd/MD2/source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,8 +11,8 @@ Notice found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models-nonbsd/MD5/BoarMan.source.txt b/test/models-nonbsd/MD5/BoarMan.source.txt index 50b2dfb53..69f0c79a2 100644 --- a/test/models-nonbsd/MD5/BoarMan.source.txt +++ b/test/models-nonbsd/MD5/BoarMan.source.txt @@ -1,8 +1,8 @@ -License: Creative Commons +License: Creative Commons - Remix - Share alike - Attribution Author: zphr (Christian Lenke) - + diff --git a/test/models-nonbsd/MDL/IDPO (Quake1)/gijoe-readme.txt b/test/models-nonbsd/MDL/IDPO (Quake1)/gijoe-readme.txt index 2febb0583..84ce9fd95 100644 --- a/test/models-nonbsd/MDL/IDPO (Quake1)/gijoe-readme.txt +++ b/test/models-nonbsd/MDL/IDPO (Quake1)/gijoe-readme.txt @@ -4,9 +4,9 @@ Version : 1 Date : 11/05/97 Author : Kenneth Whelan Email : JWHELAN@pop.prodigy.net -Credits : id software, Larry Hama, Steven Polge, and Rene Post for making Quake ME - - +Credits : id software, Larry Hama, Steven Polge, and Rene Post for making Quake ME + + Build time: ??? Time??? @@ -14,7 +14,7 @@ Type of Mod ----------- Quake C : no Sound : no -MDL : Yes +MDL : Yes Format of QuakeC (if a Quake C Mod) @@ -29,8 +29,8 @@ Description of the Modification ------------------------------- This is a new player.mdl for quake. It's main use is for bots. The Skins are Snake Eyes v4, Duke v3, Low-Light, -Storm Shadow v2, Shockwave, Repeater, Gung-Ho, Shipwreck, Dusty v3, and -Tunnel Rat v2. +Storm Shadow v2, Shockwave, Repeater, Gung-Ho, Shipwreck, Dusty v3, and +Tunnel Rat v2. @@ -40,7 +40,7 @@ None that I know of. How to Install the Modification ------------------------------- -First back up the current player.mdl(copy player.mdl player.bak). Just put +First back up the current player.mdl(copy player.mdl player.bak). Just put it in the progs dir in the hack your using, if any. Technical Details diff --git a/test/models-nonbsd/MDL/IDPO (Quake1)/steg.txt b/test/models-nonbsd/MDL/IDPO (Quake1)/steg.txt index c07c2f126..cbddccb7e 100644 --- a/test/models-nonbsd/MDL/IDPO (Quake1)/steg.txt +++ b/test/models-nonbsd/MDL/IDPO (Quake1)/steg.txt @@ -16,17 +16,17 @@ E-mail: sgalbrai@linknet.kitsap.lib.wa.us WWW: www.oz.net/~simitar -This model can be used or modified for any purpose +This model can be used or modified for any purpose as long as this text document is included with it -and all modellers listed in this document are +and all modellers listed in this document are listed wherever credits are appropriate for that purpose. -Help Wanted: +Help Wanted: The Free Models Project can use help with -models and in other areas, such as legal boilerplate +models and in other areas, such as legal boilerplate for models. WWW: www.oz.net/~simitar/model.html diff --git a/test/models-nonbsd/MDL/IDPO (Quake1)/tekmechbot.txt b/test/models-nonbsd/MDL/IDPO (Quake1)/tekmechbot.txt index dc0149b01..3b3dd9042 100644 --- a/test/models-nonbsd/MDL/IDPO (Quake1)/tekmechbot.txt +++ b/test/models-nonbsd/MDL/IDPO (Quake1)/tekmechbot.txt @@ -8,7 +8,7 @@ that the FMP is a great concept !! There is some movement in the model. The legs can be animated for walking, stomping, or running -around ! +around ! Ok, it's my first model, so I will work on less polygony in the future ;-) @@ -18,9 +18,9 @@ Contact: ebuy@optelnow.net (E-MAIL) Date Created: 7/07/2000 ====================================================== -This model can be used or modified for any purpose +This model can be used or modified for any purpose as long as this text document is included with it -and all modelers listed in this document are +and all modelers listed in this document are listed wherever credits are appropriate for that purpose. diff --git a/test/models-nonbsd/NFF/NFFSense8/credits.txt b/test/models-nonbsd/NFF/NFFSense8/credits.txt index f3cef4d09..bad7fbf15 100644 --- a/test/models-nonbsd/NFF/NFFSense8/credits.txt +++ b/test/models-nonbsd/NFF/NFFSense8/credits.txt @@ -1,4 +1,4 @@ teapot.nff, home4.nff - http://www.martinreddy.net/ukvrsig/wtk.html -cokecan.nff -www.vrupl.evl.uic.edu/Eng591_Pages/cokecan.nff +cokecan.nff -www.vrupl.evl.uic.edu/Eng591_Pages/cokecan.nff TODO: License status to be confirmed diff --git a/test/models-nonbsd/OBJ/rifle.source.txt b/test/models-nonbsd/OBJ/rifle.source.txt index 1d2cec5cf..f7b93fd0f 100644 --- a/test/models-nonbsd/OBJ/rifle.source.txt +++ b/test/models-nonbsd/OBJ/rifle.source.txt @@ -1,7 +1,7 @@ ===================================================================== From http://telias.free.fr -Model copyright: Elias Tsiantas +Model copyright: Elias Tsiantas ===================================================================== @@ -11,9 +11,9 @@ Notices found on the page: " Free the models is a site that offers free 3d models in 3ds, bryce, poser, lightwave and md2 format. Also a great collection of textures to use in -your favorite modelling and rendering program. All the content is free +your favorite modelling and rendering program. All the content is free for any use. In the future more 3d formats will be added and some other -sections such as wallpapers, 3d screensavers, 3d coding source code and +sections such as wallpapers, 3d screensavers, 3d coding source code and tutorials. " diff --git a/test/models/3DS/UVTransformTest/note.txt b/test/models/3DS/UVTransformTest/note.txt index 4c8bfedd2..9a6bab030 100644 --- a/test/models/3DS/UVTransformTest/note.txt +++ b/test/models/3DS/UVTransformTest/note.txt @@ -1,5 +1,5 @@ -All 'mirror' files are not absolutely correct. That's mainly -because it's difficult convert Max' handling of mirroring to +All 'mirror' files are not absolutely correct. That's mainly +because it's difficult convert Max' handling of mirroring to our's. In other words: TO DO, but only if someone REALLY needs it. diff --git a/test/models/ASE/MotionCaptureROM.source.txt b/test/models/ASE/MotionCaptureROM.source.txt index 2b961906a..f34e056ef 100644 --- a/test/models/ASE/MotionCaptureROM.source.txt +++ b/test/models/ASE/MotionCaptureROM.source.txt @@ -1,3 +1,3 @@ -"MotionCaptureROM.ase" - Free for any purpose. +"MotionCaptureROM.ase" - Free for any purpose. NOTE: The errors in the middle of the animation are caused by problems during recording, it's not an importer issue. diff --git a/test/models/Collada/human.zae b/test/models/Collada/human.zae new file mode 100644 index 0000000000000000000000000000000000000000..691b09f83f6ac2baf2d6bb55026da9b95470c536 GIT binary patch literal 1093924 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

    uuGR&r@9o{lX?*e|=%DZ$(yMTN$V&+tmT zq9}jKxQt?H1AQ&@QpiIJe~Z6E_mBbUN3Z$q--Ur?nl%z%Kmp+r^-OY(m+q%YFX>e# zfamFei0isY`GikyaAdrnPkr?gioP5Eyv0j$d4mkrg>`7SIq2PCTM!F5=m9_G3m`X} z+r331kGDPQ9!xTE$FDNNDNgM4&KL;V3)Rx<8un+PS69wJGciITvV^9+#1D((dpT;W zq+;~6=?GHoNwPC_iNhBP@IXegoSjZ{$`XBwf?p&boOit_O)VO4H^{#Q-{D$fN8bJ| zLOtW+MIvOwA+^AGZkm8M-S-<0aFMy(kaetM4(a`~a5)Y=cZV2HH}n*dIh6D8mktfI z!t=}>>P|X6d7DQeklt7PARf!M_X9oX-MhXGR));w9^%2?{kEAe{DN(aPVsV!>WmS_ zUB+YChpa6LuuW^WF2{zU@}Qx&c7+gX2U|!ADr>FTeMyo{dP=|Npmdg1OOPvDEex&d zM))={!X6?;S*0M@pB7%YaH6UgdO}3&0y}v?J}k0n8!sr|q%mhL5vFWES)&cEWRob^ zh0NMn<9+PD>qtnG$% zVX@QyR<-g*7)*8y@bRKxxb=d7UrZ)GEOuqm61?C7o~-pUYK78yi)e)jV5I2+QU34z(hgos(cc(LhC()+dA zsSG-+{pgK~Y{ctY_9DImv`CyC!j`o}`o0@2QizA9z$FHm2xU=9*<`vWS@*rnOA&e< z@xq)&;y;1On?Dz&t_LV8Voz)Hl&P_|7wq!b8sUa&Uf74lOM^2m>UsQmN`!}SAZHr? zI`2*yVj*A~y01yqa_d9G60q5T$$#!8ehDX0H``#jD2jMbIp~Ce@DOaWDyq>L00fzu z8DPP?!rB`#Y?wwiCD+}xD|UrZt?of9qtDDhieLw+EwXrsb-+?;nTh7j7fh&7bTTIN zTbDhW2*aIrP2ma5RL9>ong;FqFBpBjSc;xYJUhkNMHFer%9{4JB+7d4zQlZy-!)p| zM(W7)lG+!NUT3ecf8B0XyOF&5(~Hs4YgxGR?gKE7P74bJWMxU3|v-*^hU9BV+`aMP(>G5Md z?ew(U!wy5c(~ZE2JLa#jxAwf&@4lz++P?X#6d)n2ZuVXKV27{Yv;+Oj@2}9wsyei@ z&9`-rn5>m&`F~gWruLR~v6GzWbENy8_rv-&yUDOCE&ZbHdv*TEu6^ve;_m0qXH_f8 znklX6f%{6GJ>c-A|33TESEp{RW&!B4F>3ui=bv{Nf1cWvuutE!;&0lS&mN_($DSXn z_+rSjg34L@K1==X*lz{L_gS*KzOCQf-~0V}mLg+;zw)Px*I$XWrXyU9GXC68$>Q%k ze?i6jet%CtXDrXjliBq~g{o>TiaX|-sWtw)a+b&fMp!Xmi&I>PnU@^2pjxI3oK0`- zsY3@5O?;wCW9lSdmv0jBIZ|ZGjDg28x4d`g5d08%!KeI#`14j?E#u z{bHae`Ik!C$NGCEC7ZXk^I0hnUqF0R%?Ofuta6!n9^`zu0yRAEXbs9U^LqECPF4!k zZs;D*5gdGxCH7C=A3{qRekWmk8`yIg_t}gGud6-vz=&DTcclZ-T z$rVPo?7N7G3=C8&G6sUQxwVOk(f{{kN}Y23WXXOfrdc`S(LAQZV`jiRto0E-xj7tvMRqBkjnOL`{!Bc`>bS2Cy6s>KSc`U8gR~COXa8Wo9?Ek@%;**UI#SI z>m-Hf`IVl+&v{92n@@v{gcDlzH@oaVmtU4&#}}@gGL|u8Y#+6`l@h3K{Zp z1>|Tt@6bFs$b?r}Gk(+4*wk*y5_cHiz4eWL`|jS>&sDq&*IK6K`sWI3 zf3zgTqV$pq8T;9zu3l0?TzT%kl~(TlM?-s3Gr^7X9>~~HK0O^umVc*3@D4sFSHtI= zpZi^9cY6Af9!~gAdeooy`z(1sbvaL_yvR|myv&ac_}LX#9*#O$7jTU#jzsi)^&G`c zD}&s@daCk?7ZmAn-;n}YGxT(#QL5T|f9~G&9GAQK@v|yHT25*-7&1YJ%ugo9_lz~Y zCuBbR;2Z$YX4ArQ0^qg_wn@qI4v|lFkAJ84#2Za@GArTUM(^^r&2Orqm5J~9lb6H0 zxAwIbHM@xv5Gh8VRo9GPq1qXV)OW+riq;tAb9bElpYBn6w?C4s2J@-a!pfk&aAe*! z8-K3&Ous-Y;~l&^r^

    *MMruzAc9`aeV4mOVXe!g)p!=rmE|XzS3l&x zKJmy3>uzxD@eK|!r;+Uml-`u`dw%zV&CThzI!AY|CE=gu@mnZn$v8o zvZf-!f-{0D`Y!{#+dmgP>wtAy#AHHmyQ~!4*~m85-`chl^RpqxRsVWOr}+SY2uK*d zztqh{bIy3k|`U6{? z0SGBl56ca!ySCI1E!iK}_}>Ay)4Ye@zPZ2jv{VT4A|;&cxmc5mhw9?#pLD+mThm<=Md&pPq4TFz0(8QP=)m zR0pU7B)U4*3{t7-*k>;tHg%aO_Dd}b(2VphfLL!;dSQ4I#fFQ|fjZN0sco=jm!>+P zG9K16|D3g4wZXQGxH2GVBn(B}J1-V>Qw!d+JF_)&@evSL`L(#GxdEoWRumgDEvT(p z-xNN><^ak_h){%CxiqXM>df!0&|B@;0jP&VplMljMkX4uZQ-pqrR`CbyPH;Z(g{yB zMR~TCm+3wSroGgp-B5WuyWhWS4fw6-mzhH5NF<Ik@|!O`k`v2Bx42R3QGRJt_PclW!gzL`HKS;EKL zo@^OJ)>W!(&?TSO(%0^^FJ>WTz@u{rkQV_72f{{LV)`|Ji5bj9fNdTd1}->!o$%^f z3a;6Zvh==E(@xlSy2}^aZ}=YkN~{N)l4J3e2!*pPLo0}HiEg2LdwUDLVNU9i_3f3g zm8gZ|(r-m~yVy3({6Ho>N2lxr($2UL3c#!1u8>AOi=1jF_V{B)lSkyMb1w_5gn;~KY#dBk~` zW5Q3;PGvF|9J5gz*NX&qGk8VZ`@)OZK7Td=|LWtSDl1EMx=#aZ7O2?30ctu+jbkQG z2eNXMRDRozAP6=ydgNgOJKytI&lpHo`2b=O*d@LsTI{r?2T;zybV`qsU&_Sg?{IV~ zfcgC)46T&~e%}h_DxliTT-8(Ec3Cd>P! z#w?NYW%80`L}kIf2}kA8sk^m4?T!FJzfQ9qQ40zn*MupD}5y(vx{ zLP&!kX8V5p(N>yT)6N~Oy_U6ymxI`-7VMaRddvjTpylBalaEEC69vOG%*`9sau}cm@b}BpYpRe4m|geoz^_yy(?-`q51E zHxEpK`=afJC$WD>3~v5Rsr!8V{SnhZAR3!3(cL9Ms(_s>P&cZsX>6)9i)%)bCtzi4 zPLk)>wrYyluY&Hb5d5Ep6#1=(j9$$tREMMFsVjq7IwqLVu(L*}aR$$?yOfS9sRB8; z>D9=cLE*@+s`-r|0YNWGy=g`sydjx4eww%R_%}!LN`hVq*w($|rY& z^}xf{Lv`VTopEE+(JKCS&-qqr(k3-vG0;<{!}Ypu;Qem723KUvh4fn;b5Mvh?oElh zsffjrtj-l*bk|`{b=!^QPgCvFBaa)$C2&)OSc$mr=$nHzT`EEs9ZJ zY|$ByV1tkZxEtv|l6?)BUj}@~hXyY#s^$wl*GyGJSIo9UVatY zuAhP6LC#y)G8khghIc~;iaOdgtgT$;*O!dJ)SmNn=EQC{Fykl{i@%^G6~Irk?9NPP zw6>IC#@CD+r1v3HfSPm@9=Oy{^v#NH0o)oWXE8#3n=VWuN8PnOnKLj60j@?wGxP?_WVj0xj1rZ7q*Wia(ikKC?h80ep8Y$ zQySi?B-(XB&0k;)HJpT}Hps+*v3A&jZIO3#umoU+tFum^wW zoZwYildJWpVN;FZGUXYS(&AQQJeN1!m6r=Dw~05C?Qtz9e^yIPu52x|k7Vjns6rdM ze^p<%S1*D4jy2c7GG|I-c-`IO*8+o)8a?>o%wD`uk#E8DwMR0Iq4b`-&I$lpoCQzQ6_KY!fWo*KQ+ykxRj6qE+Vg2{}yJdR? zwIQ!Uw^#o1Ch*6NnO>!oO)-f|wAS+PCeSFE z1Xzg8nw!_ORN~#h)WNNH_1caO~ z{p&*caX`}QRn?~tBU3f+nkcoJ`v55=Y(N*Tb=Cpoma~g;uXp2Wh?#RRaqv)mXWNy* zmC+p5)Kwi&ipjs)d}hSuNbdJ1blMG9L6^w;zd8-KF;|-OY?r(VM-dO(mWjWSX|E;; z=Lvubq@jOKqD~mwmWL-3d6FW{)KHh>Vk&#;%DWCZSBZ*7c;O{3l*3&9pC}Ejc6<)E zs)YE7-A+t>bx5loW(*zZ=a!;<;}(I}+}uRc?_J6JY8_;BU&Q6GIwpubZrVMfsl7AM zB*0#+2N+kHheEObJw$2coc?Y50dvXDSYpnFi!5_Umk#{ zb8|a%7&b+ruP<6z_dIFZ{GTBj6<8ZmVyc z`dGaJVyl}OAVJXsJC|(-Dtg~>AdG3xS zMf&0DK5@)c3e>%AMoEx{;tifN5&)WA#2-6{gr0X1;aHzt`dJ4?>J4I1>7PDk_03ve z{43At9o+F%wJ~KSlQXPm{K|s%n>c7AC@N0I6U$*eB(gUV|M-n3w!`SH*|8|Fw&iIO zyb8p>jA_u`iOI4JcYavtRC_0!Ri^hqbP*AHP6qvKa8f>n=Zohsx(44zc1)I4pUlP9 zrZiH-IG)V9$GH5rapxQ30`*pAq&0V88nyR8gL5G63t#Eh>j^)PiWyAfwmeUQJItWV zGsIpb@#5RXrSTz;nFODmyBy7Jis)}#WLfY&4{b)pw@~p)H(XKG z^L7lm0qQWdp2qKOX0PwdQ4%(k8(qL(hA^dS!>k>Y1g|JNek#u!==^%deam%1rW)v; z#d$w;n~DZOJ$|nZKruxV$HqAow=9dBJ006Rm9X>8-cZNX@u0M?dp=P!Rv|Ny-sTf;<(yc!q;1zDQE))>Evr9vi zg}3oDbL!5rt?Z7A)vcTfKX#zI!lw&LNX?U=SIEX8c)QBFZHku zXjA0Kwd}PA4PNP=8tgWQ{Z{Ebe$G`f^`=^(nanx1HE1bM-#Ba=s=Z8X|yb0x~rYj0IOWK3{)to+d{Q*(n-|ITvs_ z@B`c#hY#ql+};Wm`^^mPcd zVk2+1=&#BvM9ZjmX33HJJ8BO?{ws%=)PLm|7A%vY?{MSg8rwRbXlwlmv#qPFst=G( z3Rky>+(h1`x%5C$nBFUcX^RldXLD42Gk92Rmc9&L|7p#_i(FDDzYz=`xwL|C2TjF1 z6}qnTr6+VsozPn8fUJeF%1q}*lM0oEZS4hrDT$^LDnlfM9o#Jt|Mk70@2QUr$@0H( zUwUt`coK$3Q|2PVYnBnDkR*YTKR6k!aBj9pD~q(UDq6n}?m!=UMp@n*p8mJ-jQ(#g z+F^Z7pzrKx2Hpj+1GpG>giay+Zd|Jdw_bH(w2QhExeBELBu04)m2T1Dm$#_Yi-j|N}Lb=tKXb} zPWPGAxh=sMFJ709hz%IhiXrJ^%3k5@*?iwD;;<(H4gO5Q_DL5}0qG0h3b{1XZZ_(U zka2eDUJrs`OjvVli*8ceO0WA3P)v^(!m}~2&Q0MDZ={Hm6&Ak=jF}QGiB=IV-O|M` zztT^F*H6RDj3Z29hAaN+?;=|alE%P+x8-u=!=IYa#wwqjcDGD5x-#A4+%J_3V4t)H z+yBlRXb$~pM;j%f6O%qZkQ2Iq0T>junQUvl@R{na;b>WVg=mJ`cVgaV63!V0#$cRq znd>#O_My%9Cn95<{j#yE6#f}t`m0%ZH4g~EHn^?2u39C&L01KtWAKUQ@K`{*A|SI^ zwd&zTB-*lQ@Gyvb8S0N;E@qk3UyD4=S}o^aKQjseV)PJdR&p0$g)(xBtGF<&LmuII z_E?(1PQO_OpN}_p2Hji4_^X#wBF7dj&y4PBjNr{fH(bq2-L4O+ZgIRwwL1?Y_k%_X zh(PjNQO^-<1F1UI%jL7;Xf2V|Mc2U_gxTqs!{`uV^<;*cw=1uo7AP>_>>HYbYE-c@ zuUEaK57HYH59aPiCjNqFV=AEQ-tyIeT!t&yGNfOA-pc+tyuD)s6E=lk9^hObCk~<) zbv^T4OODscy`Ah7ahH z2mbCxt8Npm-3R1N_x(NQ^v##s)myvf@VW`zo zR7F+T4Ri>-?!L?_OJ{;{r|$qU?)mQf!v`yCSI5MGg={7p_%~W5^F$TckJ>plX8&&` zX5y=_9J~0EW5cm4p)W7VB(p5{<^-B5>g+6mfTKEt-0n;r;ZGB@yBd@VGrlq z_7ebqUq82PP?A3%?t>umJM5o5slWH1zYH;S0{GQfwkIPK1S>`&Euxtz#oc}$z`=N> z0)OzdZ#L;%LwgGxr_iQ|2~?|7MI-5_*4WRt5~3bValpU(-Ctv_%^RJwW}9v`D(R#s z-O4Kn$w}SP=;fN-1sOfx{jyM?(4RA=sdDJ#@mHcl+Fo+KRIFQCCwgpU#;HaSWMWG- zbo8lTr|?p@@HOAu0XDMHnZOSMC^@4jK(C>usFQx36U~u8EQr35G%-Zhx^PcMpZYG9 z@GqA?14r=T=O@3DcQa;tzz z#OiK;h2Md$mNHr8q*;Kqzu(cze$VM=mRgn0j?n2C>4UYGRuN)=dV5eiLRR$Ya2BNp zPP4sUzO}w96`x~o5U%Xf^$s@Iu9#G&0MaZp+;l+{`@;I5ytnjEFl@wTIdG%kBSGKv%u{R8VM-~VF?asj7S+Of!y={vv$-_8y(B@i= ze+JD>FYgRo65Zkj3!S#_bT7TibS||;B}7Yrnv@CFah?{9z2IR3@)%;TS1133(aWj4 zA(SwMeqNiP9P3ewe3%nEKfOiUfvOJQ$~A>JH{ZqgqaXVteAvJix@o&V8EBiE z=>xAwE(53#TvEN|;G7eg_&P>&Z>!zc`bc_aIvlkTi(euIXq1&ugwEu>p90IO}=7oUAHT`ysBIMFf7oUsKR#d}2FOf297X7_i3u1wqyM_fdYbg5EosnCU| z|D0%u6m6;SV1_iGLD=C!KFWg}Y{f&po+4C=6#~`Mpb>o7kaPbkiG#Md78(jpIx=g} z&J_A;996(Rah#-+GeMYD?&&F!5J_H6>|OHf9DIE-dxnR<(XoNqoehq z-$zf~VF7Gk(Vr7y02~Ix=GCL`XNlCB*mPf{@s4xm>xc7$;sr0GH#?RJs&ovgV$T5x2urWcF=P8`jhmyf7O@PFBm48`D!v;S?weenMFqoj=Z%gB^ z^9=&8@MJ%D6r0+R2S32e3%S0AKjtg;D64#KKP-1#oaBfT79f2-h&x~E;p0M^sl1*B zUX}YJ&`zPdNDgk5vP==d_U(JrRoZ;c?*pIn$J(%<0fsMV24cK{mPJ$E(xoR_t-A~7 zJ+d;tG@pMJ|HoN2|4566H4-PBX*e|3a;e>X9xmOt0e$=|bE_Ke%X3oV@@AcDOFp*O zBBU}?KLJ+*wW=Hn_o)hU347U~qbDiZe7ey0)x1DF#H;Pr>2q;bc<|?=4F080>7I*; zH4)_+lZ~y*SxGPCeOsxlgt#04HJCvRWAn0S;O$ak~q*#=q;oEn*Esr>y3m#-*>KXk2~ z=*+ac@X#XIHpQpOUq8!N#kf=%+SwI!K4H1_bnv5RdePO-K;m}s&PAqFoTeAyi!H(S3ZgvZc`sQBsZa2Gaw}C44w;8j~ z@6IoSJ|ty6)7+sm^y1hi&=pbnQkoBHxt>q)pN@zfK{o`auymaEGb0?Pi#re7jO*YZ z3)%kO`T43R5I%N-FD(+xFW*;Ca?bSLo{qWB4@nh}nlH)MSCC~I1*1;s8ylCnN?t#A zOd^}(P|MEK1zLpb$=hA0r?7Dpt;sbi8_A7{1xCMA;}y7jNxwGBvP)dzwq8*_>?yf4 zT;wnqCAd^4|8-4#&ny#XP~nI?;#W;0iz4LbJF&JDDf#L@;c6@vsV?d<-`1qcEH16( z&UclYl~1&<`Q0sATFsRCzlZG>{se%s>o((Ik;AQC9C2i8)l%9Ia zv}wKZUwJ@{oiw;tpAR0RoWYGa#|C?PhjR$2YGiHOu$$&PCgB*hAE_e#eZEK&3DgOk z3}I8%QUi%VpcXp^_)2daj#S52b&ja9L&)Gw@u* z*_=sD_SJh2e4Z4+6-2Bxu&5!jXY^rRzkxIEp=fHjT5X|R`(eL>4ssMqBf)vlVK9wr z7BX8s@BFl7=eUh3wc&hsgC)?Iaw*7l-dKF7%Izt+D2v+@*BZPh=E=(YdHhH@;4sB< zpoE7rDu!)lf%&aAY~R9h#DeAfv+yctEPOUxrvOAMTR*vBCWDaQtMY(fJf{ z!z$t5P|WR>jE^Ez`gcTT08>6a%_22QkZv3&>zo`U$)xx8JgE1dv0^$-9JKR=QfJN& zTX}mHmP?q${1z8(h(6DKr#m*?M((IKSqxD5ZiparC0y>0QT%F*VIwf2a`%0vKjqiM z6O9}xOzoUtQq9X5MyH(k7Anvg3E)y|`RnI2%3`fjFo*gn`=+%gMZf;{UE6Ebu0^GziLmX`k9Jdm96tu%#1>lw&MJ7&*QWEDhNfLHP>9mQT%Wn{(Epb8 z8s6IuS?7~-e6AXfy)(vdtUz>_kAG`NWGw!x>06Gcv(}0@Y_PNJ@k9BoUv1=7!je>lQQcp*m_kEB*+uS^zyb?TF3g{(!HF$BNu7;0-zU29+2WS2M`(usN3l2F}W+=!tg(t2T zmDZ4d82i1i${EkWf?Dn(#t*DnQ*?5g!m+4KHCfPGr=1;(DvO+dqhL>ql~r@4(j#V< zSWV_%NH7l6FH{^_?VsT{#mmP35q)0>gLV1kW>SeOv!w{>I^5P;2q_4*9jK=5ht`~g zr@jU`r@VjvZhJ*S+jz6_d*G;kr5T0!O8tPQSwxjgMP+;g@0gtlA#M^3DWtBwA=t=k zJtHPwhPAeGZ$&CF9I^_uy=&CFKM8eaB(YeNUc^o14`;QDdM02(r_`+Aenk8vYh1Ep z;2)KpWrjF`+c!2H`6T5V7I;nYUxaq&3_d!n(D>jTVyzwao1A=nCoXPy&789;Blx)5 zZpykoFIWMVO!I^1cyzN&NZ#n@Fr>OO2)^}VAzvLl3;3GGrDN2tsp`iTT4lST+rr#^ zZ2O0IUS?`3yO^Lg{DYTAnU-k?kTvH>eCh2^c@yK)j- zyof%rH=3_t%5+j31V|M_<(0{_CxfbXrh(QM$cRQ3J*O_LAV*nEaZrhkOy&=tth&yr z@b&WOt;z5UW@{V2YOT$}eK94@Cb!WGubDtMptCd8aJK+{hqilc|f~ zZy!z7YmG$E!p>k`*~5m&%fX9%MK*mC3HLXOV8-8@bVB zw04L*{DzS(4TTRy{1{7hh7}u5WiG9&4HgER2fzN|sPWi?!JZ*fUi{ zTK3o8nXX#D?ExtPyv7>ImR{XjMecgn$Eka}X6cy1URY}3E+pqnHSt$`{FPP5L#S~d zZz)Iq-Igt`=EEk=!f%0NoABn08szWyQZ1amUWQ#yoV;u;Ra9#r9C)@14xZ{o`N#|N zlVo$EBms_xEJ`B;HC1`Mg`<+s*6W56cfPOh)KWU*KR2d*7un`9>#4%eRx&q7%`52N ze%9SlCv*Jgg6p$@J?(~6TlM8((99c?%_L&iE^r=m%4aa=byX2s1u^~@u7dYBMl3G zw{|8>DyZ2~%}>f9{Q?rDY`yDjmMw2QT4BKBe2QBke}Qs5!PC7h)Ov0jk80l0i=S$G6`L4P%8JE#dqlGPs?2SHi|D6&Ew^>3C5=BAyBE?<5>F;0 z14y!nX4#_)in2Lpna7R_kF=dCGhX85dgr~5TyPVN;4CJOxH(IeBQQ;7)f(m=+2eLe z+Uq$_q*uH!q57Kt_!g1vk#8jO&&BK0E6aoUMV~B1-!vM9*S(JM4DR@sL?xcwAmzvH zQb-zCNel2-fKm}%boI8&L67l;0?N8&rIGkKr6%u2qc2QN8W?E~+4ZjC@X;=R0$n<( zkhlLyA+NfQBRm&#>|`om>G$k!o>NHrsyU@H3IADU?|Eb<7O|pe`gg{QP9yW=FueDI zOZ8&(aHSBA+Fwb>G-)&j$K9`l_!`y42A5EhalM+yU{w_zNoJGL2CWWmz)aFoauTQ` z$)i^_E`)o{*xtQeKB80?&xTYv?9aOv@~AQ{4W|@vzud3OW#{YGV6NXjy18~lmVNok)i>HUq{mM#QbGq~ zlx2~}M>kmb<~R{+fd_M6gXxwJ))srkeywu7=i8KPSE$=y@F5sVedCzB=3vf7xMt>( zu=|UXuq_$Es9?%uV^BUF_`*=>%Td#nP6t9?=$J=g_w}<>WndeB+WCBxnz#cO7uh{K z5B=;os2bm1?cd+_Hm9=RWF>q>DW2<`(tJ>v=B{w7#g*tg!!zR|TJG0qBAdf}sk~e< z*O4~mbX%yZzdBdT3-3dr#6=z}$}|_Ylj^cb$_Qb6-SZ{3$2HIX`j$8(3R{A zNZX>KAs*h`#^yb~EUnWf3)KA8dN|LC9oK&TTR4#GEdIlB-y9IC+f*)39HnNq8ulsQ zV~hY>ZS?+e#JHR3vBlZoJpc1cBKhiZIt7(~+VhbW>R)E;Da2Qgnx1YarI7c#RQ)_O z_(iIm9yvqy>wWgBcwxn{So`N0@2byCq)zoIv;458R#%@Eq2O$XVg8~ovtLQhzDzWd z5mZl@C5TS-6|}$9DEJ|=Rn+8_$ z%n=2<5!=p@Q%CEwFU2|a^s5xyezIgI$`Kt)p3X4a<0*l0x>ahmVp8^cQRl(7*9OJL z>Fr*NfN!%6q%(oiZDN75(Ohvy+CTp4r6M%P4cni|?9C^lT;oj!^fO7nF;(=Xanq9l zIq4@wgkCsQr398LICj@RHesD!e8D$(CctNL!}HkP6<=KZ$6ZIuaNKY$d%EzXvTeTv zqHFIx?4W*eM0ex)qr|}rq6>|htF{8c{j&qw4rQLnGqDR;nMa@zt(!_7B515SNY_N% zhs$xSNRQCwx$n%$QAdr+x4n237$eAa1m9D8u%n26cA^_#ods-jJ8NN=zxx+4%fdP& z=T$*0v}Ty?=?Gg{Uf~Yns(k-1LctM;~L;XLiM`M6xTflIr_9_<|sE9VnLa5%;7=w__)*M zbc8x!w%B8RbwIs?Oh@AqY_P{u$fxD~2PFSvxNbY5%gn*da%bP`MwF8rMkMmJ87soh z;=9LZ836})bj!DpDh=>$^^2d4Zk#W(ct zz)gkmMB^p@k~;9j{0G?ATU+CnEU}q>f~6^%-P90%zH|7jPjDk&W2J5Ee8|o^XtM50 zn^=!X^KSGtpZ^od>IvcC*&fZ~?&fQIM~=)m{#(`TvE@T-M18dDCvp6WJkDF%aLARe zt;{TAI;LTEmfiGcd^rvqz}=dfMCNLxfJ0rObLSaY0z!Y+HM5^!OpyVSj(ummS%CLu zR0TpW;ul51_y>FVr^@*HWFh)+_P{j`8_R3+k;r`M@DlARob7qknhp&!X#9^}uDLE zX`jG9$0lXbo8?5^St?7udYf&}qu@e0%ItFb>Nrzk4Qp`Z^bEb3{d0&r?$!cK6}m%G z&`>*b(2q$ydte+oe;OzqM0PsR%5TxjH%uDAt+~r6mOi1{;F#%^s+&XJb52A7sJl+B zi@xRhZF~AR!TrTWC{Xw4u+7)!v-?YYwa|=224Y%WGPy~qbCFMR&Gq4ue2E`z+V(@J z+NOJBwvCEJ<0D@5`-~B)2-m}l5FPLNc_RpBOw9J)Fz?l?k#c!aWiQGEnZ(RzGK!ZF zU30ztkg}P9aN0YPmA;_Fm(F@$H{`N}dUF;0VW!J_F+YtG(!3hjxy{;!+Fs!J#z+G# zrM)=i(TGh=zz^jW`R>|Yiv^X~sS^(@vo&<)H%OJSY2SGeyN>ND|A(*OFX;0^I-O-A zIiA=ynb)|{&4_mgcixYguTwCzRXM46; zyU`liY%U`7DX6j%vaCmOJSwV}i-G-;5TcD>XeVo*kjdAUl-dx`yTe|xi0 zB1bc`Wu@W3kAGGPj;G0%=_PdbU2&**&F}ywpZYX%+HAqjtWE;e+~BB`c3yMchQyI3 zUX8(EEP_4=zkG;)Kwg}Ke(JnEVQyT*|ET)zc(|VMT|`(!ln|ZOTlC&75fM>Bh~9he zy%Q4A61@{ah~CSp5xo<=vsPcMUhk3b@BTjb{=@8P<;~Od1&5)MK_xAcm4>@8#ac;-o7%f9+>lRy)TXk zk#)QkysK&JX>QshUu=dbnej3_~W7HEGfnHQ*(A6vZznU~!o~2^XQU zPkLTfrf43C23EKE1?6RCzbGZa^`uMWf}kOodYSBHPiO9gS1a-U6m1sy;s`f0m}MpD z+o;kTy5;JPbVC}6Rr3*A+%-v5AzN>DEyasM2{5ofMit#QgVYL8cCObBy>U1P&M^gB z@;COxl{|hjtb$Y;{}2_~V!DZ#uPzIld6Nt)m=3=ZqCRg`?r=5Q5#WXU_eZ^@vin8v z{u@q2C3o?+&d|JL;G{WSEMctm?N2em5wVUgJ!iEVx~^EX*x0gPl)R6Ui|(xG83HNA zHrBf3cl9MP{msh>E;l`LrFLaDwcR6>4`_=DPvcSz%ZK*96}a1|YH|{BFmoA^tkyW% zEQb0l$NU`I?zKDS-f=N+5UDgOy6P>gfq%_iQ6YOe>#=_`Oh$2mFhlZ|MB~ias#qv`lojUVV5mwL7Y9=x%(oA=%GY{0!f9 zV!}&m)zVAwn5l2EFUEgo^YJPH@*9D+s+>E7@>f^cdSzmK{BI7alA-V2#R1!Q1 zZR$C@TfIEq4MdTS*9L!H&1T6i{&nNo2_ zUEh90o%K+`-YAse&V=UG;8vKw9-EeE57;~U+S#=|L+g@$#$cS(Jlu5K&A`*X<55ZX~+h<(Pj)!Y5Lthzk^| zJc=lN78yhxzR#Xh*~V0!q`N3IHVN$8Et~OUuW3E~7_sn5{zzTp;&bRK9ekEivHwbL z`f^gi=ggndDQ{WEW!CO>dKiCn6~`vDS$sEuCb90d_Bkvdaj0$FfcIQi#sTY)LU-6y zrRV8LT%T_Zdd8k|i9+sh-4A~}nGvf*bQgtmw9jPf5efur1ws6U zQCB*Km|W@^O%nNk^|$23f7KD?f*MjYKe`6m$0<~(HGW*5%vawj()F^;>8##*I?ot% zthD%TjmP*#=8E$y?z`(O&GsL5`)q>_XrW$07ZE~{;6!16{)N8Rr21Dw*4$d$Jd4Un z7K;g`*jqvF#j@o4$Ztt#W+U+7j&yRGuw(^R0nfYCqVix^S6FRl%G=1a?D*d6K_mkmqLz<8LQqu5e@Kf8_d#PNQ zYXeg1Wzzj3Wc40tc<&k7|2yJ34+4Am5aV^6;#CyUm8*i$uFdRoQy}dkc7lZUH*;1a zHG9p>3i1Z5hyLVIMOAc#emKI!tbF^R!FOh6$utb zcb|K*{N83GcXT@n`!fB3mT`Zzspf~ONqjy&R;6tN|C=ZFImsSJ{qI2fW1du>DEUO^ zFZ%OL-{Al1?}?6_p^cv9peJ}7VQW~pVWA-Vh<-q6E*EDNE1u*;=zMRtbJAwJcUIh4 zDF*C2nZK3=buBFg@lH}C!{fn!2x zSof0Q$IdOfULb!B)f4PiMdOr2_*&jml>@~Sl{cS6)4pqgPNieaGSc&$l{`Np<2E{} zv38t`-eTu9y;XSB?J65epZkvPg)J}Ly0bmap_#wv2D7fxYG(7$AGNNp^_Zq*)}5=Q2yQ zH>`g|Jg0m;Tlpw#;>t;}9j{Laz3O%9F;+PE=ey}%#m7dmU0fS4zR#WoD2ey|AX#gu znfNAkF7e@DG-^ZuGo_CDS(QB`W*pa23+u4XpEG_2E-^>(u?zd6K)%SeEkNAdT zcBFPFqC>`fJAp|d^+ov0`uEmlUHgrGTD3zkqMxPXlM6*Tb32x-g9@Ft5PrBP!{b+E zQAFmTHrTA-f zPa*R8r$sN&1=($ORaG?(MrE(^uAGYB9{BOQQSmPDR_u1*daCA%f(R%>ZQm9~9f zbC&C(p^w(;l3h5vYP3*Sn#-S+Fz0)*d1h%jTIOtfcR^(-Cl1-Zvzk+44eU?psklg& z$}d+v&vI8J849GAKc+)+s+$&!b|lu?#<=zKf3(}MJD$-e#y~nHI3sMqJviP)k&6wslTGD+ z4Zf;Syqr}1V`O$#O>4Ie@Z^t7j*AnOSrJ5fvo&f^%pmH(i@Uc&j!`%G z)rn46^>LY(hQFVRT<(4hv%%l=T<_aTH4_mQGQ(q-`pGXnjY64Vb@9=}>851@%JB&~ zly}CU5=CQc8z;@hT#XAR+$H10eh=KCg0*|>X-Z0$qRbbt?-?a4ZX}o@OSmMGM;9?- z%N+4wjkNTB?=gL^KS6oHtCAvr^uE{b^6 z=WUFB1NKh|lMQwyY?4f=4lFF$Ms9Tp?UPn9qnm}ZU(%yTt^f103ihC4OQ{`g|K z3*TNMrC(KNm^)_;O9CK6b0hVKQY*;?+ACj~9}%IkF7* zuMOD-Mr#-X-i+gAdb!qIXT!DkXg0e4Qt49X=~Vjh!5?7)>}s7~Wj1^$0kCG5pEMe8 z)_f{`^KF~dSV}vIRlh#hL~6$NI`}9W=KHRTo}@2r6kL6Bg!{+&5C#uC)-4j+$)AgS zoacAUsORHK$(UrB>zd$Izxv~7OEb(+huy&@k{OoVGWwAY>M^6^aJZn=8>#X26YFTH zG0Zc{rej^M(jW0XIzChmdNE5pkd(zVXW`%@iT&t!OCZ5T`@Kt}5l?*xK5t*Xq^ez0 z0wU*2Rxmel-EDz(W^c}{T?G$xM1loM3GJ|Pm~ddZ$`Wlw3h#-msO>-(QGtx-VC%l8r5n zcVe-;PkZ_bjccB85QY1F0MYf%hDKC;ujb+h*7=zc3J>v39K;0CT!%8-LCBY^(vw@y8qmw-{ZrD^Lu{tvA{W@kPPg!ANp>Gq%LMYs;pPK0r|`>FPK z(gE?$L-=|tKcaow0>5K<_|LFu67_IEoFdr?iY(zEw%$ZHbUCK)1%ZSR&vCFH+aA7Y zhn>N9v8x~Jh)Es@@%=5m*bsZ8tp2;62H%NLSLNC?NAGc}424wx;-h+uRI&omQU*$D zd(q+~64+MzuaA`xZg)qe({pc_a37ZIIGjr$p}(kRO~4OS zr$13{du2H!?)4`gV@~GXrwz@0!X#o#M0Sl65o2Muqmj9^uB=ezy1U~>#2yo8 zkTY?b#gLXLCUnm^pWE5@CATnShTx*eifEFxIqtnrDzjoLQL-1HdGC0b zWb+<3PGpQXk$RUlJ_zgZMC*5FT)!5QQ2NpdN2iBjJ7k1HP0dOSw7- zA=_npmKtmigZi@;$HGgGZQ`!xc)Viu7S!39oeK2hwG;KeW1ocj1ME5Au}JCKrd@1`eZQs?Ujcnv zi!EIH?Ty~=VaG``r_r6zYvOPHUjqgBzV!eLj54_-}$BOOqo^le`vS-{!Yu) zRwGDLpw(q5af$Qf0=L~>?fRk$f7iZOMvjgs6Vz@y@L9sX3y%F7VgDlf-SmO%RCDN1^!_1r)Af+`##4`HmmGWjey2Tz*vu8xZD|t?nyYgW-^o zlGk?pVx>!injF)8_eO_#+hZ>byb$?Pef!S}8}&j9*JsZ(;e*oakI&}Dw?S=Qdru!1 z7_xJ8(i8qHcE$-Q#a33{Vm3U#j2j$&$2Q5xbuywsLnvOZy(J&%c2 zv}mYz>rgA8sfm>OC8lrpa#_eSjAP>~<5*Ho3Kg#U^o;k}?qW&36QfDG?EVIBJ6$%N zIYM?!&iYk))B*TfJhkpwGvnsOF8e%wPP%>WlJL{hq;x5X_t??gA|lwTVh*R89R*%7 zRC7+ImRmL7Z6sa>W^nWFn-)2Z*!<~G0+)T~tHG;`>^d;AkZ{a4We{TMhK_n@!~!nyz{>6>6qshLCM zJ*wah*S+Bi^nNFhVfZ?`?Q;KoxlAqKNzv`}?zQ6`V_r+!?Wxq=#JSGt_H1Ih7 z{sh@};~9902)LsOyl=lPT=qK$uDaWtmbxqwvYK`bl#L0zIBP~2j^26SlwsU#BJK{F z9jDHFR7yAkZ{dMQ9|G?T0|FH%3$yRCc)uiH(gfa-+^;U*T;J=S1G{497LqjSTsj8c zA=@sh@2Ttk6D~%0SOR_gZ&#OT?*6|&_b=|h&b1|>-~F9xJJukVginvx zOce&8#-iwioLqSZa?rMcIs9^S?gZ9o24)(Qj$0&M6J1|Dz44WV-%Tc(1aM>~%I&@V znC+XWK1=$%if?-&&yzjkn?#_&<2Akywuth4S=L!3T;ura#c8yJW-+{c1SG!g*zUtZn+pifBQk?${4~S4w$mUA235&%$=R=kEJA^~32o~bc&0@=w zr*5CFW#RDu^!rPH1= zNc#!*+C<68;@oMJioES1nd5pETK3*g7D>l-k6@LvzduhKpQd+YUR>JLdiZl@cLF8s zq1HCe?5>BBl8$0OvlVkds}JUCU+pg~(OSuRNyjpvYRBdZ`uL!01mKFE0$6JCUq`>G zPu(je@-(yhTk2hJ8YIt+>6;6&l?-=FUtSNFU;8m{vL5!e+GuAt&ut-qF>J}+HgQqh zKD0$uqQ9d4M`Ep7Fo-PMqS1r+%FV3HQY%>O2uYm9MRXc-r+nl0ap$1iV`d8h^!bZ% zcOo)+=iwmJX}QN$jkx;9;Zh|N9?}%|cg=QYp1CRnBhpZy|Hy2Qd1>)R4|SdS?6O#m zYku=n9Gb&1vdMy>cFq%46Gpx40IfgGtUs&#P6@Y1$^%-TT@YQE8P=AllnW|0w=;|B zd}wPL83(%hyEfsU^x&}P*$34%&!h}nLQikZEe&f|Cf2?EyAiK}(ZEkr1x+4C9jSwP zvMPJ`_BVO@DOt)-a(-osS^yXHV!cAQ+IRC4?IR6`J*Q;nVp}S~Djxi{0nrxp%{YEl z!uHTj47FWX%{m4D!2n=ctw1BgDr z@^zPwi{kHt<4(`Yy{`dSYF$c3^qBv+KOS-b+V_w2!dRo4mC#@*@f$LJbhI(2qj=fzHb>ZN_;ZLY|k9n=8K8Aolr+_ zA|V=IP|`V`LG*4I<|RWEH><`LQg3YCm_b#kN&{lrz2xt?^Ep6TwiI)P*j&;3hBYrW zC{`@zv;?~bs0A<}F@8)tr5yt%mLwdj69pSZKygV<;aGPfr|ReLW}cMI}2&L}-66!Lo;(xCbkKC)BAJ26ur!Y}z*@{QGxxN8hGl=C7`EEQZSmnKhG>$=bM@ zBrm&w!CX*XZ8K+U8K^w_-$VQ)1~Ij$>!2o^LCC1Pe}bvE1nwr5+kTU$e$w=W(Oby$ zW}-ywLvdKTL#&&ko3BG9=OO^3Ln8w|28`8oSX!tb>CK;2w3gTW)BlUZ4}qn_Cg@-> z0+H6UuzSoqq30zswW)MY?Ekb05$jSil)UATfj7G7IuGUKa*c&aNX}Wfd#5J1w!N!E zeuka$C$~Co<^69d`~Q|KsbxoYt?lfdagoLTxe3ncEImS_P14}(nG148uT6JFvDs%I z$pfw@RmVT6dp|uNo>F~ry>{P>PV3^4CI4%nKg#>DdjcG3CGPqq)@Ri(^y)Iv>o+h$ zo}QdL5d;7fmI3*OEs>DJ)hl}#mYHx2o@sU^9(A1U#VBI5Z+rP6K72L@j1 zLh@id=Kx0kuqRu8N}g$oO}?H-Y3uqys@u#T+`G6W2%OkO83a}-tkc#vVCqIg7ZEvn zK!+3u-wf8p3^*j{UOKMo**|Zda)hA8Z!RADkW&CZTj@6C=>h+2^~CLzsMG_TYl5b`(sUO9C{)ta9zsJ( zi5JvQ=;)it&x6r#1xW=^&Bn;)B?ta8m@Wm;VSa>q$Fy{iQrLV~}Uq3EAtc)}f z54y}c^miNX30uc7pV#vRx<9eZ52pUMIkxi;Zz;y#Z;npSFD4h<4CR*BPje}qq{|u(Fc-JBL4P}r`o?sVExjAO zI7=2~&e1x?)RRkei`11~{Y(Di@U1(+I5k5C z`z-;>&d=iNiWm5lHXo7C-7l&OkD8#kOPiC)3L&pzGM~RktaRs)_R#HA?u4?5h=Z1j z*Xi_W_Rj;Tn6V77CaDaX`3lcnIyrpC(Y~Ak@51T_z{;ot&&-Oq+%L`}ZjVpyJO?N0 zdX4-bqpB^qtmTD`3Zt4689vJr(Ia_MzG<&Qy1!lYk9$#YCxfksvV>|Uu0RIpkhK$N z5v7VLbmJy_=h#I&y0mTvrErt6oNmCh)eC}I9x z(_hXPkv^$eLW!5h_M#nnop1cE%W$M%-_SQ~JEqd`=>r6HtZxKMm2|7|20PBv*S~Ow zoGTh`T=60_5y~efcy`z{vD?@7bI_&Fem5!QQsVY`h&V>b7;%2h$CeR=QFl2pdsl?3 zTGxW=%AWa%!dFjOO_Stv5i+_3r=1t(BYmoy;cGR2+D7`gca=Z6J%Ns7b^9rKR6reg zq=-3!&8vx2>ZXZz3M(X}und^%O3$a~4{(+Vp^bhY6x?SIjGOzDa;fo?R)f~kD+qR1 zZ-;Y*w%)h;Yrog_}yvBp;jwIz77XXm#^ESsz%&JIimDrlCOw zRB$b_UtS2Dhx(^d8kc7uc^@OH*?&OGuzkD;|uWptT(G;+nS#NF)mf z*pVMyG32zZFn@}0a9G&Sf)~1EvT|zG^MP7DbY|iy68B=q=D?Xk>93aG;xKQ~Pm_>7 z@gQae>Z(xV+(<$K8V z;%@$j-2;4-d2;J_GIk#LbW(8dnx>M$oKwuPEVEJ8jFf8DK>Tx-r5*t**qn3vx)ta<9w)rgjf%;Pdh zPP`m{?XTb;C3+~XeNOk1djtv>x3*@&Ix6Bd)HYDzQvhY9T=oOBrO^3!dGI0S{ir;3 zSrW`y{Gr$udJyZf1kDO>3!RhKcQjD>y$rbx6*9=`fQC5PK6kWiSo02mdHVtBeHQbz zRL{f6pS7ugUd5T|;G5hsE>$!^-=29BOc}4?uBZ0mrR?P|e@LTi_4E4ey{Qxr2X^+Z z&DHzfEe^TOE33yP?Ey>g8f!jEOx@|Xui$@2lhc&`;S8!PCZWXo!xh<|A1N9FUB1Trj88nq>SpasCw_YUUai$( zt!Ec!a_w1#jRD@^%_l@D=Vb4>217@_06la=F;ZIn2Tuncbg})u& zgWFs&I*CJ&pyK^%N?X27tVzz{XP~QhHuLiq!#r6ec;$TPVfez{OQoFbe4C``i~aK! z_A!z{PJzay*7%)Gj0VR*?5<#`-mg4^^F)#S%qvsaVVUsm^Bx=zGK5gUgdeba6!7>| zEVdDT3Yc<(&}7;~nWTVYOkT$VM}Yk@Xbim#UI0Y45R_z>3BQ7&o_UX{Yhm}#>M{y^?gBp{c7Go7DZk0fycd!Zbb!hIX?io7%tf|8m&5-7B>c&q8DGJ3q*y zU*&j$z;24;9BTd-PR75aB^}diQ(Y^}CLSesm4TtAJb;EYH^<=W9Cr3Q{EXBZoc-nw z=IXxav=6-(;}VJ0CLg4+!;5?TPkw|p(p1Zy$^6=xbU;QSC{_Jtlnc zyl>Zc_ThOkP_Du=K!#{q9J24BX?^%C6lIBl<=8Obx(b8Zzq$`qo+A0-(E`{;xr^IG zb|Oa5c~bCCHeJ!6YfsE?+IN$Omf&5wd^=?OGpM*!E`+-&_A}tPFCCZ;cOh)v8?bnx z@4wCt~^)C*|Zcx$KUJL;;7>Ygy z7X{pm5g2rE!~^Iq^SFaco(IuSW{WZTv~{UIuz+&#s@#Ss5yEcVMbg<|iOdg+Z^gmu%(N{&#K#z{! zmMDyhwjJ^E;x83{1C)6cZ)h7NE}zhmbi_z01T1AN5elYYTyn~)~Vp64Xn$eF)QSQz`ybv4*~A6%xFJV zbpTS&`C_w*RPE4Czi{w$VfLJby>ct?h9OG>AEd?d?QZgj-|?o>zax^~NQ<`n%Ub^{ z@1iU`Q&Z!M$p5Wn!25{>@a860kS97X#?=CNr>_EN*N>8%k>wEtALMD(<|{h?rC z>jI=w)0x`#g=u+Lqdvc7C#Wl6Zc8i{d78C~8QnqwSG%-oJIApUkttr;>k$o$#)Z4R zafBI_KUwJCfGW>vsRJPHp#5EG^vbOry{Bxhl`oEu*-(0w?56R$eJ_8-(E1BNleUXYv!B}?7S;tk(;Pa>4jVb*&zg3AV~y}R{~5MldFg)d`)2(*eW>=j z`PmZvQB-tkY~#Rq>jyxpJL`{}%Ldl*_i4sm9QvQ}JM6yp10*Q5NnJyi?^(v4yT!E< z)PAH-(ys2n&0xm0Dk9zS&j|RuBcE?_!plH0{>Jm5?W}q}E52>~2(Rt~gI)xJBC8u& z;NQ1%dfRixn7sRq@|%-?cyl$N0iB)0b^rZW4Og#v_{_z{`6pXKsXO(A1k(i2jqCVV zU!GL}HMhgJr#SP~g?#}_cUQ)Xv3Ax~G!<B^7;vJ9<^x=0Jpu+9i4wkPQAjebr#*nG?RJkMVK#j*g{Aw8>pxTW)=xwKnex}T z_4uJRsZFojs0S&$Yy%Bod=Sto{DbEYT6m9y)Udo94zxV$Aa2lH2DEHfNM&*T&}ZF1 z^t8i+xSqG~w*BxRuJ;*!+i@WPkj}w&L!C6$R*8N?Ij2I06r4kTtNwC1$A9NRO;aNZ zI@ZHE0>#pjA+3j^f}{PIPYw86HtGdSQ1wh@kJ&-Z9da_Js0n&u5?ge}pO+Xx?DPGB zJ6*$3zodUDq_j84r?6GnRMI(D;$_-Hv>cO$uzyK_aGnH7EJ(AlrW2ndjFu{&Q^$OUx3aeEVC4iGU7h-Oz2HxIRA=q`Uyv2l-TYHbd2eY37viab zdf6G7ihRlkVqH=-YGFbk{9020_)NAsk=D zpg#J+QA4i}j3C#|7B9sht5EtM`F2ZR>P_r2ja}Z%E_Fs>U5U~`jDR;NdcAB)7O)F9 zs>8O@EAI5Y8ODD}gWq4m;I?IsYl>!fU3m&;ixOCx4ckU>%V*2DURte&)Y@sLXgnFi zsi|6dkGa?=;)un)j$S$uL11DUOB;WrAUK}W{vF3w`uoyCkU>LNqKMPop8gbF~vp!*{5s~Rl@qXY;498X;PbX?T z<~cewGD+g&YH41Mq=c@`EZ@$43S|8duvY1X0XbG@7&XI-InO5hNnKjwZ%b^1@)CJV zN#wZXM$zP9B3^~_%ykJlV=lykiz82J*Pljq_!YmVJbjXlTWpT(ELp2Ye_Txs;mMuK zs9$rzRN80_+5GBzqp7EZk1xPrDam`wl#y{HOLb{Yo1Ot(@jLmQejw`DX)#*(Q(ftpf= zwWBQg*jh@2;1h^ytjnI%oj^nImm4z$K&aTj=D$LUCTO%xu4O&p+vdT@r!pm*?_=>M z2hc5XWD?*B6WT}Sq1BkQ-5&khgBQFZxRsVSz<0QQs50D-p$DX4+;oi}9Y4FH(}ub9 zr=?jvduNF+!%_%!{3^NL5(^FsusVGr6ZEYMOOiy97nL^6rI@WUW2l>OUHTL0q7AN0 z@8>RDNyG;%nVN0AWMt?Na6=rwf$?vm@W`tv`2|1=EQJHN{Lq|rO&)`cIb7yTG8h+w z@KKxe8D8=ZuN&Dn=-|^y7eb2+1@}MT$)Il~2%QcT_$$bPU%DxKKO>nFH{b&iq^TRQ zPk0yf90<`9y$(HY1fe<;a$RcW^VBpE&d+IGk{bLVyfJ}pz!rd$6XpS+{AaJ%?7Mw? zARwjj13nOH5I}T8ifE{`bS}m0(do6`3S;1xQ4XHCGOrURN#K>5A^7&9)39|5KeRDt z0yO&krtt={Y-!-JCrL(_lAo<#)elL0I{ZTDXUAw2zd;E{wu$~=t0|qAUtxW#3zwqko#l~(+MBicg7{83z$Ee zfZz+o4_NS--`T(%iijP-cdA0*-1=NDp66kzZ*WCbZ~QmvF2!YD=!Uhs6qWHm6^xCu zBcWY$4DF3-3NegI&aedc4iORry6O)r3L@}Jd#DHk)%8C++ArnqypX9`Nf0{9eXtOI zTggl7pdw!GhhM!<%dQHtajOh@(y65dF&oxlgSCvL16#X>|QC($B2riQD2q zAO42ov;9(1`+Ui>RN=jii8ba2G>&I)0CrSKh;5zDAzY?#B*k;H>cy0?uZ?ZW5VEuL zA8g^Y4Naa%hCY_*T5EmjjQ^b;?(93Tt+@1<_M4fM4Zcjcx1n0PB9_cnxnA-)%-&V% z031en)^yy^;y0d4O5>z6nIIJVdw`j$|A;P(_6(ZpXeha@8b?A4u7nTyUB@KG<^p1o zJk~vmb@OT3i<8v#sG!7Q-y&Sa0(9DNSHmiL5Ko3{TM4c?2JI(PKt=gba9c{Z=Wu*< z>-KB4S41{!_~M6{6f}(sla3^?gCIO&Q0Bv>o^viP#w-zqPvF#D7X#j`;J0|O65YRd zf$$Mm*TD}7Du4pE0|rVUViUdVz%LW`6sOQ>Q7jz_6R|Gu!YoTSphcO2u})$fG7HlQ zz>UgHSO$cEZyuPX!;ey=a#BZ^kWWNS^`eFR407UM$ly=C|F9;To{YqnK_3QQ6v{W} z09_SVRm7(o*!*8C9YYh>vsPGDT&b(;qc*Su{Dz81rgY9BQi&U!P$i|T^}-}^EiPAv z<)e5W&s%fcbDB0XzP#2b~+8E z>p_s1>u*<(PxcGRVNYfF_gjDP;nOJ<@*5;4Z+o}cA6=6fdtR@R;me#ANKR3Qt)x+AG# z$cU}=6Jqm=pX%JHH7+eRHAtv6rID*-9FcsXczYPsSld~$D2d6KoZ8bP&0`={d?}Vs z#E~4q6enj);kV3U_h_&<4R)$DPRjc0%5ka7uA6lB{n6&oAd*)@Fu%o{Z@uMhx>7w* zGvgs`rhb|(L`CP(U^e{;<{mMd&XAqzk#tp^+1ICWk&vRY?}_<3W|P@8gj5`Be0a4a z(E~jHZd@5Tl^qm}CqB4hU2e9vRf%a2`&3TSzhzZ|`6<3#TBR&((p#Dwi{;o%ktGHb z{8gG!K;MqL1LlOwnMjNg}%s^V&?YZ0zcyw`$~|FJ;l{)cLa+ z$xymLv(nCb$ZJjlPb?-OIsdoAedq_AIc7;^zzv&pZeR)chsZ1`cOc5A9H%n z5_I;38B>y(j2F^^@2$Va&W|(yrd2W8*)2f{{51xdKD@wr9RDokA;56!2?! z%SYxbNs()K85z5)n|OQJR873?T*CXNah5r+#4xbiGjL$%;2iT7Nk{pJ%tuy*?krJx z=%`DgG4!C6Nl68i3Xd5Err3SwxQbIR1FmY*RMW;~(jId3(E|7>uYZ*Po`fUk#GL9d z7)1gzPGH6iaW}?gD_aCXaVh}lb}C#3jgB9tbj!QM>WSZ84kXl65ladFJCyGpl=Ps^ zXyp5~%<+(l{KBges#Mo0*>yY=cKIN43l!$eLP7));b?MEYu9hVZpkz0-S)Sms!Idv zjfK0pNX(#;yf_reQ=ZrE;7ii!mm zy5>3}E6#Q$k3N;~fqu>}aIs_Bf=vDhyDm*~**-c$nMru{F#iTcb|F$IO_XpD+E3o2 zgb~e6?ygd5^}XJ;aew;7*rvHh}B6d1Pl&QkSL)3Cb1^Wo$mu}6PjV}lD%S?i&c8d4~0l)~$|!X}RAlHT)`5$Gr0 z8-RZrVX#m8lrw01c_g1%ly>8hlxG4L;guua{?<>T9S;c6-5pANr16o@xI~ww1%r=u zB#)US1zS5r@Kz~&0j~11^IJDXcn6le&3J}CwNkMJ@7ub}hYYrw_{wDx>YnUQi&7 zZ$0u85^Y%iqNMWN$Nw|4y(BF?DszxO5uD@@WyUj8-tS+WvQ5kdwGc~cmw4|r9>PuR z3o0vIoGc*Povkwp?G^=f-W1>nH*yGY8-pu7FoQkwja7g8l{qrD8dHb=Ol0H%TtYZH zH{MLO_hj zdj!=w`uB4f;w4HMwUfumuITpooIL;xf5aT(-?3Zmv5{0B(NEKk{j^uMJ;Z1PDJe1n zX+@z%^DUfC?jF6sfHS-`)cP&_C^)-a$JM~Cm5jlNUVW*nr&}l#9cm7^f99gQX9;ak?qQmNW>_kpW)#{%{U{$2)Mm!1 z6B4t+^U<-1UR2XFmHfJ%#b~h#w`cW|MxPK8K=ArKq?RmaY6c^`7L}s?h9lIytF`Kf zfcdYA)0vtN6hnYeUlY+I3V!ENdimavTz0)tEhI=G9R@-RAM)loqFewuF-OHhKgUMH zjY7g%eNHljjw+8bVq5+tgk8$VsItP-m)K}aT;~J*+J!8jl$M7D#azX(I=)lTzsU*v zmMHDykq_0h00upJ`3PhE^DpU!a_;gjb!*O%=jeL+#s7th+Z{@s!&(6G!H>Z#FSeuM z+0dXw#ZyM=I-avisaUYl5uu8=$ggv-5uRmBXpqLq-G*j)LNRhgH_q`hJ-N z$g3JKL(2C28-6imT=2t1kr_Y(G@e+YRC4*!+7jI-mn*1!bKy4qtnZ5q!P2*w-o)1(B8~O!p6# zYT?C81yQBDqocYe&%u$6W@z|M9z~M2@|#(w+?S8Enzb$Qot!_HX9Si}quc2ga{b*%@3@74ULVg|8bAaxOZHqstj zj8jt2bs%h;F;S6UcNG717Dxj@k!7Gha&gKM{I13{>~O)k zsTK%b6kZSJmZ5;Q@&`;g^fpF=KVgYr3xsk(rH1+9TvzK+xxAM6pLg?>VxTt0A2U%4 zW(6-i!sG(p!plTmcX{p%EOab3>mJ&C{RJzPNKD}{_$W8I_JckGl^VmcGs`&%jLDz(pHo`2T=~IQ8ZQ^e&*_){J=Qq{bz#TsT?| z0~!#CrHvJHuL@^ylMo&% z-}V+}qGWW@597qe2lKR3`aUFFAivVuqU{kZQ^abC%*NiYVL+uC|1#2IQwqghneKat z2Tv#WAnZNQZrd7yCu!DG_pSqxo0VSP-TwE$ zry=>81wqsn=p7N*KT62#RsnzX$`l?ObWYh&7D|J1W*3Gya5WB6<^;ewg>+;!-@f4q zfgoA@6H-6e=mgT_Z%F^R~0VvO;h_c;gPuGmIQd6-#ybzLZ)txD>QL!|IHlYt)fg{Mpm)LOjUbrH{l ze?I$o7{<|;GId$&{2BI)YrE=0C)aR!^fr68mMlB1>HTKtGFx^7Z;v@s#v9Fb4YTvK*u^!>Ga`1{c_D+$Gt8#C{{wnJ zg}>W{lI}Yn9r9C0N_HDd=j*(DVCuaD*CjKK_ygN)`mLvP3~u?v1zQOTt7~}6@`(Yp z5)v-cFp=dG?`I_>?4;q?$_KVCVND(HW*DdPfxAk$LdQZG4yk-%Y^;QYS2FBK`NXqW z2?=9jxPtPDb+8f={=j_y<qkQ^dtc2tnF?)FVu+b+!V6vMZkNx@6CZ7B~Zl^Xo;}2|~Rzk8znhoj)b}=g< z+p1oh&!-(K*;p-|uk-S8|HVuAqp#<`pZ)Y7|MZXSm*4!uzy8Cw-+jOT_OqY<>YMNW zp1=LHR2=9k}n^Yg#?-@f_V?^DM3`%nJVJNK(!u6F&)*LTSK{cnHy>+k;VH^2Q=O7itp z{?`NleI391|0(VM?nm#zRiH>>79`{wU{`A`4w{a^m?-~Re1U*G@oxBu;%?^kpDuRknq zx9Fe0`R(`L{L3%C{q;Zo)AwKBeY1M-{kMPnCqMbmfBjeA{L4?i;?KW$|NK{f{$Kv> z&wu_GfA)T}YC3-M)8EnDpZ=h|Kj`#Ne}Auk_R}97Gb`O!$^H@?{Chb1@4x+Te~+d2 z&A)#0-S6UT|Najg{sRv8;raiV)BpTmzxl=A{Ps8hr}O#yxBn@*oX@_~=kwwR=fcmP zL_XB%sNih#WcVqIQ>LH&^xyvc-~ao6^Jo9&Pyguu0#Hi>1QY-O00;nzbL~{yIbS#AviP^ z+}+)S1VV7=MuG<|al$GRl003mb|9j9;|MLo^;SB%J3)MzdQ6BIO&q&le0&C1Y;@ z0E_7VJwQNCF8O~xqPC*-@;+Xa=>OwHCJ@fz4-An zsW-oeh-1194^#3@X`1u*A2T?I9yL6R*(xz5Y4HGj*z@)CV<;QV|BqF2CKL#s{9u<2 z_j99d@Rn%sqNF+jCd#0+8*FP}aGNCyFktrvuI4SFh|gkyvYzX!F*%5ejHlK|Iy` zy+?~(R>V6#@Z1N(iRs9m)6#2nXx++>rbJh@d>SgaoR*}a%B!H|@PqFYupnV655mr3 z3~0iC(MA05d|kc8?s*D-3A@1^%=bs2v+6P#8)B3sE!bxkTKI3B4-O)R`7LQ&SCSYk z(b0R?@@Voe)k*d1BivJHAtC(46HV+a`{s%#M z2Ac=AtZ<~}0urPg>8@YmdU?5~@32>3&YgS)p0=c`aoT#GBz3*!AR1p8V*#olf#1aR z89_1dCard}^)41rHRE~Q-2T*$G>q7Zb7YhV(XC>2K=TN7^o~i#F+2IQ%VdbJ1&uo3 zp9sEfM~C=<-a-koI-d7%7fx5 z6Y&!%fYocR`={CP1QvkUfANHbcm@!)8B(+$A-H4$aDq7d#wLJIu|ihBU8xb*cA#vY z^0cJ1rbsg6<`f5VLp0X~R`FBR<0Axm$F@8{)`!_zU2AsAIaWjU>rZ`!`t;=Cl+l)8 z3Zxf!MF^~$Se}vN{|U5k_}x9uO)+STd9tQk#KW?egYF2r+VHwb6$#HO%8$_REzmVh z1I<7(r*EUs=gas-aee4qUkulvAgjy@O7JlCeyA)F4(DN23}y-7p<%MEz3&t)CtecE z5;orn8ywlZP6D2$Rx@PI2wFcR+^l=wR_sh42nNrsO#VcMDE=P?yFeHBtH4`_2mkG7 z^Rn~~>2SG&*H^mt4WqccAQu*^3t+a@&qJ`hEKE4=+)v7^SqrmYE9u9&RVERX;C^igC7d9`FZexuz8I z@w~r78^+88p3vs<(u%<-Z%pKViW89cv?q-?lQ5i0!SS%+^z^XYM0&}UUt zcb8PAjAJTf?S;FiGxN5?&($p-O&p40D*V0(cdHfy67Z$T4FLAn);fE6v7H&QJ%Cj*R#rq|8QbU~V} z&vz8%7QM@LiL(*Mq=&9D@PD_YSO$1Arpywh?F6I=`##}6Rru#8zduZArAV4b6Pl>W zzk&V7?A9efW*+@)OPkNTw!O261o45{X1tZQAgZ8MBd_yT zG$}L_eG>X)e&2cGKdlpIrZJ-c3c5Z&6sIN^`=7-)f;{?|jRGXB)jRVSdr9DVBubrQ zMN-3uT_b=3$ZUJyj4asFF|pz6fUVXexhkt)ie}0{WuhW)j-U7y+jdKywo;LBw2O0| zKAG}Pkch%I#e)})110L$%NW*!{h2^sdQcCw6w$F6>3lnI*U~>R^j-WlC0W!eYv|4X z?YkGi$)Ep+Q^T-l$NK{@Rmqownn2-vXqA#k$z;s%{ppEFP1&L2$q~g9MgeCP>n*i#m$pq; z$e{SVy#jKYRQOlTt;LZ8PG#t8!pA^Z|Ij1wBgCBhryb;f_6YOM&PR_Ap&SuAMo2xs zza*B@-V?b(wDUGcUccyaXFJ6mu<=i}6zjiE9h^DSdPZQ;4Wgh|qeUS$E=lJA`5zej z8-z)P4F};=^6=$WuqisPngH1ylF{f<+)ewh4G3^W8fiSg>v{roA}s@X`#)jWuf8PU zDNo{}YyTXQ9)4=~NV&gS0PwAZ;e3LcY4b?Qq>Q*Dp`7SbxQx!BLL1+H>K%b3m#>FP zux!Gp%5vbr3S zXBd7>g8CLPnGefo<%L&B(@70wc=JmI`KRSQzpSEk1d1?@+Mrr=S(!`TN%|y)uu#G6 zaCN^%wH@~GNG-NOG5StDdsJ31;()xVcX279HI*VtQ5B=e3bM~1zJc0VQEzcEJa-*T z1WGxoER)ft*^yRf0RO7UgT%>%$Bzfqgb9WmE3Q@pK5pl`PGCm*ug)G#vL!~wY)=pY zDK|#;R~nUI;No}OaCbrk4gPk;_JcPAba3Q!9q2AabbZX8x`>)pgM4j=FTA(AyN^qX zJa|?ciwQ*eix!HVkgl8hz48K7kyz81;+ zM;_&m{MbTBH-|S=9pFV=rfdWV`DW}U>#*Lf>eYr8HyMgJ2TSU`I)e+LuKW2;Az03700k*_tbH(l)2&i zms?J+dxIQ5DxA?9)d(N!<4KRuhTvspBL%exyf$!(68bH$gsfT$rGAs|^9LzryP8dOOL@Jj}m zC7>a-ShFv01J~Ul=#h~hOt*|{65-jcTa-)EDXw<>4(~imUZs0_9|ZhDGj8Ytc&&@x z*fm)e6Ah6?sVa~)F&B6sDZPHEZq?c1rdDda0aKVIJsvjNqx{g&=`*1UKtoqoYQeJC z&hCji0Q8fyRdD5SdIG$HdFmw5Zy$bQLN~oa0*>4xf8uh|JS||a9O=<95m%xZ z4blnT^)wI}4=H*)mmYeS@!K9Bdx1#*Q}Oy&o`y+q z=F*4eE4Mma2Fk$z5VbKJ9qvp88%x=-|{i_o`Qs-dM}JPdRaI}=_grC zZoA)|oy>Y7#5~*ci6rx0)?ux@LPCQ9SiAY21z3F$L*ctBP*8m@T(Z-!6VS~3ue?E@ z@$%JhPK>O@FHLwVpZwyID)PKY;gMTUc?0`ANb!_X5_0B1T+zTDN*hrxFeBB#_jmpb zahafPzKolkrlfsbDbof&=U`5QQk88U^r(wi13PSsQp*gmf^K{t##iF8 zJi1@LPq+tc8g}*eatV^v=&B=EYi*YIzv5@FP)F=aw@61XwCS1X2$9YZq`O2i zoO_IV3kpfGmHF4ldU)&`fKQgJkX8XtWE{`5AdwRnxRMu+8Su=0b_L_2&RsAoeOF-- z$b6B8CiJdv!se=V|Hf>bQD^iB_R?ps`3G(Bi!|Sj=pc8OWYLvkIW6~wrXV+CWx#t; z8Nrk%Kc6EV4iE9Eo~}0PGYUuO$A-eeE^Lo}5ILH5$R(D+%dnV7v4^<(-K)R>&d9;OiXB zf0M!oKlroBdvAA*F7|WF*y|nW9Gk0P`gv+#BfB!ovrJcL*|j=AD!D*v1!_Ie=%(O| z%-yT9q-9AyKz`VCeBLPeaju^(+nHzeU7((y?NGGAF@9#p z^!CnPj3|>TlJqRDSH}AouEaY!S1tH(;*L71jQ=Dv2l!?y^LJ4~F(=W6uXrV%=4fkP zJvQ^~daV*Y50qytAdx|~t7JP+9`puBtINTFU$!y&qn5xi8|q!3yQaxpBdy3DEj$dP zQGlR9DYc&pQArqzx~qFLH_nRcyM=EIr{bj&=zX<{pek zoLcqkyGj)sbn9Q=o?Yp?NvuzQjb)nZ>CErjU#neRjLhNDl=g46_Ktn;KSM`kvV2VR zwCvH>gNRD1Ndebd<5b)xi6U_ZKK>(bH_fmIR>U?H^sd=#l5*_qgY7Ar*3R~3Ou0{W@^Mw&8%%VQB{REJ|l%Z-m2Xl zfQi2qORpG4_o1hjiVSp`5n}W=A?6Cq@lmsFI02&)L_%~$0{2n)Oz~4lgfhU2%iGOX ziD-bd$*K~z{A1YvWc+{NYS}SeNRymZO-`RO9Pac#lf_w_77%y_e&DouuOWx%HsWW* zX!%371Nvg$i|j$8eD=fC(*-x{Xa}|!fh;{${Y941Hz{$~O&EU3;;^*8LOy@9 z|Ioe>f_-}NZT-%3ev7GnPK8Mzt@WOY;C#j86UUNC#XVmvW`gUO3iFTFkocuX{*6_H zI|oRG=%VqSlf$Bt6fOgH#sLuTh#aCkHXu;w2abcQg^?!z$=e6X29P{YGA(dj381w| zn%~P|D1ngT+=)rY<0XKZrWB4=aksuv68;OY6Np)NOmp_*j*ieD<&W}x(+If-K`BO4 z53c!0kF_`TMgH^!pA8+$5scdBz>K+l>_-4@^v zw)~&R7dsKz5XP(q?7So`!=IDidg+UBKZqy^J6qxwmn_D6<+aP zQHD&uh9mXkp3t72@6`MP=%4w!`&N{p^#gA;@W6<$HUYTlBY&L}ry?tnTya=}ri+g% z_}MeS19=?#hsJQ?U}~35`hSAj(hi_3TF0mi65}BVs=ohej2#(FQwyf#;CWAiu|`FF z_MN3mFDsD0W2(gj$$i~OP+DG+!S1aPB7EmZfMi_XJ5u40ji(}#Y2=jI$M`WX-zT4A z8wH2ke%+ycagNGS~S;PewW$($t<;-}L>?XY37r5-4rP@BzDq z`nY}|9uSf@xGt7jY45`3G@$Cpmg;>vh$|0e+IrlXe8 zPOWD)zk=qlI*{KCoMG^RP5&YF_7&*e*r4?r<4RbUbg?Sun*6}#{*e1v*2uT{{R{Fo zlXc}cat1{yt>$oo7cTAfFTvG(S4D0E@i>naG7{9gd1yH$kQR?_BQkzuJOx8EBL)c5 z&hg4{XP|j@4eDXGyLu|JY5mb?!S7#a?;@$Q{+hU6Tnnd+aS>#5N?pJbg$lJw{$YZES zL}{7QdnJz;is2Xxoo755b*(e#*okrf_NB)sMwC(%L<7{;!A?lg`CyS|*rwPDPk(4v z2O@nZH1*#M(a-FZ8U9InGvH?T!d-mvk>yFF|Fsb6+rgjN+Tm5VyDcwzykhJ>MsuT$ zC#2iUVyQh_0hiN86#*z!&ZH)|XO%B2Qn6$`6SdunDl7?zc%eDXtFWNaoAG3Km!Cv+ zKbG#Qa}0~M$aH^a_vd$sO=?gihac6cHJYzG=1X6JGi1H(v;uhF)b-gkOuiYJ!mXJb zOHwhK#Go!ZV^Nx|e5VPYKa|7148?;Ne=eG-Xn8V%xmei!K$=v)LmR(#L3u&&FA-R3 zT1aTQbv?Qr0m;4c@LpeVFckJ(G;%??e_9+|gl7)`kFo=KyDP;I1c`Mm<8N(0Tp)~O zYhx0iXy{L4J0^*s%=c1LCyDdpx^ zM^T`F?6;43c4bzAaRShFAz(z8dw&)Vcj^@t_#oGlNhug2gxz{5)pO%O2|&|+rP%kI zVyXmX$s1cv#Jf>HO91c{xh$WKcDhF3V0z-SV!_Ae!H50hIF`TZ(+yeJhLF?E;!DT_ z2o@H2V)?z^F~e$4vl0klLdN_KzYx+^g=Y6tV|u~9NdgIAWReKw*{QB^C}qOFHIKsX zXQ2nL0jGjh^IuJQM-6C?$*xd}#_ub45AF1t3ob2S*%K!k>DD;KGm#|XNGBLCrp!y+ zq2t0HUEJ&=>lQB~FD!-w5~k*NX;zo z@JX*=0Yvnf;!qx4-5#;_?$Y)=s9WW3Ar$qXof>QxA(Q^|OBIJFBoCCltanpQ9=EP| zjA{i#!rY7}35CFK|4a@B9piI5Z2yspw z7HQQF$Vhm>D2f>_OhAHgGiO_?V-G0duG|w2e-YpzOWy!1p3!zT5p+NSad9GwpsiPgu1Y^ z@kClNc%0*am!-|Vt-nnnQ(F7|JtfAdYdE}#`W_gy1-M_7azUN zqid432dCjtq(~?Yc0`k2~=%&|1SU0FCNk zf)?5@bWu6cq&|mICQ2Y^+uswaQm_e&4ANp?2!v248C9i-@Q4tuTZAI~kzmfYTJ?|Q zTf4rxFZLI(CgX(O$SRmv)w}SSkssaJy>aYyc+UUfH(8?^%tJ@DdM7nuC@DLvxFROni0r%iiu8PE=qSbFaM>U*%~M;82E6w9Wgb z^qe8`GgFyL4ZlN(kG+9Ml@l)nwe8@ItcGY78T@v}`;s;@=Dl&9?@|QGAfp3O1Zggo z8*y9arz?rfSFvxU#Z|^qO!=D8f@hbi-fCTM1!`%W&c8DytU~Sm`-41rh&oXm9=p>$^tlGw7BXhogdXHW+Jyo8$MuCUd0u@aMDA|r zqPytAd!PWygt;bK9%4XdZ^C0=c!mY6NFRU*+E?Tjck;p}y#+hPtxAOVHr&25;-*#2 z;S>FSTwH3gidojqW5mL5-=CBM_MA|v;-7N+Xr@6 z6`;HIb~&^CHHEcm{O)$nbo22;|K>&tDl;p1H-FC(*!Wo2sVi6z^S9{m#0I_kn38V* zJZU`(30c2XQ>|(J;TgwCqoJjFgz7|)Fx;p53vHZ}O3{$?NOjrS?e`0-sr*3WcLY}p zx<1b$JX+bmD%?IQWV|yooMq)U!2XGk)_<|^Ev!ui96gz?M3Qf0+bjju`EpIBxDm)n zh5Ha&8Mn8`Y0BY%ChCWkect$WAdXq!MFw&OCt^A`?B}~o)nqj;M@JVBpNewAkB6wM zLgwRQOSQXncCp2Y6IVmGq9O6m0kNa3c6uyY`0m)#>5xVeJBSXFHv#^a?Sb5_bock$ z@_tWbiCuyrMNq3aw3q+Q<4+l(7y3`p$-CWTfD>tyhn>%9niiSuyo=<4(QAUiW&$W} z#Is#d3K=@~v^1owSEt{cMh22%>}s!z{pspmqV$?p9jL6^2P9EIi$|VVahTRUhrwk^ zcxAur<&UH{;pd$W;GbK*U)nkR6TRcWSMPia&VHxqJWC!3;iVC}|3=YlG@c^v zx7_%1jDb?kFH%XOE^O%?UW(7&1`aX)j~A*4{2hz-nX3oGfM^ct$0K(CVG ze_ABr6Oio{M7E8hyuo-;JpTN3{QOP8FGf9f`hE{5p5c(mXMJdkI)!A_pj?1_xJb2}53$QpyPS#wWTgb8;Yo{2SRWDOE zHsq8ZD^i}9B)G-pXkNWlZ2ThLVZ4tr+H#kIN(8BB6G&_K&TKB zR_*XGb|Q#4F499U2d7nvr>PK{L?7M<5=Zd$p-oHZ)p_Z0?nDxBm7em(68x_KCx$)y zj=zZ&>S)_*hIk*9X`rFq{8}5CqY(1Oc@p#_ooYvY_@f6EpBU{WLSoMHjgSWmpEzX| z>JOT~qrHTd$g8i!YGU2_Xx1SyHMM-IneReV^Sk+e4c+a?3Av$oqZ~O`yxV&1T@Sf# zQAZNT-fLnTiVj5UaYUXpp?3F?yWPJk;VdB)uEzSt+V+A(-n!Y z-CyK=kECf`^Sv70SRzG`a&1%4;#&7-7f&k658^e$P^jyT7|76R#SCITd2-o-K4boM zw}YYB59g7U%Ym6ab~PUf0Qzs<=q)EeOdJavxZW*IJc`wOgHhid7<*PkP9Zrna0xSZLFQB43eLr#nI7Yz38b162|5%fWQcPZN>`N z%i)-P4Y%Wzzr1eg*Myoeu#UotC zlN{&Uv2PT;cvLId?BQIcA;FI_q4vcUJ_a2hgASj!ul{9bqLMz!rxNjFP3SYpaFVK5 z-BQWN%5vcGip|Jvy0fPq` zHh87e5>|)CUD97+N65=d^3#nh#=0Q=94caMtsWmT2W4B{n_Kax4>cic#s9?mw1a1W zrE69Lv#Ta4Y@~=EVHv5wt_~T=P3dM9F%pI7*?gEcW7Z_B%yaw^OE|JKg>gtfRlac* zR9PS8ZzhV}Y0>1)g-wJs=%-cwpc%%u5A5rQ{6~A*lQ)O{tVSRs{H3(qWSc)(dN^E~ zE_eNtHRJkQM$|_9Nw@gOnQTcr;@FWCv6kr^&Z;h19)J&To8kyk4C1$MQ3An zQ_d3zy!SJg!QmkOnNf<1t%w_P*836%FR!Q3-uT8oMidM39tFL*5?LLH!qg0lVDw<( zR)gj!sK9$f(VBWP%9bVK%V-EBU$Rsy_Bpyspxr??g~SiwMJPzFiXACT29#lV8FkJ{Ew;gy&_A zVS&xYjtORE03fyV`Uh0b*CLUl9vnz)b-6}A`h1fgC-7IzDZ(B_p&XvTX*!v94DKcw zpn(;ZCa!50n&{*C&9snHC3?n3a=URA*WOfo!{?6r@3~_*2Ay>&{?Ko88x7~wgH-3m zZ)Kqo-<8lQNsA+9T)a7+mD~+^kTun@WSA~xT|2d8snlo1_=ghX(Z$Z6cWhX!DyRX0 ztJ+PH;I~kDh@{}*Mx09i0d+~ zUGIFon=2{EauMOubv+)c@LJ0@w7U`y z_*(-a{6S=-vVJ3dUb{*O+1BkK>Kev!Jd~6bSGy=UR&Cof-E|xsO1*;d$2x|TtV9__ zDMczYa|Xvs+mzxALw$XL8awGb(q3FIBk*ZnD-~-6P(3!##j+28$N6#qJF;Ol>QtE} zq_J=PoEcq`UfZmZVelj?>LeApiDy@rqFBR%VoNF|+Dh?Rl9fET3kO3?1r@4QAl_(w zT;)UXH>h{om|mTd|aL)nw_zN--duBuw(u zi>{Az^icyWh2-Gp!>uo9#%xe52B~4af#Z}pr8?~83Et9eL-9MuLHbqrk>(QF?B*Ev z-1%q|@0$!OTn8D9!AlDRdh~wCtQz3jo0Drh`2unI| zY%R2jqu?Jh3*|rQ8rwbZekZB>&2ib+<6CDtkZK3UW#sUy>%i@vI=MT(Y;xUE-+-Nv z^aaj@g;Ek$P|@bZkg>``0rMx}8=kw;xuWgc@!KEZ3J$=LTmls`>dbMzeKzm_qJf5E zT#H(6ZY?CzuF{&=(JFfegJ>=YFsdww6t)3oQ?%1m8>qtpO12K@90P&F5LZ*+o zm-@vu(=GB2Ybg)5+)F_F8nu9zR)f|zvB5lx3l6(iZ&4W%8kSb$939T>-UHm#7lepC zCNF!tcPSmNLG9_#3Jv}(&Y1qv0K_lYLH38Jscx=O5;_Cib`YGi5b}7Ze-9A{{oU#L z$j`!xo;Ue>+mlaYU1A4cu6gYxNDrm`%@cWzUw#bq&8;MubIi#$Jy1_6WG{6L5EUZa&PP8ZW!tW$by1lxz9JAvA9pv|GetkXoetnyw>K>`~Zj1V^ zIAU!yxUS#%h!UgEnEG`%nj^+00CP{SnYg=>x^C-YhFw67ZRD$UyQ2Cc;WR$0ZDEyW zx;u;+36U``B=&u%_#Mr)cHD8XuQGJzpuz=ua5?b_y_4q&CqnkG+hz`fs?pbwHH{>@ z9L{{+i_3;!v~^mr1KWT1HE&&K)yj zROwz~L5jYBp*RJg33h_7?Z}xR?l_d6>bT@VnAsW5dW4tPNWL>_RpmG_X8)&9 zvoXi?{UCDBadxXi#=Go7oPJByQu~y~o15>GEs=hwz4Eowd~c`MNg9!h{FmEhD27@# z-65kzhVFoQ-|-Fy$m1t{eWaU-TAMJRMfjX4sFPLp_g~kB7!_s<)Q`|~z<$Wm+17-u(A}Wj-_TE|3`H8p& zV;=em;hEy|)=pE#c=8i(o`}90;jt4V^b;M|D~k_UKZMW~8re_sub8YKIrVV_u__uR z5ef#(fIA0?JPb6cK#y0bT1|G&gox`Pv``J-zf_QLjPi`tErQ0+2${9GMwS1A0>&QL zQ#Sr8&)=M69Hr1hIpk=As9)@SN3d26Xk``bd*tpdJt2p(oLhdaS;LT5T9qNab=kDq zA&p0_MQ<*&ghs{>tq~JU;ghj&1}2b9x7gFcGS3&CeR>+)uLIt%A9UQTV~U7t)xZUA zd(xJj!DU@?%dk@kU5(+F0<|3Xoz0UYehIA(=O*NFQ`t$x5u61AY+5Nb@M5c#`G=@q} z6;u@1w1)LRU}lM%%IVU0cGlHnKd7m~Q|F#yzCl^@uT#;({+il%lljo#mTPU7ZXWZ2 zL@bk*U~WZ>J)aWu89+WSIn{}|Y^bNswj~*+mNJ^2td&L|?^yh?vU&}xcnVjSIO!;_ zQSk)ecixNKjVnWJCJZL@zqKF;>98Ib3Bn*~U3ChO9evZB3vp zICKzKDEegJPOra5w#IzQ9K=OoKz|o*Z;q6yWE$pIAo67Y=5oG}e$ht7y8}jDA`f-P zK>eBjB7HMXUfTJq+^FOGefrVX={E8m46S6A6>i4+JxVqw#_^Gb#4pkvJI24>nC?I8NM`8`gy@lEwqh+0&4TYRHk#(U|K*_YyMeT4I=JgMTl`?C-X`V zn1KBAky-=`*-7wj!_n8(6|Qco(tqw-$c@;Z_cX-tL_5n*0j$zUf3W-O8$Wj4tjzN^cDI(5>C0`KD52R;jq{1FFF^hai|?&3(2We(Z(PLwDbr-G`x zz}^OP6e|UBh>ky6W@lA?ki4N+PpD2JM*9BZd|Is_Y^B1Sk<}wv@5cj1H`$!;Rzw3`~dMncP6r*Iz86R-# zb!|1*WMYM0{6s#{Ykj;&S(RA(u&uHJznq&cMn|k6yVvi^RU^)rC(>$lU5UGS;F87i z#))}-NXr4f9$j0k6C3dFlx$Y+cPCSvl}!Lz{Xgd{r493bz$_~ybl7z20#N=0Q9rm9&q)!6gOsP2t5jvqVq&$k zu5%gNoQ46PmwwEMAp&IEQ)N->y?wfEl#1-j*mYTRXJ6T@eAzsM+2QY@&R^Kog0TfL z1sFxmeWE!9`Hyf7hjH=7U|RDp@)GfD%#*!8`wPYLe7~-u%y4ZAgXRB3C08dgEFH4c z>U@sP`#Wnem`g7U z(3IoKFQiV`%j9J)MVQYb{OcbQ-KI_!j>+JC8>|{j{TKRTIJt*k;{`|Q4()FK1 zl8CZl@h1-Y&~W|xvl9y-$9S(|LQEF}i4t70qT7PQb%Re93$1Vewnd>P|ao@a~SBs#UZp!d`VhX`&r0lR{FsZNQ$>?6V}9l;nKvv zs^HN-e_A;O)mbR4vw~>()a6q6se<}01l>(JQ$k+%iAL&CxPSB?Pn`xF>Bi@b$HWd;@l*2 zZjA3%8ujUEvDI8q`QWr50L=j-KHCfMGl%MzYQIS);KUp>K@g55Kp3Nw|IW>mmD=g0 zVamAb>q%@~Edrw#6^7~bdhP00o+>6EjFRHilx8Oxit~-XEnwvYP1;3EdBb7%tx9Y; zR}W4V9i~2(2#7Z*U1tmX~B)|7nc}ajrpFw6Ycq7s9Rhy9AHX&gWE7 zu86#Vz8~$o%1tj89PJ-D*CSp|X|`^$t)oh^jY$KF@mh0j^Mijw)3ZL*5W^1Cd@vPK zn-@Cm5v2WdR`E?Ww?s`4V%umFvawEwTkY&-n>BiJd-AgqPt00Fh1*(IHFgeZ3nXrh z?Fm9;lj86ziKLzNqf=3#edfZEFpFZMg+BQh>!Ab8Y`coumO2T3S<<(LI@w_&Ki|p& zkVV;qnCOnm1j=7j9D=exDLqvdQfiutL0B)Mow=1TzpdXw)Yr?7NymtPx@H{8y6;;I zV(cfn018@~mxLf+`Fm=U5`i6abkQ#({|=o!FhibB1GD1R>=B)fa9b9!er?jP#0XMw z1=OK8N5y?8jO51gEt=hbq(B$TWPI=hPyp1pSdwcTy1yasFRuYw9R}t z6!}}l2e4ex{16Gp15u@@Bf72?Y zW2<(enthznSZFPem9iaH{mijqlUl)E&-L%iAS&bmq$&W;li_M&oB5h%zowh|U;V{{ z-tayKof=62AJoPT9e?N@dAyUns6syZaWgz14fUN#9;3qT#wVgMUaRV2vO6A~&ArYr zdrB+BAVo4_Ys&F`e}Qk2_mMP!$=}?idkZPXcW}J)q;$slKSIBTIa9VjST24mV@x}fZO7bX@0MfZr@Hh@xT2FSNOB)AlKFBhBV9PZu_;F z&%aVJFABob?Bepk56ryuufitE@8$jsVH=y^WFUFTm8Sgx;t3pI{W{(z>)&1p7t8T- z|D5y*Lx=AXO@T$bD|by&lgD=EcdbeB3HvMJoy;qeb^x2sqLBR1gS;@9=6>DRh*~w% zgHhM3#(>>&(@4NoBKw3!JB=2D7wG$=gj;{Y9fyS}b$HuwhOPgSe6f#SHa19zp;+ZG z80uN*8CAgH6h=kXc_sfo_zxjQl92qD?_VFhDbX8?+^3Y{J{4@6H~txtXyI8X;C%h_ znk@zGsVvKLs;5%5ZxQuQtuw>Q-w3BR=oPm`&GQwe@!zX=gTmp@41sQU>m4Xo$ah4q z1%b1~oIxZ_sGkc>aBtVy7en+^Y5CN3f4-)sz};bw1te#sV+y{-{q5zb>6!LAvL&}| zgPeX-j$Uc!AgU z$Gq>NeC*4EL}{N88KvXE4YN98IyQ9r81LZ3Fs|5|+nBFvpS~5zIwtW?%pV}dV^TOB z-?-B2&L8ocv+91e(mt|!%WX42%OglT#CsiIV92Dl;Ne#f3W8?375R3>CLJ?($e< z`8!G1h5kUM6?OamrE7K9H*Yn`eZqnx`a|>2l8TlS7&s0%tM{(c=&lS!)$vv)@7l(d zRx*KLfGyS%Irf^j*7n$PEfc;C>fKB01WL$V#?_o{M#h}$lc=A4fIu0ndZ_JqZO!aX z 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 |%F(BJFHCq3pF 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

    YBvu9UVk?n=SJ=$+2ziatfpq&?O74_ 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

    Qf((sXQc*MjpgC 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≫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�{S+3fZ-hMfu{#Z(yVPGkh*hYRfc)(cg0-9cef(Gw5tG+NE2Qh^& ztn7IO9SF_lhQ#^Xu-4lA@w?1C*fm)?-Ih3ADgxl+02u&d5~R(=gJvn-eXBY|0#nL|?fEGq~nPBe0rc`&HSHokHof$WLnlDMjPa#5ehL_B8LV08NZr_N3B&PWXOT zw3X1Xd?+5>uv_$}SqaF?hW8_oK=r>_9Ax@4DX5U(e5~&-N?Dn{;Q`p$0^TUY#f&)$M0k18G&xvcFlzz!Te5_JRgsx&DG9es5B))aLSM zY+}Tn@M@1ArC8+B*AM5Knh6^hm(rW2Vld8%J&W?Fhf(jDY4|&L?=6ToG3YC&@1D?Q zlEmjDM6Xbbmq7FhezYvE)P3y7i;32~SpRIm_64h9(v3v!ro)2fE?Lj&J}BMp8(;0Fxz=oe zF;kA`r|B*+;-hm_KDbUC*vD!_)QKet?9ddMabk*e2e*zyZXafUA+z0LsAYxY)RH5? zpCZ_gngRlS<4m5z2|1syb9YBCs2tl(m*R&~o%*JV3(mOIab?eHO@8FT$-o%I z$sh(*%mCI_3pA`GwW6f7D79EWsVKiZFFv(0Rj(i~J;0ll4WyI_2&;kg> -The kW Xport plug-in source is released under the MIT license. -Basically, it means "feel free to use it; credit the source; don't sue me +>> +The kW Xport plug-in source is released under the MIT license. +Basically, it means "feel free to use it; credit the source; don't sue me if something goes wrong." >> diff --git a/test/models/IRR/warn_dwarf_scaling_is_intended.txt b/test/models/IRR/warn_dwarf_scaling_is_intended.txt index f651a8643..d0e4ea361 100644 --- a/test/models/IRR/warn_dwarf_scaling_is_intended.txt +++ b/test/models/IRR/warn_dwarf_scaling_is_intended.txt @@ -1,3 +1,3 @@ for dawfInCellar_ChildOfCellar & dawfInCellar_SameHierarchy: -the strange scalings of cellar and dwarf are intended. +the strange scalings of cellar and dwarf are intended. diff --git a/test/models/MD2/faerie-source.txt b/test/models/MD2/faerie-source.txt index 4906ff4d1..afb00a170 100644 --- a/test/models/MD2/faerie-source.txt +++ b/test/models/MD2/faerie-source.txt @@ -1,5 +1,5 @@ -From IRRLICHT/media +From IRRLICHT/media The Irrlicht Engine License diff --git a/test/models/MD2/sidney-source.txt b/test/models/MD2/sidney-source.txt index 4906ff4d1..afb00a170 100644 --- a/test/models/MD2/sidney-source.txt +++ b/test/models/MD2/sidney-source.txt @@ -1,5 +1,5 @@ -From IRRLICHT/media +From IRRLICHT/media The Irrlicht Engine License diff --git a/test/models/Q3D/E-AT-AT.source.txt b/test/models/Q3D/E-AT-AT.source.txt index 2df8826f1..900ee552c 100644 --- a/test/models/Q3D/E-AT-AT.source.txt +++ b/test/models/Q3D/E-AT-AT.source.txt @@ -5,4 +5,4 @@ Downloaded 4th November 08 (Obama ftw!) Copyright notice found on the page: Where do the models in the archive come from? -All 3D files available from the3darchive.com are from the public domain. +All 3D files available from the3darchive.com are from the public domain. diff --git a/test/models/Q3D/earth.source.txt b/test/models/Q3D/earth.source.txt index 2df8826f1..900ee552c 100644 --- a/test/models/Q3D/earth.source.txt +++ b/test/models/Q3D/earth.source.txt @@ -5,4 +5,4 @@ Downloaded 4th November 08 (Obama ftw!) Copyright notice found on the page: Where do the models in the archive come from? -All 3D files available from the3darchive.com are from the public domain. +All 3D files available from the3darchive.com are from the public domain. diff --git a/test/models/WRL/credits.txt b/test/models/WRL/credits.txt index 055f73734..7be7fa192 100644 --- a/test/models/WRL/credits.txt +++ b/test/models/WRL/credits.txt @@ -1,2 +1,2 @@ "MotionCaptureROM.ase" Recorded using ViconIQ. -Converted to VRML with 3DS Max 2008. +Converted to VRML with 3DS Max 2008. diff --git a/test/models/X/anim_test.txt b/test/models/X/anim_test.txt index 3d270dc7d..f2ef6c056 100644 --- a/test/models/X/anim_test.txt +++ b/test/models/X/anim_test.txt @@ -4,6 +4,6 @@ Frame 1 - 10: Zylinder knickt ein, so dass der Knick in Richtung z+ zeigt. Frame 10 - 18: Zylinder-Spitze streckt sich in Richtung z-. Frame 18 - 24: Zylinder-Spitze bewegt sich zu Position in Richtung x+ -Remarks: The exporter failed here for some reasons... although the mesh referres to four bones, only two of them are stored in the corresponding node hierarchy. So you have a mesh with 4 bones, a hirarchy with 2 nodes and a animation that affects only those two nodes. +Remarks: The exporter failed here for some reasons... although the mesh referres to four bones, only two of them are stored in the corresponding node hierarchy. So you have a mesh with 4 bones, a hirarchy with 2 nodes and a animation that affects only those two nodes. There is no timing given for the animation. You have to scale the animation manually. For this file, the timing seems to be 24 ticks per second. diff --git a/test/models/X/kwxport_test_cubewithvcolors.source.txt b/test/models/X/kwxport_test_cubewithvcolors.source.txt index 94ee48e4a..7c81e612f 100644 --- a/test/models/X/kwxport_test_cubewithvcolors.source.txt +++ b/test/models/X/kwxport_test_cubewithvcolors.source.txt @@ -1,9 +1,9 @@ From kwxport http://www.kwxport.org/ ->> -The kW Xport plug-in source is released under the MIT license. -Basically, it means "feel free to use it; credit the source; don't sue me +>> +The kW Xport plug-in source is released under the MIT license. +Basically, it means "feel free to use it; credit the source; don't sue me if something goes wrong." >> diff --git a/test/models/X/test.txt b/test/models/X/test.txt index eaf9d9c3c..9452855b3 100644 --- a/test/models/X/test.txt +++ b/test/models/X/test.txt @@ -1,3 +1,3 @@ Simple textured test cube exported from Maya. Has a texture that does label each cube side uniquely, but the sides do not match DirectX coordinate space. -Is not readable using D3DXLoadFrameHierarchy, needs custom text parsing. +Is not readable using D3DXLoadFrameHierarchy, needs custom text parsing. diff --git a/test/models/glTF2/ClearCoat-glTF/ClearCoatLabels.png b/test/models/glTF2/ClearCoat-glTF/ClearCoatLabels.png new file mode 100644 index 0000000000000000000000000000000000000000..d47f2f5e1f06116fca5ddd9697001ffedcc5bd68 GIT binary patch literal 10270 zcmZ{K1yogCyYAYww1BkI2uOpJw19L=NQjhlcOxPtx!H8fCKUt(1WD;`kVa{c29ds# z@BC+s@0@$@9s}2M$6WQ!=Y3*EsH;B1!=}WBAP7%EUPcpwU|<&pVWEJ{7@-6Y*xYfF z*LQ^=PKMh*m={vQ1H6gprl2B=xdMAYEJCvLWd|LCXdwj|DQ&NrolI|S;`Q^t*?Pl7 z({_&5V$q+FGcSslf;C&9Xq@zMB=!rAvLY*40uBzvYF%3fQ*__5S&i2FYOG(-ITmo< zU}J^rkZ863Lb3U-k!bpZNM8EzV)<&m?#wb37xtEMXq_u1wc%*+$sXSxFJJbnVr@1_ zLM01AQrH_<5G^@`^{)+S&^=PAp?F0kZ)$&Uug%7At{&wN6g_HTVd0pVn23l7JrZ(g z=pIt@Y&|=0_eaHY)sVQWv$OtZ6Db>;t*eXEo|)rgHv)p#PX@JYB-o;3=b!gAd4F=o z8FH%0%l~Nb=Bv)HuNUFq;9z8AWMF8UoFs)lZ`qEYq@__xNHi58S1SkEvwH-}m}qp2{xBI2WDU%W`eKI3AZLs?l{ zLy=Tg#!c>HQtEt9QRTX>N*WbP=G%VR1!ZPtLbQZfFJ8PT3f-!f;NydLnEGGr)UGV9 zteBHVA>eS=ldXw~Qr&NLb+U4D#$TPJkeWu#0b&9IlLG_x3k}|(p`qH?;P|gA^uWK( zo_og9(r?Pk%bS~T{_M=|Ei~G4`wBYD$;->92s!7)#u8)?HM_3kA7%Uf{TY@n=-A|Q zQqoj?aBx7zWjwx7KRz{8SWqyS%D28#yO<^DXzbxpqs_)tTH`R^Yg!?XLwNUYt7(NE zyN;1jrU$D=*yYsAz>bcNy1F_J!>{|lI-)2uhpJu=mA$C$?dVtq&*5S<`DI#qxcrG#6*&vyt1OAVNdq+0GIW_`)RkQegFP_#!zq_Gt<-7cKZW@i*D}j zpq9k5%dd`RW?t;{!=Hj0nG{+%S|5Vrne*!V`8CLC+D?9?3&aYQ9xIV$riMO!`jj1b zU3YG+*Wh)~5k=O+IzkG+%7;2j4QSNXeKvJ6gAG*R!+xsLt~H z_iw+y`#;HE`oSP-{KDd5h4q-os3;gMEp1LgfgCfux~As(e7hWKS#>EsFM*xify^x| zB05YXWL$o=M?BIk+c2D6STNSn5%Bn}2MM{XU`@->$M2tAj*eCUh;of{synQ-6;ll`1MKwq7|c zwZPJ#@Q8@qfGh+;HehseQZ7kbM<;V5a_hly*}rFNW6x$>*9T!ia5!9se&noF%=Zr@>Fiu_7shQr!yhUw zm6Vv2l)y{(Bu&g$0LstLFM6nHZ0wK+GnqMsef;#Pv9S@JkU&XA6?nEv_WJc}C_FMU zZ!@C)>sR$ac=4$9Sb>J8)6ULL4~_O71@yhGt#}n$T3RA}@Bkkg$HR2b*W8RCeh9Tp zO(i4ce0)TpjehbIVjKP;^H1sNRFJ6mVSa9Ia2mQQsFcXa!-Ip0+S=ILXHK~0<>l3| z{~c##PuJKx^pXzVlds8YJs>J3Hc?B%XNkLjxuIqpaJCArzWHn|eWhJA;4-D{=f+%u zNI3~t)8D+oT#c72TJ-V98h0{8y?N>B^QKBdCvMm}bEHDPM9G?!47e>m;@=_7X=*aQ zdWngNc^DQQt-`g-#K))QpI2O0C(Or33@!LRh+u6B!1ivsac+U1*Pmg>+t+%%~ zrM%I38DkR#AlYL9frOdt)YR$W;o<3NP{PgE=eC)xixU$TZW^Db?MBf@yOrJDA7iu} zF>`Unw*AZyaWAQ>)6Z4M3jJ>M%>x#coxM2g*gPNgga`}Gpw{7DgbZ)0G7Cw-&DAMN zaLe@cSqOpnaGhAH)y!9CGceMo00{}n*Sfm+2$Hnyk1whtsbjR+2KxF+OG-v-YZ@9d z-gneGE)H5&6$cE4-(@iN*m?;@X;S+?+ro-v4v&r$$g*E(8W<>z((#F! z%a9WhNz0LKB_a^+o|MPnOO(Xb$74BzgM+7gi-p-32t_0tLXk5W{y+j90IAl6x3~AW zv7(|PxoXJanctS~xC%jqp7I~$_1`;mw6rZUn5_jO7@uovB?2!0M&5e_AO+m+2jYGk zdwUYzhbuWbv^K+&22OQzsF?WXmX=w%gm1tij&GqWV=E252)MKYj!l z)qHb(k?nUFk(^9cCuc=a@fn@_)fcX?aMhxuDkKuUQ0gwFpB2~& z#=?c#?er_nBffp}bq2jsU*E!FwY0pcclqE=NJwKtgR&zUZi}=ai)qgiIn)YC65>K= z|Jo=)d#K04R`?yHEqPL|EEtk6ozD&}PEH*b{ZHqal#&=9K79GjrkxRRCQ~k{U=nOue}A!)-rJ+&De@JcK9ION*81g(8gzN-x5ldyvPtzD zC6}CJB3uj)vcEnl54?*WB4y2N(Wb1dYzHXB#)jAVF{^8GPU|rq1{5j91Nl6J8X6nP zI9_}K4GrM9I+;nxsIto>z`6+2_NA5)lh8@|#ic(i7_uCSYSy;4AL@o)7#SJ8c;R!h z^*mTo{rU5WnHlUCqy0-uCYdoZ%-jkuX~f7vVyJ~H!@P$jv&l)Ieq~(jq}+r6zC8PTunb!o&VB0P;bA9maj@Lgm@eK5@KxDuFhh83 zWF&sT5`dXnw)jdMNAa@@UBXm9g={QSvLo8_evX zXTk-cv$7YjaJj99X`emo+TY&?oeS17kS=&&`d;rBZ3=%TWp0T(b zx7e59(DTUF#%A)9K_N%0xoMB~(5L@qVKL_;_ee-S>Q{BPwK1@>|Nc>7HeSleU&;@5 zFrzvEW@*0k+MeVfjk0ibRKuUfI%0JjbDH7MVrzrTYt{7kb!mT-R8dg@)l96fr>7@h z-mRT%V&iGFyU?g4KpfT4zlvbYq~No35~4ogV7*fyrBu)~r=zVMhxcc9zO;gklXGy1 z4G#?^SZ=U5+i|gJ-s;HO6R)s;C$C?PUS*n>i)KunVU%2S`PVOVUJBPc1+9+6NaZgMGV-)w~}$f16 zhqIJX+7!270s9%5Yvu=3m5~u>VVd{zqYq=pqsOyz;0;ybTJS!=y|D*hO92&!U+jP_wKDO zFPnh}MJ)x6pPFy>`7$+I#@}Lg{*b3pirac`baZquOU%{~dn-T!saY(ZMHk4OsJjI> z;a!w`NEGoFmlV+T^!Rul&)vE9_I4mU{Er3%UFtFd5CS)UyCk}iwM9im6%{;mbit9> zkPzTKQBi%Mx#2$$)iX9$&G>qZnMeF?^LXt(%az6KCvaE@%mOru)sIHsYAPz?+0E%& z8Pl!%GbX2|v;rjry_paO(VXhGwxx=3xS8jIQTIuUExln1(}5xQR?vRQLKmST-RNMGG=CGo`&IT;NU58!%P4# zUJ*qg5UeDT_4W0cPdrNsFXvVejI~sP4j;?Pm^1U6b#;f*1qq(sR+a6+X;y~EL~A#gdWs;ccQFi4r570c8i#AItk3^W%Tx<=};9IJ%RyIIgFut=+!Ak4L!#23~;}7#ONwI!+Px z96wxS5hdOR5i(9w1R@V^zowp=ZU;Ix3cMX zOi|$aQ1;O9W)C;y<5@Tk1ML?(^gAH6pcaXQvBTOnpNtgBqCv4;eLX$7UlNWrm3&4R z8yDfs5U$A6)NY`Z4H_yE?6b8t@v;X>_?#gr)<06wpWZ}41 zS@U^ht0~GG9D-IfJN$fnfHjAn*Zxb1;X-Cx{=U8!t9{AM7rRe*ZGoYYBF|wh1IM_^ zk{f>HqWtbSiRw$N2OA+4uszs_BFuSH)mbjDFZR$;f~%^IFlqdPV8eKJ_V!?E03ir= z0PRCZky2M5251c3LZ6I{g7{+-Gc#f;Dk@^)4+{t&NEa$C`W}@xm|9rKHiQDsy+XgT zy`7$rpsA;~@#`0_c{iaz`h7w|bpwNorB>7ru1ce5K|nl+h=^29+tJX_1WOXq(5Ste zY7B2}+rvrO<7Z@CNq!kX!DHGUNB4BWYZ=euC)A9UrI5Z&0Oj#gAd`x&LMwBRO<|PgHbVg`S^x1gc;)H3)Hd!lM=TX zFA9EKBg26`x=k$+;5U*d>n9%A_6|=7OYYxNK#Kc~3C{lmT@D*01&dNc9MtV#?38XH zX&yMVq)|%$yvmKfF_U1=&(BMoZ+rk+Kv}wBfHehlf z%^9&w-*HHKcnCm18zLU*+8n#;u_t-#6nZ+NeCl%nejhuj?a|r*1e`FbCRufGntWy( z7#5XUZXtiL>I|=eqZyQ@6oOI_W>$qf87gtKoK1FDxA!J7&DF*D;LL>({h2IxT# z{Nb3L!kHLey^ybLjzX}5IyfxdCXth;GVl}*w+`1 zz_}Rt@QmQ?rJIL`&hzK67GSdZoEEPIpbg}r)}phpu^sO3(>kK#qRq_C0+Rr|igc3( zApWpFuW@p;*??){;_3?eD0oGjx`IAFZcv3~S_7j^?jt553XckwY{;ggqobgp5DO$A zm%?x(V-Xbe1eF9d7!wl{@G*gT2UG$N56`n0>eiXqlt7%g^mH5uypbqn-k-wr#BG+^5h9jl9sS23e|1$IxQ{DeS32AixV%8w#_4|1K8=>T8-Uw!whpc~X(Mpe=UtUodpkV1smp~sk^YwEcM<2eSm?9PiGkKMPlOJ+X|eYZ95>rSXzwm7YUkB zT8F16w$NFK;ru4giztQ7Cy(nTfk(`SU}I;0KVYerE`WFZSIifTK0!gdHXMKx)X*S` zANCY24Lektg=BZ88V9<(yj%#lun&>giy(#fTieZjt*Ebm!3!L!yhC&q! z7<%w#O+6B3iocT$Sy@|akXZpMvH75r%J^ZW>M@f;q?jjGw8$dpX@OFxw1&F+XUhRfkPv1R%w1Sk&_~W(A2T59!3cWvn{tOHZVDL$nt5qjzIiw~g zCzpB#1Cc{XNeN~c3Iu?-5H}h_CE53McS4VfjO>{s3X(B@#;Ry$<22xCO{H>Wq0v_x zd!=Kwym>7)OZJ=cG=T&Sa=JQq|7VmA=<^%|L? z18Db3SoG#75cWU|;E=Lo;o$*{zkBy?aBwg&vobladAQUa)`{($u!SlG{tpuq6F~Zj zi;I(S8bU#!je=U`8wwI9V2y#XTj5F`0IM!IZCI3pL*;ed>guY7M4j)cgPa@&vhEtN zJ`3Jd8Twq-KmGm6%p4HB5kA`q_>#T7y-b;&uI{sXHE{D^K;qes6%!DuBp)QtmD=`` z1#Hn(D9^x|tG3xVtH^5)|c$?&&{J+vu57tleN^4@SSnbSyd5mRdj!hlpm zuFIM1937ke&JH~n{V}1wWG-O*Dk~?arltbnU~OmjduuCCoK87^7=tPQnETi+=KhnU zAASn%*-tzN+Rr}Bn|i{mNE<1J)Z?oKlXg2d=m+dGXu-xQb1R{-zpm-hFlQE9__LIl76n+pDy zM_hrnFCHX3Ha)HX{5d*g*7^Qb)wCY%%#uIkT2NfP3F1HuC|AAvr_*Oi;B7ys&@%+- zC$L3#W*+CNZ|v?~k36FR46kQ;ngR#Y0CJT>Y66pt-Kbuhk;uAS%xz=%dnis14%8?O zAR72jAoa*sfPY4Bk#f3$XJ+0fCubpwFf}s+4VjTBLOR#$cw=OD{##~72JnyF4^hk= zHMF# z4>Vsi2zXoz3xnntAW8sHGI&O6YC}L-0y_bI8p0|=uK@xHFR!maBNS!_q=D2e12_(m zL+iHLIk#D4JCF&t{{C#OV`(AqD2B4czL|Dl>*`JcUreh|bz*7?F7sBJE=zGd&yA|i zP*phuPywE=6-|P}z=&eKOW(MCD-Buks0Q}@LxLQbFNohb zFt2@6mH@K_8qh}&o^Rg@4?TsQqw&U&V4;ET7T>qJRhp={3WC>&kjc)@cgLFG0gTty z{g3FZeCJ$yJ(SECf=;-eYesB;@p~5B`fR+;6Rl9n7j5JoN(<1JHjG8oPAeD^zCzahCWy+&{7_tNedIDZ=U*D=roi-Z>7uQ3$^j67JUbgV} z?}6My$TO~>s1dCncs+mq9Ax-J5sVTNiy&c}t8zSS)59~hW8 zWoadlxCAz$f&`wTxJ2vliN2=hcR$L+N-V?a350z7vHaCTV>V0p6gAHY}4Q*|0efh%g6~m?$Dm@b08DQvFAur^2$>m88TeUt0&JDTS1Ih}6BJC=EAqj~B!ve^EEE(J00np8B?>J$32iOV zy(kY0NUTK@hES;o|%|%2l@CGFFQh!3Ep#fc&JlmDD3h32Pn(emX?tn zrU}dgRE-$1r?C>(v=N4NE}^e1$N8Ggy6_>rFIIQGk5VMfiNVu0tsqA=fq0y>vqVs7 zMMTnkz9lFVmTWwh8wcrbT8O783NZM!(#d!0gY(!bc?4gc8ax%U9E29OL! z%foZmASk$_MZQ@w3Jpv5&F9MHd=7OD&ZYZ$WhIYSORw>jx=G8JXnujl=2fcgu4m8)>#V*1~1^ze1T7@Vr02tyB zh?A8bI0$Qr#z4RyWCjvwb6heSnwtsTm-7wYV3`bi=7)8 zMu&{6ZJvX2R8or4X1fCJAn2LV;b6;uY+e!(@ zCJY;h-?OQSAPNWdX~nbEuC_o;n)~u1I6=+YC5Ws%ASLJZ9i`bjTn z>+0x`A`kg@@0vb(R0`-nuzx^f%ajPbzB*mJS%Iqo#Owaxdran-*|oj9EB@*`8gR-$ zM>Rf+eDv6$W&KFu!)xYh^!XgrB(TJb2m-GC%;|C#A6QHVNDheI^YWzUyQ9pYe9xR+ z8x6zefcwzeQ1;?pH4Ow906aT;Z*%hucz@|y6`)qI0LS+dT>w}_BM~q+F~NjTjEaox zeotJdH1Sd%sR@v4bkyZ|Lj(E?cxZ~hMnnvO^S literal 0 HcmV?d00001 diff --git a/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.bin b/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.bin new file mode 100644 index 0000000000000000000000000000000000000000..e7c6a93a4995917f931d580f7d25c94f33d1639a GIT binary patch literal 50328 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 z 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 MV6NHY-@g5S0BBn6`Tzg` literal 0 HcmV?d00001 diff --git a/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.gltf b/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.gltf new file mode 100644 index 000000000..ae12bfc11 --- /dev/null +++ b/test/models/glTF2/ClearCoat-glTF/ClearCoatTest.gltf @@ -0,0 +1,1669 @@ +{ + "asset" : { + "generator" : "Khronos glTF Blender I/O v1.2.8 with hand-edits for clearcoat", + "version" : "2.0" + }, + "extensionsUsed": [ + "KHR_materials_clearcoat" + ], + "scene" : 0, + "scenes" : [ + { + "name" : "Scene", + "nodes" : [ + 3, + 7, + 11, + 15, + 19, + 23, + 24, + 25, + 26, + 27, + 28, + 29, + 30, + 31, + 32 + ] + } + ], + "nodes" : [ + { + "mesh" : 0, + "name" : "BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 1, + "name" : "ClearCoatSample" + }, + { + "mesh" : 2, + "name" : "CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 0, + 1, + 2 + ], + "name" : "R0_SimpleCoatTest", + "translation" : [ + 0, + 5.25, + 0 + ] + }, + { + "mesh" : 3, + "name" : "R1_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 4, + "name" : "R1_ClearCoatSample" + }, + { + "mesh" : 5, + "name" : "R1_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 4, + 5, + 6 + ], + "name" : "R1_PartialCoatTest", + "translation" : [ + 0, + 3.1500000953674316, + 0 + ] + }, + { + "mesh" : 6, + "name" : "R2_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 7, + "name" : "R2_ClearCoatSample" + }, + { + "mesh" : 8, + "name" : "R2_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 8, + 9, + 10 + ], + "name" : "R2_RoughnessVariations", + "translation" : [ + 0, + 1.0499999523162842, + 0 + ] + }, + { + "mesh" : 9, + "name" : "R3_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 10, + "name" : "R3_ClearCoatSample" + }, + { + "mesh" : 11, + "name" : "R3_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 12, + 13, + 14 + ], + "name" : "R3_BaseNormals", + "translation" : [ + 0, + -1.0499999523162842, + 0 + ] + }, + { + "mesh" : 12, + "name" : "R4_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 13, + "name" : "R4_ClearCoatSample" + }, + { + "mesh" : 14, + "name" : "R4_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 16, + 17, + 18 + ], + "name" : "R4_CoatNormals", + "translation" : [ + 0, + -5.25, + 0 + ] + }, + { + "mesh" : 15, + "name" : "R5_BaseLayerSample", + "translation" : [ + -2.0999999046325684, + 0, + 0 + ] + }, + { + "mesh" : 16, + "name" : "R5_ClearCoatSample" + }, + { + "mesh" : 17, + "name" : "R5_CoatOnlySample", + "translation" : [ + 2.0999999046325684, + 0, + 0 + ] + }, + { + "children" : [ + 20, + 21, + 22 + ], + "name" : "R5_SharedNormals", + "translation" : [ + 0, + -3.1500000953674316, + 0 + ] + }, + { + "mesh" : 18, + "name" : "X2_Label_CoatingOnly", + "translation" : [ + 2.0712804794311523, + 6.619500160217285, + 0 + ] + }, + { + "mesh" : 19, + "name" : "Y0_Label_SimpleCoating", + "translation" : [ + -5.3578033447265625, + 5.25, + 0 + ] + }, + { + "mesh" : 20, + "name" : "Y1_Label_PartialCoating", + "translation" : [ + -5.3578033447265625, + 3.1673200130462646, + 0 + ] + }, + { + "mesh" : 21, + "name" : "Y2_Label_Roughness", + "translation" : [ + -5.3578033447265625, + 1.1383899450302124, + 0 + ] + }, + { + "mesh" : 22, + "name" : "Y3_Label_BaseNormals", + "translation" : [ + -5.3578033447265625, + -1.099429965019226, + 0 + ] + }, + { + "mesh" : 23, + "name" : "Y4_Label_CoatNormals", + "translation" : [ + -5.3578033447265625, + -5.252500057220459, + 0 + ] + }, + { + "mesh" : 24, + "name" : "Y5_Label_SharedNormals", + "translation" : [ + -5.3578033447265625, + -3.1963000297546387, + 0 + ] + }, + { + "mesh" : 25, + "name" : "X0_Label_BaseLayer", + "translation" : [ + -2.087031602859497, + 6.616230010986328, + 0 + ] + }, + { + "mesh" : 26, + "name" : "X1_Label_Coated", + "translation" : [ + 0, + 6.614150047302246, + 0 + ] + } + ], + "materials" : [ + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Simple_Base", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.5, + 0.019999999552965164, + 0.009999999776482582, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Simple_Coated", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.5, + 0.019999999552965164, + 0.009999999776482582, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03 + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Simple_Coating", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Partial_Base", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Partial_Coated", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03, + "clearcoatTexture": { + "index": 0, + "texCoord": 0 + } + } + } + }, + { + "alphaMode" : "BLEND", + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "Partial_Coating", + "pbrMetallicRoughness" : { + "baseColorTexture" : { + "index" : 1, + "texCoord" : 0 + }, + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "RoughVariations_Base", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.6000000238418579 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "RoughVariations_Coated", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.6000000238418579 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 1, + "clearcoatRoughnessTexture": { + "index": 2, + "texCoord": 0 + } + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "RoughVariations_Coating", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "metallicRoughnessTexture" : { + "index" : 2, + "texCoord" : 0 + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "BaseNorm_Base", + "normalTexture" : { + "index" : 3, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "BaseNorm_Coated", + "normalTexture" : { + "index" : 4, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012848637998104095, + 0.021861059591174126, + 0.11068868637084961, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03 + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "BaseNorm_Coating", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "CoatNorm_Base", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "CoatNorm_Coated", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03, + "clearcoatNormalTexture": { + "index": 5, + "texCoord": 0, + "scale": 1 + } + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "CoatNorm_Coating", + "normalTexture" : { + "index" : 5, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "SharedNorm_Base", + "normalTexture" : { + "index" : 6, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "SharedNorm_Coated", + "normalTexture" : { + "index" : 7, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0.012983020395040512, + 0.022173883393406868, + 0.10946174710988998, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.4399999976158142 + }, + "extensions": { + "KHR_materials_clearcoat": { + "clearcoatFactor": 1, + "clearcoatRoughnessFactor": 0.03, + "clearcoatNormalTexture": { + "index": 7, + "texCoord": 0 + } + } + } + }, + { + "emissiveFactor" : [ + 0, + 0, + 0 + ], + "name" : "SharedNorm_Coating", + "normalTexture" : { + "index" : 8, + "texCoord" : 0 + }, + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.029999999329447746 + } + }, + { + "emissiveFactor" : [ + 1, + 1, + 1 + ], + "emissiveTexture" : { + "index" : 9, + "texCoord" : 0 + }, + "name" : "LabelMaterial", + "pbrMetallicRoughness" : { + "baseColorFactor" : [ + 0, + 0, + 0, + 1 + ], + "metallicFactor" : 0, + "roughnessFactor" : 0.8999999761581421 + } + } + ], + "meshes" : [ + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 0 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 1 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 2 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 3 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 4 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 5 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 6 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 7 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 8 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 9 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 10 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 11 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 12 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 13 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 14 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 15 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 16 + } + ] + }, + { + "name" : "ClearCoatSampleMesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 0, + "NORMAL" : 1, + "TEXCOORD_0" : 2 + }, + "indices" : 3, + "material" : 17 + } + ] + }, + { + "name" : "Labels_Mesh", + "primitives" : [ + { + "attributes" : { + "POSITION" : 4, + "NORMAL" : 5, + "TEXCOORD_0" : 6 + }, + "indices" : 7, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.001", + "primitives" : [ + { + "attributes" : { + "POSITION" : 8, + "NORMAL" : 9, + "TEXCOORD_0" : 10 + }, + "indices" : 7, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.002", + "primitives" : [ + { + "attributes" : { + "POSITION" : 11, + "NORMAL" : 12, + "TEXCOORD_0" : 13 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.003", + "primitives" : [ + { + "attributes" : { + "POSITION" : 15, + "NORMAL" : 16, + "TEXCOORD_0" : 17 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.004", + "primitives" : [ + { + "attributes" : { + "POSITION" : 18, + "NORMAL" : 19, + "TEXCOORD_0" : 20 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.005", + "primitives" : [ + { + "attributes" : { + "POSITION" : 21, + "NORMAL" : 22, + "TEXCOORD_0" : 23 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.006", + "primitives" : [ + { + "attributes" : { + "POSITION" : 24, + "NORMAL" : 25, + "TEXCOORD_0" : 26 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.007", + "primitives" : [ + { + "attributes" : { + "POSITION" : 27, + "NORMAL" : 28, + "TEXCOORD_0" : 29 + }, + "indices" : 14, + "material" : 18 + } + ] + }, + { + "name" : "Labels_Mesh.008", + "primitives" : [ + { + "attributes" : { + "POSITION" : 30, + "NORMAL" : 31, + "TEXCOORD_0" : 32 + }, + "indices" : 7, + "material" : 18 + } + ] + } + ], + "textures" : [ + { + "source" : 0 + }, + { + "source" : 1 + }, + { + "source" : 2 + }, + { + "source" : 3 + }, + { + "source" : 3 + }, + { + "source" : 4 + }, + { + "source" : 3 + }, + { + "source" : 3 + }, + { + "source" : 3 + }, + { + "source" : 5 + } + ], + "images" : [ + { + "mimeType" : "image/png", + "name" : "PartialCoating", + "uri" : "PartialCoating.png" + }, + { + "mimeType" : "image/png", + "name" : "PartialCoating_Alpha", + "uri" : "PartialCoating_Alpha.png" + }, + { + "mimeType" : "image/png", + "name" : "RoughnessStripes", + "uri" : "RoughnessStripes.png" + }, + { + "mimeType" : "image/png", + "name" : "RibsNormal", + "uri" : "RibsNormal.png" + }, + { + "mimeType" : "image/jpeg", + "name" : "PlasticWrap_normals", + "uri" : "PlasticWrap_normals.jpg" + }, + { + "mimeType" : "image/png", + "name" : "ClearCoatLabels", + "uri" : "ClearCoatLabels.png" + } + ], + "accessors" : [ + { + "bufferView" : 0, + "componentType" : 5126, + "count" : 1113, + "max" : [ + 1, + 1, + 1.0499999523162842 + ], + "min" : [ + -1, + -1, + -0.06000000983476639 + ], + "type" : "VEC3" + }, + { + "bufferView" : 1, + "componentType" : 5126, + "count" : 1113, + "type" : "VEC3" + }, + { + "bufferView" : 2, + "componentType" : 5126, + "count" : 1113, + "type" : "VEC2" + }, + { + "bufferView" : 3, + "componentType" : 5123, + "count" : 6180, + "type" : "SCALAR" + }, + { + "bufferView" : 4, + "componentType" : 5126, + "count" : 8, + "max" : [ + 1.0280373096466064, + 0.23501670360565186, + 3.8289083903464416e-08 + ], + "min" : [ + -0.968224287033081, + -0.2350165843963623, + -0.010000125505030155 + ], + "type" : "VEC3" + }, + { + "bufferView" : 5, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 6, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 7, + "componentType" : 5123, + "count" : 12, + "type" : "SCALAR" + }, + { + "bufferView" : 8, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.23026323318481445, + 3.751463495405005e-08 + ], + "min" : [ + -2, + -0.23026317358016968, + -0.010000579059123993 + ], + "type" : "VEC3" + }, + { + "bufferView" : 9, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 10, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 11, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.2302631139755249, + 3.7514624295909016e-08 + ], + "min" : [ + -2, + -0.23026323318481445, + -0.010000428184866905 + ], + "type" : "VEC3" + }, + { + "bufferView" : 12, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 13, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 14, + "componentType" : 5123, + "count" : 12, + "type" : "SCALAR" + }, + { + "bufferView" : 15, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.22039484977722168, + 3.590687924770464e-08 + ], + "min" : [ + -2, + -0.22039473056793213, + -0.010000280104577541 + ], + "type" : "VEC3" + }, + { + "bufferView" : 16, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 17, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 18, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.21764808893203735, + 3.545937588000925e-08 + ], + "min" : [ + -2, + -0.21764802932739258, + -0.010000137612223625 + ], + "type" : "VEC3" + }, + { + "bufferView" : 19, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 20, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 21, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.20775499939918518, + 3.3847587843638394e-08 + ], + "min" : [ + -2, + -0.20775499939918518, + -0.009999996051192284 + ], + "type" : "VEC3" + }, + { + "bufferView" : 22, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 23, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 24, + "componentType" : 5126, + "count" : 8, + "max" : [ + 2, + 0.22341907024383545, + 3.6399587344249085e-08 + ], + "min" : [ + -2, + -0.22341907024383545, + -0.009999859146773815 + ], + "type" : "VEC3" + }, + { + "bufferView" : 25, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 26, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 27, + "componentType" : 5126, + "count" : 8, + "max" : [ + 0.9169960618019104, + 0.22670458257198334, + 3.69348676088066e-08 + ], + "min" : [ + -0.9199233651161194, + -0.22670456767082214, + -0.010000176727771759 + ], + "type" : "VEC3" + }, + { + "bufferView" : 28, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 29, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + }, + { + "bufferView" : 30, + "componentType" : 5126, + "count" : 8, + "max" : [ + 0.8968609571456909, + 0.20853587985038757, + 3.397480696776256e-08 + ], + "min" : [ + -0.9147982001304626, + -0.2085357904434204, + -0.010000113397836685 + ], + "type" : "VEC3" + }, + { + "bufferView" : 31, + "componentType" : 5126, + "count" : 8, + "type" : "VEC3" + }, + { + "bufferView" : 32, + "componentType" : 5126, + "count" : 8, + "type" : "VEC2" + } + ], + "bufferViews" : [ + { + "buffer" : 0, + "byteLength" : 13356, + "byteOffset" : 0 + }, + { + "buffer" : 0, + "byteLength" : 13356, + "byteOffset" : 13356 + }, + { + "buffer" : 0, + "byteLength" : 8904, + "byteOffset" : 26712 + }, + { + "buffer" : 0, + "byteLength" : 12360, + "byteOffset" : 35616 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 47976 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48072 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 48168 + }, + { + "buffer" : 0, + "byteLength" : 24, + "byteOffset" : 48232 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48256 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48352 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 48448 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48512 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48608 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 48704 + }, + { + "buffer" : 0, + "byteLength" : 24, + "byteOffset" : 48768 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48792 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 48888 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 48984 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49048 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49144 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 49240 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49304 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49400 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 49496 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49560 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49656 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 49752 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49816 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 49912 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 50008 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 50072 + }, + { + "buffer" : 0, + "byteLength" : 96, + "byteOffset" : 50168 + }, + { + "buffer" : 0, + "byteLength" : 64, + "byteOffset" : 50264 + } + ], + "buffers" : [ + { + "byteLength" : 50328, + "uri" : "ClearCoatTest.bin" + } + ] +} diff --git a/test/models/glTF2/ClearCoat-glTF/PartialCoating.png b/test/models/glTF2/ClearCoat-glTF/PartialCoating.png new file mode 100644 index 0000000000000000000000000000000000000000..d579bbebb8a423eedb16594a7ef715fe7521a752 GIT binary patch literal 5077 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 Q@JSA$qoJ>ky=fQyKcnMWr2qf` literal 0 HcmV?d00001 diff --git a/test/models/glTF2/ClearCoat-glTF/PartialCoating_Alpha.png b/test/models/glTF2/ClearCoat-glTF/PartialCoating_Alpha.png new file mode 100644 index 0000000000000000000000000000000000000000..b1911d9f0a303a57181d44856f3f31ea18c11063 GIT binary patch literal 5065 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 Q_!|ylXYGipvh+*(4_Os-!2kdN literal 0 HcmV?d00001 diff --git a/test/models/glTF2/ClearCoat-glTF/PlasticWrap_normals.jpg b/test/models/glTF2/ClearCoat-glTF/PlasticWrap_normals.jpg new file mode 100644 index 0000000000000000000000000000000000000000..73e6260ef7126ccb11113179c58cf6611d3f3e9d GIT binary patch literal 144210 zcmbTddpy&9{QtkM4mwFHL}gd0Tv3)p6n2#džOVOJDoNkR^>T_xnWONE@;Rbq*4 zIn0@rb2*>3a?BiOIgBl{!+XD%>+?JP@%`ib`wh3fUNvRhT{&|%F#j%c0KH#lWzblU9v1#^pwmR5Gx>>be89Wm}V zZ+Up$zT@TRAMh|RC^#hiX+&hyv*?)TFJ2}kr@Tr{dz!pljPw6`{4d9)2*&l> zx^-*U$*qoS&2NF=XRYG8_4{=<>^OTx?uPHq11BDD+;uMTO~L0)8hTe3l-wS4ZB{;b za`X^yb!h*M?0;`yPyT<6?EfCv|1++B*w(dcz~rq}gdt&#orEzV+B#P%5TW(|mBE@8 zS=JvGIDFd7SL`O?FBfPEX_wMR7@O`>;h^bu&`Yma)=B7aXdi*y(i>1J>Z1 zz1CTd`n94v@k@%hY{oRAvM!yOmqt`BfRxOCOs>p8Il6b?qs|=`>mHp9NH~CHPDbQ; z^oD7};Qa5Vbo$NjC2*@cyYwj$^JG%CMwAih&>(lyF~Q^4J92YNnA906$eL`kJ?7NQ}nL{3T+omFSO1Ea7EQ zTxSKZ_o0e>q!F98vd4Y}Z_XsjVCpFQd==<$452>{V<>~2^J#qB!-5X~DQ4yFd#eRL zee{YtiA-pEFG8mWeJ;wShuB0ZXK`FT*>#y$7;1}uAVP9Qo_nmJ9n&W9 z%!KlIG!>J9Ipm+Chwh$Od4VOo_sRCdsGROWTfQf}_nP$IDTD1KQ$tIoUujv&Au`yg z_2#Os@77RmP0}QEXm@!CKh-F{CTU7t6SNJ(xF_%PXyRhW>>q?OJe}z*9d#x>hDJp! zC*R&{li2yZPVQ*O%Fa&{l8X-unY5?d{?aG1!y~a2FX8dY4K#~gj8P*ITt6~X7};cb z$PY6$qSSpcnCjz<)DNArk@(FmO+d{zhKc+en0`%32(7!2T25EWd&|4auoC^mE|_}O zO;~+vk-_{siFsk#&v-s|0_ud^Fm+EuCW={JIL~gLW)oIaq!Zp7*?RnZAH@<+ipwsx z-u3<^4?ovFmsH^|PlOQlM0X_ne8@H8vX_B=BQZL9+Eot8Lr~{!!5Z~*R(XOPt7iq@ zXeg(h(Q};|Bv{^mA%oq@hIY1A-Pe1h$$URRlUR2b8{=d!cwv}Vc7+U9Uyd8j>gj^N zV8$FRT5q+iu@!XA;r8RJ;G4EC_w0f5Gvz61e`AsA6+!kBLfKtWTmx1~Y5dOTT4vz0vYu>_Lx!1B3foKu+rhynMZ%q} z1SUtDhCW@B!L}I3_y{@#l!3KS_`bhdAUZ^^{EN`}ye5WJ-!aj;NBXllz*qcOkiQ9~ z-|3Yl-OVzU6y%Z`Z^dA14)vs~z~e&>zc_r zt70#$&;vxQ!1umnQuh5ccv~KgSy_#WUCXB-R3#W(t5hyRMXY?-m8|&zE=-0~?vGD` zMaQ~^`-^Am6(+Jhg zP~0eXW#ZA@U(V`qK~l%`F%Y}jz4SuaR?`b6~~l7y`jt`!3FzW4P9%7`&Y-nERpCt&zbJ9|=GRiZ(OYB%peJ))q5GEEI3AM+2VT9(2^r%(Z>3wB0nS-s6MBo?zDb zFY-9mq6t?K#LfF#!gcP5PdV|1dUr>O#&{TZr)g-3b=%&C&kc!6af6AN0Uh{z6V~;pYpC zPcf_NZkKqzS*ZI?@E*1?SPBO63urDg7O z>xgW#FL()mE@@w;+~P8LGdP=V=!N{YK*+?#w@cW?|27eNawM^}zvPgTMLk*X7$&0N zcTpd5`_ch)fX`D2VISg`LsgeTWV?QlDV_UO2Gc?lg`s2iQC!!eJ>%xu?Rk3|5xpW? zC6dmL4@>5UKj^(Tx%rk*|8>orw)<7pUiBh(lrU|8_$wlyZq#tbEFA<-ABg;}UROk6)ZhT~SS>VDG9MhJlyrJh4{MWLdDWHNJb7i3Dpk

    8#fXvU&U@TnHe66M)}` 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≫PJyOL#2noO42?ak;#zU>=*qI;o{}k?8jYM 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�-h@x9GW3t>NHF#%4~dx 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 literal 0 HcmV?d00001 diff --git a/test/models/invalid/readme.txt b/test/models/invalid/readme.txt index cab740a84..6ad8b4380 100644 --- a/test/models/invalid/readme.txt +++ b/test/models/invalid/readme.txt @@ -4,9 +4,9 @@ GENERAL ********************************************************* -The files in this directory are invalid ... some of them are empty, +The files in this directory are invalid ... some of them are empty, others have invalid vertices or faces, others are prepared to make - assimp allocate a few hundreds gigs of memory ... most are + assimp allocate a few hundreds gigs of memory ... most are actually regression tests, i.e. there was once a bugfix that fixed the respective loaders. @@ -18,8 +18,8 @@ crash. FILES ********************************************************* -OutOfMemory.off - the number of faces is invalid. There won't be - enough memory so std::vector::reserve() will most likely fail. +OutOfMemory.off - the number of faces is invalid. There won't be + enough memory so std::vector::reserve() will most likely fail. The exception should be caught in Importer.cpp. empty. - These files are completely empty. The corresponding diff --git a/test/regression/README.txt b/test/regression/README.txt index 3e90a143b..a37da9255 100644 --- a/test/regression/README.txt +++ b/test/regression/README.txt @@ -8,7 +8,7 @@ against a regression database provided with assimp (db.zip). A few failures are totally fine (see sections 7+). You need to worry if a huge majority of all files in a particular format (or post-processing configuration) fails as this might be a sign of a recent regression in assimp's codebase or -gross incompatibility with your system or compiler. +gross incompatibility with your system or compiler. 2) What do I need? --------------------------------------------------------------------------------- @@ -53,8 +53,8 @@ Edit the reg_settings.py file and add the path to your repository to The regression database includes mini dumps of the aiScene data structure, i.e. the scene hierarchy plus the sizes of all data arrays MUST match. Floating-point data buffers, such as vertex positions are handled less strictly: min, max and -average values are stored with low precision. This takes hardware- or -compiler-specific differences in floating-point computations into account. +average values are stored with low precision. This takes hardware- or +compiler-specific differences in floating-point computations into account. Generally, almost all significant regressions will be detected while the number of false positives is relatively low. diff --git a/test/unit/AbstractImportExportBase.h b/test/unit/AbstractImportExportBase.h index 72530aedc..7651d2e52 100644 --- a/test/unit/AbstractImportExportBase.h +++ b/test/unit/AbstractImportExportBase.h @@ -67,7 +67,7 @@ bool AbstractImportExportBase::importerTest() { return true; } -inline +inline bool AbstractImportExportBase::exporterTest() { return true; } diff --git a/test/unit/Common/utStandardShapes.cpp b/test/unit/Common/utStandardShapes.cpp index a5df5d898..e1bb6eef9 100644 --- a/test/unit/Common/utStandardShapes.cpp +++ b/test/unit/Common/utStandardShapes.cpp @@ -51,7 +51,7 @@ TEST_F( utStandardShapes, testMakeMesh ) { // The mNumIndices member of the second face is now incorrect const auto& face = aiMeshPtr->mFaces[0]; - EXPECT_EQ(face.mNumIndices, numIndicesPerPrimitive); + EXPECT_EQ(face.mNumIndices, numIndicesPerPrimitive); delete aiMeshPtr; } diff --git a/test/unit/ImportExport/MDL/utMDLImporter_HL1_Nodes.cpp b/test/unit/ImportExport/MDL/utMDLImporter_HL1_Nodes.cpp index ff3b4930c..6fa92f950 100644 --- a/test/unit/ImportExport/MDL/utMDLImporter_HL1_Nodes.cpp +++ b/test/unit/ImportExport/MDL/utMDLImporter_HL1_Nodes.cpp @@ -73,7 +73,7 @@ public: "Bone_3" | "" <----+ "Bone_2" | - "Bone_5" | + "Bone_5" | "" <----+ "" <----+ */ @@ -139,7 +139,7 @@ public: $body "Bodypart_1" <--+ | $body "Bodypart_2" | | $body "Bodypart1" | | - $body "Bodypart" ---|--+ + $body "Bodypart" ---|--+ $body "Bodypart_1" ---+ | $body "Bodypart2" | $body "Bodypart" ------+ diff --git a/test/unit/ImportExport/utAssjsonImportExport.cpp b/test/unit/ImportExport/utAssjsonImportExport.cpp index 7987804a9..13724f755 100644 --- a/test/unit/ImportExport/utAssjsonImportExport.cpp +++ b/test/unit/ImportExport/utAssjsonImportExport.cpp @@ -58,7 +58,18 @@ public: Exporter exporter; aiReturn res = exporter.Export(scene, "assjson", "./spider_test.json"); - return aiReturn_SUCCESS == res; + if (aiReturn_SUCCESS != res) { + return false; + } + + Assimp::ExportProperties exportProperties; + exportProperties.SetPropertyBool("JSON_SKIP_WHITESPACES", true); + aiReturn resNoWhitespace = exporter.Export(scene, "assjson", "./spider_test_nowhitespace.json", 0u, &exportProperties); + if (aiReturn_SUCCESS != resNoWhitespace) { + return false; + } + + return true; } }; diff --git a/test/unit/RandomNumberGeneration.h b/test/unit/RandomNumberGeneration.h index 81fcfb59c..892e78c06 100644 --- a/test/unit/RandomNumberGeneration.h +++ b/test/unit/RandomNumberGeneration.h @@ -53,11 +53,11 @@ class RandomUniformRealGenerator { public: RandomUniformRealGenerator() : dist_(), - rd_(), + rd_(), re_(rd_()) { // empty } - + RandomUniformRealGenerator(T min, T max) : dist_(min, max), rd_(), diff --git a/test/unit/SceneDiffer.cpp b/test/unit/SceneDiffer.cpp index 6ea28671a..368589bf3 100644 --- a/test/unit/SceneDiffer.cpp +++ b/test/unit/SceneDiffer.cpp @@ -48,7 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { -SceneDiffer::SceneDiffer() +SceneDiffer::SceneDiffer() : m_diffs() { // empty } diff --git a/test/unit/SceneDiffer.h b/test/unit/SceneDiffer.h index 9e04c7210..e0dc6005a 100644 --- a/test/unit/SceneDiffer.h +++ b/test/unit/SceneDiffer.h @@ -72,4 +72,4 @@ private: std::vector m_diffs; }; -} +} diff --git a/test/unit/TestIOSystem.h b/test/unit/TestIOSystem.h index fdc3cc49b..4a42b23f0 100644 --- a/test/unit/TestIOSystem.h +++ b/test/unit/TestIOSystem.h @@ -61,7 +61,7 @@ public: virtual ~TestIOSystem() { // empty } - + virtual bool Exists( const char* ) const { return true; } diff --git a/test/unit/utColladaImportExport.cpp b/test/unit/utColladaImportExport.cpp index d5d81e396..76a39336e 100644 --- a/test/unit/utColladaImportExport.cpp +++ b/test/unit/utColladaImportExport.cpp @@ -382,3 +382,25 @@ public: TEST_F(utColladaZaeImportExport, importBlenFromFileTest) { EXPECT_TRUE(importerTest()); } + +TEST_F(utColladaZaeImportExport, importMakeHumanTest) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/Collada/human.zae", aiProcess_ValidateDataStructure); + ASSERT_NE(nullptr, scene); + + // Expected number of items + EXPECT_EQ(scene->mNumMeshes, 2u); + EXPECT_EQ(scene->mNumMaterials, 2u); + EXPECT_EQ(scene->mNumAnimations, 0u); + EXPECT_EQ(scene->mNumTextures, 2u); + EXPECT_EQ(scene->mNumLights, 0u); + EXPECT_EQ(scene->mNumCameras, 0u); + + // Expected common metadata + aiString value; + EXPECT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT, value)) << "No importer format metadata"; + EXPECT_STREQ("Collada Importer", value.C_Str()); + + EXPECT_TRUE(scene->mMetaData->Get(AI_METADATA_SOURCE_FORMAT_VERSION, value)) << "No format version metadata"; + EXPECT_STREQ("1.4.1", value.C_Str()); +} diff --git a/test/unit/utDefaultIOStream.cpp b/test/unit/utDefaultIOStream.cpp index 800fddbc9..d3e2c8a7e 100644 --- a/test/unit/utDefaultIOStream.cpp +++ b/test/unit/utDefaultIOStream.cpp @@ -66,7 +66,7 @@ TEST_F( utDefaultIOStream, FileSizeTest ) { { auto written = std::fwrite(data, sizeof(*data), dataCount, fs ); EXPECT_NE( 0U, written ); - + auto vflush = std::fflush( fs ); ASSERT_EQ(vflush, 0); diff --git a/test/unit/utFBXImporterExporter.cpp b/test/unit/utFBXImporterExporter.cpp index c78d56b97..4cfc9b152 100644 --- a/test/unit/utFBXImporterExporter.cpp +++ b/test/unit/utFBXImporterExporter.cpp @@ -365,7 +365,7 @@ TEST_F(utFBXImporterExporter, importMaxPbrMaterialsMetalRoughness) { float bumpMapAmt; // Presumably amount. ASSERT_EQ(mat->Get("$raw.3dsMax|main|bump_map_amt", aiTextureType_NONE, 0, bumpMapAmt), aiReturn_SUCCESS); EXPECT_EQ(bumpMapAmt, 0.75f); - + aiColor4D emitColor; ASSERT_EQ(mat->Get("$raw.3dsMax|main|emit_color", aiTextureType_NONE, 0, emitColor), aiReturn_SUCCESS); EXPECT_EQ(emitColor, aiColor4D(1, 1, 0, 1)); @@ -418,7 +418,7 @@ TEST_F(utFBXImporterExporter, importMaxPbrMaterialsSpecularGloss) { float bumpMapAmt; // Presumably amount. ASSERT_EQ(mat->Get("$raw.3dsMax|main|bump_map_amt", aiTextureType_NONE, 0, bumpMapAmt), aiReturn_SUCCESS); EXPECT_EQ(bumpMapAmt, 0.66f); - + aiColor4D emitColor; ASSERT_EQ(mat->Get("$raw.3dsMax|main|emit_color", aiTextureType_NONE, 0, emitColor), aiReturn_SUCCESS); EXPECT_EQ(emitColor, aiColor4D(1, 0, 1, 1)); diff --git a/test/unit/utFindDegenerates.cpp b/test/unit/utFindDegenerates.cpp index 1f8e8e93f..6f2abebfb 100644 --- a/test/unit/utFindDegenerates.cpp +++ b/test/unit/utFindDegenerates.cpp @@ -199,10 +199,10 @@ TEST_F(FindDegeneratesProcessTest, meshRemoval) { scene->mRootNode->mMeshes[3] = 3; scene->mRootNode->mMeshes[4] = 4; - mProcess->Execute(scene.get()); + mProcess->Execute(scene.get()); EXPECT_EQ(scene->mNumMeshes, 1u); EXPECT_EQ(scene->mMeshes[0], meshWhichSurvives); EXPECT_EQ(scene->mRootNode->mNumMeshes, 1u); - EXPECT_EQ(scene->mRootNode->mMeshes[0], 0u); + EXPECT_EQ(scene->mRootNode->mMeshes[0], 0u); } diff --git a/test/unit/utIOStreamBuffer.cpp b/test/unit/utIOStreamBuffer.cpp index 6d0d6a7d7..a0e4660df 100644 --- a/test/unit/utIOStreamBuffer.cpp +++ b/test/unit/utIOStreamBuffer.cpp @@ -81,14 +81,14 @@ TEST_F( IOStreamBufferTest, open_close_Test ) { EXPECT_FALSE( myBuffer.open( nullptr ) ); EXPECT_FALSE( myBuffer.close() ); - + const auto dataSize = sizeof(data); const auto dataCount = dataSize / sizeof(*data); char fname[]={ "octest.XXXXXX" }; auto* fs = MakeTmpFile(fname); ASSERT_NE(nullptr, fs); - + auto written = std::fwrite( data, sizeof(*data), dataCount, fs ); EXPECT_NE( 0U, written ); auto flushResult = std::fflush( fs ); @@ -107,7 +107,7 @@ TEST_F( IOStreamBufferTest, open_close_Test ) { } TEST_F( IOStreamBufferTest, readlineTest ) { - + const auto dataSize = sizeof(data); const auto dataCount = dataSize / sizeof(*data); diff --git a/test/unit/utIOSystem.cpp b/test/unit/utIOSystem.cpp index 767984deb..1e866515e 100644 --- a/test/unit/utIOSystem.cpp +++ b/test/unit/utIOSystem.cpp @@ -50,12 +50,12 @@ using namespace Assimp; class IOSystemTest : public ::testing::Test { public: - virtual void SetUp() { - pImp = new TestIOSystem(); + virtual void SetUp() { + pImp = new TestIOSystem(); } - - virtual void TearDown() { - delete pImp; + + virtual void TearDown() { + delete pImp; } protected: diff --git a/test/unit/utIssues.cpp b/test/unit/utIssues.cpp index cb1adb22c..5eeed6ad8 100644 --- a/test/unit/utIssues.cpp +++ b/test/unit/utIssues.cpp @@ -62,7 +62,7 @@ TEST_F( utIssues, OpacityBugWhenExporting_727 ) { aiScene *scene( TestModelFacttory::createDefaultTestModel( opacity ) ); Assimp::Importer importer; Assimp::Exporter exporter; - + std::string path = "dae"; const aiExportFormatDesc *desc = exporter.GetExportFormatDescription( 0 ); EXPECT_NE( desc, nullptr ); diff --git a/test/unit/utTypes.cpp b/test/unit/utTypes.cpp index cc354eb3d..1ac9a1d5e 100644 --- a/test/unit/utTypes.cpp +++ b/test/unit/utTypes.cpp @@ -53,8 +53,8 @@ class utTypes : public ::testing::Test { TEST_F( utTypes, Color3dCpmpareOpTest ) { aiColor3D col1( 1, 2, 3 ); aiColor3D col2( 4, 5, 6 ); - aiColor3D col3( col1 ); - + const aiColor3D &col3(col1); + EXPECT_FALSE( col1 == col2 ); EXPECT_FALSE( col2 == col3 ); EXPECT_TRUE( col1 == col3 ); diff --git a/test/unit/utVersion.cpp b/test/unit/utVersion.cpp index 0189cd2a9..0de6ef39c 100644 --- a/test/unit/utVersion.cpp +++ b/test/unit/utVersion.cpp @@ -55,7 +55,7 @@ TEST_F( utVersion, aiGetLegalStringTest ) { TEST_F( utVersion, aiGetVersionMinorTest ) { EXPECT_EQ( aiGetVersionMinor(), 0U ); } - + TEST_F( utVersion, aiGetVersionMajorTest ) { EXPECT_EQ( aiGetVersionMajor(), 5U ); } diff --git a/test/unit/utglTF2ImportExport.cpp b/test/unit/utglTF2ImportExport.cpp index 4110edcfc..e29d09145 100644 --- a/test/unit/utglTF2ImportExport.cpp +++ b/test/unit/utglTF2ImportExport.cpp @@ -57,10 +57,9 @@ using namespace Assimp; class utglTF2ImportExport : public AbstractImportExportBase { public: - virtual bool importerTest() { + virtual bool importerMatTest(const char *file, bool spec_gloss, std::array exp_modes = { aiTextureMapMode_Wrap, aiTextureMapMode_Wrap }) { Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", - aiProcess_ValidateDataStructure); + const aiScene *scene = importer.ReadFile(file, aiProcess_ValidateDataStructure); EXPECT_NE(scene, nullptr); if (!scene) { return false; @@ -72,13 +71,49 @@ public: } const aiMaterial *material = scene->mMaterials[0]; + // This Material should be a PBR + aiShadingMode shadingMode; + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_SHADING_MODEL, shadingMode)); + EXPECT_EQ(aiShadingMode_PBR_BRDF, shadingMode); + + // Should import the texture as diffuse and as base color aiString path; - aiTextureMapMode modes[2]; + std::array modes; EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_DIFFUSE, 0, &path, nullptr, nullptr, - nullptr, nullptr, modes)); + nullptr, nullptr, modes.data())); EXPECT_STREQ(path.C_Str(), "CesiumLogoFlat.png"); - EXPECT_EQ(modes[0], aiTextureMapMode_Mirror); - EXPECT_EQ(modes[1], aiTextureMapMode_Clamp); + EXPECT_EQ(exp_modes, modes); + + // Also as Base Color + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_BASE_COLOR, 0, &path, nullptr, nullptr, + nullptr, nullptr, modes.data())); + EXPECT_STREQ(path.C_Str(), "CesiumLogoFlat.png"); + EXPECT_EQ(exp_modes, modes); + + // Should have a MetallicFactor (default is 1.0) + ai_real metal_factor = ai_real(0.5); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_METALLIC_FACTOR, metal_factor)); + EXPECT_EQ(ai_real(0.0), metal_factor); + + // And a roughness factor (default is 1.0) + ai_real roughness_factor = ai_real(0.5); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_ROUGHNESS_FACTOR, roughness_factor)); + EXPECT_EQ(ai_real(1.0), roughness_factor); + + aiColor3D spec_color = { 0, 0, 0 }; + ai_real glossiness = ai_real(0.5); + if (spec_gloss) { + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_COLOR_SPECULAR, spec_color)); + constexpr ai_real spec_val(0.20000000298023225); // From the file + EXPECT_EQ(spec_val, spec_color.r); + EXPECT_EQ(spec_val, spec_color.g); + EXPECT_EQ(spec_val, spec_color.b); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossiness)); + EXPECT_EQ(ai_real(1.0), glossiness); + } else { + EXPECT_EQ(aiReturn_FAILURE, material->Get(AI_MATKEY_COLOR_SPECULAR, spec_color)); + EXPECT_EQ(aiReturn_FAILURE, material->Get(AI_MATKEY_GLOSSINESS_FACTOR, glossiness)); + } return true; } @@ -105,14 +140,89 @@ public: }; TEST_F(utglTF2ImportExport, importglTF2FromFileTest) { - EXPECT_TRUE(importerTest()); + EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF/BoxTextured.gltf", false, {aiTextureMapMode_Mirror, aiTextureMapMode_Clamp})); } TEST_F(utglTF2ImportExport, importBinaryglTF2FromFileTest) { EXPECT_TRUE(binaryImporterTest()); } +TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_pbrSpecularGlossiness) { + EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf", true)); +} + +void VerifyClearCoatScene(const aiScene *scene) { + ASSERT_NE(nullptr, scene); + + ASSERT_TRUE(scene->HasMaterials()); + + // Find a specific Clearcoat material and check the values + const aiString partial_coated("Partial_Coated"); + bool found_partial_coat = false; + for (size_t i = 0; i < scene->mNumMaterials; ++i) { + const aiMaterial *material = scene->mMaterials[i]; + ASSERT_NE(nullptr, material); + if (material->GetName() == partial_coated) { + found_partial_coat = true; + + ai_real clearcoat_factor(0.0f); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_CLEARCOAT_FACTOR, clearcoat_factor)); + EXPECT_EQ(ai_real(1.0f), clearcoat_factor); + + ai_real clearcoat_rough_factor(0.0f); + EXPECT_EQ(aiReturn_SUCCESS, material->Get(AI_MATKEY_CLEARCOAT_ROUGHNESS_FACTOR, clearcoat_rough_factor)); + EXPECT_EQ(ai_real(0.03f), clearcoat_rough_factor); + + // Should import the texture as diffuse and as base color + aiString path; + std::array modes; + static const std::array exp_modes = { aiTextureMapMode_Wrap, aiTextureMapMode_Wrap }; + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_CLEARCOAT_TEXTURE, &path, nullptr, nullptr, + nullptr, nullptr, modes.data())); + EXPECT_STREQ(path.C_Str(), "PartialCoating.png"); + EXPECT_EQ(exp_modes, modes); + } + } + EXPECT_TRUE(found_partial_coat); +} + +TEST_F(utglTF2ImportExport, importglTF2_KHR_materials_clearcoat) { + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest.gltf", aiProcess_ValidateDataStructure); + VerifyClearCoatScene(scene); +} + #ifndef ASSIMP_BUILD_NO_EXPORT + +TEST_F(utglTF2ImportExport, importglTF2AndExport_KHR_materials_clearcoat) { + { + Assimp::Importer importer; + Assimp::Exporter exporter; + const aiScene* scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest.gltf", aiProcess_ValidateDataStructure); + ASSERT_NE(nullptr, scene); + // Export + EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest_out.glb")); + } + + // And re-import + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/ClearCoat-glTF/ClearCoatTest_out.glb", aiProcess_ValidateDataStructure); + VerifyClearCoatScene(scene); +} + +TEST_F(utglTF2ImportExport, importglTF2AndExport_KHR_materials_pbrSpecularGlossiness) { + Assimp::Importer importer; + Assimp::Exporter exporter; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured.gltf", + aiProcess_ValidateDataStructure); + EXPECT_NE(nullptr, scene); + // Export + EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured_out.glb")); + + // And re-import + EXPECT_TRUE(importerMatTest(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-pbrSpecularGlossiness/BoxTextured_out.glb", true)); +} + TEST_F(utglTF2ImportExport, importglTF2AndExportToOBJ) { Assimp::Importer importer; Assimp::Exporter exporter; @@ -130,6 +240,7 @@ TEST_F(utglTF2ImportExport, importglTF2EmbeddedAndExportToOBJ) { EXPECT_NE(nullptr, scene); EXPECT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "obj", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTextured-glTF-Embedded/BoxTextured_out.obj")); } + #endif // ASSIMP_BUILD_NO_EXPORT TEST_F(utglTF2ImportExport, importglTF2PrimitiveModePointsWithoutIndices) { @@ -492,32 +603,58 @@ TEST_F(utglTF2ImportExport, sceneMetadata) { } TEST_F(utglTF2ImportExport, texcoords) { + Assimp::Importer importer; - const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", - aiProcess_ValidateDataStructure); + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", aiProcess_ValidateDataStructure); + ASSERT_NE(scene, nullptr); + ASSERT_TRUE(scene->HasMaterials()); + const aiMaterial *material = scene->mMaterials[0]; + + aiString path; + unsigned int uvIndex = 255; + aiTextureMapMode modes[2]; + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_BASE_COLOR_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); + EXPECT_STREQ(path.C_Str(), "texture.png"); + EXPECT_EQ(uvIndex, 0u); + + uvIndex = 255; + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); + EXPECT_STREQ(path.C_Str(), "texture.png"); + EXPECT_EQ(uvIndex, 1u); +} + +#ifndef ASSIMP_BUILD_NO_EXPORT + +TEST_F(utglTF2ImportExport, texcoords_export) { + { + Assimp::Importer importer; + Assimp::Exporter exporter; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", aiProcess_ValidateDataStructure); + ASSERT_NE(scene, nullptr); + ASSERT_EQ(aiReturn_SUCCESS, exporter.Export(scene, "glb2", ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf_out.glb")); + } + + Assimp::Importer importer; + const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/BoxTexcoords-glTF/boxTexcoords.gltf", aiProcess_ValidateDataStructure); ASSERT_NE(scene, nullptr); ASSERT_TRUE(scene->HasMaterials()); const aiMaterial *material = scene->mMaterials[0]; aiString path; + unsigned int uvIndex = 255; aiTextureMapMode modes[2]; - EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(aiTextureType_DIFFUSE, 0, &path, nullptr, nullptr, - nullptr, nullptr, modes)); + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_BASE_COLOR_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); EXPECT_STREQ(path.C_Str(), "texture.png"); + EXPECT_EQ(uvIndex, 0u); - int uvIndex = -1; - EXPECT_EQ(aiGetMaterialInteger(material, AI_MATKEY_GLTF_TEXTURE_TEXCOORD(aiTextureType_DIFFUSE, 0), &uvIndex), aiReturn_SUCCESS); - EXPECT_EQ(uvIndex, 0); - - // Using manual macro expansion of AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE here. - // The following works with some but not all compilers: - // #define APPLY(X, Y) X(Y) - // ..., APPLY(AI_MATKEY_GLTF_TEXTURE_TEXCOORD, AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE), ... - EXPECT_EQ(aiGetMaterialInteger(material, AI_MATKEY_GLTF_TEXTURE_TEXCOORD(aiTextureType_UNKNOWN, 0), &uvIndex), aiReturn_SUCCESS); - EXPECT_EQ(uvIndex, 1); + uvIndex = 255; + EXPECT_EQ(aiReturn_SUCCESS, material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &path, nullptr, &uvIndex, nullptr, nullptr, modes)); + EXPECT_STREQ(path.C_Str(), "texture.png"); + EXPECT_EQ(uvIndex, 1u); } +#endif // ASSIMP_BUILD_NO_EXPORT TEST_F(utglTF2ImportExport, recursive_nodes) { Assimp::Importer importer; const aiScene* scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/RecursiveNodes/RecursiveNodes.gltf", aiProcess_ValidateDataStructure); @@ -555,7 +692,7 @@ TEST_F(utglTF2ImportExport, indexOutOfRange) { } }; LogObserver logObserver; - + DefaultLogger::get()->attachStream(&logObserver); const aiScene *scene = importer.ReadFile(ASSIMP_TEST_MODELS_DIR "/glTF2/IndexOutOfRange/IndexOutOfRange.gltf", aiProcess_ValidateDataStructure); ASSERT_NE(scene, nullptr); @@ -611,7 +748,8 @@ TEST_F(utglTF2ImportExport, import_dracoEncoded) { TEST_F(utglTF2ImportExport, wrongTypes) { // Deliberately broken version of the BoxTextured.gltf asset. - std::vector> wrongTypes = { + using tup_T = std::tuple; + std::vector wrongTypes = { { "/glTF2/wrongTypes/badArray.gltf", "array", "primitives", "meshes[0]" }, { "/glTF2/wrongTypes/badString.gltf", "string", "name", "scenes[0]" }, { "/glTF2/wrongTypes/badUint.gltf", "uint", "index", "materials[0]" }, diff --git a/tools/assimp_cmd/CMakeLists.txt b/tools/assimp_cmd/CMakeLists.txt index 3a39fa748..5aeac0f7b 100644 --- a/tools/assimp_cmd/CMakeLists.txt +++ b/tools/assimp_cmd/CMakeLists.txt @@ -1,6 +1,6 @@ # Open Asset Import Library (assimp) # ---------------------------------------------------------------------- -# +# # Copyright (c) 2006-2021, assimp team diff --git a/tools/assimp_cmd/Export.cpp b/tools/assimp_cmd/Export.cpp index 1e2f10541..6c3c41de9 100644 --- a/tools/assimp_cmd/Export.cpp +++ b/tools/assimp_cmd/Export.cpp @@ -7,8 +7,8 @@ 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 +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 @@ -25,16 +25,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ diff --git a/tools/assimp_cmd/ImageExtractor.cpp b/tools/assimp_cmd/ImageExtractor.cpp index 105c4fe37..23aa9c249 100644 --- a/tools/assimp_cmd/ImageExtractor.cpp +++ b/tools/assimp_cmd/ImageExtractor.cpp @@ -7,8 +7,8 @@ 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 +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 @@ -25,16 +25,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ @@ -356,6 +356,6 @@ int Assimp_Extract(const char *const *params, unsigned int num) { return m; } } - + return AssimpCmdError::Success; } diff --git a/tools/assimp_cmd/Info.cpp b/tools/assimp_cmd/Info.cpp index e0d511a73..2c35ba227 100644 --- a/tools/assimp_cmd/Info.cpp +++ b/tools/assimp_cmd/Info.cpp @@ -316,7 +316,7 @@ int Assimp_Info (const char* const* params, unsigned int num) { printf("assimp info: Invalid arguments, verbose and silent at the same time are forbitten. "); return AssimpCmdInfoError::InvalidCombinaisonOfArguments; } - + // Parse post-processing flags unless -r was specified ImportData import; if (!raw) { diff --git a/tools/assimp_cmd/Main.cpp b/tools/assimp_cmd/Main.cpp index 2fb7559bb..8d76e1f5e 100644 --- a/tools/assimp_cmd/Main.cpp +++ b/tools/assimp_cmd/Main.cpp @@ -9,8 +9,8 @@ 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 +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 @@ -27,16 +27,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ @@ -47,7 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "Main.h" -const char* AICMD_MSG_ABOUT = +const char* AICMD_MSG_ABOUT = "------------------------------------------------------ \n" "Open Asset Import Library (\"Assimp\", https://github.com/assimp/assimp) \n" " -- Commandline toolchain --\n" @@ -55,7 +55,7 @@ const char* AICMD_MSG_ABOUT = "Version %i.%i %s%s%s%s%s(GIT commit %x)\n\n"; -const char* AICMD_MSG_HELP = +const char* AICMD_MSG_HELP = "assimp \n\n" " verbs:\n" " \tinfo - Quick file stats\n" @@ -106,7 +106,7 @@ int main (int argc, char* argv[]) } // assimp help - // Display some basic help (--help and -h work as well + // Display some basic help (--help and -h work as well // because people could try them intuitively) if (!strcmp(argv[1], "help") || !strcmp(argv[1], "--help") || !strcmp(argv[1], "-h")) { printf("%s",AICMD_MSG_HELP); @@ -114,7 +114,7 @@ int main (int argc, char* argv[]) } // assimp cmpdump - // Compare two mini model dumps (regression suite) + // Compare two mini model dumps (regression suite) if (! strcmp(argv[1], "cmpdump")) { return Assimp_CompareDump (&argv[2],argc-2); } @@ -125,7 +125,7 @@ int main (int argc, char* argv[]) globalImporter = &imp; #ifndef ASSIMP_BUILD_NO_EXPORT - // + // Assimp::Exporter exp; globalExporter = &exp; #endif @@ -145,7 +145,7 @@ int main (int argc, char* argv[]) // List all export file formats supported by Assimp (not the file extensions, just the format identifiers!) if (! strcmp(argv[1], "listexport")) { aiString s; - + for(size_t i = 0, end = exp.GetExportFormatCount(); i < end; ++i) { const aiExportFormatDesc* const e = exp.GetExportFormatDescription(i); s.Append( e->id ); @@ -176,7 +176,7 @@ int main (int argc, char* argv[]) return AssimpCmdError::Success; } } - + printf("Unknown file format id: \'%s\'\n",argv[2]); return AssimpCmdError::UnknownFileFormat; } @@ -207,13 +207,13 @@ int main (int argc, char* argv[]) return Assimp_Info ((const char**)&argv[2],argc-2); } - // assimp dump - // Dump a model to a file + // assimp dump + // Dump a model to a file if (! strcmp(argv[1], "dump")) { return Assimp_Dump (&argv[2],argc-2); } - // assimp extract + // assimp extract // Extract an embedded texture from a file if (! strcmp(argv[1], "extract")) { return Assimp_Extract (&argv[2],argc-2); @@ -236,7 +236,7 @@ int main (int argc, char* argv[]) void SetLogStreams(const ImportData& imp) { printf("\nAttaching log stream ... OK\n"); - + unsigned int flags = 0; if (imp.logFile.length()) { flags |= aiDefaultLogStream_FILE; @@ -264,7 +264,7 @@ void PrintHorBar() // ------------------------------------------------------------------------------ // Import a specific file const aiScene* ImportModel( - const ImportData& imp, + const ImportData& imp, const std::string& path) { // Attach log streams @@ -282,7 +282,7 @@ const aiScene* ImportModel( if (imp.showLog) { PrintHorBar(); } - + // do the actual import, measure time const clock_t first = clock(); @@ -302,7 +302,7 @@ const aiScene* ImportModel( printf("Importing file ... OK \n import took approx. %.5f seconds\n" "\n",seconds); - if (imp.log) { + if (imp.log) { FreeLogStreams(); } return scene; @@ -310,8 +310,8 @@ const aiScene* ImportModel( #ifndef ASSIMP_BUILD_NO_EXPORT // ------------------------------------------------------------------------------ -bool ExportModel(const aiScene* pOut, - const ImportData& imp, +bool ExportModel(const aiScene* pOut, + const ImportData& imp, const std::string& path, const char* pID) { @@ -352,7 +352,7 @@ bool ExportModel(const aiScene* pOut, printf("Exporting file ... OK \n export took approx. %.5f seconds\n" "\n",seconds); - if (imp.log) { + if (imp.log) { FreeLogStreams(); } @@ -363,7 +363,7 @@ bool ExportModel(const aiScene* pOut, // ------------------------------------------------------------------------------ // Process standard arguments int ProcessStandardArguments( - ImportData& fill, + ImportData& fill, const char* const * params, unsigned int num) { @@ -396,7 +396,7 @@ int ProcessStandardArguments( // // -c --config-file= - for (unsigned int i = 0; i < num;++i) + for (unsigned int i = 0; i < num;++i) { const char *param = params[ i ]; printf( "param = %s\n", param ); @@ -504,11 +504,11 @@ int ProcessStandardArguments( else if (!strncmp(params[i], "-rx=", 4) || !strncmp(params[i], "--rotation-x=", 13)) { std::string value = std::string(params[i] + (params[i][1] == '-' ? 13 : 4)); fill.rot.x = std::stof(value); - } + } else if (!strncmp(params[i], "-ry=", 4) || !strncmp(params[i], "--rotation-y=", 13)) { std::string value = std::string(params[i] + (params[i][1] == '-' ? 13 : 4)); fill.rot.y = std::stof(value); - } + } else if (!strncmp(params[i], "-rz=", 4) || !strncmp(params[i], "--rotation-z=", 13)) { std::string value = std::string(params[i] + (params[i][1] == '-' ? 13 : 4)); fill.rot.z = std::stof(value); @@ -530,7 +530,7 @@ int ProcessStandardArguments( // ------------------------------------------------------------------------------ int Assimp_TestBatchLoad ( - const char* const* params, + const char* const* params, unsigned int num) { for(unsigned int i = 0; i < num; ++i) { diff --git a/tools/assimp_cmd/Main.h b/tools/assimp_cmd/Main.h index e7fbb6c75..5ac306abd 100644 --- a/tools/assimp_cmd/Main.h +++ b/tools/assimp_cmd/Main.h @@ -7,8 +7,8 @@ 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 +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 @@ -25,16 +25,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ @@ -144,7 +144,7 @@ enum AssimpCmdError { * @param params Command line parameters to be processed * @param num NUmber of params * @return An #AssimpCmdError value. */ -int ProcessStandardArguments(ImportData& fill, +int ProcessStandardArguments(ImportData& fill, const char* const* params, unsigned int num); @@ -153,7 +153,7 @@ int ProcessStandardArguments(ImportData& fill, * @param imp Import configuration to be used * @param path Path to the file to be read */ const aiScene* ImportModel( - const ImportData& imp, + const ImportData& imp, const std::string& path); #ifndef ASSIMP_BUILD_NO_EXPORT @@ -163,8 +163,8 @@ const aiScene* ImportModel( * @param imp Import configuration to be used * @param path Path to the file to be written * @param format Format id*/ -bool ExportModel(const aiScene* pOut, - const ImportData& imp, +bool ExportModel(const aiScene* pOut, + const ImportData& imp, const std::string& path, const char* pID); @@ -176,7 +176,7 @@ bool ExportModel(const aiScene* pOut, * @param Number of params * @return An #AssimpCmdError value.*/ int Assimp_Dump ( - const char* const* params, + const char* const* params, unsigned int num); /// \enum AssimpCmdExportError @@ -186,7 +186,7 @@ enum AssimpCmdExportError { FailedToExportModel, // Add new error codes here... - + LastAssimpCmdExportError, // Must be last. }; @@ -196,7 +196,7 @@ enum AssimpCmdExportError { * @param Number of params * @return Either an #AssimpCmdError or #AssimpCmdExportError value. */ int Assimp_Export ( - const char* const* params, + const char* const* params, unsigned int num); /// \enum AssimpCmdExtractError @@ -217,7 +217,7 @@ enum AssimpCmdExtractError { * @param Number of params * @return Either an #AssimpCmdError or #AssimpCmdExtractError value. */ int Assimp_Extract ( - const char* const* params, + const char* const* params, unsigned int num); /// \enum AssimpCmdCompareDumpError @@ -238,7 +238,7 @@ enum AssimpCmdCompareDumpError { * @param Number of params * @return Either an #AssimpCmdError or #AssimpCmdCompareDumpError. */ int Assimp_CompareDump ( - const char* const* params, + const char* const* params, unsigned int num); /// \enum AssimpCmdInfoError @@ -257,7 +257,7 @@ enum AssimpCmdInfoError { * @param Number of params * @return Either an #AssimpCmdError or #AssimpCmdInfoError value. */ int Assimp_Info ( - const char* const* params, + const char* const* params, unsigned int num); // ------------------------------------------------------------------------------ @@ -266,7 +266,7 @@ int Assimp_Info ( * @param Number of params * @return An #AssimpCmdError value. */ int Assimp_TestBatchLoad ( - const char* const* params, + const char* const* params, unsigned int num); diff --git a/tools/assimp_cmd/WriteDump.cpp b/tools/assimp_cmd/WriteDump.cpp index 5809d4ce6..fd8839a17 100644 --- a/tools/assimp_cmd/WriteDump.cpp +++ b/tools/assimp_cmd/WriteDump.cpp @@ -7,8 +7,8 @@ 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 +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 @@ -25,16 +25,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ diff --git a/tools/assimp_cmd/resource.h b/tools/assimp_cmd/resource.h index c516b5e5c..caf3a0a69 100644 --- a/tools/assimp_cmd/resource.h +++ b/tools/assimp_cmd/resource.h @@ -9,7 +9,7 @@ // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 diff --git a/tools/assimp_view/AnimEvaluator.cpp b/tools/assimp_view/AnimEvaluator.cpp index df5167923..5a2ddc182 100644 --- a/tools/assimp_view/AnimEvaluator.cpp +++ b/tools/assimp_view/AnimEvaluator.cpp @@ -39,9 +39,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -#include "assimp_view.h" +#include "AnimEvaluator.h" -#include +#include +#include using namespace AssimpView; diff --git a/tools/assimp_view/AnimEvaluator.h b/tools/assimp_view/AnimEvaluator.h index 950763081..394ebef4a 100644 --- a/tools/assimp_view/AnimEvaluator.h +++ b/tools/assimp_view/AnimEvaluator.h @@ -1,4 +1,3 @@ -/** Calculates a pose for a given time of an animation */ /* --------------------------------------------------------------------------- Open Asset Import Library (assimp) @@ -40,15 +39,21 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ +#pragma once #ifndef AV_ANIMEVALUATOR_H_INCLUDED #define AV_ANIMEVALUATOR_H_INCLUDED +/** Calculates a pose for a given time of an animation */ + #include #include +#include + +struct aiAnimation; namespace AssimpView { -/** +/** * @brief Calculates transformations for a given timestamp from a set of animation tracks. Not directly useful, * better use the AnimPlayer class. */ @@ -63,18 +68,19 @@ public: /// @brief The class destructor. ~AnimEvaluator(); - /** Evaluates the animation tracks for a given time stamp. The calculated pose can be retrieved as a - * array of transformation matrices afterwards by calling GetTransformations(). - * @param pTime The time for which you want to evaluate the animation, in seconds. Will be mapped into the animation cycle, so - * it can be an arbitrary value. Best use with ever-increasing time stamps. - */ + /// @brief Evaluates the animation tracks for a given time stamp. + /// The calculated pose can be retrieved as an array of transformation + /// matrices afterwards by calling GetTransformations(). + /// @param pTime The time for which you want to evaluate the animation, in seconds. + /// Will be mapped into the animation cycle, so it can get an arbitrary + /// value. Best use with ever-increasing time stamps. void Evaluate(double pTime); - /** Returns the transform matrices calculated at the last Evaluate() call. The array matches the mChannels array of - * the aiAnimation. */ + /// @brief Returns the transform matrices calculated at the last Evaluate() call. + /// The array matches the mChannels array of the aiAnimation. const std::vector &GetTransformations() const { return mTransforms; } -protected: +private: const aiAnimation *mAnim; double mLastTime; std::vector> mLastPositions; diff --git a/tools/assimp_view/CMakeLists.txt b/tools/assimp_view/CMakeLists.txt index 8ff556f05..0199392fe 100644 --- a/tools/assimp_view/CMakeLists.txt +++ b/tools/assimp_view/CMakeLists.txt @@ -1,6 +1,6 @@ # Open Asset Import Library (assimp) # ---------------------------------------------------------------------- -# +# # Copyright (c) 2006-2021, assimp team diff --git a/tools/assimp_view/Display.cpp b/tools/assimp_view/Display.cpp index 4178ab955..ac9aa5329 100644 --- a/tools/assimp_view/Display.cpp +++ b/tools/assimp_view/Display.cpp @@ -105,7 +105,7 @@ void GetNodeCount(aiNode* pcNode, unsigned int* piCnt) int CDisplay::EnableAnimTools(BOOL hm) { EnableWindow(GetDlgItem(g_hDlg,IDC_PLAY),hm); EnableWindow(GetDlgItem(g_hDlg,IDC_SLIDERANIM),hm); - + return 1; } @@ -171,7 +171,7 @@ int CDisplay::AddNodeToDisplayList( { iIndex += iDepth * 100; } - else + else iIndex += iDepth * 10; ai_snprintf(chTemp, MAXLEN,"Node %u",iIndex); } @@ -1053,7 +1053,7 @@ int CDisplay::OnSetupTextureView(TextureInfo* pcNew) case aiTextureOp_SmoothAdd: szOp = "addsmooth"; break; - default: + default: szOp = "mul"; break; }; diff --git a/tools/assimp_view/Material.cpp b/tools/assimp_view/Material.cpp index bcc93011e..100074445 100644 --- a/tools/assimp_view/Material.cpp +++ b/tools/assimp_view/Material.cpp @@ -325,9 +325,10 @@ int CMaterialManager::FindValidPath(aiString* p_szString) // first check whether we can directly load the file FILE* pFile = fopen(p_szString->data,"rb"); - if (pFile)fclose(pFile); - else - { + if (pFile) { + fclose(pFile); + } + else { // check whether we can use the directory of the asset as relative base char szTemp[MAX_PATH*2], tmp2[MAX_PATH*2]; strcpy(szTemp, g_szFileName); diff --git a/tools/assimp_view/MaterialManager.h b/tools/assimp_view/MaterialManager.h index 77ca4224f..9aeb5e992 100644 --- a/tools/assimp_view/MaterialManager.h +++ b/tools/assimp_view/MaterialManager.h @@ -52,24 +52,11 @@ namespace AssimpView { */ //------------------------------------------------------------------------------- class CMaterialManager { -private: friend class CDisplay; - // default constructor - CMaterialManager() : - m_iShaderCount(0), sDefaultTexture() {} - - ~CMaterialManager() { - if (sDefaultTexture) { - sDefaultTexture->Release(); - } - Reset(); - } - public: //------------------------------------------------------------------ // Singleton accessors - static CMaterialManager s_cInstance; inline static CMaterialManager &Instance() { return s_cInstance; } @@ -80,24 +67,20 @@ public: // Must be called before CreateMaterial() to prevent memory leaking void DeleteMaterial(AssetHelper::MeshHelper *pcIn); - //------------------------------------------------------------------ - // Create the material for a mesh. - // - // The function checks whether an identical shader is already in use. - // A shader is considered to be identical if it has the same input - // signature and takes the same number of texture channels. - int CreateMaterial(AssetHelper::MeshHelper *pcMesh, - const aiMesh *pcSource); - - //------------------------------------------------------------------ - // Setup the material for a given mesh - // pcMesh Mesh to be rendered - // pcProj Projection matrix - // aiMe Current world matrix - // pcCam Camera matrix - // vPos Position of the camera - // TODO: Extract camera position from matrix ... - // + /// @brief Create the material for a mesh. + /// + /// The function checks whether an identical shader is already in use. + /// A shader is considered to be identical if it has the same input + /// signature and takes the same number of texture channels. + int CreateMaterial(AssetHelper::MeshHelper *pcMesh, const aiMesh *pcSource); + + /// @brief Setup the material for a given mesh. + /// @param pcMesh Mesh to be rendered + /// @param pcProj Projection matrix + /// @param aiMe Current world matrix + /// @param pcCam Camera matrix + /// @param vPos Position of the camera + /// @return 0 if successful. int SetupMaterial(AssetHelper::MeshHelper *pcMesh, const aiMatrix4x4 &pcProj, const aiMatrix4x4 &aiMe, @@ -143,14 +126,29 @@ public: // Reset the state of the class // Called whenever a new asset is loaded inline void Reset() { - this->m_iShaderCount = 0; - for (TextureCache::iterator it = sCachedTextures.begin(); it != sCachedTextures.end(); ++it) { - (*it).second->Release(); + m_iShaderCount = 0; + for (auto & sCachedTexture : sCachedTextures) { + sCachedTexture.second->Release(); } sCachedTextures.clear(); } private: + // The default constructor + CMaterialManager() : + m_iShaderCount(0), + sDefaultTexture() { + // empty + } + + // Destructor, private. + ~CMaterialManager() { + if (sDefaultTexture) { + sDefaultTexture->Release(); + } + Reset(); + } + //------------------------------------------------------------------ // find a valid path to a texture file // @@ -183,15 +181,14 @@ private: bool HasAlphaPixels(IDirect3DTexture9 *piTexture); private: - // + static CMaterialManager s_cInstance; + // Specifies the number of different shaders generated for // the current asset. This number is incremented by CreateMaterial() // each time a shader isn't found in cache and needs to be created - // unsigned int m_iShaderCount; IDirect3DTexture9 *sDefaultTexture; - - typedef std::map TextureCache; + using TextureCache = std::map; TextureCache sCachedTextures; }; diff --git a/tools/assimp_view/MeshRenderer.cpp b/tools/assimp_view/MeshRenderer.cpp index 0ec12e5b1..bc1a5236f 100644 --- a/tools/assimp_view/MeshRenderer.cpp +++ b/tools/assimp_view/MeshRenderer.cpp @@ -61,11 +61,14 @@ int CMeshRenderer::DrawUnsorted(unsigned int iIndex) { D3DPRIMITIVETYPE type = D3DPT_POINTLIST; switch (g_pcAsset->pcScene->mMeshes[iIndex]->mPrimitiveTypes) { case aiPrimitiveType_POINT: - type = D3DPT_POINTLIST;break; + type = D3DPT_POINTLIST; + break; case aiPrimitiveType_LINE: - type = D3DPT_LINELIST;break; + type = D3DPT_LINELIST; + break; case aiPrimitiveType_TRIANGLE: - type = D3DPT_TRIANGLELIST;break; + type = D3DPT_TRIANGLELIST; + break; } // and draw the mesh g_piDevice->DrawIndexedPrimitive(type, diff --git a/tools/assimp_view/MessageProc.cpp b/tools/assimp_view/MessageProc.cpp index 580d35bf1..56831eb2a 100644 --- a/tools/assimp_view/MessageProc.cpp +++ b/tools/assimp_view/MessageProc.cpp @@ -2206,7 +2206,7 @@ int APIENTRY _tWinMain(HINSTANCE hInstance, "ASSIMP ModelViewer",MB_OK); return -4; } - + CLogDisplay::Instance().AddEntry("[OK] Here we go!"); // create the log window diff --git a/tools/assimp_view/Shaders.cpp b/tools/assimp_view/Shaders.cpp index a3404d5bf..b8ee8dbf8 100644 --- a/tools/assimp_view/Shaders.cpp +++ b/tools/assimp_view/Shaders.cpp @@ -7,8 +7,8 @@ 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 +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 @@ -25,16 +25,16 @@ conditions are met: 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ diff --git a/tools/assimp_view/assimp_view.cpp b/tools/assimp_view/assimp_view.cpp index 260b22941..e780e2aaf 100644 --- a/tools/assimp_view/assimp_view.cpp +++ b/tools/assimp_view/assimp_view.cpp @@ -7,8 +7,8 @@ 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 +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 @@ -25,16 +25,16 @@ 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 +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 +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 +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 +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. --------------------------------------------------------------------------- */ @@ -489,7 +489,7 @@ int CreateAssetData() { nidx = 3; break; default: - ai_assert(false); + CLogWindow::Instance().WriteLine("Unknown primitiv type"); break; }; @@ -500,8 +500,7 @@ int CreateAssetData() { // check whether we can use 16 bit indices if (numIndices >= 65536) { // create 32 bit index buffer - if (FAILED(g_piDevice->CreateIndexBuffer(4 * - numIndices, + if (FAILED(g_piDevice->CreateIndexBuffer(4 * numIndices, D3DUSAGE_WRITEONLY | dwUsage, D3DFMT_INDEX32, D3DPOOL_DEFAULT, @@ -523,7 +522,7 @@ int CreateAssetData() { } else { // create 16 bit index buffer if (FAILED(g_piDevice->CreateIndexBuffer(2 * - numIndices, +numIndices, D3DUSAGE_WRITEONLY | dwUsage, D3DFMT_INDEX16, D3DPOOL_DEFAULT, diff --git a/tools/assimp_view/resource.h b/tools/assimp_view/resource.h index 5077f6ccf..754eb69bd 100644 --- a/tools/assimp_view/resource.h +++ b/tools/assimp_view/resource.h @@ -223,7 +223,7 @@ #define IDC_STATIC -1 // Next default values for new objects -// +// #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 From dfd95633a4dfc1fff397ed30a4ffda5edf97418f Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 8 Sep 2021 08:31:33 +0200 Subject: [PATCH 261/335] Add patreon --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index e1ab13396..3f820ec7d 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1,2 @@ open_collective: assimp +patreon: assimp From f47479aba4797fc9c9b119cd21ab427748a9f839 Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Wed, 8 Sep 2021 23:18:13 +0200 Subject: [PATCH 262/335] Rework format + introdule missing C++11 features --- code/Common/Bitmap.cpp | 57 ++++++++++++------------ include/assimp/Bitmap.h | 14 ++++-- include/assimp/BlobIOSystem.h | 75 +++++++++++++++++--------------- include/assimp/ByteSwapper.h | 4 +- include/assimp/ColladaMetaData.h | 1 - include/assimp/CreateAnimMesh.h | 1 - include/assimp/DefaultIOStream.h | 37 +++++++--------- include/assimp/DefaultIOSystem.h | 11 +++-- include/assimp/DefaultLogger.hpp | 34 ++++++++------- include/assimp/Exceptional.h | 30 +++++++++++-- include/assimp/IOSystem.hpp | 54 ++++++++++------------- include/assimp/Importer.hpp | 2 +- include/assimp/LineSplitter.h | 56 +++++++++--------------- include/assimp/LogStream.hpp | 9 ++-- include/assimp/Logger.hpp | 27 ++++-------- include/assimp/NullLogger.hpp | 5 +++ include/assimp/aabb.h | 24 +++++----- include/assimp/ai_assert.h | 26 +++++++---- include/assimp/camera.h | 34 +++++++-------- include/assimp/cfileio.h | 5 +-- include/assimp/color4.inl | 2 - include/assimp/commonMetaData.h | 2 - 22 files changed, 258 insertions(+), 252 deletions(-) diff --git a/code/Common/Bitmap.cpp b/code/Common/Bitmap.cpp index 23b96d42d..51f31625e 100644 --- a/code/Common/Bitmap.cpp +++ b/code/Common/Bitmap.cpp @@ -5,8 +5,6 @@ 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, @@ -54,33 +52,36 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { -void Bitmap::Save(aiTexture *texture, IOStream *file) { - if (file != nullptr) { - Header header; - DIB dib; - - dib.size = DIB::dib_size; - dib.width = texture->mWidth; - dib.height = texture->mHeight; - dib.planes = 1; - dib.bits_per_pixel = 8 * mBytesPerPixel; - dib.compression = 0; - dib.image_size = (((dib.width * mBytesPerPixel) + 3) & 0x0000FFFC) * dib.height; - dib.x_resolution = 0; - dib.y_resolution = 0; - dib.nb_colors = 0; - dib.nb_important_colors = 0; - - header.type = 0x4D42; // 'BM' - header.offset = Header::header_size + DIB::dib_size; - header.size = header.offset + dib.image_size; - header.reserved1 = 0; - header.reserved2 = 0; - - WriteHeader(header, file); - WriteDIB(dib, file); - WriteData(texture, file); +bool Bitmap::Save(aiTexture *texture, IOStream *file) { + if (file == nullptr) { + return false; } + + Header header; + DIB dib; + dib.size = DIB::dib_size; + dib.width = texture->mWidth; + dib.height = texture->mHeight; + dib.planes = 1; + dib.bits_per_pixel = 8 * mBytesPerPixel; + dib.compression = 0; + dib.image_size = (((dib.width * mBytesPerPixel) + 3) & 0x0000FFFC) * dib.height; + dib.x_resolution = 0; + dib.y_resolution = 0; + dib.nb_colors = 0; + dib.nb_important_colors = 0; + + header.type = 0x4D42; // 'BM' + header.offset = Header::header_size + DIB::dib_size; + header.size = header.offset + dib.image_size; + header.reserved1 = 0; + header.reserved2 = 0; + + WriteHeader(header, file); + WriteDIB(dib, file); + WriteData(texture, file); + + return true; } template diff --git a/include/assimp/Bitmap.h b/include/assimp/Bitmap.h index e4ce194d9..228f6e9ba 100644 --- a/include/assimp/Bitmap.h +++ b/include/assimp/Bitmap.h @@ -5,8 +5,6 @@ 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, @@ -55,7 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endif #include "defs.h" -#include +#include #include struct aiTexture; @@ -64,6 +62,10 @@ namespace Assimp { class IOStream; +// --------------------------------------------------------------------------- +/** + * This class is used to store and write bitmap information. + */ class ASSIMP_API Bitmap { protected: @@ -114,7 +116,11 @@ protected: static constexpr std::size_t mBytesPerPixel = 4; public: - static void Save(aiTexture* texture, IOStream* file); + /// @brief Will save an aiTexture instance as a bitmap. + /// @param texture The pointer to the texture instance + /// @param file The filename to save into. + /// @return true if successfully saved, false if not. + static bool Save(aiTexture* texture, IOStream* file); protected: static void WriteHeader(Header& header, IOStream* file); diff --git a/include/assimp/BlobIOSystem.h b/include/assimp/BlobIOSystem.h index 0abf166bc..90e806431 100644 --- a/include/assimp/BlobIOSystem.h +++ b/include/assimp/BlobIOSystem.h @@ -5,8 +5,6 @@ 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, @@ -44,14 +42,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** @file Provides cheat implementations for IOSystem and IOStream to * redirect exporter output to a blob chain.*/ +#pragma once #ifndef AI_BLOBIOSYSTEM_H_INCLUDED #define AI_BLOBIOSYSTEM_H_INCLUDED #include -#include #include #include #include +#include #include #include @@ -63,6 +62,10 @@ class BlobIOSystem; // -------------------------------------------------------------------------------------------- class BlobIOStream : public IOStream { public: + /// @brief The class constructor with all needed parameters + /// @param creator Pointer to the creator instance + /// @param file The filename + /// @param initial The initial size BlobIOStream(BlobIOSystem *creator, const std::string &file, size_t initial = 4096) : buffer(), cur_size(), @@ -74,7 +77,8 @@ public: // empty } - virtual ~BlobIOStream(); + /// @brief The class destructor. + ~BlobIOStream() override; public: // ------------------------------------------------------------------- @@ -89,16 +93,12 @@ public: } // ------------------------------------------------------------------- - virtual size_t Read(void *, - size_t, - size_t) { + size_t Read(void *, size_t, size_t) override { return 0; } // ------------------------------------------------------------------- - virtual size_t Write(const void *pvBuffer, - size_t pSize, - size_t pCount) { + size_t Write(const void *pvBuffer, size_t pSize, size_t pCount) override { pSize *= pCount; if (cursor + pSize > cur_size) { Grow(cursor + pSize); @@ -112,23 +112,22 @@ public: } // ------------------------------------------------------------------- - virtual aiReturn Seek(size_t pOffset, - aiOrigin pOrigin) { + aiReturn Seek(size_t pOffset, aiOrigin pOrigin) override { switch (pOrigin) { - case aiOrigin_CUR: - cursor += pOffset; - break; + case aiOrigin_CUR: + cursor += pOffset; + break; - case aiOrigin_END: - cursor = file_size - pOffset; - break; + case aiOrigin_END: + cursor = file_size - pOffset; + break; - case aiOrigin_SET: - cursor = pOffset; - break; + case aiOrigin_SET: + cursor = pOffset; + break; - default: - return AI_FAILURE; + default: + return AI_FAILURE; } if (cursor > file_size) { @@ -136,21 +135,22 @@ public: } file_size = std::max(cursor, file_size); + return AI_SUCCESS; } // ------------------------------------------------------------------- - virtual size_t Tell() const { + size_t Tell() const override { return cursor; } // ------------------------------------------------------------------- - virtual size_t FileSize() const { + size_t FileSize() const override { return file_size; } // ------------------------------------------------------------------- - virtual void Flush() { + void Flush() override { // ignore } @@ -196,15 +196,19 @@ class BlobIOSystem : public IOSystem { public: + /// @brief The default class constructor. BlobIOSystem() : baseName{AI_BLOBIO_MAGIC} { } + /// @brief The class constructor with the base name. + /// @param baseName The base name. BlobIOSystem(const std::string &baseName) : baseName(baseName) { + // empty } - virtual ~BlobIOSystem() { + ~BlobIOSystem() override { for (BlobEntry &blobby : blobs) { delete blobby.second; } @@ -263,18 +267,17 @@ public: public: // ------------------------------------------------------------------- - virtual bool Exists(const char *pFile) const { + bool Exists(const char *pFile) const override { return created.find(std::string(pFile)) != created.end(); } // ------------------------------------------------------------------- - virtual char getOsSeparator() const { + char getOsSeparator() const override { return '/'; } // ------------------------------------------------------------------- - virtual IOStream *Open(const char *pFile, - const char *pMode) { + IOStream *Open(const char *pFile, const char *pMode) override { if (pMode[0] != 'w') { return nullptr; } @@ -284,7 +287,7 @@ public: } // ------------------------------------------------------------------- - virtual void Close(IOStream *pFile) { + void Close(IOStream *pFile) override { delete pFile; } @@ -294,7 +297,7 @@ private: // we don't know in which the files are closed, so we // can't reliably say that the first must be the master // file ... - blobs.push_back(BlobEntry(filename, child->GetBlob())); + blobs.emplace_back(filename, child->GetBlob()); } private: @@ -304,8 +307,10 @@ private: }; // -------------------------------------------------------------------------------------------- -BlobIOStream ::~BlobIOStream() { - creator->OnDestruct(file, this); +BlobIOStream::~BlobIOStream() { + if (nullptr != creator) { + creator->OnDestruct(file, this); + } delete[] buffer; } diff --git a/include/assimp/ByteSwapper.h b/include/assimp/ByteSwapper.h index 98fdafc98..9e1eea10f 100644 --- a/include/assimp/ByteSwapper.h +++ b/include/assimp/ByteSwapper.h @@ -52,10 +52,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -#include +#include #if _MSC_VER >= 1400 -#include +#include #endif namespace Assimp { diff --git a/include/assimp/ColladaMetaData.h b/include/assimp/ColladaMetaData.h index 602f342f9..f8fd5e6b4 100644 --- a/include/assimp/ColladaMetaData.h +++ b/include/assimp/ColladaMetaData.h @@ -4,7 +4,6 @@ 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, diff --git a/include/assimp/CreateAnimMesh.h b/include/assimp/CreateAnimMesh.h index dda4dc63a..868a1f399 100644 --- a/include/assimp/CreateAnimMesh.h +++ b/include/assimp/CreateAnimMesh.h @@ -4,7 +4,6 @@ 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, diff --git a/include/assimp/DefaultIOStream.h b/include/assimp/DefaultIOStream.h index 6d0e13149..aae80a2a4 100644 --- a/include/assimp/DefaultIOStream.h +++ b/include/assimp/DefaultIOStream.h @@ -4,7 +4,6 @@ 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, @@ -82,32 +81,27 @@ public: // ------------------------------------------------------------------- /// Read from stream - size_t Read(void* pvBuffer, - size_t pSize, - size_t pCount); + size_t Read(void* pvBuffer, size_t pSize, size_t pCount) override; // ------------------------------------------------------------------- /// Write to stream - size_t Write(const void* pvBuffer, - size_t pSize, - size_t pCount); + size_t Write(const void* pvBuffer, size_t pSize, size_t pCount) override; // ------------------------------------------------------------------- /// Seek specific position - aiReturn Seek(size_t pOffset, - aiOrigin pOrigin); + aiReturn Seek(size_t pOffset, aiOrigin pOrigin) override; // ------------------------------------------------------------------- /// Get current seek position - size_t Tell() const; + size_t Tell() const override; // ------------------------------------------------------------------- /// Get size of file - size_t FileSize() const; + size_t FileSize() const override; // ------------------------------------------------------------------- /// Flush file contents - void Flush(); + void Flush() override; private: FILE* mFile; @@ -116,22 +110,21 @@ private: }; // ---------------------------------------------------------------------------------- -AI_FORCE_INLINE -DefaultIOStream::DefaultIOStream() AI_NO_EXCEPT -: mFile(nullptr) -, mFilename() -, mCachedSize(SIZE_MAX) { +AI_FORCE_INLINE DefaultIOStream::DefaultIOStream() AI_NO_EXCEPT : + mFile(nullptr), + mFilename(), + mCachedSize(SIZE_MAX) { // empty } // ---------------------------------------------------------------------------------- -AI_FORCE_INLINE -DefaultIOStream::DefaultIOStream (FILE* pFile, const std::string &strFilename) -: mFile(pFile) -, mFilename(strFilename) -, mCachedSize(SIZE_MAX) { +AI_FORCE_INLINE DefaultIOStream::DefaultIOStream (FILE* pFile, const std::string &strFilename) : + mFile(pFile), + mFilename(strFilename), + mCachedSize(SIZE_MAX) { // empty } + // ---------------------------------------------------------------------------------- } // ns assimp diff --git a/include/assimp/DefaultIOSystem.h b/include/assimp/DefaultIOSystem.h index 3f7235f5b..c2f1aac61 100644 --- a/include/assimp/DefaultIOSystem.h +++ b/include/assimp/DefaultIOSystem.h @@ -4,7 +4,6 @@ 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, @@ -59,23 +58,23 @@ class ASSIMP_API DefaultIOSystem : public IOSystem { public: // ------------------------------------------------------------------- /** Tests for the existence of a file at the given path. */ - bool Exists( const char* pFile) const; + bool Exists( const char* pFile) const override; // ------------------------------------------------------------------- /** Returns the directory separator. */ - char getOsSeparator() const; + char getOsSeparator() const override; // ------------------------------------------------------------------- /** Open a new file with a given path. */ - IOStream* Open( const char* pFile, const char* pMode = "rb"); + IOStream* Open( const char* pFile, const char* pMode = "rb") override; // ------------------------------------------------------------------- /** Closes the given file and releases all resources associated with it. */ - void Close( IOStream* pFile); + void Close( IOStream* pFile) override; // ------------------------------------------------------------------- /** Compare two paths */ - bool ComparePaths (const char* one, const char* second) const; + bool ComparePaths (const char* one, const char* second) const override; /** @brief get the file name of a full filepath * example: /tmp/archive.tar.gz -> archive.tar.gz diff --git a/include/assimp/DefaultLogger.hpp b/include/assimp/DefaultLogger.hpp index f71bb07aa..e06c94f31 100644 --- a/include/assimp/DefaultLogger.hpp +++ b/include/assimp/DefaultLogger.hpp @@ -4,7 +4,6 @@ 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, @@ -42,9 +41,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** @file DefaultLogger.hpp */ +#pragma once #ifndef INCLUDED_AI_DEFAULTLOGGER #define INCLUDED_AI_DEFAULTLOGGER +#ifdef __GNUC__ +#pragma GCC system_header +#endif + #include "LogStream.hpp" #include "Logger.hpp" #include "NullLogger.hpp" @@ -55,7 +59,7 @@ namespace Assimp { class IOStream; struct LogStreamInfo; -/** default name of logfile */ +/** default name of log-file */ #define ASSIMP_DEFAULT_LOG_NAME "AssimpLog.txt" // ------------------------------------------------------------------------------------ @@ -72,7 +76,6 @@ struct LogStreamInfo; * implementation of #Logger to #set(). * @note The whole logging stuff causes a small extra overhead for all imports. */ class ASSIMP_API DefaultLogger : public Logger { - public: // ---------------------------------------------------------------------- /** @brief Creates a logging instance. @@ -121,13 +124,11 @@ public: // ---------------------------------------------------------------------- /** @copydoc Logger::attachStream */ - bool attachStream(LogStream *pStream, - unsigned int severity); + bool attachStream(LogStream *pStream, unsigned int severity) override; // ---------------------------------------------------------------------- /** @copydoc Logger::detachStream */ - bool detachStream(LogStream *pStream, - unsigned int severity); + bool detachStream(LogStream *pStream, unsigned int severity) override; private: // ---------------------------------------------------------------------- @@ -137,22 +138,22 @@ private: // ---------------------------------------------------------------------- /** @briefDestructor */ - ~DefaultLogger(); + ~DefaultLogger() override; /** @brief Logs debug infos, only been written when severity level DEBUG or higher is set */ - void OnDebug(const char *message); + void OnDebug(const char *message) override; /** @brief Logs debug infos, only been written when severity level VERBOSE is set */ - void OnVerboseDebug(const char *message); + void OnVerboseDebug(const char *message) override; /** @brief Logs an info message */ - void OnInfo(const char *message); + void OnInfo(const char *message) override; /** @brief Logs a warning message */ - void OnWarn(const char *message); + void OnWarn(const char *message) override; /** @brief Logs an error message */ - void OnError(const char *message); + void OnError(const char *message) override; // ---------------------------------------------------------------------- /** @brief Writes a message to all streams */ @@ -167,9 +168,9 @@ private: private: // Aliases for stream container - typedef std::vector StreamArray; - typedef std::vector::iterator StreamIt; - typedef std::vector::const_iterator ConstStreamIt; + using StreamArray = std::vector; + using StreamIt = std::vector::iterator; + using ConstStreamIt = std::vector::const_iterator; //! only logging instance static Logger *m_pLogger; @@ -182,6 +183,7 @@ private: char lastMsg[MAX_LOG_MESSAGE_LENGTH * 2]; size_t lastLen; }; + // ------------------------------------------------------------------------------------ } // Namespace Assimp diff --git a/include/assimp/Exceptional.h b/include/assimp/Exceptional.h index 1bf399cbc..3078de9f4 100644 --- a/include/assimp/Exceptional.h +++ b/include/assimp/Exceptional.h @@ -56,10 +56,22 @@ using std::runtime_error; #pragma warning(disable : 4275) #endif +// --------------------------------------------------------------------------- +/** + * The base-class for all other exceptions + */ class ASSIMP_API DeadlyErrorBase : public runtime_error { protected: + /// @brief The class constructor with the formatter. + /// @param f The formatter. DeadlyErrorBase(Assimp::Formatter::format f); + /// @brief The class constructor with the parameter ellipse. + /// @tparam ...T The type for the ellipse + /// @tparam U The other type + /// @param f The formatter + /// @param u One parameter + /// @param ...args The rest template DeadlyErrorBase(Assimp::Formatter::format f, U&& u, T&&... args) : DeadlyErrorBase(std::move(f << std::forward(u)), std::forward(args)...) {} @@ -71,19 +83,31 @@ protected: * nullptr instead of a valid aiScene then. */ class ASSIMP_API DeadlyImportError : public DeadlyErrorBase { public: + /// @brief The class constructor with the message. + /// @param message The message DeadlyImportError(const char *message) : - DeadlyErrorBase(Assimp::Formatter::format(), std::forward(message)) {} + DeadlyErrorBase(Assimp::Formatter::format(), std::forward(message)) { + // empty + } - /** Constructor with arguments */ + /// @brief The class constructor with the parameter ellipse. + /// @tparam ...T The type for the ellipse + /// @param ...args The args template explicit DeadlyImportError(T&&... args) : - DeadlyErrorBase(Assimp::Formatter::format(), std::forward(args)...) {} + DeadlyErrorBase(Assimp::Formatter::format(), std::forward(args)...) { + // empty + } #if defined(_MSC_VER) && defined(__clang__) DeadlyImportError(DeadlyImportError& other) = delete; #endif }; +// --------------------------------------------------------------------------- +/** FOR EXPORTER PLUGINS ONLY: Simple exception class to be thrown if an + * unrecoverable error occurs while exporting. Exporting APIs return + * nullptr instead of a valid aiScene then. */ class ASSIMP_API DeadlyExportError : public DeadlyErrorBase { public: /** Constructor with arguments */ diff --git a/include/assimp/IOSystem.hpp b/include/assimp/IOSystem.hpp index 7be373cf1..dda0718e6 100644 --- a/include/assimp/IOSystem.hpp +++ b/include/assimp/IOSystem.hpp @@ -5,8 +5,6 @@ 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, @@ -63,8 +61,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifdef _WIN32 # include -# include -# include +# include +# include #else # include # include @@ -75,7 +73,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { - class IOStream; +class IOStream; // --------------------------------------------------------------------------- /** @brief CPP-API: Interface to the file system. @@ -226,22 +224,26 @@ public: */ virtual bool ChangeDirectory( const std::string &path ); - virtual bool DeleteFile( const std::string &file ); + // ------------------------------------------------------------------- + /** + * @brief Will delete the given file. + * @param file [in] The filename + * @return true, if the file wase deleted, false if not. + */ + virtual bool DeleteFile(const std::string &file); private: std::vector m_pathStack; }; // ---------------------------------------------------------------------------- -AI_FORCE_INLINE -IOSystem::IOSystem() AI_NO_EXCEPT -: m_pathStack() { +AI_FORCE_INLINE IOSystem::IOSystem() AI_NO_EXCEPT : + m_pathStack() { // empty } // ---------------------------------------------------------------------------- -AI_FORCE_INLINE -IOSystem::~IOSystem() { +AI_FORCE_INLINE IOSystem::~IOSystem() { // empty } @@ -252,8 +254,7 @@ IOSystem::~IOSystem() { // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- -AI_FORCE_INLINE -IOStream* IOSystem::Open(const std::string& pFile, const std::string& pMode) { +AI_FORCE_INLINE IOStream* IOSystem::Open(const std::string& pFile, const std::string& pMode) { // NOTE: // For compatibility, interface was changed to const char* to // avoid crashes between binary incompatible STL versions @@ -261,8 +262,7 @@ IOStream* IOSystem::Open(const std::string& pFile, const std::string& pMode) { } // ---------------------------------------------------------------------------- -AI_FORCE_INLINE -bool IOSystem::Exists( const std::string& pFile) const { +AI_FORCE_INLINE bool IOSystem::Exists( const std::string& pFile) const { // NOTE: // For compatibility, interface was changed to const char* to // avoid crashes between binary incompatible STL versions @@ -270,8 +270,7 @@ bool IOSystem::Exists( const std::string& pFile) const { } // ---------------------------------------------------------------------------- -AI_FORCE_INLINE -bool IOSystem::ComparePaths (const std::string& one, const std::string& second) const { +AI_FORCE_INLINE bool IOSystem::ComparePaths(const std::string& one, const std::string& second) const { // NOTE: // For compatibility, interface was changed to const char* to // avoid crashes between binary incompatible STL versions @@ -279,8 +278,7 @@ bool IOSystem::ComparePaths (const std::string& one, const std::string& second) } // ---------------------------------------------------------------------------- -AI_FORCE_INLINE -bool IOSystem::PushDirectory( const std::string &path ) { +AI_FORCE_INLINE bool IOSystem::PushDirectory( const std::string &path ) { if ( path.empty() ) { return false; } @@ -291,8 +289,7 @@ bool IOSystem::PushDirectory( const std::string &path ) { } // ---------------------------------------------------------------------------- -AI_FORCE_INLINE -const std::string &IOSystem::CurrentDirectory() const { +AI_FORCE_INLINE const std::string &IOSystem::CurrentDirectory() const { if ( m_pathStack.empty() ) { static const std::string Dummy; return Dummy; @@ -301,14 +298,12 @@ const std::string &IOSystem::CurrentDirectory() const { } // ---------------------------------------------------------------------------- -AI_FORCE_INLINE -size_t IOSystem::StackSize() const { +AI_FORCE_INLINE size_t IOSystem::StackSize() const { return m_pathStack.size(); } // ---------------------------------------------------------------------------- -AI_FORCE_INLINE -bool IOSystem::PopDirectory() { +AI_FORCE_INLINE bool IOSystem::PopDirectory() { if ( m_pathStack.empty() ) { return false; } @@ -319,8 +314,7 @@ bool IOSystem::PopDirectory() { } // ---------------------------------------------------------------------------- -AI_FORCE_INLINE -bool IOSystem::CreateDirectory( const std::string &path ) { +AI_FORCE_INLINE bool IOSystem::CreateDirectory( const std::string &path ) { if ( path.empty() ) { return false; } @@ -333,8 +327,7 @@ bool IOSystem::CreateDirectory( const std::string &path ) { } // ---------------------------------------------------------------------------- -AI_FORCE_INLINE -bool IOSystem::ChangeDirectory( const std::string &path ) { +AI_FORCE_INLINE bool IOSystem::ChangeDirectory( const std::string &path ) { if ( path.empty() ) { return false; } @@ -348,8 +341,7 @@ bool IOSystem::ChangeDirectory( const std::string &path ) { // ---------------------------------------------------------------------------- -AI_FORCE_INLINE -bool IOSystem::DeleteFile( const std::string &file ) { +AI_FORCE_INLINE bool IOSystem::DeleteFile( const std::string &file ) { if ( file.empty() ) { return false; } diff --git a/include/assimp/Importer.hpp b/include/assimp/Importer.hpp index 09b5b6883..97f34090e 100644 --- a/include/assimp/Importer.hpp +++ b/include/assimp/Importer.hpp @@ -59,7 +59,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // Public ASSIMP data structures #include -#include +//#include namespace Assimp { // ======================================================================= diff --git a/include/assimp/LineSplitter.h b/include/assimp/LineSplitter.h index 44e6f0f1e..0a9d24437 100644 --- a/include/assimp/LineSplitter.h +++ b/include/assimp/LineSplitter.h @@ -4,7 +4,6 @@ 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, @@ -144,26 +143,23 @@ private: bool mSwallow, mSkip_empty_lines, mTrim; }; -AI_FORCE_INLINE -LineSplitter::LineSplitter(StreamReaderLE& stream, bool skip_empty_lines, bool trim ) -: mIdx(0) -, mCur() -, mStream(stream) -, mSwallow() -, mSkip_empty_lines(skip_empty_lines) -, mTrim(trim) { +AI_FORCE_INLINE LineSplitter::LineSplitter(StreamReaderLE& stream, bool skip_empty_lines, bool trim ) : + mIdx(0), + mCur(), + mStream(stream), + mSwallow(), + mSkip_empty_lines(skip_empty_lines), + mTrim(trim) { mCur.reserve(1024); operator++(); mIdx = 0; } -AI_FORCE_INLINE -LineSplitter::~LineSplitter() { +AI_FORCE_INLINE LineSplitter::~LineSplitter() { // empty } -AI_FORCE_INLINE -LineSplitter& LineSplitter::operator++() { +AI_FORCE_INLINE LineSplitter& LineSplitter::operator++() { if (mSwallow) { mSwallow = false; return *this; @@ -203,18 +199,15 @@ LineSplitter& LineSplitter::operator++() { return *this; } -AI_FORCE_INLINE -LineSplitter &LineSplitter::operator++(int) { +AI_FORCE_INLINE LineSplitter &LineSplitter::operator++(int) { return ++(*this); } -AI_FORCE_INLINE -const char *LineSplitter::operator[] (size_t idx) const { +AI_FORCE_INLINE const char *LineSplitter::operator[] (size_t idx) const { const char* s = operator->()->c_str(); SkipSpaces(&s); for (size_t i = 0; i < idx; ++i) { - for (; !IsSpace(*s); ++s) { if (IsLineEnd(*s)) { throw std::range_error("Token index out of range, EOL reached"); @@ -226,8 +219,7 @@ const char *LineSplitter::operator[] (size_t idx) const { } template -AI_FORCE_INLINE -void LineSplitter::get_tokens(const char* (&tokens)[N]) const { +AI_FORCE_INLINE void LineSplitter::get_tokens(const char* (&tokens)[N]) const { const char* s = operator->()->c_str(); SkipSpaces(&s); @@ -242,45 +234,37 @@ void LineSplitter::get_tokens(const char* (&tokens)[N]) const { } } -AI_FORCE_INLINE -const std::string* LineSplitter::operator -> () const { +AI_FORCE_INLINE const std::string* LineSplitter::operator -> () const { return &mCur; } -AI_FORCE_INLINE -std::string LineSplitter::operator* () const { +AI_FORCE_INLINE std::string LineSplitter::operator* () const { return mCur; } -AI_FORCE_INLINE -LineSplitter::operator bool() const { +AI_FORCE_INLINE LineSplitter::operator bool() const { return mStream.GetRemainingSize() > 0; } -AI_FORCE_INLINE -LineSplitter::operator line_idx() const { +AI_FORCE_INLINE LineSplitter::operator line_idx() const { return mIdx; } -AI_FORCE_INLINE -LineSplitter::line_idx LineSplitter::get_index() const { +AI_FORCE_INLINE LineSplitter::line_idx LineSplitter::get_index() const { return mIdx; } -AI_FORCE_INLINE -StreamReaderLE &LineSplitter::get_stream() { +AI_FORCE_INLINE StreamReaderLE &LineSplitter::get_stream() { return mStream; } -AI_FORCE_INLINE -bool LineSplitter::match_start(const char* check) { +AI_FORCE_INLINE bool LineSplitter::match_start(const char* check) { const size_t len = ::strlen(check); return len <= mCur.length() && std::equal(check, check + len, mCur.begin()); } -AI_FORCE_INLINE -void LineSplitter::swallow_next_increment() { +AI_FORCE_INLINE void LineSplitter::swallow_next_increment() { mSwallow = true; } diff --git a/include/assimp/LogStream.hpp b/include/assimp/LogStream.hpp index 3c2a587a2..64a7955fd 100644 --- a/include/assimp/LogStream.hpp +++ b/include/assimp/LogStream.hpp @@ -4,7 +4,6 @@ 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, @@ -43,9 +42,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** @file LogStream.hpp * @brief Abstract base class 'LogStream', representing an output log stream. */ +#pragma once #ifndef INCLUDED_AI_LOGSTREAM_H #define INCLUDED_AI_LOGSTREAM_H +#ifdef __GNUC__ +#pragma GCC system_header +#endif + #include "types.h" namespace Assimp { @@ -103,7 +107,6 @@ inline LogStream::~LogStream() { // empty } -// ------------------------------------------------------------------------------------ } // Namespace Assimp -#endif +#endif // INCLUDED_AI_LOGSTREAM_H diff --git a/include/assimp/Logger.hpp b/include/assimp/Logger.hpp index aa7ffba7c..549b0fbc2 100644 --- a/include/assimp/Logger.hpp +++ b/include/assimp/Logger.hpp @@ -4,7 +4,6 @@ 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, @@ -43,6 +42,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** @file Logger.hpp * @brief Abstract base class 'Logger', base of the logging system. */ +#pragma once #ifndef INCLUDED_AI_LOGGER_H #define INCLUDED_AI_LOGGER_H @@ -93,8 +93,6 @@ public: Err = 8 //!< Error log message }; -public: - /** @brief Virtual destructor */ virtual ~Logger(); @@ -259,39 +257,30 @@ protected: }; // ---------------------------------------------------------------------------------- -// Default constructor -inline -Logger::Logger() AI_NO_EXCEPT -: m_Severity(NORMAL) { +inline Logger::Logger() AI_NO_EXCEPT : + m_Severity(NORMAL) { // empty } // ---------------------------------------------------------------------------------- -// Virtual destructor -inline -Logger::~Logger() { +inline Logger::~Logger() { // empty } // ---------------------------------------------------------------------------------- -// Construction with given logging severity -inline -Logger::Logger(LogSeverity severity) -: m_Severity(severity) { +inline Logger::Logger(LogSeverity severity) : + m_Severity(severity) { // empty } // ---------------------------------------------------------------------------------- -// Log severity setter -inline -void Logger::setLogSeverity(LogSeverity log_severity){ +inline void Logger::setLogSeverity(LogSeverity log_severity){ m_Severity = log_severity; } // ---------------------------------------------------------------------------------- // Log severity getter -inline -Logger::LogSeverity Logger::getLogSeverity() const { +inline Logger::LogSeverity Logger::getLogSeverity() const { return m_Severity; } diff --git a/include/assimp/NullLogger.hpp b/include/assimp/NullLogger.hpp index 8281da4f7..8277b014a 100644 --- a/include/assimp/NullLogger.hpp +++ b/include/assimp/NullLogger.hpp @@ -44,9 +44,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * @brief Dummy logger */ +#pragma once #ifndef INCLUDED_AI_NULLLOGGER_H #define INCLUDED_AI_NULLLOGGER_H +#ifdef __GNUC__ +#pragma GCC system_header +#endif + #include "Logger.hpp" namespace Assimp { diff --git a/include/assimp/aabb.h b/include/assimp/aabb.h index cbf6193cb..a6ccdd452 100644 --- a/include/assimp/aabb.h +++ b/include/assimp/aabb.h @@ -44,36 +44,40 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define AI_AABB_H_INC #ifdef __GNUC__ -# pragma GCC system_header +#pragma GCC system_header #endif #include +// --------------------------------------------------------------------------- +/** + * An axis-aligned bounding box. + */ struct aiAABB { C_STRUCT aiVector3D mMin; C_STRUCT aiVector3D mMax; #ifdef __cplusplus - - aiAABB() - : mMin() - , mMax() { + /// @brief The default class constructor. + aiAABB() : + mMin(), mMax() { // empty } - aiAABB(const aiVector3D &min, const aiVector3D &max ) - : mMin(min) - , mMax(max) { + /// @brief The class constructor with the minimum and maximum. + /// @param min The minimum dimension. + /// @param max The maximum dimension. + aiAABB(const aiVector3D &min, const aiVector3D &max) : + mMin(min), mMax(max) { // empty } + /// @brief The class destructor. ~aiAABB() { // empty } #endif // __cplusplus - }; - #endif // AI_AABB_H_INC diff --git a/include/assimp/ai_assert.h b/include/assimp/ai_assert.h index b377b6e8b..b4f1ef8b2 100644 --- a/include/assimp/ai_assert.h +++ b/include/assimp/ai_assert.h @@ -38,6 +38,11 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ + +/** @file ai_assert.h + * @brief Declares the assimp-specific assertion handler. + */ + #pragma once #ifndef AI_ASSERT_H_INC #define AI_ASSERT_H_INC @@ -46,19 +51,24 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #if defined(ASSIMP_BUILD_DEBUG) -namespace Assimp -{ - // Assert violation behavior can be customized: see AssertHandler.h. - ASSIMP_API void aiAssertViolation(const char* failedExpression, const char* file, int line); +namespace Assimp { + +/// @brief Assert violation behavior can be customized: see AssertHandler.h. +/// @param failedExpression The expression to validate. +/// @param file The file location +/// @param line The line number +ASSIMP_API void aiAssertViolation(const char* failedExpression, const char* file, int line); + } +#endif -# define ai_assert(expression) (void)((!!(expression)) || (Assimp::aiAssertViolation(#expression, __FILE__, __LINE__), 0)) -# define ai_assert_entry() ai_assert(false) - +// Define assertion resolinig +#if defined(ASSIMP_BUILD_DEBUG) +# define ai_assert(expression) (void)((!!(expression)) || (Assimp::aiAssertViolation(#expression, __FILE__, __LINE__), 0)) +# define ai_assert_entry() ai_assert(false) #else # define ai_assert(expression) # define ai_assert_entry() #endif // ASSIMP_BUILD_DEBUG #endif // AI_ASSERT_H_INC - diff --git a/include/assimp/camera.h b/include/assimp/camera.h index 6a7acadbb..56c4dbe3f 100644 --- a/include/assimp/camera.h +++ b/include/assimp/camera.h @@ -48,7 +48,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define AI_CAMERA_H_INC #ifdef __GNUC__ -# pragma GCC system_header +#pragma GCC system_header #endif #include "types.h" @@ -100,8 +100,7 @@ extern "C" { * camera already look in the right direction. * */ -struct aiCamera -{ +struct aiCamera { /** The name of the camera. * * There must be a node in the scenegraph with the same name. @@ -127,7 +126,6 @@ struct aiCamera */ C_STRUCT aiVector3D mUp; - /** 'LookAt' - vector of the camera coordinate system relative to * the coordinate space defined by the corresponding node. * @@ -184,26 +182,27 @@ struct aiCamera #ifdef __cplusplus aiCamera() AI_NO_EXCEPT - : mUp (0.f,1.f,0.f) - , mLookAt (0.f,0.f,1.f) - , mHorizontalFOV (0.25f * (float)AI_MATH_PI) - , mClipPlaneNear (0.1f) - , mClipPlaneFar (1000.f) - , mAspect (0.f) - , mOrthographicWidth (0.f) - {} + : mUp(0.f, 1.f, 0.f), + mLookAt(0.f, 0.f, 1.f), + mHorizontalFOV(0.25f * (float)AI_MATH_PI), + mClipPlaneNear(0.1f), + mClipPlaneFar(1000.f), + mAspect(0.f), + mOrthographicWidth(0.f) {} /** @brief Get a *right-handed* camera matrix from me * @param out Camera matrix to be filled */ - void GetCameraMatrix (aiMatrix4x4& out) const - { + void GetCameraMatrix(aiMatrix4x4 &out) const { /** todo: test ... should work, but i'm not absolutely sure */ /** We don't know whether these vectors are already normalized ...*/ - aiVector3D zaxis = mLookAt; zaxis.Normalize(); - aiVector3D yaxis = mUp; yaxis.Normalize(); - aiVector3D xaxis = mUp^mLookAt; xaxis.Normalize(); + aiVector3D zaxis = mLookAt; + zaxis.Normalize(); + aiVector3D yaxis = mUp; + yaxis.Normalize(); + aiVector3D xaxis = mUp ^ mLookAt; + xaxis.Normalize(); out.a4 = -(xaxis * mPosition); out.b4 = -(yaxis * mPosition); @@ -228,7 +227,6 @@ struct aiCamera #endif }; - #ifdef __cplusplus } #endif diff --git a/include/assimp/cfileio.h b/include/assimp/cfileio.h index c1f8625b9..48a728a2a 100644 --- a/include/assimp/cfileio.h +++ b/include/assimp/cfileio.h @@ -5,8 +5,6 @@ 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, @@ -106,8 +104,7 @@ struct aiFileIO * the CRT. However, you can supply a custom implementation to Assimp by * delivering a custom aiFileIO. Use this to enable reading from other sources, * such as ZIP archives or memory locations. */ -struct aiFile -{ +struct aiFile { /** Callback to read from a file */ aiFileReadProc ReadProc; diff --git a/include/assimp/color4.inl b/include/assimp/color4.inl index fb31afa27..e5bfdf356 100644 --- a/include/assimp/color4.inl +++ b/include/assimp/color4.inl @@ -5,8 +5,6 @@ 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, diff --git a/include/assimp/commonMetaData.h b/include/assimp/commonMetaData.h index 128281bfb..34a375bf3 100644 --- a/include/assimp/commonMetaData.h +++ b/include/assimp/commonMetaData.h @@ -5,8 +5,6 @@ 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, From 93edbe883f0e89cdd207d18f85ccaa0544ae1c6d Mon Sep 17 00:00:00 2001 From: Hill Ma Date: Fri, 10 Sep 2021 11:00:21 -0700 Subject: [PATCH 263/335] Use Safe Constants Idioms for ObjFileParser::DEFAULT_MATERIAL. Reference: "A static Class Member" in https://abseil.io/tips/140 --- code/AssetLib/Obj/ObjFileParser.cpp | 3 +-- code/AssetLib/Obj/ObjFileParser.h | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/code/AssetLib/Obj/ObjFileParser.cpp b/code/AssetLib/Obj/ObjFileParser.cpp index 767805c10..2e998a815 100644 --- a/code/AssetLib/Obj/ObjFileParser.cpp +++ b/code/AssetLib/Obj/ObjFileParser.cpp @@ -47,7 +47,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include #include #include #include @@ -56,7 +55,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace Assimp { -const std::string ObjFileParser::DEFAULT_MATERIAL = AI_DEFAULT_MATERIAL_NAME; +constexpr char ObjFileParser::DEFAULT_MATERIAL[]; ObjFileParser::ObjFileParser() : m_DataIt(), diff --git a/code/AssetLib/Obj/ObjFileParser.h b/code/AssetLib/Obj/ObjFileParser.h index 5d7ce065a..05a3b8046 100644 --- a/code/AssetLib/Obj/ObjFileParser.h +++ b/code/AssetLib/Obj/ObjFileParser.h @@ -42,6 +42,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define OBJ_FILEPARSER_H_INC #include +#include #include #include #include @@ -141,7 +142,7 @@ private: // because the class contains pointer to allocated memory /// Default material name - static const std::string DEFAULT_MATERIAL; + static constexpr char DEFAULT_MATERIAL[] = AI_DEFAULT_MATERIAL_NAME; //! Iterator to current position in buffer DataArrayIt m_DataIt; //! Iterator to end position of buffer From 4cf4e7454f883cb5b34a484111fd06d7a3c7951c Mon Sep 17 00:00:00 2001 From: Krishty Date: Sat, 11 Sep 2021 21:51:20 +0200 Subject: [PATCH 264/335] removed useless code In destructors, zeroing attributes or clearing containers is utterly useless. --- code/AssetLib/3MF/D3MFOpcPackage.cpp | 1 - code/AssetLib/OpenGEX/OpenGEXImporter.cpp | 1 - code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp | 4 ---- code/AssetLib/Q3BSP/Q3BSPFileParser.cpp | 1 - code/AssetLib/XGL/XGLLoader.cpp | 1 - code/CApi/CInterfaceIOWrapper.cpp | 1 - code/Common/DefaultIOStream.cpp | 1 - code/Common/ZipArchiveIOSystem.cpp | 1 - 8 files changed, 11 deletions(-) diff --git a/code/AssetLib/3MF/D3MFOpcPackage.cpp b/code/AssetLib/3MF/D3MFOpcPackage.cpp index c29cec368..37fb80111 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.cpp +++ b/code/AssetLib/3MF/D3MFOpcPackage.cpp @@ -186,7 +186,6 @@ D3MFOpcPackage::D3MFOpcPackage(IOSystem *pIOHandler, const std::string &rFile) : D3MFOpcPackage::~D3MFOpcPackage() { mZipArchive->Close(mRootStream); delete mZipArchive; - mZipArchive = nullptr; } IOStream *D3MFOpcPackage::RootStream() const { diff --git a/code/AssetLib/OpenGEX/OpenGEXImporter.cpp b/code/AssetLib/OpenGEX/OpenGEXImporter.cpp index 05507944e..5b62594ef 100644 --- a/code/AssetLib/OpenGEX/OpenGEXImporter.cpp +++ b/code/AssetLib/OpenGEX/OpenGEXImporter.cpp @@ -290,7 +290,6 @@ OpenGEXImporter::OpenGEXImporter() : //------------------------------------------------------------------------------------------------ OpenGEXImporter::~OpenGEXImporter() { - m_ctx = nullptr; } //------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp b/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp index a1da8fd74..897bcb994 100644 --- a/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp +++ b/code/AssetLib/Q3BSP/Q3BSPFileImporter.cpp @@ -146,9 +146,6 @@ Q3BSPFileImporter::Q3BSPFileImporter() : // ------------------------------------------------------------------------------------------------ // Destructor. Q3BSPFileImporter::~Q3BSPFileImporter() { - m_pCurrentMesh = nullptr; - m_pCurrentFace = nullptr; - // Clear face-to-material map for (FaceMap::iterator it = m_MaterialLookupMap.begin(); it != m_MaterialLookupMap.end(); ++it) { const std::string &matName = it->first; @@ -156,7 +153,6 @@ Q3BSPFileImporter::~Q3BSPFileImporter() { delete it->second; } } - m_MaterialLookupMap.clear(); } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/Q3BSP/Q3BSPFileParser.cpp b/code/AssetLib/Q3BSP/Q3BSPFileParser.cpp index 9269e1e2b..a647985b0 100644 --- a/code/AssetLib/Q3BSP/Q3BSPFileParser.cpp +++ b/code/AssetLib/Q3BSP/Q3BSPFileParser.cpp @@ -78,7 +78,6 @@ Q3BSPFileParser::Q3BSPFileParser( const std::string &mapName, ZipArchiveIOSystem // ------------------------------------------------------------------------------------------------ Q3BSPFileParser::~Q3BSPFileParser() { delete m_pModel; - m_pModel = nullptr; } // ------------------------------------------------------------------------------------------------ diff --git a/code/AssetLib/XGL/XGLLoader.cpp b/code/AssetLib/XGL/XGLLoader.cpp index bbfa31829..df8eab5f7 100644 --- a/code/AssetLib/XGL/XGLLoader.cpp +++ b/code/AssetLib/XGL/XGLLoader.cpp @@ -100,7 +100,6 @@ XGLImporter::XGLImporter() : // Destructor, private as well XGLImporter::~XGLImporter() { delete mXmlParser; - mXmlParser = nullptr; } // ------------------------------------------------------------------------------------------------ diff --git a/code/CApi/CInterfaceIOWrapper.cpp b/code/CApi/CInterfaceIOWrapper.cpp index 8e2ac95c0..245f3bc7a 100644 --- a/code/CApi/CInterfaceIOWrapper.cpp +++ b/code/CApi/CInterfaceIOWrapper.cpp @@ -51,7 +51,6 @@ CIOStreamWrapper::~CIOStreamWrapper() { /* Various places depend on this destructor to close the file */ if (mFile) { mIO->mFileSystem->CloseProc(mIO->mFileSystem, mFile); - mFile = nullptr; } } diff --git a/code/Common/DefaultIOStream.cpp b/code/Common/DefaultIOStream.cpp index 557fbc78f..e18dcd3f5 100644 --- a/code/Common/DefaultIOStream.cpp +++ b/code/Common/DefaultIOStream.cpp @@ -82,7 +82,6 @@ inline int select_fseek<8>(FILE *file, int64_t offset, int origin) { DefaultIOStream::~DefaultIOStream() { if (mFile) { ::fclose(mFile); - mFile = nullptr; } } diff --git a/code/Common/ZipArchiveIOSystem.cpp b/code/Common/ZipArchiveIOSystem.cpp index 1f46ab0a9..7df51b8aa 100644 --- a/code/Common/ZipArchiveIOSystem.cpp +++ b/code/Common/ZipArchiveIOSystem.cpp @@ -352,7 +352,6 @@ ZipArchiveIOSystem::Implement::Implement(IOSystem *pIOHandler, const char *pFile ZipArchiveIOSystem::Implement::~Implement() { if (m_ZipFileHandle != nullptr) { unzClose(m_ZipFileHandle); - m_ZipFileHandle = nullptr; } } From 5895c0c22cc88763c016de821d2da2da7f4fd5dd Mon Sep 17 00:00:00 2001 From: Krishty Date: Sat, 11 Sep 2021 23:23:05 +0200 Subject: [PATCH 265/335] more const in format detection BaseImporter::SearchFileHeaderForToken() expected a pointer to a non-const token list. This was probably an oversight, as nobody would realistically expect the function to change the list. Furthermore, it prevented token lists from being compiled to read-only memory, in some cases even causing the compiler to generate thread-safe initialization. The list is now const and all callers declare their token lists static const, thus compiling them to read-only memory. --- code/AssetLib/AMF/AMFImporter.cpp | 2 +- code/AssetLib/ASE/ASELoader.cpp | 2 +- code/AssetLib/BVH/BVHLoader.cpp | 2 +- code/AssetLib/Blender/BlenderLoader.cpp | 4 ++-- code/AssetLib/COB/COBLoader.cpp | 2 +- code/AssetLib/CSM/CSMLoader.cpp | 2 +- code/AssetLib/Collada/ColladaLoader.cpp | 2 +- code/AssetLib/DXF/DXFLoader.cpp | 4 ++-- code/AssetLib/FBX/FBXImporter.cpp | 2 +- code/AssetLib/IFC/IFCLoader.cpp | 5 ++--- code/AssetLib/Irr/IRRLoader.cpp | 2 +- code/AssetLib/Irr/IRRMeshLoader.cpp | 2 +- code/AssetLib/MD5/MD5Loader.cpp | 2 +- code/AssetLib/MMD/MMDImporter.cpp | 4 ++-- code/AssetLib/MS3D/MS3DLoader.cpp | 2 +- code/AssetLib/NDO/NDOLoader.cpp | 2 +- code/AssetLib/OFF/OFFLoader.cpp | 2 +- code/AssetLib/Ogre/OgreImporter.cpp | 2 +- code/AssetLib/OpenGEX/OpenGEXImporter.cpp | 4 ++-- code/AssetLib/Ply/PlyLoader.cpp | 2 +- code/AssetLib/Q3D/Q3DLoader.cpp | 2 +- code/AssetLib/STL/STLLoader.cpp | 2 +- code/AssetLib/Terragen/TerragenLoader.cpp | 2 +- code/AssetLib/XGL/XGLLoader.cpp | 2 +- code/Common/BaseImporter.cpp | 2 +- include/assimp/BaseImporter.h | 2 +- 26 files changed, 31 insertions(+), 32 deletions(-) diff --git a/code/AssetLib/AMF/AMFImporter.cpp b/code/AssetLib/AMF/AMFImporter.cpp index 88a38b827..9a9ab94ca 100644 --- a/code/AssetLib/AMF/AMFImporter.cpp +++ b/code/AssetLib/AMF/AMFImporter.cpp @@ -511,7 +511,7 @@ bool AMFImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool p } if (extension.empty() || pCheckSig) { - const char *tokens[] = { "" }; + static const char * const tokens[] = { "" }; return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); } else { /// @todo Read and validate first header chunk? diff --git a/code/AssetLib/OpenGEX/OpenGEXImporter.cpp b/code/AssetLib/OpenGEX/OpenGEXImporter.cpp index 05507944e..0f224ea28 100644 --- a/code/AssetLib/OpenGEX/OpenGEXImporter.cpp +++ b/code/AssetLib/OpenGEX/OpenGEXImporter.cpp @@ -299,8 +299,8 @@ bool OpenGEXImporter::CanRead(const std::string &file, IOSystem *pIOHandler, boo if (!checkSig) { canRead = SimpleExtensionCheck(file, "ogex"); } else { - static const char *token[] = { "Metric", "GeometryNode", "VertexArray (attrib", "IndexArray" }; - canRead = BaseImporter::SearchFileHeaderForToken(pIOHandler, file, token, 4); + static const char * const token[] = { "Metric", "GeometryNode", "VertexArray (attrib", "IndexArray" }; + canRead = SearchFileHeaderForToken(pIOHandler, file, token, 4); } return canRead; diff --git a/code/AssetLib/Ply/PlyLoader.cpp b/code/AssetLib/Ply/PlyLoader.cpp index 93d48bcbf..b58ef1ef2 100644 --- a/code/AssetLib/Ply/PlyLoader.cpp +++ b/code/AssetLib/Ply/PlyLoader.cpp @@ -111,7 +111,7 @@ bool PLYImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool c if (!pIOHandler) { return true; } - static const char *tokens[] = { + static const char * const tokens[] = { "ply" }; return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); diff --git a/code/AssetLib/Q3D/Q3DLoader.cpp b/code/AssetLib/Q3D/Q3DLoader.cpp index f81026547..fe8541a5d 100644 --- a/code/AssetLib/Q3D/Q3DLoader.cpp +++ b/code/AssetLib/Q3D/Q3DLoader.cpp @@ -92,7 +92,7 @@ bool Q3DImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool c else if (!extension.length() || checkSig) { if (!pIOHandler) return true; - const char *tokens[] = { "quick3Do", "quick3Ds" }; + static const char * const tokens[] = { "quick3Do", "quick3Ds" }; return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 2); } return false; diff --git a/code/AssetLib/STL/STLLoader.cpp b/code/AssetLib/STL/STLLoader.cpp index 8cfe63e0d..4d23b93e2 100644 --- a/code/AssetLib/STL/STLLoader.cpp +++ b/code/AssetLib/STL/STLLoader.cpp @@ -150,7 +150,7 @@ bool STLImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool c if (!pIOHandler) { return true; } - const char *tokens[] = { "STL", "solid" }; + static const char * const tokens[] = { "STL", "solid" }; return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 2); } diff --git a/code/AssetLib/Terragen/TerragenLoader.cpp b/code/AssetLib/Terragen/TerragenLoader.cpp index e0bdbf026..9a6e7fa24 100644 --- a/code/AssetLib/Terragen/TerragenLoader.cpp +++ b/code/AssetLib/Terragen/TerragenLoader.cpp @@ -93,7 +93,7 @@ bool TerragenImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, b return true; } - const char *tokens[] = { "terragen" }; + static const char * const tokens[] = { "terragen" }; return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1); } diff --git a/code/AssetLib/XGL/XGLLoader.cpp b/code/AssetLib/XGL/XGLLoader.cpp index bbfa31829..c3341af5e 100644 --- a/code/AssetLib/XGL/XGLLoader.cpp +++ b/code/AssetLib/XGL/XGLLoader.cpp @@ -118,7 +118,7 @@ bool XGLImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool c } else if (extension == "xml" || checkSig) { ai_assert(pIOHandler != NULL); - const char *tokens[] = { "", "", "" }; + static const char * const tokens[] = { "", "", "" }; return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 3); } return false; diff --git a/code/Common/BaseImporter.cpp b/code/Common/BaseImporter.cpp index 0f41e9e65..fa6cac938 100644 --- a/code/Common/BaseImporter.cpp +++ b/code/Common/BaseImporter.cpp @@ -155,7 +155,7 @@ void BaseImporter::GetExtensionList(std::set &extensions) { // ------------------------------------------------------------------------------------------------ /*static*/ bool BaseImporter::SearchFileHeaderForToken(IOSystem *pIOHandler, const std::string &pFile, - const char **tokens, + const char * const *tokens, unsigned int numTokens, unsigned int searchBytes /* = 200 */, bool tokensSol /* false */, diff --git a/include/assimp/BaseImporter.h b/include/assimp/BaseImporter.h index 54b5daac1..656e0f165 100644 --- a/include/assimp/BaseImporter.h +++ b/include/assimp/BaseImporter.h @@ -259,7 +259,7 @@ public: // static utilities static bool SearchFileHeaderForToken( IOSystem *pIOSystem, const std::string &file, - const char **tokens, + const char * const *tokens, unsigned int numTokens, unsigned int searchBytes = 200, bool tokensSol = false, From 3f6a371b648ab3dc4971859488473064cb0276a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Martin?= Date: Sun, 12 Sep 2021 11:37:33 +0200 Subject: [PATCH 266/335] migrated x3d importer to pugixml reader --- code/AssetLib/X3D/X3DGeoHelper.cpp | 2 +- code/AssetLib/X3D/X3DGeoHelper.h | 2 +- code/AssetLib/X3D/X3DImporter.cpp | 2702 +---------------- code/AssetLib/X3D/X3DImporter.hpp | 531 +--- code/AssetLib/X3D/X3DImporter_Geometry2D.cpp | 467 +++ code/AssetLib/X3D/X3DImporter_Geometry3D.cpp | 981 ++++++ code/AssetLib/X3D/X3DImporter_Group.cpp | 273 ++ code/AssetLib/X3D/X3DImporter_Light.cpp | 270 ++ code/AssetLib/X3D/X3DImporter_Macro.hpp | 110 + code/AssetLib/X3D/X3DImporter_Metadata.cpp | 255 ++ code/AssetLib/X3D/X3DImporter_Networking.cpp | 125 + code/AssetLib/X3D/X3DImporter_Node.hpp | 459 +++ code/AssetLib/X3D/X3DImporter_Postprocess.cpp | 731 +++++ code/AssetLib/X3D/X3DImporter_Rendering.cpp | 987 ++++++ code/AssetLib/X3D/X3DImporter_Shape.cpp | 221 ++ code/AssetLib/X3D/X3DImporter_Texturing.cpp | 179 ++ code/AssetLib/X3D/X3DXmlHelper.cpp | 294 ++ code/AssetLib/X3D/X3DXmlHelper.h | 30 + code/CMakeLists.txt | 14 + code/Common/ImporterRegistry.cpp | 4 +- 20 files changed, 5662 insertions(+), 2975 deletions(-) create mode 100644 code/AssetLib/X3D/X3DImporter_Geometry2D.cpp create mode 100644 code/AssetLib/X3D/X3DImporter_Geometry3D.cpp create mode 100644 code/AssetLib/X3D/X3DImporter_Group.cpp create mode 100644 code/AssetLib/X3D/X3DImporter_Light.cpp create mode 100644 code/AssetLib/X3D/X3DImporter_Macro.hpp create mode 100644 code/AssetLib/X3D/X3DImporter_Metadata.cpp create mode 100644 code/AssetLib/X3D/X3DImporter_Networking.cpp create mode 100644 code/AssetLib/X3D/X3DImporter_Node.hpp create mode 100644 code/AssetLib/X3D/X3DImporter_Postprocess.cpp create mode 100644 code/AssetLib/X3D/X3DImporter_Rendering.cpp create mode 100644 code/AssetLib/X3D/X3DImporter_Shape.cpp create mode 100644 code/AssetLib/X3D/X3DImporter_Texturing.cpp create mode 100644 code/AssetLib/X3D/X3DXmlHelper.cpp create mode 100644 code/AssetLib/X3D/X3DXmlHelper.h diff --git a/code/AssetLib/X3D/X3DGeoHelper.cpp b/code/AssetLib/X3D/X3DGeoHelper.cpp index be41cc012..a9ac57e06 100644 --- a/code/AssetLib/X3D/X3DGeoHelper.cpp +++ b/code/AssetLib/X3D/X3DGeoHelper.cpp @@ -116,7 +116,7 @@ void X3DGeoHelper::polylineIdx_to_lineIdx(const std::list &pPolylineCoo vert_set[6].Set(x1, y2, z1); \ vert_set[7].Set(x1, y1, z1) -void X3DGeoHelper::rect_parallele_piped(const aiVector3D &pSize, std::list &pVertices) { +void X3DGeoHelper::rect_parallel_epiped(const aiVector3D &pSize, std::list &pVertices) { MESH_RectParallelepiped_CREATE_VERT; MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 3, 2, 1, 0); // front MACRO_FACE_ADD_QUAD_FA(true, pVertices, vert_set, 6, 7, 4, 5); // back diff --git a/code/AssetLib/X3D/X3DGeoHelper.h b/code/AssetLib/X3D/X3DGeoHelper.h index e730d3a9c..78e57f9da 100644 --- a/code/AssetLib/X3D/X3DGeoHelper.h +++ b/code/AssetLib/X3D/X3DGeoHelper.h @@ -19,7 +19,7 @@ public: static void make_arc2D(float pStartAngle, float pEndAngle, float pRadius, size_t numSegments, std::list &pVertices); static void extend_point_to_line(const std::list &pPoint, std::list &pLine); static void polylineIdx_to_lineIdx(const std::list &pPolylineCoordIdx, std::list &pLineCoordIdx); - static void rect_parallele_piped(const aiVector3D &pSize, std::list &pVertices); + static void rect_parallel_epiped(const aiVector3D &pSize, std::list &pVertices); static void coordIdx_str2faces_arr(const std::vector &pCoordIdx, std::vector &pFaces, unsigned int &pPrimitiveTypes); static void add_color(aiMesh &pMesh, const std::list &pColors, const bool pColorPerVertex); static void add_color(aiMesh &pMesh, const std::list &pColors, const bool pColorPerVertex); diff --git a/code/AssetLib/X3D/X3DImporter.cpp b/code/AssetLib/X3D/X3DImporter.cpp index eeb1a53d7..bfe83a49f 100644 --- a/code/AssetLib/X3D/X3DImporter.cpp +++ b/code/AssetLib/X3D/X3DImporter.cpp @@ -46,11 +46,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER #include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" -#include -#include #include -#include // Header files, stdlib. #include @@ -72,59 +70,15 @@ const aiImporterDesc X3DImporter::Description = { "x3d x3db" }; -struct WordIterator { - using iterator_category = std::input_iterator_tag; - using value_type = const char *; - using difference_type = ptrdiff_t; - using pointer = value_type *; - using reference = value_type &; +bool X3DImporter::isNodeEmpty(XmlNode &node) { + return node.first_child().empty(); +} - static const char *whitespace; - const char *mStart, *mEnd; +void X3DImporter::checkNodeMustBeEmpty(XmlNode &node) { + if (isNodeEmpty(node)) throw DeadlyImportError(std::string("Node <") + node.name() + "> must be empty."); +} - WordIterator(const char *start, const char *end) : - mStart(start), - mEnd(end) { - mStart = start + ::strspn(start, whitespace); - if (mStart >= mEnd) { - mStart = 0; - } - } - WordIterator() : - mStart(0), - mEnd(0) {} - WordIterator(const WordIterator &other) : - mStart(other.mStart), - mEnd(other.mEnd) {} - WordIterator &operator=(const WordIterator &other) { - mStart = other.mStart; - mEnd = other.mEnd; - return *this; - } - - bool operator==(const WordIterator &other) const { return mStart == other.mStart; } - - bool operator!=(const WordIterator &other) const { return mStart != other.mStart; } - - WordIterator &operator++() { - mStart += strcspn(mStart, whitespace); - mStart += strspn(mStart, whitespace); - if (mStart >= mEnd) { - mStart = 0; - } - return *this; - } - WordIterator operator++(int) { - WordIterator result(*this); - ++(*this); - return result; - } - const char *operator*() const { return mStart; } -}; - -const char *WordIterator::whitespace = ", \t\r\n"; - -void skipUnsupportedNode(const std::string &pParentNodeName, XmlNode &node) { +void X3DImporter::skipUnsupportedNode(const std::string &pParentNodeName, XmlNode &node) { static const size_t Uns_Skip_Len = 192; static const char *Uns_Skip[Uns_Skip_Len] = { // CAD geometry component @@ -203,16 +157,20 @@ void skipUnsupportedNode(const std::string &pParentNodeName, XmlNode &node) { }; const std::string nn = node.name(); + + if (nn.empty()) { + const std::string nv = node.value(); + if (!nv.empty()) { + LogInfo("Ignoring comment \"" + nv + "\" in " + pParentNodeName + "."); + return; + } + } + bool found = false; - bool close_found = false; for (size_t i = 0; i < Uns_Skip_Len; i++) { if (nn == Uns_Skip[i]) { found = true; - if (node.empty()) { - close_found = true; - break; - } } } @@ -223,7 +181,8 @@ void skipUnsupportedNode(const std::string &pParentNodeName, XmlNode &node) { X3DImporter::X3DImporter() : mNodeElementCur(nullptr), - mScene(nullptr) { + mScene(nullptr), + mpIOHandler(nullptr) { // empty } @@ -265,9 +224,11 @@ void X3DImporter::ParseFile(const std::string &file, IOSystem *pIOHandler) { for (auto ¤tNode : node->children()) { const std::string ¤tName = currentNode.name(); if (currentName == "head") { - readMetadata(currentNode); + readHead(currentNode); } else if (currentName == "Scene") { readScene(currentNode); + } else { + skipUnsupportedNode("X3D", currentNode); } } } @@ -283,23 +244,25 @@ bool X3DImporter::CanRead(const std::string &pFile, IOSystem * /*pIOHandler*/, b return false; } -void X3DImporter::InternReadFile( const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler ) { +void X3DImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) { + mpIOHandler = pIOHandler; + + Clear(); std::shared_ptr stream(pIOHandler->Open(pFile, "rb")); if (!stream) { throw DeadlyImportError("Could not open file for reading"); } std::string::size_type slashPos = pFile.find_last_of("\\/"); - pIOHandler->PushDirectory(slashPos == std::string::npos ? std::string() : pFile.substr(0, slashPos + 1)); - ParseFile(pFile, pIOHandler); - pIOHandler->PopDirectory(); - - // mScene = pScene; pScene->mRootNode = new aiNode(pFile); pScene->mRootNode->mParent = nullptr; pScene->mFlags |= AI_SCENE_FLAGS_ALLOW_SHARED; + pIOHandler->PushDirectory(slashPos == std::string::npos ? std::string() : pFile.substr(0, slashPos + 1)); + ParseFile(pFile, pIOHandler); + pIOHandler->PopDirectory(); + //search for root node element mNodeElementCur = NodeElement_List.front(); @@ -342,7 +305,6 @@ void X3DImporter::InternReadFile( const std::string &pFile, aiScene *pScene, IOS pScene->mLights[i] = *it++; } } - } const aiImporterDesc *X3DImporter::GetInfo() const { @@ -354,2540 +316,162 @@ struct meta_entry { std::string value; }; -void X3DImporter::readMetadata(XmlNode &node) { +void X3DImporter::readHead(XmlNode &node) { std::vector metaArray; for (auto currentNode : node.children()) { const std::string ¤tName = currentNode.name(); if (currentName == "meta") { + checkNodeMustBeEmpty(node); meta_entry entry; if (XmlParser::getStdStrAttribute(currentNode, "name", entry.name)) { XmlParser::getStdStrAttribute(currentNode, "content", entry.value); metaArray.emplace_back(entry); } } + // TODO: check if other node types in head should be supported } mScene->mMetaData = aiMetadata::Alloc(static_cast(metaArray.size())); unsigned int i = 0; for (auto currentMeta : metaArray) { - mScene->mMetaData->Set(i, currentMeta.name, currentMeta.value); + mScene->mMetaData->Set(i, currentMeta.name, aiString(currentMeta.value)); ++i; } } -void X3DImporter::readScene(XmlNode &node) { +void X3DImporter::readChildNodes(XmlNode &node, const std::string &pParentNodeName) { for (auto currentNode : node.children()) { const std::string ¤tName = currentNode.name(); - if (currentName == "Viewpoint") { - readViewpoint(currentNode); + if (currentName == "Shape") + readShape(currentNode); + else if (currentName == "Group") { + startReadGroup(currentNode); + readChildNodes(currentNode, "Group"); + endReadGroup(); + } else if (currentName == "StaticGroup") { + startReadStaticGroup(currentNode); + readChildNodes(currentNode, "StaticGroup"); + endReadStaticGroup(); + } else if (currentName == "Transform") { + startReadTransform(currentNode); + readChildNodes(currentNode, "Transform"); + endReadTransform(); + } else if (currentName == "Switch") { + startReadSwitch(currentNode); + readChildNodes(currentNode, "Switch"); + endReadSwitch(); + } else if (currentName == "DirectionalLight") { + readDirectionalLight(currentNode); + } else if (currentName == "PointLight") { + readPointLight(currentNode); + } else if (currentName == "SpotLight") { + readSpotLight(currentNode); + } else if (currentName == "Inline") { + readInline(currentNode); + } else if (!checkForMetadataNode(currentNode)) { + skipUnsupportedNode(pParentNodeName, currentNode); } } } -void X3DImporter::readViewpoint(XmlNode &node) { - for (auto currentNode : node.children()) { - //const std::string ¤tName = currentNode.name(); - } +void X3DImporter::readScene(XmlNode &node) { + ParseHelper_Group_Begin(true); + readChildNodes(node, "Scene"); + ParseHelper_Node_Exit(); } -void readMetadataBoolean(XmlNode &node, X3DNodeElementBase *parent) { - std::string val; - X3DNodeElementMetaBoolean *boolean = nullptr; - if (XmlParser::getStdStrAttribute(node, "value", val)) { - std::vector values; - tokenize(val, values, " "); - boolean = new X3DNodeElementMetaBoolean(parent); - for (size_t i = 0; i < values.size(); ++i) { - bool current_boolean = false; - if (values[i] == "true") { - current_boolean = true; - } - boolean->Value.emplace_back(current_boolean); +/*********************************************************************************************************************************************/ +/************************************************************ Functions: find set ************************************************************/ +/*********************************************************************************************************************************************/ + +bool X3DImporter::FindNodeElement_FromRoot(const std::string &pID, const X3DElemType pType, X3DNodeElementBase **pElement) { + for (std::list::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); ++it) { + if (((*it)->Type == pType) && ((*it)->ID == pID)) { + if (pElement != nullptr) *pElement = *it; + + return true; } - } -} + } // for(std::list::iterator it = NodeElement_List.begin(); it != NodeElement_List.end(); it++) -void readMetadataDouble(XmlNode &node, X3DNodeElementBase *parent) { - std::string val; - X3DNodeElementMetaDouble *doubleNode = nullptr; - if (XmlParser::getStdStrAttribute(node, "value", val)) { - std::vector values; - tokenize(val, values, " "); - doubleNode = new X3DNodeElementMetaDouble(parent); - for (size_t i = 0; i < values.size(); ++i) { - double current_double = static_cast(fast_atof(values[i].c_str())); - doubleNode->Value.emplace_back(current_double); - } - } -} - -void readMetadataFloat(XmlNode &node, X3DNodeElementBase *parent) { - std::string val; - X3DNodeElementMetaFloat *floatNode = nullptr; - if (XmlParser::getStdStrAttribute(node, "value", val)) { - std::vector values; - tokenize(val, values, " "); - floatNode = new X3DNodeElementMetaFloat(parent); - for (size_t i = 0; i < values.size(); ++i) { - float current_float = static_cast(fast_atof(values[i].c_str())); - floatNode->Value.emplace_back(current_float); - } - } -} - -void readMetadataInteger(XmlNode &node, X3DNodeElementBase *parent) { - std::string val; - X3DNodeElementMetaInt *intNode = nullptr; - if (XmlParser::getStdStrAttribute(node, "value", val)) { - std::vector values; - tokenize(val, values, " "); - intNode = new X3DNodeElementMetaInt(parent); - for (size_t i = 0; i < values.size(); ++i) { - int current_int = static_cast(std::atoi(values[i].c_str())); - intNode->Value.emplace_back(current_int); - } - } -} - -void readMetadataSet(XmlNode &node, X3DNodeElementBase *parent) { - std::string val; - X3DNodeElementMetaSet *setNode = new X3DNodeElementMetaSet(parent); - if (XmlParser::getStdStrAttribute(node, "name", val)) { - setNode->Name = val; - } - - if (XmlParser::getStdStrAttribute(node, "reference", val)) { - setNode->Reference = val; - } -} - -void readMetadataString(XmlNode &node, X3DNodeElementBase *parent) { - std::string val; - X3DNodeElementMetaString *strNode = nullptr; - if (XmlParser::getStdStrAttribute(node, "value", val)) { - std::vector values; - tokenize(val, values, " "); - strNode = new X3DNodeElementMetaString(parent); - for (size_t i = 0; i < values.size(); ++i) { - strNode->Value.emplace_back(values[i]); - } - } -} - -void X3DImporter::ParseDirectionalLight(XmlNode &node) { - std::string def, use; - float ambientIntensity = 0; - aiColor3D color(1, 1, 1); - aiVector3D direction(0, 0, -1); - bool global = false; - float intensity = 1; - bool on = true; - X3DNodeElementBase *ne = nullptr; - - //MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - XmlParser::getFloatAttribute(node, "ambientIntensity", ambientIntensity); - //MACRO_ATTRREAD_CHECK_RET("ambientIntensity", ambientIntensity, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsCol3f); - MACRO_ATTRREAD_CHECK_REF("direction", direction, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_RET("global", global, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("intensity", intensity, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("on", on, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_DirectionalLight, ne); - } else { - if (on) { - // create and if needed - define new geometry object. - ne = new X3DNodeNodeElementLight(CX3DImporter_NodeElement::ENET_DirectionalLight, NodeElement_Cur); - if (!def.empty()) - ne->ID = def; - else - ne->ID = "DirectionalLight_" + to_string((size_t)ne); // make random name - - ((X3DNodeNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; - ((X3DNodeNodeElementLight *)ne)->Color = color; - ((X3DNodeNodeElementLight *)ne)->Direction = direction; - ((X3DNodeNodeElementLight *)ne)->Global = global; - ((X3DNodeNodeElementLight *)ne)->Intensity = intensity; - // Assimp want a node with name similar to a light. "Why? I don't no." ) - ParseHelper_Group_Begin(false); - - mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. - ParseHelper_Node_Exit(); - // check for child nodes - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "DirectionalLight"); - else - mNodeElementCur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(on) - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Lighting_PointLight() { - std::string def, use; - float ambientIntensity = 0; - aiVector3D attenuation(1, 0, 0); - aiColor3D color(1, 1, 1); - bool global = true; - float intensity = 1; - aiVector3D location(0, 0, 0); - bool on = true; - float radius = 100; - X3DNodeElementBase *ne = nullptr; - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("ambientIntensity", ambientIntensity, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("attenuation", attenuation, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsCol3f); - MACRO_ATTRREAD_CHECK_RET("global", global, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("intensity", intensity, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("location", location, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_RET("on", on, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_PointLight, ne); - } else { - if (on) { - // create and if needed - define new geometry object. - ne = new X3DNodeNodeElementLight(X3DElemType::ENET_PointLight, mNodeElementCur); - if (!def.empty()) ne->ID = def; - - ((X3DNodeNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; - ((X3DNodeNodeElementLight *)ne)->Attenuation = attenuation; - ((X3DNodeNodeElementLight *)ne)->Color = color; - ((X3DNodeNodeElementLight *)ne)->Global = global; - ((X3DNodeNodeElementLight *)ne)->Intensity = intensity; - ((X3DNodeNodeElementLight *)ne)->Location = location; - ((X3DNodeNodeElementLight *)ne)->Radius = radius; - // Assimp want a node with name similar to a light. "Why? I don't no." ) - ParseHelper_Group_Begin(false); - // make random name - if (ne->ID.empty()) ne->ID = "PointLight_" + to_string((size_t)ne); - - mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. - ParseHelper_Node_Exit(); - // check for child nodes - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "PointLight"); - else - mNodeElementCur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(on) - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Lighting_SpotLight() { - std::string def, use; - float ambientIntensity = 0; - aiVector3D attenuation(1, 0, 0); - float beamWidth = 0.7854f; - aiColor3D color(1, 1, 1); - float cutOffAngle = 1.570796f; - aiVector3D direction(0, 0, -1); - bool global = true; - float intensity = 1; - aiVector3D location(0, 0, 0); - bool on = true; - float radius = 100; - X3DNodeElementBase *ne = nullptr; - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("ambientIntensity", ambientIntensity, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("attenuation", attenuation, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_RET("beamWidth", beamWidth, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("color", color, XML_ReadNode_GetAttrVal_AsCol3f); - MACRO_ATTRREAD_CHECK_RET("cutOffAngle", cutOffAngle, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("direction", direction, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_RET("global", global, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("intensity", intensity, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("location", location, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_RET("on", on, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_SpotLight, ne); - } else { - if (on) { - // create and if needed - define new geometry object. - ne = new X3DNodeNodeElementLight(X3DElemType::ENET_SpotLight, mNodeElementCur); - if (!def.empty()) - ne->ID = def; - - if (beamWidth > cutOffAngle) - beamWidth = cutOffAngle; - - ((X3DNodeNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; - ((X3DNodeNodeElementLight *)ne)->Attenuation = attenuation; - ((X3DNodeNodeElementLight *)ne)->BeamWidth = beamWidth; - ((X3DNodeNodeElementLight *)ne)->Color = color; - ((X3DNodeNodeElementLight *)ne)->CutOffAngle = cutOffAngle; - ((X3DNodeNodeElementLight *)ne)->Direction = direction; - ((X3DNodeNodeElementLight *)ne)->Global = global; - ((X3DNodeNodeElementLight *)ne)->Intensity = intensity; - ((X3DNodeNodeElementLight *)ne)->Location = location; - ((X3DNodeNodeElementLight *)ne)->Radius = radius; - - // Assimp want a node with name similar to a light. "Why? I don't no." ) - ParseHelper_Group_Begin(false); - // make random name - if (ne->ID.empty()) ne->ID = "SpotLight_" + to_string((size_t)ne); - - mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. - ParseHelper_Node_Exit(); - // check for child nodes - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "SpotLight"); - else - mNodeElementCur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(on) - } // if(!use.empty()) else -} - -void X3DImporter::ParseNode_Grouping_Group() { - std::string def, use; - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - X3DNodeElementBase *ne = nullptr; - - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); - } else { - ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. - // at this place new group mode created and made current, so we can name it. - if (!def.empty()) mNodeElementCur->ID = def; - // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. - - // for empty element exit from node in that place - if (mReader->isEmptyElement()) ParseHelper_Node_Exit(); - } // if(!use.empty()) else -} - -void X3DImporter::ParseNode_Grouping_GroupEnd() { - ParseHelper_Node_Exit(); // go up in scene graph -} - -// -// -// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, -// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the -// precise palette of legal nodes that are available depends on assigned profile and components. -// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. -// -// The StaticGroup node contains children nodes which cannot be modified. StaticGroup children are guaranteed to not change, send events, receive events or -// contain any USE references outside the StaticGroup. -void X3DImporter::ParseNode_Grouping_StaticGroup() { - std::string def, use; - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - X3DNodeElementBase *ne = nullptr; - - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); - } else { - ParseHelper_Group_Begin(true); // create new grouping element and go deeper if node has children. - // at this place new group mode created and made current, so we can name it. - if (!def.empty()) mNodeElementCur->ID = def; - // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. - - // for empty element exit from node in that place - if (mReader->isEmptyElement()) ParseHelper_Node_Exit(); - } // if(!use.empty()) else -} - -void X3DImporter::ParseNode_Grouping_StaticGroupEnd() { - ParseHelper_Node_Exit(); // go up in scene graph -} - -// -// -// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, -// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the -// precise palette of legal nodes that are available depends on assigned profile and components. -// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. -// -// The Switch grouping node traverses zero or one of the nodes specified in the children field. The whichChoice field specifies the index of the child -// to traverse, with the first child having index 0. If whichChoice is less than zero or greater than the number of nodes in the children field, nothing -// is chosen. -void X3DImporter::ParseNode_Grouping_Switch() { - std::string def, use; - int32_t whichChoice = -1; - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("whichChoice", whichChoice, XML_ReadNode_GetAttrVal_AsI32); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - CX3DImporter_NodeElement *ne; - - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); - } else { - ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. - // at this place new group mode created and made current, so we can name it. - if (!def.empty()) NodeElement_Cur->ID = def; - - // also set values specific to this type of group - ((CX3DNodeElementGroup *)NodeElement_Cur)->UseChoice = true; - ((CX3DNodeElementGroup *)NodeElement_Cur)->Choice = whichChoice; - // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. - - // for empty element exit from node in that place - if (mReader->isEmptyElement()) ParseHelper_Node_Exit(); - } // if(!use.empty()) else -} - -void X3DImporter::ParseNode_Grouping_SwitchEnd() { - // just exit from node. Defined choice will be accepted at postprocessing stage. - ParseHelper_Node_Exit(); // go up in scene graph -} - -// -// -// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, -// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the -// precise palette of legal nodes that are available depends on assigned profile and components. -// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. -// -// The Transform node is a grouping node that defines a coordinate system for its children that is relative to the coordinate systems of its ancestors. -// Given a 3-dimensional point P and Transform node, P is transformed into point P' in its parent's coordinate system by a series of intermediate -// transformations. In matrix transformation notation, where C (center), SR (scaleOrientation), T (translation), R (rotation), and S (scale) are the -// equivalent transformation matrices, -// P' = T * C * R * SR * S * -SR * -C * P -void X3DImporter::ParseNode_Grouping_Transform() { - aiVector3D center(0, 0, 0); - float rotation[4] = { 0, 0, 1, 0 }; - aiVector3D scale(1, 1, 1); // A value of zero indicates that any child geometry shall not be displayed - float scale_orientation[4] = { 0, 0, 1, 0 }; - aiVector3D translation(0, 0, 0); - aiMatrix4x4 matr, tmatr; - std::string use, def; - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_REF("center", center, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_REF("scale", scale, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_REF("translation", translation, XML_ReadNode_GetAttrVal_AsVec3f); - if (an == "rotation") { - std::vector tvec; - - XML_ReadNode_GetAttrVal_AsArrF(idx, tvec); - if (tvec.size() != 4) throw DeadlyImportError(": rotation vector must have 4 elements."); - - memcpy(rotation, tvec.data(), sizeof(rotation)); - - continue; - } - - if (an == "scaleOrientation") { - std::vector tvec; - XML_ReadNode_GetAttrVal_AsArrF(idx, tvec); - if (tvec.size() != 4) { - throw DeadlyImportError(": scaleOrientation vector must have 4 elements."); - } - - ::memcpy(scale_orientation, tvec.data(), sizeof(scale_orientation)); - - continue; - } - - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Group, ne); - } else { - ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. - // at this place new group mode created and made current, so we can name it. - if (!def.empty()) { - NodeElement_Cur->ID = def; - } - - // - // also set values specific to this type of group - // - // calculate transformation matrix - aiMatrix4x4::Translation(translation, matr); // T - aiMatrix4x4::Translation(center, tmatr); // C - matr *= tmatr; - aiMatrix4x4::Rotation(rotation[3], aiVector3D(rotation[0], rotation[1], rotation[2]), tmatr); // R - matr *= tmatr; - aiMatrix4x4::Rotation(scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr); // SR - matr *= tmatr; - aiMatrix4x4::Scaling(scale, tmatr); // S - matr *= tmatr; - aiMatrix4x4::Rotation(-scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr); // -SR - matr *= tmatr; - aiMatrix4x4::Translation(-center, tmatr); // -C - matr *= tmatr; - // and assign it - ((CX3DNodeElementGroup *)mNodeElementCur)->Transformation = matr; - // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. - - // for empty element exit from node in that place - if (mReader->isEmptyElement()) { - ParseHelper_Node_Exit(); - } - } // if(!use.empty()) else -} - -void X3DImporter::ParseNode_Grouping_TransformEnd() { - ParseHelper_Node_Exit(); // go up in scene graph -} - -void X3DImporter::ParseNode_Geometry2D_Arc2D() { - std::string def, use; - float endAngle = AI_MATH_HALF_PI_F; - float radius = 1; - float startAngle = 0; - X3DNodeElementBase *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("endAngle", endAngle, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("startAngle", startAngle, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Arc2D, ne); - } else { - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Arc2D, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - // create point list of geometry object and convert it to line set. - std::list tlist; - - GeometryHelper_Make_Arc2D(startAngle, endAngle, radius, 10, tlist); ///TODO: IME - AI_CONFIG for NumSeg - GeometryHelper_Extend_PointToLine(tlist, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices); - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 2; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Arc2D"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -// The ArcClose node specifies a portion of a circle whose center is at (0,0) and whose angles are measured starting at the positive x-axis and sweeping -// towards the positive y-axis. The end points of the arc specified are connected as defined by the closureType field. The radius field specifies the radius -// of the circle of which the arc is a portion. The arc extends from the startAngle counterclockwise to the endAngle. The value of radius shall be greater -// than zero. The values of startAngle and endAngle shall be in the range [-2pi, 2pi] radians (or the equivalent if a different default angle base unit has -// been specified). If startAngle and endAngle have the same value, a circle is specified and closureType is ignored. If the absolute difference between -// startAngle and endAngle is greater than or equal to 2pi, a complete circle is produced with no chord or radial line(s) drawn from the center. -// A closureType of "PIE" connects the end point to the start point by defining two straight line segments first from the end point to the center and then -// the center to the start point. A closureType of "CHORD" connects the end point to the start point by defining a straight line segment from the end point -// to the start point. Textures are applied individually to each face of the ArcClose2D. On the front (+Z) and back (-Z) faces of the ArcClose2D, when -// viewed from the +Z-axis, the texture is mapped onto each face with the same orientation as if the image were displayed normally in 2D. -void X3DImporter::ParseNode_Geometry2D_ArcClose2D() { - std::string def, use; - std::string closureType("PIE"); - float endAngle = AI_MATH_HALF_PI_F; - float radius = 1; - bool solid = false; - float startAngle = 0; - X3DNodeElementBase *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("closureType", closureType, mReader->getAttributeValue); - MACRO_ATTRREAD_CHECK_RET("endAngle", endAngle, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("startAngle", startAngle, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_ArcClose2D, ne); - } else { - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_ArcClose2D, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - ((CX3DImporter_NodeElement_Geometry2D *)ne)->Solid = solid; - // create point list of geometry object. - GeometryHelper_Make_Arc2D(startAngle, endAngle, radius, 10, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices); ///TODO: IME - AI_CONFIG for NumSeg - // add chord or two radiuses only if not a circle was defined - if (!((std::fabs(endAngle - startAngle) >= AI_MATH_TWO_PI_F) || (endAngle == startAngle))) { - std::list &vlist = ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices; // just short alias. - - if ((closureType == "PIE") || (closureType == "\"PIE\"")) - vlist.push_back(aiVector3D(0, 0, 0)); // center point - first radial line - else if ((closureType != "CHORD") && (closureType != "\"CHORD\"")) - Throw_IncorrectAttrValue("closureType"); - - vlist.push_back(*vlist.begin()); // arc first point - chord from first to last point of arc(if CHORD) or second radial line(if PIE). - } - - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices.size(); - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "ArcClose2D"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Geometry2D_Circle2D() { - std::string def, use; - float radius = 1; - X3DNodeElementBase *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Circle2D, ne); - } else { - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Circle2D, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - // create point list of geometry object and convert it to line set. - std::list tlist; - - GeometryHelper_Make_Arc2D(0, 0, radius, 10, tlist); ///TODO: IME - AI_CONFIG for NumSeg - GeometryHelper_Extend_PointToLine(tlist, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices); - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 2; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Circle2D"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -// The Disk2D node specifies a circular disk which is centred at (0, 0) in the local coordinate system. The outerRadius field specifies the radius of the -// outer dimension of the Disk2D. The innerRadius field specifies the inner dimension of the Disk2D. The value of outerRadius shall be greater than zero. -// The value of innerRadius shall be greater than or equal to zero and less than or equal to outerRadius. If innerRadius is zero, the Disk2D is completely -// filled. Otherwise, the area within the innerRadius forms a hole in the Disk2D. If innerRadius is equal to outerRadius, a solid circular line shall -// be drawn using the current line properties. Textures are applied individually to each face of the Disk2D. On the front (+Z) and back (-Z) faces of -// the Disk2D, when viewed from the +Z-axis, the texture is mapped onto each face with the same orientation as if the image were displayed normally in 2D. -void X3DImporter::ParseNode_Geometry2D_Disk2D() { - std::string def, use; - float innerRadius = 0; - float outerRadius = 1; - bool solid = false; - X3DNodeElementBase *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("innerRadius", innerRadius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("outerRadius", outerRadius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Disk2D, ne); - } else { - std::list tlist_o, tlist_i; - - if (innerRadius > outerRadius) Throw_IncorrectAttrValue("innerRadius"); - - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Disk2D, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - // create point list of geometry object. - ///TODO: IME - AI_CONFIG for NumSeg - GeometryHelper_Make_Arc2D(0, 0, outerRadius, 10, tlist_o); // outer circle - if (innerRadius == 0.0f) { // make filled disk - // in tlist_o we already have points of circle. just copy it and sign as polygon. - ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices = tlist_o; - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = tlist_o.size(); - } else if (innerRadius == outerRadius) { // make circle - // in tlist_o we already have points of circle. convert it to line set. - GeometryHelper_Extend_PointToLine(tlist_o, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices); - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 2; - } else { // make disk - std::list &vlist = ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices; // just short alias. - - GeometryHelper_Make_Arc2D(0, 0, innerRadius, 10, tlist_i); // inner circle - // - // create quad list from two point lists - // - if (tlist_i.size() < 2) throw DeadlyImportError("Disk2D. Not enough points for creating quad list."); // tlist_i and tlist_o has equal size. - - // add all quads except last - for (std::list::iterator it_i = tlist_i.begin(), it_o = tlist_o.begin(); it_i != tlist_i.end();) { - // do not forget - CCW direction - vlist.push_back(*it_i++); // 1st point - vlist.push_back(*it_o++); // 2nd point - vlist.push_back(*it_o); // 3rd point - vlist.push_back(*it_i); // 4th point - } - - // add last quad - vlist.push_back(*tlist_i.end()); // 1st point - vlist.push_back(*tlist_o.end()); // 2nd point - vlist.push_back(*tlist_o.begin()); // 3rd point - vlist.push_back(*tlist_o.begin()); // 4th point - - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 4; - } - - ((CX3DImporter_NodeElement_Geometry2D *)ne)->Solid = solid; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Disk2D"); - else - mNodeElementCur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Geometry2D_Polyline2D() { - std::string def, use; - std::list lineSegments; - X3DNodeElementBase *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_REF("lineSegments", lineSegments, XML_ReadNode_GetAttrVal_AsListVec2f); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Polyline2D, ne); - } else { - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Polyline2D, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - // - // convert read point list of geometry object to line set. - // - std::list tlist; - - // convert vec2 to vec3 - for (std::list::iterator it2 = lineSegments.begin(); it2 != lineSegments.end(); ++it2) - tlist.push_back(aiVector3D(it2->x, it2->y, 0)); - - // convert point set to line set - GeometryHelper_Extend_PointToLine(tlist, ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices); - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 2; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Polyline2D"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Geometry2D_Polypoint2D() { - std::string def, use; - std::list point; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_REF("point", point, XML_ReadNode_GetAttrVal_AsListVec2f); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Polypoint2D, ne); - } else { - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Polypoint2D, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - // convert vec2 to vec3 - for (std::list::iterator it2 = point.begin(); it2 != point.end(); ++it2) { - ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0)); - } - - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 1; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Polypoint2D"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Geometry2D_Rectangle2D() { - std::string def, use; - aiVector2D size(2, 2); - bool solid = false; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_REF("size", size, XML_ReadNode_GetAttrVal_AsVec2f); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Rectangle2D, ne); - } else { - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_Rectangle2D, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - float x1 = -size.x / 2.0f; - float x2 = size.x / 2.0f; - float y1 = -size.y / 2.0f; - float y2 = size.y / 2.0f; - std::list &vlist = ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices; // just short alias. - - vlist.push_back(aiVector3D(x2, y1, 0)); // 1st point - vlist.push_back(aiVector3D(x2, y2, 0)); // 2nd point - vlist.push_back(aiVector3D(x1, y2, 0)); // 3rd point - vlist.push_back(aiVector3D(x1, y1, 0)); // 4th point - ((CX3DImporter_NodeElement_Geometry2D *)ne)->Solid = solid; - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 4; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Rectangle2D"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Geometry2D_TriangleSet2D() { - std::string def, use; - bool solid = false; - std::list vertices; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_REF("vertices", vertices, XML_ReadNode_GetAttrVal_AsListVec2f); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_TriangleSet2D, ne); - } else { - if (vertices.size() % 3) throw DeadlyImportError("TriangleSet2D. Not enough points for defining triangle."); - - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry2D(CX3DImporter_NodeElement::ENET_TriangleSet2D, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - // convert vec2 to vec3 - for (std::list::iterator it2 = vertices.begin(); it2 != vertices.end(); ++it2) { - ((CX3DImporter_NodeElement_Geometry2D *)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0)); - } - - ((CX3DImporter_NodeElement_Geometry2D *)ne)->Solid = solid; - ((CX3DImporter_NodeElement_Geometry2D *)ne)->NumIndices = 3; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "TriangleSet2D"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -// The Box node specifies a rectangular parallelepiped box centred at (0, 0, 0) in the local coordinate system and aligned with the local coordinate axes. -// By default, the box measures 2 units in each dimension, from -1 to +1. The size field specifies the extents of the box along the X-, Y-, and Z-axes -// respectively and each component value shall be greater than zero. -void X3DImporter::ParseNode_Geometry3D_Box() { - std::string def, use; - bool solid = true; - aiVector3D size(2, 2, 2); - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_REF("size", size, XML_ReadNode_GetAttrVal_AsVec3f); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Box, ne); - } else { - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Box, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - GeometryHelper_MakeQL_RectParallelepiped(size, ((CX3DImporter_NodeElement_Geometry3D *)ne)->Vertices); // get quad list - ((CX3DImporter_NodeElement_Geometry3D *)ne)->Solid = solid; - ((CX3DImporter_NodeElement_Geometry3D *)ne)->NumIndices = 4; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Box"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Geometry3D_Cone() { - std::string use, def; - bool bottom = true; - float bottomRadius = 1; - float height = 2; - bool side = true; - bool solid = true; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("side", side, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("bottom", bottom, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("height", height, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("bottomRadius", bottomRadius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Cone, ne); - } else { - const unsigned int tess = 30; ///TODO: IME tessellation factor through ai_property - - std::vector tvec; // temp array for vertices. - - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Cone, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - // make cone or parts according to flags. - if (side) { - StandardShapes::MakeCone(height, 0, bottomRadius, tess, tvec, !bottom); - } else if (bottom) { - StandardShapes::MakeCircle(bottomRadius, tess, tvec); - height = -(height / 2); - for (std::vector::iterator it = tvec.begin(); it != tvec.end(); ++it) - it->y = height; // y - because circle made in oXZ. - } - - // copy data from temp array - for (std::vector::iterator it = tvec.begin(); it != tvec.end(); ++it) - ((CX3DImporter_NodeElement_Geometry3D *)ne)->Vertices.push_back(*it); - - ((CX3DImporter_NodeElement_Geometry3D *)ne)->Solid = solid; - ((CX3DImporter_NodeElement_Geometry3D *)ne)->NumIndices = 3; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Cone"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Geometry3D_Cylinder() { - std::string use, def; - bool bottom = true; - float height = 2; - float radius = 1; - bool side = true; - bool solid = true; - bool top = true; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("bottom", bottom, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("top", top, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("side", side, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("height", height, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Cylinder, ne); - } else { - const unsigned int tess = 30; ///TODO: IME tessellation factor through ai_property - - std::vector tside; // temp array for vertices of side. - std::vector tcir; // temp array for vertices of circle. - - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Cylinder, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - // make cilynder or parts according to flags. - if (side) StandardShapes::MakeCone(height, radius, radius, tess, tside, true); - - height /= 2; // height defined for whole cylinder, when creating top and bottom circle we are using just half of height. - if (top || bottom) StandardShapes::MakeCircle(radius, tess, tcir); - // copy data from temp arrays - std::list &vlist = ((CX3DImporter_NodeElement_Geometry3D *)ne)->Vertices; // just short alias. - - for (std::vector::iterator it = tside.begin(); it != tside.end(); ++it) - vlist.push_back(*it); - - if (top) { - for (std::vector::iterator it = tcir.begin(); it != tcir.end(); ++it) { - (*it).y = height; // y - because circle made in oXZ. - vlist.push_back(*it); - } - } // if(top) - - if (bottom) { - for (std::vector::iterator it = tcir.begin(); it != tcir.end(); ++it) { - (*it).y = -height; // y - because circle made in oXZ. - vlist.push_back(*it); - } - } // if(top) - - ((CX3DImporter_NodeElement_Geometry3D *)ne)->Solid = solid; - ((CX3DImporter_NodeElement_Geometry3D *)ne)->NumIndices = 3; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Cylinder"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -// -// ColorNormalTexCoordContentModel can contain Color (or ColorRGBA), Normal and TextureCoordinate, in any order. No more than one instance of any single -// node type is allowed. A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. -// -// The ElevationGrid node specifies a uniform rectangular grid of varying height in the Y=0 plane of the local coordinate system. The geometry is described -// by a scalar array of height values that specify the height of a surface above each point of the grid. The xDimension and zDimension fields indicate -// the number of elements of the grid height array in the X and Z directions. Both xDimension and zDimension shall be greater than or equal to zero. -// If either the xDimension or the zDimension is less than two, the ElevationGrid contains no quadrilaterals. -void X3DImporter::ParseNode_Geometry3D_ElevationGrid() { - std::string use, def; - bool ccw = true; - bool colorPerVertex = true; - float creaseAngle = 0; - std::vector height; - bool normalPerVertex = true; - bool solid = true; - int32_t xDimension = 0; - float xSpacing = 1; - int32_t zDimension = 0; - float zSpacing = 1; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("height", height, XML_ReadNode_GetAttrVal_AsArrF); - MACRO_ATTRREAD_CHECK_RET("xDimension", xDimension, XML_ReadNode_GetAttrVal_AsI32); - MACRO_ATTRREAD_CHECK_RET("xSpacing", xSpacing, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("zDimension", zDimension, XML_ReadNode_GetAttrVal_AsI32); - MACRO_ATTRREAD_CHECK_RET("zSpacing", zSpacing, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_ElevationGrid, ne); - } else { - if ((xSpacing == 0.0f) || (zSpacing == 0.0f)) throw DeadlyImportError("Spacing in must be grater than zero."); - if ((xDimension <= 0) || (zDimension <= 0)) throw DeadlyImportError("Dimension in must be grater than zero."); - if ((size_t)(xDimension * zDimension) != height.size()) Throw_IncorrectAttrValue("Heights count must be equal to \"xDimension * zDimension\""); - - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_ElevationGrid(CX3DImporter_NodeElement::ENET_ElevationGrid, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - CX3DImporter_NodeElement_ElevationGrid &grid_alias = *((CX3DImporter_NodeElement_ElevationGrid *)ne); // create alias for conveience - - { // create grid vertices list - std::vector::const_iterator he_it = height.begin(); - - for (int32_t zi = 0; zi < zDimension; zi++) // rows - { - for (int32_t xi = 0; xi < xDimension; xi++) // columns - { - aiVector3D tvec(xSpacing * xi, *he_it, zSpacing * zi); - - grid_alias.Vertices.push_back(tvec); - ++he_it; - } - } - } // END: create grid vertices list - // - // create faces list. In "coordIdx" format - // - // check if we have quads - if ((xDimension < 2) || (zDimension < 2)) // only one element in dimension is set, create line set. - { - ((CX3DImporter_NodeElement_ElevationGrid *)ne)->NumIndices = 2; // will be holded as line set. - for (size_t i = 0, i_e = (grid_alias.Vertices.size() - 1); i < i_e; i++) { - grid_alias.CoordIdx.push_back(static_cast(i)); - grid_alias.CoordIdx.push_back(static_cast(i + 1)); - grid_alias.CoordIdx.push_back(-1); - } - } else // two or more elements in every dimension is set. create quad set. - { - ((CX3DImporter_NodeElement_ElevationGrid *)ne)->NumIndices = 4; - for (int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++) // rows - { - for (int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++) // columns - { - // points direction in face. - if (ccw) { - // CCW: - // 3 2 - // 0 1 - grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi); - grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1)); - grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1)); - grid_alias.CoordIdx.push_back(fzi * xDimension + fxi); - } else { - // CW: - // 0 1 - // 3 2 - grid_alias.CoordIdx.push_back(fzi * xDimension + fxi); - grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1)); - grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1)); - grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi); - } // if(ccw) else - - grid_alias.CoordIdx.push_back(-1); - } // for(int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++) - } // for(int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++) - } // if((xDimension < 2) || (zDimension < 2)) else - - grid_alias.ColorPerVertex = colorPerVertex; - grid_alias.NormalPerVertex = normalPerVertex; - grid_alias.CreaseAngle = creaseAngle; - grid_alias.Solid = solid; - // check for child nodes - if (!mReader->isEmptyElement()) { - ParseHelper_Node_Enter(ne); - MACRO_NODECHECK_LOOPBEGIN("ElevationGrid"); - // check for X3DComposedGeometryNodes - if (XML_CheckNode_NameEqual("Color")) { - ParseNode_Rendering_Color(); - continue; - } - if (XML_CheckNode_NameEqual("ColorRGBA")) { - ParseNode_Rendering_ColorRGBA(); - continue; - } - if (XML_CheckNode_NameEqual("Normal")) { - ParseNode_Rendering_Normal(); - continue; - } - if (XML_CheckNode_NameEqual("TextureCoordinate")) { - ParseNode_Texturing_TextureCoordinate(); - continue; - } - // check for X3DMetadataObject - if (!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("ElevationGrid"); - - MACRO_NODECHECK_LOOPEND("ElevationGrid"); - ParseHelper_Node_Exit(); - } // if(!mReader->isEmptyElement()) - else { - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - } // if(!mReader->isEmptyElement()) else - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -template -static void GeometryHelper_Extrusion_CurveIsClosed(std::vector &pCurve, const bool pDropTail, const bool pRemoveLastPoint, bool &pCurveIsClosed) { - size_t cur_sz = pCurve.size(); - - pCurveIsClosed = false; - // for curve with less than four points checking is have no sense, - if (cur_sz < 4) return; - - for (size_t s = 3, s_e = cur_sz; s < s_e; s++) { - // search for first point of duplicated part. - if (pCurve[0] == pCurve[s]) { - bool found = true; - - // check if tail(indexed by b2) is duplicate of head(indexed by b1). - for (size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++) { - if (pCurve[b1] != pCurve[b2]) { // points not match: clear flag and break loop. - found = false; - - break; - } - } // for(size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++) - - // if duplicate tail is found then drop or not it depending on flags. - if (found) { - pCurveIsClosed = true; - if (pDropTail) { - if (!pRemoveLastPoint) s++; // prepare value for iterator's arithmetics. - - pCurve.erase(pCurve.begin() + s, pCurve.end()); // remove tail - } - - break; - } // if(found) - } // if(pCurve[0] == pCurve[s]) - } // for(size_t s = 3, s_e = (cur_sz - 1); s < s_e; s++) -} - -static aiVector3D GeometryHelper_Extrusion_GetNextY(const size_t pSpine_PointIdx, const std::vector &pSpine, const bool pSpine_Closed) { - const size_t spine_idx_last = pSpine.size() - 1; - aiVector3D tvec; - - if ((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) // at first special cases - { - if (pSpine_Closed) { // If the spine curve is closed: The SCP for the first and last points is the same and is found using (spine[1] - spine[n - 2]) to compute the Y-axis. - // As we even for closed spine curve last and first point in pSpine are not the same: duplicates(spine[n - 1] which are equivalent to spine[0]) - // in tail are removed. - // So, last point in pSpine is a spine[n - 2] - tvec = pSpine[1] - pSpine[spine_idx_last]; - } else if (pSpine_PointIdx == 0) { // The Y-axis used for the first point is the vector from spine[0] to spine[1] - tvec = pSpine[1] - pSpine[0]; - } else { // The Y-axis used for the last point it is the vector from spine[n-2] to spine[n-1]. In our case(see above about dropping tail) spine[n - 1] is - // the spine[0]. - tvec = pSpine[spine_idx_last] - pSpine[spine_idx_last - 1]; - } - } // if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) - else { // For all points other than the first or last: The Y-axis for spine[i] is found by normalizing the vector defined by (spine[i+1] - spine[i-1]). - tvec = pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx - 1]; - } // if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) else - - return tvec.Normalize(); -} - -static aiVector3D GeometryHelper_Extrusion_GetNextZ(const size_t pSpine_PointIdx, const std::vector &pSpine, const bool pSpine_Closed, - const aiVector3D pVecZ_Prev) { - const aiVector3D zero_vec(0); - const size_t spine_idx_last = pSpine.size() - 1; - - aiVector3D tvec; - - // at first special cases - if (pSpine.size() < 3) // spine have not enough points for vector calculations. - { - tvec.Set(0, 0, 1); - } else if (pSpine_PointIdx == 0) // special case: first point - { - if (pSpine_Closed) // for calculating use previous point in curve s[n - 2]. In list it's a last point, because point s[n - 1] was removed as duplicate. - { - tvec = (pSpine[1] - pSpine[0]) ^ (pSpine[spine_idx_last] - pSpine[0]); - } else // for not closed curve first and next point(s[0] and s[1]) has the same vector Z. - { - bool found = false; - - // As said: "If the Z-axis of the first point is undefined (because the spine is not closed and the first two spine segments are collinear) - // then the Z-axis for the first spine point with a defined Z-axis is used." - // Walk through spine and find Z. - for (size_t next_point = 2; (next_point <= spine_idx_last) && !found; next_point++) { - // (pSpine[2] - pSpine[1]) ^ (pSpine[0] - pSpine[1]) - tvec = (pSpine[next_point] - pSpine[next_point - 1]) ^ (pSpine[next_point - 2] - pSpine[next_point - 1]); - found = !tvec.Equal(zero_vec); - } - - // if entire spine are collinear then use OZ axis. - if (!found) tvec.Set(0, 0, 1); - } // if(pSpine_Closed) else - } // else if(pSpine_PointIdx == 0) - else if (pSpine_PointIdx == spine_idx_last) // special case: last point - { - if (pSpine_Closed) { // do not forget that real last point s[n - 1] is removed as duplicated. And in this case we are calculating vector Z for point s[n - 2]. - tvec = (pSpine[0] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]); - // if taken spine vectors are collinear then use previous vector Z. - if (tvec.Equal(zero_vec)) tvec = pVecZ_Prev; - } else { // vector Z for last point of not closed curve is previous vector Z. - tvec = pVecZ_Prev; - } - } else // regular point - { - tvec = (pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]); - // if taken spine vectors are collinear then use previous vector Z. - if (tvec.Equal(zero_vec)) tvec = pVecZ_Prev; - } - - // After determining the Z-axis, its dot product with the Z-axis of the previous spine point is computed. If this value is negative, the Z-axis - // is flipped (multiplied by -1). - if ((tvec * pVecZ_Prev) < 0) tvec = -tvec; - - return tvec.Normalize(); -} - -// -void X3DImporter::ParseNode_Geometry3D_Extrusion() { - std::string use, def; - bool beginCap = true; - bool ccw = true; - bool convex = true; - float creaseAngle = 0; - std::vector crossSection; - bool endCap = true; - std::vector orientation; - std::vector scale; - bool solid = true; - std::vector spine; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("beginCap", beginCap, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("convex", convex, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("crossSection", crossSection, XML_ReadNode_GetAttrVal_AsArrVec2f); - MACRO_ATTRREAD_CHECK_RET("endCap", endCap, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_REF("orientation", orientation, XML_ReadNode_GetAttrVal_AsArrF); - MACRO_ATTRREAD_CHECK_REF("scale", scale, XML_ReadNode_GetAttrVal_AsArrVec2f); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_REF("spine", spine, XML_ReadNode_GetAttrVal_AsArrVec3f); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Extrusion, ne); - } else { - // - // check if default values must be assigned - // - if (spine.size() == 0) { - spine.resize(2); - spine[0].Set(0, 0, 0), spine[1].Set(0, 1, 0); - } else if (spine.size() == 1) { - throw DeadlyImportError("ParseNode_Geometry3D_Extrusion. Spine must have at least two points."); - } - - if (crossSection.size() == 0) { - crossSection.resize(5); - crossSection[0].Set(1, 1), crossSection[1].Set(1, -1), crossSection[2].Set(-1, -1), crossSection[3].Set(-1, 1), crossSection[4].Set(1, 1); - } - - { // orientation - size_t ori_size = orientation.size() / 4; - - if (ori_size < spine.size()) { - float add_ori[4]; // values that will be added - - if (ori_size == 1) // if "orientation" has one element(means one MFRotation with four components) then use it value for all spine points. - { - add_ori[0] = orientation[0], add_ori[1] = orientation[1], add_ori[2] = orientation[2], add_ori[3] = orientation[3]; - } else // else - use default values - { - add_ori[0] = 0, add_ori[1] = 0, add_ori[2] = 1, add_ori[3] = 0; - } - - orientation.reserve(spine.size() * 4); - for (size_t i = 0, i_e = (spine.size() - ori_size); i < i_e; i++) - orientation.push_back(add_ori[0]), orientation.push_back(add_ori[1]), orientation.push_back(add_ori[2]), orientation.push_back(add_ori[3]); - } - - if (orientation.size() % 4) throw DeadlyImportError("Attribute \"orientation\" in must has multiple four quantity of numbers."); - } // END: orientation - - { // scale - if (scale.size() < spine.size()) { - aiVector2D add_sc; - - if (scale.size() == 1) // if "scale" has one element then use it value for all spine points. - add_sc = scale[0]; - else // else - use default values - add_sc.Set(1, 1); - - scale.reserve(spine.size()); - for (size_t i = 0, i_e = (spine.size() - scale.size()); i < i_e; i++) - scale.push_back(add_sc); - } - } // END: scale - // - // create and if needed - define new geometry object. - // - ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_Extrusion, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - CX3DImporter_NodeElement_IndexedSet &ext_alias = *((CX3DImporter_NodeElement_IndexedSet *)ne); // create alias for conveience - // assign part of input data - ext_alias.CCW = ccw; - ext_alias.Convex = convex; - ext_alias.CreaseAngle = creaseAngle; - ext_alias.Solid = solid; - - // - // How we done it at all? - // 1. At first we will calculate array of basises for every point in spine(look SCP in ISO-dic). Also "orientation" vector - // are applied vor every basis. - // 2. After that we can create array of point sets: which are scaled, transferred to basis of relative basis and at final translated to real position - // using relative spine point. - // 3. Next step is creating CoordIdx array(do not forget "-1" delimiter). While creating CoordIdx also created faces for begin and end caps, if - // needed. While createing CootdIdx is taking in account CCW flag. - // 4. The last step: create Vertices list. - // - bool spine_closed; // flag: true if spine curve is closed. - bool cross_closed; // flag: true if cross curve is closed. - std::vector basis_arr; // array of basises. ROW_a - X, ROW_b - Y, ROW_c - Z. - std::vector> pointset_arr; // array of point sets: cross curves. - - // detect closed curves - GeometryHelper_Extrusion_CurveIsClosed(crossSection, true, true, cross_closed); // true - drop tail, true - remove duplicate end. - GeometryHelper_Extrusion_CurveIsClosed(spine, true, true, spine_closed); // true - drop tail, true - remove duplicate end. - // If both cap are requested and spine curve is closed then we can make only one cap. Because second cap will be the same surface. - if (spine_closed) { - beginCap |= endCap; - endCap = false; - } - - { // 1. Calculate array of basises. - aiMatrix4x4 rotmat; - aiVector3D vecX(0), vecY(0), vecZ(0); - - basis_arr.resize(spine.size()); - for (size_t i = 0, i_e = spine.size(); i < i_e; i++) { - aiVector3D tvec; - - // get axises of basis. - vecY = GeometryHelper_Extrusion_GetNextY(i, spine, spine_closed); - vecZ = GeometryHelper_Extrusion_GetNextZ(i, spine, spine_closed, vecZ); - vecX = (vecY ^ vecZ).Normalize(); - // get rotation matrix and apply "orientation" to basis - aiMatrix4x4::Rotation(orientation[i * 4 + 3], aiVector3D(orientation[i * 4], orientation[i * 4 + 1], orientation[i * 4 + 2]), rotmat); - tvec = vecX, tvec *= rotmat, basis_arr[i].a1 = tvec.x, basis_arr[i].a2 = tvec.y, basis_arr[i].a3 = tvec.z; - tvec = vecY, tvec *= rotmat, basis_arr[i].b1 = tvec.x, basis_arr[i].b2 = tvec.y, basis_arr[i].b3 = tvec.z; - tvec = vecZ, tvec *= rotmat, basis_arr[i].c1 = tvec.x, basis_arr[i].c2 = tvec.y, basis_arr[i].c3 = tvec.z; - } // for(size_t i = 0, i_e = spine.size(); i < i_e; i++) - } // END: 1. Calculate array of basises - - { // 2. Create array of point sets. - aiMatrix4x4 scmat; - std::vector tcross(crossSection.size()); - - pointset_arr.resize(spine.size()); - for (size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++) { - aiVector3D tc23vec; - - tc23vec.Set(scale[spi].x, 0, scale[spi].y); - aiMatrix4x4::Scaling(tc23vec, scmat); - for (size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++) { - aiVector3D tvecX, tvecY, tvecZ; - - tc23vec.Set(crossSection[cri].x, 0, crossSection[cri].y); - // apply scaling to point - tcross[cri] = scmat * tc23vec; - // - // transfer point to new basis - // calculate coordinate in new basis - tvecX.Set(basis_arr[spi].a1, basis_arr[spi].a2, basis_arr[spi].a3), tvecX *= tcross[cri].x; - tvecY.Set(basis_arr[spi].b1, basis_arr[spi].b2, basis_arr[spi].b3), tvecY *= tcross[cri].y; - tvecZ.Set(basis_arr[spi].c1, basis_arr[spi].c2, basis_arr[spi].c3), tvecZ *= tcross[cri].z; - // apply new coordinates and translate it to spine point. - tcross[cri] = tvecX + tvecY + tvecZ + spine[spi]; - } // for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; i++) - - pointset_arr[spi] = tcross; // store transferred point set - } // for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; i++) - } // END: 2. Create array of point sets. - - { // 3. Create CoordIdx. - // add caps if needed - if (beginCap) { - // add cap as polygon. vertices of cap are places at begin, so just add numbers from zero. - for (size_t i = 0, i_e = crossSection.size(); i < i_e; i++) - ext_alias.CoordIndex.push_back(static_cast(i)); - - // add delimiter - ext_alias.CoordIndex.push_back(-1); - } // if(beginCap) - - if (endCap) { - // add cap as polygon. vertices of cap are places at end, as for beginCap use just sequence of numbers but with offset. - size_t beg = (pointset_arr.size() - 1) * crossSection.size(); - - for (size_t i = beg, i_e = (beg + crossSection.size()); i < i_e; i++) - ext_alias.CoordIndex.push_back(static_cast(i)); - - // add delimiter - ext_alias.CoordIndex.push_back(-1); - } // if(beginCap) - - // add quads - for (size_t spi = 0, spi_e = (spine.size() - 1); spi <= spi_e; spi++) { - const size_t cr_sz = crossSection.size(); - const size_t cr_last = crossSection.size() - 1; - - size_t right_col; // hold index basis for points of quad placed in right column; - - if (spi != spi_e) - right_col = spi + 1; - else if (spine_closed) // if spine curve is closed then one more quad is needed: between first and last points of curve. - right_col = 0; - else - break; // if spine curve is not closed then break the loop, because spi is out of range for that type of spine. - - for (size_t cri = 0; cri < cr_sz; cri++) { - if (cri != cr_last) { - MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex, - static_cast(spi * cr_sz + cri), - static_cast(right_col * cr_sz + cri), - static_cast(right_col * cr_sz + cri + 1), - static_cast(spi * cr_sz + cri + 1)); - // add delimiter - ext_alias.CoordIndex.push_back(-1); - } else if (cross_closed) // if cross curve is closed then one more quad is needed: between first and last points of curve. - { - MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex, - static_cast(spi * cr_sz + cri), - static_cast(right_col * cr_sz + cri), - static_cast(right_col * cr_sz + 0), - static_cast(spi * cr_sz + 0)); - // add delimiter - ext_alias.CoordIndex.push_back(-1); - } - } // for(size_t cri = 0; cri < cr_sz; cri++) - } // for(size_t spi = 0, spi_e = (spine.size() - 2); spi < spi_e; spi++) - } // END: 3. Create CoordIdx. - - { // 4. Create vertices list. - // just copy all vertices - for (size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++) { - for (size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++) { - ext_alias.Vertices.push_back(pointset_arr[spi][cri]); - } - } - } // END: 4. Create vertices list. - //PrintVectorSet("Ext. CoordIdx", ext_alias.CoordIndex); - //PrintVectorSet("Ext. Vertices", ext_alias.Vertices); - // check for child nodes - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Extrusion"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -// -// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, -// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, -// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. -// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. -// -void X3DImporter::ParseNode_Geometry3D_IndexedFaceSet() { - std::string use, def; - bool ccw = true; - std::vector colorIndex; - bool colorPerVertex = true; - bool convex = true; - std::vector coordIndex; - float creaseAngle = 0; - std::vector normalIndex; - bool normalPerVertex = true; - bool solid = true; - std::vector texCoordIndex; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("ccw", ccw, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_REF("colorIndex", colorIndex, XML_ReadNode_GetAttrVal_AsArrI32); - MACRO_ATTRREAD_CHECK_RET("colorPerVertex", colorPerVertex, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("convex", convex, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_REF("coordIndex", coordIndex, XML_ReadNode_GetAttrVal_AsArrI32); - MACRO_ATTRREAD_CHECK_RET("creaseAngle", creaseAngle, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_REF("normalIndex", normalIndex, XML_ReadNode_GetAttrVal_AsArrI32); - MACRO_ATTRREAD_CHECK_RET("normalPerVertex", normalPerVertex, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_CHECK_REF("texCoordIndex", texCoordIndex, XML_ReadNode_GetAttrVal_AsArrI32); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_IndexedFaceSet, ne); - } else { - // check data - if (coordIndex.size() == 0) throw DeadlyImportError("IndexedFaceSet must contain not empty \"coordIndex\" attribute."); - - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_IndexedSet(CX3DImporter_NodeElement::ENET_IndexedFaceSet, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - CX3DImporter_NodeElement_IndexedSet &ne_alias = *((CX3DImporter_NodeElement_IndexedSet *)ne); - - ne_alias.CCW = ccw; - ne_alias.ColorIndex = colorIndex; - ne_alias.ColorPerVertex = colorPerVertex; - ne_alias.Convex = convex; - ne_alias.CoordIndex = coordIndex; - ne_alias.CreaseAngle = creaseAngle; - ne_alias.NormalIndex = normalIndex; - ne_alias.NormalPerVertex = normalPerVertex; - ne_alias.Solid = solid; - ne_alias.TexCoordIndex = texCoordIndex; - // check for child nodes - if (!mReader->isEmptyElement()) { - ParseHelper_Node_Enter(ne); - MACRO_NODECHECK_LOOPBEGIN("IndexedFaceSet"); - // check for X3DComposedGeometryNodes - if (XML_CheckNode_NameEqual("Color")) { - ParseNode_Rendering_Color(); - continue; - } - if (XML_CheckNode_NameEqual("ColorRGBA")) { - ParseNode_Rendering_ColorRGBA(); - continue; - } - if (XML_CheckNode_NameEqual("Coordinate")) { - ParseNode_Rendering_Coordinate(); - continue; - } - if (XML_CheckNode_NameEqual("Normal")) { - ParseNode_Rendering_Normal(); - continue; - } - if (XML_CheckNode_NameEqual("TextureCoordinate")) { - ParseNode_Texturing_TextureCoordinate(); - continue; - } - // check for X3DMetadataObject - if (!ParseHelper_CheckRead_X3DMetadataObject()) XML_CheckNode_SkipUnsupported("IndexedFaceSet"); - - MACRO_NODECHECK_LOOPEND("IndexedFaceSet"); - ParseHelper_Node_Exit(); - } // if(!mReader->isEmptyElement()) - else { - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - } - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - -// -void X3DImporter::ParseNode_Geometry3D_Sphere() { - std::string use, def; - ai_real radius = 1; - bool solid = true; - CX3DImporter_NodeElement *ne(nullptr); - - MACRO_ATTRREAD_LOOPBEG; - MACRO_ATTRREAD_CHECKUSEDEF_RET(def, use); - MACRO_ATTRREAD_CHECK_RET("radius", radius, XML_ReadNode_GetAttrVal_AsFloat); - MACRO_ATTRREAD_CHECK_RET("solid", solid, XML_ReadNode_GetAttrVal_AsBool); - MACRO_ATTRREAD_LOOPEND; - - // if "USE" defined then find already defined element. - if (!use.empty()) { - MACRO_USE_CHECKANDAPPLY(def, use, ENET_Sphere, ne); - } else { - const unsigned int tess = 3; ///TODO: IME tessellation factor through ai_property - - std::vector tlist; - - // create and if needed - define new geometry object. - ne = new CX3DImporter_NodeElement_Geometry3D(CX3DImporter_NodeElement::ENET_Sphere, NodeElement_Cur); - if (!def.empty()) ne->ID = def; - - StandardShapes::MakeSphere(tess, tlist); - // copy data from temp array and apply scale - for (std::vector::iterator it = tlist.begin(); it != tlist.end(); ++it) { - ((CX3DImporter_NodeElement_Geometry3D *)ne)->Vertices.push_back(*it * radius); - } - - ((CX3DImporter_NodeElement_Geometry3D *)ne)->Solid = solid; - ((CX3DImporter_NodeElement_Geometry3D *)ne)->NumIndices = 3; - // check for X3DMetadataObject childs. - if (!mReader->isEmptyElement()) - ParseNode_Metadata(ne, "Sphere"); - else - NodeElement_Cur->Child.push_back(ne); // add made object as child to current element - - NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph - } // if(!use.empty()) else -} - - - -void X3DImporter::readMetadataObject(XmlNode &node) { - const std::string &name = node.name(); - if (name == "MetadataBoolean") { - readMetadataBoolean(node, mNodeElementCur); - } else if (name == "MetadataDouble") { - readMetadataDouble(node, mNodeElementCur); - } else if (name == "MetadataFloat") { - readMetadataFloat(node, mNodeElementCur); - } else if (name == "MetadataInteger") { - readMetadataInteger(node, mNodeElementCur); - } else if (name == "MetadataSet") { - readMetadataSet(node, mNodeElementCur); - } else if (name == "MetadataString") { - readMetadataString(node, mNodeElementCur); - } -} - - -aiMatrix4x4 PostprocessHelper_Matrix_GlobalToCurrent() { - X3DNodeElementBase *cur_node = nullptr; - std::list matr; - aiMatrix4x4 out_matr; - - // starting walk from current element to root - cur_node = cur_node; - if (cur_node != nullptr) { - do { - // if cur_node is group then store group transformation matrix in list. - if (cur_node->Type == X3DNodeElementBase::ENET_Group) matr.push_back(((X3DNodeElementBase *)cur_node)->Transformation); - - cur_node = cur_node->Parent; - } while (cur_node != nullptr); - } - - // multiplicate all matrices in reverse order - for (std::list::reverse_iterator rit = matr.rbegin(); rit != matr.rend(); ++rit) - out_matr = out_matr * (*rit); - - return out_matr; -} - -void X3DImporter::PostprocessHelper_CollectMetadata(const CX3DImporter_NodeElement &pNodeElement, std::list &pList) const { - // walk through childs and find for metadata. - for (std::list::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); ++el_it) { - if (((*el_it)->Type == X3DElemType::ENET_MetaBoolean) || ((*el_it)->Type == X3DElemType::ENET_MetaDouble) || - ((*el_it)->Type == X3DElemType::ENET_MetaFloat) || ((*el_it)->Type == X3DElemType::ENET_MetaInteger) || - ((*el_it)->Type == X3DElemType::ENET_MetaString)) { - pList.push_back(*el_it); - } else if ((*el_it)->Type == X3DElemType::ENET_MetaSet) { - PostprocessHelper_CollectMetadata(**el_it, pList); - } - } // for(std::list::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); el_it++) -} - -bool X3DImporter::PostprocessHelper_ElementIsMetadata(const CX3DImporter_NodeElement::EType pType) const { - if ((pType == X3DNodeElementBase::ENET_MetaBoolean) || (pType == X3DElemType::ENET_MetaDouble) || - (pType == X3DElemType::ENET_MetaFloat) || (pType == X3DElemType::ENET_MetaInteger) || - (pType == X3DElemType::ENET_MetaString) || (pType == X3DElemType::ENET_MetaSet)) { - return true; - } return false; } -bool X3DImporter::PostprocessHelper_ElementIsMesh(const CX3DImporter_NodeElement::EType pType) const { - if ((pType == X3DElemType::ENET_Arc2D) || (pType == X3DElemType::ENET_ArcClose2D) || - (pType == X3DElemType::ENET_Box) || (pType == X3DElemType::ENET_Circle2D) || - (pType == X3DElemType::ENET_Cone) || (pType == X3DElemType::ENET_Cylinder) || - (pType == X3DElemType::ENET_Disk2D) || (pType == X3DElemType::ENET_ElevationGrid) || - (pType == X3DElemType::ENET_Extrusion) || (pType == X3DElemType::ENET_IndexedFaceSet) || - (pType == X3DElemType::ENET_IndexedLineSet) || (pType == X3DElemType::ENET_IndexedTriangleFanSet) || - (pType == X3DElemType::ENET_IndexedTriangleSet) || (pType == X3DElemType::ENET_IndexedTriangleStripSet) || - (pType == X3DElemType::ENET_PointSet) || (pType == X3DElemType::ENET_LineSet) || - (pType == X3DElemType::ENET_Polyline2D) || (pType == X3DElemType::ENET_Polypoint2D) || - (pType == X3DElemType::ENET_Rectangle2D) || (pType == X3DElemType::ENET_Sphere) || - (pType == X3DElemType::ENET_TriangleFanSet) || (pType == X3DElemType::ENET_TriangleSet) || - (pType == X3DElemType::ENET_TriangleSet2D) || (pType == X3DElemType::ENET_TriangleStripSet)) { - return true; +bool X3DImporter::FindNodeElement_FromNode(X3DNodeElementBase *pStartNode, const std::string &pID, + const X3DElemType pType, X3DNodeElementBase **pElement) { + bool found = false; // flag: true - if requested element is found. + + // Check if pStartNode - this is the element, we are looking for. + if ((pStartNode->Type == pType) && (pStartNode->ID == pID)) { + found = true; + if (pElement != nullptr) { + *pElement = pStartNode; + } + + goto fne_fn_end; + } // if((pStartNode->Type() == pType) && (pStartNode->ID() == pID)) + + // Check childs of pStartNode. + for (std::list::iterator ch_it = pStartNode->Children.begin(); ch_it != pStartNode->Children.end(); ++ch_it) { + found = FindNodeElement_FromNode(*ch_it, pID, pType, pElement); + if (found) { + break; + } + } // for(std::list::iterator ch_it = it->Children.begin(); ch_it != it->Children.end(); ch_it++) + +fne_fn_end: + + return found; +} + +bool X3DImporter::FindNodeElement(const std::string &pID, const X3DElemType pType, X3DNodeElementBase **pElement) { + X3DNodeElementBase *tnd = mNodeElementCur; // temporary pointer to node. + bool static_search = false; // flag: true if searching in static node. + + // At first check if we have deal with static node. Go up through parent nodes and check flag. + while (tnd != nullptr) { + if (tnd->Type == X3DElemType::ENET_Group) { + if (((X3DNodeElementGroup *)tnd)->Static) { + static_search = true; // Flag found, stop walking up. Node with static flag will holded in tnd variable. + break; + } + } + + tnd = tnd->Parent; // go up in graph. + } // while (tnd != nullptr) + + // at now call appropriate search function. + if (static_search) { + return FindNodeElement_FromNode(tnd, pID, pType, pElement); } else { - return false; + return FindNodeElement_FromRoot(pID, pType, pElement); } } -void X3DImporter::Postprocess_BuildLight(const CX3DImporter_NodeElement &pNodeElement, std::list &pSceneLightList) const { - const CX3DImporter_NodeElement_Light &ne = *((CX3DImporter_NodeElement_Light *)&pNodeElement); - aiMatrix4x4 transform_matr = PostprocessHelper_Matrix_GlobalToCurrent(); - aiLight *new_light = new aiLight; +/*********************************************************************************************************************************************/ +/************************************************************ Functions: parse set ***********************************************************/ +/*********************************************************************************************************************************************/ - new_light->mName = ne.ID; - new_light->mColorAmbient = ne.Color * ne.AmbientIntensity; - new_light->mColorDiffuse = ne.Color * ne.Intensity; - new_light->mColorSpecular = ne.Color * ne.Intensity; - switch (pNodeElement.Type) { - case CX3DImporter_NodeElement::ENET_DirectionalLight: - new_light->mType = aiLightSource_DIRECTIONAL; - new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr; +void X3DImporter::ParseHelper_Group_Begin(const bool pStatic) { + X3DNodeElementGroup *new_group = new X3DNodeElementGroup(mNodeElementCur, pStatic); // create new node with current node as parent. - break; - case CX3DImporter_NodeElement::ENET_PointLight: - new_light->mType = aiLightSource_POINT; - new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr; - new_light->mAttenuationConstant = ne.Attenuation.x; - new_light->mAttenuationLinear = ne.Attenuation.y; - new_light->mAttenuationQuadratic = ne.Attenuation.z; - - break; - case CX3DImporter_NodeElement::ENET_SpotLight: - new_light->mType = aiLightSource_SPOT; - new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr; - new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr; - new_light->mAttenuationConstant = ne.Attenuation.x; - new_light->mAttenuationLinear = ne.Attenuation.y; - new_light->mAttenuationQuadratic = ne.Attenuation.z; - new_light->mAngleInnerCone = ne.BeamWidth; - new_light->mAngleOuterCone = ne.CutOffAngle; - - break; - default: - throw DeadlyImportError("Postprocess_BuildLight. Unknown type of light: " + to_string(pNodeElement.Type) + "."); + // if we are adding not the root element then add new element to current element child list. + if (mNodeElementCur != nullptr) { + mNodeElementCur->Children.push_back(new_group); } - pSceneLightList.push_back(new_light); + NodeElement_List.push_back(new_group); // it's a new element - add it to list. + mNodeElementCur = new_group; // switch current element to new one. } -void X3DImporter::Postprocess_BuildMaterial(const CX3DImporter_NodeElement &pNodeElement, aiMaterial **pMaterial) const { - // check argument - if (pMaterial == nullptr) throw DeadlyImportError("Postprocess_BuildMaterial. pMaterial is nullptr."); - if (*pMaterial != nullptr) throw DeadlyImportError("Postprocess_BuildMaterial. *pMaterial must be nullptr."); +void X3DImporter::ParseHelper_Node_Enter(X3DNodeElementBase *pNode) { + mNodeElementCur->Children.push_back(pNode); // add new element to current element child list. + mNodeElementCur = pNode; // switch current element to new one. +} - *pMaterial = new aiMaterial; - aiMaterial &taimat = **pMaterial; // creating alias for convenience. - - // at this point pNodeElement point to node. Walk through childs and add all stored data. - for (std::list::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); ++el_it) { - if ((*el_it)->Type == CX3DImporter_NodeElement::ENET_Material) { - aiColor3D tcol3; - float tvalf; - CX3DImporter_NodeElement_Material &tnemat = *((CX3DImporter_NodeElement_Material *)*el_it); - - tcol3.r = tnemat.AmbientIntensity, tcol3.g = tnemat.AmbientIntensity, tcol3.b = tnemat.AmbientIntensity; - taimat.AddProperty(&tcol3, 1, AI_MATKEY_COLOR_AMBIENT); - taimat.AddProperty(&tnemat.DiffuseColor, 1, AI_MATKEY_COLOR_DIFFUSE); - taimat.AddProperty(&tnemat.EmissiveColor, 1, AI_MATKEY_COLOR_EMISSIVE); - taimat.AddProperty(&tnemat.SpecularColor, 1, AI_MATKEY_COLOR_SPECULAR); - tvalf = 1; - taimat.AddProperty(&tvalf, 1, AI_MATKEY_SHININESS_STRENGTH); - taimat.AddProperty(&tnemat.Shininess, 1, AI_MATKEY_SHININESS); - tvalf = 1.0f - tnemat.Transparency; - taimat.AddProperty(&tvalf, 1, AI_MATKEY_OPACITY); - } // if((*el_it)->Type == CX3DImporter_NodeElement::ENET_Material) - else if ((*el_it)->Type == CX3DImporter_NodeElement::ENET_ImageTexture) { - CX3DImporter_NodeElement_ImageTexture &tnetex = *((CX3DImporter_NodeElement_ImageTexture *)*el_it); - aiString url_str(tnetex.URL.c_str()); - int mode = aiTextureOp_Multiply; - - taimat.AddProperty(&url_str, AI_MATKEY_TEXTURE_DIFFUSE(0)); - taimat.AddProperty(&tnetex.RepeatS, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0)); - taimat.AddProperty(&tnetex.RepeatT, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0)); - taimat.AddProperty(&mode, 1, AI_MATKEY_TEXOP_DIFFUSE(0)); - } // else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_ImageTexture) - else if ((*el_it)->Type == CX3DImporter_NodeElement::ENET_TextureTransform) { - aiUVTransform trans; - CX3DImporter_NodeElement_TextureTransform &tnetextr = *((CX3DImporter_NodeElement_TextureTransform *)*el_it); - - trans.mTranslation = tnetextr.Translation - tnetextr.Center; - trans.mScaling = tnetextr.Scale; - trans.mRotation = tnetextr.Rotation; - taimat.AddProperty(&trans, 1, AI_MATKEY_UVTRANSFORM_DIFFUSE(0)); - } // else if((*el_it)->Type == CX3DImporter_NodeElement::ENET_TextureTransform) - } // for(std::list::const_iterator el_it = pNodeElement.Child.begin(); el_it != pNodeElement.Child.end(); el_it++) -} - -void X3DImporter::Postprocess_BuildMesh(const CX3DImporter_NodeElement &pNodeElement, aiMesh **pMesh) const { - // check argument - if (pMesh == nullptr) throw DeadlyImportError("Postprocess_BuildMesh. pMesh is nullptr."); - if (*pMesh != nullptr) throw DeadlyImportError("Postprocess_BuildMesh. *pMesh must be nullptr."); - - /************************************************************************************************************************************/ - /************************************************************ Geometry2D ************************************************************/ - /************************************************************************************************************************************/ - if ((pNodeElement.Type == CX3DImporter_NodeElement::ENET_Arc2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_ArcClose2D) || - (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Circle2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Disk2D) || - (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Polyline2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Polypoint2D) || - (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Rectangle2D) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet2D)) { - CX3DImporter_NodeElement_Geometry2D &tnemesh = *((CX3DImporter_NodeElement_Geometry2D *)&pNodeElement); // create alias for convenience - std::vector tarr; - - tarr.reserve(tnemesh.Vertices.size()); - for (std::list::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); ++it) - tarr.push_back(*it); - *pMesh = StandardShapes::MakeMesh(tarr, static_cast(tnemesh.NumIndices)); // create mesh from vertices using Assimp help. - - return; // mesh is build, nothing to do anymore. +void X3DImporter::ParseHelper_Node_Exit() { + // check if we can walk up. + if (mNodeElementCur != nullptr) { + mNodeElementCur = mNodeElementCur->Parent; } - /************************************************************************************************************************************/ - /************************************************************ Geometry3D ************************************************************/ - /************************************************************************************************************************************/ - // - // Predefined figures - // - if ((pNodeElement.Type == CX3DImporter_NodeElement::ENET_Box) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Cone) || - (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Cylinder) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Sphere)) { - CX3DImporter_NodeElement_Geometry3D &tnemesh = *((CX3DImporter_NodeElement_Geometry3D *)&pNodeElement); // create alias for convenience - std::vector tarr; - - tarr.reserve(tnemesh.Vertices.size()); - for (std::list::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); ++it) - tarr.push_back(*it); - - *pMesh = StandardShapes::MakeMesh(tarr, static_cast(tnemesh.NumIndices)); // create mesh from vertices using Assimp help. - - return; // mesh is build, nothing to do anymore. - } - // - // Parametric figures - // - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_ElevationGrid) { - CX3DImporter_NodeElement_ElevationGrid &tnemesh = *((CX3DImporter_NodeElement_ElevationGrid *)&pNodeElement); // create alias for convenience - - // at first create mesh from existing vertices. - *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIdx, tnemesh.Vertices); - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) - MeshGeometry_AddNormal(**pMesh, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, tnemesh.NormalPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) - MeshGeometry_AddTexCoord(**pMesh, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of ElevationGrid: " + to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_ElevationGrid) - // - // Indexed primitives sets - // - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedFaceSet) { - CX3DImporter_NodeElement_IndexedSet &tnemesh = *((CX3DImporter_NodeElement_IndexedSet *)&pNodeElement); // create alias for convenience - - // at first search for node and create mesh. - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); - } - } - - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, - tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - } // skip because already read when mesh created. - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) - MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, - tnemesh.NormalPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) - MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedFaceSet: " + to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedFaceSet) - - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedLineSet) { - CX3DImporter_NodeElement_IndexedSet &tnemesh = *((CX3DImporter_NodeElement_IndexedSet *)&pNodeElement); // create alias for convenience - - // at first search for node and create mesh. - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); - } - } - - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - ai_assert(*pMesh); - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, - tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - } // skip because already read when mesh created. - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedLineSet: " + to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedLineSet) - - if ((pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleSet) || - (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleFanSet) || - (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleStripSet)) { - CX3DImporter_NodeElement_IndexedSet &tnemesh = *((CX3DImporter_NodeElement_IndexedSet *)&pNodeElement); // create alias for convenience - - // at first search for node and create mesh. - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); - } - } - - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - ai_assert(*pMesh); - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, - tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - } // skip because already read when mesh created. - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) - MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, - tnemesh.NormalPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) - MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedTriangleSet or IndexedTriangleFanSet, or \ - IndexedTriangleStripSet: " + - to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if((pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleFanSet) || (pNodeElement.Type == CX3DImporter_NodeElement::ENET_IndexedTriangleStripSet)) - - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_Extrusion) { - CX3DImporter_NodeElement_IndexedSet &tnemesh = *((CX3DImporter_NodeElement_IndexedSet *)&pNodeElement); // create alias for convenience - - *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, tnemesh.Vertices); - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Extrusion) - - // - // Primitives sets - // - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_PointSet) { - CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience - - // at first search for node and create mesh. - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - std::vector vec_copy; - - vec_copy.reserve(((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.size()); - for (std::list::const_iterator it = ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.begin(); - it != ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.end(); ++it) { - vec_copy.push_back(*it); - } - - *pMesh = StandardShapes::MakeMesh(vec_copy, 1); - } - } - - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - ai_assert(*pMesh); - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, true); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, true); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - } // skip because already read when mesh created. - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of PointSet: " + to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_PointSet) - - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_LineSet) { - CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience - - // at first search for node and create mesh. - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); - } - } - - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - ai_assert(*pMesh); - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, true); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, true); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - } // skip because already read when mesh created. - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of LineSet: " + to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_LineSet) - - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleFanSet) { - CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience - - // at first search for node and create mesh. - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); - } - } - - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if (nullptr == *pMesh) { - break; - } - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - } // skip because already read when mesh created. - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) - MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, - tnemesh.NormalPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) - MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeFanSet: " + to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleFanSet) - - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet) { - CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience - - // at first search for node and create mesh. - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - std::vector vec_copy; - - vec_copy.reserve(((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.size()); - for (std::list::const_iterator it = ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.begin(); - it != ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value.end(); ++it) { - vec_copy.push_back(*it); - } - - *pMesh = StandardShapes::MakeMesh(vec_copy, 3); - } - } - - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - ai_assert(*pMesh); - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - } // skip because already read when mesh created. - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) - MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, - tnemesh.NormalPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) - MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeSet: " + to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleSet) - - if (pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleStripSet) { - CX3DImporter_NodeElement_Set &tnemesh = *((CX3DImporter_NodeElement_Set *)&pNodeElement); // create alias for convenience - - // at first search for node and create mesh. - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - *pMesh = GeometryHelper_MakeMesh(tnemesh.CoordIndex, ((CX3DImporter_NodeElement_Coordinate *)*ch_it)->Value); - } - } - - // copy additional information from children - for (std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) { - ai_assert(*pMesh); - if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Color) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_Color *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_ColorRGBA) - MeshGeometry_AddColor(**pMesh, ((CX3DImporter_NodeElement_ColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Coordinate) { - } // skip because already read when mesh created. - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_Normal) - MeshGeometry_AddNormal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((CX3DImporter_NodeElement_Normal *)*ch_it)->Value, - tnemesh.NormalPerVertex); - else if ((*ch_it)->Type == CX3DImporter_NodeElement::ENET_TextureCoordinate) - MeshGeometry_AddTexCoord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((CX3DImporter_NodeElement_TextureCoordinate *)*ch_it)->Value); - else - throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TriangleStripSet: " + to_string((*ch_it)->Type) + "."); - } // for(std::list::iterator ch_it = tnemesh.Child.begin(); ch_it != tnemesh.Child.end(); ++ch_it) - - return; // mesh is build, nothing to do anymore. - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_TriangleStripSet) - - throw DeadlyImportError("Postprocess_BuildMesh. Unknown mesh type: " + to_string(pNodeElement.Type) + "."); -} - -void X3DImporter::Postprocess_BuildNode(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode, std::list &pSceneMeshList, - std::list &pSceneMaterialList, std::list &pSceneLightList) const { - X3DElementList::const_iterator chit_begin = pNodeElement.Children.begin(); - X3DElementList::const_iterator chit_end = pNodeElement.Children.end(); - std::list SceneNode_Child; - std::list SceneNode_Mesh; - - // At first read all metadata - Postprocess_CollectMetadata(pNodeElement, pSceneNode); - // check if we have deal with grouping node. Which can contain transformation or switch - if (pNodeElement.Type == X3DElemType::ENET_Group) { - const CX3DNodeElementGroup &tne_group = *((CX3DNodeElementGroup*)&pNodeElement); // create alias for convenience - - pSceneNode.mTransformation = tne_group.Transformation; - if (tne_group.UseChoice) { - // If Choice is less than zero or greater than the number of nodes in the children field, nothing is chosen. - if ((tne_group.Choice < 0) || ((size_t)tne_group.Choice >= pNodeElement.Children.size())) { - chit_begin = pNodeElement.Children.end(); - chit_end = pNodeElement.Children.end(); - } else { - for (size_t i = 0; i < (size_t)tne_group.Choice; i++) - ++chit_begin; // forward iterator to chosen node. - - chit_end = chit_begin; - ++chit_end; // point end iterator to next element after chosen node. - } - } // if(tne_group.UseChoice) - } // if(pNodeElement.Type == CX3DImporter_NodeElement::ENET_Group) - - // Reserve memory for fast access and check children. - for (std::list::const_iterator it = chit_begin; it != chit_end; ++it) { // in this loop we do not read metadata because it's already read at begin. - if ((*it)->Type == X3DElemType::ENET_Group) { - // if child is group then create new node and do recursive call. - aiNode *new_node = new aiNode; - - new_node->mName = (*it)->ID; - new_node->mParent = &pSceneNode; - SceneNode_Child.push_back(new_node); - Postprocess_BuildNode(**it, *new_node, pSceneMeshList, pSceneMaterialList, pSceneLightList); - } else if ((*it)->Type == X3DElemType::ENET_Shape) { - // shape can contain only one geometry and one appearance nodes. - Postprocess_BuildShape(*((CX3DImporter_NodeElement_Shape *)*it), SceneNode_Mesh, pSceneMeshList, pSceneMaterialList); - } else if (((*it)->Type == X3DElemType::ENET_DirectionalLight) || ((*it)->Type == X3DElemType::ENET_PointLight) || - ((*it)->Type == X3DElemType::ENET_SpotLight)) { - Postprocess_BuildLight(*((X3DElemType *)*it), pSceneLightList); - } else if (!PostprocessHelper_ElementIsMetadata((*it)->Type)) // skip metadata - { - throw DeadlyImportError("Postprocess_BuildNode. Unknown type: " + to_string((*it)->Type) + "."); - } - } // for(std::list::const_iterator it = chit_begin; it != chit_end; it++) - - // copy data about children and meshes to aiNode. - if (!SceneNode_Child.empty()) { - std::list::const_iterator it = SceneNode_Child.begin(); - - pSceneNode.mNumChildren = static_cast(SceneNode_Child.size()); - pSceneNode.mChildren = new aiNode *[pSceneNode.mNumChildren]; - for (size_t i = 0; i < pSceneNode.mNumChildren; i++) - pSceneNode.mChildren[i] = *it++; - } - - if (!SceneNode_Mesh.empty()) { - std::list::const_iterator it = SceneNode_Mesh.begin(); - - pSceneNode.mNumMeshes = static_cast(SceneNode_Mesh.size()); - pSceneNode.mMeshes = new unsigned int[pSceneNode.mNumMeshes]; - for (size_t i = 0; i < pSceneNode.mNumMeshes; i++) - pSceneNode.mMeshes[i] = *it++; - } - - // that's all. return to previous deals -} - -void X3DImporter::Postprocess_BuildShape(const CX3DImporter_NodeElement_Shape &pShapeNodeElement, std::list &pNodeMeshInd, - std::list &pSceneMeshList, std::list &pSceneMaterialList) const { - aiMaterial *tmat = nullptr; - aiMesh *tmesh = nullptr; - X3DElemType mesh_type = X3DElemType::ENET_Invalid; - unsigned int mat_ind = 0; - - for (X3DElementList::const_iterator it = pShapeNodeElement.Children.begin(); it != pShapeNodeElement.Children.end(); ++it) { - if (PostprocessHelper_ElementIsMesh((*it)->Type)) { - Postprocess_BuildMesh(**it, &tmesh); - if (tmesh != nullptr) { - // if mesh successfully built then add data about it to arrays - pNodeMeshInd.push_back(static_cast(pSceneMeshList.size())); - pSceneMeshList.push_back(tmesh); - // keep mesh type. Need above for texture coordinate generation. - mesh_type = (*it)->Type; - } - } else if ((*it)->Type == X3DElemType::ENET_Appearance) { - Postprocess_BuildMaterial(**it, &tmat); - if (tmat != nullptr) { - // if material successfully built then add data about it to array - mat_ind = static_cast(pSceneMaterialList.size()); - pSceneMaterialList.push_back(tmat); - } - } - } // for(std::list::const_iterator it = pShapeNodeElement.Child.begin(); it != pShapeNodeElement.Child.end(); it++) - - // associate read material with read mesh. - if ((tmesh != nullptr) && (tmat != nullptr)) { - tmesh->mMaterialIndex = mat_ind; - // Check texture mapping. If material has texture but mesh has no texture coordinate then try to ask Assimp to generate texture coordinates. - if ((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0)) { - int32_t tm; - aiVector3D tvec3; - - switch (mesh_type) { - case X3DElemType::ENET_Box: - tm = aiTextureMapping_BOX; - break; - case X3DElemType::ENET_Cone: - case X3DElemType::ENET_Cylinder: - tm = aiTextureMapping_CYLINDER; - break; - case X3DElemType::ENET_Sphere: - tm = aiTextureMapping_SPHERE; - break; - default: - tm = aiTextureMapping_PLANE; - break; - } // switch(mesh_type) - - tmat->AddProperty(&tm, 1, AI_MATKEY_MAPPING_DIFFUSE(0)); - } // if((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0)) - } // if((tmesh != nullptr) && (tmat != nullptr)) -} - -void X3DImporter::Postprocess_CollectMetadata(const CX3DImporter_NodeElement &pNodeElement, aiNode &pSceneNode) const { - X3DElementList meta_list; - size_t meta_idx; - - PostprocessHelper_CollectMetadata(pNodeElement, meta_list); // find metadata in current node element. - if (!meta_list.empty()) { - if (pSceneNode.mMetaData != nullptr) { - throw DeadlyImportError("Postprocess. MetaData member in node are not nullptr. Something went wrong."); - } - - // copy collected metadata to output node. - pSceneNode.mMetaData = aiMetadata::Alloc(static_cast(meta_list.size())); - meta_idx = 0; - for (X3DElementList::const_iterator it = meta_list.begin(); it != meta_list.end(); ++it, ++meta_idx) { - CX3DImporter_NodeElement_Meta *cur_meta = (CX3DImporter_NodeElement_Meta *)*it; - - // due to limitations we can add only first element of value list. - // Add an element according to its type. - if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaBoolean) { - if (((CX3DImporter_NodeElement_MetaBoolean *)cur_meta)->Value.size() > 0) - pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, *(((CX3DImporter_NodeElement_MetaBoolean *)cur_meta)->Value.begin())); - } else if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaDouble) { - if (((CX3DImporter_NodeElement_MetaDouble *)cur_meta)->Value.size() > 0) - pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, (float)*(((CX3DImporter_NodeElement_MetaDouble *)cur_meta)->Value.begin())); - } else if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaFloat) { - if (((CX3DImporter_NodeElement_MetaFloat *)cur_meta)->Value.size() > 0) - pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, *(((CX3DImporter_NodeElement_MetaFloat *)cur_meta)->Value.begin())); - } else if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaInteger) { - if (((CX3DImporter_NodeElement_MetaInteger *)cur_meta)->Value.size() > 0) - pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, *(((CX3DImporter_NodeElement_MetaInteger *)cur_meta)->Value.begin())); - } else if ((*it)->Type == CX3DImporter_NodeElement::ENET_MetaString) { - if (((CX3DImporter_NodeElement_MetaString *)cur_meta)->Value.size() > 0) { - aiString tstr(((CX3DImporter_NodeElement_MetaString *)cur_meta)->Value.begin()->data()); - - pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, tstr); - } - } else { - throw DeadlyImportError("Postprocess. Unknown metadata type."); - } // if((*it)->Type == CX3DImporter_NodeElement::ENET_Meta*) else - } // for(std::list::const_iterator it = meta_list.begin(); it != meta_list.end(); it++) - } // if( !meta_list.empty() ) } #endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter.hpp b/code/AssetLib/X3D/X3DImporter.hpp index 559e4932f..c9509a035 100644 --- a/code/AssetLib/X3D/X3DImporter.hpp +++ b/code/AssetLib/X3D/X3DImporter.hpp @@ -41,6 +41,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef INCLUDED_AI_X3D_IMPORTER_H #define INCLUDED_AI_X3D_IMPORTER_H +#include "X3DImporter_Node.hpp" + #include #include #include @@ -51,7 +53,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include -#include namespace Assimp { @@ -68,6 +69,21 @@ inline void Throw_ConvertFail_Str2ArrF(const std::string &nodeName, const std::s "\" from string to array of floats."); } +inline void Throw_ConvertFail_Str2ArrD(const std::string &nodeName, const std::string &pAttrValue) { + throw DeadlyImportError("In <" + nodeName + "> failed to convert attribute value \"" + pAttrValue + + "\" from string to array of doubles."); +} + +inline void Throw_ConvertFail_Str2ArrB(const std::string &nodeName, const std::string &pAttrValue) { + throw DeadlyImportError("In <" + nodeName + "> failed to convert attribute value \"" + pAttrValue + + "\" from string to array of booleans."); +} + +inline void Throw_ConvertFail_Str2ArrI(const std::string &nodeName, const std::string &pAttrValue) { + throw DeadlyImportError("In <" + nodeName + "> failed to convert attribute value \"" + pAttrValue + + "\" from string to array of integers."); +} + inline void Throw_DEF_And_USE(const std::string &nodeName) { throw DeadlyImportError("\"DEF\" and \"USE\" can not be defined both in <" + nodeName + ">."); } @@ -225,410 +241,8 @@ inline void LogInfo(const std::string &message) { /// /// That's all for now. Enjoy /// -enum class X3DElemType { - ENET_Group, ///< Element has type "Group". - ENET_MetaBoolean, ///< Element has type "Metadata boolean". - ENET_MetaDouble, ///< Element has type "Metadata double". - ENET_MetaFloat, ///< Element has type "Metadata float". - ENET_MetaInteger, ///< Element has type "Metadata integer". - ENET_MetaSet, ///< Element has type "Metadata set". - ENET_MetaString, ///< Element has type "Metadata string". - ENET_Arc2D, ///< Element has type "Arc2D". - ENET_ArcClose2D, ///< Element has type "ArcClose2D". - ENET_Circle2D, ///< Element has type "Circle2D". - ENET_Disk2D, ///< Element has type "Disk2D". - ENET_Polyline2D, ///< Element has type "Polyline2D". - ENET_Polypoint2D, ///< Element has type "Polypoint2D". - ENET_Rectangle2D, ///< Element has type "Rectangle2D". - ENET_TriangleSet2D, ///< Element has type "TriangleSet2D". - ENET_Box, ///< Element has type "Box". - ENET_Cone, ///< Element has type "Cone". - ENET_Cylinder, ///< Element has type "Cylinder". - ENET_Sphere, ///< Element has type "Sphere". - ENET_ElevationGrid, ///< Element has type "ElevationGrid". - ENET_Extrusion, ///< Element has type "Extrusion". - ENET_Coordinate, ///< Element has type "Coordinate". - ENET_Normal, ///< Element has type "Normal". - ENET_TextureCoordinate, ///< Element has type "TextureCoordinate". - ENET_IndexedFaceSet, ///< Element has type "IndexedFaceSet". - ENET_IndexedLineSet, ///< Element has type "IndexedLineSet". - ENET_IndexedTriangleSet, ///< Element has type "IndexedTriangleSet". - ENET_IndexedTriangleFanSet, ///< Element has type "IndexedTriangleFanSet". - ENET_IndexedTriangleStripSet, ///< Element has type "IndexedTriangleStripSet". - ENET_LineSet, ///< Element has type "LineSet". - ENET_PointSet, ///< Element has type "PointSet". - ENET_TriangleSet, ///< Element has type "TriangleSet". - ENET_TriangleFanSet, ///< Element has type "TriangleFanSet". - ENET_TriangleStripSet, ///< Element has type "TriangleStripSet". - ENET_Color, ///< Element has type "Color". - ENET_ColorRGBA, ///< Element has type "ColorRGBA". - ENET_Shape, ///< Element has type "Shape". - ENET_Appearance, ///< Element has type "Appearance". - ENET_Material, ///< Element has type "Material". - ENET_ImageTexture, ///< Element has type "ImageTexture". - ENET_TextureTransform, ///< Element has type "TextureTransform". - ENET_DirectionalLight, ///< Element has type "DirectionalLight". - ENET_PointLight, ///< Element has type "PointLight". - ENET_SpotLight, ///< Element has type "SpotLight". - ENET_Invalid ///< Element has invalid type and possible contain invalid data. -}; - -struct X3DNodeElementBase { - X3DNodeElementBase *Parent; - std::string ID; - std::list Children; - X3DElemType Type; - -protected: - X3DNodeElementBase(X3DElemType type, X3DNodeElementBase *pParent) : - Type(type), Parent(pParent) { - // empty - } -}; - -/// This struct hold value. -struct CX3DImporter_NodeElement_Color : X3DNodeElementBase { - std::list Value; ///< Stored value. - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_Color(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_Color, pParent) {} - -}; // struct CX3DImporter_NodeElement_Color - -/// This struct hold value. -struct CX3DImporter_NodeElement_ColorRGBA : X3DNodeElementBase { - std::list Value; ///< Stored value. - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_ColorRGBA(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_ColorRGBA, pParent) {} - -}; // struct CX3DImporter_NodeElement_ColorRGBA - -/// This struct hold value. -struct CX3DImporter_NodeElement_Coordinate : public X3DNodeElementBase { - std::list Value; ///< Stored value. - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_Coordinate(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_Coordinate, pParent) {} - -}; // struct CX3DImporter_NodeElement_Coordinate - -/// This struct hold value. -struct CX3DImporter_NodeElement_Normal : X3DNodeElementBase { - std::list Value; ///< Stored value. - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_Normal(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_Normal, pParent) {} - -}; // struct CX3DImporter_NodeElement_Normal - -/// This struct hold value. -struct CX3DImporter_NodeElement_TextureCoordinate : X3DNodeElementBase { - std::list Value; ///< Stored value. - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_TextureCoordinate(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_TextureCoordinate, pParent) {} - -}; // struct CX3DImporter_NodeElement_TextureCoordinate - -/// Two-dimensional figure. -struct CX3DImporter_NodeElement_Geometry2D : X3DNodeElementBase { - std::list Vertices; ///< Vertices list. - size_t NumIndices; ///< Number of indices in one face. - bool Solid; ///< Flag: if true then render must use back-face culling, else render must draw both sides of object. - - /// Constructor. - /// \param [in] pParent - pointer to parent node. - /// \param [in] pType - type of geometry object. - CX3DImporter_NodeElement_Geometry2D(X3DElemType pType, X3DNodeElementBase *pParent) : - X3DNodeElementBase(pType, pParent), Solid(true) {} - -}; // class CX3DImporter_NodeElement_Geometry2D - -/// Three-dimensional body. -struct CX3DImporter_NodeElement_Geometry3D : X3DNodeElementBase { - std::list Vertices; ///< Vertices list. - size_t NumIndices; ///< Number of indices in one face. - bool Solid; ///< Flag: if true then render must use back-face culling, else render must draw both sides of object. - - /// Constructor. - /// \param [in] pParent - pointer to parent node. - /// \param [in] pType - type of geometry object. - CX3DImporter_NodeElement_Geometry3D(X3DElemType pType, X3DNodeElementBase *pParent) : - X3DNodeElementBase(pType, pParent), Vertices(), NumIndices(0), Solid(true) { - // empty - } -}; // class CX3DImporter_NodeElement_Geometry3D - -/// Uniform rectangular grid of varying height. -struct CX3DImporter_NodeElement_ElevationGrid : CX3DImporter_NodeElement_Geometry3D { - bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). - bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). - /// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are - /// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced. - float CreaseAngle; - std::vector CoordIdx; ///< Coordinates list by faces. In X3D format: "-1" - delimiter for faces. - - /// Constructor. - /// \param [in] pParent - pointer to parent node. - /// \param [in] pType - type of geometry object. - CX3DImporter_NodeElement_ElevationGrid(X3DElemType pType, X3DNodeElementBase *pParent) : - CX3DImporter_NodeElement_Geometry3D(pType, pParent) {} -}; // class CX3DImporter_NodeElement_IndexedSet - -/// Shape with indexed vertices. -struct CX3DImporter_NodeElement_IndexedSet : public CX3DImporter_NodeElement_Geometry3D { - /// The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors - /// used in the lighting model equations. If ccw is TRUE, the normals shall follow the right hand rule; the orientation of each normal with respect to - /// the vertices (taken in order) shall be such that the vertices appear to be oriented in a counterclockwise order when the vertices are viewed (in the - /// local coordinate system of the Shape) from the opposite direction as the normal. If ccw is FALSE, the normals shall be oriented in the opposite - /// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the - /// ccw field, results are undefined. - bool CCW; - std::vector ColorIndex; ///< Field to specify the polygonal faces by indexing into the or . - bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). - /// The convex field indicates whether all polygons in the shape are convex (TRUE). A polygon is convex if it is planar, does not intersect itself, - /// and all of the interior angles at its vertices are less than 180 degrees. Non planar and self intersecting polygons may produce undefined results - /// even if the convex field is FALSE. - bool Convex; - std::vector CoordIndex; ///< Field to specify the polygonal faces by indexing into the . - /// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are - /// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced. - float CreaseAngle; - std::vector NormalIndex; ///< Field to specify the polygonal faces by indexing into the . - bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). - std::vector TexCoordIndex; ///< Field to specify the polygonal faces by indexing into the . - - /// Constructor. - /// \param [in] pParent - pointer to parent node. - /// \param [in] pType - type of geometry object. - CX3DImporter_NodeElement_IndexedSet(X3DElemType pType, X3DNodeElementBase *pParent) : - CX3DImporter_NodeElement_Geometry3D(pType, pParent) {} -}; // class CX3DImporter_NodeElement_IndexedSet - -/// Shape with set of vertices. -struct CX3DImporter_NodeElement_Set : CX3DImporter_NodeElement_Geometry3D { - /// The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors - /// used in the lighting model equations. If ccw is TRUE, the normals shall follow the right hand rule; the orientation of each normal with respect to - /// the vertices (taken in order) shall be such that the vertices appear to be oriented in a counterclockwise order when the vertices are viewed (in the - /// local coordinate system of the Shape) from the opposite direction as the normal. If ccw is FALSE, the normals shall be oriented in the opposite - /// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the - /// ccw field, results are undefined. - bool CCW; - bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). - bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). - std::vector CoordIndex; ///< Field to specify the polygonal faces by indexing into the . - std::vector NormalIndex; ///< Field to specify the polygonal faces by indexing into the . - std::vector TexCoordIndex; ///< Field to specify the polygonal faces by indexing into the . - std::vector VertexCount; ///< Field describes how many vertices are to be used in each polyline(polygon) from the field. - - /// Constructor. - /// \param [in] pParent - pointer to parent node. - /// \param [in] pType - type of geometry object. - CX3DImporter_NodeElement_Set(X3DElemType type, X3DNodeElementBase *pParent) : - CX3DImporter_NodeElement_Geometry3D(type, pParent) {} - -}; // class CX3DImporter_NodeElement_Set - -/// This struct hold value. -struct CX3DImporter_NodeElement_Shape : X3DNodeElementBase { - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_Shape(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_Shape, pParent) {} -}; // struct CX3DImporter_NodeElement_Shape - -/// This struct hold value. -struct CX3DImporter_NodeElement_Appearance : public X3DNodeElementBase { - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_Appearance(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_Appearance, pParent) {} - -}; // struct CX3DImporter_NodeElement_Appearance - -struct CX3DImporter_NodeElement_Material : public X3DNodeElementBase { - float AmbientIntensity; ///< Specifies how much ambient light from light sources this surface shall reflect. - aiColor3D DiffuseColor; ///< Reflects all X3D light sources depending on the angle of the surface with respect to the light source. - aiColor3D EmissiveColor; ///< Models "glowing" objects. This can be useful for displaying pre-lit models. - float Shininess; ///< Lower shininess values produce soft glows, while higher values result in sharper, smaller highlights. - aiColor3D SpecularColor; ///< The specularColor and shininess fields determine the specular highlights. - float Transparency; ///< Specifies how "clear" an object is, with 1.0 being completely transparent, and 0.0 completely opaque. - - /// Constructor. - /// \param [in] pParent - pointer to parent node. - /// \param [in] pType - type of geometry object. - CX3DImporter_NodeElement_Material(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_Material, pParent), - AmbientIntensity(0.0f), - DiffuseColor(), - EmissiveColor(), - Shininess(0.0f), - SpecularColor(), - Transparency(1.0f) { - // empty - } -}; // class CX3DImporter_NodeElement_Material - -/// This struct hold value. -struct CX3DImporter_NodeElement_ImageTexture : X3DNodeElementBase { - /// RepeatS and RepeatT, that specify how the texture wraps in the S and T directions. If repeatS is TRUE (the default), the texture map is repeated - /// outside the [0.0, 1.0] texture coordinate range in the S direction so that it fills the shape. If repeatS is FALSE, the texture coordinates are - /// clamped in the S direction to lie within the [0.0, 1.0] range. The repeatT field is analogous to the repeatS field. - bool RepeatS; - bool RepeatT; ///< See \ref RepeatS. - std::string URL; ///< URL of the texture. - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_ImageTexture(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_ImageTexture, pParent) {} - -}; // struct CX3DImporter_NodeElement_ImageTexture - -/// This struct hold value. -struct CX3DImporter_NodeElement_TextureTransform : X3DNodeElementBase { - aiVector2D Center; ///< Specifies a translation offset in texture coordinate space about which the rotation and scale fields are applied. - float Rotation; ///< Specifies a rotation in angle base units of the texture coordinates about the center point after the scale has been applied. - aiVector2D Scale; ///< Specifies a scaling factor in S and T of the texture coordinates about the center point. - aiVector2D Translation; ///< Specifies a translation of the texture coordinates. - - /// Constructor - /// \param [in] pParent - pointer to parent node. - CX3DImporter_NodeElement_TextureTransform(X3DNodeElementBase *pParent) : - X3DNodeElementBase(X3DElemType::ENET_TextureTransform, pParent) {} - -}; // struct CX3DImporter_NodeElement_TextureTransform - -struct CX3DNodeElementGroup : X3DNodeElementBase { - aiMatrix4x4 Transformation; ///< Transformation matrix. - - /// As you know node elements can use already defined node elements when attribute "USE" is defined. - /// Standard search when looking for an element in the whole scene graph, existing at this moment. - /// If a node is marked as static, the children(or lower) can not search for elements in the nodes upper then static. - bool Static; - - bool UseChoice; ///< Flag: if true then use number from \ref Choice to choose what the child will be kept. - int32_t Choice; ///< Number of the child which will be kept. - - /// Constructor. - /// \param [in] pParent - pointer to parent node. - /// \param [in] pStatic - static node flag. - CX3DNodeElementGroup(X3DNodeElementBase *pParent, const bool pStatic = false) : - X3DNodeElementBase(X3DElemType::ENET_Group, pParent), Static(pStatic), UseChoice(false) {} -}; - -struct X3DNodeElementMeta : X3DNodeElementBase { - std::string Name; ///< Name of metadata object. - std::string Reference; - - virtual ~X3DNodeElementMeta() { - // empty - } - -protected: - X3DNodeElementMeta(X3DElemType type, X3DNodeElementBase *parent) : - X3DNodeElementBase(type, parent) { - // empty - } -}; - -struct X3DNodeElementMetaBoolean : X3DNodeElementMeta { - std::vector Value; ///< Stored value. - - explicit X3DNodeElementMetaBoolean(X3DNodeElementBase *pParent) : - X3DNodeElementMeta(X3DElemType::ENET_MetaBoolean, pParent) { - // empty - } -}; - -struct X3DNodeElementMetaDouble : X3DNodeElementMeta { - std::vector Value; ///< Stored value. - - explicit X3DNodeElementMetaDouble(X3DNodeElementBase *pParent) : - X3DNodeElementMeta(X3DElemType::ENET_MetaDouble, pParent) { - // empty - } -}; - -struct X3DNodeElementMetaFloat : public X3DNodeElementMeta { - std::vector Value; ///< Stored value. - - explicit X3DNodeElementMetaFloat(X3DNodeElementBase *pParent) : - X3DNodeElementMeta(X3DElemType::ENET_MetaFloat, pParent) { - // empty - } -}; - -struct X3DNodeElementMetaInt : public X3DNodeElementMeta { - std::vector Value; ///< Stored value. - - explicit X3DNodeElementMetaInt(X3DNodeElementBase *pParent) : - X3DNodeElementMeta(X3DElemType::ENET_MetaInteger, pParent) { - // empty - } -}; - -struct X3DNodeElementMetaSet : public X3DNodeElementMeta { - std::list Value; ///< Stored value. - - explicit X3DNodeElementMetaSet(X3DNodeElementBase *pParent) : - X3DNodeElementMeta(X3DElemType::ENET_MetaSet, pParent) { - // empty - } -}; - -struct X3DNodeElementMetaString : X3DNodeElementMeta { - std::list Value; ///< Stored value. - - explicit X3DNodeElementMetaString(X3DNodeElementBase *pParent) : - X3DNodeElementMeta(X3DElemType::ENET_MetaString, pParent) { - // empty - } -}; - -/// \struct CX3DImporter_NodeElement_Light -/// This struct hold value. -struct X3DNodeNodeElementLight : X3DNodeElementBase { - float AmbientIntensity; ///< Specifies the intensity of the ambient emission from the light. - aiColor3D Color; ///< specifies the spectral colour properties of both the direct and ambient light emission as an RGB value. - aiVector3D Direction; ///< Specifies the direction vector of the illumination emanating from the light source in the local coordinate system. - /// \var Global - /// Field that determines whether the light is global or scoped. Global lights illuminate all objects that fall within their volume of lighting influence. - /// Scoped lights only illuminate objects that are in the same transformation hierarchy as the light. - bool Global; - float Intensity; ///< Specifies the brightness of the direct emission from the light. - /// \var Attenuation - /// PointLight node's illumination falls off with distance as specified by three attenuation coefficients. The attenuation factor - /// is: "1 / max(attenuation[0] + attenuation[1] * r + attenuation[2] * r2, 1)", where r is the distance from the light to the surface being illuminated. - aiVector3D Attenuation; - aiVector3D Location; ///< Specifies a translation offset of the centre point of the light source from the light's local coordinate system origin. - float Radius; ///< Specifies the radial extent of the solid angle and the maximum distance from location that may be illuminated by the light source. - float BeamWidth; ///< Specifies an inner solid angle in which the light source emits light at uniform full intensity. - float CutOffAngle; ///< The light source's emission intensity drops off from the inner solid angle (beamWidth) to the outer solid angle (cutOffAngle). - - /// Constructor - /// \param [in] pParent - pointer to parent node. - /// \param [in] pLightType - type of the light source. - X3DNodeNodeElementLight(X3DElemType pLightType, X3DNodeElementBase *pParent) : - X3DNodeElementBase(pLightType, pParent) {} - -}; // struct CX3DImporter_NodeElement_Light - -using X3DElementList = std::list; +using X3DElementList = std::list; class X3DImporter : public BaseImporter { public: @@ -654,18 +268,113 @@ public: void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler); const aiImporterDesc *GetInfo() const; void Clear(); - void readMetadata(XmlNode &node); - void readScene(XmlNode &node); - void readViewpoint(XmlNode &node); - void readMetadataObject(XmlNode &node); - void ParseDirectionalLight(XmlNode &node); - void Postprocess_BuildNode(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode, std::list &pSceneMeshList, - std::list &pSceneMaterialList, std::list &pSceneLightList) const; private: + bool isNodeEmpty(XmlNode &node); + void checkNodeMustBeEmpty(XmlNode &node); + void skipUnsupportedNode(const std::string &pParentNodeName, XmlNode &node); + void readHead(XmlNode &node); + void readChildNodes(XmlNode &node, const std::string &pParentNodeName); + void readScene(XmlNode &node); + + bool FindNodeElement_FromRoot(const std::string &pID, const X3DElemType pType, X3DNodeElementBase **pElement); + bool FindNodeElement_FromNode(X3DNodeElementBase *pStartNode, const std::string &pID, + const X3DElemType pType, X3DNodeElementBase **pElement); + bool FindNodeElement(const std::string &pID, const X3DElemType pType, X3DNodeElementBase **pElement); + void ParseHelper_Group_Begin(const bool pStatic = false); + void ParseHelper_Node_Enter(X3DNodeElementBase *pNode); + void ParseHelper_Node_Exit(); + + // 2D geometry + void readArc2D(XmlNode &node); + void readArcClose2D(XmlNode &node); + void readCircle2D(XmlNode &node); + void readDisk2D(XmlNode &node); + void readPolyline2D(XmlNode &node); + void readPolypoint2D(XmlNode &node); + void readRectangle2D(XmlNode &node); + void readTriangleSet2D(XmlNode &node); + + // 3D geometry + void readBox(XmlNode &node); + void readCone(XmlNode &node); + void readCylinder(XmlNode &node); + void readElevationGrid(XmlNode &node); + void readExtrusion(XmlNode &node); + void readIndexedFaceSet(XmlNode &node); + void readSphere(XmlNode &node); + + // group + void startReadGroup(XmlNode &node); + void endReadGroup(); + void startReadStaticGroup(XmlNode &node); + void endReadStaticGroup(); + void startReadSwitch(XmlNode &node); + void endReadSwitch(); + void startReadTransform(XmlNode &node); + void endReadTransform(); + + // light + void readDirectionalLight(XmlNode &node); + void readPointLight(XmlNode &node); + void readSpotLight(XmlNode &node); + + // metadata + bool checkForMetadataNode(XmlNode &node); + void childrenReadMetadata(XmlNode &node, X3DNodeElementBase *pParentElement, const std::string &pNodeName); + void readMetadataBoolean(XmlNode &node); + void readMetadataDouble(XmlNode &node); + void readMetadataFloat(XmlNode &node); + void readMetadataInteger(XmlNode &node); + void readMetadataSet(XmlNode &node); + void readMetadataString(XmlNode &node); + + // networking + void readInline(XmlNode &node); + + // postprocessing + aiMatrix4x4 PostprocessHelper_Matrix_GlobalToCurrent() const; + void PostprocessHelper_CollectMetadata(const X3DNodeElementBase &pNodeElement, std::list &pList) const; + bool PostprocessHelper_ElementIsMetadata(const X3DElemType pType) const; + bool PostprocessHelper_ElementIsMesh(const X3DElemType pType) const; + void Postprocess_BuildLight(const X3DNodeElementBase &pNodeElement, std::list &pSceneLightList) const; + void Postprocess_BuildMaterial(const X3DNodeElementBase &pNodeElement, aiMaterial **pMaterial) const; + void Postprocess_BuildMesh(const X3DNodeElementBase &pNodeElement, aiMesh **pMesh) const; + void Postprocess_BuildNode(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode, std::list &pSceneMeshList, + std::list &pSceneMaterialList, std::list &pSceneLightList) const; + void Postprocess_BuildShape(const X3DNodeElementShape &pShapeNodeElement, std::list &pNodeMeshInd, + std::list &pSceneMeshList, std::list &pSceneMaterialList) const; + void Postprocess_CollectMetadata(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode) const; + + // rendering + void readColor(XmlNode &node); + void readColorRGBA(XmlNode &node); + void readCoordinate(XmlNode &node); + void readIndexedLineSet(XmlNode &node); + void readIndexedTriangleFanSet(XmlNode &node); + void readIndexedTriangleSet(XmlNode &node); + void readIndexedTriangleStripSet(XmlNode &node); + void readLineSet(XmlNode &node); + void readPointSet(XmlNode &node); + void readTriangleFanSet(XmlNode &node); + void readTriangleSet(XmlNode &node); + void readTriangleStripSet(XmlNode &node); + void readNormal(XmlNode &node); + + // shape + void readShape(XmlNode &node); + void readAppearance(XmlNode &node); + void readMaterial(XmlNode &node); + + // texturing + void readImageTexture(XmlNode &node); + void readTextureCoordinate(XmlNode &node); + void readTextureTransform(XmlNode &node); + static const aiImporterDesc Description; X3DNodeElementBase *mNodeElementCur; aiScene *mScene; + IOSystem *mpIOHandler; }; // class X3DImporter } // namespace Assimp diff --git a/code/AssetLib/X3D/X3DImporter_Geometry2D.cpp b/code/AssetLib/X3D/X3DImporter_Geometry2D.cpp new file mode 100644 index 000000000..171075556 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Geometry2D.cpp @@ -0,0 +1,467 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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 X3DImporter_Geometry2D.cpp +/// \brief Parsing data from nodes of "Geometry2D" set of X3D. +/// date 2015-2016 +/// author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" +#include "X3DGeoHelper.h" + +namespace Assimp { + +// +// The Arc2D node specifies a linear circular arc whose center is at (0,0) and whose angles are measured starting at the positive x-axis and sweeping +// towards the positive y-axis. The radius field specifies the radius of the circle of which the arc is a portion. The arc extends from the startAngle +// counterclockwise to the endAngle. The values of startAngle and endAngle shall be in the range [-2pi, 2pi] radians (or the equivalent if a different +// angle base unit has been specified). If startAngle and endAngle have the same value, a circle is specified. +void X3DImporter::readArc2D(XmlNode &node) { + std::string def, use; + float endAngle = AI_MATH_HALF_PI_F; + float radius = 1; + float startAngle = 0; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "endAngle", endAngle); + XmlParser::getFloatAttribute(node, "radius", radius); + XmlParser::getFloatAttribute(node, "startAngle", startAngle); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Arc2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Arc2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // create point list of geometry object and convert it to line set. + std::list tlist; + + X3DGeoHelper::make_arc2D(startAngle, endAngle, radius, 10, tlist); ///TODO: IME - AI_CONFIG for NumSeg + X3DGeoHelper::extend_point_to_line(tlist, ((X3DNodeElementGeometry2D *)ne)->Vertices); + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 2; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Arc2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// The ArcClose node specifies a portion of a circle whose center is at (0,0) and whose angles are measured starting at the positive x-axis and sweeping +// towards the positive y-axis. The end points of the arc specified are connected as defined by the closureType field. The radius field specifies the radius +// of the circle of which the arc is a portion. The arc extends from the startAngle counterclockwise to the endAngle. The value of radius shall be greater +// than zero. The values of startAngle and endAngle shall be in the range [-2pi, 2pi] radians (or the equivalent if a different default angle base unit has +// been specified). If startAngle and endAngle have the same value, a circle is specified and closureType is ignored. If the absolute difference between +// startAngle and endAngle is greater than or equal to 2pi, a complete circle is produced with no chord or radial line(s) drawn from the center. +// A closureType of "PIE" connects the end point to the start point by defining two straight line segments first from the end point to the center and then +// the center to the start point. A closureType of "CHORD" connects the end point to the start point by defining a straight line segment from the end point +// to the start point. Textures are applied individually to each face of the ArcClose2D. On the front (+Z) and back (-Z) faces of the ArcClose2D, when +// viewed from the +Z-axis, the texture is mapped onto each face with the same orientation as if the image were displayed normally in 2D. +void X3DImporter::readArcClose2D(XmlNode &node) { + std::string def, use; + std::string closureType("PIE"); + float endAngle = AI_MATH_HALF_PI_F; + float radius = 1; + bool solid = false; + float startAngle = 0; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "closureType", closureType); + XmlParser::getFloatAttribute(node, "endAngle", endAngle); + XmlParser::getFloatAttribute(node, "endAngle", endAngle); + XmlParser::getFloatAttribute(node, "radius", radius); + XmlParser::getBoolAttribute(node, "solid", solid); + XmlParser::getFloatAttribute(node, "startAngle", startAngle); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_ArcClose2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_ArcClose2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementGeometry2D *)ne)->Solid = solid; + // create point list of geometry object. + X3DGeoHelper::make_arc2D(startAngle, endAngle, radius, 10, ((X3DNodeElementGeometry2D *)ne)->Vertices); ///TODO: IME - AI_CONFIG for NumSeg + // add chord or two radiuses only if not a circle was defined + if (!((std::fabs(endAngle - startAngle) >= AI_MATH_TWO_PI_F) || (endAngle == startAngle))) { + std::list &vlist = ((X3DNodeElementGeometry2D *)ne)->Vertices; // just short alias. + + if ((closureType == "PIE") || (closureType == "\"PIE\"")) + vlist.push_back(aiVector3D(0, 0, 0)); // center point - first radial line + else if ((closureType != "CHORD") && (closureType != "\"CHORD\"")) + Throw_IncorrectAttrValue("ArcClose2D", "closureType"); + + vlist.push_back(*vlist.begin()); // arc first point - chord from first to last point of arc(if CHORD) or second radial line(if PIE). + } + + ((X3DNodeElementGeometry2D *)ne)->NumIndices = ((X3DNodeElementGeometry2D *)ne)->Vertices.size(); + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "ArcClose2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readCircle2D(XmlNode &node) { + std::string def, use; + float radius = 1; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "radius", radius); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Circle2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Circle2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // create point list of geometry object and convert it to line set. + std::list tlist; + + X3DGeoHelper::make_arc2D(0, 0, radius, 10, tlist); ///TODO: IME - AI_CONFIG for NumSeg + X3DGeoHelper::extend_point_to_line(tlist, ((X3DNodeElementGeometry2D *)ne)->Vertices); + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 2; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Circle2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// The Disk2D node specifies a circular disk which is centred at (0, 0) in the local coordinate system. The outerRadius field specifies the radius of the +// outer dimension of the Disk2D. The innerRadius field specifies the inner dimension of the Disk2D. The value of outerRadius shall be greater than zero. +// The value of innerRadius shall be greater than or equal to zero and less than or equal to outerRadius. If innerRadius is zero, the Disk2D is completely +// filled. Otherwise, the area within the innerRadius forms a hole in the Disk2D. If innerRadius is equal to outerRadius, a solid circular line shall +// be drawn using the current line properties. Textures are applied individually to each face of the Disk2D. On the front (+Z) and back (-Z) faces of +// the Disk2D, when viewed from the +Z-axis, the texture is mapped onto each face with the same orientation as if the image were displayed normally in 2D. +void X3DImporter::readDisk2D(XmlNode &node) { + std::string def, use; + float innerRadius = 0; + float outerRadius = 1; + bool solid = false; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "innerRadius", innerRadius); + XmlParser::getFloatAttribute(node, "outerRadius", outerRadius); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Disk2D, ne); + } else { + std::list tlist_o, tlist_i; + + if (innerRadius > outerRadius) Throw_IncorrectAttrValue("Disk2D", "innerRadius"); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Disk2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // create point list of geometry object. + ///TODO: IME - AI_CONFIG for NumSeg + X3DGeoHelper::make_arc2D(0, 0, outerRadius, 10, tlist_o); // outer circle + if (innerRadius == 0.0f) { // make filled disk + // in tlist_o we already have points of circle. just copy it and sign as polygon. + ((X3DNodeElementGeometry2D *)ne)->Vertices = tlist_o; + ((X3DNodeElementGeometry2D *)ne)->NumIndices = tlist_o.size(); + } else if (innerRadius == outerRadius) { // make circle + // in tlist_o we already have points of circle. convert it to line set. + X3DGeoHelper::extend_point_to_line(tlist_o, ((X3DNodeElementGeometry2D *)ne)->Vertices); + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 2; + } else { // make disk + std::list &vlist = ((X3DNodeElementGeometry2D *)ne)->Vertices; // just short alias. + + X3DGeoHelper::make_arc2D(0, 0, innerRadius, 10, tlist_i); // inner circle + // + // create quad list from two point lists + // + if (tlist_i.size() < 2) throw DeadlyImportError("Disk2D. Not enough points for creating quad list."); // tlist_i and tlist_o has equal size. + + // add all quads except last + for (std::list::iterator it_i = tlist_i.begin(), it_o = tlist_o.begin(); it_i != tlist_i.end();) { + // do not forget - CCW direction + vlist.push_back(*it_i++); // 1st point + vlist.push_back(*it_o++); // 2nd point + vlist.push_back(*it_o); // 3rd point + vlist.push_back(*it_i); // 4th point + } + + // add last quad + vlist.push_back(*tlist_i.end()); // 1st point + vlist.push_back(*tlist_o.end()); // 2nd point + vlist.push_back(*tlist_o.begin()); // 3rd point + vlist.push_back(*tlist_o.begin()); // 4th point + + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 4; + } + + ((X3DNodeElementGeometry2D *)ne)->Solid = solid; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Disk2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readPolyline2D(XmlNode &node) { + std::string def, use; + std::list lineSegments; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector2DListAttribute(node, "lineSegments", lineSegments); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Polyline2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Polyline2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // + // convert read point list of geometry object to line set. + // + std::list tlist; + + // convert vec2 to vec3 + for (std::list::iterator it2 = lineSegments.begin(); it2 != lineSegments.end(); ++it2) + tlist.push_back(aiVector3D(it2->x, it2->y, 0)); + + // convert point set to line set + X3DGeoHelper::extend_point_to_line(tlist, ((X3DNodeElementGeometry2D *)ne)->Vertices); + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 2; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Polyline2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readPolypoint2D(XmlNode &node) { + std::string def, use; + std::list point; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector2DListAttribute(node, "point", point); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Polypoint2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Polypoint2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // convert vec2 to vec3 + for (std::list::iterator it2 = point.begin(); it2 != point.end(); ++it2) { + ((X3DNodeElementGeometry2D *)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0)); + } + + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 1; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Polypoint2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readRectangle2D(XmlNode &node) { + std::string def, use; + aiVector2D size(2, 2); + bool solid = false; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector2DAttribute(node, "size", size); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Rectangle2D, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_Rectangle2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + float x1 = -size.x / 2.0f; + float x2 = size.x / 2.0f; + float y1 = -size.y / 2.0f; + float y2 = size.y / 2.0f; + std::list &vlist = ((X3DNodeElementGeometry2D *)ne)->Vertices; // just short alias. + + vlist.push_back(aiVector3D(x2, y1, 0)); // 1st point + vlist.push_back(aiVector3D(x2, y2, 0)); // 2nd point + vlist.push_back(aiVector3D(x1, y2, 0)); // 3rd point + vlist.push_back(aiVector3D(x1, y1, 0)); // 4th point + ((X3DNodeElementGeometry2D *)ne)->Solid = solid; + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 4; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Rectangle2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readTriangleSet2D(XmlNode &node) { + std::string def, use; + bool solid = false; + std::list vertices; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector2DListAttribute(node, "vertices", vertices); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TriangleSet2D, ne); + } else { + if (vertices.size() % 3) throw DeadlyImportError("TriangleSet2D. Not enough points for defining triangle."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry2D(X3DElemType::ENET_TriangleSet2D, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // convert vec2 to vec3 + for (std::list::iterator it2 = vertices.begin(); it2 != vertices.end(); ++it2) { + ((X3DNodeElementGeometry2D *)ne)->Vertices.push_back(aiVector3D(it2->x, it2->y, 0)); + } + + ((X3DNodeElementGeometry2D *)ne)->Solid = solid; + ((X3DNodeElementGeometry2D *)ne)->NumIndices = 3; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "TriangleSet2D"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Geometry3D.cpp b/code/AssetLib/X3D/X3DImporter_Geometry3D.cpp new file mode 100644 index 000000000..4250bfb00 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Geometry3D.cpp @@ -0,0 +1,981 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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 X3DImporter_Geometry3D.cpp +/// \brief Parsing data from nodes of "Geometry3D" set of X3D. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" +#include "X3DGeoHelper.h" + +// Header files, Assimp. +#include + +namespace Assimp +{ + +// +// The Box node specifies a rectangular parallelepiped box centred at (0, 0, 0) in the local coordinate system and aligned with the local coordinate axes. +// By default, the box measures 2 units in each dimension, from -1 to +1. The size field specifies the extents of the box along the X-, Y-, and Z-axes +// respectively and each component value shall be greater than zero. +void X3DImporter::readBox(XmlNode &node) { + std::string def, use; + bool solid = true; + aiVector3D size(2, 2, 2); + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector3DAttribute(node, "size", size); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if(!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Box, ne); + } + else + { + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry3D(X3DElemType::ENET_Box, mNodeElementCur); + if(!def.empty()) ne->ID = def; + + X3DGeoHelper::rect_parallel_epiped(size, ((X3DNodeElementGeometry3D *)ne)->Vertices); // get quad list + ((X3DNodeElementGeometry3D *)ne)->Solid = solid; + ((X3DNodeElementGeometry3D *)ne)->NumIndices = 4; + // check for X3DMetadataObject childs. + if(!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Box"); + else + mNodeElementCur->Children.push_back(ne);// add made object as child to current element + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +// +void X3DImporter::readCone(XmlNode &node) { + std::string use, def; + bool bottom = true; + float bottomRadius = 1; + float height = 2; + bool side = true; + bool solid = true; + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "solid", solid); + XmlParser::getBoolAttribute(node, "side", side); + XmlParser::getBoolAttribute(node, "bottom", bottom); + XmlParser::getFloatAttribute(node, "height", height); + XmlParser::getFloatAttribute(node, "bottomRadius", bottomRadius); + + // if "USE" defined then find already defined element. + if(!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Cone, ne); + } + else + { + const unsigned int tess = 30;///TODO: IME tessellation factor through ai_property + + std::vector tvec;// temp array for vertices. + + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry3D(X3DElemType::ENET_Cone, mNodeElementCur); + if(!def.empty()) ne->ID = def; + + // make cone or parts according to flags. + if(side) + { + StandardShapes::MakeCone(height, 0, bottomRadius, tess, tvec, !bottom); + } + else if(bottom) + { + StandardShapes::MakeCircle(bottomRadius, tess, tvec); + height = -(height / 2); + for(std::vector::iterator it = tvec.begin(); it != tvec.end(); ++it) it->y = height;// y - because circle made in oXZ. + } + + // copy data from temp array + for (std::vector::iterator it = tvec.begin(); it != tvec.end(); ++it) + ((X3DNodeElementGeometry3D*)ne)->Vertices.push_back(*it); + + ((X3DNodeElementGeometry3D *)ne)->Solid = solid; + ((X3DNodeElementGeometry3D *)ne)->NumIndices = 3; + // check for X3DMetadataObject childs. + if(!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Cone"); + else + mNodeElementCur->Children.push_back(ne);// add made object as child to current element + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +// +void X3DImporter::readCylinder(XmlNode &node) { + std::string use, def; + bool bottom = true; + float height = 2; + float radius = 1; + bool side = true; + bool solid = true; + bool top = true; + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "radius", radius); + XmlParser::getBoolAttribute(node, "solid", solid); + XmlParser::getBoolAttribute(node, "bottom", bottom); + XmlParser::getBoolAttribute(node, "top", top); + XmlParser::getBoolAttribute(node, "side", side); + XmlParser::getFloatAttribute(node, "height", height); + + // if "USE" defined then find already defined element. + if(!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Cylinder, ne); + } + else + { + const unsigned int tess = 30;///TODO: IME tessellation factor through ai_property + + std::vector tside;// temp array for vertices of side. + std::vector tcir;// temp array for vertices of circle. + + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry3D(X3DElemType::ENET_Cylinder, mNodeElementCur); + if(!def.empty()) ne->ID = def; + + // make cilynder or parts according to flags. + if(side) StandardShapes::MakeCone(height, radius, radius, tess, tside, true); + + height /= 2;// height defined for whole cylinder, when creating top and bottom circle we are using just half of height. + if(top || bottom) StandardShapes::MakeCircle(radius, tess, tcir); + // copy data from temp arrays + std::list &vlist = ((X3DNodeElementGeometry3D *)ne)->Vertices; // just short alias. + + for(std::vector::iterator it = tside.begin(); it != tside.end(); ++it) vlist.push_back(*it); + + if(top) + { + for(std::vector::iterator it = tcir.begin(); it != tcir.end(); ++it) + { + (*it).y = height;// y - because circle made in oXZ. + vlist.push_back(*it); + } + }// if(top) + + if(bottom) + { + for(std::vector::iterator it = tcir.begin(); it != tcir.end(); ++it) + { + (*it).y = -height;// y - because circle made in oXZ. + vlist.push_back(*it); + } + }// if(top) + + ((X3DNodeElementGeometry3D *)ne)->Solid = solid; + ((X3DNodeElementGeometry3D *)ne)->NumIndices = 3; + // check for X3DMetadataObject childs. + if(!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Cylinder"); + else + mNodeElementCur->Children.push_back(ne);// add made object as child to current element + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +// +// +// ColorNormalTexCoordContentModel can contain Color (or ColorRGBA), Normal and TextureCoordinate, in any order. No more than one instance of any single +// node type is allowed. A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +// The ElevationGrid node specifies a uniform rectangular grid of varying height in the Y=0 plane of the local coordinate system. The geometry is described +// by a scalar array of height values that specify the height of a surface above each point of the grid. The xDimension and zDimension fields indicate +// the number of elements of the grid height array in the X and Z directions. Both xDimension and zDimension shall be greater than or equal to zero. +// If either the xDimension or the zDimension is less than two, the ElevationGrid contains no quadrilaterals. +void X3DImporter::readElevationGrid(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + float creaseAngle = 0; + std::vector height; + bool normalPerVertex = true; + bool solid = true; + int32_t xDimension = 0; + float xSpacing = 1; + int32_t zDimension = 0; + float zSpacing = 1; + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "solid", solid); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getFloatAttribute(node, "creaseAngle", creaseAngle); + X3DXmlHelper::getFloatArrayAttribute(node, "height", height); + XmlParser::getIntAttribute(node, "xDimension", xDimension); + XmlParser::getFloatAttribute(node, "xSpacing", xSpacing); + XmlParser::getIntAttribute(node, "zDimension", zDimension); + XmlParser::getFloatAttribute(node, "zSpacing", zSpacing); + + // if "USE" defined then find already defined element. + if(!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_ElevationGrid, ne); + } + else + { + if((xSpacing == 0.0f) || (zSpacing == 0.0f)) throw DeadlyImportError("Spacing in must be grater than zero."); + if((xDimension <= 0) || (zDimension <= 0)) throw DeadlyImportError("Dimension in must be grater than zero."); + if ((size_t)(xDimension * zDimension) != height.size()) DeadlyImportError("Heights count must be equal to \"xDimension * zDimension\" in "); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementElevationGrid(X3DElemType::ENET_ElevationGrid, mNodeElementCur); + if(!def.empty()) ne->ID = def; + + X3DNodeElementElevationGrid &grid_alias = *((X3DNodeElementElevationGrid *)ne); // create alias for conveience + + {// create grid vertices list + std::vector::const_iterator he_it = height.begin(); + + for(int32_t zi = 0; zi < zDimension; zi++)// rows + { + for(int32_t xi = 0; xi < xDimension; xi++)// columns + { + aiVector3D tvec(xSpacing * xi, *he_it, zSpacing * zi); + + grid_alias.Vertices.push_back(tvec); + ++he_it; + } + } + }// END: create grid vertices list + // + // create faces list. In "coordIdx" format + // + // check if we have quads + if((xDimension < 2) || (zDimension < 2))// only one element in dimension is set, create line set. + { + ((X3DNodeElementElevationGrid *)ne)->NumIndices = 2; // will be holded as line set. + for(size_t i = 0, i_e = (grid_alias.Vertices.size() - 1); i < i_e; i++) + { + grid_alias.CoordIdx.push_back(static_cast(i)); + grid_alias.CoordIdx.push_back(static_cast(i + 1)); + grid_alias.CoordIdx.push_back(-1); + } + } + else// two or more elements in every dimension is set. create quad set. + { + ((X3DNodeElementElevationGrid *)ne)->NumIndices = 4; + for(int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++)// rows + { + for(int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++)// columns + { + // points direction in face. + if(ccw) + { + // CCW: + // 3 2 + // 0 1 + grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi); + grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1)); + grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1)); + grid_alias.CoordIdx.push_back(fzi * xDimension + fxi); + } + else + { + // CW: + // 0 1 + // 3 2 + grid_alias.CoordIdx.push_back(fzi * xDimension + fxi); + grid_alias.CoordIdx.push_back(fzi * xDimension + (fxi + 1)); + grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + (fxi + 1)); + grid_alias.CoordIdx.push_back((fzi + 1) * xDimension + fxi); + }// if(ccw) else + + grid_alias.CoordIdx.push_back(-1); + }// for(int32_t fxi = 0, fxi_e = (xDimension - 1); fxi < fxi_e; fxi++) + }// for(int32_t fzi = 0, fzi_e = (zDimension - 1); fzi < fzi_e; fzi++) + }// if((xDimension < 2) || (zDimension < 2)) else + + grid_alias.ColorPerVertex = colorPerVertex; + grid_alias.NormalPerVertex = normalPerVertex; + grid_alias.CreaseAngle = creaseAngle; + grid_alias.Solid = solid; + // check for child nodes + if(!isNodeEmpty(node)) + { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") readColorRGBA(currentChildNode); + else if (currentChildName == "Normal") readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) skipUnsupportedNode("ElevationGrid", currentChildNode); + } + ParseHelper_Node_Exit(); + }// if(!mReader->isEmptyElement()) + else + { + mNodeElementCur->Children.push_back(ne);// add made object as child to current element + }// if(!mReader->isEmptyElement()) else + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +template +static void GeometryHelper_Extrusion_CurveIsClosed(std::vector& pCurve, const bool pDropTail, const bool pRemoveLastPoint, bool& pCurveIsClosed) +{ + size_t cur_sz = pCurve.size(); + + pCurveIsClosed = false; + // for curve with less than four points checking is have no sense, + if(cur_sz < 4) return; + + for(size_t s = 3, s_e = cur_sz; s < s_e; s++) + { + // search for first point of duplicated part. + if(pCurve[0] == pCurve[s]) + { + bool found = true; + + // check if tail(indexed by b2) is duplicate of head(indexed by b1). + for(size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++) + { + if(pCurve[b1] != pCurve[b2]) + {// points not match: clear flag and break loop. + found = false; + + break; + } + }// for(size_t b1 = 1, b2 = (s + 1); b2 < cur_sz; b1++, b2++) + + // if duplicate tail is found then drop or not it depending on flags. + if(found) + { + pCurveIsClosed = true; + if(pDropTail) + { + if(!pRemoveLastPoint) s++;// prepare value for iterator's arithmetics. + + pCurve.erase(pCurve.begin() + s, pCurve.end());// remove tail + } + + break; + }// if(found) + }// if(pCurve[0] == pCurve[s]) + }// for(size_t s = 3, s_e = (cur_sz - 1); s < s_e; s++) +} + +static aiVector3D GeometryHelper_Extrusion_GetNextY(const size_t pSpine_PointIdx, const std::vector& pSpine, const bool pSpine_Closed) +{ + const size_t spine_idx_last = pSpine.size() - 1; + aiVector3D tvec; + + if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last))// at first special cases + { + if(pSpine_Closed) + {// If the spine curve is closed: The SCP for the first and last points is the same and is found using (spine[1] - spine[n - 2]) to compute the Y-axis. + // As we even for closed spine curve last and first point in pSpine are not the same: duplicates(spine[n - 1] which are equivalent to spine[0]) + // in tail are removed. + // So, last point in pSpine is a spine[n - 2] + tvec = pSpine[1] - pSpine[spine_idx_last]; + } + else if(pSpine_PointIdx == 0) + {// The Y-axis used for the first point is the vector from spine[0] to spine[1] + tvec = pSpine[1] - pSpine[0]; + } + else + {// The Y-axis used for the last point it is the vector from spine[n-2] to spine[n-1]. In our case(see above about dropping tail) spine[n - 1] is + // the spine[0]. + tvec = pSpine[spine_idx_last] - pSpine[spine_idx_last - 1]; + } + }// if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) + else + {// For all points other than the first or last: The Y-axis for spine[i] is found by normalizing the vector defined by (spine[i+1] - spine[i-1]). + tvec = pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx - 1]; + }// if((pSpine_PointIdx == 0) || (pSpine_PointIdx == spine_idx_last)) else + + return tvec.Normalize(); +} + +static aiVector3D GeometryHelper_Extrusion_GetNextZ(const size_t pSpine_PointIdx, const std::vector& pSpine, const bool pSpine_Closed, + const aiVector3D pVecZ_Prev) +{ + const aiVector3D zero_vec(0); + const size_t spine_idx_last = pSpine.size() - 1; + + aiVector3D tvec; + + // at first special cases + if(pSpine.size() < 3)// spine have not enough points for vector calculations. + { + tvec.Set(0, 0, 1); + } + else if(pSpine_PointIdx == 0)// special case: first point + { + if(pSpine_Closed)// for calculating use previous point in curve s[n - 2]. In list it's a last point, because point s[n - 1] was removed as duplicate. + { + tvec = (pSpine[1] - pSpine[0]) ^ (pSpine[spine_idx_last] - pSpine[0]); + } + else // for not closed curve first and next point(s[0] and s[1]) has the same vector Z. + { + bool found = false; + + // As said: "If the Z-axis of the first point is undefined (because the spine is not closed and the first two spine segments are collinear) + // then the Z-axis for the first spine point with a defined Z-axis is used." + // Walk through spine and find Z. + for(size_t next_point = 2; (next_point <= spine_idx_last) && !found; next_point++) + { + // (pSpine[2] - pSpine[1]) ^ (pSpine[0] - pSpine[1]) + tvec = (pSpine[next_point] - pSpine[next_point - 1]) ^ (pSpine[next_point - 2] - pSpine[next_point - 1]); + found = !tvec.Equal(zero_vec); + } + + // if entire spine are collinear then use OZ axis. + if(!found) tvec.Set(0, 0, 1); + }// if(pSpine_Closed) else + }// else if(pSpine_PointIdx == 0) + else if(pSpine_PointIdx == spine_idx_last)// special case: last point + { + if(pSpine_Closed) + {// do not forget that real last point s[n - 1] is removed as duplicated. And in this case we are calculating vector Z for point s[n - 2]. + tvec = (pSpine[0] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]); + // if taken spine vectors are collinear then use previous vector Z. + if(tvec.Equal(zero_vec)) tvec = pVecZ_Prev; + } + else + {// vector Z for last point of not closed curve is previous vector Z. + tvec = pVecZ_Prev; + } + } + else// regular point + { + tvec = (pSpine[pSpine_PointIdx + 1] - pSpine[pSpine_PointIdx]) ^ (pSpine[pSpine_PointIdx - 1] - pSpine[pSpine_PointIdx]); + // if taken spine vectors are collinear then use previous vector Z. + if(tvec.Equal(zero_vec)) tvec = pVecZ_Prev; + } + + // After determining the Z-axis, its dot product with the Z-axis of the previous spine point is computed. If this value is negative, the Z-axis + // is flipped (multiplied by -1). + if((tvec * pVecZ_Prev) < 0) tvec = -tvec; + + return tvec.Normalize(); +} + +// +void X3DImporter::readExtrusion(XmlNode &node) { + std::string use, def; + bool beginCap = true; + bool ccw = true; + bool convex = true; + float creaseAngle = 0; + std::vector crossSection; + bool endCap = true; + std::vector orientation; + std::vector scale; + bool solid = true; + std::vector spine; + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "beginCap", beginCap); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "convex", convex); + XmlParser::getFloatAttribute(node, "creaseAngle", creaseAngle); + X3DXmlHelper::getVector2DArrayAttribute(node, "crossSection", crossSection); + XmlParser::getBoolAttribute(node, "endCap", endCap); + X3DXmlHelper::getFloatArrayAttribute(node, "orientation", orientation); + X3DXmlHelper::getVector2DArrayAttribute(node, "scale", scale); + XmlParser::getBoolAttribute(node, "solid", solid); + X3DXmlHelper::getVector3DArrayAttribute(node, "spine", spine); + + // if "USE" defined then find already defined element. + if(!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Extrusion, ne); + } + else + { + // + // check if default values must be assigned + // + if(spine.size() == 0) + { + spine.resize(2); + spine[0].Set(0, 0, 0), spine[1].Set(0, 1, 0); + } + else if(spine.size() == 1) + { + throw DeadlyImportError("ParseNode_Geometry3D_Extrusion. Spine must have at least two points."); + } + + if(crossSection.size() == 0) + { + crossSection.resize(5); + crossSection[0].Set(1, 1), crossSection[1].Set(1, -1), crossSection[2].Set(-1, -1), crossSection[3].Set(-1, 1), crossSection[4].Set(1, 1); + } + + {// orientation + size_t ori_size = orientation.size() / 4; + + if(ori_size < spine.size()) + { + float add_ori[4];// values that will be added + + if(ori_size == 1)// if "orientation" has one element(means one MFRotation with four components) then use it value for all spine points. + { + add_ori[0] = orientation[0], add_ori[1] = orientation[1], add_ori[2] = orientation[2], add_ori[3] = orientation[3]; + } + else// else - use default values + { + add_ori[0] = 0, add_ori[1] = 0, add_ori[2] = 1, add_ori[3] = 0; + } + + orientation.reserve(spine.size() * 4); + for(size_t i = 0, i_e = (spine.size() - ori_size); i < i_e; i++) + orientation.push_back(add_ori[0]), orientation.push_back(add_ori[1]), orientation.push_back(add_ori[2]), orientation.push_back(add_ori[3]); + } + + if(orientation.size() % 4) throw DeadlyImportError("Attribute \"orientation\" in must has multiple four quantity of numbers."); + }// END: orientation + + {// scale + if(scale.size() < spine.size()) + { + aiVector2D add_sc; + + if(scale.size() == 1)// if "scale" has one element then use it value for all spine points. + add_sc = scale[0]; + else// else - use default values + add_sc.Set(1, 1); + + scale.reserve(spine.size()); + for(size_t i = 0, i_e = (spine.size() - scale.size()); i < i_e; i++) scale.push_back(add_sc); + } + }// END: scale + // + // create and if needed - define new geometry object. + // + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_Extrusion, mNodeElementCur); + if(!def.empty()) ne->ID = def; + + X3DNodeElementIndexedSet &ext_alias = *((X3DNodeElementIndexedSet *)ne); // create alias for conveience + // assign part of input data + ext_alias.CCW = ccw; + ext_alias.Convex = convex; + ext_alias.CreaseAngle = creaseAngle; + ext_alias.Solid = solid; + + // + // How we done it at all? + // 1. At first we will calculate array of basises for every point in spine(look SCP in ISO-dic). Also "orientation" vector + // are applied vor every basis. + // 2. After that we can create array of point sets: which are scaled, transferred to basis of relative basis and at final translated to real position + // using relative spine point. + // 3. Next step is creating CoordIdx array(do not forget "-1" delimiter). While creating CoordIdx also created faces for begin and end caps, if + // needed. While createing CootdIdx is taking in account CCW flag. + // 4. The last step: create Vertices list. + // + bool spine_closed;// flag: true if spine curve is closed. + bool cross_closed;// flag: true if cross curve is closed. + std::vector basis_arr;// array of basises. ROW_a - X, ROW_b - Y, ROW_c - Z. + std::vector > pointset_arr;// array of point sets: cross curves. + + // detect closed curves + GeometryHelper_Extrusion_CurveIsClosed(crossSection, true, true, cross_closed);// true - drop tail, true - remove duplicate end. + GeometryHelper_Extrusion_CurveIsClosed(spine, true, true, spine_closed);// true - drop tail, true - remove duplicate end. + // If both cap are requested and spine curve is closed then we can make only one cap. Because second cap will be the same surface. + if(spine_closed) + { + beginCap |= endCap; + endCap = false; + } + + {// 1. Calculate array of basises. + aiMatrix4x4 rotmat; + aiVector3D vecX(0), vecY(0), vecZ(0); + + basis_arr.resize(spine.size()); + for(size_t i = 0, i_e = spine.size(); i < i_e; i++) + { + aiVector3D tvec; + + // get axises of basis. + vecY = GeometryHelper_Extrusion_GetNextY(i, spine, spine_closed); + vecZ = GeometryHelper_Extrusion_GetNextZ(i, spine, spine_closed, vecZ); + vecX = (vecY ^ vecZ).Normalize(); + // get rotation matrix and apply "orientation" to basis + aiMatrix4x4::Rotation(orientation[i * 4 + 3], aiVector3D(orientation[i * 4], orientation[i * 4 + 1], orientation[i * 4 + 2]), rotmat); + tvec = vecX, tvec *= rotmat, basis_arr[i].a1 = tvec.x, basis_arr[i].a2 = tvec.y, basis_arr[i].a3 = tvec.z; + tvec = vecY, tvec *= rotmat, basis_arr[i].b1 = tvec.x, basis_arr[i].b2 = tvec.y, basis_arr[i].b3 = tvec.z; + tvec = vecZ, tvec *= rotmat, basis_arr[i].c1 = tvec.x, basis_arr[i].c2 = tvec.y, basis_arr[i].c3 = tvec.z; + }// for(size_t i = 0, i_e = spine.size(); i < i_e; i++) + }// END: 1. Calculate array of basises + + {// 2. Create array of point sets. + aiMatrix4x4 scmat; + std::vector tcross(crossSection.size()); + + pointset_arr.resize(spine.size()); + for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++) + { + aiVector3D tc23vec; + + tc23vec.Set(scale[spi].x, 0, scale[spi].y); + aiMatrix4x4::Scaling(tc23vec, scmat); + for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++) + { + aiVector3D tvecX, tvecY, tvecZ; + + tc23vec.Set(crossSection[cri].x, 0, crossSection[cri].y); + // apply scaling to point + tcross[cri] = scmat * tc23vec; + // + // transfer point to new basis + // calculate coordinate in new basis + tvecX.Set(basis_arr[spi].a1, basis_arr[spi].a2, basis_arr[spi].a3), tvecX *= tcross[cri].x; + tvecY.Set(basis_arr[spi].b1, basis_arr[spi].b2, basis_arr[spi].b3), tvecY *= tcross[cri].y; + tvecZ.Set(basis_arr[spi].c1, basis_arr[spi].c2, basis_arr[spi].c3), tvecZ *= tcross[cri].z; + // apply new coordinates and translate it to spine point. + tcross[cri] = tvecX + tvecY + tvecZ + spine[spi]; + }// for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; i++) + + pointset_arr[spi] = tcross;// store transferred point set + }// for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; i++) + }// END: 2. Create array of point sets. + + {// 3. Create CoordIdx. + // add caps if needed + if(beginCap) + { + // add cap as polygon. vertices of cap are places at begin, so just add numbers from zero. + for(size_t i = 0, i_e = crossSection.size(); i < i_e; i++) ext_alias.CoordIndex.push_back(static_cast(i)); + + // add delimiter + ext_alias.CoordIndex.push_back(-1); + }// if(beginCap) + + if(endCap) + { + // add cap as polygon. vertices of cap are places at end, as for beginCap use just sequence of numbers but with offset. + size_t beg = (pointset_arr.size() - 1) * crossSection.size(); + + for(size_t i = beg, i_e = (beg + crossSection.size()); i < i_e; i++) ext_alias.CoordIndex.push_back(static_cast(i)); + + // add delimiter + ext_alias.CoordIndex.push_back(-1); + }// if(beginCap) + + // add quads + for(size_t spi = 0, spi_e = (spine.size() - 1); spi <= spi_e; spi++) + { + const size_t cr_sz = crossSection.size(); + const size_t cr_last = crossSection.size() - 1; + + size_t right_col;// hold index basis for points of quad placed in right column; + + if(spi != spi_e) + right_col = spi + 1; + else if(spine_closed)// if spine curve is closed then one more quad is needed: between first and last points of curve. + right_col = 0; + else + break;// if spine curve is not closed then break the loop, because spi is out of range for that type of spine. + + for(size_t cri = 0; cri < cr_sz; cri++) + { + if(cri != cr_last) + { + MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex, + static_cast(spi * cr_sz + cri), + static_cast(right_col * cr_sz + cri), + static_cast(right_col * cr_sz + cri + 1), + static_cast(spi * cr_sz + cri + 1)); + // add delimiter + ext_alias.CoordIndex.push_back(-1); + } + else if(cross_closed)// if cross curve is closed then one more quad is needed: between first and last points of curve. + { + MACRO_FACE_ADD_QUAD(ccw, ext_alias.CoordIndex, + static_cast(spi * cr_sz + cri), + static_cast(right_col * cr_sz + cri), + static_cast(right_col * cr_sz + 0), + static_cast(spi * cr_sz + 0)); + // add delimiter + ext_alias.CoordIndex.push_back(-1); + } + }// for(size_t cri = 0; cri < cr_sz; cri++) + }// for(size_t spi = 0, spi_e = (spine.size() - 2); spi < spi_e; spi++) + }// END: 3. Create CoordIdx. + + {// 4. Create vertices list. + // just copy all vertices + for(size_t spi = 0, spi_e = spine.size(); spi < spi_e; spi++) + { + for(size_t cri = 0, cri_e = crossSection.size(); cri < cri_e; cri++) + { + ext_alias.Vertices.push_back(pointset_arr[spi][cri]); + } + } + }// END: 4. Create vertices list. +//PrintVectorSet("Ext. CoordIdx", ext_alias.CoordIndex); +//PrintVectorSet("Ext. Vertices", ext_alias.Vertices); + // check for child nodes + if(!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Extrusion"); + else + mNodeElementCur->Children.push_back(ne);// add made object as child to current element + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +// +// +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readIndexedFaceSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + std::vector colorIndex; + bool colorPerVertex = true; + bool convex = true; + std::vector coordIndex; + float creaseAngle = 0; + std::vector normalIndex; + bool normalPerVertex = true; + bool solid = true; + std::vector texCoordIndex; + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + X3DXmlHelper::getInt32ArrayAttribute(node, "colorIndex", colorIndex); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + XmlParser::getBoolAttribute(node, "convex", convex); + X3DXmlHelper::getInt32ArrayAttribute(node, "coordIndex", coordIndex); + XmlParser::getFloatAttribute(node, "creaseAngle", creaseAngle); + X3DXmlHelper::getInt32ArrayAttribute(node, "normalIndex", normalIndex); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + X3DXmlHelper::getInt32ArrayAttribute(node, "texCoordIndex", texCoordIndex); + + // if "USE" defined then find already defined element. + if(!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedFaceSet, ne); + } + else + { + // check data + if(coordIndex.size() == 0) throw DeadlyImportError("IndexedFaceSet must contain not empty \"coordIndex\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedFaceSet, mNodeElementCur); + if(!def.empty()) ne->ID = def; + + X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorIndex = colorIndex; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.Convex = convex; + ne_alias.CoordIndex = coordIndex; + ne_alias.CreaseAngle = creaseAngle; + ne_alias.NormalIndex = normalIndex; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + ne_alias.TexCoordIndex = texCoordIndex; + // check for child nodes + if(!isNodeEmpty(node)) + { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") readCoordinate(currentChildNode); + else if (currentChildName == "Normal") readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) skipUnsupportedNode("IndexedFaceSet", currentChildNode); + } + ParseHelper_Node_Exit(); + }// if(!isNodeEmpty(node)) + else + { + mNodeElementCur->Children.push_back(ne);// add made object as child to current element + } + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +// +void X3DImporter::readSphere(XmlNode &node) { + std::string use, def; + ai_real radius = 1; + bool solid = true; + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "radius", radius); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if(!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Sphere, ne); + } + else + { + const unsigned int tess = 3;///TODO: IME tessellation factor through ai_property + + std::vector tlist; + + // create and if needed - define new geometry object. + ne = new X3DNodeElementGeometry3D(X3DElemType::ENET_Sphere, mNodeElementCur); + if(!def.empty()) ne->ID = def; + + StandardShapes::MakeSphere(tess, tlist); + // copy data from temp array and apply scale + for(std::vector::iterator it = tlist.begin(); it != tlist.end(); ++it) + { + ((X3DNodeElementGeometry3D *)ne)->Vertices.push_back(*it * radius); + } + + ((X3DNodeElementGeometry3D *)ne)->Solid = solid; + ((X3DNodeElementGeometry3D *)ne)->NumIndices = 3; + // check for X3DMetadataObject childs. + if(!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Sphere"); + else + mNodeElementCur->Children.push_back(ne);// add made object as child to current element + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +}// namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Group.cpp b/code/AssetLib/X3D/X3DImporter_Group.cpp new file mode 100644 index 000000000..d672d741b --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Group.cpp @@ -0,0 +1,273 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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 X3DImporter_Group.cpp +/// \brief Parsing data from nodes of "Grouping" set of X3D. +/// date 2015-2016 +/// author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" + +namespace Assimp { + +// +// <\!-- ChildContentModel --> +// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, +// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the +// precise palette of legal nodes that are available depends on assigned profile and components. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +// A Group node contains children nodes without introducing a new transformation. It is equivalent to a Transform node containing an identity transform. +void X3DImporter::startReadGroup(XmlNode &node) { + std::string def, use; + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + X3DNodeElementBase *ne; + + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) mNodeElementCur->ID = def; + // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. + + // for empty element exit from node in that place + if (isNodeEmpty(node)) ParseHelper_Node_Exit(); + } // if(!use.empty()) else +} + +void X3DImporter::endReadGroup() { + ParseHelper_Node_Exit(); // go up in scene graph +} + +// +// <\!-- ChildContentModel --> +// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, +// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the +// precise palette of legal nodes that are available depends on assigned profile and components. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +// The StaticGroup node contains children nodes which cannot be modified. StaticGroup children are guaranteed to not change, send events, receive events or +// contain any USE references outside the StaticGroup. +void X3DImporter::startReadStaticGroup(XmlNode &node) { + std::string def, use; + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + X3DNodeElementBase *ne; + + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(true); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) mNodeElementCur->ID = def; + // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. + + // for empty element exit from node in that place + if (isNodeEmpty(node)) ParseHelper_Node_Exit(); + } // if(!use.empty()) else +} + +void X3DImporter::endReadStaticGroup() { + ParseHelper_Node_Exit(); // go up in scene graph +} + +// +// <\!-- ChildContentModel --> +// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, +// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the +// precise palette of legal nodes that are available depends on assigned profile and components. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +// The Switch grouping node traverses zero or one of the nodes specified in the children field. The whichChoice field specifies the index of the child +// to traverse, with the first child having index 0. If whichChoice is less than zero or greater than the number of nodes in the children field, nothing +// is chosen. +void X3DImporter::startReadSwitch(XmlNode &node) { + std::string def, use; + int32_t whichChoice = -1; + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getIntAttribute(node, "whichChoice", whichChoice); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + X3DNodeElementBase *ne; + + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) mNodeElementCur->ID = def; + + // also set values specific to this type of group + ((X3DNodeElementGroup *)mNodeElementCur)->UseChoice = true; + ((X3DNodeElementGroup *)mNodeElementCur)->Choice = whichChoice; + // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. + + // for empty element exit from node in that place + if (isNodeEmpty(node)) ParseHelper_Node_Exit(); + } // if(!use.empty()) else +} + +void X3DImporter::endReadSwitch() { + // just exit from node. Defined choice will be accepted at postprocessing stage. + ParseHelper_Node_Exit(); // go up in scene graph +} + +// +// <\!-- ChildContentModel --> +// ChildContentModel is the child-node content model corresponding to X3DChildNode, combining all profiles. ChildContentModel can contain most nodes, +// other Grouping nodes, Prototype declarations and ProtoInstances in any order and any combination. When the assigned profile is less than Full, the +// precise palette of legal nodes that are available depends on assigned profile and components. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +// The Transform node is a grouping node that defines a coordinate system for its children that is relative to the coordinate systems of its ancestors. +// Given a 3-dimensional point P and Transform node, P is transformed into point P' in its parent's coordinate system by a series of intermediate +// transformations. In matrix transformation notation, where C (center), SR (scaleOrientation), T (translation), R (rotation), and S (scale) are the +// equivalent transformation matrices, +// P' = T * C * R * SR * S * -SR * -C * P +void X3DImporter::startReadTransform(XmlNode &node) { + aiVector3D center(0, 0, 0); + float rotation[4] = { 0, 0, 1, 0 }; + aiVector3D scale(1, 1, 1); // A value of zero indicates that any child geometry shall not be displayed + float scale_orientation[4] = { 0, 0, 1, 0 }; + aiVector3D translation(0, 0, 0); + aiMatrix4x4 matr, tmatr; + std::string use, def; + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector3DAttribute(node, "center", center); + X3DXmlHelper::getVector3DAttribute(node, "scale", scale); + X3DXmlHelper::getVector3DAttribute(node, "translation", translation); + std::vector tvec; + if (X3DXmlHelper::getFloatArrayAttribute(node, "rotation", tvec)) { + if (tvec.size() != 4) throw DeadlyImportError(": rotation vector must have 4 elements."); + memcpy(rotation, tvec.data(), sizeof(rotation)); + tvec.clear(); + } + if (X3DXmlHelper::getFloatArrayAttribute(node, "scaleOrientation", tvec)) { + if (tvec.size() != 4) throw DeadlyImportError(": scaleOrientation vector must have 4 elements."); + memcpy(scale_orientation, tvec.data(), sizeof(scale_orientation)); + tvec.clear(); + } + + // if "USE" defined then find already defined element. + if (!use.empty()) { + X3DNodeElementBase *ne(nullptr); + + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) { + mNodeElementCur->ID = def; + } + + // + // also set values specific to this type of group + // + // calculate transformation matrix + aiMatrix4x4::Translation(translation, matr); // T + aiMatrix4x4::Translation(center, tmatr); // C + matr *= tmatr; + aiMatrix4x4::Rotation(rotation[3], aiVector3D(rotation[0], rotation[1], rotation[2]), tmatr); // R + matr *= tmatr; + aiMatrix4x4::Rotation(scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr); // SR + matr *= tmatr; + aiMatrix4x4::Scaling(scale, tmatr); // S + matr *= tmatr; + aiMatrix4x4::Rotation(-scale_orientation[3], aiVector3D(scale_orientation[0], scale_orientation[1], scale_orientation[2]), tmatr); // -SR + matr *= tmatr; + aiMatrix4x4::Translation(-center, tmatr); // -C + matr *= tmatr; + // and assign it + ((X3DNodeElementGroup *)mNodeElementCur)->Transformation = matr; + // in grouping set of nodes check X3DMetadataObject is not needed, because it is done in parser function. + + // for empty element exit from node in that place + if (isNodeEmpty(node)) { + ParseHelper_Node_Exit(); + } + } // if(!use.empty()) else +} + +void X3DImporter::endReadTransform() { + ParseHelper_Node_Exit(); // go up in scene graph +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Light.cpp b/code/AssetLib/X3D/X3DImporter_Light.cpp new file mode 100644 index 000000000..b9bd17164 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Light.cpp @@ -0,0 +1,270 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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 X3DImporter_Light.cpp +/// \brief Parsing data from nodes of "Lighting" set of X3D. +/// date 2015-2016 +/// author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" +#include + +namespace Assimp { + +// +void X3DImporter::readDirectionalLight(XmlNode &node) { + std::string def, use; + float ambientIntensity = 0; + aiColor3D color(1, 1, 1); + aiVector3D direction(0, 0, -1); + bool global = false; + float intensity = 1; + bool on = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "ambientIntensity", ambientIntensity); + X3DXmlHelper::getColor3DAttribute(node, "color", color); + X3DXmlHelper::getVector3DAttribute(node, "direction", direction); + XmlParser::getBoolAttribute(node, "global", global); + XmlParser::getFloatAttribute(node, "intensity", intensity); + XmlParser::getBoolAttribute(node, "on", on); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_DirectionalLight, ne); + } else { + if (on) { + // create and if needed - define new geometry object. + ne = new X3DNodeElementLight(X3DElemType::ENET_DirectionalLight, mNodeElementCur); + if (!def.empty()) + ne->ID = def; + else + ne->ID = "DirectionalLight_" + ai_to_string((size_t)ne); // make random name + + ((X3DNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; + ((X3DNodeElementLight *)ne)->Color = color; + ((X3DNodeElementLight *)ne)->Direction = direction; + ((X3DNodeElementLight *)ne)->Global = global; + ((X3DNodeElementLight *)ne)->Intensity = intensity; + // Assimp want a node with name similar to a light. "Why? I don't no." ) + ParseHelper_Group_Begin(false); + + mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. + ParseHelper_Node_Exit(); + // check for child nodes + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "DirectionalLight"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(on) + } // if(!use.empty()) else +} + +// +void X3DImporter::readPointLight(XmlNode &node) { + std::string def, use; + float ambientIntensity = 0; + aiVector3D attenuation(1, 0, 0); + aiColor3D color(1, 1, 1); + bool global = true; + float intensity = 1; + aiVector3D location(0, 0, 0); + bool on = true; + float radius = 100; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "ambientIntensity", ambientIntensity); + X3DXmlHelper::getVector3DAttribute(node, "attenuation", attenuation); + X3DXmlHelper::getColor3DAttribute(node, "color", color); + XmlParser::getBoolAttribute(node, "global", global); + XmlParser::getFloatAttribute(node, "intensity", intensity); + X3DXmlHelper::getVector3DAttribute(node, "location", location); + XmlParser::getBoolAttribute(node, "on", on); + XmlParser::getFloatAttribute(node, "radius", radius); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_PointLight, ne); + } else { + if (on) { + // create and if needed - define new geometry object. + ne = new X3DNodeElementLight(X3DElemType::ENET_PointLight, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; + ((X3DNodeElementLight *)ne)->Attenuation = attenuation; + ((X3DNodeElementLight *)ne)->Color = color; + ((X3DNodeElementLight *)ne)->Global = global; + ((X3DNodeElementLight *)ne)->Intensity = intensity; + ((X3DNodeElementLight *)ne)->Location = location; + ((X3DNodeElementLight *)ne)->Radius = radius; + // Assimp want a node with name similar to a light. "Why? I don't no." ) + ParseHelper_Group_Begin(false); + // make random name + if (ne->ID.empty()) ne->ID = "PointLight_" + ai_to_string((size_t)ne); + + mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. + ParseHelper_Node_Exit(); + // check for child nodes + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "PointLight"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(on) + } // if(!use.empty()) else +} + +// +void X3DImporter::readSpotLight(XmlNode &node) { + std::string def, use; + float ambientIntensity = 0; + aiVector3D attenuation(1, 0, 0); + float beamWidth = 0.7854f; + aiColor3D color(1, 1, 1); + float cutOffAngle = 1.570796f; + aiVector3D direction(0, 0, -1); + bool global = true; + float intensity = 1; + aiVector3D location(0, 0, 0); + bool on = true; + float radius = 100; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "ambientIntensity", ambientIntensity); + X3DXmlHelper::getVector3DAttribute(node, "attenuation", attenuation); + XmlParser::getFloatAttribute(node, "beamWidth", beamWidth); + X3DXmlHelper::getColor3DAttribute(node, "color", color); + XmlParser::getFloatAttribute(node, "cutOffAngle", cutOffAngle); + X3DXmlHelper::getVector3DAttribute(node, "direction", direction); + XmlParser::getBoolAttribute(node, "global", global); + XmlParser::getFloatAttribute(node, "intensity", intensity); + X3DXmlHelper::getVector3DAttribute(node, "location", location); + XmlParser::getBoolAttribute(node, "on", on); + XmlParser::getFloatAttribute(node, "radius", radius); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_SpotLight, ne); + } else { + if (on) { + // create and if needed - define new geometry object. + ne = new X3DNodeElementLight(X3DElemType::ENET_SpotLight, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + if (beamWidth > cutOffAngle) beamWidth = cutOffAngle; + + ((X3DNodeElementLight *)ne)->AmbientIntensity = ambientIntensity; + ((X3DNodeElementLight *)ne)->Attenuation = attenuation; + ((X3DNodeElementLight *)ne)->BeamWidth = beamWidth; + ((X3DNodeElementLight *)ne)->Color = color; + ((X3DNodeElementLight *)ne)->CutOffAngle = cutOffAngle; + ((X3DNodeElementLight *)ne)->Direction = direction; + ((X3DNodeElementLight *)ne)->Global = global; + ((X3DNodeElementLight *)ne)->Intensity = intensity; + ((X3DNodeElementLight *)ne)->Location = location; + ((X3DNodeElementLight *)ne)->Radius = radius; + + // Assimp want a node with name similar to a light. "Why? I don't no." ) + ParseHelper_Group_Begin(false); + // make random name + if (ne->ID.empty()) ne->ID = "SpotLight_" + ai_to_string((size_t)ne); + + mNodeElementCur->ID = ne->ID; // assign name to node and return to light element. + ParseHelper_Node_Exit(); + // check for child nodes + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "SpotLight"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(on) + } // if(!use.empty()) else +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Macro.hpp b/code/AssetLib/X3D/X3DImporter_Macro.hpp new file mode 100644 index 000000000..8d902b346 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Macro.hpp @@ -0,0 +1,110 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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 X3DImporter_Macro.hpp +/// \brief Useful macrodefines. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef X3DIMPORTER_MACRO_HPP_INCLUDED +#define X3DIMPORTER_MACRO_HPP_INCLUDED + +/// \def MACRO_USE_CHECKANDAPPLY(pDEF, pUSE, pNE) +/// Used for regular checking while attribute "USE" is defined. +/// \param [in] pNode - pugi xml node to read. +/// \param [in] pDEF - string holding "DEF" value. +/// \param [in] pUSE - string holding "USE" value. +/// \param [in] pType - type of element to find. +/// \param [out] pNE - pointer to found node element. +#define MACRO_USE_CHECKANDAPPLY(pNode, pDEF, pUSE, pType, pNE) \ + do { \ + checkNodeMustBeEmpty(pNode); \ + if (!pDEF.empty()) Throw_DEF_And_USE(pNode.name()); \ + if (!FindNodeElement(pUSE, X3DElemType::pType, &pNE)) Throw_USE_NotFound(pNode.name(), pUSE); \ + mNodeElementCur->Children.push_back(pNE); /* add found object as child to current element */ \ + } while (false) + +/// \def MACRO_ATTRREAD_CHECKUSEDEF_RET +/// Compact variant for checking "USE" and "DEF". +/// \param [in] pNode - pugi xml node to read. +/// \param [out] pDEF_Var - output variable name for "DEF" value. +/// \param [out] pUSE_Var - output variable name for "USE" value. +#define MACRO_ATTRREAD_CHECKUSEDEF_RET(pNode, pDEF_Var, pUSE_Var) \ + do { \ + XmlParser::getStdStrAttribute(pNode, "def", pDEF_Var); \ + XmlParser::getStdStrAttribute(pNode, "use", pUSE_Var); \ + } while (false) + +/// \def MACRO_FACE_ADD_QUAD_FA(pCCW, pOut, pIn, pP1, pP2, pP3, pP4) +/// Add points as quad. Means that pP1..pP4 set in CCW order. +#define MACRO_FACE_ADD_QUAD_FA(pCCW, pOut, pIn, pP1, pP2, pP3, pP4) \ + do { \ + if (pCCW) { \ + pOut.push_back(pIn[pP1]); \ + pOut.push_back(pIn[pP2]); \ + pOut.push_back(pIn[pP3]); \ + pOut.push_back(pIn[pP4]); \ + } else { \ + pOut.push_back(pIn[pP4]); \ + pOut.push_back(pIn[pP3]); \ + pOut.push_back(pIn[pP2]); \ + pOut.push_back(pIn[pP1]); \ + } \ + } while (false) + +/// \def MACRO_FACE_ADD_QUAD(pCCW, pOut, pP1, pP2, pP3, pP4) +/// Add points as quad. Means that pP1..pP4 set in CCW order. +#define MACRO_FACE_ADD_QUAD(pCCW, pOut, pP1, pP2, pP3, pP4) \ + do { \ + if (pCCW) { \ + pOut.push_back(pP1); \ + pOut.push_back(pP2); \ + pOut.push_back(pP3); \ + pOut.push_back(pP4); \ + } else { \ + pOut.push_back(pP4); \ + pOut.push_back(pP3); \ + pOut.push_back(pP2); \ + pOut.push_back(pP1); \ + } \ + } while (false) + +#endif // X3DIMPORTER_MACRO_HPP_INCLUDED diff --git a/code/AssetLib/X3D/X3DImporter_Metadata.cpp b/code/AssetLib/X3D/X3DImporter_Metadata.cpp new file mode 100644 index 000000000..bb1ba9a9d --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Metadata.cpp @@ -0,0 +1,255 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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 X3DImporter_Metadata.cpp +/// \brief Parsing data from nodes of "Metadata" set of X3D. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" + +namespace Assimp { + +bool X3DImporter::checkForMetadataNode(XmlNode &node) { + const std::string &name = node.name(); + if (name == "MetadataBoolean") { + readMetadataBoolean(node); + } else if (name == "MetadataDouble") { + readMetadataDouble(node); + } else if (name == "MetadataFloat") { + readMetadataFloat(node); + } else if (name == "MetadataInteger") { + readMetadataInteger(node); + } else if (name == "MetadataSet") { + readMetadataSet(node); + } else if (name == "MetadataString") { + readMetadataString(node); + } else + return false; + return true; +} + +void X3DImporter::childrenReadMetadata(XmlNode &node, X3DNodeElementBase *pParentElement, const std::string &pNodeName) { + ParseHelper_Node_Enter(pParentElement); + for (auto childNode : node.children()) { + if (!checkForMetadataNode(childNode)) skipUnsupportedNode(pNodeName, childNode); + } + ParseHelper_Node_Exit(); +} + +/// \def MACRO_METADATA_FINDCREATE(pDEF_Var, pUSE_Var, pReference, pValue, pNE, pMetaName) +/// Find element by "USE" or create new one. +/// \param [in] pNode - pugi xml node to read. +/// \param [in] pDEF_Var - variable name with "DEF" value. +/// \param [in] pUSE_Var - variable name with "USE" value. +/// \param [in] pReference - variable name with "reference" value. +/// \param [in] pValue - variable name with "value" value. +/// \param [in, out] pNE - pointer to node element. +/// \param [in] pMetaClass - Class of node. +/// \param [in] pMetaName - Name of node. +/// \param [in] pType - type of element to find. +#define MACRO_METADATA_FINDCREATE(pNode, pDEF_Var, pUSE_Var, pReference, pValue, pNE, pMetaClass, pMetaName, pType) \ + /* if "USE" defined then find already defined element. */ \ + if (!pUSE_Var.empty()) { \ + MACRO_USE_CHECKANDAPPLY(pNode, pDEF_Var, pUSE_Var, pType, pNE); \ + } else { \ + pNE = new pMetaClass(mNodeElementCur); \ + if (!pDEF_Var.empty()) pNE->ID = pDEF_Var; \ + \ + ((pMetaClass *)pNE)->Reference = pReference; \ + ((pMetaClass *)pNE)->Value = pValue; \ + /* also metadata node can contain childs */ \ + if (!isNodeEmpty(pNode)) \ + childrenReadMetadata(pNode, pNE, pMetaName); /* in that case node element will be added to child elements list of current node. */ \ + else \ + mNodeElementCur->Children.push_back(pNE); /* else - add element to child list manually */ \ + \ + NodeElement_List.push_back(pNE); /* add new element to elements list. */ \ + } /* if(!pUSE_Var.empty()) else */ \ + \ + do { \ + } while (false) + +// +void X3DImporter::readMetadataBoolean(XmlNode &node) { + std::string def, use; + std::string name, reference; + std::vector value; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "name", name); + XmlParser::getStdStrAttribute(node, "reference", reference); + X3DXmlHelper::getBooleanArrayAttribute(node, "value", value); + + MACRO_METADATA_FINDCREATE(node, def, use, reference, value, ne, X3DNodeElementMetaBoolean, "MetadataBoolean", ENET_MetaBoolean); +} + +// +void X3DImporter::readMetadataDouble(XmlNode &node) { + std::string def, use; + std::string name, reference; + std::vector value; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "name", name); + XmlParser::getStdStrAttribute(node, "reference", reference); + X3DXmlHelper::getDoubleArrayAttribute(node, "value", value); + + MACRO_METADATA_FINDCREATE(node, def, use, reference, value, ne, X3DNodeElementMetaDouble, "MetadataDouble", ENET_MetaDouble); +} + +// +void X3DImporter::readMetadataFloat(XmlNode &node) { + std::string def, use; + std::string name, reference; + std::vector value; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "name", name); + XmlParser::getStdStrAttribute(node, "reference", reference); + X3DXmlHelper::getFloatArrayAttribute(node, "value", value); + + MACRO_METADATA_FINDCREATE(node, def, use, reference, value, ne, X3DNodeElementMetaFloat, "MetadataFloat", ENET_MetaFloat); +} + +// +void X3DImporter::readMetadataInteger(XmlNode &node) { + std::string def, use; + std::string name, reference; + std::vector value; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "name", name); + XmlParser::getStdStrAttribute(node, "reference", reference); + X3DXmlHelper::getInt32ArrayAttribute(node, "value", value); + + MACRO_METADATA_FINDCREATE(node, def, use, reference, value, ne, X3DNodeElementMetaInt, "MetadataInteger", ENET_MetaInteger); +} + +// +void X3DImporter::readMetadataSet(XmlNode &node) { + std::string def, use; + std::string name, reference; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "name", name); + XmlParser::getStdStrAttribute(node, "reference", reference); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_MetaSet, ne); + } else { + ne = new X3DNodeElementMetaSet(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementMetaSet *)ne)->Reference = reference; + // also metadata node can contain childs + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "MetadataSet"); + else + mNodeElementCur->Children.push_back(ne); // made object as child to current element + + NodeElement_List.push_back(ne); // add new element to elements list. + } // if(!use.empty()) else +} + +// +void X3DImporter::readMetadataString(XmlNode &node) { + std::string def, use; + std::string name, reference; + std::vector value; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getStdStrAttribute(node, "name", name); + XmlParser::getStdStrAttribute(node, "reference", reference); + X3DXmlHelper::getStringArrayAttribute(node, "value", value); + + MACRO_METADATA_FINDCREATE(node, def, use, reference, value, ne, X3DNodeElementMetaString, "MetadataString", ENET_MetaString); +} + +}// namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Networking.cpp b/code/AssetLib/X3D/X3DImporter_Networking.cpp new file mode 100644 index 000000000..d5ef58397 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Networking.cpp @@ -0,0 +1,125 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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 X3DImporter_Networking.cpp +/// \brief Parsing data from nodes of "Networking" set of X3D. +/// date 2015-2016 +/// author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" + +// Header files, Assimp. +#include + +//#include + +namespace Assimp { + +//static std::regex pattern_parentDir(R"((^|/)[^/]+/../)"); +static std::string parentDir("/../"); + +// +void X3DImporter::readInline(XmlNode &node) { + std::string def, use; + bool load = true; + std::list url; + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "load", load); + X3DXmlHelper::getStringListAttribute(node, "url", url); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + X3DNodeElementBase *ne; + + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Group, ne); + } else { + ParseHelper_Group_Begin(true); // create new grouping element and go deeper if node has children. + // at this place new group mode created and made current, so we can name it. + if (!def.empty()) mNodeElementCur->ID = def; + + if (load && !url.empty()) { + std::string full_path = mpIOHandler->CurrentDirectory() + url.front(); + + //full_path = std::regex_replace(full_path, pattern_parentDir, "$1"); + for (std::string::size_type pos = full_path.find(parentDir); pos != std::string::npos; pos = full_path.find(parentDir, pos)) { + if (pos > 0) { + std::string::size_type pos2 = full_path.rfind('/', pos - 1); + if (pos2 != std::string::npos) { + full_path.erase(pos2, pos - pos2 + 3); + pos = pos2; + } else { + full_path.erase(0, pos + 4); + pos = 0; + } + } else { + pos += 3; + } + } + // Attribute "url" can contain list of strings. But we need only one - first. + std::string::size_type slashPos = full_path.find_last_of("\\/"); + mpIOHandler->PushDirectory(slashPos == std::string::npos ? std::string() : full_path.substr(0, slashPos + 1)); + ParseFile(full_path, mpIOHandler); + mpIOHandler->PopDirectory(); + } + + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) childrenReadMetadata(node, mNodeElementCur, "Inline"); + + // exit from node in that place + ParseHelper_Node_Exit(); + } // if(!use.empty()) else +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Node.hpp b/code/AssetLib/X3D/X3DImporter_Node.hpp new file mode 100644 index 000000000..8079cbf11 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Node.hpp @@ -0,0 +1,459 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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 X3DImporter_Node.hpp +/// \brief Elements of scene graph. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef INCLUDED_AI_X3D_IMPORTER_NODE_H +#define INCLUDED_AI_X3D_IMPORTER_NODE_H + +// Header files, Assimp. +#include + +#include +#include + +enum X3DElemType { + ENET_Group, ///< Element has type "Group". + ENET_MetaBoolean, ///< Element has type "Metadata boolean". + ENET_MetaDouble, ///< Element has type "Metadata double". + ENET_MetaFloat, ///< Element has type "Metadata float". + ENET_MetaInteger, ///< Element has type "Metadata integer". + ENET_MetaSet, ///< Element has type "Metadata set". + ENET_MetaString, ///< Element has type "Metadata string". + ENET_Arc2D, ///< Element has type "Arc2D". + ENET_ArcClose2D, ///< Element has type "ArcClose2D". + ENET_Circle2D, ///< Element has type "Circle2D". + ENET_Disk2D, ///< Element has type "Disk2D". + ENET_Polyline2D, ///< Element has type "Polyline2D". + ENET_Polypoint2D, ///< Element has type "Polypoint2D". + ENET_Rectangle2D, ///< Element has type "Rectangle2D". + ENET_TriangleSet2D, ///< Element has type "TriangleSet2D". + ENET_Box, ///< Element has type "Box". + ENET_Cone, ///< Element has type "Cone". + ENET_Cylinder, ///< Element has type "Cylinder". + ENET_Sphere, ///< Element has type "Sphere". + ENET_ElevationGrid, ///< Element has type "ElevationGrid". + ENET_Extrusion, ///< Element has type "Extrusion". + ENET_Coordinate, ///< Element has type "Coordinate". + ENET_Normal, ///< Element has type "Normal". + ENET_TextureCoordinate, ///< Element has type "TextureCoordinate". + ENET_IndexedFaceSet, ///< Element has type "IndexedFaceSet". + ENET_IndexedLineSet, ///< Element has type "IndexedLineSet". + ENET_IndexedTriangleSet, ///< Element has type "IndexedTriangleSet". + ENET_IndexedTriangleFanSet, ///< Element has type "IndexedTriangleFanSet". + ENET_IndexedTriangleStripSet, ///< Element has type "IndexedTriangleStripSet". + ENET_LineSet, ///< Element has type "LineSet". + ENET_PointSet, ///< Element has type "PointSet". + ENET_TriangleSet, ///< Element has type "TriangleSet". + ENET_TriangleFanSet, ///< Element has type "TriangleFanSet". + ENET_TriangleStripSet, ///< Element has type "TriangleStripSet". + ENET_Color, ///< Element has type "Color". + ENET_ColorRGBA, ///< Element has type "ColorRGBA". + ENET_Shape, ///< Element has type "Shape". + ENET_Appearance, ///< Element has type "Appearance". + ENET_Material, ///< Element has type "Material". + ENET_ImageTexture, ///< Element has type "ImageTexture". + ENET_TextureTransform, ///< Element has type "TextureTransform". + ENET_DirectionalLight, ///< Element has type "DirectionalLight". + ENET_PointLight, ///< Element has type "PointLight". + ENET_SpotLight, ///< Element has type "SpotLight". + + ENET_Invalid ///< Element has invalid type and possible contain invalid data. +}; + +struct X3DNodeElementBase { + X3DNodeElementBase *Parent; + std::string ID; + std::list Children; + X3DElemType Type; + +protected: + X3DNodeElementBase(X3DElemType type, X3DNodeElementBase *pParent) : + Type(type), Parent(pParent) { + // empty + } +}; + +/// This struct hold value. +struct X3DNodeElementColor : X3DNodeElementBase { + std::list Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementColor(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Color, pParent) {} + +}; // struct X3DNodeElementColor + +/// This struct hold value. +struct X3DNodeElementColorRGBA : X3DNodeElementBase { + std::list Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementColorRGBA(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_ColorRGBA, pParent) {} + +}; // struct X3DNodeElementColorRGBA + +/// This struct hold value. +struct X3DNodeElementCoordinate : public X3DNodeElementBase { + std::list Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementCoordinate(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Coordinate, pParent) {} + +}; // struct X3DNodeElementCoordinate + +/// This struct hold value. +struct X3DNodeElementNormal : X3DNodeElementBase { + std::list Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementNormal(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Normal, pParent) {} + +}; // struct X3DNodeElementNormal + +/// This struct hold value. +struct X3DNodeElementTextureCoordinate : X3DNodeElementBase { + std::list Value; ///< Stored value. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementTextureCoordinate(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_TextureCoordinate, pParent) {} + +}; // struct X3DNodeElementTextureCoordinate + +/// Two-dimensional figure. +struct X3DNodeElementGeometry2D : X3DNodeElementBase { + std::list Vertices; ///< Vertices list. + size_t NumIndices; ///< Number of indices in one face. + bool Solid; ///< Flag: if true then render must use back-face culling, else render must draw both sides of object. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + X3DNodeElementGeometry2D(X3DElemType pType, X3DNodeElementBase *pParent) : + X3DNodeElementBase(pType, pParent), Solid(true) {} + +}; // class X3DNodeElementGeometry2D + +/// Three-dimensional body. +struct X3DNodeElementGeometry3D : X3DNodeElementBase { + std::list Vertices; ///< Vertices list. + size_t NumIndices; ///< Number of indices in one face. + bool Solid; ///< Flag: if true then render must use back-face culling, else render must draw both sides of object. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + X3DNodeElementGeometry3D(X3DElemType pType, X3DNodeElementBase *pParent) : + X3DNodeElementBase(pType, pParent), Vertices(), NumIndices(0), Solid(true) { + // empty + } +}; // class X3DNodeElementGeometry3D + +/// Uniform rectangular grid of varying height. +struct X3DNodeElementElevationGrid : X3DNodeElementGeometry3D { + bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). + bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). + /// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are + /// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced. + float CreaseAngle; + std::vector CoordIdx; ///< Coordinates list by faces. In X3D format: "-1" - delimiter for faces. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + X3DNodeElementElevationGrid(X3DElemType pType, X3DNodeElementBase *pParent) : + X3DNodeElementGeometry3D(pType, pParent) {} +}; // class X3DNodeElementIndexedSet + +/// Shape with indexed vertices. +struct X3DNodeElementIndexedSet : public X3DNodeElementGeometry3D { + /// The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors + /// used in the lighting model equations. If ccw is TRUE, the normals shall follow the right hand rule; the orientation of each normal with respect to + /// the vertices (taken in order) shall be such that the vertices appear to be oriented in a counterclockwise order when the vertices are viewed (in the + /// local coordinate system of the Shape) from the opposite direction as the normal. If ccw is FALSE, the normals shall be oriented in the opposite + /// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the + /// ccw field, results are undefined. + bool CCW; + std::vector ColorIndex; ///< Field to specify the polygonal faces by indexing into the or . + bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). + /// The convex field indicates whether all polygons in the shape are convex (TRUE). A polygon is convex if it is planar, does not intersect itself, + /// and all of the interior angles at its vertices are less than 180 degrees. Non planar and self intersecting polygons may produce undefined results + /// even if the convex field is FALSE. + bool Convex; + std::vector CoordIndex; ///< Field to specify the polygonal faces by indexing into the . + /// If the angle between the geometric normals of two adjacent faces is less than the crease angle, normals shall be calculated so that the faces are + /// shaded smoothly across the edge; otherwise, normals shall be calculated so that a lighting discontinuity across the edge is produced. + float CreaseAngle; + std::vector NormalIndex; ///< Field to specify the polygonal faces by indexing into the . + bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). + std::vector TexCoordIndex; ///< Field to specify the polygonal faces by indexing into the . + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + X3DNodeElementIndexedSet(X3DElemType pType, X3DNodeElementBase *pParent) : + X3DNodeElementGeometry3D(pType, pParent) {} +}; // class X3DNodeElementIndexedSet + +/// Shape with set of vertices. +struct X3DNodeElementSet : X3DNodeElementGeometry3D { + /// The ccw field defines the ordering of the vertex coordinates of the geometry with respect to user-given or automatically generated normal vectors + /// used in the lighting model equations. If ccw is TRUE, the normals shall follow the right hand rule; the orientation of each normal with respect to + /// the vertices (taken in order) shall be such that the vertices appear to be oriented in a counterclockwise order when the vertices are viewed (in the + /// local coordinate system of the Shape) from the opposite direction as the normal. If ccw is FALSE, the normals shall be oriented in the opposite + /// direction. If normals are not generated but are supplied using a Normal node, and the orientation of the normals does not match the setting of the + /// ccw field, results are undefined. + bool CCW; + bool ColorPerVertex; ///< If true then colors are defined for every vertex, else for every face(line). + bool NormalPerVertex; ///< If true then normals are defined for every vertex, else for every face(line). + std::vector CoordIndex; ///< Field to specify the polygonal faces by indexing into the . + std::vector NormalIndex; ///< Field to specify the polygonal faces by indexing into the . + std::vector TexCoordIndex; ///< Field to specify the polygonal faces by indexing into the . + std::vector VertexCount; ///< Field describes how many vertices are to be used in each polyline(polygon) from the field. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + X3DNodeElementSet(X3DElemType type, X3DNodeElementBase *pParent) : + X3DNodeElementGeometry3D(type, pParent) {} + +}; // class X3DNodeElementSet + +/// This struct hold value. +struct X3DNodeElementShape : X3DNodeElementBase { + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementShape(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Shape, pParent) {} +}; // struct X3DNodeElementShape + +/// This struct hold value. +struct X3DNodeElementAppearance : public X3DNodeElementBase { + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementAppearance(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Appearance, pParent) {} + +}; // struct X3DNodeElementAppearance + +struct X3DNodeElementMaterial : public X3DNodeElementBase { + float AmbientIntensity; ///< Specifies how much ambient light from light sources this surface shall reflect. + aiColor3D DiffuseColor; ///< Reflects all X3D light sources depending on the angle of the surface with respect to the light source. + aiColor3D EmissiveColor; ///< Models "glowing" objects. This can be useful for displaying pre-lit models. + float Shininess; ///< Lower shininess values produce soft glows, while higher values result in sharper, smaller highlights. + aiColor3D SpecularColor; ///< The specularColor and shininess fields determine the specular highlights. + float Transparency; ///< Specifies how "clear" an object is, with 1.0 being completely transparent, and 0.0 completely opaque. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pType - type of geometry object. + X3DNodeElementMaterial(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_Material, pParent), + AmbientIntensity(0.0f), + DiffuseColor(), + EmissiveColor(), + Shininess(0.0f), + SpecularColor(), + Transparency(1.0f) { + // empty + } +}; // class X3DNodeElementMaterial + +/// This struct hold value. +struct X3DNodeElementImageTexture : X3DNodeElementBase { + /// RepeatS and RepeatT, that specify how the texture wraps in the S and T directions. If repeatS is TRUE (the default), the texture map is repeated + /// outside the [0.0, 1.0] texture coordinate range in the S direction so that it fills the shape. If repeatS is FALSE, the texture coordinates are + /// clamped in the S direction to lie within the [0.0, 1.0] range. The repeatT field is analogous to the repeatS field. + bool RepeatS; + bool RepeatT; ///< See \ref RepeatS. + std::string URL; ///< URL of the texture. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementImageTexture(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_ImageTexture, pParent) {} + +}; // struct X3DNodeElementImageTexture + +/// This struct hold value. +struct X3DNodeElementTextureTransform : X3DNodeElementBase { + aiVector2D Center; ///< Specifies a translation offset in texture coordinate space about which the rotation and scale fields are applied. + float Rotation; ///< Specifies a rotation in angle base units of the texture coordinates about the center point after the scale has been applied. + aiVector2D Scale; ///< Specifies a scaling factor in S and T of the texture coordinates about the center point. + aiVector2D Translation; ///< Specifies a translation of the texture coordinates. + + /// Constructor + /// \param [in] pParent - pointer to parent node. + X3DNodeElementTextureTransform(X3DNodeElementBase *pParent) : + X3DNodeElementBase(X3DElemType::ENET_TextureTransform, pParent) {} + +}; // struct X3DNodeElementTextureTransform + +struct X3DNodeElementGroup : X3DNodeElementBase { + aiMatrix4x4 Transformation; ///< Transformation matrix. + + /// As you know node elements can use already defined node elements when attribute "USE" is defined. + /// Standard search when looking for an element in the whole scene graph, existing at this moment. + /// If a node is marked as static, the children(or lower) can not search for elements in the nodes upper then static. + bool Static; + + bool UseChoice; ///< Flag: if true then use number from \ref Choice to choose what the child will be kept. + int32_t Choice; ///< Number of the child which will be kept. + + /// Constructor. + /// \param [in] pParent - pointer to parent node. + /// \param [in] pStatic - static node flag. + X3DNodeElementGroup(X3DNodeElementBase *pParent, const bool pStatic = false) : + X3DNodeElementBase(X3DElemType::ENET_Group, pParent), Static(pStatic), UseChoice(false) {} +}; + +struct X3DNodeElementMeta : X3DNodeElementBase { + std::string Name; ///< Name of metadata object. + std::string Reference; + + virtual ~X3DNodeElementMeta() { + // empty + } + +protected: + X3DNodeElementMeta(X3DElemType type, X3DNodeElementBase *parent) : + X3DNodeElementBase(type, parent) { + // empty + } +}; + +struct X3DNodeElementMetaBoolean : X3DNodeElementMeta { + std::vector Value; ///< Stored value. + + explicit X3DNodeElementMetaBoolean(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaBoolean, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaDouble : X3DNodeElementMeta { + std::vector Value; ///< Stored value. + + explicit X3DNodeElementMetaDouble(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaDouble, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaFloat : public X3DNodeElementMeta { + std::vector Value; ///< Stored value. + + explicit X3DNodeElementMetaFloat(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaFloat, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaInt : public X3DNodeElementMeta { + std::vector Value; ///< Stored value. + + explicit X3DNodeElementMetaInt(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaInteger, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaSet : public X3DNodeElementMeta { + std::list Value; ///< Stored value. + + explicit X3DNodeElementMetaSet(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaSet, pParent) { + // empty + } +}; + +struct X3DNodeElementMetaString : X3DNodeElementMeta { + std::vector Value; ///< Stored value. + + explicit X3DNodeElementMetaString(X3DNodeElementBase *pParent) : + X3DNodeElementMeta(X3DElemType::ENET_MetaString, pParent) { + // empty + } +}; + +/// \struct X3DNodeElementLight +/// This struct hold value. +struct X3DNodeElementLight : X3DNodeElementBase { + float AmbientIntensity; ///< Specifies the intensity of the ambient emission from the light. + aiColor3D Color; ///< specifies the spectral colour properties of both the direct and ambient light emission as an RGB value. + aiVector3D Direction; ///< Specifies the direction vector of the illumination emanating from the light source in the local coordinate system. + /// \var Global + /// Field that determines whether the light is global or scoped. Global lights illuminate all objects that fall within their volume of lighting influence. + /// Scoped lights only illuminate objects that are in the same transformation hierarchy as the light. + bool Global; + float Intensity; ///< Specifies the brightness of the direct emission from the light. + /// \var Attenuation + /// PointLight node's illumination falls off with distance as specified by three attenuation coefficients. The attenuation factor + /// is: "1 / max(attenuation[0] + attenuation[1] * r + attenuation[2] * r2, 1)", where r is the distance from the light to the surface being illuminated. + aiVector3D Attenuation; + aiVector3D Location; ///< Specifies a translation offset of the centre point of the light source from the light's local coordinate system origin. + float Radius; ///< Specifies the radial extent of the solid angle and the maximum distance from location that may be illuminated by the light source. + float BeamWidth; ///< Specifies an inner solid angle in which the light source emits light at uniform full intensity. + float CutOffAngle; ///< The light source's emission intensity drops off from the inner solid angle (beamWidth) to the outer solid angle (cutOffAngle). + + /// Constructor + /// \param [in] pParent - pointer to parent node. + /// \param [in] pLightType - type of the light source. + X3DNodeElementLight(X3DElemType pLightType, X3DNodeElementBase *pParent) : + X3DNodeElementBase(pLightType, pParent) {} + +}; // struct X3DNodeElementLight + +#endif // INCLUDED_AI_X3D_IMPORTER_NODE_H diff --git a/code/AssetLib/X3D/X3DImporter_Postprocess.cpp b/code/AssetLib/X3D/X3DImporter_Postprocess.cpp new file mode 100644 index 000000000..24e03ca16 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Postprocess.cpp @@ -0,0 +1,731 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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 X3DImporter_Postprocess.cpp +/// \brief Convert built scenegraph and objects to Assimp scenegraph. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DGeoHelper.h" +#include "X3DImporter.hpp" + +// Header files, Assimp. +#include +#include +#include + +// Header files, stdlib. +#include +#include +#include + +namespace Assimp { + +aiMatrix4x4 X3DImporter::PostprocessHelper_Matrix_GlobalToCurrent() const { + X3DNodeElementBase *cur_node; + std::list matr; + aiMatrix4x4 out_matr; + + // starting walk from current element to root + cur_node = mNodeElementCur; + if (cur_node != nullptr) { + do { + // if cur_node is group then store group transformation matrix in list. + if (cur_node->Type == X3DElemType::ENET_Group) matr.push_back(((X3DNodeElementGroup *)cur_node)->Transformation); + + cur_node = cur_node->Parent; + } while (cur_node != nullptr); + } + + // multiplicate all matrices in reverse order + for (std::list::reverse_iterator rit = matr.rbegin(); rit != matr.rend(); ++rit) + out_matr = out_matr * (*rit); + + return out_matr; +} + +void X3DImporter::PostprocessHelper_CollectMetadata(const X3DNodeElementBase &pNodeElement, std::list &pList) const { + // walk through childs and find for metadata. + for (std::list::const_iterator el_it = pNodeElement.Children.begin(); el_it != pNodeElement.Children.end(); ++el_it) { + if (((*el_it)->Type == X3DElemType::ENET_MetaBoolean) || ((*el_it)->Type == X3DElemType::ENET_MetaDouble) || + ((*el_it)->Type == X3DElemType::ENET_MetaFloat) || ((*el_it)->Type == X3DElemType::ENET_MetaInteger) || + ((*el_it)->Type == X3DElemType::ENET_MetaString)) { + pList.push_back(*el_it); + } else if ((*el_it)->Type == X3DElemType::ENET_MetaSet) { + PostprocessHelper_CollectMetadata(**el_it, pList); + } + } // for(std::list::const_iterator el_it = pNodeElement.Children.begin(); el_it != pNodeElement.Children.end(); el_it++) +} + +bool X3DImporter::PostprocessHelper_ElementIsMetadata(const X3DElemType pType) const { + if ((pType == X3DElemType::ENET_MetaBoolean) || (pType == X3DElemType::ENET_MetaDouble) || + (pType == X3DElemType::ENET_MetaFloat) || (pType == X3DElemType::ENET_MetaInteger) || + (pType == X3DElemType::ENET_MetaString) || (pType == X3DElemType::ENET_MetaSet)) { + return true; + } else { + return false; + } +} + +bool X3DImporter::PostprocessHelper_ElementIsMesh(const X3DElemType pType) const { + if ((pType == X3DElemType::ENET_Arc2D) || (pType == X3DElemType::ENET_ArcClose2D) || + (pType == X3DElemType::ENET_Box) || (pType == X3DElemType::ENET_Circle2D) || + (pType == X3DElemType::ENET_Cone) || (pType == X3DElemType::ENET_Cylinder) || + (pType == X3DElemType::ENET_Disk2D) || (pType == X3DElemType::ENET_ElevationGrid) || + (pType == X3DElemType::ENET_Extrusion) || (pType == X3DElemType::ENET_IndexedFaceSet) || + (pType == X3DElemType::ENET_IndexedLineSet) || (pType == X3DElemType::ENET_IndexedTriangleFanSet) || + (pType == X3DElemType::ENET_IndexedTriangleSet) || (pType == X3DElemType::ENET_IndexedTriangleStripSet) || + (pType == X3DElemType::ENET_PointSet) || (pType == X3DElemType::ENET_LineSet) || + (pType == X3DElemType::ENET_Polyline2D) || (pType == X3DElemType::ENET_Polypoint2D) || + (pType == X3DElemType::ENET_Rectangle2D) || (pType == X3DElemType::ENET_Sphere) || + (pType == X3DElemType::ENET_TriangleFanSet) || (pType == X3DElemType::ENET_TriangleSet) || + (pType == X3DElemType::ENET_TriangleSet2D) || (pType == X3DElemType::ENET_TriangleStripSet)) { + return true; + } else { + return false; + } +} + +void X3DImporter::Postprocess_BuildLight(const X3DNodeElementBase &pNodeElement, std::list &pSceneLightList) const { + const X3DNodeElementLight &ne = *((X3DNodeElementLight *)&pNodeElement); + aiMatrix4x4 transform_matr = PostprocessHelper_Matrix_GlobalToCurrent(); + aiLight *new_light = new aiLight; + + new_light->mName = ne.ID; + new_light->mColorAmbient = ne.Color * ne.AmbientIntensity; + new_light->mColorDiffuse = ne.Color * ne.Intensity; + new_light->mColorSpecular = ne.Color * ne.Intensity; + switch (pNodeElement.Type) { + case X3DElemType::ENET_DirectionalLight: + new_light->mType = aiLightSource_DIRECTIONAL; + new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr; + + break; + case X3DElemType::ENET_PointLight: + new_light->mType = aiLightSource_POINT; + new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr; + new_light->mAttenuationConstant = ne.Attenuation.x; + new_light->mAttenuationLinear = ne.Attenuation.y; + new_light->mAttenuationQuadratic = ne.Attenuation.z; + + break; + case X3DElemType::ENET_SpotLight: + new_light->mType = aiLightSource_SPOT; + new_light->mPosition = ne.Location, new_light->mPosition *= transform_matr; + new_light->mDirection = ne.Direction, new_light->mDirection *= transform_matr; + new_light->mAttenuationConstant = ne.Attenuation.x; + new_light->mAttenuationLinear = ne.Attenuation.y; + new_light->mAttenuationQuadratic = ne.Attenuation.z; + new_light->mAngleInnerCone = ne.BeamWidth; + new_light->mAngleOuterCone = ne.CutOffAngle; + + break; + default: + throw DeadlyImportError("Postprocess_BuildLight. Unknown type of light: " + ai_to_string(pNodeElement.Type) + "."); + } + + pSceneLightList.push_back(new_light); +} + +void X3DImporter::Postprocess_BuildMaterial(const X3DNodeElementBase &pNodeElement, aiMaterial **pMaterial) const { + // check argument + if (pMaterial == nullptr) throw DeadlyImportError("Postprocess_BuildMaterial. pMaterial is nullptr."); + if (*pMaterial != nullptr) throw DeadlyImportError("Postprocess_BuildMaterial. *pMaterial must be nullptr."); + + *pMaterial = new aiMaterial; + aiMaterial &taimat = **pMaterial; // creating alias for convenience. + + // at this point pNodeElement point to node. Walk through childs and add all stored data. + for (std::list::const_iterator el_it = pNodeElement.Children.begin(); el_it != pNodeElement.Children.end(); ++el_it) { + if ((*el_it)->Type == X3DElemType::ENET_Material) { + aiColor3D tcol3; + float tvalf; + X3DNodeElementMaterial &tnemat = *((X3DNodeElementMaterial *)*el_it); + + tcol3.r = tnemat.AmbientIntensity, tcol3.g = tnemat.AmbientIntensity, tcol3.b = tnemat.AmbientIntensity; + taimat.AddProperty(&tcol3, 1, AI_MATKEY_COLOR_AMBIENT); + taimat.AddProperty(&tnemat.DiffuseColor, 1, AI_MATKEY_COLOR_DIFFUSE); + taimat.AddProperty(&tnemat.EmissiveColor, 1, AI_MATKEY_COLOR_EMISSIVE); + taimat.AddProperty(&tnemat.SpecularColor, 1, AI_MATKEY_COLOR_SPECULAR); + tvalf = 1; + taimat.AddProperty(&tvalf, 1, AI_MATKEY_SHININESS_STRENGTH); + taimat.AddProperty(&tnemat.Shininess, 1, AI_MATKEY_SHININESS); + tvalf = 1.0f - tnemat.Transparency; + taimat.AddProperty(&tvalf, 1, AI_MATKEY_OPACITY); + } // if((*el_it)->Type == X3DElemType::ENET_Material) + else if ((*el_it)->Type == X3DElemType::ENET_ImageTexture) { + X3DNodeElementImageTexture &tnetex = *((X3DNodeElementImageTexture *)*el_it); + aiString url_str(tnetex.URL.c_str()); + int mode = aiTextureOp_Multiply; + + taimat.AddProperty(&url_str, AI_MATKEY_TEXTURE_DIFFUSE(0)); + taimat.AddProperty(&tnetex.RepeatS, 1, AI_MATKEY_MAPPINGMODE_U_DIFFUSE(0)); + taimat.AddProperty(&tnetex.RepeatT, 1, AI_MATKEY_MAPPINGMODE_V_DIFFUSE(0)); + taimat.AddProperty(&mode, 1, AI_MATKEY_TEXOP_DIFFUSE(0)); + } // else if((*el_it)->Type == X3DElemType::ENET_ImageTexture) + else if ((*el_it)->Type == X3DElemType::ENET_TextureTransform) { + aiUVTransform trans; + X3DNodeElementTextureTransform &tnetextr = *((X3DNodeElementTextureTransform *)*el_it); + + trans.mTranslation = tnetextr.Translation - tnetextr.Center; + trans.mScaling = tnetextr.Scale; + trans.mRotation = tnetextr.Rotation; + taimat.AddProperty(&trans, 1, AI_MATKEY_UVTRANSFORM_DIFFUSE(0)); + } // else if((*el_it)->Type == X3DElemType::ENET_TextureTransform) + } // for(std::list::const_iterator el_it = pNodeElement.Children.begin(); el_it != pNodeElement.Children.end(); el_it++) +} + +void X3DImporter::Postprocess_BuildMesh(const X3DNodeElementBase &pNodeElement, aiMesh **pMesh) const { + // check argument + if (pMesh == nullptr) throw DeadlyImportError("Postprocess_BuildMesh. pMesh is nullptr."); + if (*pMesh != nullptr) throw DeadlyImportError("Postprocess_BuildMesh. *pMesh must be nullptr."); + + /************************************************************************************************************************************/ + /************************************************************ Geometry2D ************************************************************/ + /************************************************************************************************************************************/ + if ((pNodeElement.Type == X3DElemType::ENET_Arc2D) || (pNodeElement.Type == X3DElemType::ENET_ArcClose2D) || + (pNodeElement.Type == X3DElemType::ENET_Circle2D) || (pNodeElement.Type == X3DElemType::ENET_Disk2D) || + (pNodeElement.Type == X3DElemType::ENET_Polyline2D) || (pNodeElement.Type == X3DElemType::ENET_Polypoint2D) || + (pNodeElement.Type == X3DElemType::ENET_Rectangle2D) || (pNodeElement.Type == X3DElemType::ENET_TriangleSet2D)) { + X3DNodeElementGeometry2D &tnemesh = *((X3DNodeElementGeometry2D *)&pNodeElement); // create alias for convenience + std::vector tarr; + + tarr.reserve(tnemesh.Vertices.size()); + for (std::list::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); ++it) + tarr.push_back(*it); + *pMesh = StandardShapes::MakeMesh(tarr, static_cast(tnemesh.NumIndices)); // create mesh from vertices using Assimp help. + + return; // mesh is build, nothing to do anymore. + } + /************************************************************************************************************************************/ + /************************************************************ Geometry3D ************************************************************/ + /************************************************************************************************************************************/ + // + // Predefined figures + // + if ((pNodeElement.Type == X3DElemType::ENET_Box) || (pNodeElement.Type == X3DElemType::ENET_Cone) || + (pNodeElement.Type == X3DElemType::ENET_Cylinder) || (pNodeElement.Type == X3DElemType::ENET_Sphere)) { + X3DNodeElementGeometry3D &tnemesh = *((X3DNodeElementGeometry3D *)&pNodeElement); // create alias for convenience + std::vector tarr; + + tarr.reserve(tnemesh.Vertices.size()); + for (std::list::iterator it = tnemesh.Vertices.begin(); it != tnemesh.Vertices.end(); ++it) + tarr.push_back(*it); + + *pMesh = StandardShapes::MakeMesh(tarr, static_cast(tnemesh.NumIndices)); // create mesh from vertices using Assimp help. + + return; // mesh is build, nothing to do anymore. + } + // + // Parametric figures + // + if (pNodeElement.Type == X3DElemType::ENET_ElevationGrid) { + X3DNodeElementElevationGrid &tnemesh = *((X3DNodeElementElevationGrid *)&pNodeElement); // create alias for convenience + + // at first create mesh from existing vertices. + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIdx, tnemesh.Vertices); + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Normal) + X3DGeoHelper::add_normal(**pMesh, ((X3DNodeElementNormal *)*ch_it)->Value, tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate) + X3DGeoHelper::add_tex_coord(**pMesh, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of ElevationGrid: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_ElevationGrid) + // + // Indexed primitives sets + // + if (pNodeElement.Type == X3DElemType::ENET_IndexedFaceSet) { + X3DNodeElementIndexedSet &tnemesh = *((X3DNodeElementIndexedSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColorRGBA *)*ch_it)->Value, + tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == X3DElemType::ENET_Normal) + X3DGeoHelper::add_normal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((X3DNodeElementNormal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate) + X3DGeoHelper::add_tex_coord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedFaceSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_IndexedFaceSet) + + if (pNodeElement.Type == X3DElemType::ENET_IndexedLineSet) { + X3DNodeElementIndexedSet &tnemesh = *((X3DNodeElementIndexedSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColorRGBA *)*ch_it)->Value, + tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedLineSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_IndexedLineSet) + + if ((pNodeElement.Type == X3DElemType::ENET_IndexedTriangleSet) || + (pNodeElement.Type == X3DElemType::ENET_IndexedTriangleFanSet) || + (pNodeElement.Type == X3DElemType::ENET_IndexedTriangleStripSet)) { + X3DNodeElementIndexedSet &tnemesh = *((X3DNodeElementIndexedSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, tnemesh.CoordIndex, tnemesh.ColorIndex, ((X3DNodeElementColorRGBA *)*ch_it)->Value, + tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == X3DElemType::ENET_Normal) + X3DGeoHelper::add_normal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((X3DNodeElementNormal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate) + X3DGeoHelper::add_tex_coord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of IndexedTriangleSet or IndexedTriangleFanSet, or \ + IndexedTriangleStripSet: " + + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if((pNodeElement.Type == X3DElemType::ENET_IndexedTriangleFanSet) || (pNodeElement.Type == X3DElemType::ENET_IndexedTriangleStripSet)) + + if (pNodeElement.Type == X3DElemType::ENET_Extrusion) { + X3DNodeElementIndexedSet &tnemesh = *((X3DNodeElementIndexedSet *)&pNodeElement); // create alias for convenience + + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, tnemesh.Vertices); + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_Extrusion) + + // + // Primitives sets + // + if (pNodeElement.Type == X3DElemType::ENET_PointSet) { + X3DNodeElementSet &tnemesh = *((X3DNodeElementSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + std::vector vec_copy; + + vec_copy.reserve(((X3DNodeElementCoordinate *)*ch_it)->Value.size()); + for (std::list::const_iterator it = ((X3DNodeElementCoordinate *)*ch_it)->Value.begin(); + it != ((X3DNodeElementCoordinate *)*ch_it)->Value.end(); ++it) { + vec_copy.push_back(*it); + } + + *pMesh = StandardShapes::MakeMesh(vec_copy, 1); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, true); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, true); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of PointSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_PointSet) + + if (pNodeElement.Type == X3DElemType::ENET_LineSet) { + X3DNodeElementSet &tnemesh = *((X3DNodeElementSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, true); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, true); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of LineSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_LineSet) + + if (pNodeElement.Type == X3DElemType::ENET_TriangleFanSet) { + X3DNodeElementSet &tnemesh = *((X3DNodeElementSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if (nullptr == *pMesh) { + break; + } + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == X3DElemType::ENET_Normal) + X3DGeoHelper::add_normal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((X3DNodeElementNormal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate) + X3DGeoHelper::add_tex_coord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeFanSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_TriangleFanSet) + + if (pNodeElement.Type == X3DElemType::ENET_TriangleSet) { + X3DNodeElementSet &tnemesh = *((X3DNodeElementSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + std::vector vec_copy; + + vec_copy.reserve(((X3DNodeElementCoordinate *)*ch_it)->Value.size()); + for (std::list::const_iterator it = ((X3DNodeElementCoordinate *)*ch_it)->Value.begin(); + it != ((X3DNodeElementCoordinate *)*ch_it)->Value.end(); ++it) { + vec_copy.push_back(*it); + } + + *pMesh = StandardShapes::MakeMesh(vec_copy, 3); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == X3DElemType::ENET_Normal) + X3DGeoHelper::add_normal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((X3DNodeElementNormal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate) + X3DGeoHelper::add_tex_coord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TrianlgeSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_TriangleSet) + + if (pNodeElement.Type == X3DElemType::ENET_TriangleStripSet) { + X3DNodeElementSet &tnemesh = *((X3DNodeElementSet *)&pNodeElement); // create alias for convenience + + // at first search for node and create mesh. + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + *pMesh = X3DGeoHelper::make_mesh(tnemesh.CoordIndex, ((X3DNodeElementCoordinate *)*ch_it)->Value); + } + } + + // copy additional information from children + for (std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) { + ai_assert(*pMesh); + if ((*ch_it)->Type == X3DElemType::ENET_Color) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColor *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_ColorRGBA) + X3DGeoHelper::add_color(**pMesh, ((X3DNodeElementColorRGBA *)*ch_it)->Value, tnemesh.ColorPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_Coordinate) { + } // skip because already read when mesh created. + else if ((*ch_it)->Type == X3DElemType::ENET_Normal) + X3DGeoHelper::add_normal(**pMesh, tnemesh.CoordIndex, tnemesh.NormalIndex, ((X3DNodeElementNormal *)*ch_it)->Value, + tnemesh.NormalPerVertex); + else if ((*ch_it)->Type == X3DElemType::ENET_TextureCoordinate) + X3DGeoHelper::add_tex_coord(**pMesh, tnemesh.CoordIndex, tnemesh.TexCoordIndex, ((X3DNodeElementTextureCoordinate *)*ch_it)->Value); + else + throw DeadlyImportError("Postprocess_BuildMesh. Unknown child of TriangleStripSet: " + ai_to_string((*ch_it)->Type) + "."); + } // for(std::list::iterator ch_it = tnemesh.Children.begin(); ch_it != tnemesh.Children.end(); ++ch_it) + + return; // mesh is build, nothing to do anymore. + } // if(pNodeElement.Type == X3DElemType::ENET_TriangleStripSet) + + throw DeadlyImportError("Postprocess_BuildMesh. Unknown mesh type: " + ai_to_string(pNodeElement.Type) + "."); +} + +void X3DImporter::Postprocess_BuildNode(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode, std::list &pSceneMeshList, + std::list &pSceneMaterialList, std::list &pSceneLightList) const { + std::list::const_iterator chit_begin = pNodeElement.Children.begin(); + std::list::const_iterator chit_end = pNodeElement.Children.end(); + std::list SceneNode_Child; + std::list SceneNode_Mesh; + + // At first read all metadata + Postprocess_CollectMetadata(pNodeElement, pSceneNode); + // check if we have deal with grouping node. Which can contain transformation or switch + if (pNodeElement.Type == X3DElemType::ENET_Group) { + const X3DNodeElementGroup &tne_group = *((X3DNodeElementGroup *)&pNodeElement); // create alias for convenience + + pSceneNode.mTransformation = tne_group.Transformation; + if (tne_group.UseChoice) { + // If Choice is less than zero or greater than the number of nodes in the children field, nothing is chosen. + if ((tne_group.Choice < 0) || ((size_t)tne_group.Choice >= pNodeElement.Children.size())) { + chit_begin = pNodeElement.Children.end(); + chit_end = pNodeElement.Children.end(); + } else { + for (size_t i = 0; i < (size_t)tne_group.Choice; i++) + ++chit_begin; // forward iterator to chosen node. + + chit_end = chit_begin; + ++chit_end; // point end iterator to next element after chosen node. + } + } // if(tne_group.UseChoice) + } // if(pNodeElement.Type == X3DElemType::ENET_Group) + + // Reserve memory for fast access and check children. + for (std::list::const_iterator it = chit_begin; it != chit_end; ++it) { // in this loop we do not read metadata because it's already read at begin. + if ((*it)->Type == X3DElemType::ENET_Group) { + // if child is group then create new node and do recursive call. + aiNode *new_node = new aiNode; + + new_node->mName = (*it)->ID; + new_node->mParent = &pSceneNode; + SceneNode_Child.push_back(new_node); + Postprocess_BuildNode(**it, *new_node, pSceneMeshList, pSceneMaterialList, pSceneLightList); + } else if ((*it)->Type == X3DElemType::ENET_Shape) { + // shape can contain only one geometry and one appearance nodes. + Postprocess_BuildShape(*((X3DNodeElementShape *)*it), SceneNode_Mesh, pSceneMeshList, pSceneMaterialList); + } else if (((*it)->Type == X3DElemType::ENET_DirectionalLight) || ((*it)->Type == X3DElemType::ENET_PointLight) || + ((*it)->Type == X3DElemType::ENET_SpotLight)) { + Postprocess_BuildLight(*((X3DNodeElementLight *)*it), pSceneLightList); + } else if (!PostprocessHelper_ElementIsMetadata((*it)->Type)) // skip metadata + { + throw DeadlyImportError("Postprocess_BuildNode. Unknown type: " + ai_to_string((*it)->Type) + "."); + } + } // for(std::list::const_iterator it = chit_begin; it != chit_end; it++) + + // copy data about children and meshes to aiNode. + if (!SceneNode_Child.empty()) { + std::list::const_iterator it = SceneNode_Child.begin(); + + pSceneNode.mNumChildren = static_cast(SceneNode_Child.size()); + pSceneNode.mChildren = new aiNode *[pSceneNode.mNumChildren]; + for (size_t i = 0; i < pSceneNode.mNumChildren; i++) + pSceneNode.mChildren[i] = *it++; + } + + if (!SceneNode_Mesh.empty()) { + std::list::const_iterator it = SceneNode_Mesh.begin(); + + pSceneNode.mNumMeshes = static_cast(SceneNode_Mesh.size()); + pSceneNode.mMeshes = new unsigned int[pSceneNode.mNumMeshes]; + for (size_t i = 0; i < pSceneNode.mNumMeshes; i++) + pSceneNode.mMeshes[i] = *it++; + } + + // that's all. return to previous deals +} + +void X3DImporter::Postprocess_BuildShape(const X3DNodeElementShape &pShapeNodeElement, std::list &pNodeMeshInd, + std::list &pSceneMeshList, std::list &pSceneMaterialList) const { + aiMaterial *tmat = nullptr; + aiMesh *tmesh = nullptr; + X3DElemType mesh_type = X3DElemType::ENET_Invalid; + unsigned int mat_ind = 0; + + for (std::list::const_iterator it = pShapeNodeElement.Children.begin(); it != pShapeNodeElement.Children.end(); ++it) { + if (PostprocessHelper_ElementIsMesh((*it)->Type)) { + Postprocess_BuildMesh(**it, &tmesh); + if (tmesh != nullptr) { + // if mesh successfully built then add data about it to arrays + pNodeMeshInd.push_back(static_cast(pSceneMeshList.size())); + pSceneMeshList.push_back(tmesh); + // keep mesh type. Need above for texture coordinate generation. + mesh_type = (*it)->Type; + } + } else if ((*it)->Type == X3DElemType::ENET_Appearance) { + Postprocess_BuildMaterial(**it, &tmat); + if (tmat != nullptr) { + // if material successfully built then add data about it to array + mat_ind = static_cast(pSceneMaterialList.size()); + pSceneMaterialList.push_back(tmat); + } + } + } // for(std::list::const_iterator it = pShapeNodeElement.Children.begin(); it != pShapeNodeElement.Children.end(); it++) + + // associate read material with read mesh. + if ((tmesh != nullptr) && (tmat != nullptr)) { + tmesh->mMaterialIndex = mat_ind; + // Check texture mapping. If material has texture but mesh has no texture coordinate then try to ask Assimp to generate texture coordinates. + if ((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0)) { + int32_t tm; + aiVector3D tvec3; + + switch (mesh_type) { + case X3DElemType::ENET_Box: + tm = aiTextureMapping_BOX; + break; + case X3DElemType::ENET_Cone: + case X3DElemType::ENET_Cylinder: + tm = aiTextureMapping_CYLINDER; + break; + case X3DElemType::ENET_Sphere: + tm = aiTextureMapping_SPHERE; + break; + default: + tm = aiTextureMapping_PLANE; + break; + } // switch(mesh_type) + + tmat->AddProperty(&tm, 1, AI_MATKEY_MAPPING_DIFFUSE(0)); + } // if((tmat->GetTextureCount(aiTextureType_DIFFUSE) != 0) && !tmesh->HasTextureCoords(0)) + } // if((tmesh != nullptr) && (tmat != nullptr)) +} + +void X3DImporter::Postprocess_CollectMetadata(const X3DNodeElementBase &pNodeElement, aiNode &pSceneNode) const { + std::list meta_list; + size_t meta_idx; + + PostprocessHelper_CollectMetadata(pNodeElement, meta_list); // find metadata in current node element. + if (!meta_list.empty()) { + if (pSceneNode.mMetaData != nullptr) { + throw DeadlyImportError("Postprocess. MetaData member in node are not nullptr. Something went wrong."); + } + + // copy collected metadata to output node. + pSceneNode.mMetaData = aiMetadata::Alloc(static_cast(meta_list.size())); + meta_idx = 0; + for (std::list::const_iterator it = meta_list.begin(); it != meta_list.end(); ++it, ++meta_idx) { + X3DNodeElementMeta *cur_meta = (X3DNodeElementMeta *)*it; + + // due to limitations we can add only first element of value list. + // Add an element according to its type. + if ((*it)->Type == X3DElemType::ENET_MetaBoolean) { + if (((X3DNodeElementMetaBoolean *)cur_meta)->Value.size() > 0) + pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, *(((X3DNodeElementMetaBoolean *)cur_meta)->Value.begin())); + } else if ((*it)->Type == X3DElemType::ENET_MetaDouble) { + if (((X3DNodeElementMetaDouble *)cur_meta)->Value.size() > 0) + pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, (float)*(((X3DNodeElementMetaDouble *)cur_meta)->Value.begin())); + } else if ((*it)->Type == X3DElemType::ENET_MetaFloat) { + if (((X3DNodeElementMetaFloat *)cur_meta)->Value.size() > 0) + pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, *(((X3DNodeElementMetaFloat *)cur_meta)->Value.begin())); + } else if ((*it)->Type == X3DElemType::ENET_MetaInteger) { + if (((X3DNodeElementMetaInt *)cur_meta)->Value.size() > 0) + pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, *(((X3DNodeElementMetaInt *)cur_meta)->Value.begin())); + } else if ((*it)->Type == X3DElemType::ENET_MetaString) { + if (((X3DNodeElementMetaString *)cur_meta)->Value.size() > 0) { + aiString tstr(((X3DNodeElementMetaString *)cur_meta)->Value.begin()->data()); + + pSceneNode.mMetaData->Set(static_cast(meta_idx), cur_meta->Name, tstr); + } + } else { + throw DeadlyImportError("Postprocess. Unknown metadata type."); + } // if((*it)->Type == X3DElemType::ENET_Meta*) else + } // for(std::list::const_iterator it = meta_list.begin(); it != meta_list.end(); it++) + } // if( !meta_list.empty() ) +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Rendering.cpp b/code/AssetLib/X3D/X3DImporter_Rendering.cpp new file mode 100644 index 000000000..fe7e68081 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Rendering.cpp @@ -0,0 +1,987 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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 X3DImporter_Rendering.cpp +/// \brief Parsing data from nodes of "Rendering" set of X3D. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" + +namespace Assimp { + +// +void X3DImporter::readColor(XmlNode &node) { + std::string use, def; + std::list color; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getColor3DListAttribute(node, "color", color); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Color, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementColor(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementColor *)ne)->Value = color; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Color"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readColorRGBA(XmlNode &node) { + std::string use, def; + std::list color; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getColor4DListAttribute(node, "color", color); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_ColorRGBA, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementColorRGBA(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementColorRGBA *)ne)->Value = color; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "ColorRGBA"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readCoordinate(XmlNode &node) { + std::string use, def; + std::list point; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector3DListAttribute(node, "point", point); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Coordinate, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementCoordinate(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementCoordinate *)ne)->Value = point; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Coordinate"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ColorCoordinateContentModel is the child-node content model corresponding to IndexedLineSet, LineSet and PointSet. ColorCoordinateContentModel can +// contain any-order Coordinate node with Color (or ColorRGBA) node. No more than one instance of any single node type is allowed. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readIndexedLineSet(XmlNode &node) { + std::string use, def; + std::vector colorIndex; + bool colorPerVertex = true; + std::vector coordIndex; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getInt32ArrayAttribute(node, "colorIndex", colorIndex); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + X3DXmlHelper::getInt32ArrayAttribute(node, "coordIndex", coordIndex); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedLineSet, ne); + } else { + // check data + if ((coordIndex.size() < 2) || ((coordIndex.back() == (-1)) && (coordIndex.size() < 3))) + throw DeadlyImportError("IndexedLineSet must contain not empty \"coordIndex\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedLineSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne); + + ne_alias.ColorIndex = colorIndex; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.CoordIndex = coordIndex; + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for Color and Coordinate nodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("IndexedLineSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readIndexedTriangleFanSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + std::vector index; + bool normalPerVertex = true; + bool solid = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + X3DXmlHelper::getInt32ArrayAttribute(node, "index", index); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedTriangleFanSet, ne); + } else { + // check data + if (index.size() == 0) throw DeadlyImportError("IndexedTriangleFanSet must contain not empty \"index\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedTriangleFanSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + + ne_alias.CoordIndex.clear(); + int counter = 0; + int32_t idx[3]; + for (std::vector::const_iterator idx_it = index.begin(); idx_it != index.end(); ++idx_it) { + idx[2] = *idx_it; + if (idx[2] < 0) { + counter = 0; + } else { + if (counter >= 2) { + if (ccw) { + ne_alias.CoordIndex.push_back(idx[0]); + ne_alias.CoordIndex.push_back(idx[1]); + ne_alias.CoordIndex.push_back(idx[2]); + } else { + ne_alias.CoordIndex.push_back(idx[0]); + ne_alias.CoordIndex.push_back(idx[2]); + ne_alias.CoordIndex.push_back(idx[1]); + } + ne_alias.CoordIndex.push_back(-1); + idx[1] = idx[2]; + } else { + idx[counter] = idx[2]; + } + ++counter; + } + } // for(std::list::const_iterator idx_it = index.begin(); idx_it != ne_alias.index.end(); idx_it++) + + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + else if (currentChildName == "Normal") + readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") + readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("IndexedTriangleFanSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readIndexedTriangleSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + std::vector index; + bool normalPerVertex = true; + bool solid = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + X3DXmlHelper::getInt32ArrayAttribute(node, "index", index); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedTriangleSet, ne); + } else { + // check data + if (index.size() == 0) throw DeadlyImportError("IndexedTriangleSet must contain not empty \"index\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedTriangleSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + + ne_alias.CoordIndex.clear(); + int counter = 0; + int32_t idx[3]; + for (std::vector::const_iterator idx_it = index.begin(); idx_it != index.end(); ++idx_it) { + idx[counter++] = *idx_it; + if (counter > 2) { + counter = 0; + if (ccw) { + ne_alias.CoordIndex.push_back(idx[0]); + ne_alias.CoordIndex.push_back(idx[1]); + ne_alias.CoordIndex.push_back(idx[2]); + } else { + ne_alias.CoordIndex.push_back(idx[0]); + ne_alias.CoordIndex.push_back(idx[2]); + ne_alias.CoordIndex.push_back(idx[1]); + } + ne_alias.CoordIndex.push_back(-1); + } + } // for(std::list::const_iterator idx_it = index.begin(); idx_it != ne_alias.index.end(); idx_it++) + + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + else if (currentChildName == "Normal") + readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") + readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("IndexedTriangleSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readIndexedTriangleStripSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + std::vector index; + bool normalPerVertex = true; + bool solid = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + X3DXmlHelper::getInt32ArrayAttribute(node, "index", index); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_IndexedTriangleStripSet, ne); + } else { + // check data + if (index.size() == 0) throw DeadlyImportError("IndexedTriangleStripSet must contain not empty \"index\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_IndexedTriangleStripSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementIndexedSet &ne_alias = *((X3DNodeElementIndexedSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + + ne_alias.CoordIndex.clear(); + int counter = 0; + int32_t idx[3]; + for (std::vector::const_iterator idx_it = index.begin(); idx_it != index.end(); ++idx_it) { + idx[2] = *idx_it; + if (idx[2] < 0) { + counter = 0; + } else { + if (counter >= 2) { + if (ccw) { + ne_alias.CoordIndex.push_back(idx[0]); + ne_alias.CoordIndex.push_back(idx[1]); + ne_alias.CoordIndex.push_back(idx[2]); + } else { + ne_alias.CoordIndex.push_back(idx[0]); + ne_alias.CoordIndex.push_back(idx[2]); + ne_alias.CoordIndex.push_back(idx[1]); + } + ne_alias.CoordIndex.push_back(-1); + } + idx[counter & 1] = idx[2]; + ++counter; + } + } // for(std::list::const_iterator idx_it = index.begin(); idx_it != ne_alias.index.end(); idx_it++) + + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + else if (currentChildName == "Normal") + readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") + readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("IndexedTriangleStripSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ColorCoordinateContentModel is the child-node content model corresponding to IndexedLineSet, LineSet and PointSet. ColorCoordinateContentModel can +// contain any-order Coordinate node with Color (or ColorRGBA) node. No more than one instance of any single node type is allowed. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readLineSet(XmlNode &node) { + std::string use, def; + std::vector vertexCount; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getInt32ArrayAttribute(node, "vertexCount", vertexCount); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_LineSet, ne); + } else { + // check data + if (vertexCount.size() == 0) throw DeadlyImportError("LineSet must contain not empty \"vertexCount\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementSet(X3DElemType::ENET_LineSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementSet &ne_alias = *((X3DNodeElementSet *)ne); + + ne_alias.VertexCount = vertexCount; + // create CoordIdx + size_t coord_num = 0; + + ne_alias.CoordIndex.clear(); + for (std::vector::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); ++vc_it) { + if (*vc_it < 2) throw DeadlyImportError("LineSet. vertexCount shall be greater than or equal to two."); + + for (int32_t i = 0; i < *vc_it; i++) + ne_alias.CoordIndex.push_back(static_cast(coord_num++)); // add vertices indices + + ne_alias.CoordIndex.push_back(-1); // add face delimiter. + } + + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("LineSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ColorCoordinateContentModel is the child-node content model corresponding to IndexedLineSet, LineSet and PointSet. ColorCoordinateContentModel can +// contain any-order Coordinate node with Color (or ColorRGBA) node. No more than one instance of any single node type is allowed. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readPointSet(XmlNode &node) { + std::string use, def; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_PointSet, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_PointSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("PointSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readTriangleFanSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + std::vector fanCount; + bool normalPerVertex = true; + bool solid = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + X3DXmlHelper::getInt32ArrayAttribute(node, "fanCount", fanCount); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TriangleFanSet, ne); + } else { + // check data + if (fanCount.size() == 0) throw DeadlyImportError("TriangleFanSet must contain not empty \"fanCount\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementSet(X3DElemType::ENET_TriangleFanSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementSet &ne_alias = *((X3DNodeElementSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.VertexCount = fanCount; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + // create CoordIdx + size_t coord_num_first, coord_num_prev; + + ne_alias.CoordIndex.clear(); + // assign indices for first triangle + coord_num_first = 0; + coord_num_prev = 1; + for (std::vector::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); ++vc_it) { + if (*vc_it < 3) throw DeadlyImportError("TriangleFanSet. fanCount shall be greater than or equal to three."); + + for (int32_t vc = 2; vc < *vc_it; vc++) { + if (ccw) { + // 2 1 + // 0 + ne_alias.CoordIndex.push_back(static_cast(coord_num_first)); // first vertex is a center and always is [0]. + ne_alias.CoordIndex.push_back(static_cast(coord_num_prev++)); + ne_alias.CoordIndex.push_back(static_cast(coord_num_prev)); + } else { + // 1 2 + // 0 + ne_alias.CoordIndex.push_back(static_cast(coord_num_first)); // first vertex is a center and always is [0]. + ne_alias.CoordIndex.push_back(static_cast(coord_num_prev + 1)); + ne_alias.CoordIndex.push_back(static_cast(coord_num_prev++)); + } // if(ccw) else + + ne_alias.CoordIndex.push_back(-1); // add face delimiter. + } // for(int32_t vc = 2; vc < *vc_it; vc++) + + coord_num_prev++; // that index will be center of next fan + coord_num_first = coord_num_prev++; // forward to next point - second point of fan + } // for(std::list::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++) + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + else if (currentChildName == "Normal") + readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") + readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("TriangleFanSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readTriangleSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + bool normalPerVertex = true; + bool solid = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TriangleSet, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementIndexedSet(X3DElemType::ENET_TriangleSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementSet &ne_alias = *((X3DNodeElementSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + else if (currentChildName == "Normal") + readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") + readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("TriangleSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// ComposedGeometryContentModel is the child-node content model corresponding to X3DComposedGeometryNodes. It can contain Color (or ColorRGBA), Coordinate, +// Normal and TextureCoordinate, in any order. No more than one instance of these nodes is allowed. Multiple VertexAttribute (FloatVertexAttribute, +// Matrix3VertexAttribute, Matrix4VertexAttribute) nodes can also be contained. +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model. +// +void X3DImporter::readTriangleStripSet(XmlNode &node) { + std::string use, def; + bool ccw = true; + bool colorPerVertex = true; + std::vector stripCount; + bool normalPerVertex = true; + bool solid = true; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "ccw", ccw); + XmlParser::getBoolAttribute(node, "colorPerVertex", colorPerVertex); + X3DXmlHelper::getInt32ArrayAttribute(node, "stripCount", stripCount); + XmlParser::getBoolAttribute(node, "normalPerVertex", normalPerVertex); + XmlParser::getBoolAttribute(node, "solid", solid); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TriangleStripSet, ne); + } else { + // check data + if (stripCount.size() == 0) throw DeadlyImportError("TriangleStripSet must contain not empty \"stripCount\" attribute."); + + // create and if needed - define new geometry object. + ne = new X3DNodeElementSet(X3DElemType::ENET_TriangleStripSet, mNodeElementCur); + if (!def.empty()) ne->ID = def; + + X3DNodeElementSet &ne_alias = *((X3DNodeElementSet *)ne); + + ne_alias.CCW = ccw; + ne_alias.ColorPerVertex = colorPerVertex; + ne_alias.VertexCount = stripCount; + ne_alias.NormalPerVertex = normalPerVertex; + ne_alias.Solid = solid; + // create CoordIdx + size_t coord_num0, coord_num1, coord_num2; // indices of current triangle + bool odd_tri; // sequence of current triangle + size_t coord_num_sb; // index of first point of strip + + ne_alias.CoordIndex.clear(); + coord_num_sb = 0; + for (std::vector::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); ++vc_it) { + if (*vc_it < 3) throw DeadlyImportError("TriangleStripSet. stripCount shall be greater than or equal to three."); + + // set initial values for first triangle + coord_num0 = coord_num_sb; + coord_num1 = coord_num_sb + 1; + coord_num2 = coord_num_sb + 2; + odd_tri = true; + + for (int32_t vc = 2; vc < *vc_it; vc++) { + if (ccw) { + // 0 2 + // 1 + ne_alias.CoordIndex.push_back(static_cast(coord_num0)); + ne_alias.CoordIndex.push_back(static_cast(coord_num1)); + ne_alias.CoordIndex.push_back(static_cast(coord_num2)); + } else { + // 0 1 + // 2 + ne_alias.CoordIndex.push_back(static_cast(coord_num0)); + ne_alias.CoordIndex.push_back(static_cast(coord_num2)); + ne_alias.CoordIndex.push_back(static_cast(coord_num1)); + } // if(ccw) else + + ne_alias.CoordIndex.push_back(-1); // add face delimiter. + // prepare values for next triangle + if (odd_tri) { + coord_num0 = coord_num2; + coord_num2++; + } else { + coord_num1 = coord_num2; + coord_num2 = coord_num1 + 1; + } + + odd_tri = !odd_tri; + coord_num_sb = coord_num2; // that index will be start of next strip + } // for(int32_t vc = 2; vc < *vc_it; vc++) + } // for(std::list::const_iterator vc_it = ne_alias.VertexCount.begin(); vc_it != ne_alias.VertexCount.end(); vc_it++) + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for X3DComposedGeometryNodes + if (currentChildName == "Color") + readColor(currentChildNode); + else if (currentChildName == "ColorRGBA") + readColorRGBA(currentChildNode); + else if (currentChildName == "Coordinate") + readCoordinate(currentChildNode); + else if (currentChildName == "Normal") + readNormal(currentChildNode); + else if (currentChildName == "TextureCoordinate") + readTextureCoordinate(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) + skipUnsupportedNode("TriangleStripSet", currentChildNode); + } + ParseHelper_Node_Exit(); + } // if(!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readNormal(XmlNode &node) { + std::string use, def; + std::list vector; + X3DNodeElementBase *ne; + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector3DListAttribute(node, "vector", vector); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Normal, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementNormal(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementNormal *)ne)->Value = vector; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Normal"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Shape.cpp b/code/AssetLib/X3D/X3DImporter_Shape.cpp new file mode 100644 index 000000000..7a292be5c --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Shape.cpp @@ -0,0 +1,221 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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 X3DImporter_Shape.cpp +/// \brief Parsing data from nodes of "Shape" set of X3D. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" + +namespace Assimp { + +void X3DImporter::readShape(XmlNode &node) { + std::string use, def; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Shape, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementShape(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + // check for child nodes + if (!isNodeEmpty(node)) { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + // check for appearance node + if (currentChildName == "Appearance") readAppearance(currentChildNode); + // check for X3DGeometryNodes + else if (currentChildName == "Arc2D") readArc2D(currentChildNode); + else if (currentChildName == "ArcClose2D") readArcClose2D(currentChildNode); + else if (currentChildName == "Circle2D") readCircle2D(currentChildNode); + else if (currentChildName == "Disk2D") readDisk2D(currentChildNode); + else if (currentChildName == "Polyline2D") readPolyline2D(currentChildNode); + else if (currentChildName == "Polypoint2D") readPolypoint2D(currentChildNode); + else if (currentChildName == "Rectangle2D") readRectangle2D(currentChildNode); + else if (currentChildName == "TriangleSet2D") readTriangleSet2D(currentChildNode); + else if (currentChildName == "Box") readBox(currentChildNode); + else if (currentChildName == "Cone") readCone(currentChildNode); + else if (currentChildName == "Cylinder") readCylinder(currentChildNode); + else if (currentChildName == "ElevationGrid") readElevationGrid(currentChildNode); + else if (currentChildName == "Extrusion") readExtrusion(currentChildNode); + else if (currentChildName == "IndexedFaceSet") readIndexedFaceSet(currentChildNode); + else if (currentChildName == "Sphere") readSphere(currentChildNode); + else if (currentChildName == "IndexedLineSet") readIndexedLineSet(currentChildNode); + else if (currentChildName == "LineSet") readLineSet(currentChildNode); + else if (currentChildName == "PointSet") readPointSet(currentChildNode); + else if (currentChildName == "IndexedTriangleFanSet") readIndexedTriangleFanSet(currentChildNode); + else if (currentChildName == "IndexedTriangleSet") readIndexedTriangleSet(currentChildNode); + else if (currentChildName == "IndexedTriangleStripSet") readIndexedTriangleStripSet(currentChildNode); + else if (currentChildName == "TriangleFanSet") readTriangleFanSet(currentChildNode); + else if (currentChildName == "TriangleSet") readTriangleSet(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) skipUnsupportedNode("Shape", currentChildNode); + } + + ParseHelper_Node_Exit(); + } // if (!isNodeEmpty(node)) + else { + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + } + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +// +// "Child-node content model corresponding to X3DAppearanceChildNode. Appearance can contain FillProperties, LineProperties, Material, any Texture node and +// any TextureTransform node, in any order. No more than one instance of these nodes is allowed. Appearance may also contain multiple shaders (ComposedShader, +// PackagedShader, ProgramShader). +// A ProtoInstance node (with the proper node type) can be substituted for any node in this content model." +// +void X3DImporter::readAppearance(XmlNode &node) { + std::string use, def; + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + + // if "USE" defined then find already defined element. + if (!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Appearance, ne); + } + else + { + // create and if needed - define new geometry object. + ne = new X3DNodeElementAppearance(mNodeElementCur); + if(!def.empty()) ne->ID = def; + + // check for child nodes + if(!isNodeEmpty(node)) + { + ParseHelper_Node_Enter(ne); + for (auto currentChildNode : node.children()) { + const std::string ¤tChildName = currentChildNode.name(); + if (currentChildName == "Material") readMaterial(currentChildNode); + else if (currentChildName == "ImageTexture") readImageTexture(currentChildNode); + else if (currentChildName == "TextureTransform") readTextureTransform(currentChildNode); + // check for X3DMetadataObject + else if (!checkForMetadataNode(currentChildNode)) skipUnsupportedNode("Appearance", currentChildNode); + } + ParseHelper_Node_Exit(); + }// if(!isNodeEmpty(node)) + else + { + mNodeElementCur->Children.push_back(ne);// add made object as child to current element + } + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +// +void X3DImporter::readMaterial(XmlNode &node) { + std::string use, def; + float ambientIntensity = 0.2f; + float shininess = 0.2f; + float transparency = 0; + aiColor3D diffuseColor(0.8f, 0.8f, 0.8f); + aiColor3D emissiveColor(0, 0, 0); + aiColor3D specularColor(0, 0, 0); + X3DNodeElementBase* ne( nullptr ); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getFloatAttribute(node, "ambientIntensity", ambientIntensity); + XmlParser::getFloatAttribute(node, "shininess", shininess); + XmlParser::getFloatAttribute(node, "transparency", transparency); + X3DXmlHelper::getColor3DAttribute(node, "diffuseColor", diffuseColor); + X3DXmlHelper::getColor3DAttribute(node, "emissiveColor", emissiveColor); + X3DXmlHelper::getColor3DAttribute(node, "specularColor", specularColor); + + // if "USE" defined then find already defined element. + if(!use.empty()) + { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_Material, ne); + } + else + { + // create and if needed - define new geometry object. + ne = new X3DNodeElementMaterial(mNodeElementCur); + if(!def.empty()) ne->ID = def; + + ((X3DNodeElementMaterial *)ne)->AmbientIntensity = ambientIntensity; + ((X3DNodeElementMaterial *)ne)->Shininess = shininess; + ((X3DNodeElementMaterial *)ne)->Transparency = transparency; + ((X3DNodeElementMaterial *)ne)->DiffuseColor = diffuseColor; + ((X3DNodeElementMaterial *)ne)->EmissiveColor = emissiveColor; + ((X3DNodeElementMaterial *)ne)->SpecularColor = specularColor; + // check for child nodes + if(!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "Material"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne);// add element to node element list because its a new object in graph + }// if(!use.empty()) else +} + +}// namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DImporter_Texturing.cpp b/code/AssetLib/X3D/X3DImporter_Texturing.cpp new file mode 100644 index 000000000..6463e2808 --- /dev/null +++ b/code/AssetLib/X3D/X3DImporter_Texturing.cpp @@ -0,0 +1,179 @@ +/* +Open Asset Import Library (assimp) +---------------------------------------------------------------------- + +Copyright (c) 2006-2019, 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 X3DImporter_Texturing.cpp +/// \brief Parsing data from nodes of "Texturing" set of X3D. +/// \date 2015-2016 +/// \author smal.root@gmail.com + +#ifndef ASSIMP_BUILD_NO_X3D_IMPORTER + +#include "X3DImporter.hpp" +#include "X3DImporter_Macro.hpp" +#include "X3DXmlHelper.h" + +namespace Assimp { + +// +// When the url field contains no values ([]), texturing is disabled. +void X3DImporter::readImageTexture(XmlNode &node) { + std::string use, def; + bool repeatS = true; + bool repeatT = true; + std::list url; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + XmlParser::getBoolAttribute(node, "repeatS", repeatS); + XmlParser::getBoolAttribute(node, "repeatT", repeatT); + X3DXmlHelper::getStringListAttribute(node, "url", url); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_ImageTexture, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementImageTexture(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementImageTexture *)ne)->RepeatS = repeatS; + ((X3DNodeElementImageTexture *)ne)->RepeatT = repeatT; + // Attribute "url" can contain list of strings. But we need only one - first. + if (!url.empty()) + ((X3DNodeElementImageTexture *)ne)->URL = url.front(); + else + ((X3DNodeElementImageTexture *)ne)->URL = ""; + + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "ImageTexture"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readTextureCoordinate(XmlNode &node) { + std::string use, def; + std::list point; + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector2DListAttribute(node, "point", point); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TextureCoordinate, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementTextureCoordinate(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementTextureCoordinate *)ne)->Value = point; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "TextureCoordinate"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +// +void X3DImporter::readTextureTransform(XmlNode &node) { + std::string use, def; + aiVector2D center(0, 0); + float rotation = 0; + aiVector2D scale(1, 1); + aiVector2D translation(0, 0); + X3DNodeElementBase *ne(nullptr); + + MACRO_ATTRREAD_CHECKUSEDEF_RET(node, def, use); + X3DXmlHelper::getVector2DAttribute(node, "center", center); + XmlParser::getFloatAttribute(node, "rotation", rotation); + X3DXmlHelper::getVector2DAttribute(node, "scale", scale); + X3DXmlHelper::getVector2DAttribute(node, "translation", translation); + + // if "USE" defined then find already defined element. + if (!use.empty()) { + MACRO_USE_CHECKANDAPPLY(node, def, use, ENET_TextureTransform, ne); + } else { + // create and if needed - define new geometry object. + ne = new X3DNodeElementTextureTransform(mNodeElementCur); + if (!def.empty()) ne->ID = def; + + ((X3DNodeElementTextureTransform *)ne)->Center = center; + ((X3DNodeElementTextureTransform *)ne)->Rotation = rotation; + ((X3DNodeElementTextureTransform *)ne)->Scale = scale; + ((X3DNodeElementTextureTransform *)ne)->Translation = translation; + // check for X3DMetadataObject childs. + if (!isNodeEmpty(node)) + childrenReadMetadata(node, ne, "TextureTransform"); + else + mNodeElementCur->Children.push_back(ne); // add made object as child to current element + + NodeElement_List.push_back(ne); // add element to node element list because its a new object in graph + } // if(!use.empty()) else +} + +} // namespace Assimp + +#endif // !ASSIMP_BUILD_NO_X3D_IMPORTER diff --git a/code/AssetLib/X3D/X3DXmlHelper.cpp b/code/AssetLib/X3D/X3DXmlHelper.cpp new file mode 100644 index 000000000..ff24b74b3 --- /dev/null +++ b/code/AssetLib/X3D/X3DXmlHelper.cpp @@ -0,0 +1,294 @@ +#include "X3DXmlHelper.h" +#include "X3DImporter.hpp" + +#include + +namespace Assimp { + +bool X3DXmlHelper::getColor3DAttribute(XmlNode &node, const char *attributeName, aiColor3D &color) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + if (values.size() != 3) { + Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + return false; + } + auto it = values.begin(); + color.r = stof(*it++); + color.g = stof(*it++); + color.b = stof(*it); + return true; + } + return false; +} + +bool X3DXmlHelper::getVector2DAttribute(XmlNode &node, const char *attributeName, aiVector2D &color) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + if (values.size() != 2) { + Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + return false; + } + auto it = values.begin(); + color.x = stof(*it++); + color.y = stof(*it); + return true; + } + return false; +} + +bool X3DXmlHelper::getVector3DAttribute(XmlNode &node, const char *attributeName, aiVector3D &color) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + if (values.size() != 3) { + Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + return false; + } + auto it = values.begin(); + color.x = stof(*it++); + color.y = stof(*it++); + color.z = stof(*it); + return true; + } + return false; +} + +bool X3DXmlHelper::getBooleanArrayAttribute(XmlNode &node, const char *attributeName, std::vector &boolArray) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + auto it = values.begin(); + while (it != values.end()) { + auto s = *it++; + if (!s.empty()) + boolArray.push_back(s[0] == 't' || s[0] == '1'); + else + Throw_ConvertFail_Str2ArrB(node.name(), attributeName); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getDoubleArrayAttribute(XmlNode &node, const char *attributeName, std::vector &doubleArray) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + auto it = values.begin(); + while (it != values.end()) { + auto s = *it++; + if (!s.empty()) + doubleArray.push_back(atof(s.c_str())); + else + Throw_ConvertFail_Str2ArrD(node.name(), attributeName); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getFloatArrayAttribute(XmlNode &node, const char *attributeName, std::vector &floatArray) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + auto it = values.begin(); + while (it != values.end()) { + auto s = *it++; + if (!s.empty()) + floatArray.push_back((float)atof(s.c_str())); + else + Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getInt32ArrayAttribute(XmlNode &node, const char *attributeName, std::vector &intArray) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + auto it = values.begin(); + while (it != values.end()) { + auto s = *it++; + if (!s.empty()) + intArray.push_back((int32_t)atof(s.c_str())); + else + Throw_ConvertFail_Str2ArrI(node.name(), attributeName); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getStringListAttribute(XmlNode &node, const char *attributeName, std::list &stringList) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + auto it = values.begin(); + std::string currentConcat = ""; + bool inQuotes = false; + while (it != values.end()) { + auto s = *it++; + if (!s.empty()) { + if (inQuotes) { + if (*(s.rbegin()) == '"') { + stringList.push_back(currentConcat + s.substr(0, s.length() - 1)); + currentConcat = ""; + inQuotes = false; + } else { + currentConcat += " " + s; + } + } else { + if (s[0] == '"') { + currentConcat = s.substr(1); + inQuotes = true; + } else { + stringList.push_back(s); + } + } + } else if (!inQuotes) + Throw_ConvertFail_Str2ArrI(node.name(), attributeName); + } + if (inQuotes) Throw_ConvertFail_Str2ArrI(node.name(), attributeName); + return true; + } + return false; +} + +bool X3DXmlHelper::getStringArrayAttribute(XmlNode &node, const char *attributeName, std::vector &stringArray) { + std::list tlist; + + if (getStringListAttribute(node, attributeName, tlist)) { + if (!tlist.empty()) { + stringArray.reserve(tlist.size()); + for (std::list::iterator it = tlist.begin(); it != tlist.end(); ++it) { + stringArray.push_back(*it); + } + return true; + } + } + return false; +} + +bool X3DXmlHelper::getVector2DListAttribute(XmlNode &node, const char *attributeName, std::list &vectorList) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + if (values.size() % 2) Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + auto it = values.begin(); + while (it != values.end()) { + aiVector2D tvec; + + tvec.x = (float)atof((*it++).c_str()); + tvec.y = (float)atof((*it++).c_str()); + vectorList.push_back(tvec); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getVector2DArrayAttribute(XmlNode &node, const char *attributeName, std::vector &vectorArray) { + std::list tlist; + + if (getVector2DListAttribute(node, attributeName, tlist)) { + if (!tlist.empty()) { + vectorArray.reserve(tlist.size()); + for (std::list::iterator it = tlist.begin(); it != tlist.end(); ++it) { + vectorArray.push_back(*it); + } + return true; + } + } + return false; +} + +bool X3DXmlHelper::getVector3DListAttribute(XmlNode &node, const char *attributeName, std::list &vectorList) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + if (values.size() % 3 != 0) Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + auto it = values.begin(); + while (it != values.end()) { + aiVector3D tvec; + + tvec.x = (float)atof((*it++).c_str()); + tvec.y = (float)atof((*it++).c_str()); + tvec.z = (float)atof((*it++).c_str()); + vectorList.push_back(tvec); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getVector3DArrayAttribute(XmlNode &node, const char *attributeName, std::vector &vectorArray) { + std::list tlist; + + if (getVector3DListAttribute(node, attributeName, tlist)) { + if (!tlist.empty()) { + vectorArray.reserve(tlist.size()); + for (std::list::iterator it = tlist.begin(); it != tlist.end(); ++it) { + vectorArray.push_back(*it); + } + return true; + } + } + return false; +} + +bool X3DXmlHelper::getColor3DListAttribute(XmlNode &node, const char *attributeName, std::list &colorList) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + if (values.size() % 3 != 0) Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + auto it = values.begin(); + while (it != values.end()) { + aiColor3D tvec; + + tvec.r = (float)atof((*it++).c_str()); + tvec.g = (float)atof((*it++).c_str()); + tvec.b = (float)atof((*it++).c_str()); + colorList.push_back(tvec); + } + return true; + } + return false; +} + +bool X3DXmlHelper::getColor4DListAttribute(XmlNode &node, const char *attributeName, std::list &colorList) { + std::string val; + if (XmlParser::getStdStrAttribute(node, attributeName, val)) { + std::vector values; + tokenize(val, values, " "); + if (values.size() % 4 != 0) Throw_ConvertFail_Str2ArrF(node.name(), attributeName); + auto it = values.begin(); + while (it != values.end()) { + aiColor4D tvec; + + tvec.r = (float)atof((*it++).c_str()); + tvec.g = (float)atof((*it++).c_str()); + tvec.b = (float)atof((*it++).c_str()); + tvec.a = (float)atof((*it++).c_str()); + colorList.push_back(tvec); + } + return true; + } + return false; +} + +} // namespace Assimp diff --git a/code/AssetLib/X3D/X3DXmlHelper.h b/code/AssetLib/X3D/X3DXmlHelper.h new file mode 100644 index 000000000..dd305f883 --- /dev/null +++ b/code/AssetLib/X3D/X3DXmlHelper.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include + +namespace Assimp { + +class X3DXmlHelper { +public: + static bool getColor3DAttribute(XmlNode &node, const char *attributeName, aiColor3D &color); + static bool getVector2DAttribute(XmlNode &node, const char *attributeName, aiVector2D &vector); + static bool getVector3DAttribute(XmlNode &node, const char *attributeName, aiVector3D &vector); + + static bool getBooleanArrayAttribute(XmlNode &node, const char *attributeName, std::vector &boolArray); + static bool getDoubleArrayAttribute(XmlNode &node, const char *attributeName, std::vector &doubleArray); + static bool getFloatArrayAttribute(XmlNode &node, const char *attributeName, std::vector &floatArray); + static bool getInt32ArrayAttribute(XmlNode &node, const char *attributeName, std::vector &intArray); + static bool getStringListAttribute(XmlNode &node, const char *attributeName, std::list &stringArray); + static bool getStringArrayAttribute(XmlNode &node, const char *attributeName, std::vector &stringArray); + + static bool getVector2DListAttribute(XmlNode &node, const char *attributeName, std::list &vectorList); + static bool getVector2DArrayAttribute(XmlNode &node, const char *attributeName, std::vector &vectorArray); + static bool getVector3DListAttribute(XmlNode &node, const char *attributeName, std::list &vectorList); + static bool getVector3DArrayAttribute(XmlNode &node, const char *attributeName, std::vector &vectorArray); + static bool getColor3DListAttribute(XmlNode &node, const char *attributeName, std::list &colorList); + static bool getColor4DListAttribute(XmlNode &node, const char *attributeName, std::list &colorList); +}; + +} // namespace Assimp diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 933b5488c..c99b81558 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -800,9 +800,23 @@ ADD_ASSIMP_IMPORTER( X ADD_ASSIMP_IMPORTER( X3D AssetLib/X3D/X3DImporter.cpp + AssetLib/X3D/X3DImporter_Geometry2D.cpp + AssetLib/X3D/X3DImporter_Geometry3D.cpp + AssetLib/X3D/X3DImporter_Group.cpp + AssetLib/X3D/X3DImporter_Light.cpp + AssetLib/X3D/X3DImporter_Metadata.cpp + AssetLib/X3D/X3DImporter_Networking.cpp + AssetLib/X3D/X3DImporter_Postprocess.cpp + AssetLib/X3D/X3DImporter_Rendering.cpp + AssetLib/X3D/X3DImporter_Shape.cpp + AssetLib/X3D/X3DImporter_Texturing.cpp AssetLib/X3D/X3DImporter.hpp + AssetLib/X3D/X3DImporter_Macro.hpp + AssetLib/X3D/X3DImporter_Node.hpp AssetLib/X3D/X3DGeoHelper.cpp AssetLib/X3D/X3DGeoHelper.h + AssetLib/X3D/X3DXmlHelper.cpp + AssetLib/X3D/X3DXmlHelper.h ) ADD_ASSIMP_IMPORTER( GLTF diff --git a/code/Common/ImporterRegistry.cpp b/code/Common/ImporterRegistry.cpp index 5df096166..7500d2610 100644 --- a/code/Common/ImporterRegistry.cpp +++ b/code/Common/ImporterRegistry.cpp @@ -365,9 +365,7 @@ void GetImporterInstanceList(std::vector &out) { out.push_back(new D3MFImporter()); #endif #ifndef ASSIMP_BUILD_NO_X3D_IMPORTER - if (devImportersEnabled) { // https://github.com/assimp/assimp/issues/3647 - out.push_back(new X3DImporter()); - } + out.push_back(new X3DImporter()); #endif #ifndef ASSIMP_BUILD_NO_MMD_IMPORTER out.push_back(new MMDImporter()); From 18531e3677e8c8f78d6f7c56bfd642e748c08e3a Mon Sep 17 00:00:00 2001 From: Kim Kulling Date: Mon, 13 Sep 2021 22:38:20 +0200 Subject: [PATCH 267/335] Next iteration for c++11 features. --- code/AssetLib/3DS/3DSExporter.cpp | 2 +- code/AssetLib/3DS/3DSHelper.h | 2 +- code/AssetLib/3DS/3DSLoader.h | 10 +- code/AssetLib/3MF/D3MFExporter.h | 5 +- code/AssetLib/3MF/D3MFImporter.h | 4 + code/AssetLib/3MF/D3MFOpcPackage.cpp | 2 +- code/AssetLib/3MF/D3MFOpcPackage.h | 2 +- code/AssetLib/AC/ACLoader.h | 10 +- code/AssetLib/AMF/AMFImporter.hpp | 8 +- code/AssetLib/AMF/AMFImporter_Node.hpp | 9 +- code/AssetLib/ASE/ASELoader.cpp | 2 - code/AssetLib/ASE/ASELoader.h | 25 +- code/AssetLib/ASE/ASEParser.cpp | 2 - code/AssetLib/Assbin/AssbinExporter.h | 2 +- code/AssetLib/Assbin/AssbinLoader.h | 17 +- code/AssetLib/Assjson/mesh_splitter.cpp | 13 +- code/AssetLib/Assjson/mesh_splitter.h | 35 +- code/AssetLib/Assxml/AssxmlExporter.h | 1 + code/AssetLib/Assxml/AssxmlFileWriter.h | 3 +- code/AssetLib/B3D/B3DImporter.h | 18 +- code/AssetLib/Blender/BlenderLoader.h | 186 ++++--- code/AssetLib/COB/COBLoader.h | 106 ++-- code/AssetLib/COB/COBScene.h | 9 +- code/AssetLib/CSM/CSMLoader.h | 28 +- code/AssetLib/Collada/ColladaLoader.cpp | 1 - code/AssetLib/Collada/ColladaParser.h | 1 + code/AssetLib/DXF/DXFLoader.h | 15 +- code/AssetLib/FBX/FBXImporter.h | 11 +- code/AssetLib/HMP/HMPLoader.h | 53 +- code/AssetLib/IFC/IFCBoolean.cpp | 1 - code/AssetLib/IFC/IFCLoader.h | 92 ++-- code/AssetLib/IFC/IFCUtil.cpp | 1 - code/AssetLib/Irr/IRRLoader.h | 11 +- code/AssetLib/Irr/IRRMeshLoader.h | 8 +- code/AssetLib/Irr/IRRShared.h | 2 +- code/AssetLib/LWO/LWOLoader.h | 208 ++++---- code/AssetLib/LWS/LWSLoader.h | 14 +- code/AssetLib/M3D/M3DImporter.h | 22 +- code/AssetLib/MD2/MD2Loader.h | 38 +- code/AssetLib/MD3/MD3Loader.h | 108 ++-- code/AssetLib/MD5/MD5Loader.h | 12 +- code/AssetLib/MDC/MDCLoader.h | 34 +- code/AssetLib/MDL/MDLLoader.h | 14 +- code/AssetLib/MDL/MDLMaterialLoader.cpp | 3 +- code/AssetLib/MMD/MMDImporter.h | 4 +- code/AssetLib/MS3D/MS3DLoader.h | 64 +-- code/AssetLib/NDO/NDOLoader.h | 50 +- code/AssetLib/NFF/NFFLoader.h | 118 ++--- code/AssetLib/OFF/OFFLoader.cpp | 2 - code/AssetLib/OFF/OFFLoader.h | 26 +- code/AssetLib/Obj/ObjFileData.h | 39 +- code/AssetLib/Obj/ObjFileImporter.h | 12 +- code/AssetLib/Obj/ObjFileMtlImporter.h | 6 +- code/AssetLib/Obj/ObjFileParser.h | 1 - code/AssetLib/Ogre/OgreImporter.cpp | 37 +- code/AssetLib/Ogre/OgreImporter.h | 42 +- code/AssetLib/OpenGEX/OpenGEXExporter.h | 2 +- code/AssetLib/OpenGEX/OpenGEXImporter.cpp | 3 - code/AssetLib/OpenGEX/OpenGEXImporter.h | 29 +- code/AssetLib/Ply/PlyLoader.h | 9 +- code/AssetLib/Ply/PlyParser.h | 3 +- code/AssetLib/Q3BSP/Q3BSPFileImporter.h | 15 +- code/AssetLib/Q3BSP/Q3BSPFileParser.cpp | 1 - code/AssetLib/Q3BSP/Q3BSPFileParser.h | 5 +- code/AssetLib/Q3D/Q3DLoader.h | 48 +- code/AssetLib/Raw/RawLoader.h | 33 +- code/AssetLib/SIB/SIBImporter.h | 31 +- code/AssetLib/SMD/SMDLoader.h | 57 +- code/AssetLib/STL/STLExporter.h | 19 +- code/AssetLib/STL/STLLoader.cpp | 5 +- code/AssetLib/STL/STLLoader.h | 9 +- code/AssetLib/Terragen/TerragenLoader.cpp | 8 +- code/AssetLib/Terragen/TerragenLoader.h | 13 +- code/AssetLib/Unreal/UnrealLoader.cpp | 2 +- code/AssetLib/Unreal/UnrealLoader.h | 16 +- code/AssetLib/X/XFileHelper.h | 115 ++--- code/AssetLib/X/XFileImporter.cpp | 1 - code/AssetLib/X/XFileImporter.h | 13 +- code/AssetLib/XGL/XGLLoader.cpp | 18 +- code/AssetLib/XGL/XGLLoader.h | 8 +- code/AssetLib/glTF/glTFAsset.h | 30 +- code/AssetLib/glTF/glTFImporter.h | 39 +- code/AssetLib/glTF2/glTF2Exporter.cpp | 603 +++++++++++----------- code/AssetLib/glTF2/glTF2Importer.cpp | 107 ++-- code/AssetLib/glTF2/glTF2Importer.h | 41 +- code/CMakeLists.txt | 1 - code/Common/StandardShapes.cpp | 1 - include/assimp/BlobIOSystem.h | 4 + include/assimp/ColladaMetaData.h | 4 + include/assimp/DefaultIOStream.h | 13 +- include/assimp/DefaultIOSystem.h | 4 +- include/assimp/DefaultLogger.hpp | 8 +- include/assimp/Defines.h | 58 --- include/assimp/IOStream.hpp | 2 - include/assimp/IOStreamBuffer.h | 183 +++---- include/assimp/LogAux.h | 7 +- include/assimp/SceneCombiner.h | 5 +- include/assimp/StreamReader.h | 3 +- include/assimp/commonMetaData.h | 4 + include/assimp/defs.h | 39 +- include/assimp/light.h | 12 +- include/assimp/matrix3x3.h | 2 - include/assimp/mesh.h | 11 +- 103 files changed, 1410 insertions(+), 1785 deletions(-) delete mode 100644 include/assimp/Defines.h diff --git a/code/AssetLib/3DS/3DSExporter.cpp b/code/AssetLib/3DS/3DSExporter.cpp index 0beecd563..d1059ae43 100644 --- a/code/AssetLib/3DS/3DSExporter.cpp +++ b/code/AssetLib/3DS/3DSExporter.cpp @@ -56,8 +56,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include -using namespace Assimp; namespace Assimp { + using namespace D3DS; namespace { diff --git a/code/AssetLib/3DS/3DSHelper.h b/code/AssetLib/3DS/3DSHelper.h index e8efbf949..933795f65 100644 --- a/code/AssetLib/3DS/3DSHelper.h +++ b/code/AssetLib/3DS/3DSHelper.h @@ -53,7 +53,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include -#include //sprintf +#include //sprintf namespace Assimp { namespace D3DS { diff --git a/code/AssetLib/3DS/3DSLoader.h b/code/AssetLib/3DS/3DSLoader.h index 04dcac237..bd167872f 100644 --- a/code/AssetLib/3DS/3DSLoader.h +++ b/code/AssetLib/3DS/3DSLoader.h @@ -46,11 +46,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef AI_3DSIMPORTER_H_INC #define AI_3DSIMPORTER_H_INC +#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER #include #include -#ifndef ASSIMP_BUILD_NO_3DS_IMPORTER #include "3DSHelper.h" #include @@ -75,14 +75,14 @@ public: * See BaseImporter::CanRead() for details. */ bool CanRead( const std::string& pFile, IOSystem* pIOHandler, - bool checkSig) const; + bool checkSig) const override; // ------------------------------------------------------------------- /** Called prior to ReadFile(). * The function is a request to the importer to update its configuration * basing on the Importer's configuration property list. */ - void SetupProperties(const Importer* pImp); + void SetupProperties(const Importer* pImp) override; protected: @@ -90,14 +90,14 @@ protected: /** Return importer meta information. * See #BaseImporter::GetInfo for the details */ - const aiImporterDesc* GetInfo () const; + const aiImporterDesc* GetInfo () const override; // ------------------------------------------------------------------- /** Imports the given file into the given scene structure. * See BaseImporter::InternReadFile() for details */ void InternReadFile( const std::string& pFile, aiScene* pScene, - IOSystem* pIOHandler); + IOSystem* pIOHandler) override; // ------------------------------------------------------------------- /** Converts a temporary material to the outer representation diff --git a/code/AssetLib/3MF/D3MFExporter.h b/code/AssetLib/3MF/D3MFExporter.h index d2fc82483..3da74fb37 100644 --- a/code/AssetLib/3MF/D3MFExporter.h +++ b/code/AssetLib/3MF/D3MFExporter.h @@ -40,6 +40,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once +#ifndef ASSIMP_BUILD_NO_EXPORT +#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER + #include #include #include @@ -58,8 +61,6 @@ class IOStream; namespace D3MF { -#ifndef ASSIMP_BUILD_NO_EXPORT -#ifndef ASSIMP_BUILD_NO_3MF_EXPORTER struct OpcPackageRelationship; diff --git a/code/AssetLib/3MF/D3MFImporter.h b/code/AssetLib/3MF/D3MFImporter.h index 2b37010d9..7219fa39d 100644 --- a/code/AssetLib/3MF/D3MFImporter.h +++ b/code/AssetLib/3MF/D3MFImporter.h @@ -42,6 +42,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef AI_D3MFLOADER_H_INCLUDED #define AI_D3MFLOADER_H_INCLUDED +#ifndef ASSIMP_BUILD_NO_3MF_IMPORTER + #include namespace Assimp { @@ -84,4 +86,6 @@ protected: } // Namespace Assimp +#endif // #ifndef ASSIMP_BUILD_NO_3MF_IMPORTER + #endif // AI_D3MFLOADER_H_INCLUDED diff --git a/code/AssetLib/3MF/D3MFOpcPackage.cpp b/code/AssetLib/3MF/D3MFOpcPackage.cpp index c29cec368..33428b124 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.cpp +++ b/code/AssetLib/3MF/D3MFOpcPackage.cpp @@ -51,11 +51,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include "3MFXmlTags.h" + #include #include #include #include -#include #include namespace Assimp { diff --git a/code/AssetLib/3MF/D3MFOpcPackage.h b/code/AssetLib/3MF/D3MFOpcPackage.h index fda74a879..27bf44ff2 100644 --- a/code/AssetLib/3MF/D3MFOpcPackage.h +++ b/code/AssetLib/3MF/D3MFOpcPackage.h @@ -42,9 +42,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef D3MFOPCPACKAGE_H #define D3MFOPCPACKAGE_H +#include #include #include -#include struct aiTexture; diff --git a/code/AssetLib/AC/ACLoader.h b/code/AssetLib/AC/ACLoader.h index 5ae2e80bb..5b706a4a0 100644 --- a/code/AssetLib/AC/ACLoader.h +++ b/code/AssetLib/AC/ACLoader.h @@ -63,7 +63,7 @@ namespace Assimp { class AC3DImporter : public BaseImporter { public: AC3DImporter(); - ~AC3DImporter(); + ~AC3DImporter() override; // Represents an AC3D material struct Material { @@ -185,25 +185,25 @@ public: * See BaseImporter::CanRead() for details. */ bool CanRead(const std::string &pFile, IOSystem *pIOHandler, - bool checkSig) const; + bool checkSig) const override; protected: // ------------------------------------------------------------------- /** Return importer meta information. * See #BaseImporter::GetInfo for the details */ - const aiImporterDesc *GetInfo() const; + const aiImporterDesc *GetInfo() const override; // ------------------------------------------------------------------- /** Imports the given file into the given scene structure. * See BaseImporter::InternReadFile() for details*/ void InternReadFile(const std::string &pFile, aiScene *pScene, - IOSystem *pIOHandler); + IOSystem *pIOHandler) override; // ------------------------------------------------------------------- /** Called prior to ReadFile(). * The function is a request to the importer to update its configuration * basing on the Importer's configuration property list.*/ - void SetupProperties(const Importer *pImp); + void SetupProperties(const Importer *pImp) override; private: // ------------------------------------------------------------------- diff --git a/code/AssetLib/AMF/AMFImporter.hpp b/code/AssetLib/AMF/AMFImporter.hpp index 18ef83c24..a71b6671e 100644 --- a/code/AssetLib/AMF/AMFImporter.hpp +++ b/code/AssetLib/AMF/AMFImporter.hpp @@ -267,7 +267,7 @@ public: AMFImporter() AI_NO_EXCEPT; /// Default destructor. - ~AMFImporter(); + ~AMFImporter() override; /// Parse AMF file and fill scene graph. The function has no return value. Result can be found by analyzing the generated graph. /// Also exception can be thrown if trouble will found. @@ -276,9 +276,9 @@ public: void ParseFile(const std::string &pFile, IOSystem *pIOHandler); void ParseHelper_Node_Enter(AMFNodeElementBase *child); void ParseHelper_Node_Exit(); - bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool pCheckSig) const; - void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler); - const aiImporterDesc *GetInfo() const; + bool CanRead(const std::string &pFile, IOSystem *pIOHandler, bool pCheckSig) const override; + void InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) override; + const aiImporterDesc *GetInfo() const override; bool Find_NodeElement(const std::string &pID, const AMFNodeElementBase::EType pType, AMFNodeElementBase **pNodeElement) const; bool Find_ConvertedNode(const std::string &pID, NodeArray &nodeArray, aiNode **pNode) const; bool Find_ConvertedMaterial(const std::string &pID, const SPP_Material **pConvertedMaterial) const; diff --git a/code/AssetLib/AMF/AMFImporter_Node.hpp b/code/AssetLib/AMF/AMFImporter_Node.hpp index 9051b2871..b466e339b 100644 --- a/code/AssetLib/AMF/AMFImporter_Node.hpp +++ b/code/AssetLib/AMF/AMFImporter_Node.hpp @@ -48,15 +48,14 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef INCLUDED_AI_AMF_IMPORTER_NODE_H #define INCLUDED_AI_AMF_IMPORTER_NODE_H -// Header files, stdlib. +// Header files, Assimp. +#include +#include + #include #include #include -// Header files, Assimp. -#include "assimp/scene.h" -#include "assimp/types.h" - /// \class CAMFImporter_NodeElement /// Base class for elements of nodes. class AMFNodeElementBase { diff --git a/code/AssetLib/ASE/ASELoader.cpp b/code/AssetLib/ASE/ASELoader.cpp index 1a42c2f52..a9c7314c1 100644 --- a/code/AssetLib/ASE/ASELoader.cpp +++ b/code/AssetLib/ASE/ASELoader.cpp @@ -5,8 +5,6 @@ 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, diff --git a/code/AssetLib/ASE/ASELoader.h b/code/AssetLib/ASE/ASELoader.h index 1f30d28e7..9ec9d439f 100644 --- a/code/AssetLib/ASE/ASELoader.h +++ b/code/AssetLib/ASE/ASELoader.h @@ -62,42 +62,37 @@ namespace Assimp { class ASEImporter : public BaseImporter { public: ASEImporter(); - ~ASEImporter(); + ~ASEImporter() override; // ------------------------------------------------------------------- /** Returns whether the class can handle the format of the given file. * See BaseImporter::CanRead() for details. */ bool CanRead( const std::string& pFile, IOSystem* pIOHandler, - bool checkSig) const; + bool checkSig) const override; protected: - // ------------------------------------------------------------------- /** Return importer meta information. * See #BaseImporter::GetInfo for the details */ - const aiImporterDesc* GetInfo () const; - + const aiImporterDesc* GetInfo () const override; // ------------------------------------------------------------------- /** Imports the given file into the given scene structure. * See BaseImporter::InternReadFile() for details */ void InternReadFile( const std::string& pFile, aiScene* pScene, - IOSystem* pIOHandler); - + IOSystem* pIOHandler) override; // ------------------------------------------------------------------- /** Called prior to ReadFile(). * The function is a request to the importer to update its configuration * basing on the Importer's configuration property list. */ - void SetupProperties(const Importer* pImp); - + void SetupProperties(const Importer* pImp) override; private: - // ------------------------------------------------------------------- /** Generate normal vectors basing on smoothing groups * (in some cases the normal are already contained in the file) @@ -106,7 +101,6 @@ private: */ bool GenerateNormals(ASE::Mesh& mesh); - // ------------------------------------------------------------------- /** Create valid vertex/normal/UV/color/face lists. * All elements are unique, faces have only one set of indices @@ -115,51 +109,43 @@ private: */ void BuildUniqueRepresentation(ASE::Mesh& mesh); - /** Create one-material-per-mesh meshes ;-) * \param mesh Mesh to work with * \param Receives the list of all created meshes */ void ConvertMeshes(ASE::Mesh& mesh, std::vector& avOut); - // ------------------------------------------------------------------- /** Convert a material to a aiMaterial object * \param mat Input material */ void ConvertMaterial(ASE::Material& mat); - // ------------------------------------------------------------------- /** Setup the final material indices for each mesh */ void BuildMaterialIndices(); - // ------------------------------------------------------------------- /** Build the node graph */ void BuildNodes(std::vector& nodes); - // ------------------------------------------------------------------- /** Build output cameras */ void BuildCameras(); - // ------------------------------------------------------------------- /** Build output lights */ void BuildLights(); - // ------------------------------------------------------------------- /** Build output animations */ void BuildAnimations(const std::vector& nodes); - // ------------------------------------------------------------------- /** Add sub nodes to a node * \param pcParent parent node to be filled @@ -183,7 +169,6 @@ private: void GenerateDefaultMaterial(); protected: - /** Parser instance */ ASE::Parser* mParser; diff --git a/code/AssetLib/ASE/ASEParser.cpp b/code/AssetLib/ASE/ASEParser.cpp index 1f7018b11..a2884b8dd 100644 --- a/code/AssetLib/ASE/ASEParser.cpp +++ b/code/AssetLib/ASE/ASEParser.cpp @@ -5,8 +5,6 @@ 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, diff --git a/code/AssetLib/Assbin/AssbinExporter.h b/code/AssetLib/Assbin/AssbinExporter.h index 96dfdd1c2..4871b5021 100644 --- a/code/AssetLib/Assbin/AssbinExporter.h +++ b/code/AssetLib/Assbin/AssbinExporter.h @@ -4,7 +4,6 @@ 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, @@ -43,6 +42,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** @file AssbinExporter.h * ASSBIN Exporter Main Header */ +#pragma once #ifndef AI_ASSBINEXPORTER_H_INC #define AI_ASSBINEXPORTER_H_INC diff --git a/code/AssetLib/Assbin/AssbinLoader.h b/code/AssetLib/Assbin/AssbinLoader.h index 59d799a3d..47f38db2a 100644 --- a/code/AssetLib/Assbin/AssbinLoader.h +++ b/code/AssetLib/Assbin/AssbinLoader.h @@ -5,7 +5,6 @@ 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, @@ -74,17 +73,11 @@ private: bool compressed; public: - virtual bool CanRead( - const std::string& pFile, - IOSystem* pIOHandler, - bool checkSig - ) const; - virtual const aiImporterDesc* GetInfo() const; - virtual void InternReadFile( - const std::string& pFile, - aiScene* pScene, - IOSystem* pIOHandler - ); + bool CanRead(const std::string& pFile, + IOSystem* pIOHandler, bool checkSig) const override; + const aiImporterDesc* GetInfo() const override; + void InternReadFile( + const std::string& pFile,aiScene* pScene,IOSystem* pIOHandler) override; void ReadHeader(); void ReadBinaryScene( IOStream * stream, aiScene* pScene ); void ReadBinaryNode( IOStream * stream, aiNode** mRootNode, aiNode* parent ); diff --git a/code/AssetLib/Assjson/mesh_splitter.cpp b/code/AssetLib/Assjson/mesh_splitter.cpp index 9301cc27e..978437c41 100644 --- a/code/AssetLib/Assjson/mesh_splitter.cpp +++ b/code/AssetLib/Assjson/mesh_splitter.cpp @@ -69,13 +69,12 @@ void MeshSplitter::UpdateNode(aiNode* pcNode, const std::vectormNumChildren; i < end;++i) { UpdateNode ( pcNode->mChildren[i], source_mesh_map ); } - return; } -#define WAS_NOT_COPIED 0xffffffff +static const unsigned int WAS_NOT_COPIED = 0xffffffff; -typedef std::pair PerVertexWeight; -typedef std::vector VertexWeightTable; +using PerVertexWeight = std::pair ; +using VertexWeightTable = std::vector ; // ------------------------------------------------------------------------------------------------ VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh) { @@ -89,7 +88,7 @@ VertexWeightTable* ComputeVertexBoneWeightTable(const aiMesh* pMesh) { aiBone* bone = pMesh->mBones[i]; for (unsigned int a = 0; a < bone->mNumWeights;++a) { const aiVertexWeight& weight = bone->mWeights[a]; - avPerVertexWeights[weight.mVertexId].push_back( std::make_pair(i,weight.mWeight) ); + avPerVertexWeights[weight.mVertexId].emplace_back(i,weight.mWeight); } } return avPerVertexWeights; @@ -100,7 +99,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vectormNumVertices <= LIMIT) { - source_mesh_map.push_back(std::make_pair(in_mesh,a)); + source_mesh_map.emplace_back(in_mesh,a); return; } @@ -187,7 +186,7 @@ void MeshSplitter :: SplitMesh(unsigned int a, aiMesh* in_mesh, std::vector> &source_mesh_map); + void SplitMesh(unsigned int index, aiMesh *mesh, std::vector> &source_mesh_map); - void UpdateNode(aiNode* pcNode, const std::vector >& source_mesh_map); - void SplitMesh (unsigned int index, aiMesh* mesh, std::vector >& source_mesh_map); - -public: - - unsigned int LIMIT; }; #endif // INCLUDED_MESH_SPLITTER - diff --git a/code/AssetLib/Assxml/AssxmlExporter.h b/code/AssetLib/Assxml/AssxmlExporter.h index 5c7f155d1..9a3d1f9d5 100644 --- a/code/AssetLib/Assxml/AssxmlExporter.h +++ b/code/AssetLib/Assxml/AssxmlExporter.h @@ -43,6 +43,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** @file AssxmlExporter.h * ASSXML Exporter Main Header */ +#pragma once #ifndef AI_ASSXMLEXPORTER_H_INC #define AI_ASSXMLEXPORTER_H_INC diff --git a/code/AssetLib/Assxml/AssxmlFileWriter.h b/code/AssetLib/Assxml/AssxmlFileWriter.h index e5f27095e..125869d1a 100644 --- a/code/AssetLib/Assxml/AssxmlFileWriter.h +++ b/code/AssetLib/Assxml/AssxmlFileWriter.h @@ -4,7 +4,6 @@ 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, @@ -43,7 +42,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** @file AssxmlFileWriter.h * @brief Declaration of Assxml file writer. */ - +#pragma once #ifndef AI_ASSXMLFILEWRITER_H_INC #define AI_ASSXMLFILEWRITER_H_INC diff --git a/code/AssetLib/B3D/B3DImporter.h b/code/AssetLib/B3D/B3DImporter.h index a7ed65c3b..bb535afd1 100644 --- a/code/AssetLib/B3D/B3DImporter.h +++ b/code/AssetLib/B3D/B3DImporter.h @@ -4,7 +4,6 @@ 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, @@ -40,8 +39,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ -/** @file Definition of the .b3d importer class. */ - +/** + * @file Definition of the .b3d importer class. + */ +#pragma once #ifndef AI_B3DIMPORTER_H_INC #define AI_B3DIMPORTER_H_INC @@ -62,14 +63,12 @@ namespace Assimp{ class B3DImporter : public BaseImporter{ public: B3DImporter() = default; - virtual ~B3DImporter(); - - virtual bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const; + ~B3DImporter() override; + bool CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const override; protected: - - virtual const aiImporterDesc* GetInfo () const; - virtual void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler); + const aiImporterDesc* GetInfo () const override; + void InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler) override; private: @@ -113,7 +112,6 @@ private: void ReadBB3D( aiScene *scene ); size_t _pos; -// unsigned _size; std::vector _buf; std::vector _stack; diff --git a/code/AssetLib/Blender/BlenderLoader.h b/code/AssetLib/Blender/BlenderLoader.h index 42da76514..1df0fcf90 100644 --- a/code/AssetLib/Blender/BlenderLoader.h +++ b/code/AssetLib/Blender/BlenderLoader.h @@ -4,7 +4,6 @@ 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, @@ -43,6 +42,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** @file BlenderLoader.h * @brief Declaration of the Blender 3D (*.blend) importer class. */ +#pragma once #ifndef INCLUDED_AI_BLEND_LOADER_H #define INCLUDED_AI_BLEND_LOADER_H @@ -56,149 +56,139 @@ struct aiLight; struct aiCamera; struct aiMaterial; -namespace Assimp { +namespace Assimp { - // TinyFormatter.h - namespace Formatter { - template class basic_formatter; - typedef class basic_formatter< char, std::char_traits, std::allocator > format; - } +// TinyFormatter.h +namespace Formatter { - // BlenderDNA.h - namespace Blender { - class FileDatabase; - struct ElemBase; - } +template +class basic_formatter; - // BlenderScene.h - namespace Blender { - struct Scene; - struct Object; - struct Mesh; - struct Camera; - struct Lamp; - struct MTex; - struct Image; - struct Material; - } +typedef class basic_formatter, std::allocator> format; - // BlenderIntermediate.h - namespace Blender { - struct ConversionData; - template

    Ao$|V)IS!XrjDofR+cp% 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