From ad214c03560d721d9b8bbff03835fc7fa4884943 Mon Sep 17 00:00:00 2001 From: Gordon MacPherson Date: Fri, 30 Aug 2019 02:21:40 +0100 Subject: Assimp FBX Import support Issues fixed: - Updated assimp to latest and backported fixes into godot. - Fixed file scale being ignored from FBX file. - Fixed bone removal - Implemented proper armature binding - Fixed recursion not always going through the entire path - Implemented assimp global scaling system - Fixed assimp global scale process to support unit conversion - Implemented proper fbx scaling - Fixed asserts caused by missing faces in some models which could crash - Fixed valid bone removal - Fixed root node being overwriten by assimp which caused data loss - Fixed armature construction so that it works with multiple roots - Implemented basic support for FBX standard materials - Refactoring to improve code quality and improve function reuse. - Simplified node creation from assimp scene into subsections: create_light, create_mesh, create_bone. - Creating meshes is now done after hierarchy is created so that the skeleton is always available. - Added support to assimp to support file scale in all formats which call SetFileScale. - Many other fixes provided into assimp. Known issues: - FBX pivots from Maya do not currently work. (workaround: for now use blender import and export to remove pivot tracks) - Hierarchy creates an extra node for each mesh - this was done intentionally but we intended to do a pass to remove these as they're a required node. - When an animated mesh has not executed any animation the rest pose is wrong. Co-authored-by: K. S. Ernest (iFire) Lee --- thirdparty/assimp/assimp/config.h | 7 + thirdparty/assimp/code/Common/BaseImporter.cpp | 27 +- thirdparty/assimp/code/FBX/FBXConverter.cpp | 96 +- thirdparty/assimp/code/FBX/FBXConverter.h | 7 - thirdparty/assimp/code/FBX/FBXDocument.cpp | 8 - thirdparty/assimp/code/FBX/FBXExporter.cpp | 43 +- thirdparty/assimp/code/FBX/FBXImporter.cpp | 10 +- thirdparty/assimp/code/FBX/FBXMeshGeometry.cpp | 7 +- .../code/PostProcessing/CalcTangentsProcess.cpp | 6 +- .../assimp/code/PostProcessing/ScaleProcess.cpp | 125 +- .../assimp/code/PostProcessing/ScaleProcess.h | 13 +- thirdparty/assimp/contrib/utf8cpp/doc/ReleaseNotes | 12 + thirdparty/assimp/contrib/utf8cpp/doc/utf8cpp.html | 1789 ++++++++++++++++++++ thirdparty/assimp/include/assimp/.editorconfig | 8 - thirdparty/assimp/include/assimp/BaseImporter.h | 61 +- thirdparty/assimp/include/assimp/config.h.in | 9 +- thirdparty/assimp/include/assimp/irrXMLWrapper.h | 144 -- thirdparty/assimp/include/assimp/scene.h | 2 +- 18 files changed, 2077 insertions(+), 297 deletions(-) create mode 100644 thirdparty/assimp/contrib/utf8cpp/doc/ReleaseNotes create mode 100644 thirdparty/assimp/contrib/utf8cpp/doc/utf8cpp.html delete mode 100644 thirdparty/assimp/include/assimp/.editorconfig delete mode 100644 thirdparty/assimp/include/assimp/irrXMLWrapper.h (limited to 'thirdparty') diff --git a/thirdparty/assimp/assimp/config.h b/thirdparty/assimp/assimp/config.h index 382a698268..d0e4817349 100644 --- a/thirdparty/assimp/assimp/config.h +++ b/thirdparty/assimp/assimp/config.h @@ -983,6 +983,13 @@ enum aiComponent { #define AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT 1.0f #endif // !! AI_DEBONE_THRESHOLD +#define AI_CONFIG_APP_SCALE_KEY "APP_SCALE_FACTOR" + +#if (!defined AI_CONFIG_APP_SCALE_KEY) +# define AI_CONFIG_APP_SCALE_KEY 1.0 +#endif // AI_CONFIG_APP_SCALE_KEY + + // ---------- All the Build/Compile-time defines ------------ /** @brief Specifies if double precision is supported inside assimp diff --git a/thirdparty/assimp/code/Common/BaseImporter.cpp b/thirdparty/assimp/code/Common/BaseImporter.cpp index 0a5694aa0e..de5018a250 100644 --- a/thirdparty/assimp/code/Common/BaseImporter.cpp +++ b/thirdparty/assimp/code/Common/BaseImporter.cpp @@ -76,9 +76,25 @@ BaseImporter::~BaseImporter() { // nothing to do here } +void BaseImporter::UpdateImporterScale( Importer* pImp ) +{ + ai_assert(pImp != nullptr); + ai_assert(importerScale != 0.0); + ai_assert(fileScale != 0.0); + + double activeScale = importerScale * fileScale; + + // Set active scaling + pImp->SetPropertyFloat( AI_CONFIG_APP_SCALE_KEY, activeScale); + + ASSIMP_LOG_DEBUG_F("UpdateImporterScale scale set: %f", activeScale ); +} + // ------------------------------------------------------------------------------------------------ // Imports the given file and returns the imported data. -aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile, IOSystem* pIOHandler) { +aiScene* BaseImporter::ReadFile(Importer* pImp, const std::string& pFile, IOSystem* pIOHandler) { + + m_progress = pImp->GetProgressHandler(); if (nullptr == m_progress) { return nullptr; @@ -100,6 +116,11 @@ aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile, { InternReadFile( pFile, sc.get(), &filter); + // Calculate import scale hook - required because pImp not available anywhere else + // passes scale into ScaleProcess + UpdateImporterScale(pImp); + + } catch( const std::exception& err ) { // extract error description m_ErrorText = err.what(); @@ -112,7 +133,7 @@ aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile, } // ------------------------------------------------------------------------------------------------ -void BaseImporter::SetupProperties(const Importer* /*pImp*/) +void BaseImporter::SetupProperties(const Importer* pImp) { // the default implementation does nothing } @@ -588,6 +609,8 @@ aiScene* BatchLoader::GetImport( unsigned int which ) return nullptr; } + + // ------------------------------------------------------------------------------------------------ void BatchLoader::LoadAll() { diff --git a/thirdparty/assimp/code/FBX/FBXConverter.cpp b/thirdparty/assimp/code/FBX/FBXConverter.cpp index 9f940d3226..9bd970098e 100644 --- a/thirdparty/assimp/code/FBX/FBXConverter.cpp +++ b/thirdparty/assimp/code/FBX/FBXConverter.cpp @@ -66,6 +66,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include +#include namespace Assimp { @@ -90,7 +91,6 @@ namespace Assimp { , anim_fps() , out(out) , doc(doc) - , mRemoveEmptyBones( removeEmptyBones ) , mCurrentUnit(FbxUnit::cm) { // animations need to be converted first since this will // populate the node_anim_chain_bits map, which is needed @@ -119,7 +119,6 @@ namespace Assimp { ConvertGlobalSettings(); TransferDataToScene(); - ConvertToUnitScale(unit); // if we didn't read any meshes set the AI_SCENE_FLAGS_INCOMPLETE // to make sure the scene passes assimp's validation. FBX files @@ -685,30 +684,37 @@ namespace Assimp { bool ok; aiMatrix4x4 chain[TransformationComp_MAXIMUM]; + + ai_assert(TransformationComp_MAXIMUM < 32); + std::uint32_t chainBits = 0; + // A node won't need a node chain if it only has these. + const std::uint32_t chainMaskSimple = (1 << TransformationComp_Translation) + (1 << TransformationComp_Scaling) + (1 << TransformationComp_Rotation); + // A node will need a node chain if it has any of these. + const std::uint32_t chainMaskComplex = ((1 << (TransformationComp_MAXIMUM)) - 1) - chainMaskSimple; + std::fill_n(chain, static_cast(TransformationComp_MAXIMUM), aiMatrix4x4()); // generate transformation matrices for all the different transformation components const float zero_epsilon = 1e-6f; const aiVector3D all_ones(1.0f, 1.0f, 1.0f); - bool is_complex = false; const aiVector3D& PreRotation = PropertyGet(props, "PreRotation", ok); if (ok && PreRotation.SquareLength() > zero_epsilon) { - is_complex = true; + chainBits = chainBits | (1 << TransformationComp_PreRotation); GetRotationMatrix(Model::RotOrder::RotOrder_EulerXYZ, PreRotation, chain[TransformationComp_PreRotation]); } const aiVector3D& PostRotation = PropertyGet(props, "PostRotation", ok); if (ok && PostRotation.SquareLength() > zero_epsilon) { - is_complex = true; + chainBits = chainBits | (1 << TransformationComp_PostRotation); GetRotationMatrix(Model::RotOrder::RotOrder_EulerXYZ, PostRotation, chain[TransformationComp_PostRotation]); } const aiVector3D& RotationPivot = PropertyGet(props, "RotationPivot", ok); if (ok && RotationPivot.SquareLength() > zero_epsilon) { - is_complex = true; + chainBits = chainBits | (1 << TransformationComp_RotationPivot) | (1 << TransformationComp_RotationPivotInverse); aiMatrix4x4::Translation(RotationPivot, chain[TransformationComp_RotationPivot]); aiMatrix4x4::Translation(-RotationPivot, chain[TransformationComp_RotationPivotInverse]); @@ -716,21 +722,21 @@ namespace Assimp { const aiVector3D& RotationOffset = PropertyGet(props, "RotationOffset", ok); if (ok && RotationOffset.SquareLength() > zero_epsilon) { - is_complex = true; + chainBits = chainBits | (1 << TransformationComp_RotationOffset); aiMatrix4x4::Translation(RotationOffset, chain[TransformationComp_RotationOffset]); } const aiVector3D& ScalingOffset = PropertyGet(props, "ScalingOffset", ok); if (ok && ScalingOffset.SquareLength() > zero_epsilon) { - is_complex = true; + chainBits = chainBits | (1 << TransformationComp_ScalingOffset); aiMatrix4x4::Translation(ScalingOffset, chain[TransformationComp_ScalingOffset]); } const aiVector3D& ScalingPivot = PropertyGet(props, "ScalingPivot", ok); if (ok && ScalingPivot.SquareLength() > zero_epsilon) { - is_complex = true; + chainBits = chainBits | (1 << TransformationComp_ScalingPivot) | (1 << TransformationComp_ScalingPivotInverse); aiMatrix4x4::Translation(ScalingPivot, chain[TransformationComp_ScalingPivot]); aiMatrix4x4::Translation(-ScalingPivot, chain[TransformationComp_ScalingPivotInverse]); @@ -738,22 +744,28 @@ namespace Assimp { const aiVector3D& Translation = PropertyGet(props, "Lcl Translation", ok); if (ok && Translation.SquareLength() > zero_epsilon) { + chainBits = chainBits | (1 << TransformationComp_Translation); + aiMatrix4x4::Translation(Translation, chain[TransformationComp_Translation]); } const aiVector3D& Scaling = PropertyGet(props, "Lcl Scaling", ok); if (ok && (Scaling - all_ones).SquareLength() > zero_epsilon) { + chainBits = chainBits | (1 << TransformationComp_Scaling); + aiMatrix4x4::Scaling(Scaling, chain[TransformationComp_Scaling]); } const aiVector3D& Rotation = PropertyGet(props, "Lcl Rotation", ok); if (ok && Rotation.SquareLength() > zero_epsilon) { + chainBits = chainBits | (1 << TransformationComp_Rotation); + GetRotationMatrix(rot, Rotation, chain[TransformationComp_Rotation]); } const aiVector3D& GeometricScaling = PropertyGet(props, "GeometricScaling", ok); if (ok && (GeometricScaling - all_ones).SquareLength() > zero_epsilon) { - is_complex = true; + chainBits = chainBits | (1 << TransformationComp_GeometricScaling); aiMatrix4x4::Scaling(GeometricScaling, chain[TransformationComp_GeometricScaling]); aiVector3D GeometricScalingInverse = GeometricScaling; bool canscale = true; @@ -768,13 +780,14 @@ namespace Assimp { } } if (canscale) { + chainBits = chainBits | (1 << TransformationComp_GeometricScalingInverse); aiMatrix4x4::Scaling(GeometricScalingInverse, chain[TransformationComp_GeometricScalingInverse]); } } const aiVector3D& GeometricRotation = PropertyGet(props, "GeometricRotation", ok); if (ok && GeometricRotation.SquareLength() > zero_epsilon) { - is_complex = true; + chainBits = chainBits | (1 << TransformationComp_GeometricRotation) | (1 << TransformationComp_GeometricRotationInverse); GetRotationMatrix(rot, GeometricRotation, chain[TransformationComp_GeometricRotation]); GetRotationMatrix(rot, GeometricRotation, chain[TransformationComp_GeometricRotationInverse]); chain[TransformationComp_GeometricRotationInverse].Inverse(); @@ -782,7 +795,7 @@ namespace Assimp { const aiVector3D& GeometricTranslation = PropertyGet(props, "GeometricTranslation", ok); if (ok && GeometricTranslation.SquareLength() > zero_epsilon) { - is_complex = true; + chainBits = chainBits | (1 << TransformationComp_GeometricTranslation) | (1 << TransformationComp_GeometricTranslationInverse); aiMatrix4x4::Translation(GeometricTranslation, chain[TransformationComp_GeometricTranslation]); aiMatrix4x4::Translation(-GeometricTranslation, chain[TransformationComp_GeometricTranslationInverse]); } @@ -790,12 +803,12 @@ namespace Assimp { // is_complex needs to be consistent with NeedsComplexTransformationChain() // or the interplay between this code and the animation converter would // not be guaranteed. - ai_assert(NeedsComplexTransformationChain(model) == is_complex); + ai_assert(NeedsComplexTransformationChain(model) == ((chainBits & chainMaskComplex) != 0)); // now, if we have more than just Translation, Scaling and Rotation, // we need to generate a full node chain to accommodate for assimp's // lack to express pivots and offsets. - if (is_complex && doc.Settings().preservePivots) { + if ((chainBits & chainMaskComplex) && doc.Settings().preservePivots) { FBXImporter::LogInfo("generating full transformation chain for node: " + name); // query the anim_chain_bits dictionary to find out which chain elements @@ -808,7 +821,7 @@ namespace Assimp { for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i, bit <<= 1) { const TransformationComp comp = static_cast(i); - if (chain[i].IsIdentity() && (anim_chain_bitmask & bit) == 0) { + if ((chainBits & bit) == 0 && (anim_chain_bitmask & bit) == 0) { continue; } @@ -1462,14 +1475,8 @@ namespace Assimp { const WeightIndexArray& indices = cluster->GetIndices(); - if (indices.empty() && mRemoveEmptyBones ) { - continue; - } - const MatIndexArray& mats = geo.GetMaterialIndices(); - bool ok = false; - const size_t no_index_sentinel = std::numeric_limits::max(); count_out_indices.clear(); @@ -1509,8 +1516,7 @@ namespace Assimp { out_indices.push_back(std::distance(outputVertStartIndices->begin(), it)); } - ++count_out_indices.back(); - ok = true; + ++count_out_indices.back(); } } } @@ -1518,10 +1524,8 @@ namespace Assimp { // if we found at least one, generate the output bones // XXX this could be heavily simplified by collecting the bone // data in a single step. - if (ok && mRemoveEmptyBones) { - ConvertCluster(bones, model, *cluster, out_indices, index_out_indices, + ConvertCluster(bones, model, *cluster, out_indices, index_out_indices, count_out_indices, node_global_transform); - } } } catch (std::exception&) { @@ -3532,46 +3536,6 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa out->mMetaData->Set(14, "CustomFrameRate", doc.GlobalSettings().CustomFrameRate()); } - void FBXConverter::ConvertToUnitScale( FbxUnit unit ) { - if (mCurrentUnit == unit) { - return; - } - - ai_real scale = 1.0; - if (mCurrentUnit == FbxUnit::cm) { - if (unit == FbxUnit::m) { - scale = (ai_real)0.01; - } else if (unit == FbxUnit::km) { - scale = (ai_real)0.00001; - } - } else if (mCurrentUnit == FbxUnit::m) { - if (unit == FbxUnit::cm) { - scale = (ai_real)100.0; - } else if (unit == FbxUnit::km) { - scale = (ai_real)0.001; - } - } else if (mCurrentUnit == FbxUnit::km) { - if (unit == FbxUnit::cm) { - scale = (ai_real)100000.0; - } else if (unit == FbxUnit::m) { - scale = (ai_real)1000.0; - } - } - - for (auto mesh : meshes) { - if (nullptr == mesh) { - continue; - } - - if (mesh->HasPositions()) { - for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { - aiVector3D &pos = mesh->mVertices[i]; - pos *= scale; - } - } - } - } - void FBXConverter::TransferDataToScene() { ai_assert(!out->mMeshes); diff --git a/thirdparty/assimp/code/FBX/FBXConverter.h b/thirdparty/assimp/code/FBX/FBXConverter.h index 17a7bc56b7..b458627392 100644 --- a/thirdparty/assimp/code/FBX/FBXConverter.h +++ b/thirdparty/assimp/code/FBX/FBXConverter.h @@ -430,10 +430,6 @@ private: void ConvertGlobalSettings(); - // ------------------------------------------------------------------------------------------------ - // Will perform the conversion from a given unit to the requested unit. - void ConvertToUnitScale(FbxUnit unit); - // ------------------------------------------------------------------------------------------------ // copy generated meshes, animations, lights, cameras and textures to the output scene void TransferDataToScene(); @@ -470,9 +466,6 @@ private: aiScene* const out; const FBX::Document& doc; - - bool mRemoveEmptyBones; - FbxUnit mCurrentUnit; }; diff --git a/thirdparty/assimp/code/FBX/FBXDocument.cpp b/thirdparty/assimp/code/FBX/FBXDocument.cpp index 1af08fe6d8..506fd978dd 100644 --- a/thirdparty/assimp/code/FBX/FBXDocument.cpp +++ b/thirdparty/assimp/code/FBX/FBXDocument.cpp @@ -90,14 +90,6 @@ const Object* LazyObject::Get(bool dieOnError) return object.get(); } - // if this is the root object, we return a dummy since there - // is no root object int he fbx file - it is just referenced - // with id 0. - if(id == 0L) { - object.reset(new Object(id, element, "Model::RootNode")); - return object.get(); - } - const Token& key = element.KeyToken(); const TokenList& tokens = element.Tokens(); diff --git a/thirdparty/assimp/code/FBX/FBXExporter.cpp b/thirdparty/assimp/code/FBX/FBXExporter.cpp index 153e676506..8ebc8555a2 100644 --- a/thirdparty/assimp/code/FBX/FBXExporter.cpp +++ b/thirdparty/assimp/code/FBX/FBXExporter.cpp @@ -1706,8 +1706,7 @@ void FBXExporter::WriteObjects () } if (end) { break; } } - limbnodes.insert(parent); - skeleton.insert(parent); + // if it was the skeleton root we can finish here if (end) { break; } } @@ -1848,44 +1847,10 @@ void FBXExporter::WriteObjects () inverse_bone_xform.Inverse(); aiMatrix4x4 tr = inverse_bone_xform * mesh_xform; - // this should be the same as the bone's mOffsetMatrix. - // if it's not the same, the skeleton isn't in the bind pose. - float epsilon = 1e-4f; // some error is to be expected - float epsilon_custom = mProperties->GetPropertyFloat("BINDPOSE_EPSILON", -1); - if(epsilon_custom > 0) - epsilon = epsilon_custom; - bool bone_xform_okay = true; - if (b && ! tr.Equal(b->mOffsetMatrix, epsilon)) { - not_in_bind_pose.insert(b); - bone_xform_okay = false; - } + sdnode.AddChild("Transform", tr); - // if we have a bone we should use the mOffsetMatrix, - // otherwise try to just use the calculated transform. - if (b) { - sdnode.AddChild("Transform", b->mOffsetMatrix); - } else { - sdnode.AddChild("Transform", tr); - } - // note: it doesn't matter if we mix these, - // because if they disagree we'll throw an exception later. - // it could be that the skeleton is not in the bone pose - // but all bones are still defined, - // in which case this would use the mOffsetMatrix for everything - // and a correct skeleton would still be output. - - // transformlink should be the position of the bone in world space. - // if the bone is in the bind pose (or nonexistent), - // we can just use the matrix we already calculated - if (bone_xform_okay) { - sdnode.AddChild("TransformLink", bone_xform); - // otherwise we can only work it out using the mesh position. - } else { - aiMatrix4x4 trl = b->mOffsetMatrix; - trl.Inverse(); - trl *= mesh_xform; - sdnode.AddChild("TransformLink", trl); - } + + sdnode.AddChild("TransformLink", bone_xform); // note: this means we ALWAYS rely on the mesh node transform // being unchanged from the time the skeleton was bound. // there's not really any way around this at the moment. diff --git a/thirdparty/assimp/code/FBX/FBXImporter.cpp b/thirdparty/assimp/code/FBX/FBXImporter.cpp index ec8bbd2b47..bd359dbf29 100644 --- a/thirdparty/assimp/code/FBX/FBXImporter.cpp +++ b/thirdparty/assimp/code/FBX/FBXImporter.cpp @@ -189,8 +189,16 @@ void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS if (settings.convertToMeters) { unit = FbxUnit::m; } + // convert the FBX DOM to aiScene - ConvertToAssimpScene(pScene,doc, settings.removeEmptyBones, unit); + ConvertToAssimpScene(pScene, doc, settings.removeEmptyBones, unit); + + // size relative to cm + float size_relative_to_cm = doc.GlobalSettings().UnitScaleFactor(); + + // Set FBX file scale is relative to CM must be converted to M for + // assimp universal format (M) + SetFileScale( size_relative_to_cm * 0.01f); std::for_each(tokens.begin(),tokens.end(),Util::delete_fun()); } diff --git a/thirdparty/assimp/code/FBX/FBXMeshGeometry.cpp b/thirdparty/assimp/code/FBX/FBXMeshGeometry.cpp index 44a0264ca0..5c9a0e309d 100644 --- a/thirdparty/assimp/code/FBX/FBXMeshGeometry.cpp +++ b/thirdparty/assimp/code/FBX/FBXMeshGeometry.cpp @@ -115,7 +115,6 @@ MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::strin if(tempVerts.empty()) { FBXImporter::LogWarn("encountered mesh with no vertices"); - return; } std::vector tempFaces; @@ -123,7 +122,6 @@ MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::strin if(tempFaces.empty()) { FBXImporter::LogWarn("encountered mesh with no faces"); - return; } m_vertices.reserve(tempFaces.size()); @@ -612,7 +610,10 @@ void MeshGeometry::ReadVertexDataMaterials(std::vector& materials_out, cons const std::string& ReferenceInformationType) { const size_t face_count = m_faces.size(); - ai_assert(face_count); + if(face_count <= 0) + { + return; + } // materials are handled separately. First of all, they are assigned per-face // and not per polyvert. Secondly, ReferenceInformationType=IndexToDirect diff --git a/thirdparty/assimp/code/PostProcessing/CalcTangentsProcess.cpp b/thirdparty/assimp/code/PostProcessing/CalcTangentsProcess.cpp index b30f39c274..a3f7dd2557 100644 --- a/thirdparty/assimp/code/PostProcessing/CalcTangentsProcess.cpp +++ b/thirdparty/assimp/code/PostProcessing/CalcTangentsProcess.cpp @@ -212,7 +212,7 @@ bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) // project tangent and bitangent into the plane formed by the vertex' normal aiVector3D localTangent = tangent - meshNorm[p] * (tangent * meshNorm[p]); aiVector3D localBitangent = bitangent - meshNorm[p] * (bitangent * meshNorm[p]); - localTangent.Normalize(); localBitangent.Normalize(); + localTangent.NormalizeSafe(); localBitangent.NormalizeSafe(); // reconstruct tangent/bitangent according to normal and bitangent/tangent when it's infinite or NaN. bool invalid_tangent = is_special_float(localTangent.x) || is_special_float(localTangent.y) || is_special_float(localTangent.z); @@ -220,10 +220,10 @@ bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) if (invalid_tangent != invalid_bitangent) { if (invalid_tangent) { localTangent = meshNorm[p] ^ localBitangent; - localTangent.Normalize(); + localTangent.NormalizeSafe(); } else { localBitangent = localTangent ^ meshNorm[p]; - localBitangent.Normalize(); + localBitangent.NormalizeSafe(); } } diff --git a/thirdparty/assimp/code/PostProcessing/ScaleProcess.cpp b/thirdparty/assimp/code/PostProcessing/ScaleProcess.cpp index 6d458c4b11..ac770c41f2 100644 --- a/thirdparty/assimp/code/PostProcessing/ScaleProcess.cpp +++ b/thirdparty/assimp/code/PostProcessing/ScaleProcess.cpp @@ -39,19 +39,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ -#ifndef ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS - #include "ScaleProcess.h" #include #include +#include namespace Assimp { ScaleProcess::ScaleProcess() : BaseProcess() , mScale( AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT ) { - // empty } ScaleProcess::~ScaleProcess() { @@ -71,10 +69,26 @@ bool ScaleProcess::IsActive( unsigned int pFlags ) const { } void ScaleProcess::SetupProperties( const Importer* pImp ) { - mScale = pImp->GetPropertyFloat( AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY, 0 ); + // User scaling + mScale = pImp->GetPropertyFloat( AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY, 1.0f ); + + // File scaling * Application Scaling + float importerScale = pImp->GetPropertyFloat( AI_CONFIG_APP_SCALE_KEY, 1.0f ); + + // apply scale to the scale + // helps prevent bugs with backward compatibility for anyone using normal scaling. + mScale *= importerScale; } 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 ); + if ( nullptr == pScene ) { return; } @@ -82,22 +96,113 @@ 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++ ) + { + aiAnimation* animation = pScene->mAnimations[animationID]; + + 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]; + vectorKey.mValue *= mScale; + } + } + } + + for( unsigned int meshID = 0; meshID < pScene->mNumMeshes; meshID++) + { + aiMesh *mesh = pScene->mMeshes[meshID]; + + // Reconstruct mesh vertexes to the new unit system + for( unsigned int vertexID = 0; vertexID < mesh->mNumVertices; vertexID++) + { + aiVector3D& vertex = mesh->mVertices[vertexID]; + vertex *= mScale; + } + + + // bone placement / scaling + for( unsigned int boneID = 0; boneID < mesh->mNumBones; boneID++) + { + // 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 see 1:1 compatibility. + aiBone* bone = mesh->mBones[boneID]; + + aiVector3D pos, scale; + aiQuaternion rotation; + + bone->mOffsetMatrix.Decompose( scale, rotation, pos); + + aiMatrix4x4 translation; + aiMatrix4x4::Translation( pos * mScale, translation ); + + aiMatrix4x4 scaling; + aiMatrix4x4::Scaling( aiVector3D(scale), scaling ); + + aiMatrix4x4 RotMatrix = aiMatrix4x4 (rotation.GetMatrix()); + + bone->mOffsetMatrix = translation * RotMatrix * scaling; + } + + + // animation mesh processing + // convert by position rather than scale. + 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]; + vertex *= mScale; + } + } + } traverseNodes( pScene->mRootNode ); } -void ScaleProcess::traverseNodes( aiNode *node ) { +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 ); + } } void ScaleProcess::applyScaling( aiNode *currentNode ) { if ( nullptr != currentNode ) { - currentNode->mTransformation.a1 = currentNode->mTransformation.a1 * mScale; - currentNode->mTransformation.b2 = currentNode->mTransformation.b2 * mScale; - currentNode->mTransformation.c3 = currentNode->mTransformation.c3 * mScale; + // 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 + // 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. + aiMatrix4x4::Scaling( scale, scaling ); + + aiMatrix4x4 RotMatrix = aiMatrix4x4 (rotation.GetMatrix()); + + currentNode->mTransformation = translation * RotMatrix * scaling; } } } // Namespace Assimp - -#endif // !! ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS diff --git a/thirdparty/assimp/code/PostProcessing/ScaleProcess.h b/thirdparty/assimp/code/PostProcessing/ScaleProcess.h index 2567378759..468a216736 100644 --- a/thirdparty/assimp/code/PostProcessing/ScaleProcess.h +++ b/thirdparty/assimp/code/PostProcessing/ScaleProcess.h @@ -39,7 +39,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ -#pragma once +#ifndef SCALE_PROCESS_H_ +#define SCALE_PROCESS_H_ #include "Common/BaseProcess.h" @@ -53,6 +54,11 @@ 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 + * from your model package are preserved, so this is completely intentional + * bugs should be reported as soon as they are found. */ class ASSIMP_API ScaleProcess : public BaseProcess { public: @@ -78,7 +84,7 @@ public: virtual void Execute( aiScene* pScene ); private: - void traverseNodes( aiNode *currentNode ); + void traverseNodes( aiNode *currentNode, unsigned int nested_node_id = 0 ); void applyScaling( aiNode *currentNode ); private: @@ -86,3 +92,6 @@ private: }; } // Namespace Assimp + + +#endif // SCALE_PROCESS_H_ \ No newline at end of file diff --git a/thirdparty/assimp/contrib/utf8cpp/doc/ReleaseNotes b/thirdparty/assimp/contrib/utf8cpp/doc/ReleaseNotes new file mode 100644 index 0000000000..364411a23d --- /dev/null +++ b/thirdparty/assimp/contrib/utf8cpp/doc/ReleaseNotes @@ -0,0 +1,12 @@ +utf8 cpp library +Release 2.3.4 + +A minor bug fix release. Thanks to all who reported bugs. + +Note: Version 2.3.3 contained a regression, and therefore was removed. + +Changes from version 2.3.2 +- Bug fix [39]: checked.h Line 273 and unchecked.h Line 182 have an extra ';' +- Bug fix [36]: replace_invalid() only works with back_inserter + +Files included in the release: utf8.h, core.h, checked.h, unchecked.h, utf8cpp.html, ReleaseNotes diff --git a/thirdparty/assimp/contrib/utf8cpp/doc/utf8cpp.html b/thirdparty/assimp/contrib/utf8cpp/doc/utf8cpp.html new file mode 100644 index 0000000000..6f2aacbe7b --- /dev/null +++ b/thirdparty/assimp/contrib/utf8cpp/doc/utf8cpp.html @@ -0,0 +1,1789 @@ + + + + + + + + + UTF8-CPP: UTF-8 with C++ in a Portable Way + + + + +

