Browse Source

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 <[email protected]>
Gordon MacPherson 6 years ago
parent
commit
ad214c0356

File diff suppressed because it is too large
+ 86 - 754
modules/assimp/editor_scene_importer_assimp.cpp


+ 33 - 114
modules/assimp/editor_scene_importer_assimp.h

@@ -44,60 +44,31 @@
 #include "scene/resources/animation.h"
 #include "scene/resources/animation.h"
 #include "scene/resources/surface_tool.h"
 #include "scene/resources/surface_tool.h"
 
 
-#include "assimp/DefaultLogger.hpp"
-#include "assimp/LogStream.hpp"
-#include "assimp/Logger.hpp"
-#include "assimp/matrix4x4.h"
-#include "assimp/scene.h"
-#include "assimp/types.h"
+#include <assimp/matrix4x4.h>
+#include <assimp/scene.h>
+#include <assimp/types.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/LogStream.hpp>
+#include <assimp/Logger.hpp>
+
+#include "import_state.h"
+#include "import_utils.h"
+
+using namespace AssimpImporter;
 
 
 class AssimpStream : public Assimp::LogStream {
 class AssimpStream : public Assimp::LogStream {
 public:
 public:
 	// Constructor
 	// Constructor
-	AssimpStream();
+	AssimpStream() {}
 
 
 	// Destructor
 	// Destructor
-	~AssimpStream();
+	~AssimpStream() {}
 	// Write something using your own functionality
 	// Write something using your own functionality
-	void write(const char *message);
+	void write(const char *message) {
+		print_verbose(String("Open Asset Import: ") + String(message).strip_edges());
+	}
 };
 };
 
 
-#define AI_MATKEY_FBX_MAYA_BASE_COLOR_FACTOR "$raw.Maya|baseColor", 0, 0
-#define AI_MATKEY_FBX_MAYA_METALNESS_FACTOR "$raw.Maya|metalness", 0, 0
-#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_FACTOR "$raw.Maya|diffuseRoughness", 0, 0
-
-#define AI_MATKEY_FBX_MAYA_METALNESS_TEXTURE "$raw.Maya|metalness|file", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_METALNESS_UV_XFORM "$raw.Maya|metalness|uvtrafo", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_TEXTURE "$raw.Maya|diffuseRoughness|file", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_UV_XFORM "$raw.Maya|diffuseRoughness|uvtrafo", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_BASE_COLOR_TEXTURE "$raw.Maya|baseColor|file", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_BASE_COLOR_UV_XFORM "$raw.Maya|baseColor|uvtrafo", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_NORMAL_TEXTURE "$raw.Maya|normalCamera|file", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_NORMAL_UV_XFORM "$raw.Maya|normalCamera|uvtrafo", aiTextureType_UNKNOWN, 0
-
-#define AI_MATKEY_FBX_NORMAL_TEXTURE "$raw.Maya|normalCamera|file", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_NORMAL_UV_XFORM "$raw.Maya|normalCamera|uvtrafo", aiTextureType_UNKNOWN, 0
-
-#define AI_MATKEY_FBX_MAYA_STINGRAY_DISPLACEMENT_SCALING_FACTOR "$raw.Maya|displacementscaling", 0, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_BASE_COLOR_FACTOR "$raw.Maya|base_color", 0, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_FACTOR "$raw.Maya|emissive", 0, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_FACTOR "$raw.Maya|metallic", 0, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_FACTOR "$raw.Maya|roughness", 0, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_INTENSITY_FACTOR "$raw.Maya|emissive_intensity", 0, 0
-
-#define AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_TEXTURE "$raw.Maya|TEX_normal_map|file", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_UV_XFORM "$raw.Maya|TEX_normal_map|uvtrafo", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_TEXTURE "$raw.Maya|TEX_color_map|file", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_UV_XFORM "$raw.Maya|TEX_color_map|uvtrafo", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_TEXTURE "$raw.Maya|TEX_metallic_map|file", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_UV_XFORM "$raw.Maya|TEX_metallic_map|uvtrafo", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_TEXTURE "$raw.Maya|TEX_roughness_map|file", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_UV_XFORM "$raw.Maya|TEX_roughness_map|uvtrafo", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_TEXTURE "$raw.Maya|TEX_emissive_map|file", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_UV_XFORM "$raw.Maya|TEX_emissive_map|uvtrafo", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_AO_TEXTURE "$raw.Maya|TEX_ao_map|file", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_AO_UV_XFORM "$raw.Maya|TEX_ao_map|uvtrafo", aiTextureType_UNKNOWN, 0
-
 class EditorSceneImporterAssimp : public EditorSceneImporter {
 class EditorSceneImporterAssimp : public EditorSceneImporter {
 private:
 private:
 	GDCLASS(EditorSceneImporterAssimp, EditorSceneImporter);
 	GDCLASS(EditorSceneImporterAssimp, EditorSceneImporter);
@@ -112,59 +83,6 @@ private:
 		};
 		};
 	};
 	};
 
 
-	struct AssetImportFbx {
-		enum ETimeMode {
-			TIME_MODE_DEFAULT = 0,
-			TIME_MODE_120 = 1,
-			TIME_MODE_100 = 2,
-			TIME_MODE_60 = 3,
-			TIME_MODE_50 = 4,
-			TIME_MODE_48 = 5,
-			TIME_MODE_30 = 6,
-			TIME_MODE_30_DROP = 7,
-			TIME_MODE_NTSC_DROP_FRAME = 8,
-			TIME_MODE_NTSC_FULL_FRAME = 9,
-			TIME_MODE_PAL = 10,
-			TIME_MODE_CINEMA = 11,
-			TIME_MODE_1000 = 12,
-			TIME_MODE_CINEMA_ND = 13,
-			TIME_MODE_CUSTOM = 14,
-			TIME_MODE_TIME_MODE_COUNT = 15
-		};
-		enum UpAxis {
-			UP_VECTOR_AXIS_X = 1,
-			UP_VECTOR_AXIS_Y = 2,
-			UP_VECTOR_AXIS_Z = 3
-		};
-		enum FrontAxis {
-			FRONT_PARITY_EVEN = 1,
-			FRONT_PARITY_ODD = 2,
-		};
-
-		enum CoordAxis {
-			COORD_RIGHT = 0,
-			COORD_LEFT = 1
-		};
-	};
-
-	struct ImportState {
-
-		String path;
-		const aiScene *assimp_scene;
-		uint32_t max_bone_weights;
-		Spatial *root;
-		Map<String, Ref<Mesh> > mesh_cache;
-		Map<int, Ref<Material> > material_cache;
-		Map<String, int> light_cache;
-		Map<String, int> camera_cache;
-		Vector<Skeleton *> skeletons;
-		Map<String, int> bone_owners; //maps bones to skeleton index owned by
-		Map<String, Node *> node_map;
-		Map<MeshInstance *, Skeleton *> mesh_skeletons;
-		bool fbx; //for some reason assimp does some things different for FBX
-		AnimationPlayer *animation_player;
-	};
-
 	struct BoneInfo {
 	struct BoneInfo {
 		uint32_t bone;
 		uint32_t bone;
 		float weight;
 		float weight;
@@ -177,28 +95,29 @@ private:
 		const aiNode *node;
 		const aiNode *node;
 	};
 	};
 
 
-	const Transform _assimp_matrix_transform(const aiMatrix4x4 p_matrix);
-	String _assimp_get_string(const aiString &p_string) const;
-	Transform _get_global_assimp_node_transform(const aiNode *p_current_node);
-
 	void _calc_tangent_from_mesh(const aiMesh *ai_mesh, int i, int tri_index, int index, PoolColorArray::Write &w);
 	void _calc_tangent_from_mesh(const aiMesh *ai_mesh, int i, int tri_index, int index, PoolColorArray::Write &w);
 	void _set_texture_mapping_mode(aiTextureMapMode *map_mode, Ref<Texture> texture);
 	void _set_texture_mapping_mode(aiTextureMapMode *map_mode, Ref<Texture> texture);
-	void _find_texture_path(const String &p_path, String &path, bool &r_found);
-	void _find_texture_path(const String &p_path, _Directory &dir, String &path, bool &found, String extension);
-
-	Ref<Texture> _load_texture(ImportState &state, String p_path);
-	Ref<Material> _generate_material_from_index(ImportState &state, int p_index, bool p_double_sided);
-	Ref<Mesh> _generate_mesh_from_surface_indices(ImportState &state, const Vector<int> &p_surface_indices, Skeleton *p_skeleton = NULL, bool p_double_sided_material = false);
-	void _generate_node(ImportState &state, const aiNode *p_assimp_node, Node *p_parent);
-	void _generate_bone_groups(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<String, Transform> &bind_xforms);
-	void _fill_node_relationships(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<int, int> &skeleton_map, int p_skeleton_id, Skeleton *p_skeleton, const String &p_parent_name, int &holecount, const Vector<SkeletonHole> &p_holes, const Map<String, Transform> &bind_xforms);
-	void _generate_skeletons(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<int, int> &skeleton_map, const Map<String, Transform> &bind_xforms);
 
 
+	Ref<Mesh> _generate_mesh_from_surface_indices(ImportState &state, const Vector<int> &p_surface_indices, const aiNode *assimp_node, Skeleton *p_skeleton = NULL);
+
+	// utility for node creation
+	void attach_new_node(ImportState &state, Spatial *new_node, const aiNode *node, Node *parent_node, String Name, Transform &transform);
+	// simple object creation functions
+	void create_light(ImportState &state, RecursiveState &recursive_state);
+	void create_camera(ImportState &state, RecursiveState &recursive_state);
+	void create_bone(ImportState &state, RecursiveState &recursive_state);
+	// non recursive - linear so must not use recursive arguments
+	void create_mesh(ImportState &state, const aiNode *assimp_node, const String &node_name, Node *current_node, Node *parent_node, Transform node_transform);
+
+	// recursive node generator
+	void _generate_node(ImportState &state, Skeleton *skeleton, const aiNode *assimp_node, Node *parent_node);
+	// runs after _generate_node as it must then use pre-created godot skeleton.
+	void generate_mesh_phase_from_skeletal_mesh(ImportState &state);
 	void _insert_animation_track(ImportState &scene, const aiAnimation *assimp_anim, int p_track, int p_bake_fps, Ref<Animation> animation, float ticks_per_second, Skeleton *p_skeleton, const NodePath &p_path, const String &p_name);
 	void _insert_animation_track(ImportState &scene, const aiAnimation *assimp_anim, int p_track, int p_bake_fps, Ref<Animation> animation, float ticks_per_second, Skeleton *p_skeleton, const NodePath &p_path, const String &p_name);
 
 
 	void _import_animation(ImportState &state, int p_animation_index, int p_bake_fps);
 	void _import_animation(ImportState &state, int p_animation_index, int p_bake_fps);
 
 
-	Spatial *_generate_scene(const String &p_path, const aiScene *scene, const uint32_t p_flags, int p_bake_fps, const int32_t p_max_bone_weights);
+	Spatial *_generate_scene(const String &p_path, aiScene *scene, const uint32_t p_flags, int p_bake_fps, const int32_t p_max_bone_weights);
 
 
 	String _assimp_anim_string_to_string(const aiString &p_string) const;
 	String _assimp_anim_string_to_string(const aiString &p_string) const;
 	String _assimp_raw_string_to_string(const aiString &p_string) const;
 	String _assimp_raw_string_to_string(const aiString &p_string) const;
@@ -228,7 +147,7 @@ public:
 	virtual void get_extensions(List<String> *r_extensions) const;
 	virtual void get_extensions(List<String> *r_extensions) const;
 	virtual uint32_t get_import_flags() const;
 	virtual uint32_t get_import_flags() const;
 	virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = NULL);
 	virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = NULL);
-	virtual Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps);
+	Ref<Image> load_image(ImportState &state, const aiScene *p_scene, String p_path);
 };
 };
 #endif
 #endif
 #endif
 #endif

+ 115 - 0
modules/assimp/import_state.h

@@ -0,0 +1,115 @@
+/*************************************************************************/
+/*  import_state.h                                                       */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* 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.                */
+/*************************************************************************/
+
+#ifndef EDITOR_SCENE_IMPORT_STATE_H
+#define EDITOR_SCENE_IMPORT_STATE_H
+
+#include "core/bind/core_bind.h"
+#include "core/io/resource_importer.h"
+#include "core/vector.h"
+#include "editor/import/resource_importer_scene.h"
+#include "editor/project_settings_editor.h"
+#include "scene/3d/mesh_instance.h"
+#include "scene/3d/skeleton.h"
+#include "scene/3d/spatial.h"
+#include "scene/animation/animation_player.h"
+#include "scene/resources/animation.h"
+#include "scene/resources/surface_tool.h"
+
+#include <assimp/matrix4x4.h>
+#include <assimp/scene.h>
+#include <assimp/types.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/LogStream.hpp>
+#include <assimp/Logger.hpp>
+
+namespace AssimpImporter {
+/** Import state is for global scene import data
+	 * This makes the code simpler and contains useful lookups.
+	 */
+struct ImportState {
+
+	String path;
+	const aiScene *assimp_scene;
+	uint32_t max_bone_weights;
+
+	Spatial *root;
+	Map<String, Ref<Mesh> > mesh_cache;
+	Map<int, Ref<Material> > material_cache;
+	Map<String, int> light_cache;
+	Map<String, int> camera_cache;
+	//Vector<Skeleton *> skeletons;
+	Map<Skeleton *, const Spatial *> armature_skeletons; // maps skeletons based on their armature nodes.
+	Map<const aiBone *, Skeleton *> bone_to_skeleton_lookup; // maps bones back into their skeleton
+	// very useful for when you need to ask assimp for the bone mesh
+	Map<String, Node *> node_map;
+	Map<const aiNode *, const Node *> assimp_node_map;
+	Map<String, Ref<Image> > path_to_image_cache;
+	bool fbx; //for some reason assimp does some things different for FBX
+	AnimationPlayer *animation_player;
+};
+
+struct AssimpImageData {
+	Ref<Image> raw_image;
+	Ref<ImageTexture> texture;
+	aiTextureMapMode *map_mode = NULL;
+};
+
+/** Recursive state is used to push state into functions instead of specifying them
+	* This makes the code easier to handle too and add extra arguments without breaking things
+	*/
+struct RecursiveState {
+	RecursiveState(
+			Transform &_node_transform,
+			Skeleton *_skeleton,
+			Spatial *_new_node,
+			const String &_node_name,
+			const aiNode *_assimp_node,
+			Node *_parent_node,
+			const aiBone *_bone) :
+			node_transform(_node_transform),
+			skeleton(_skeleton),
+			new_node(_new_node),
+			node_name(_node_name),
+			assimp_node(_assimp_node),
+			parent_node(_parent_node),
+			bone(_bone) {}
+
+	Transform &node_transform;
+	Skeleton *skeleton;
+	Spatial *new_node;
+	const String &node_name;
+	const aiNode *assimp_node;
+	Node *parent_node;
+	const aiBone *bone;
+};
+} // namespace AssimpImporter
+
+#endif // EDITOR_SCENE_IMPORT_STATE_H

+ 448 - 0
modules/assimp/import_utils.h