+ UTF8-CPP: UTF-8 with C++ in a Portable Way +

+

+ The Sourceforge project page +

+ +

+ Introduction +

+

+ Many C++ developers miss an easy and portable way of handling Unicode encoded + strings. The original C++ Standard (known as C++98 or C++03) is Unicode agnostic. + C++11 provides some support for Unicode on core language and library level: + u8, u, and U character and string literals, char16_t and char32_t character types, + u16string and u32string library classes, and codecvt support for conversions + between Unicode encoding forms. + In the meantime, developers use third party libraries like ICU, OS specific capabilities, or simply + roll out their own solutions. +

+

+ In order to easily handle UTF-8 encoded Unicode strings, I came up with a small + generic library. For anybody used to work with STL algorithms and iterators, it should be + easy and natural to use. The code is freely available for any purpose - check out + the license at the beginning of the utf8.h file. If you run into + bugs or performance issues, please let me know and I'll do my best to address them. +

+

+ The purpose of this article is not to offer an introduction to Unicode in general, + and UTF-8 in particular. If you are not familiar with Unicode, be sure to check out + Unicode Home Page or some other source of + information for Unicode. Also, it is not my aim to advocate the use of UTF-8 + encoded strings in C++ programs; if you want to handle UTF-8 encoded strings from + C++, I am sure you have good reasons for it. +

+

+ Examples of use +

+

+ Introductionary Sample +

+

+ To illustrate the use of the library, let's start with a small but complete program + that opens a file containing UTF-8 encoded text, reads it line by line, checks each line + for invalid UTF-8 byte sequences, and converts it to UTF-16 encoding and back to UTF-8: +

+
+#include <fstream>
+#include <iostream>
+#include <string>
+#include <vector>
+#include "utf8.h"
+using namespace std;
+int main(int argc, char** argv)
+{
+    if (argc != 2) {
+        cout << "\nUsage: docsample filename\n";
+        return 0;
+    }
+
+    const char* test_file_path = argv[1];
+    // Open the test file (contains UTF-8 encoded text)
+    ifstream fs8(test_file_path);
+    if (!fs8.is_open()) {
+    cout << "Could not open " << test_file_path << endl;
+    return 0;
+    }
+
+    unsigned line_count = 1;
+    string line;
+    // Play with all the lines in the file
+    while (getline(fs8, line)) {
+       // check for invalid utf-8 (for a simple yes/no check, there is also utf8::is_valid function)
+        string::iterator end_it = utf8::find_invalid(line.begin(), line.end());
+        if (end_it != line.end()) {
+            cout << "Invalid UTF-8 encoding detected at line " << line_count << "\n";
+            cout << "This part is fine: " << string(line.begin(), end_it) << "\n";
+        }
+
+        // Get the line length (at least for the valid part)
+        int length = utf8::distance(line.begin(), end_it);
+        cout << "Length of line " << line_count << " is " << length <<  "\n";
+
+        // Convert it to utf-16
+        vector<unsigned short> utf16line;
+        utf8::utf8to16(line.begin(), end_it, back_inserter(utf16line));
+
+        // And back to utf-8
+        string utf8line; 
+        utf8::utf16to8(utf16line.begin(), utf16line.end(), back_inserter(utf8line));
+
+        // Confirm that the conversion went OK:
+        if (utf8line != string(line.begin(), end_it))
+            cout << "Error in UTF-16 conversion at line: " << line_count << "\n";        
+
+        line_count++;
+    }
+    return 0;
+}
+
+