@@ -0,0 +1,448 @@
+/*************************************************************************/
+/*  import_utils.h                                                       */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md)    */
+/*                                                                       */
+/* 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.                */
+/*************************************************************************/
+
+#ifndef IMPORT_UTILS_IMPORTER_ASSIMP_H
+#define IMPORT_UTILS_IMPORTER_ASSIMP_H
+
+#include "core/io/image_loader.h"
+#include "import_state.h"
+
+#include <assimp/SceneCombiner.h>
+#include <assimp/cexport.h>
+#include <assimp/cimport.h>
+#include <assimp/matrix4x4.h>
+#include <assimp/pbrmaterial.h>
+#include <assimp/postprocess.h>
+#include <assimp/scene.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/Importer.hpp>
+#include <assimp/LogStream.hpp>
+#include <assimp/Logger.hpp>
+#include <string>
+
+using namespace AssimpImporter;
+
+#define AI_PROPERTIES aiTextureType_UNKNOWN, 0
+#define AI_NULL 0, 0
+#define AI_MATKEY_FBX_MAYA_BASE_COLOR_FACTOR "$raw.Maya|baseColor"
+#define AI_MATKEY_FBX_MAYA_METALNESS_FACTOR "$raw.Maya|metalness"
+#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_FACTOR "$raw.Maya|diffuseRoughness"
+
+#define AI_MATKEY_FBX_MAYA_EMISSION_TEXTURE "$raw.Maya|emissionColor|file"
+#define AI_MATKEY_FBX_MAYA_EMISSIVE_FACTOR "$raw.Maya|emission"
+#define AI_MATKEY_FBX_MAYA_METALNESS_TEXTURE "$raw.Maya|metalness|file"
+#define AI_MATKEY_FBX_MAYA_METALNESS_UV_XFORM "$raw.Maya|metalness|uvtrafo"
+#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_TEXTURE "$raw.Maya|diffuseRoughness|file"
+#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_UV_XFORM "$raw.Maya|diffuseRoughness|uvtrafo"
+#define AI_MATKEY_FBX_MAYA_BASE_COLOR_TEXTURE "$raw.Maya|baseColor|file"
+#define AI_MATKEY_FBX_MAYA_BASE_COLOR_UV_XFORM "$raw.Maya|baseColor|uvtrafo"
+#define AI_MATKEY_FBX_MAYA_NORMAL_TEXTURE "$raw.Maya|normalCamera|file"
+#define AI_MATKEY_FBX_MAYA_NORMAL_UV_XFORM "$raw.Maya|normalCamera|uvtrafo"
+
+#define AI_MATKEY_FBX_NORMAL_TEXTURE "$raw.Maya|normalCamera|file"
+#define AI_MATKEY_FBX_NORMAL_UV_XFORM "$raw.Maya|normalCamera|uvtrafo"
+
+#define AI_MATKEY_FBX_MAYA_STINGRAY_DISPLACEMENT_SCALING_FACTOR "$raw.Maya|displacementscaling"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_BASE_COLOR_FACTOR "$raw.Maya|base_color"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_FACTOR "$raw.Maya|emissive"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_FACTOR "$raw.Maya|metallic"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_FACTOR "$raw.Maya|roughness"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_INTENSITY_FACTOR "$raw.Maya|emissive_intensity"
+
+#define AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_TEXTURE "$raw.Maya|TEX_normal_map|file"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_UV_XFORM "$raw.Maya|TEX_normal_map|uvtrafo"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_TEXTURE "$raw.Maya|TEX_color_map|file"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_UV_XFORM "$raw.Maya|TEX_color_map|uvtrafo"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_TEXTURE "$raw.Maya|TEX_metallic_map|file"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_UV_XFORM "$raw.Maya|TEX_metallic_map|uvtrafo"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_TEXTURE "$raw.Maya|TEX_roughness_map|file"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_UV_XFORM "$raw.Maya|TEX_roughness_map|uvtrafo"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_TEXTURE "$raw.Maya|TEX_emissive_map|file"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_UV_XFORM "$raw.Maya|TEX_emissive_map|uvtrafo"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_AO_TEXTURE "$raw.Maya|TEX_ao_map|file"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_AO_UV_XFORM "$raw.Maya|TEX_ao_map|uvtrafo"
+
+/**
+ * Assimp Utils
+ * Conversion tools / glue code to convert from assimp to godot
+*/
+class AssimpUtils {
+public:
+	/**
+	 * calculate tangents for mesh data from assimp data
+	 */
+	static void calc_tangent_from_mesh(const aiMesh *ai_mesh, int i, int tri_index, int index, PoolColorArray::Write &w) {
+		const aiVector3D normals = ai_mesh->mAnimMeshes[i]->mNormals[tri_index];
+		const Vector3 godot_normal = Vector3(normals.x, normals.y, normals.z);
+		const aiVector3D tangent = ai_mesh->mAnimMeshes[i]->mTangents[tri_index];
+		const Vector3 godot_tangent = Vector3(tangent.x, tangent.y, tangent.z);
+		const aiVector3D bitangent = ai_mesh->mAnimMeshes[i]->mBitangents[tri_index];
+		const Vector3 godot_bitangent = Vector3(bitangent.x, bitangent.y, bitangent.z);
+		float d = godot_normal.cross(godot_tangent).dot(godot_bitangent) > 0.0f ? 1.0f : -1.0f;
+		Color plane_tangent = Color(tangent.x, tangent.y, tangent.z, d);
+		w[index] = plane_tangent;
+	}
+
+	struct AssetImportFbx {
+		enum ETimeMode {
+			TIME_MODE_DEFAULT = 0,
+			TIME_MODE_120 = 1,
+			TIME_MODE_100 = 2,
+			TIME_MODE_60 = 3,
+			TIME_MODE_50 = 4,
+			TIME_MODE_48 = 5,
+			TIME_MODE_30 = 6,
+			TIME_MODE_30_DROP = 7,
+			TIME_MODE_NTSC_DROP_FRAME = 8,
+			TIME_MODE_NTSC_FULL_FRAME = 9,
+			TIME_MODE_PAL = 10,
+			TIME_MODE_CINEMA = 11,
+			TIME_MODE_1000 = 12,
+			TIME_MODE_CINEMA_ND = 13,
+			TIME_MODE_CUSTOM = 14,
+			TIME_MODE_TIME_MODE_COUNT = 15
+		};
+		enum UpAxis {
+			UP_VECTOR_AXIS_X = 1,
+			UP_VECTOR_AXIS_Y = 2,
+			UP_VECTOR_AXIS_Z = 3
+		};
+		enum FrontAxis {
+			FRONT_PARITY_EVEN = 1,
+			FRONT_PARITY_ODD = 2,
+		};
+
+		enum CoordAxis {
+			COORD_RIGHT = 0,
+			COORD_LEFT = 1
+		};
+	};
+
+	/** Get assimp string
+    * automatically filters the string data
+    */
+	static String get_assimp_string(const aiString &p_string) {
+		//convert an assimp String to a Godot String
+		String name;
+		name.parse_utf8(p_string.C_Str() /*,p_string.length*/);
+		if (name.find(":") != -1) {
+			String replaced_name = name.split(":")[1];
+			print_verbose("Replacing " + name + " containing : with " + replaced_name);
+			name = replaced_name;
+		}
+
+		return name;
+	}
+
+	static String get_anim_string_from_assimp(const aiString &p_string) {
+
+		String name;
+		name.parse_utf8(p_string.C_Str() /*,p_string.length*/);
+		if (name.find(":") != -1) {
+			String replaced_name = name.split(":")[1];
+			print_verbose("Replacing " + name + " containing : with " + replaced_name);
+			name = replaced_name;
+		}
+		return name;
+	}
+
+	/**
+     * No filter logic get_raw_string_from_assimp
+     * This just convers the aiString to a parsed utf8 string
+     * Without removing special chars etc
+     */
+	static String get_raw_string_from_assimp(const aiString &p_string) {
+		String name;
+		name.parse_utf8(p_string.C_Str() /*,p_string.length*/);
+		return name;
+	}
+
+	static Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) {
+		return Ref<Animation>();
+	}
+
+	/**
+     * Converts aiMatrix4x4 to godot Transform 
+    */
+	static const Transform assimp_matrix_transform(const aiMatrix4x4 p_matrix) {
+		aiMatrix4x4 matrix = p_matrix;
+		Transform xform;
+		xform.set(matrix.a1, matrix.a2, matrix.a3, matrix.b1, matrix.b2, matrix.b3, matrix.c1, matrix.c2, matrix.c3, matrix.a4, matrix.b4, matrix.c4);
+		return xform;
+	}
+
+	/** Get fbx fps for time mode meta data
+     */
+	static float get_fbx_fps(int32_t time_mode, const aiScene *p_scene) {
+		switch (time_mode) {
+			case AssetImportFbx::TIME_MODE_DEFAULT: return 24; //hack
+			case AssetImportFbx::TIME_MODE_120: return 120;
+			case AssetImportFbx::TIME_MODE_100: return 100;
+			case AssetImportFbx::TIME_MODE_60: return 60;
+			case AssetImportFbx::TIME_MODE_50: return 50;
+			case AssetImportFbx::TIME_MODE_48: return 48;
+			case AssetImportFbx::TIME_MODE_30: return 30;
+			case AssetImportFbx::TIME_MODE_30_DROP: return 30;
+			case AssetImportFbx::TIME_MODE_NTSC_DROP_FRAME: return 29.9700262f;
+			case AssetImportFbx::TIME_MODE_NTSC_FULL_FRAME: return 29.9700262f;
+			case AssetImportFbx::TIME_MODE_PAL: return 25;
+			case AssetImportFbx::TIME_MODE_CINEMA: return 24;
+			case AssetImportFbx::TIME_MODE_1000: return 1000;
+			case AssetImportFbx::TIME_MODE_CINEMA_ND: return 23.976f;
+			case AssetImportFbx::TIME_MODE_CUSTOM:
+				int32_t frame_rate = -1;
+				p_scene->mMetaData->Get("FrameRate", frame_rate);
+				return frame_rate;
+		}
+		return 0;
+	}
+
+	/**
+      * Get global transform for the current node - so we can use world space rather than
+      * local space coordinates
+      * useful if you need global - although recommend using local wherever possible over global
+      * as you could break fbx scaling :)
+      */
+	static Transform _get_global_assimp_node_transform(const aiNode *p_current_node) {
+		aiNode const *current_node = p_current_node;
+		Transform xform;
+		while (current_node != NULL) {
+			xform = assimp_matrix_transform(current_node->mTransformation) * xform;
+			current_node = current_node->mParent;
+		}
+		return xform;
+	}
+
+	/**
+	  * Find hardcoded textures from assimp which could be in many different directories
+	  */
+	static void find_texture_path(const String &p_path, _Directory &dir, String &path, bool &found, String extension) {
+		Vector<String> paths;
+		paths.push_back(path.get_basename() + extension);
+		paths.push_back(path + extension);
+		paths.push_back(path);
+		paths.push_back(p_path.get_base_dir().plus_file(path.get_file().get_basename() + extension));
+		paths.push_back(p_path.get_base_dir().plus_file(path.get_file() + extension));
+		paths.push_back(p_path.get_base_dir().plus_file(path.get_file()));
+		paths.push_back(p_path.get_base_dir().plus_file("textures/" + path.get_file().get_basename() + extension));
+		paths.push_back(p_path.get_base_dir().plus_file("textures/" + path.get_file() + extension));
+		paths.push_back(p_path.get_base_dir().plus_file("textures/" + path.get_file()));
+		paths.push_back(p_path.get_base_dir().plus_file("Textures/" + path.get_file().get_basename() + extension));
+		paths.push_back(p_path.get_base_dir().plus_file("Textures/" + path.get_file() + extension));
+		paths.push_back(p_path.get_base_dir().plus_file("Textures/" + path.get_file()));
+		paths.push_back(p_path.get_base_dir().plus_file("../Textures/" + path.get_file() + extension));
+		paths.push_back(p_path.get_base_dir().plus_file("../Textures/" + path.get_file().get_basename() + extension));
+		paths.push_back(p_path.get_base_dir().plus_file("../Textures/" + path.get_file()));
+		paths.push_back(p_path.get_base_dir().plus_file("../textures/" + path.get_file().get_basename() + extension));
+		paths.push_back(p_path.get_base_dir().plus_file("../textures/" + path.get_file() + extension));
+		paths.push_back(p_path.get_base_dir().plus_file("../textures/" + path.get_file()));
+		paths.push_back(p_path.get_base_dir().plus_file("texture/" + path.get_file().get_basename() + extension));
+		paths.push_back(p_path.get_base_dir().plus_file("texture/" + path.get_file() + extension));
+		paths.push_back(p_path.get_base_dir().plus_file("texture/" + path.get_file()));
+		paths.push_back(p_path.get_base_dir().plus_file("Texture/" + path.get_file().get_basename() + extension));
+		paths.push_back(p_path.get_base_dir().plus_file("Texture/" + path.get_file() + extension));
+		paths.push_back(p_path.get_base_dir().plus_file("Texture/" + path.get_file()));
+		paths.push_back(p_path.get_base_dir().plus_file("../Texture/" + path.get_file() + extension));
+		paths.push_back(p_path.get_base_dir().plus_file("../Texture/" + path.get_file().get_basename() + extension));
+		paths.push_back(p_path.get_base_dir().plus_file("../Texture/" + path.get_file()));
+		paths.push_back(p_path.get_base_dir().plus_file("../texture/" + path.get_file().get_basename() + extension));
+		paths.push_back(p_path.get_base_dir().plus_file("../texture/" + path.get_file() + extension));
+		paths.push_back(p_path.get_base_dir().plus_file("../texture/" + path.get_file()));
+		for (int i = 0; i < paths.size(); i++) {
+			if (dir.file_exists(paths[i])) {
+				found = true;
+				path = paths[i];
+				return;
+			}
+		}
+	}
+
+	/** find the texture path for the supplied fbx path inside godot
+      * very simple lookup for subfolders etc for a texture which may or may not be in a directory
+      */
+	static void find_texture_path(const String &r_p_path, String &r_path, bool &r_found) {
+		_Directory dir;
+
+		List<String> exts;
+		ImageLoader::get_recognized_extensions(&exts);
+
+		Vector<String> split_path = r_path.get_basename().split("*");
+		if (split_path.size() == 2) {
+			r_found = true;
+			return;
+		}
+
+		if (dir.file_exists(r_p_path.get_base_dir() + r_path.get_file())) {
+			r_path = r_p_path.get_base_dir() + r_path.get_file();
+			r_found = true;
+			return;
+		}
+
+		for (int32_t i = 0; i < exts.size(); i++) {
+			if (r_found) {
+				return;
+			}
+			if (r_found == false) {
+				find_texture_path(r_p_path, dir, r_path, r_found, "." + exts[i]);
+			}
+		}
+	}
+
+	/**
+	  * set_texture_mapping_mode
+	  * Helper to check the mapping mode of the texture (repeat, clamp and mirror)
+	  */
+	static void set_texture_mapping_mode(aiTextureMapMode *map_mode, Ref<ImageTexture> texture) {
+		ERR_FAIL_COND(texture.is_null());
+		ERR_FAIL_COND(map_mode == NULL);
+		aiTextureMapMode tex_mode = aiTextureMapMode::aiTextureMapMode_Wrap;
+
+		tex_mode = map_mode[0];
+
+		int32_t flags = Texture::FLAGS_DEFAULT;
+		if (tex_mode == aiTextureMapMode_Wrap) {
+			//Default
+		} else if (tex_mode == aiTextureMapMode_Clamp) {
+			flags = flags & ~Texture::FLAG_REPEAT;
+		} else if (tex_mode == aiTextureMapMode_Mirror) {
+			flags = flags | Texture::FLAG_MIRRORED_REPEAT;
+		}
+		texture->set_flags(flags);
+	}
+
+	/**
+	  * Load or load from cache image :)
+	  */
+	static Ref<Image> load_image(ImportState &state, const aiScene *p_scene, String p_path) {
+
+		Map<String, Ref<Image> >::Element *match = state.path_to_image_cache.find(p_path);
+
+		// if our cache contains this image then don't bother
+		if (match) {
+			return match->get();
+		}
+
+		Vector<String> split_path = p_path.get_basename().split("*");
+		if (split_path.size() == 2) {
+			size_t texture_idx = split_path[1].to_int();
+			ERR_FAIL_COND_V(texture_idx >= p_scene->mNumTextures, Ref<Image>());
+			aiTexture *tex = p_scene->mTextures[texture_idx];
+			String filename = AssimpUtils::get_raw_string_from_assimp(tex->mFilename);
+			filename = filename.get_file();
+			print_verbose("Open Asset Import: Loading embedded texture " + filename);
+			if (tex->mHeight == 0) {
+				if (tex->CheckFormat("png")) {
+					Ref<Image> img = Image::_png_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth);
+					ERR_FAIL_COND_V(img.is_null(), Ref<Image>());
+					state.path_to_image_cache.insert(p_path, img);
+					return img;
+				} else if (tex->CheckFormat("jpg")) {
+					Ref<Image> img = Image::_jpg_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth);
+					ERR_FAIL_COND_V(img.is_null(), Ref<Image>());
+					state.path_to_image_cache.insert(p_path, img);
+					return img;
+				} else if (tex->CheckFormat("dds")) {
+					ERR_EXPLAIN("Open Asset Import: Embedded dds not implemented");
+					ERR_FAIL_COND_V(true, Ref<Image>());
+				}
+			} else {
+				Ref<Image> img;
+				img.instance();
+				PoolByteArray arr;
+				uint32_t size = tex->mWidth * tex->mHeight;
+				arr.resize(size);
+				memcpy(arr.write().ptr(), tex->pcData, size);
+				ERR_FAIL_COND_V(arr.size() % 4 != 0, Ref<Image>());
+				//ARGB8888 to RGBA8888
+				for (int32_t i = 0; i < arr.size() / 4; i++) {
+					arr.write().ptr()[(4 * i) + 3] = arr[(4 * i) + 0];
+					arr.write().ptr()[(4 * i) + 0] = arr[(4 * i) + 1];
+					arr.write().ptr()[(4 * i) + 1] = arr[(4 * i) + 2];
+					arr.write().ptr()[(4 * i) + 2] = arr[(4 * i) + 3];
+				}
+				img->create(tex->mWidth, tex->mHeight, true, Image::FORMAT_RGBA8, arr);
+				ERR_FAIL_COND_V(img.is_null(), Ref<Image>());
+				state.path_to_image_cache.insert(p_path, img);
+				return img;
+			}
+			return Ref<Image>();
+		} else {
+			Ref<Texture> texture = ResourceLoader::load(p_path);
+			Ref<Image> image = texture->get_data();
+			state.path_to_image_cache.insert(p_path, image);
+			return image;
+		}
+
+		return Ref<Image>();
+	}
+
+	/* create texture from assimp data, if found in path */
+	static bool CreateAssimpTexture(
+			AssimpImporter::ImportState &state,
+			aiString texture_path,
+			String &filename,
+			String &path,
+			AssimpImageData &image_state) {
+		filename = get_raw_string_from_assimp(texture_path);
+		path = state.path.get_base_dir().plus_file(filename.replace("\\", "/"));
+		bool found = false;
+		find_texture_path(state.path, path, found);
+		if (found) {
+			image_state.raw_image = AssimpUtils::load_image(state, state.assimp_scene, path);
+			if (image_state.raw_image.is_valid()) {
+				image_state.texture.instance();
+				image_state.texture->create_from_image(image_state.raw_image);
+				image_state.texture->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY);
+				return true;
+			}
+		}
+
+		return false;
+	}
+	/** GetAssimpTexture
+	  * Designed to retrieve textures for you
+	  */
+	static bool GetAssimpTexture(
+			AssimpImporter::ImportState &state,
+			aiMaterial *ai_material,
+			aiTextureType texture_type,
+			String &filename,
+			String &path,
+			AssimpImageData &image_state) {
+		aiString ai_filename = aiString();
+		if (AI_SUCCESS == ai_material->GetTexture(texture_type, 0, &ai_filename, NULL, NULL, NULL, NULL, image_state.map_mode)) {
+			return CreateAssimpTexture(state, ai_filename, filename, path, image_state);
+		}
+
+		return false;
+	}
+};
+
+#endif // IMPORT_UTILS_IMPORTER_ASSIMP_H