+ In the previous code sample, for each line we performed + a detection of invalid UTF-8 sequences with find_invalid; the number + of characters (more precisely - the number of Unicode code points, including the end + of line and even BOM if there is one) in each line was + determined with a use of utf8::distance; finally, we have converted + each line to UTF-16 encoding with utf8to16 and back to UTF-8 with + utf16to8. +

+

Checking if a file contains valid UTF-8 text

+

+Here is a function that checks whether the content of a file is valid UTF-8 encoded text without +reading the content into the memory: +

+
    
+bool valid_utf8_file(iconst char* file_name)
+{
+    ifstream ifs(file_name);
+    if (!ifs)
+        return false; // even better, throw here
+
+    istreambuf_iterator<char> it(ifs.rdbuf());
+    istreambuf_iterator<char> eos;
+
+    return utf8::is_valid(it, eos);
+}
+
+

+Because the function utf8::is_valid() works with input iterators, we were able +to pass an istreambuf_iterator to it and read the content of the file directly +without loading it to the memory first.

+

+Note that other functions that take input iterator arguments can be used in a similar way. For +instance, to read the content of a UTF-8 encoded text file and convert the text to UTF-16, just +do something like: +

+
+    utf8::utf8to16(it, eos, back_inserter(u16string));
+
+

Ensure that a string contains valid UTF-8 text

+

+If we have some text that "probably" contains UTF-8 encoded text and we want to +replace any invalid UTF-8 sequence with a replacement character, something like +the following function may be used: +

+
+void fix_utf8_string(std::string& str)
+{
+    std::string temp;
+    utf8::replace_invalid(str.begin(), str.end(), back_inserter(temp));
+    str = temp;
+}
+
+

The function will replace any invalid UTF-8 sequence with a Unicode replacement character. +There is an overloaded function that enables the caller to supply their own replacement character. +

+

+ Reference +

+

+ Functions From utf8 Namespace +

+

+ utf8::append +

+

+ Available in version 1.0 and later. +

+

+ Encodes a 32 bit code point as a UTF-8 sequence of octets and appends the sequence + to a UTF-8 string. +

+
+template <typename octet_iterator>
+octet_iterator append(uint32_t cp, octet_iterator result);
+   
+
+

+ octet_iterator: an output iterator.
+ cp: a 32 bit integer representing a code point to append to the + sequence.
+ result: an output iterator to the place in the sequence where to + append the code point.
+ Return value: an iterator pointing to the place + after the newly appended sequence. +

+

+ Example of use: +

+
+unsigned char u[5] = {0,0,0,0,0};
+unsigned char* end = append(0x0448, u);
+assert (u[0] == 0xd1 && u[1] == 0x88 && u[2] == 0 && u[3] == 0 && u[4] == 0);
+
+

+ Note that append does not allocate any memory - it is the burden of + the caller to make sure there is enough memory allocated for the operation. To make + things more interesting, append can add anywhere between 1 and 4 + octets to the sequence. In practice, you would most often want to use + std::back_inserter to ensure that the necessary memory is allocated. +

+

+ In case of an invalid code point, a utf8::invalid_code_point exception + is thrown. +

+

+ utf8::next +

+

+ Available in version 1.0 and later. +

+

+ Given the iterator to the beginning of the UTF-8 sequence, it returns the code + point and moves the iterator to the next position. +

+
+template <typename octet_iterator> 
+uint32_t next(octet_iterator& it, octet_iterator end);
+   
+
+

+ octet_iterator: an input iterator.
+ it: a reference to an iterator pointing to the beginning of an UTF-8 + encoded code point. After the function returns, it is incremented to point to the + beginning of the next code point.
+ end: end of the UTF-8 sequence to be processed. If it + gets equal to end during the extraction of a code point, an + utf8::not_enough_room exception is thrown.
+ Return value: the 32 bit representation of the + processed UTF-8 code point. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+char* w = twochars;
+int cp = next(w, twochars + 6);
+assert (cp == 0x65e5);
+assert (w == twochars + 3);
+
+

+ This function is typically used to iterate through a UTF-8 encoded string. +

+

+ In case of an invalid UTF-8 seqence, a utf8::invalid_utf8 exception is + thrown. +

+

+ utf8::peek_next +

+

+ Available in version 2.1 and later. +

+

+ Given the iterator to the beginning of the UTF-8 sequence, it returns the code + point for the following sequence without changing the value of the iterator. +

+
+template <typename octet_iterator> 
+uint32_t peek_next(octet_iterator it, octet_iterator end);
+   
+
+

+ octet_iterator: an input iterator.
+ it: an iterator pointing to the beginning of an UTF-8 + encoded code point.
+ end: end of the UTF-8 sequence to be processed. If it + gets equal to end during the extraction of a code point, an + utf8::not_enough_room exception is thrown.
+ Return value: the 32 bit representation of the + processed UTF-8 code point. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+char* w = twochars;
+int cp = peek_next(w, twochars + 6);
+assert (cp == 0x65e5);
+assert (w == twochars);
+
+

+ In case of an invalid UTF-8 seqence, a utf8::invalid_utf8 exception is + thrown. +

+

+ utf8::prior +

+

+ Available in version 1.02 and later. +

+

+ Given a reference to an iterator pointing to an octet in a UTF-8 sequence, it + decreases the iterator until it hits the beginning of the previous UTF-8 encoded + code point and returns the 32 bits representation of the code point. +

+
+template <typename octet_iterator> 
+uint32_t prior(octet_iterator& it, octet_iterator start);
+   
+
+

+ octet_iterator: a bidirectional iterator.
+ it: a reference pointing to an octet within a UTF-8 encoded string. + After the function returns, it is decremented to point to the beginning of the + previous code point.
+ start: an iterator to the beginning of the sequence where the search + for the beginning of a code point is performed. It is a + safety measure to prevent passing the beginning of the string in the search for a + UTF-8 lead octet.
+ Return value: the 32 bit representation of the + previous code point. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+unsigned char* w = twochars + 3;
+int cp = prior (w, twochars);
+assert (cp == 0x65e5);
+assert (w == twochars);
+
+

+ This function has two purposes: one is two iterate backwards through a UTF-8 + encoded string. Note that it is usually a better idea to iterate forward instead, + since utf8::next is faster. The second purpose is to find a beginning + of a UTF-8 sequence if we have a random position within a string. Note that in that + case utf8::prior may not detect an invalid UTF-8 sequence in some scenarios: + for instance if there are superfluous trail octets, it will just skip them. +

+

+ it will typically point to the beginning of + a code point, and start will point to the + beginning of the string to ensure we don't go backwards too far. it is + decreased until it points to a lead UTF-8 octet, and then the UTF-8 sequence + beginning with that octet is decoded to a 32 bit representation and returned. +

+

+ In case start is reached before a UTF-8 lead octet is hit, or if an + invalid UTF-8 sequence is started by the lead octet, an invalid_utf8 + exception is thrown. +

+

In case start equals it, a not_enough_room + exception is thrown. +

+ utf8::previous +

+

+ Deprecated in version 1.02 and later. +

+

+ Given a reference to an iterator pointing to an octet in a UTF-8 seqence, it + decreases the iterator until it hits the beginning of the previous UTF-8 encoded + code point and returns the 32 bits representation of the code point. +

+
+template <typename octet_iterator> 
+uint32_t previous(octet_iterator& it, octet_iterator pass_start);
+   
+
+

+ octet_iterator: a random access iterator.
+ it: a reference pointing to an octet within a UTF-8 encoded string. + After the function returns, it is decremented to point to the beginning of the + previous code point.
+ pass_start: an iterator to the point in the sequence where the search + for the beginning of a code point is aborted if no result was reached. It is a + safety measure to prevent passing the beginning of the string in the search for a + UTF-8 lead octet.
+ Return value: the 32 bit representation of the + previous code point. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+unsigned char* w = twochars + 3;
+int cp = previous (w, twochars - 1);
+assert (cp == 0x65e5);
+assert (w == twochars);
+
+

+ utf8::previous is deprecated, and utf8::prior should + be used instead, although the existing code can continue using this function. + The problem is the parameter pass_start that points to the position + just before the beginning of the sequence. Standard containers don't have the + concept of "pass start" and the function can not be used with their iterators. +

+

+ it will typically point to the beginning of + a code point, and pass_start will point to the octet just before the + beginning of the string to ensure we don't go backwards too far. it is + decreased until it points to a lead UTF-8 octet, and then the UTF-8 sequence + beginning with that octet is decoded to a 32 bit representation and returned. +

+

+ In case pass_start is reached before a UTF-8 lead octet is hit, or if an + invalid UTF-8 sequence is started by the lead octet, an invalid_utf8 + exception is thrown +

+

+ utf8::advance +

+

+ Available in version 1.0 and later. +

+

+ Advances an iterator by the specified number of code points within an UTF-8 + sequence. +

+
+template <typename octet_iterator, typename distance_type> 
+void advance (octet_iterator& it, distance_type n, octet_iterator end);
+   
+
+

+ octet_iterator: an input iterator.
+ distance_type: an integral type convertible to octet_iterator's difference type.
+ it: a reference to an iterator pointing to the beginning of an UTF-8 + encoded code point. After the function returns, it is incremented to point to the + nth following code point.
+ n: a positive integer that shows how many code points we want to + advance.
+ end: end of the UTF-8 sequence to be processed. If it + gets equal to end during the extraction of a code point, an + utf8::not_enough_room exception is thrown.
+

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+unsigned char* w = twochars;
+advance (w, 2, twochars + 6);
+assert (w == twochars + 5);
+
+

+ This function works only "forward". In case of a negative n, there is + no effect. +

+

+ In case of an invalid code point, a utf8::invalid_code_point exception + is thrown. +

+

+ utf8::distance +

+

+ Available in version 1.0 and later. +

+

+ Given the iterators to two UTF-8 encoded code points in a seqence, returns the + number of code points between them. +

+
+template <typename octet_iterator> 
+typename std::iterator_traits<octet_iterator>::difference_type distance (octet_iterator first, octet_iterator last);
+   
+
+

+ octet_iterator: an input iterator.
+ first: an iterator to a beginning of a UTF-8 encoded code point.
+ last: an iterator to a "post-end" of the last UTF-8 encoded code + point in the sequence we are trying to determine the length. It can be the + beginning of a new code point, or not.
+ Return value the distance between the iterators, + in code points. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+size_t dist = utf8::distance(twochars, twochars + 5);
+assert (dist == 2);
+
+

+ This function is used to find the length (in code points) of a UTF-8 encoded + string. The reason it is called distance, rather than, say, + length is mainly because developers are used that length is an + O(1) function. Computing the length of an UTF-8 string is a linear operation, and + it looked better to model it after std::distance algorithm. +

+

+ In case of an invalid UTF-8 seqence, a utf8::invalid_utf8 exception is + thrown. If last does not point to the past-of-end of a UTF-8 seqence, + a utf8::not_enough_room exception is thrown. +

+

+ utf8::utf16to8 +

+

+ Available in version 1.0 and later. +

+

+ Converts a UTF-16 encoded string to UTF-8. +

+
+template <typename u16bit_iterator, typename octet_iterator>
+octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result);
+   
+
+

+ u16bit_iterator: an input iterator.
+ octet_iterator: an output iterator.
+ start: an iterator pointing to the beginning of the UTF-16 encoded + string to convert.
+ end: an iterator pointing to pass-the-end of the UTF-16 encoded + string to convert.
+ result: an output iterator to the place in the UTF-8 string where to + append the result of conversion.
+ Return value: An iterator pointing to the place + after the appended UTF-8 string. +

+

+ Example of use: +

+
+unsigned short utf16string[] = {0x41, 0x0448, 0x65e5, 0xd834, 0xdd1e};
+vector<unsigned char> utf8result;
+utf16to8(utf16string, utf16string + 5, back_inserter(utf8result));
+assert (utf8result.size() == 10);    
+
+

+ In case of invalid UTF-16 sequence, a utf8::invalid_utf16 exception is + thrown. +

+

+ utf8::utf8to16 +

+

+ Available in version 1.0 and later. +

+

+ Converts an UTF-8 encoded string to UTF-16 +

+
+template <typename u16bit_iterator, typename octet_iterator>
+u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result);
+   
+
+

+ octet_iterator: an input iterator.
+ u16bit_iterator: an output iterator.
+ start: an iterator pointing to the beginning of the UTF-8 encoded + string to convert. < br /> end: an iterator pointing to + pass-the-end of the UTF-8 encoded string to convert.
+ result: an output iterator to the place in the UTF-16 string where to + append the result of conversion.
+ Return value: An iterator pointing to the place + after the appended UTF-16 string. +

+

+ Example of use: +

+
+char utf8_with_surrogates[] = "\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e";
+vector <unsigned short> utf16result;
+utf8to16(utf8_with_surrogates, utf8_with_surrogates + 9, back_inserter(utf16result));
+assert (utf16result.size() == 4);
+assert (utf16result[2] == 0xd834);
+assert (utf16result[3] == 0xdd1e);
+
+

+ In case of an invalid UTF-8 seqence, a utf8::invalid_utf8 exception is + thrown. If end does not point to the past-of-end of a UTF-8 seqence, a + utf8::not_enough_room exception is thrown. +

+

+ utf8::utf32to8 +

+

+ Available in version 1.0 and later. +

+

+ Converts a UTF-32 encoded string to UTF-8. +

+
+template <typename octet_iterator, typename u32bit_iterator>
+octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result);
+   
+
+

+ octet_iterator: an output iterator.
+ u32bit_iterator: an input iterator.
+ start: an iterator pointing to the beginning of the UTF-32 encoded + string to convert.
+ end: an iterator pointing to pass-the-end of the UTF-32 encoded + string to convert.
+ result: an output iterator to the place in the UTF-8 string where to + append the result of conversion.
+ Return value: An iterator pointing to the place + after the appended UTF-8 string. +

+

+ Example of use: +

+
+int utf32string[] = {0x448, 0x65E5, 0x10346, 0};
+vector<unsigned char> utf8result;
+utf32to8(utf32string, utf32string + 3, back_inserter(utf8result));
+assert (utf8result.size() == 9);
+
+

+ In case of invalid UTF-32 string, a utf8::invalid_code_point exception + is thrown. +

+

+ utf8::utf8to32 +

+

+ Available in version 1.0 and later. +

+

+ Converts a UTF-8 encoded string to UTF-32. +

+
+template <typename octet_iterator, typename u32bit_iterator>
+u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result);
+   
+
+

+ octet_iterator: an input iterator.
+ u32bit_iterator: an output iterator.
+ start: an iterator pointing to the beginning of the UTF-8 encoded + string to convert.
+ end: an iterator pointing to pass-the-end of the UTF-8 encoded string + to convert.
+ result: an output iterator to the place in the UTF-32 string where to + append the result of conversion.
+ Return value: An iterator pointing to the place + after the appended UTF-32 string. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+vector<int> utf32result;
+utf8to32(twochars, twochars + 5, back_inserter(utf32result));
+assert (utf32result.size() == 2);
+
+

+ In case of an invalid UTF-8 seqence, a utf8::invalid_utf8 exception is + thrown. If end does not point to the past-of-end of a UTF-8 seqence, a + utf8::not_enough_room exception is thrown. +

+

+ utf8::find_invalid +

+

+ Available in version 1.0 and later. +

+

+ Detects an invalid sequence within a UTF-8 string. +

+
+template <typename octet_iterator> 
+octet_iterator find_invalid(octet_iterator start, octet_iterator end);
+
+

+ octet_iterator: an input iterator.
+ start: an iterator pointing to the beginning of the UTF-8 string to + test for validity.
+ end: an iterator pointing to pass-the-end of the UTF-8 string to test + for validity.
+ Return value: an iterator pointing to the first + invalid octet in the UTF-8 string. In case none were found, equals + end. +

+

+ Example of use: +

+
+char utf_invalid[] = "\xe6\x97\xa5\xd1\x88\xfa";
+char* invalid = find_invalid(utf_invalid, utf_invalid + 6);
+assert (invalid == utf_invalid + 5);
+
+

+ This function is typically used to make sure a UTF-8 string is valid before + processing it with other functions. It is especially important to call it if before + doing any of the unchecked operations on it. +

+

+ utf8::is_valid +

+

+ Available in version 1.0 and later. +

+

+ Checks whether a sequence of octets is a valid UTF-8 string. +

+
+template <typename octet_iterator> 
+bool is_valid(octet_iterator start, octet_iterator end);
+   
+
+

+ octet_iterator: an input iterator.
+ start: an iterator pointing to the beginning of the UTF-8 string to + test for validity.
+ end: an iterator pointing to pass-the-end of the UTF-8 string to test + for validity.
+ Return value: true if the sequence + is a valid UTF-8 string; false if not. +

+ Example of use: +
+char utf_invalid[] = "\xe6\x97\xa5\xd1\x88\xfa";
+bool bvalid = is_valid(utf_invalid, utf_invalid + 6);
+assert (bvalid == false);
+
+

+ is_valid is a shorthand for find_invalid(start, end) == + end;. You may want to use it to make sure that a byte seqence is a valid + UTF-8 string without the need to know where it fails if it is not valid. +