+ 7 - 0
thirdparty/assimp/assimp/config.h

@@ -983,6 +983,13 @@ enum aiComponent {
 #define AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT 1.0f
 #define AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT 1.0f
 #endif // !! AI_DEBONE_THRESHOLD
 #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 ------------
 // ---------- All the Build/Compile-time defines ------------
 
 
 /** @brief Specifies if double precision is supported inside assimp
 /** @brief Specifies if double precision is supported inside assimp

+ 25 - 2
thirdparty/assimp/code/Common/BaseImporter.cpp

@@ -76,9 +76,25 @@ BaseImporter::~BaseImporter() {
     // nothing to do here
     // 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.
 // 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();
     m_progress = pImp->GetProgressHandler();
     if (nullptr == m_progress) {
     if (nullptr == m_progress) {
         return nullptr;
         return nullptr;
@@ -100,6 +116,11 @@ aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile,
     {
     {
         InternReadFile( pFile, sc.get(), &filter);
         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 )    {
     } catch( const std::exception& err )    {
         // extract error description
         // extract error description
         m_ErrorText = err.what();
         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
     // the default implementation does nothing
 }
 }
@@ -588,6 +609,8 @@ aiScene* BatchLoader::GetImport( unsigned int which )
     return nullptr;
     return nullptr;
 }
 }
 
 
+
+
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 void BatchLoader::LoadAll()
 void BatchLoader::LoadAll()
 {
 {

+ 30 - 66
thirdparty/assimp/code/FBX/FBXConverter.cpp

@@ -66,6 +66,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <vector>
 #include <vector>
 #include <sstream>
 #include <sstream>
 #include <iomanip>
 #include <iomanip>
+#include <cstdint>
 
 
 
 
 namespace Assimp {
 namespace Assimp {
@@ -90,7 +91,6 @@ namespace Assimp {
         , anim_fps()
         , anim_fps()
         , out(out)
         , out(out)
         , doc(doc)
         , doc(doc)
-        , mRemoveEmptyBones( removeEmptyBones )
         , mCurrentUnit(FbxUnit::cm) {
         , mCurrentUnit(FbxUnit::cm) {
             // animations need to be converted first since this will
             // animations need to be converted first since this will
             // populate the node_anim_chain_bits map, which is needed
             // populate the node_anim_chain_bits map, which is needed
@@ -119,7 +119,6 @@ namespace Assimp {
 
 
             ConvertGlobalSettings();
             ConvertGlobalSettings();
             TransferDataToScene();
             TransferDataToScene();
-            ConvertToUnitScale(unit);
 
 
             // if we didn't read any meshes set the AI_SCENE_FLAGS_INCOMPLETE
             // if we didn't read any meshes set the AI_SCENE_FLAGS_INCOMPLETE
             // to make sure the scene passes assimp's validation. FBX files
             // to make sure the scene passes assimp's validation. FBX files
@@ -685,30 +684,37 @@ namespace Assimp {
             bool ok;
             bool ok;
 
 
             aiMatrix4x4 chain[TransformationComp_MAXIMUM];
             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<unsigned int>(TransformationComp_MAXIMUM), aiMatrix4x4());
             std::fill_n(chain, static_cast<unsigned int>(TransformationComp_MAXIMUM), aiMatrix4x4());
 
 
             // generate transformation matrices for all the different transformation components
             // generate transformation matrices for all the different transformation components
             const float zero_epsilon = 1e-6f;
             const float zero_epsilon = 1e-6f;
             const aiVector3D all_ones(1.0f, 1.0f, 1.0f);
             const aiVector3D all_ones(1.0f, 1.0f, 1.0f);
-            bool is_complex = false;
 
 
             const aiVector3D& PreRotation = PropertyGet<aiVector3D>(props, "PreRotation", ok);
             const aiVector3D& PreRotation = PropertyGet<aiVector3D>(props, "PreRotation", ok);
             if (ok && PreRotation.SquareLength() > zero_epsilon) {
             if (ok && PreRotation.SquareLength() > zero_epsilon) {
-                is_complex = true;
+                chainBits = chainBits | (1 << TransformationComp_PreRotation);
 
 
                 GetRotationMatrix(Model::RotOrder::RotOrder_EulerXYZ, PreRotation, chain[TransformationComp_PreRotation]);
                 GetRotationMatrix(Model::RotOrder::RotOrder_EulerXYZ, PreRotation, chain[TransformationComp_PreRotation]);
             }
             }
 
 
             const aiVector3D& PostRotation = PropertyGet<aiVector3D>(props, "PostRotation", ok);
             const aiVector3D& PostRotation = PropertyGet<aiVector3D>(props, "PostRotation", ok);
             if (ok && PostRotation.SquareLength() > zero_epsilon) {
             if (ok && PostRotation.SquareLength() > zero_epsilon) {
-                is_complex = true;
+                chainBits = chainBits | (1 << TransformationComp_PostRotation);
 
 
                 GetRotationMatrix(Model::RotOrder::RotOrder_EulerXYZ, PostRotation, chain[TransformationComp_PostRotation]);
                 GetRotationMatrix(Model::RotOrder::RotOrder_EulerXYZ, PostRotation, chain[TransformationComp_PostRotation]);
             }
             }
 
 
             const aiVector3D& RotationPivot = PropertyGet<aiVector3D>(props, "RotationPivot", ok);
             const aiVector3D& RotationPivot = PropertyGet<aiVector3D>(props, "RotationPivot", ok);
             if (ok && RotationPivot.SquareLength() > zero_epsilon) {
             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_RotationPivot]);
                 aiMatrix4x4::Translation(-RotationPivot, chain[TransformationComp_RotationPivotInverse]);
                 aiMatrix4x4::Translation(-RotationPivot, chain[TransformationComp_RotationPivotInverse]);
@@ -716,21 +722,21 @@ namespace Assimp {
 
 
             const aiVector3D& RotationOffset = PropertyGet<aiVector3D>(props, "RotationOffset", ok);
             const aiVector3D& RotationOffset = PropertyGet<aiVector3D>(props, "RotationOffset", ok);
             if (ok && RotationOffset.SquareLength() > zero_epsilon) {
             if (ok && RotationOffset.SquareLength() > zero_epsilon) {
-                is_complex = true;
+                chainBits = chainBits | (1 << TransformationComp_RotationOffset);
 
 
                 aiMatrix4x4::Translation(RotationOffset, chain[TransformationComp_RotationOffset]);
                 aiMatrix4x4::Translation(RotationOffset, chain[TransformationComp_RotationOffset]);
             }
             }
 
 
             const aiVector3D& ScalingOffset = PropertyGet<aiVector3D>(props, "ScalingOffset", ok);
             const aiVector3D& ScalingOffset = PropertyGet<aiVector3D>(props, "ScalingOffset", ok);
             if (ok && ScalingOffset.SquareLength() > zero_epsilon) {
             if (ok && ScalingOffset.SquareLength() > zero_epsilon) {
-                is_complex = true;
+                chainBits = chainBits | (1 << TransformationComp_ScalingOffset);
 
 
                 aiMatrix4x4::Translation(ScalingOffset, chain[TransformationComp_ScalingOffset]);
                 aiMatrix4x4::Translation(ScalingOffset, chain[TransformationComp_ScalingOffset]);
             }
             }
 
 
             const aiVector3D& ScalingPivot = PropertyGet<aiVector3D>(props, "ScalingPivot", ok);
             const aiVector3D& ScalingPivot = PropertyGet<aiVector3D>(props, "ScalingPivot", ok);
             if (ok && ScalingPivot.SquareLength() > zero_epsilon) {
             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_ScalingPivot]);
                 aiMatrix4x4::Translation(-ScalingPivot, chain[TransformationComp_ScalingPivotInverse]);
                 aiMatrix4x4::Translation(-ScalingPivot, chain[TransformationComp_ScalingPivotInverse]);
@@ -738,22 +744,28 @@ namespace Assimp {
 
 
             const aiVector3D& Translation = PropertyGet<aiVector3D>(props, "Lcl Translation", ok);
             const aiVector3D& Translation = PropertyGet<aiVector3D>(props, "Lcl Translation", ok);
             if (ok && Translation.SquareLength() > zero_epsilon) {
             if (ok && Translation.SquareLength() > zero_epsilon) {
+                chainBits = chainBits | (1 << TransformationComp_Translation);
+
                 aiMatrix4x4::Translation(Translation, chain[TransformationComp_Translation]);
                 aiMatrix4x4::Translation(Translation, chain[TransformationComp_Translation]);
             }
             }
 
 
             const aiVector3D& Scaling = PropertyGet<aiVector3D>(props, "Lcl Scaling", ok);
             const aiVector3D& Scaling = PropertyGet<aiVector3D>(props, "Lcl Scaling", ok);
             if (ok && (Scaling - all_ones).SquareLength() > zero_epsilon) {
             if (ok && (Scaling - all_ones).SquareLength() > zero_epsilon) {
+                chainBits = chainBits | (1 << TransformationComp_Scaling);
+
                 aiMatrix4x4::Scaling(Scaling, chain[TransformationComp_Scaling]);
                 aiMatrix4x4::Scaling(Scaling, chain[TransformationComp_Scaling]);
             }
             }
 
 
             const aiVector3D& Rotation = PropertyGet<aiVector3D>(props, "Lcl Rotation", ok);
             const aiVector3D& Rotation = PropertyGet<aiVector3D>(props, "Lcl Rotation", ok);
             if (ok && Rotation.SquareLength() > zero_epsilon) {
             if (ok && Rotation.SquareLength() > zero_epsilon) {
+                chainBits = chainBits | (1 << TransformationComp_Rotation);
+
                 GetRotationMatrix(rot, Rotation, chain[TransformationComp_Rotation]);
                 GetRotationMatrix(rot, Rotation, chain[TransformationComp_Rotation]);
             }
             }
 
 
             const aiVector3D& GeometricScaling = PropertyGet<aiVector3D>(props, "GeometricScaling", ok);
             const aiVector3D& GeometricScaling = PropertyGet<aiVector3D>(props, "GeometricScaling", ok);
             if (ok && (GeometricScaling - all_ones).SquareLength() > zero_epsilon) {
             if (ok && (GeometricScaling - all_ones).SquareLength() > zero_epsilon) {
-                is_complex = true;
+                chainBits = chainBits | (1 << TransformationComp_GeometricScaling);
                 aiMatrix4x4::Scaling(GeometricScaling, chain[TransformationComp_GeometricScaling]);
                 aiMatrix4x4::Scaling(GeometricScaling, chain[TransformationComp_GeometricScaling]);
                 aiVector3D GeometricScalingInverse = GeometricScaling;
                 aiVector3D GeometricScalingInverse = GeometricScaling;
                 bool canscale = true;
                 bool canscale = true;
@@ -768,13 +780,14 @@ namespace Assimp {
                     }
                     }
                 }
                 }
                 if (canscale) {
                 if (canscale) {
+                    chainBits = chainBits | (1 << TransformationComp_GeometricScalingInverse);
                     aiMatrix4x4::Scaling(GeometricScalingInverse, chain[TransformationComp_GeometricScalingInverse]);
                     aiMatrix4x4::Scaling(GeometricScalingInverse, chain[TransformationComp_GeometricScalingInverse]);
                 }
                 }
             }
             }
 
 
             const aiVector3D& GeometricRotation = PropertyGet<aiVector3D>(props, "GeometricRotation", ok);
             const aiVector3D& GeometricRotation = PropertyGet<aiVector3D>(props, "GeometricRotation", ok);
             if (ok && GeometricRotation.SquareLength() > zero_epsilon) {
             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_GeometricRotation]);
                 GetRotationMatrix(rot, GeometricRotation, chain[TransformationComp_GeometricRotationInverse]);
                 GetRotationMatrix(rot, GeometricRotation, chain[TransformationComp_GeometricRotationInverse]);
                 chain[TransformationComp_GeometricRotationInverse].Inverse();
                 chain[TransformationComp_GeometricRotationInverse].Inverse();
@@ -782,7 +795,7 @@ namespace Assimp {
 
 
             const aiVector3D& GeometricTranslation = PropertyGet<aiVector3D>(props, "GeometricTranslation", ok);
             const aiVector3D& GeometricTranslation = PropertyGet<aiVector3D>(props, "GeometricTranslation", ok);
             if (ok && GeometricTranslation.SquareLength() > zero_epsilon) {
             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_GeometricTranslation]);
                 aiMatrix4x4::Translation(-GeometricTranslation, chain[TransformationComp_GeometricTranslationInverse]);
                 aiMatrix4x4::Translation(-GeometricTranslation, chain[TransformationComp_GeometricTranslationInverse]);
             }
             }
@@ -790,12 +803,12 @@ namespace Assimp {
             // is_complex needs to be consistent with NeedsComplexTransformationChain()
             // is_complex needs to be consistent with NeedsComplexTransformationChain()
             // or the interplay between this code and the animation converter would
             // or the interplay between this code and the animation converter would
             // not be guaranteed.
             // 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,
             // now, if we have more than just Translation, Scaling and Rotation,
             // we need to generate a full node chain to accommodate for assimp's
             // we need to generate a full node chain to accommodate for assimp's
             // lack to express pivots and offsets.
             // 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);
                 FBXImporter::LogInfo("generating full transformation chain for node: " + name);
 
 
                 // query the anim_chain_bits dictionary to find out which chain elements
                 // 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) {
                 for (size_t i = 0; i < TransformationComp_MAXIMUM; ++i, bit <<= 1) {
                     const TransformationComp comp = static_cast<TransformationComp>(i);
                     const TransformationComp comp = static_cast<TransformationComp>(i);
 
 
-                    if (chain[i].IsIdentity() && (anim_chain_bitmask & bit) == 0) {
+                    if ((chainBits & bit) == 0 && (anim_chain_bitmask & bit) == 0) {
                         continue;
                         continue;
                     }
                     }
 
 
@@ -1462,14 +1475,8 @@ namespace Assimp {
 
 
                     const WeightIndexArray& indices = cluster->GetIndices();
                     const WeightIndexArray& indices = cluster->GetIndices();
 
 
-                    if (indices.empty() && mRemoveEmptyBones ) {
-                        continue;
-                    }
-
                     const MatIndexArray& mats = geo.GetMaterialIndices();
                     const MatIndexArray& mats = geo.GetMaterialIndices();
 
 
-                    bool ok = false;
-
                     const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
                     const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
 
 
                     count_out_indices.clear();
                     count_out_indices.clear();
@@ -1509,8 +1516,7 @@ namespace Assimp {
                                     out_indices.push_back(std::distance(outputVertStartIndices->begin(), it));
                                     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
                     // if we found at least one, generate the output bones
                     // XXX this could be heavily simplified by collecting the bone
                     // XXX this could be heavily simplified by collecting the bone
                     // data in a single step.
                     // 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);
                             count_out_indices, node_global_transform);
-                    }
                 }
                 }
             }
             }
             catch (std::exception&) {
             catch (std::exception&) {
@@ -3532,46 +3536,6 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
             out->mMetaData->Set(14, "CustomFrameRate", doc.GlobalSettings().CustomFrameRate());
             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()
         void FBXConverter::TransferDataToScene()
         {
         {
             ai_assert(!out->mMeshes);
             ai_assert(!out->mMeshes);

+ 0 - 7
thirdparty/assimp/code/FBX/FBXConverter.h

@@ -430,10 +430,6 @@ private:
 
 
     void ConvertGlobalSettings();
     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
     // copy generated meshes, animations, lights, cameras and textures to the output scene
     void TransferDataToScene();
     void TransferDataToScene();
@@ -470,9 +466,6 @@ private:
 
 
     aiScene* const out;
     aiScene* const out;
     const FBX::Document& doc;
     const FBX::Document& doc;
-
-    bool mRemoveEmptyBones;
-
     FbxUnit mCurrentUnit;
     FbxUnit mCurrentUnit;
 };
 };
 
 

+ 0 - 8
thirdparty/assimp/code/FBX/FBXDocument.cpp

@@ -90,14 +90,6 @@ const Object* LazyObject::Get(bool dieOnError)
         return object.get();
         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 Token& key = element.KeyToken();
     const TokenList& tokens = element.Tokens();
     const TokenList& tokens = element.Tokens();
 
 

+ 4 - 39
thirdparty/assimp/code/FBX/FBXExporter.cpp

@@ -1706,8 +1706,7 @@ void FBXExporter::WriteObjects ()
                     }
                     }
                     if (end) { break; }
                     if (end) { break; }
                 }
                 }
-                limbnodes.insert(parent);
-                skeleton.insert(parent);
+                
                 // if it was the skeleton root we can finish here
                 // if it was the skeleton root we can finish here
                 if (end) { break; }
                 if (end) { break; }
             }
             }
@@ -1848,44 +1847,10 @@ void FBXExporter::WriteObjects ()
             inverse_bone_xform.Inverse();
             inverse_bone_xform.Inverse();
             aiMatrix4x4 tr = inverse_bone_xform * mesh_xform;
             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
             // note: this means we ALWAYS rely on the mesh node transform
             // being unchanged from the time the skeleton was bound.
             // being unchanged from the time the skeleton was bound.
             // there's not really any way around this at the moment.
             // there's not really any way around this at the moment.

+ 9 - 1
thirdparty/assimp/code/FBX/FBXImporter.cpp

@@ -189,8 +189,16 @@ void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
         if (settings.convertToMeters) {
         if (settings.convertToMeters) {
             unit = FbxUnit::m;
             unit = FbxUnit::m;
         }
         }
+
         // convert the FBX DOM to aiScene
         // 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<Token>());
         std::for_each(tokens.begin(),tokens.end(),Util::delete_fun<Token>());
     }
     }

+ 4 - 3
thirdparty/assimp/code/FBX/FBXMeshGeometry.cpp

@@ -115,7 +115,6 @@ MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::strin
 
 
     if(tempVerts.empty()) {
     if(tempVerts.empty()) {
         FBXImporter::LogWarn("encountered mesh with no vertices");
         FBXImporter::LogWarn("encountered mesh with no vertices");
-        return;
     }
     }
 
 
     std::vector<int> tempFaces;
     std::vector<int> tempFaces;
@@ -123,7 +122,6 @@ MeshGeometry::MeshGeometry(uint64_t id, const Element& element, const std::strin
 
 
     if(tempFaces.empty()) {
     if(tempFaces.empty()) {
         FBXImporter::LogWarn("encountered mesh with no faces");
         FBXImporter::LogWarn("encountered mesh with no faces");
-        return;
     }
     }
 
 
     m_vertices.reserve(tempFaces.size());
     m_vertices.reserve(tempFaces.size());
@@ -612,7 +610,10 @@ void MeshGeometry::ReadVertexDataMaterials(std::vector<int>& materials_out, cons
     const std::string& ReferenceInformationType)
     const std::string& ReferenceInformationType)
 {
 {
     const size_t face_count = m_faces.size();
     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
     // materials are handled separately. First of all, they are assigned per-face
     // and not per polyvert. Secondly, ReferenceInformationType=IndexToDirect
     // and not per polyvert. Secondly, ReferenceInformationType=IndexToDirect

+ 3 - 3
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
             // project tangent and bitangent into the plane formed by the vertex' normal
             aiVector3D localTangent = tangent - meshNorm[p] * (tangent * meshNorm[p]);
             aiVector3D localTangent = tangent - meshNorm[p] * (tangent * meshNorm[p]);
             aiVector3D localBitangent = bitangent - meshNorm[p] * (bitangent * 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.
             // 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);
             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 != invalid_bitangent) {
                 if (invalid_tangent) {
                 if (invalid_tangent) {
                     localTangent = meshNorm[p] ^ localBitangent;
                     localTangent = meshNorm[p] ^ localBitangent;
-                    localTangent.Normalize();
+                    localTangent.NormalizeSafe();
                 } else {
                 } else {
                     localBitangent = localTangent ^ meshNorm[p];
                     localBitangent = localTangent ^ meshNorm[p];
-                    localBitangent.Normalize();
+                    localBitangent.NormalizeSafe();
                 }
                 }
             }
             }
 
 

+ 115 - 10
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 "ScaleProcess.h"
 
 
 #include <assimp/scene.h>
 #include <assimp/scene.h>
 #include <assimp/postprocess.h>
 #include <assimp/postprocess.h>
+#include <assimp/BaseImporter.h>
 
 
 namespace Assimp {
 namespace Assimp {
 
 
 ScaleProcess::ScaleProcess()
 ScaleProcess::ScaleProcess()
 : BaseProcess()
 : BaseProcess()
 , mScale( AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT ) {
 , mScale( AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT ) {
-    // empty
 }
 }
 
 
 ScaleProcess::~ScaleProcess() {
 ScaleProcess::~ScaleProcess() {
@@ -71,10 +69,26 @@ bool ScaleProcess::IsActive( unsigned int pFlags ) const {
 }
 }
 
 
 void ScaleProcess::SetupProperties( const Importer* pImp ) {
 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 ) {
 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 ) {
     if ( nullptr == pScene ) {
         return;
         return;
     }
     }
@@ -82,22 +96,113 @@ void ScaleProcess::Execute( aiScene* pScene ) {
     if ( nullptr == pScene->mRootNode ) {
     if ( nullptr == pScene->mRootNode ) {
         return;
         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 );
     traverseNodes( pScene->mRootNode );
 }
 }
 
 
-void ScaleProcess::traverseNodes( aiNode *node ) {
+void ScaleProcess::traverseNodes( aiNode *node, unsigned int nested_node_id ) {    
     applyScaling( node );
     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 ) {
 void ScaleProcess::applyScaling( aiNode *currentNode ) {
     if ( nullptr != 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
 } // Namespace Assimp
-
-#endif // !! ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS

+ 11 - 2
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"
 #include "Common/BaseProcess.h"
 
 
@@ -53,6 +54,11 @@ namespace Assimp {
 
 
 // ---------------------------------------------------------------------------
 // ---------------------------------------------------------------------------
 /** ScaleProcess: Class to rescale the whole model.
 /** 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 {
 class ASSIMP_API ScaleProcess : public BaseProcess {
 public:
 public:
@@ -78,7 +84,7 @@ public:
     virtual void Execute( aiScene* pScene );
     virtual void Execute( aiScene* pScene );
 
 
 private:
 private:
-    void traverseNodes( aiNode *currentNode );
+    void traverseNodes( aiNode *currentNode, unsigned int nested_node_id = 0 );
     void applyScaling( aiNode *currentNode );
     void applyScaling( aiNode *currentNode );
 
 
 private:
 private:
@@ -86,3 +92,6 @@ private:
 };
 };
 
 
 } // Namespace Assimp
 } // Namespace Assimp
+
+
+#endif // SCALE_PROCESS_H_

+ 12 - 0
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

+ 1789 - 0
thirdparty/assimp/contrib/utf8cpp/doc/utf8cpp.html

@@ -0,0 +1,1789 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+  <head>
+    <meta name="generator" content=
+    "HTML Tidy for Linux/x86 (vers 1st November 2002), see www.w3.org">
+    <meta name="description" content=
+    "A simple, portable and lightweigt C++ library for easy handling of UTF-8 encoded strings">
+    <meta name="keywords" content="UTF-8 C++ portable utf8 unicode generic templates">
+    <meta name="author" content="Nemanja Trifunovic">
+    <title>
+      UTF8-CPP: UTF-8 with C++ in a Portable Way
+    </title>
+    <style type="text/css">
+    <!--
+    span.return_value {
+      color: brown;
+    }
+    span.keyword {
+      color: blue;
+    }
+    span.preprocessor {
+      color: navy;
+    }
+    span.literal {
+      color: olive;
+    }
+    span.comment {
+      color: green;
+    }
+    code {
+      font-weight: bold; 
+    }
+    ul.toc {
+      list-style-type: none;
+    }
+    p.version {
+      font-size: small;
+      font-style: italic;
+    }
+    -->
+        </style>
+  </head>
+  <body>
+    <h1>
+      UTF8-CPP: UTF-8 with C++ in a Portable Way
+    </h1>
+    <p>
+      <a href="https://sourceforge.net/projects/utfcpp">The Sourceforge project page</a>
+    </p>
+    <div id="toc">
+      <h2>
+        Table of Contents
+      </h2>
+      <ul class="toc">
+        <li>
+          <a href="#introduction">Introduction</a>
+        </li>
+        <li>
+          <a href="#examples">Examples of Use</a>
+          <ul class="toc">
+            <li>
+              <a href=#introsample>Introductionary Sample </a>
+            </li>
+            <li>
+              <a href=#validfile>Checking if a file contains valid UTF-8 text</a>
+            </li>
+            <li>
+              <a href=#fixinvalid>Ensure that a string contains valid UTF-8 text</a>
+            </li>
+          </ul>
+        <li>
+          <a href="#reference">Reference</a>
+          <ul class="toc">
+            <li>
+              <a href="#funutf8">Functions From utf8 Namespace </a>
+            </li>
+            <li>
+              <a href="#typesutf8">Types From utf8 Namespace </a>
+            </li>
+            <li>
+              <a href="#fununchecked">Functions From utf8::unchecked Namespace </a>
+            </li>
+            <li>
+              <a href="#typesunchecked">Types From utf8::unchecked Namespace </a>
+            </li>
+          </ul>
+        </li>
+        <li>
+          <a href="#points">Points of Interest</a>
+        </li>
+        <li>
+          <a href="#links">Links</a>
+        </li>
+      </ul>
+    </div>
+    <h2 id="introduction">
+      Introduction
+    </h2>
+    <p>
+      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.
+    </p>
+    <p>
+      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.
+    </p>
+    <p>
+      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
+      <a href="http://www.unicode.org/">Unicode Home Page</a> 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.
+    </p>
+    <h2 id="examples">
+      Examples of use
+    </h2>
+    <h3 id="introsample">
+      Introductionary Sample
+    </h3>
+    <p>
+      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:
+    </p>
+<pre>
+<span class="preprocessor">#include &lt;fstream&gt;</span>
+<span class="preprocessor">#include &lt;iostream&gt;</span>
+<span class="preprocessor">#include &lt;string&gt;</span>
+<span class="preprocessor">#include &lt;vector&gt;</span>
+<span class="preprocessor">#include "utf8.h"</span>
+<span class="keyword">using namespace</span> std;
+<span class="keyword">int</span> main(<span class="keyword">int</span> argc, <span class="keyword">char</span>** argv)
+{
+    <span class="keyword">if</span> (argc != <span class="literal">2</span>) {
+        cout &lt;&lt; <span class="literal">"\nUsage: docsample filename\n"</span>;
+        <span class="keyword">return</span> <span class="literal">0</span>;
+    }
+
+    <span class="keyword">const char</span>* test_file_path = argv[1];
+    <span class="comment">// Open the test file (contains UTF-8 encoded text)</span>
+    ifstream fs8(test_file_path);
+    <span class="keyword">if</span> (!fs8.is_open()) {
+    cout &lt;&lt; <span class=
+"literal">"Could not open "</span> &lt;&lt; test_file_path &lt;&lt; endl;
+    <span class="keyword">return</span> <span class="literal">0</span>;
+    }
+
+    <span class="keyword">unsigned</span> line_count = <span class="literal">1</span>;
+    string line;
+    <span class="comment">// Play with all the lines in the file</span>
+    <span class="keyword">while</span> (getline(fs8, line)) {
+       <span class="comment">// check for invalid utf-8 (for a simple yes/no check, there is also utf8::is_valid function)</span>
+        string::iterator end_it = utf8::find_invalid(line.begin(), line.end());
+        <span class="keyword">if</span> (end_it != line.end()) {
+            cout &lt;&lt; <span class=
+"literal">"Invalid UTF-8 encoding detected at line "</span> &lt;&lt; line_count &lt;&lt; <span
+ class="literal">"\n"</span>;
+            cout &lt;&lt; <span class=
+"literal">"This part is fine: "</span> &lt;&lt; string(line.begin(), end_it) &lt;&lt; <span
+ class="literal">"\n"</span>;
+        }
+
+        <span class="comment">// Get the line length (at least for the valid part)</span>
+        <span class="keyword">int</span> length = utf8::distance(line.begin(), end_it);
+        cout &lt;&lt; <span class=
+"literal">"Length of line "</span> &lt;&lt; line_count &lt;&lt; <span class=
+"literal">" is "</span> &lt;&lt; length &lt;&lt;  <span class="literal">"\n"</span>;
+
+        <span class="comment">// Convert it to utf-16</span>
+        vector&lt;unsigned short&gt; utf16line;
+        utf8::utf8to16(line.begin(), end_it, back_inserter(utf16line));
+
+        <span class="comment">// And back to utf-8</span>
+        string utf8line; 
+        utf8::utf16to8(utf16line.begin(), utf16line.end(), back_inserter(utf8line));
+
+        <span class="comment">// Confirm that the conversion went OK:</span>
+        <span class="keyword">if</span> (utf8line != string(line.begin(), end_it))
+            cout &lt;&lt; <span class=
+"literal">"Error in UTF-16 conversion at line: "</span> &lt;&lt; line_count &lt;&lt; <span
+ class="literal">"\n"</span>;        
+
+        line_count++;
+    }
+    <span class="keyword">return</span> <span class="literal">0</span>;
+}
+</pre>
+    <p>
+      In the previous code sample, for each line we performed
+      a detection of invalid UTF-8 sequences with <code>find_invalid</code>; 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 <code>utf8::distance</code>; finally, we have converted
+      each line to UTF-16 encoding with <code>utf8to16</code> and back to UTF-8 with
+      <code>utf16to8</code>.
+    </p>
+    <h3 id="validfile">Checking if a file contains valid UTF-8 text</h3>
+<p>
+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:
+</p>
+<pre>    
+<span class="keyword">bool</span> valid_utf8_file(i<span class="keyword">const char</span>* file_name)
+{
+    ifstream ifs(file_name);
+    <span class="keyword">if</span> (!ifs)
+        <span class="keyword">return false</span>; <span class="comment">// even better, throw here</span>
+
+    istreambuf_iterator&lt;<span class="keyword">char</span>&gt; it(ifs.rdbuf());
+    istreambuf_iterator&lt;<span class="keyword">char</span>&gt; eos;
+
+    <span class="keyword">return</span> utf8::is_valid(it, eos);
+}
+</pre>
+<p>
+Because the function <code>utf8::is_valid()</code> works with input iterators, we were able
+to pass an <code>istreambuf_iterator</code> to it and read the content of the file directly 
+without loading it to the memory first.</p>
+<p>
+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:
+</p>
+<pre>
+    utf8::utf8to16(it, eos, back_inserter(u16string));
+</pre>
+    <h3 id="fixinvalid">Ensure that a string contains valid UTF-8 text</h3>
+<p>
+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:
+</p>
+<pre>
+<span class="keyword">void</span> fix_utf8_string(std::string&amp; str)
+{
+    std::string temp;
+    utf8::replace_invalid(str.begin(), str.end(), back_inserter(temp));
+    str = temp;
+}
+</pre>
+<p>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.
+</p>
+    <h2 id="reference">
+      Reference
+    </h2>
+    <h3 id="funutf8">
+      Functions From utf8 Namespace
+    </h3>
+    <h4>
+      utf8::append
+    </h4>
+    <p class="version">
+    Available in version 1.0 and later.
+    </p>
+    <p>
+      Encodes a 32 bit code point as a UTF-8 sequence of octets and appends the sequence
+      to a UTF-8 string.
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+octet_iterator append(uint32_t cp, octet_iterator result);
+   
+</pre>
+    <p>
+	  <code>octet_iterator</code>: an output iterator.<br>
+      <code>cp</code>: a 32 bit integer representing a code point to append to the
+      sequence.<br>
+       <code>result</code>: an output iterator to the place in the sequence where to
+      append the code point.<br>
+       <span class="return_value">Return value</span>: an iterator pointing to the place
+      after the newly appended sequence.
+    </p>
+    <p>
+      Example of use:
+    </p>
+<pre>
+<span class="keyword">unsigned char</span> u[<span class="literal">5</span>] = {<span
+class="literal">0</span>,<span class="literal">0</span>,<span class=
+"literal">0</span>,<span class="literal">0</span>,<span class="literal">0</span>};
+<span class="keyword">unsigned char</span>* end = append(<span class=
+"literal">0x0448</span>, u);
+assert (u[<span class="literal">0</span>] == <span class=
+"literal">0xd1</span> &amp;&amp; u[<span class="literal">1</span>] == <span class=
+"literal">0x88</span> &amp;&amp; u[<span class="literal">2</span>] == <span class=
+"literal">0</span> &amp;&amp; u[<span class="literal">3</span>] == <span class=
+"literal">0</span> &amp;&amp; u[<span class="literal">4</span>] == <span class=
+"literal">0</span>);
+</pre>
+    <p>
+      Note that <code>append</code> 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, <code>append</code> can add anywhere between 1 and 4
+      octets to the sequence. In practice, you would most often want to use
+      <code>std::back_inserter</code> to ensure that the necessary memory is allocated.
+    </p>
+    <p>
+      In case of an invalid code point, a <code>utf8::invalid_code_point</code> exception
+      is thrown.
+    </p>
+    <h4>
+      utf8::next
+    </h4>
+    <p class="version">
+    Available in version 1.0 and later.
+    </p>
+    <p>
+      Given the iterator to the beginning of the UTF-8 sequence, it returns the code
+      point and moves the iterator to the next position.
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt; 
+uint32_t next(octet_iterator&amp; it, octet_iterator end);
+   
+</pre>
+    <p>
+	  <code>octet_iterator</code>: an input iterator.<br>
+      <code>it</code>: 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.<br>
+       <code>end</code>: end of the UTF-8 sequence to be processed. If <code>it</code>
+      gets equal to <code>end</code> during the extraction of a code point, an
+      <code>utf8::not_enough_room</code> exception is thrown.<br>
+       <span class="return_value">Return value</span>: the 32 bit representation of the
+      processed UTF-8 code point.
+    </p>
+    <p>
+      Example of use:
+    </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">char</span>* w = twochars;
+<span class="keyword">int</span> cp = next(w, twochars + <span class="literal">6</span>);
+assert (cp == <span class="literal">0x65e5</span>);
+assert (w == twochars + <span class="literal">3</span>);
+</pre>
+    <p>
+      This function is typically used to iterate through a UTF-8 encoded string.
+    </p>
+    <p>
+      In case of an invalid UTF-8 seqence, a <code>utf8::invalid_utf8</code> exception is
+      thrown.
+    </p>
+    <h4>
+      utf8::peek_next
+    </h4>
+    <p class="version">
+    Available in version 2.1 and later.
+    </p>
+    <p>
+      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. 
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt; 
+uint32_t peek_next(octet_iterator it, octet_iterator end);
+   
+</pre>
+    <p>
+	  <code>octet_iterator</code>: an input iterator.<br>
+      <code>it</code>: an iterator pointing to the beginning of an UTF-8
+      encoded code point.<br>
+       <code>end</code>: end of the UTF-8 sequence to be processed. If <code>it</code>
+      gets equal to <code>end</code> during the extraction of a code point, an
+      <code>utf8::not_enough_room</code> exception is thrown.<br>
+       <span class="return_value">Return value</span>: the 32 bit representation of the
+      processed UTF-8 code point.
+    </p>
+    <p>
+      Example of use:
+    </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">char</span>* w = twochars;
+<span class="keyword">int</span> cp = peek_next(w, twochars + <span class="literal">6</span>);
+assert (cp == <span class="literal">0x65e5</span>);
+assert (w == twochars);
+</pre>
+    <p>
+      In case of an invalid UTF-8 seqence, a <code>utf8::invalid_utf8</code> exception is
+      thrown.
+    </p>
+    <h4>
+      utf8::prior
+    </h4>
+    <p class="version">
+    Available in version 1.02 and later.
+    </p>
+    <p>
+      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.
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt; 
+uint32_t prior(octet_iterator&amp; it, octet_iterator start);
+   
+</pre>
+    <p>
+	  <code>octet_iterator</code>: a bidirectional iterator.<br>
+      <code>it</code>: 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.<br>
+       <code>start</code>: 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.<br>
+       <span class="return_value">Return value</span>: the 32 bit representation of the
+      previous code point.
+    </p>
+    <p>
+      Example of use:
+    </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">unsigned char</span>* w = twochars + <span class=
+"literal">3</span>;
+<span class="keyword">int</span> cp = prior (w, twochars);
+assert (cp == <span class="literal">0x65e5</span>);
+assert (w == twochars);
+</pre>
+    <p> 
+      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 <code>utf8::next</code> 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 <code>utf8::prior</code> may not detect an invalid UTF-8 sequence in some scenarios:
+      for instance if there are superfluous trail octets, it will just skip them.
+    </p> 
+    <p>
+      <code>it</code> will typically point to the beginning of
+      a code point, and <code>start</code> will point to the
+      beginning of the string to ensure we don't go backwards too far. <code>it</code> 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.
+    </p>
+    <p>
+      In case <code>start</code> is reached before a UTF-8 lead octet is hit, or if an
+      invalid UTF-8 sequence is started by the lead octet, an <code>invalid_utf8</code>
+      exception is thrown.
+    </p>
+    <p>In case <code>start</code> equals <code>it</code>, a <code>not_enough_room</code>
+      exception is thrown.
+    <h4>
+      utf8::previous
+    </h4>
+    <p class="version">
+    Deprecated in version 1.02 and later.
+    </p>
+    <p>
+      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.
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt; 
+uint32_t previous(octet_iterator&amp; it, octet_iterator pass_start);
+   
+</pre>
+    <p>
+	  <code>octet_iterator</code>: a random access iterator.<br>
+      <code>it</code>: 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.<br>
+       <code>pass_start</code>: 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.<br>
+       <span class="return_value">Return value</span>: the 32 bit representation of the
+      previous code point.
+    </p>
+    <p>
+      Example of use:
+    </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">unsigned char</span>* w = twochars + <span class=
+"literal">3</span>;
+<span class="keyword">int</span> cp = previous (w, twochars - <span class=
+"literal">1</span>);
+assert (cp == <span class="literal">0x65e5</span>);
+assert (w == twochars);
+</pre>
+    <p>
+      <code>utf8::previous</code> is deprecated, and <code>utf8::prior</code> should
+      be used instead, although the existing code can continue using this function.
+      The problem is the parameter <code>pass_start</code> 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.
+    </p>
+    <p>
+      <code>it</code> will typically point to the beginning of
+      a code point, and <code>pass_start</code> will point to the octet just before the
+      beginning of the string to ensure we don't go backwards too far. <code>it</code> 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.
+    </p>
+    <p>
+      In case <code>pass_start</code> is reached before a UTF-8 lead octet is hit, or if an
+      invalid UTF-8 sequence is started by the lead octet, an <code>invalid_utf8</code>
+      exception is thrown
+    </p>
+    <h4>
+      utf8::advance
+    </h4>
+    <p class="version">
+    Available in version 1.0 and later.
+    </p>
+    <p>
+      Advances an iterator by the specified number of code points within an UTF-8
+      sequence.
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator, typename distance_type&gt; 
+<span class=
+"keyword">void</span> advance (octet_iterator&amp; it, distance_type n, octet_iterator end);
+   
+</pre>
+    <p>
+      <code>octet_iterator</code>: an input iterator.<br>
+      <code>distance_type</code>: an integral type convertible to <code>octet_iterator</code>'s difference type.<br>
+      <code>it</code>: 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.<br>
+       <code>n</code>: a positive integer that shows how many code points we want to
+      advance.<br>
+       <code>end</code>: end of the UTF-8 sequence to be processed. If <code>it</code>
+      gets equal to <code>end</code> during the extraction of a code point, an
+      <code>utf8::not_enough_room</code> exception is thrown.<br>
+    </p>
+    <p>
+      Example of use:
+    </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">unsigned char</span>* w = twochars;
+advance (w, <span class="literal">2</span>, twochars + <span class="literal">6</span>);
+assert (w == twochars + <span class="literal">5</span>);
+</pre>
+    <p>
+      This function works only "forward". In case of a negative <code>n</code>, there is
+      no effect.
+    </p>
+    <p>
+      In case of an invalid code point, a <code>utf8::invalid_code_point</code> exception
+      is thrown.
+    </p>
+    <h4>
+      utf8::distance
+    </h4>
+    <p class="version">
+    Available in version 1.0 and later.
+    </p>
+    <p>
+      Given the iterators to two UTF-8 encoded code points in a seqence, returns the
+      number of code points between them.
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt; 
+<span class=
+"keyword">typename</span> std::iterator_traits&lt;octet_iterator&gt;::difference_type distance (octet_iterator first, octet_iterator last);
+   
+</pre>
+    <p>
+      <code>octet_iterator</code>: an input iterator.<br>
+      <code>first</code>: an iterator to a beginning of a UTF-8 encoded code point.<br>
+      <code>last</code>: 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.<br>
+       <span class="return_value">Return value</span> the distance between the iterators,
+      in code points.
+    </p>
+    <p>
+      Example of use:
+    </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+size_t dist = utf8::distance(twochars, twochars + <span class="literal">5</span>);
+assert (dist == <span class="literal">2</span>);
+</pre>
+    <p>
+      This function is used to find the length (in code points) of a UTF-8 encoded
+      string. The reason it is called <em>distance</em>, rather than, say,
+      <em>length</em> is mainly because developers are used that <em>length</em> 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 <code>std::distance</code> algorithm.
+    </p>
+    <p>
+      In case of an invalid UTF-8 seqence, a <code>utf8::invalid_utf8</code> exception is
+      thrown. If <code>last</code> does not point to the past-of-end of a UTF-8 seqence,
+      a <code>utf8::not_enough_room</code> exception is thrown.
+    </p>
+    <h4>
+      utf8::utf16to8
+    </h4>
+    <p class="version">
+    Available in version 1.0 and later.
+    </p>
+    <p>
+      Converts a UTF-16 encoded string to UTF-8.
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> u16bit_iterator, <span class=
+"keyword">typename</span> octet_iterator&gt;
+octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result);
+   
+</pre>
+    <p>
+      <code>u16bit_iterator</code>: an input iterator.<br>
+      <code>octet_iterator</code>: an output iterator.<br>
+      <code>start</code>: an iterator pointing to the beginning of the UTF-16 encoded
+      string to convert.<br>
+       <code>end</code>: an iterator pointing to pass-the-end of the UTF-16 encoded
+      string to convert.<br>
+       <code>result</code>: an output iterator to the place in the UTF-8 string where to
+      append the result of conversion.<br>
+       <span class="return_value">Return value</span>: An iterator pointing to the place
+      after the appended UTF-8 string.
+    </p>
+    <p>
+      Example of use:
+    </p>
+<pre>
+<span class="keyword">unsigned short</span> utf16string[] = {<span class=
+"literal">0x41</span>, <span class="literal">0x0448</span>, <span class=
+"literal">0x65e5</span>, <span class="literal">0xd834</span>, <span class=
+"literal">0xdd1e</span>};
+vector&lt;<span class="keyword">unsigned char</span>&gt; utf8result;
+utf16to8(utf16string, utf16string + <span class=
+"literal">5</span>, back_inserter(utf8result));
+assert (utf8result.size() == <span class="literal">10</span>);    
+</pre>
+    <p>
+      In case of invalid UTF-16 sequence, a <code>utf8::invalid_utf16</code> exception is
+      thrown.
+    </p>
+    <h4>
+      utf8::utf8to16
+    </h4>
+    <p class="version">
+    Available in version 1.0 and later.
+    </p>
+    <p>
+      Converts an UTF-8 encoded string to UTF-16
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> u16bit_iterator, typename octet_iterator&gt;
+u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result);
+   
+</pre>
+    <p>
+      <code>octet_iterator</code>: an input iterator.<br>
+      <code>u16bit_iterator</code>: an output iterator.<br>
+      <code>start</code>: an iterator pointing to the beginning of the UTF-8 encoded
+      string to convert. &lt; br /&gt; <code>end</code>: an iterator pointing to
+      pass-the-end of the UTF-8 encoded string to convert.<br>
+       <code>result</code>: an output iterator to the place in the UTF-16 string where to
+      append the result of conversion.<br>
+       <span class="return_value">Return value</span>: An iterator pointing to the place
+      after the appended UTF-16 string.
+    </p>
+    <p>
+      Example of use:
+    </p>
+<pre>
+<span class="keyword">char</span> utf8_with_surrogates[] = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e"</span>;
+vector &lt;<span class="keyword">unsigned short</span>&gt; utf16result;
+utf8to16(utf8_with_surrogates, utf8_with_surrogates + <span class=
+"literal">9</span>, back_inserter(utf16result));
+assert (utf16result.size() == <span class="literal">4</span>);
+assert (utf16result[<span class="literal">2</span>] == <span class=
+"literal">0xd834</span>);
+assert (utf16result[<span class="literal">3</span>] == <span class=
+"literal">0xdd1e</span>);
+</pre>
+    <p>
+      In case of an invalid UTF-8 seqence, a <code>utf8::invalid_utf8</code> exception is
+      thrown. If <code>end</code> does not point to the past-of-end of a UTF-8 seqence, a
+      <code>utf8::not_enough_room</code> exception is thrown.
+    </p>
+    <h4>
+      utf8::utf32to8
+    </h4>
+    <p class="version">
+    Available in version 1.0 and later.
+    </p>
+    <p>
+      Converts a UTF-32 encoded string to UTF-8.
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator, typename u32bit_iterator&gt;
+octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result);
+   
+</pre>
+    <p>
+      <code>octet_iterator</code>: an output iterator.<br>
+      <code>u32bit_iterator</code>: an input iterator.<br>
+      <code>start</code>: an iterator pointing to the beginning of the UTF-32 encoded
+      string to convert.<br>
+       <code>end</code>: an iterator pointing to pass-the-end of the UTF-32 encoded
+      string to convert.<br>
+       <code>result</code>: an output iterator to the place in the UTF-8 string where to
+      append the result of conversion.<br>
+       <span class="return_value">Return value</span>: An iterator pointing to the place
+      after the appended UTF-8 string.
+    </p>
+    <p>
+      Example of use:
+    </p>
+<pre>
+<span class="keyword">int</span> utf32string[] = {<span class=
+"literal">0x448</span>, <span class="literal">0x65E5</span>, <span class=
+"literal">0x10346</span>, <span class="literal">0</span>};
+vector&lt;<span class="keyword">unsigned char</span>&gt; utf8result;
+utf32to8(utf32string, utf32string + <span class=
+"literal">3</span>, back_inserter(utf8result));
+assert (utf8result.size() == <span class="literal">9</span>);
+</pre>
+    <p>
+      In case of invalid UTF-32 string, a <code>utf8::invalid_code_point</code> exception
+      is thrown.
+    </p>
+    <h4>
+      utf8::utf8to32
+    </h4>
+    <p class="version">
+    Available in version 1.0 and later.
+    </p>
+    <p>
+      Converts a UTF-8 encoded string to UTF-32.
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator, <span class=
+"keyword">typename</span> u32bit_iterator&gt;
+u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result);
+   
+</pre>
+    <p>
+      <code>octet_iterator</code>: an input iterator.<br>
+      <code>u32bit_iterator</code>: an output iterator.<br>
+      <code>start</code>: an iterator pointing to the beginning of the UTF-8 encoded
+      string to convert.<br>
+       <code>end</code>: an iterator pointing to pass-the-end of the UTF-8 encoded string
+      to convert.<br>
+       <code>result</code>: an output iterator to the place in the UTF-32 string where to
+      append the result of conversion.<br>
+       <span class="return_value">Return value</span>: An iterator pointing to the place
+      after the appended UTF-32 string.
+    </p>
+    <p>
+      Example of use:
+    </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+vector&lt;<span class="keyword">int</span>&gt; utf32result;
+utf8to32(twochars, twochars + <span class=
+"literal">5</span>, back_inserter(utf32result));
+assert (utf32result.size() == <span class="literal">2</span>);
+</pre>
+    <p>
+      In case of an invalid UTF-8 seqence, a <code>utf8::invalid_utf8</code> exception is
+      thrown. If <code>end</code> does not point to the past-of-end of a UTF-8 seqence, a
+      <code>utf8::not_enough_room</code> exception is thrown.
+    </p>
+    <h4>
+      utf8::find_invalid
+    </h4>
+    <p class="version">
+    Available in version 1.0 and later.
+    </p>
+    <p>
+      Detects an invalid sequence within a UTF-8 string.
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt; 
+octet_iterator find_invalid(octet_iterator start, octet_iterator end);
+</pre>
+    <p>
+      <code>octet_iterator</code>: an input iterator.<br>
+      <code>start</code>: an iterator pointing to the beginning of the UTF-8 string to
+      test for validity.<br>
+       <code>end</code>: an iterator pointing to pass-the-end of the UTF-8 string to test
+      for validity.<br>
+       <span class="return_value">Return value</span>: an iterator pointing to the first
+      invalid octet in the UTF-8 string. In case none were found, equals
+      <code>end</code>.
+    </p>
+    <p>
+      Example of use:
+    </p>
+<pre>
+<span class="keyword">char</span> utf_invalid[] = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88\xfa"</span>;
+<span class=
+"keyword">char</span>* invalid = find_invalid(utf_invalid, utf_invalid + <span class=
+"literal">6</span>);
+assert (invalid == utf_invalid + <span class="literal">5</span>);
+</pre>
+    <p>
+      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 <em>unchecked</em> operations on it.
+    </p>
+    <h4>
+      utf8::is_valid
+    </h4>
+    <p class="version">
+    Available in version 1.0 and later.
+    </p>
+    <p>
+      Checks whether a sequence of octets is a valid UTF-8 string.
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt; 
+<span class="keyword">bool</span> is_valid(octet_iterator start, octet_iterator end);
+   
+</pre>
+    <p>
+      <code>octet_iterator</code>: an input iterator.<br>
+      <code>start</code>: an iterator pointing to the beginning of the UTF-8 string to
+      test for validity.<br>
+       <code>end</code>: an iterator pointing to pass-the-end of the UTF-8 string to test
+      for validity.<br>
+       <span class="return_value">Return value</span>: <code>true</code> if the sequence
+      is a valid UTF-8 string; <code>false</code> if not.
+    </p>
+    Example of use: 
+<pre>
+<span class="keyword">char</span> utf_invalid[] = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88\xfa"</span>;
+<span class="keyword">bool</span> bvalid = is_valid(utf_invalid, utf_invalid + <span
+class="literal">6</span>);
+assert (bvalid == false);
+</pre>
+    <p>
+      <code>is_valid</code> is a shorthand for <code>find_invalid(start, end) ==
+      end;</code>. 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.
+    </p>
+    <h4>
+      utf8::replace_invalid
+    </h4>
+    <p class="version">
+    Available in version 2.0 and later.
+    </p>
+    <p>
+      Replaces all invalid UTF-8 sequences within a string with a replacement marker.
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator, <span class=
+"keyword">typename</span> output_iterator&gt;
+output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement);
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator, <span class=
+"keyword">typename</span> output_iterator&gt;
+output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out);
+   
+</pre>
+    <p>
+      <code>octet_iterator</code>: an input iterator.<br>
+      <code>output_iterator</code>: an output iterator.<br>
+      <code>start</code>: an iterator pointing to the beginning of the UTF-8 string to
+      look for invalid UTF-8 sequences.<br>
+       <code>end</code>: an iterator pointing to pass-the-end of the UTF-8 string to look
+      for invalid UTF-8 sequences.<br>
+       <code>out</code>: An output iterator to the range where the result of replacement
+      is stored.<br>
+       <code>replacement</code>: A Unicode code point for the replacement marker. The
+      version without this parameter assumes the value <code>0xfffd</code><br>
+       <span class="return_value">Return value</span>: An iterator pointing to the place
+      after the UTF-8 string with replaced invalid sequences.
+    </p>
+    <p>
+      Example of use:
+    </p>
+<pre>
+<span class="keyword">char</span> invalid_sequence[] = <span class=
+"literal">"a\x80\xe0\xa0\xc0\xaf\xed\xa0\x80z"</span>;
+vector&lt;<span class="keyword">char</span>&gt; replace_invalid_result;
+replace_invalid (invalid_sequence, invalid_sequence + sizeof(invalid_sequence), back_inserter(replace_invalid_result), <span
+ class="literal">'?'</span>);
+bvalid = is_valid(replace_invalid_result.begin(), replace_invalid_result.end());
+assert (bvalid);
+<span class="keyword">char</span>* fixed_invalid_sequence = <span class=
+"literal">"a????z"</span>;
+assert (std::equal(replace_invalid_result.begin(), replace_invalid_result.end(), fixed_invalid_sequence));
+</pre>
+    <p>
+      <code>replace_invalid</code> 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, <code>out</code> must not
+      be in the <code>[start, end]</code> range.
+    </p>
+    <p>
+      If <code>end</code> does not point to the past-of-end of a UTF-8 sequence, a
+      <code>utf8::not_enough_room</code> exception is thrown.
+    </p>
+    <h4>
+      utf8::starts_with_bom
+    </h4>
+    <p class="version">
+    Available in version 2.3 and later. Relaces deprecated <code>is_bom()</code> function.
+    </p>
+    <p>
+      Checks whether an octet sequence starts with a UTF-8 byte order mark (BOM)
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt; 
+<span class="keyword">bool</span> starts_with_bom (octet_iterator it, octet_iterator end);
+</pre>
+    <p>
+      <code>octet_iterator</code>: an input iterator.<br>
+      <code>it</code>: beginning of the octet sequence to check<br>
+      <code>end</code>: pass-end of the sequence to check<br>
+       <span class="return_value">Return value</span>: <code>true</code> if the sequence
+      starts with a UTF-8 byte order mark; <code>false</code> if not.
+    </p>
+    <p>
+      Example of use:
+    </p>
+<pre>
+<span class="keyword">unsigned char</span> byte_order_mark[] = {<span class=
+"literal">0xef</span>, <span class="literal">0xbb</span>, <span class=
+"literal">0xbf</span>};
+<span class="keyword">bool</span> bbom = starts_with_bom(byte_order_mark, byte_order_mark + <span class="keyword">sizeof</span>(byte_order_mark));
+assert (bbom == <span class="literal">true</span>);
+</pre>
+    <p>
+      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.
+    </p>
+    <h4>
+      utf8::is_bom
+    </h4>
+    <p class="version">
+    Available in version 1.0 and later. Deprecated in version 2.3. <code>starts_with_bom()</code> should be used
+    instead.
+    </p>
+    <p>
+      Checks whether a sequence of three octets is a UTF-8 byte order mark (BOM)
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt; 
+<span class="keyword">bool</span> is_bom (octet_iterator it); <span class="comment"> // Deprecated</span>
+</pre>
+    <p>
+      <code>octet_iterator</code>: an input iterator.<br>
+      <code>it</code>: beginning of the 3-octet sequence to check<br>
+       <span class="return_value">Return value</span>: <code>true</code> if the sequence
+      is UTF-8 byte order mark; <code>false</code> if not.
+    </p>
+    <p>
+      Example of use:
+    </p>
+<pre>
+<span class="keyword">unsigned char</span> byte_order_mark[] = {<span class=
+"literal">0xef</span>, <span class="literal">0xbb</span>, <span class=
+"literal">0xbf</span>};
+<span class="keyword">bool</span> bbom = is_bom(byte_order_mark);
+assert (bbom == <span class="literal">true</span>);
+</pre>
+    <p>
+      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.
+    </p>
+    <p>
+      If a sequence is 
+      shorter than three bytes, an invalid iterator will be dereferenced. Therefore, this function is deprecated
+      in favor of <code>starts_with_bom()</code>that takes the end of sequence as an argument.
+    </p>
+    <h3 id="typesutf8">
+      Types From utf8 Namespace
+    </h3>
+    <h4>utf8::exception
+    </h4>
+    <p class="version">
+    Available in version 2.3 and later.
+    </p>
+    <p>
+    Base class for the exceptions thrown by UTF CPP library functions.
+    </p>
+<pre>
+<span class="keyword">class</span> exception : <span class="keyword">public</span> std::exception {};
+</pre>
+    <p>
+    Example of use:
+    </p>
+<pre>
+<span class="keyword">try</span> {
+  code_that_uses_utf_cpp_library();
+}
+<span class="keyword">catch</span>(<span class="keyword">const</span> utf8::exception&amp; utfcpp_ex) {
+  cerr &lt;&lt; utfcpp_ex.what();
+}
+</pre>
+    
+    <h4>utf8::invalid_code_point
+    </h4>
+    <p class="version">
+    Available in version 1.0 and later.
+    </p>
+    <p>
+    Thrown by UTF8 CPP functions such as <code>advance</code> and <code>next</code> if an UTF-8 sequence represents and invalid code point.
+    </p>
+
+<pre>
+<span class="keyword">class</span> invalid_code_point : <span class="keyword">public</span> exception {
+<span class="keyword">public</span>: 
+    uint32_t code_point() <span class="keyword">const</span>;
+};
+
+</pre>
+    <p>
+    Member function <code>code_point()</code> can be used to determine the invalid code point that
+    caused the exception to be thrown.
+    </p>
+    <h4>utf8::invalid_utf8
+    </h4>
+    <p class="version">
+    Available in version 1.0 and later.
+    </p>
+    <p>
+    Thrown by UTF8 CPP functions such as <code>next</code> and <code>prior</code> if an invalid UTF-8 sequence
+    is detected during decoding.
+    </p>
+
+<pre>
+<span class="keyword">class</span> invalid_utf8 : <span class="keyword">public</span> exception {
+<span class="keyword">public</span>: 
+    uint8_t utf8_octet() <span class="keyword">const</span>;
+};
+</pre>
+
+    <p>
+    Member function <code>utf8_octet()</code> can be used to determine the beginning of the byte 
+    sequence that caused the exception to be thrown.
+    </p>
+</pre>
+    <h4>utf8::invalid_utf16
+    </h4>
+    <p class="version">
+    Available in version 1.0 and later.
+    </p>
+    <p>
+    Thrown by UTF8 CPP function <code>utf16to8</code> if an invalid UTF-16 sequence
+    is detected during decoding.
+    </p>
+
+<pre>
+<span class="keyword">class</span> invalid_utf16 : <span class="keyword">public</span> exception {
+<span class="keyword">public</span>: 
+    uint16_t utf16_word() <span class="keyword">const</span>;
+};
+</pre>
+
+    <p>
+    Member function <code>utf16_word()</code> can be used to determine the UTF-16 code unit 
+    that caused the exception to be thrown.
+    </p>
+    <h4>utf8::not_enough_room
+    </h4>
+    <p class="version">
+    Available in version 1.0 and later.
+    </p>
+    <p>
+    Thrown by UTF8 CPP functions such as <code>next</code> if the end of the decoded UTF-8 sequence
+    was reached before the code point was decoded.
+    </p>
+
+<pre>
+<span class="keyword">class</span> not_enough_room : <span class="keyword">public</span> exception {};
+</pre>
+    <h4>
+      utf8::iterator
+    </h4>
+    <p class="version">
+    Available in version 2.0 and later.
+    </p>
+    <p>
+      Adapts the underlying octet iterator to iterate over the sequence of code points,
+      rather than raw octets.
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class="keyword">typename</span> octet_iterator&gt;
+<span class="keyword">class</span> iterator;
+</pre>
+    
+    <h5>Member functions</h5>
+      <dl>
+      <dt><code>iterator();</code> <dd> the deafult constructor; the underlying <code>octet_iterator</code> is
+      constructed with its default constructor.
+      <dt><code><span class="keyword">explicit</span> iterator (const octet_iterator&amp; octet_it, 
+                         const octet_iterator&amp; range_start,
+                         const octet_iterator&amp; range_end);</code> <dd> a constructor 
+      that initializes the underlying <code>octet_iterator</code> with <code>octet_it</code>
+      and sets the range in which the iterator is considered valid.
+      <dt><code>octet_iterator base () <span class="keyword">const</span>;</code> <dd> returns the 
+      underlying <code>octet_iterator</code>.
+      <dt><code>uint32_t operator * () <span class="keyword">const</span>;</code> <dd> decodes the utf-8 sequence
+      the underlying <code>octet_iterator</code> is pointing to and returns the code point.
+      <dt><code><span class="keyword">bool operator</span> == (const iterator&amp; rhs)
+      <span class="keyword">const</span>;</code> <dd> returns <span class="keyword">true</span>
+      if the two underlaying iterators are equal.
+      <dt><code><span class="keyword">bool operator</span> != (const iterator&amp; rhs)
+      <span class="keyword">const</span>;</code> <dd> returns <span class="keyword">true</span>
+      if the two underlaying iterators are not equal.
+      <dt><code>iterator&amp; <span class="keyword">operator</span> ++ (); </code> <dd> the prefix increment - moves
+      the iterator to the next UTF-8 encoded code point.
+      <dt><code>iterator <span class="keyword">operator</span> ++ (<span class="keyword">int</span>); </code> <dd>
+      the postfix increment - moves the iterator to the next UTF-8 encoded code point and returns the current one.
+      <dt><code>iterator&amp; <span class="keyword">operator</span> -- (); </code> <dd> the prefix decrement - moves
+      the iterator to the previous UTF-8 encoded code point.
+      <dt><code>iterator <span class="keyword">operator</span> -- (<span class="keyword">int</span>); </code> <dd>
+      the postfix decrement - moves the iterator to the previous UTF-8 encoded code point and returns the current one.
+      </dl>
+      <p>
+      Example of use:
+      </p>
+<pre>
+<span class="keyword">char</span>* threechars = <span class="literal">"\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"</span>;
+utf8::iterator&lt;<span class="keyword">char</span>*&gt; it(threechars, threechars, threechars + <span class="literal">9</span>);
+utf8::iterator&lt;<span class="keyword">char</span>*&gt; it2 = it;
+assert (it2 == it);
+assert (*it == <span class="literal">0x10346</span>);
+assert (*(++it) == <span class="literal">0x65e5</span>);
+assert ((*it++) == <span class="literal">0x65e5</span>);
+assert (*it == <span class="literal">0x0448</span>);
+assert (it != it2);
+utf8::iterator&lt;<span class="keyword">char</span>*&gt; endit (threechars + <span class="literal">9</span>, threechars, threechars + <span class="literal">9</span>);  
+assert (++it == endit);
+assert (*(--it) == <span class="literal">0x0448</span>);
+assert ((*it--) == <span class="literal">0x0448</span>);
+assert (*it == <span class="literal">0x65e5</span>);
+assert (--it == utf8::iterator&lt;<span class="keyword">char</span>*&gt;(threechars, threechars, threechars + <span class="literal">9</span>));
+assert (*it == <span class="literal">0x10346</span>);
+</pre>
+      <p>
+      The purpose of <code>utf8::iterator</code> 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 
+      <code>utf8::next()</code> and <code>utf8::prior()</code> functions. 
+      </p>
+      <p>
+      Note that <code>utf8::iterator</code> 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 <code>begin</code> and <code>end</code>, i.e.:
+      </p>
+<pre>
+std::string s = <span class="literal">"example"</span>;
+utf8::iterator i (s.begin(), s.begin(), s.end());
+</pre>
+    <h3 id="fununchecked">
+      Functions From utf8::unchecked Namespace
+    </h3>
+    <h4>
+      utf8::unchecked::append
+    </h4>
+    <p class="version">
+    Available in version 1.0 and later.
+    </p>
+    <p>
+      Encodes a 32 bit code point as a UTF-8 sequence of octets and appends the sequence
+      to a UTF-8 string.
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+octet_iterator append(uint32_t cp, octet_iterator result);
+   
+</pre>
+    <p>
+      <code>cp</code>: A 32 bit integer representing a code point to append to the
+      sequence.<br>
+       <code>result</code>: An output iterator to the place in the sequence where to
+      append the code point.<br>
+       <span class="return_value">Return value</span>: An iterator pointing to the place
+      after the newly appended sequence.
+    </p>
+    <p>
+      Example of use:
+    </p>
+<pre>
+<span class="keyword">unsigned char</span> u[<span class="literal">5</span>] = {<span
+class="literal">0</span>,<span class="literal">0</span>,<span class=
+"literal">0</span>,<span class="literal">0</span>,<span class="literal">0</span>};
+<span class="keyword">unsigned char</span>* end = unchecked::append(<span class=
+"literal">0x0448</span>, u);
+assert (u[<span class="literal">0</span>] == <span class=
+"literal">0xd1</span> &amp;&amp; u[<span class="literal">1</span>] == <span class=
+"literal">0x88</span> &amp;&amp; u[<span class="literal">2</span>] == <span class=
+"literal">0</span> &amp;&amp; u[<span class="literal">3</span>] == <span class=
+"literal">0</span> &amp;&amp; u[<span class="literal">4</span>] == <span class=
+"literal">0</span>);
+</pre>
+    <p>
+      This is a faster but less safe version of <code>utf8::append</code>. It does not
+      check for validity of the supplied code point, and may produce an invalid UTF-8
+      sequence.
+    </p>
+    <h4>
+      utf8::unchecked::next
+    </h4>
+    <p class="version">
+    Available in version 1.0 and later.
+    </p>
+    <p>
+      Given the iterator to the beginning of a UTF-8 sequence, it returns the code point
+      and moves the iterator to the next position.
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+uint32_t next(octet_iterator&amp; it);
+   
+</pre>
+    <p>
+      <code>it</code>: 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.<br>
+       <span class="return_value">Return value</span>: the 32 bit representation of the
+      processed UTF-8 code point.
+    </p>
+    <p>
+      Example of use:
+    </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">char</span>* w = twochars;
+<span class="keyword">int</span> cp = unchecked::next(w);
+assert (cp == <span class="literal">0x65e5</span>);
+assert (w == twochars + <span class="literal">3</span>);
+</pre>
+    <p>
+      This is a faster but less safe version of <code>utf8::next</code>. It does not
+      check for validity of the supplied UTF-8 sequence.
+    </p>
+    <h4>
+      utf8::unchecked::peek_next
+    </h4>
+    <p class="version">
+    Available in version 2.1 and later.
+    </p>
+    <p>
+      Given the iterator to the beginning of a UTF-8 sequence, it returns the code point.
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+uint32_t peek_next(octet_iterator it);
+   
+</pre>
+    <p>
+      <code>it</code>: an iterator pointing to the beginning of an UTF-8
+      encoded code point.<br>
+       <span class="return_value">Return value</span>: the 32 bit representation of the
+      processed UTF-8 code point.
+    </p>
+    <p>
+      Example of use:
+    </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">char</span>* w = twochars;
+<span class="keyword">int</span> cp = unchecked::peek_next(w);
+assert (cp == <span class="literal">0x65e5</span>);
+assert (w == twochars);
+</pre>
+    <p>
+      This is a faster but less safe version of <code>utf8::peek_next</code>. It does not
+      check for validity of the supplied UTF-8 sequence.
+    </p>
+    <h4>
+      utf8::unchecked::prior
+    </h4>
+    <p class="version">
+    Available in version 1.02 and later.
+    </p>
+    <p>
+      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.
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+uint32_t prior(octet_iterator&amp; it);
+   
+</pre>
+    <p>
+      <code>it</code>: 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.<br>
+       <span class="return_value">Return value</span>: the 32 bit representation of the
+      previous code point.
+    </p>
+    <p>
+      Example of use:
+    </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">char</span>* w = twochars + <span class="literal">3</span>;
+<span class="keyword">int</span> cp = unchecked::prior (w);
+assert (cp == <span class="literal">0x65e5</span>);
+assert (w == twochars);
+</pre>
+    <p>
+      This is a faster but less safe version of <code>utf8::prior</code>. It does not
+      check for validity of the supplied UTF-8 sequence and offers no boundary checking.
+    </p>
+    <h4>
+      utf8::unchecked::previous (deprecated, see utf8::unchecked::prior)
+    </h4>
+    <p class="version">
+    Deprecated in version 1.02 and later.
+    </p>
+    <p>
+      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.
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+uint32_t previous(octet_iterator&amp; it);
+   
+</pre>
+    <p>
+      <code>it</code>: 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.<br>
+       <span class="return_value">Return value</span>: the 32 bit representation of the
+      previous code point.
+    </p>
+    <p>
+      Example of use:
+    </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">char</span>* w = twochars + <span class="literal">3</span>;
+<span class="keyword">int</span> cp = unchecked::previous (w);
+assert (cp == <span class="literal">0x65e5</span>);
+assert (w == twochars);
+</pre>
+    <p>
+     The reason this function is deprecated is just the consistency with the "checked"
+     versions, where <code>prior</code> should be used instead of <code>previous</code>.
+     In fact, <code>unchecked::previous</code> behaves exactly the same as <code>
+     unchecked::prior</code>
+    </p>
+    <p>
+      This is a faster but less safe version of <code>utf8::previous</code>. It does not
+      check for validity of the supplied UTF-8 sequence and offers no boundary checking.
+    </p>
+    <h4>
+      utf8::unchecked::advance
+    </h4>
+    <p class="version">
+    Available in version 1.0 and later.
+    </p>
+    <p>
+      Advances an iterator by the specified number of code points within an UTF-8
+      sequence.
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator, typename distance_type&gt;
+<span class="keyword">void</span> advance (octet_iterator&amp; it, distance_type n);
+   
+</pre>
+    <p>
+      <code>it</code>: 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.<br>
+       <code>n</code>: a positive integer that shows how many code points we want to
+      advance.<br>
+    </p>
+    <p>
+      Example of use:
+    </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+<span class="keyword">char</span>* w = twochars;
+unchecked::advance (w, <span class="literal">2</span>);
+assert (w == twochars + <span class="literal">5</span>);
+</pre>
+    <p>
+      This function works only "forward". In case of a negative <code>n</code>, there is
+      no effect.
+    </p>
+    <p>
+      This is a faster but less safe version of <code>utf8::advance</code>. It does not
+      check for validity of the supplied UTF-8 sequence and offers no boundary checking.
+    </p>
+    <h4>
+      utf8::unchecked::distance
+    </h4>
+    <p class="version">
+    Available in version 1.0 and later.
+    </p>
+    <p>
+      Given the iterators to two UTF-8 encoded code points in a seqence, returns the
+      number of code points between them.
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator&gt;
+<span class=
+"keyword">typename</span> std::iterator_traits&lt;octet_iterator&gt;::difference_type distance (octet_iterator first, octet_iterator last);
+</pre>
+    <p>
+      <code>first</code>: an iterator to a beginning of a UTF-8 encoded code point.<br>
+       <code>last</code>: 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.<br>
+       <span class="return_value">Return value</span> the distance between the iterators,
+      in code points.
+    </p>
+    <p>
+      Example of use:
+    </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+size_t dist = utf8::unchecked::distance(twochars, twochars + <span class=
+"literal">5</span>);
+assert (dist == <span class="literal">2</span>);
+</pre>
+    <p>
+      This is a faster but less safe version of <code>utf8::distance</code>. It does not
+      check for validity of the supplied UTF-8 sequence.
+    </p>
+    <h4>
+      utf8::unchecked::utf16to8
+    </h4>
+    <p class="version">
+    Available in version 1.0 and later.
+    </p>
+    <p>
+      Converts a UTF-16 encoded string to UTF-8.
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> u16bit_iterator, <span class=
+"keyword">typename</span> octet_iterator&gt;
+octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result);
+   
+</pre>
+    <p>
+      <code>start</code>: an iterator pointing to the beginning of the UTF-16 encoded
+      string to convert.<br>
+       <code>end</code>: an iterator pointing to pass-the-end of the UTF-16 encoded
+      string to convert.<br>
+       <code>result</code>: an output iterator to the place in the UTF-8 string where to
+      append the result of conversion.<br>
+       <span class="return_value">Return value</span>: An iterator pointing to the place
+      after the appended UTF-8 string.
+    </p>
+    <p>
+      Example of use:
+    </p>
+<pre>
+<span class="keyword">unsigned short</span> utf16string[] = {<span class=
+"literal">0x41</span>, <span class="literal">0x0448</span>, <span class=
+"literal">0x65e5</span>, <span class="literal">0xd834</span>, <span class=
+"literal">0xdd1e</span>};
+vector&lt;<span class="keyword">unsigned char</span>&gt; utf8result;
+unchecked::utf16to8(utf16string, utf16string + <span class=
+"literal">5</span>, back_inserter(utf8result));
+assert (utf8result.size() == <span class="literal">10</span>);    
+</pre>
+    <p>
+      This is a faster but less safe version of <code>utf8::utf16to8</code>. It does not
+      check for validity of the supplied UTF-16 sequence.
+    </p>
+    <h4>
+      utf8::unchecked::utf8to16
+    </h4>
+    <p class="version">
+    Available in version 1.0 and later.
+    </p>
+    <p>
+      Converts an UTF-8 encoded string to UTF-16
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> u16bit_iterator, typename octet_iterator&gt;
+u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result);
+   
+</pre>
+    <p>
+      <code>start</code>: an iterator pointing to the beginning of the UTF-8 encoded
+      string to convert. &lt; br /&gt; <code>end</code>: an iterator pointing to
+      pass-the-end of the UTF-8 encoded string to convert.<br>
+       <code>result</code>: an output iterator to the place in the UTF-16 string where to
+      append the result of conversion.<br>
+       <span class="return_value">Return value</span>: An iterator pointing to the place
+      after the appended UTF-16 string.
+    </p>
+    <p>
+      Example of use:
+    </p>
+<pre>
+<span class="keyword">char</span> utf8_with_surrogates[] = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e"</span>;
+vector &lt;<span class="keyword">unsigned short</span>&gt; utf16result;
+unchecked::utf8to16(utf8_with_surrogates, utf8_with_surrogates + <span class=
+"literal">9</span>, back_inserter(utf16result));
+assert (utf16result.size() == <span class="literal">4</span>);
+assert (utf16result[<span class="literal">2</span>] == <span class=
+"literal">0xd834</span>);
+assert (utf16result[<span class="literal">3</span>] == <span class=
+"literal">0xdd1e</span>);
+</pre>
+    <p>
+      This is a faster but less safe version of <code>utf8::utf8to16</code>. It does not
+      check for validity of the supplied UTF-8 sequence.
+    </p>
+    <h4>
+      utf8::unchecked::utf32to8
+    </h4>
+    <p class="version">
+    Available in version 1.0 and later.
+    </p>
+    <p>
+      Converts a UTF-32 encoded string to UTF-8.
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator, <span class=
+"keyword">typename</span> u32bit_iterator&gt;
+octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result);
+   
+</pre>
+    <p>
+      <code>start</code>: an iterator pointing to the beginning of the UTF-32 encoded
+      string to convert.<br>
+       <code>end</code>: an iterator pointing to pass-the-end of the UTF-32 encoded
+      string to convert.<br>
+       <code>result</code>: an output iterator to the place in the UTF-8 string where to
+      append the result of conversion.<br>
+       <span class="return_value">Return value</span>: An iterator pointing to the place
+      after the appended UTF-8 string.
+    </p>
+    <p>
+      Example of use:
+    </p>
+<pre>
+<span class="keyword">int</span> utf32string[] = {<span class=
+"literal">0x448</span>, <span class="literal">0x65e5</span>, <span class=
+"literal">0x10346</span>, <span class="literal">0</span>};
+vector&lt;<span class="keyword">unsigned char</span>&gt; utf8result;
+utf32to8(utf32string, utf32string + <span class=
+"literal">3</span>, back_inserter(utf8result));
+assert (utf8result.size() == <span class="literal">9</span>);
+</pre>
+    <p>
+      This is a faster but less safe version of <code>utf8::utf32to8</code>. It does not
+      check for validity of the supplied UTF-32 sequence.
+    </p>
+    <h4>
+      utf8::unchecked::utf8to32
+    </h4>
+    <p class="version">
+    Available in version 1.0 and later.
+    </p>
+    <p>
+      Converts a UTF-8 encoded string to UTF-32.
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class=
+"keyword">typename</span> octet_iterator, typename u32bit_iterator&gt;
+u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result);
+   
+</pre>
+    <p>
+      <code>start</code>: an iterator pointing to the beginning of the UTF-8 encoded
+      string to convert.<br>
+       <code>end</code>: an iterator pointing to pass-the-end of the UTF-8 encoded string
+      to convert.<br>
+       <code>result</code>: an output iterator to the place in the UTF-32 string where to
+      append the result of conversion.<br>
+       <span class="return_value">Return value</span>: An iterator pointing to the place
+      after the appended UTF-32 string.
+    </p>
+    <p>
+      Example of use:
+    </p>
+<pre>
+<span class="keyword">char</span>* twochars = <span class=
+"literal">"\xe6\x97\xa5\xd1\x88"</span>;
+vector&lt;<span class="keyword">int</span>&gt; utf32result;
+unchecked::utf8to32(twochars, twochars + <span class=
+"literal">5</span>, back_inserter(utf32result));
+assert (utf32result.size() == <span class="literal">2</span>);
+</pre>
+    <p>
+      This is a faster but less safe version of <code>utf8::utf8to32</code>. It does not
+      check for validity of the supplied UTF-8 sequence.
+    </p>
+    <h3 id="typesunchecked">
+      Types From utf8::unchecked Namespace
+    </h3>
+    <h4>
+      utf8::iterator
+    </h4>
+    <p class="version">
+    Available in version 2.0 and later.
+    </p>
+    <p>
+      Adapts the underlying octet iterator to iterate over the sequence of code points,
+      rather than raw octets.
+    </p>
+<pre>
+<span class="keyword">template</span> &lt;<span class="keyword">typename</span> octet_iterator&gt;
+<span class="keyword">class</span> iterator;
+</pre>
+    
+    <h5>Member functions</h5>
+      <dl>
+      <dt><code>iterator();</code> <dd> the deafult constructor; the underlying <code>octet_iterator</code> is
+      constructed with its default constructor.
+      <dt><code><span class="keyword">explicit</span> iterator (const octet_iterator&amp; octet_it); 
+                         </code> <dd> a constructor 
+      that initializes the underlying <code>octet_iterator</code> with <code>octet_it</code>
+      <dt><code>octet_iterator base () <span class="keyword">const</span>;</code> <dd> returns the 
+      underlying <code>octet_iterator</code>.
+      <dt><code>uint32_t operator * () <span class="keyword">const</span>;</code> <dd> decodes the utf-8 sequence
+      the underlying <code>octet_iterator</code> is pointing to and returns the code point.
+      <dt><code><span class="keyword">bool operator</span> == (const iterator&amp; rhs)
+      <span class="keyword">const</span>;</code> <dd> returns <span class="keyword">true</span>
+      if the two underlaying iterators are equal.
+      <dt><code><span class="keyword">bool operator</span> != (const iterator&amp; rhs)
+      <span class="keyword">const</span>;</code> <dd> returns <span class="keyword">true</span>
+      if the two underlaying iterators are not equal.
+      <dt><code>iterator&amp; <span class="keyword">operator</span> ++ (); </code> <dd> the prefix increment - moves
+      the iterator to the next UTF-8 encoded code point.
+      <dt><code>iterator <span class="keyword">operator</span> ++ (<span class="keyword">int</span>); </code> <dd>
+      the postfix increment - moves the iterator to the next UTF-8 encoded code point and returns the current one.
+      <dt><code>iterator&amp; <span class="keyword">operator</span> -- (); </code> <dd> the prefix decrement - moves
+      the iterator to the previous UTF-8 encoded code point.
+      <dt><code>iterator <span class="keyword">operator</span> -- (<span class="keyword">int</span>); </code> <dd>
+      the postfix decrement - moves the iterator to the previous UTF-8 encoded code point and returns the current one.
+      </dl>
+      <p>
+      Example of use:
+      </p>
+<pre>
+<span class="keyword">char</span>* threechars = <span class="literal">"\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"</span>;
+utf8::unchecked::iterator&lt;<span class="keyword">char</span>*&gt; un_it(threechars);
+utf8::unchecked::iterator&lt;<span class="keyword">char</span>*&gt; un_it2 = un_it;
+assert (un_it2 == un_it);
+assert (*un_it == <span class="literal">0x10346</span>);
+assert (*(++un_it) == <span class="literal">0x65e5</span>);
+assert ((*un_it++) == <span class="literal">0x65e5</span>);
+assert (*un_it == <span class="literal">0x0448</span>);
+assert (un_it != un_it2);
+utf8::::unchecked::iterator&lt;<span class="keyword">char</span>*&gt; un_endit (threechars + <span class="literal">9</span>);  
+assert (++un_it == un_endit);
+assert (*(--un_it) == <span class="literal">0x0448</span>);
+assert ((*un_it--) == <span class="literal">0x0448</span>);
+assert (*un_it == <span class="literal">0x65e5</span>);
+assert (--un_it == utf8::unchecked::iterator&lt;<span class="keyword">char</span>*&gt;(threechars));
+assert (*un_it == <span class="literal">0x10346</span>);
+</pre>
+      <p>
+      This is an unchecked version of <code>utf8::iterator</code>. It is faster in many cases, but offers
+      no validity or range checks.
+      </p>
+    <h2 id="points">
+      Points of interest
+    </h2>
+    <h4>
+      Design goals and decisions
+    </h4>
+    <p>
+      The library was designed to be:
+    </p>
+    <ol>
+      <li>
+        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.
+      </li>
+      <li>
+        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.
+      </li>
+      <li>
+        Lightweight: follow the "pay only for what you use" guideline.
+      </li>
+      <li>
+        Unintrusive: avoid forcing any particular design or even programming style on the
+        user. This is a library, not a framework.
+      </li>
+    </ol>
+    <h4>
+      Alternatives
+    </h4>
+    <p>
+      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:
+    </p>
+    <ol>
+      <li>
+        <a href="http://icu.sourceforge.net/">ICU Library</a>. 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.
+      </li>
+      <li>
+        C++11 language and library features. Still far from complete, and not widely
+        supported by compiler vendors. 
+      </li>
+      <li>
+        <a href=
+        "http://www.gtkmm.org/gtkmm2/docs/tutorial/html/ch03s04.html">Glib::ustring</a>.
+        A class specifically made to work with UTF-8 strings, and also feel like
+        <code>std::string</code>. 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.
+      </li>
+      <li>
+        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.
+      </li>
+    </ol>
+    <h2 id="links">
+      Links
+    </h2>
+    <ol>
+      <li>
+        <a href="http://www.unicode.org/">The Unicode Consortium</a>.
+      </li>
+      <li>
+        <a href="http://icu.sourceforge.net/">ICU Library</a>.
+      </li>
+      <li>
+        <a href="http://en.wikipedia.org/wiki/UTF-8">UTF-8 at Wikipedia</a>
+      </li>
+      <li>
+        <a href="http://www.cl.cam.ac.uk/~mgk25/unicode.html">UTF-8 and Unicode FAQ for
+        Unix/Linux</a>
+      </li>
+    </ol>
+  </body>
+</html>

+ 0 - 8
thirdparty/assimp/include/assimp/.editorconfig

@@ -1,8 +0,0 @@
-# See <http://EditorConfig.org> for details
-
-[*.{h,hpp,inl}]
-end_of_line = lf
-insert_final_newline = true
-trim_trailing_whitespace = true
-indent_size = 4
-indent_style = space

+ 59 - 2
thirdparty/assimp/include/assimp/BaseImporter.h

@@ -48,8 +48,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 #include <vector>
 #include <vector>
 #include <set>
 #include <set>
+#include <map>
 #include <assimp/types.h>
 #include <assimp/types.h>
 #include <assimp/ProgressHandler.hpp>
 #include <assimp/ProgressHandler.hpp>
+#include <assimp/ai_assert.h>
 
 
 struct aiScene;
 struct aiScene;
 struct aiImporterDesc;
 struct aiImporterDesc;
@@ -80,6 +82,10 @@ class IOStream;
 class ASSIMP_API BaseImporter {
 class ASSIMP_API BaseImporter {
     friend class Importer;
     friend class Importer;
 
 
+private:
+    /* Pushes state into importer for the importer scale */
+    virtual void UpdateImporterScale( Importer* pImp );
+
 public:
 public:
 
 
     /** Constructor to be privately used by #Importer */
     /** Constructor to be privately used by #Importer */
@@ -132,7 +138,7 @@ public:
      *  a suitable response to the caller.
      *  a suitable response to the caller.
      */
      */
     aiScene* ReadFile(
     aiScene* ReadFile(
-        const Importer* pImp,
+        Importer* pImp,
         const std::string& pFile,
         const std::string& pFile,
         IOSystem* pIOHandler
         IOSystem* pIOHandler
         );
         );
@@ -161,14 +167,65 @@ public:
      *  some loader features. Importers must provide this information. */
      *  some loader features. Importers must provide this information. */
     virtual const aiImporterDesc* GetInfo() const = 0;
     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, double> 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.
     /** Called by #Importer::GetExtensionList for each loaded importer.
      *  Take the extension list contained in the structure returned by
      *  Take the extension list contained in the structure returned by
      *  #GetInfo and insert all file extensions into the given set.
      *  #GetInfo and insert all file extensions into the given set.
      *  @param extension set to collect file extensions in*/
      *  @param extension set to collect file extensions in*/
     void GetExtensionList(std::set<std::string>& extensions);
     void GetExtensionList(std::set<std::string>& 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
     /** Imports the given file into the given scene structure. The

+ 8 - 1
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
 /** @brief  Specifies the maximum angle that may be between two vertex tangents
  *         that their tangents and bi-tangents are smoothed.
  *         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.
  * in degrees. The maximum value is 175.
  * Property type: float. Default value: 45 degrees
  * Property type: float. Default value: 45 degrees
  */
  */
@@ -999,6 +999,13 @@ enum aiComponent
 #   define AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT  1.0f
 #   define AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT  1.0f
 #endif // !! AI_DEBONE_THRESHOLD
 #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 ------------
 // ---------- All the Build/Compile-time defines ------------
 
 
 /** @brief Specifies if double precision is supported inside assimp
 /** @brief Specifies if double precision is supported inside assimp

+ 0 - 144
thirdparty/assimp/include/assimp/irrXMLWrapper.h

@@ -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 <irrXML.h>
-#include "IOStream.hpp"
-#include "BaseImporter.h"
-#include <vector>
-
-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<IOStream> file( pIOHandler->Open( pFile));
- * if( file.get() == NULL) {
- *    throw DeadlyImportError( "Failed to open file " + pFile + ".");
- * }
- *
- * // generate a XML reader for it
- * std::unique_ptr<CIrrXML_IOStreamReader> 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<unsigned int>(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<int>(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<char> data;
-    size_t t;
-
-}; // ! class CIrrXML_IOStreamReader
-
-} // ! Assimp
-
-#endif // !! INCLUDED_AI_IRRXML_WRAPPER

+ 1 - 1
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 "material.h"
 #include "anim.h"
 #include "anim.h"
 #include "metadata.h"
 #include "metadata.h"
-#include <cstdlib>
 
 
 #ifdef __cplusplus
 #ifdef __cplusplus
+#  include <cstdlib>
 extern "C" {
 extern "C" {
 #endif
 #endif
 
 

Some files were not shown because too many files changed in this diff