+

+ utf8::replace_invalid +

+

+ Available in version 2.0 and later. +

+

+ Replaces all invalid UTF-8 sequences within a string with a replacement marker. +

+
+template <typename octet_iterator, typename output_iterator>
+output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement);
+template <typename octet_iterator, typename output_iterator>
+output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out);
+   
+
+

+ octet_iterator: an input iterator.
+ output_iterator: an output iterator.
+ start: an iterator pointing to the beginning of the UTF-8 string to + look for invalid UTF-8 sequences.
+ end: an iterator pointing to pass-the-end of the UTF-8 string to look + for invalid UTF-8 sequences.
+ out: An output iterator to the range where the result of replacement + is stored.
+ replacement: A Unicode code point for the replacement marker. The + version without this parameter assumes the value 0xfffd
+ Return value: An iterator pointing to the place + after the UTF-8 string with replaced invalid sequences. +

+

+ Example of use: +

+
+char invalid_sequence[] = "a\x80\xe0\xa0\xc0\xaf\xed\xa0\x80z";
+vector<char> replace_invalid_result;
+replace_invalid (invalid_sequence, invalid_sequence + sizeof(invalid_sequence), back_inserter(replace_invalid_result), '?');
+bvalid = is_valid(replace_invalid_result.begin(), replace_invalid_result.end());
+assert (bvalid);
+char* fixed_invalid_sequence = "a????z";
+assert (std::equal(replace_invalid_result.begin(), replace_invalid_result.end(), fixed_invalid_sequence));
+
+

+ replace_invalid does not perform in-place replacement of invalid + sequences. Rather, it produces a copy of the original string with the invalid + sequences replaced with a replacement marker. Therefore, out must not + be in the [start, end] range. +

+

+ If end does not point to the past-of-end of a UTF-8 sequence, a + utf8::not_enough_room exception is thrown. +

+

+ utf8::starts_with_bom +

+

+ Available in version 2.3 and later. Relaces deprecated is_bom() function. +

+

+ Checks whether an octet sequence starts with a UTF-8 byte order mark (BOM) +

+
+template <typename octet_iterator> 
+bool starts_with_bom (octet_iterator it, octet_iterator end);
+
+

+ octet_iterator: an input iterator.
+ it: beginning of the octet sequence to check
+ end: pass-end of the sequence to check
+ Return value: true if the sequence + starts with a UTF-8 byte order mark; false if not. +

+

+ Example of use: +

+
+unsigned char byte_order_mark[] = {0xef, 0xbb, 0xbf};
+bool bbom = starts_with_bom(byte_order_mark, byte_order_mark + sizeof(byte_order_mark));
+assert (bbom == true);
+
+

+ The typical use of this function is to check the first three bytes of a file. If + they form the UTF-8 BOM, we want to skip them before processing the actual UTF-8 + encoded text. +

+

+ utf8::is_bom +

+

+ Available in version 1.0 and later. Deprecated in version 2.3. starts_with_bom() should be used + instead. +

+

+ Checks whether a sequence of three octets is a UTF-8 byte order mark (BOM) +

+
+template <typename octet_iterator> 
+bool is_bom (octet_iterator it);  // Deprecated
+
+

+ octet_iterator: an input iterator.
+ it: beginning of the 3-octet sequence to check
+ Return value: true if the sequence + is UTF-8 byte order mark; false if not. +

+

+ Example of use: +

+
+unsigned char byte_order_mark[] = {0xef, 0xbb, 0xbf};
+bool bbom = is_bom(byte_order_mark);
+assert (bbom == true);
+
+

+ The typical use of this function is to check the first three bytes of a file. If + they form the UTF-8 BOM, we want to skip them before processing the actual UTF-8 + encoded text. +

+

+ If a sequence is + shorter than three bytes, an invalid iterator will be dereferenced. Therefore, this function is deprecated + in favor of starts_with_bom()that takes the end of sequence as an argument. +

+

+ Types From utf8 Namespace +

+

utf8::exception +

+

+ Available in version 2.3 and later. +

+

+ Base class for the exceptions thrown by UTF CPP library functions. +

+
+class exception : public std::exception {};
+
+

+ Example of use: +

+
+try {
+  code_that_uses_utf_cpp_library();
+}
+catch(const utf8::exception& utfcpp_ex) {
+  cerr << utfcpp_ex.what();
+}
+
+ +

utf8::invalid_code_point +

+

+ Available in version 1.0 and later. +

+

+ Thrown by UTF8 CPP functions such as advance and next if an UTF-8 sequence represents and invalid code point. +

+ +
+class invalid_code_point : public exception {
+public: 
+    uint32_t code_point() const;
+};
+
+
+

+ Member function code_point() can be used to determine the invalid code point that + caused the exception to be thrown. +

+

utf8::invalid_utf8 +

+

+ Available in version 1.0 and later. +

+

+ Thrown by UTF8 CPP functions such as next and prior if an invalid UTF-8 sequence + is detected during decoding. +

+ +
+class invalid_utf8 : public exception {
+public: 
+    uint8_t utf8_octet() const;
+};
+
+ +

+ Member function utf8_octet() can be used to determine the beginning of the byte + sequence that caused the exception to be thrown. +

+ +

utf8::invalid_utf16 +

+

+ Available in version 1.0 and later. +

+

+ Thrown by UTF8 CPP function utf16to8 if an invalid UTF-16 sequence + is detected during decoding. +

+ +
+class invalid_utf16 : public exception {
+public: 
+    uint16_t utf16_word() const;
+};
+
+ +

+ Member function utf16_word() can be used to determine the UTF-16 code unit + that caused the exception to be thrown. +

+

utf8::not_enough_room +

+

+ Available in version 1.0 and later. +

+

+ Thrown by UTF8 CPP functions such as next if the end of the decoded UTF-8 sequence + was reached before the code point was decoded. +

+ +
+class not_enough_room : public exception {};
+
+

+ utf8::iterator +

+

+ Available in version 2.0 and later. +

+

+ Adapts the underlying octet iterator to iterate over the sequence of code points, + rather than raw octets. +

+
+template <typename octet_iterator>
+class iterator;
+
+ +
Member functions
+
+
iterator();
the deafult constructor; the underlying octet_iterator is + constructed with its default constructor. +
explicit iterator (const octet_iterator& octet_it, + const octet_iterator& range_start, + const octet_iterator& range_end);
a constructor + that initializes the underlying octet_iterator with octet_it + and sets the range in which the iterator is considered valid. +
octet_iterator base () const;
returns the + underlying octet_iterator. +
uint32_t operator * () const;
decodes the utf-8 sequence + the underlying octet_iterator is pointing to and returns the code point. +
bool operator == (const iterator& rhs) + const;
returns true + if the two underlaying iterators are equal. +
bool operator != (const iterator& rhs) + const;
returns true + if the two underlaying iterators are not equal. +
iterator& operator ++ ();
the prefix increment - moves + the iterator to the next UTF-8 encoded code point. +
iterator operator ++ (int);
+ the postfix increment - moves the iterator to the next UTF-8 encoded code point and returns the current one. +
iterator& operator -- ();
the prefix decrement - moves + the iterator to the previous UTF-8 encoded code point. +
iterator operator -- (int);
+ the postfix decrement - moves the iterator to the previous UTF-8 encoded code point and returns the current one. +
+

+ Example of use: +

+
+char* threechars = "\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88";
+utf8::iterator<char*> it(threechars, threechars, threechars + 9);
+utf8::iterator<char*> it2 = it;
+assert (it2 == it);
+assert (*it == 0x10346);
+assert (*(++it) == 0x65e5);
+assert ((*it++) == 0x65e5);
+assert (*it == 0x0448);
+assert (it != it2);
+utf8::iterator<char*> endit (threechars + 9, threechars, threechars + 9);  
+assert (++it == endit);
+assert (*(--it) == 0x0448);
+assert ((*it--) == 0x0448);
+assert (*it == 0x65e5);
+assert (--it == utf8::iterator<char*>(threechars, threechars, threechars + 9));
+assert (*it == 0x10346);
+
+

+ The purpose of utf8::iterator adapter is to enable easy iteration as well as the use of STL + algorithms with UTF-8 encoded strings. Increment and decrement operators are implemented in terms of + utf8::next() and utf8::prior() functions. +

+

+ Note that utf8::iterator adapter is a checked iterator. It operates on the range specified in + the constructor; any attempt to go out of that range will result in an exception. Even the comparison operators + require both iterator object to be constructed against the same range - otherwise an exception is thrown. Typically, + the range will be determined by sequence container functions begin and end, i.e.: +

+
+std::string s = "example";
+utf8::iterator i (s.begin(), s.begin(), s.end());
+
+

+ Functions From utf8::unchecked Namespace +

+

+ utf8::unchecked::append +

+

+ Available in version 1.0 and later. +

+

+ Encodes a 32 bit code point as a UTF-8 sequence of octets and appends the sequence + to a UTF-8 string. +

+
+template <typename octet_iterator>
+octet_iterator append(uint32_t cp, octet_iterator result);
+   
+
+

+ cp: A 32 bit integer representing a code point to append to the + sequence.
+ result: An output iterator to the place in the sequence where to + append the code point.
+ Return value: An iterator pointing to the place + after the newly appended sequence. +

+

+ Example of use: +

+
+unsigned char u[5] = {0,0,0,0,0};
+unsigned char* end = unchecked::append(0x0448, u);
+assert (u[0] == 0xd1 && u[1] == 0x88 && u[2] == 0 && u[3] == 0 && u[4] == 0);
+
+

+ This is a faster but less safe version of utf8::append. It does not + check for validity of the supplied code point, and may produce an invalid UTF-8 + sequence. +

+

+ utf8::unchecked::next +

+

+ Available in version 1.0 and later. +

+

+ Given the iterator to the beginning of a UTF-8 sequence, it returns the code point + and moves the iterator to the next position. +

+
+template <typename octet_iterator>
+uint32_t next(octet_iterator& it);
+   
+
+

+ it: a reference to an iterator pointing to the beginning of an UTF-8 + encoded code point. After the function returns, it is incremented to point to the + beginning of the next code point.
+ Return value: the 32 bit representation of the + processed UTF-8 code point. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+char* w = twochars;
+int cp = unchecked::next(w);
+assert (cp == 0x65e5);
+assert (w == twochars + 3);
+
+

+ This is a faster but less safe version of utf8::next. It does not + check for validity of the supplied UTF-8 sequence. +

+

+ utf8::unchecked::peek_next +

+

+ Available in version 2.1 and later. +

+

+ Given the iterator to the beginning of a UTF-8 sequence, it returns the code point. +

+
+template <typename octet_iterator>
+uint32_t peek_next(octet_iterator it);
+   
+
+

+ it: an iterator pointing to the beginning of an UTF-8 + encoded code point.
+ Return value: the 32 bit representation of the + processed UTF-8 code point. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+char* w = twochars;
+int cp = unchecked::peek_next(w);
+assert (cp == 0x65e5);
+assert (w == twochars);
+
+

+ This is a faster but less safe version of utf8::peek_next. It does not + check for validity of the supplied UTF-8 sequence. +

+

+ utf8::unchecked::prior +

+

+ Available in version 1.02 and later. +

+

+ Given a reference to an iterator pointing to an octet in a UTF-8 seqence, it + decreases the iterator until it hits the beginning of the previous UTF-8 encoded + code point and returns the 32 bits representation of the code point. +

+
+template <typename octet_iterator>
+uint32_t prior(octet_iterator& it);
+   
+
+

+ it: a reference pointing to an octet within a UTF-8 encoded string. + After the function returns, it is decremented to point to the beginning of the + previous code point.
+ Return value: the 32 bit representation of the + previous code point. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+char* w = twochars + 3;
+int cp = unchecked::prior (w);
+assert (cp == 0x65e5);
+assert (w == twochars);
+
+

+ This is a faster but less safe version of utf8::prior. It does not + check for validity of the supplied UTF-8 sequence and offers no boundary checking. +

+

+ utf8::unchecked::previous (deprecated, see utf8::unchecked::prior) +

+

+ Deprecated in version 1.02 and later. +

+

+ Given a reference to an iterator pointing to an octet in a UTF-8 seqence, it + decreases the iterator until it hits the beginning of the previous UTF-8 encoded + code point and returns the 32 bits representation of the code point. +

+
+template <typename octet_iterator>
+uint32_t previous(octet_iterator& it);
+   
+
+

+ it: a reference pointing to an octet within a UTF-8 encoded string. + After the function returns, it is decremented to point to the beginning of the + previous code point.
+ Return value: the 32 bit representation of the + previous code point. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+char* w = twochars + 3;
+int cp = unchecked::previous (w);
+assert (cp == 0x65e5);
+assert (w == twochars);
+
+

+ The reason this function is deprecated is just the consistency with the "checked" + versions, where prior should be used instead of previous. + In fact, unchecked::previous behaves exactly the same as + unchecked::prior +

+

+ This is a faster but less safe version of utf8::previous. It does not + check for validity of the supplied UTF-8 sequence and offers no boundary checking. +

+

+ utf8::unchecked::advance +

+

+ Available in version 1.0 and later. +

+

+ Advances an iterator by the specified number of code points within an UTF-8 + sequence. +

+
+template <typename octet_iterator, typename distance_type>
+void advance (octet_iterator& it, distance_type n);
+   
+
+

+ it: a reference to an iterator pointing to the beginning of an UTF-8 + encoded code point. After the function returns, it is incremented to point to the + nth following code point.
+ n: a positive integer that shows how many code points we want to + advance.
+

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+char* w = twochars;
+unchecked::advance (w, 2);
+assert (w == twochars + 5);
+
+

+ This function works only "forward". In case of a negative n, there is + no effect. +

+

+ This is a faster but less safe version of utf8::advance. It does not + check for validity of the supplied UTF-8 sequence and offers no boundary checking. +

+

+ utf8::unchecked::distance +

+

+ Available in version 1.0 and later. +

+

+ Given the iterators to two UTF-8 encoded code points in a seqence, returns the + number of code points between them. +

+
+template <typename octet_iterator>
+typename std::iterator_traits<octet_iterator>::difference_type distance (octet_iterator first, octet_iterator last);
+
+

+ first: an iterator to a beginning of a UTF-8 encoded code point.
+ last: an iterator to a "post-end" of the last UTF-8 encoded code + point in the sequence we are trying to determine the length. It can be the + beginning of a new code point, or not.
+ Return value the distance between the iterators, + in code points. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+size_t dist = utf8::unchecked::distance(twochars, twochars + 5);
+assert (dist == 2);
+
+

+ This is a faster but less safe version of utf8::distance. It does not + check for validity of the supplied UTF-8 sequence. +

+

+ utf8::unchecked::utf16to8 +

+

+ Available in version 1.0 and later. +

+

+ Converts a UTF-16 encoded string to UTF-8. +

+
+template <typename u16bit_iterator, typename octet_iterator>
+octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result);
+   
+
+

+ start: an iterator pointing to the beginning of the UTF-16 encoded + string to convert.
+ end: an iterator pointing to pass-the-end of the UTF-16 encoded + string to convert.
+ result: an output iterator to the place in the UTF-8 string where to + append the result of conversion.
+ Return value: An iterator pointing to the place + after the appended UTF-8 string. +

+

+ Example of use: +

+
+unsigned short utf16string[] = {0x41, 0x0448, 0x65e5, 0xd834, 0xdd1e};
+vector<unsigned char> utf8result;
+unchecked::utf16to8(utf16string, utf16string + 5, back_inserter(utf8result));
+assert (utf8result.size() == 10);    
+
+

+ This is a faster but less safe version of utf8::utf16to8. It does not + check for validity of the supplied UTF-16 sequence. +

+

+ utf8::unchecked::utf8to16 +

+

+ Available in version 1.0 and later. +

+

+ Converts an UTF-8 encoded string to UTF-16 +

+
+template <typename u16bit_iterator, typename octet_iterator>
+u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result);
+   
+
+

+ start: an iterator pointing to the beginning of the UTF-8 encoded + string to convert. < br /> end: an iterator pointing to + pass-the-end of the UTF-8 encoded string to convert.
+ result: an output iterator to the place in the UTF-16 string where to + append the result of conversion.
+ Return value: An iterator pointing to the place + after the appended UTF-16 string. +

+

+ Example of use: +

+
+char utf8_with_surrogates[] = "\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e";
+vector <unsigned short> utf16result;
+unchecked::utf8to16(utf8_with_surrogates, utf8_with_surrogates + 9, back_inserter(utf16result));
+assert (utf16result.size() == 4);
+assert (utf16result[2] == 0xd834);
+assert (utf16result[3] == 0xdd1e);
+
+

+ This is a faster but less safe version of utf8::utf8to16. It does not + check for validity of the supplied UTF-8 sequence. +

+

+ utf8::unchecked::utf32to8 +

+

+ Available in version 1.0 and later. +

+

+ Converts a UTF-32 encoded string to UTF-8. +

+
+template <typename octet_iterator, typename u32bit_iterator>
+octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result);
+   
+
+

+ start: an iterator pointing to the beginning of the UTF-32 encoded + string to convert.
+ end: an iterator pointing to pass-the-end of the UTF-32 encoded + string to convert.
+ result: an output iterator to the place in the UTF-8 string where to + append the result of conversion.
+ Return value: An iterator pointing to the place + after the appended UTF-8 string. +

+

+ Example of use: +

+
+int utf32string[] = {0x448, 0x65e5, 0x10346, 0};
+vector<unsigned char> utf8result;
+utf32to8(utf32string, utf32string + 3, back_inserter(utf8result));
+assert (utf8result.size() == 9);
+
+

+ This is a faster but less safe version of utf8::utf32to8. It does not + check for validity of the supplied UTF-32 sequence. +

+

+ utf8::unchecked::utf8to32 +

+

+ Available in version 1.0 and later. +

+

+ Converts a UTF-8 encoded string to UTF-32. +

+
+template <typename octet_iterator, typename u32bit_iterator>
+u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result);
+   
+
+

+ start: an iterator pointing to the beginning of the UTF-8 encoded + string to convert.
+ end: an iterator pointing to pass-the-end of the UTF-8 encoded string + to convert.
+ result: an output iterator to the place in the UTF-32 string where to + append the result of conversion.
+ Return value: An iterator pointing to the place + after the appended UTF-32 string. +

+

+ Example of use: +

+
+char* twochars = "\xe6\x97\xa5\xd1\x88";
+vector<int> utf32result;
+unchecked::utf8to32(twochars, twochars + 5, back_inserter(utf32result));
+assert (utf32result.size() == 2);
+
+

+ This is a faster but less safe version of utf8::utf8to32. It does not + check for validity of the supplied UTF-8 sequence. +

+

+ Types From utf8::unchecked Namespace +

+

+ utf8::iterator +

+

+ Available in version 2.0 and later. +

+

+ Adapts the underlying octet iterator to iterate over the sequence of code points, + rather than raw octets. +

+
+template <typename octet_iterator>
+class iterator;
+
+ +
Member functions
+
+
iterator();
the deafult constructor; the underlying octet_iterator is + constructed with its default constructor. +
explicit iterator (const octet_iterator& octet_it); +
a constructor + that initializes the underlying octet_iterator with octet_it +
octet_iterator base () const;
returns the + underlying octet_iterator. +
uint32_t operator * () const;
decodes the utf-8 sequence + the underlying octet_iterator is pointing to and returns the code point. +
bool operator == (const iterator& rhs) + const;
returns true + if the two underlaying iterators are equal. +
bool operator != (const iterator& rhs) + const;
returns true + if the two underlaying iterators are not equal. +
iterator& operator ++ ();
the prefix increment - moves + the iterator to the next UTF-8 encoded code point. +
iterator operator ++ (int);
+ the postfix increment - moves the iterator to the next UTF-8 encoded code point and returns the current one. +
iterator& operator -- ();
the prefix decrement - moves + the iterator to the previous UTF-8 encoded code point. +
iterator operator -- (int);
+ the postfix decrement - moves the iterator to the previous UTF-8 encoded code point and returns the current one. +
+

+ Example of use: +

+
+char* threechars = "\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88";
+utf8::unchecked::iterator<char*> un_it(threechars);
+utf8::unchecked::iterator<char*> un_it2 = un_it;
+assert (un_it2 == un_it);
+assert (*un_it == 0x10346);
+assert (*(++un_it) == 0x65e5);
+assert ((*un_it++) == 0x65e5);
+assert (*un_it == 0x0448);
+assert (un_it != un_it2);
+utf8::::unchecked::iterator<char*> un_endit (threechars + 9);  
+assert (++un_it == un_endit);
+assert (*(--un_it) == 0x0448);
+assert ((*un_it--) == 0x0448);
+assert (*un_it == 0x65e5);
+assert (--un_it == utf8::unchecked::iterator<char*>(threechars));
+assert (*un_it == 0x10346);
+
+

+ This is an unchecked version of utf8::iterator. It is faster in many cases, but offers + no validity or range checks. +

+

+ Points of interest +

+

+ Design goals and decisions +

+

+ The library was designed to be: +

+
    +
  1. + Generic: for better or worse, there are many C++ string classes out there, and + the library should work with as many of them as possible. +
  2. +
  3. + Portable: the library should be portable both accross different platforms and + compilers. The only non-portable code is a small section that declares unsigned + integers of different sizes: three typedefs. They can be changed by the users of + the library if they don't match their platform. The default setting should work + for Windows (both 32 and 64 bit), and most 32 bit and 64 bit Unix derivatives. +
  4. +
  5. + Lightweight: follow the "pay only for what you use" guideline. +
  6. +
  7. + Unintrusive: avoid forcing any particular design or even programming style on the + user. This is a library, not a framework. +
  8. +
+

+ Alternatives +

+

+ In case you want to look into other means of working with UTF-8 strings from C++, + here is the list of solutions I am aware of: +

+
    +
  1. + ICU Library. It is very powerful, + complete, feature-rich, mature, and widely used. Also big, intrusive, + non-generic, and doesn't play well with the Standard Library. I definitelly + recommend looking at ICU even if you don't plan to use it. +
  2. +
  3. + C++11 language and library features. Still far from complete, and not widely + supported by compiler vendors. +
  4. +
  5. + Glib::ustring. + A class specifically made to work with UTF-8 strings, and also feel like + std::string. If you prefer to have yet another string class in your + code, it may be worth a look. Be aware of the licensing issues, though. +
  6. +
  7. + Platform dependent solutions: Windows and POSIX have functions to convert strings + from one encoding to another. That is only a subset of what my library offers, + but if that is all you need it may be good enough. +
  8. +
+ +
    +
  1. + The Unicode Consortium. +
  2. +
  3. + ICU Library. +
  4. +
  5. + UTF-8 at Wikipedia +
  6. +
  7. + UTF-8 and Unicode FAQ for + Unix/Linux +
  8. +
+ + diff --git a/thirdparty/assimp/include/assimp/.editorconfig b/thirdparty/assimp/include/assimp/.editorconfig deleted file mode 100644 index 9ea66423ad..0000000000 --- a/thirdparty/assimp/include/assimp/.editorconfig +++ /dev/null @@ -1,8 +0,0 @@ -# See for details - -[*.{h,hpp,inl}] -end_of_line = lf -insert_final_newline = true -trim_trailing_whitespace = true -indent_size = 4 -indent_style = space diff --git a/thirdparty/assimp/include/assimp/BaseImporter.h b/thirdparty/assimp/include/assimp/BaseImporter.h index 48dfc8ed8b..55f7fe3754 100644 --- a/thirdparty/assimp/include/assimp/BaseImporter.h +++ b/thirdparty/assimp/include/assimp/BaseImporter.h @@ -48,8 +48,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include +#include #include #include +#include struct aiScene; struct aiImporterDesc; @@ -80,6 +82,10 @@ class IOStream; class ASSIMP_API BaseImporter { friend class Importer; +private: + /* Pushes state into importer for the importer scale */ + virtual void UpdateImporterScale( Importer* pImp ); + public: /** Constructor to be privately used by #Importer */ @@ -132,7 +138,7 @@ public: * a suitable response to the caller. */ aiScene* ReadFile( - const Importer* pImp, + Importer* pImp, const std::string& pFile, IOSystem* pIOHandler ); @@ -161,14 +167,65 @@ public: * some loader features. Importers must provide this information. */ virtual const aiImporterDesc* GetInfo() const = 0; + /** + * Will be called only by scale process when scaling is requested. + */ + virtual void SetFileScale(double scale) + { + fileScale = scale; + } + + virtual double GetFileScale() const + { + return fileScale; + } + + enum ImporterUnits { + M, + MM, + CM, + INCHES, + FEET + }; + + /** + * Assimp Importer + * unit conversions available + * if you need another measurment unit add it below. + * it's currently defined in assimp that we prefer meters. + * */ + std::map importerUnits = { + {ImporterUnits::M, 1}, + {ImporterUnits::CM, 0.01}, + {ImporterUnits::MM, 0.001}, + {ImporterUnits::INCHES, 0.0254}, + {ImporterUnits::FEET, 0.3048} + }; + + 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 * #GetInfo and insert all file extensions into the given set. * @param extension set to collect file extensions in*/ void GetExtensionList(std::set& extensions); + +protected: + ImporterUnits applicationUnits = ImporterUnits::M; + double importerScale = 1.0; + double fileScale = 1.0; + -protected: // ------------------------------------------------------------------- /** Imports the given file into the given scene structure. The diff --git a/thirdparty/assimp/include/assimp/config.h.in b/thirdparty/assimp/include/assimp/config.h.in index d08b929a10..3a6379bf40 100644 --- a/thirdparty/assimp/include/assimp/config.h.in +++ b/thirdparty/assimp/include/assimp/config.h.in @@ -142,7 +142,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** @brief Specifies the maximum angle that may be between two vertex tangents * that their tangents and bi-tangents are smoothed. * - * This applies to the CalcTangentSpace-Step. TFvhe angle is specified + * This applies to the CalcTangentSpace-Step. The angle is specified * in degrees. The maximum value is 175. * Property type: float. Default value: 45 degrees */ @@ -999,6 +999,13 @@ enum aiComponent # define AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT 1.0f #endif // !! AI_DEBONE_THRESHOLD +#define AI_CONFIG_APP_SCALE_KEY "APP_SCALE_FACTOR" + +#if (!defined AI_CONFIG_APP_SCALE_KEY) +# define AI_CONFIG_APP_SCALE_KEY 1.0 +#endif // AI_CONFIG_APP_SCALE_KEY + + // ---------- All the Build/Compile-time defines ------------ /** @brief Specifies if double precision is supported inside assimp diff --git a/thirdparty/assimp/include/assimp/irrXMLWrapper.h b/thirdparty/assimp/include/assimp/irrXMLWrapper.h deleted file mode 100644 index ec8ee7c76e..0000000000 --- a/thirdparty/assimp/include/assimp/irrXMLWrapper.h +++ /dev/null @@ -1,144 +0,0 @@ -/* -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. - ----------------------------------------------------------------------- -*/ - -#ifndef INCLUDED_AI_IRRXML_WRAPPER -#define INCLUDED_AI_IRRXML_WRAPPER - -// some long includes .... -#include -#include "IOStream.hpp" -#include "BaseImporter.h" -#include - -namespace Assimp { - -// --------------------------------------------------------------------------------- -/** @brief Utility class to make IrrXML work together with our custom IO system - * See the IrrXML docs for more details. - * - * Construct IrrXML-Reader in BaseImporter::InternReadFile(): - * @code - * // open the file - * std::unique_ptr file( pIOHandler->Open( pFile)); - * if( file.get() == NULL) { - * throw DeadlyImportError( "Failed to open file " + pFile + "."); - * } - * - * // generate a XML reader for it - * std::unique_ptr mIOWrapper( new CIrrXML_IOStreamReader( file.get())); - * mReader = irr::io::createIrrXMLReader( mIOWrapper.get()); - * if( !mReader) { - * ThrowException( "xxxx: Unable to open file."); - * } - * @endcode - **/ -class CIrrXML_IOStreamReader : public irr::io::IFileReadCallBack { -public: - - // ---------------------------------------------------------------------------------- - //! Construction from an existing IOStream - explicit CIrrXML_IOStreamReader(IOStream* _stream) - : stream (_stream) - , t (0) - { - - // Map the buffer into memory and convert it to UTF8. IrrXML provides its - // own conversion, which is merely a cast from uintNN_t to uint8_t. Thus, - // it is not suitable for our purposes and we have to do it BEFORE IrrXML - // gets the buffer. Sadly, this forces us to map the whole file into - // memory. - - data.resize(stream->FileSize()); - stream->Read(&data[0],data.size(),1); - - // Remove null characters from the input sequence otherwise the parsing will utterly fail - unsigned int size = 0; - unsigned int size_max = static_cast(data.size()); - for(unsigned int i = 0; i < size_max; i++) { - if(data[i] != '\0') { - data[size++] = data[i]; - } - } - data.resize(size); - - BaseImporter::ConvertToUTF8(data); - } - - // ---------------------------------------------------------------------------------- - //! Virtual destructor - virtual ~CIrrXML_IOStreamReader() {} - - // ---------------------------------------------------------------------------------- - //! Reads an amount of bytes from the file. - /** @param buffer: Pointer to output buffer. - * @param sizeToRead: Amount of bytes to read - * @return Returns how much bytes were read. */ - virtual int read(void* buffer, int sizeToRead) { - if(sizeToRead<0) { - return 0; - } - if(t+sizeToRead>data.size()) { - sizeToRead = static_cast(data.size()-t); - } - - memcpy(buffer,&data.front()+t,sizeToRead); - - t += sizeToRead; - return sizeToRead; - } - - // ---------------------------------------------------------------------------------- - //! Returns size of file in bytes - virtual int getSize() { - return (int)data.size(); - } - -private: - IOStream* stream; - std::vector data; - size_t t; - -}; // ! class CIrrXML_IOStreamReader - -} // ! Assimp - -#endif // !! INCLUDED_AI_IRRXML_WRAPPER diff --git a/thirdparty/assimp/include/assimp/scene.h b/thirdparty/assimp/include/assimp/scene.h index df5d6f3b5e..2667db85b3 100644 --- a/thirdparty/assimp/include/assimp/scene.h +++ b/thirdparty/assimp/include/assimp/scene.h @@ -56,9 +56,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "material.h" #include "anim.h" #include "metadata.h" -#include #ifdef __cplusplus +# include extern "C" { #endif -- cgit v1.2.3