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 <ernest.lee@chibifire.com>
This commit is contained in:
Gordon MacPherson 2019-08-30 02:21:40 +01:00
parent a5e0aa32d9
commit ad214c0356
22 changed files with 3267 additions and 1504 deletions

File diff suppressed because it is too large Load diff

View file

@ -44,60 +44,31 @@
#include "scene/resources/animation.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 {
public:
// Constructor
AssimpStream();
AssimpStream() {}
// Destructor
~AssimpStream();
~AssimpStream() {}
// 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 {
private:
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 {
uint32_t bone;
float weight;
@ -177,28 +95,29 @@ private:
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 _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 _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_raw_string_to_string(const aiString &p_string) const;
@ -228,7 +147,7 @@ public:
virtual void get_extensions(List<String> *r_extensions) 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 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

View file

@ -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

View file

@ -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

View file

@ -983,6 +983,13 @@ enum aiComponent {
#define AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT 1.0f
#endif // !! AI_DEBONE_THRESHOLD
#define AI_CONFIG_APP_SCALE_KEY "APP_SCALE_FACTOR"
#if (!defined AI_CONFIG_APP_SCALE_KEY)
# define AI_CONFIG_APP_SCALE_KEY 1.0
#endif // AI_CONFIG_APP_SCALE_KEY
// ---------- All the Build/Compile-time defines ------------
/** @brief Specifies if double precision is supported inside assimp

View file

@ -76,9 +76,25 @@ BaseImporter::~BaseImporter() {
// nothing to do here
}
void BaseImporter::UpdateImporterScale( Importer* pImp )
{
ai_assert(pImp != nullptr);
ai_assert(importerScale != 0.0);
ai_assert(fileScale != 0.0);
double activeScale = importerScale * fileScale;
// Set active scaling
pImp->SetPropertyFloat( AI_CONFIG_APP_SCALE_KEY, activeScale);
ASSIMP_LOG_DEBUG_F("UpdateImporterScale scale set: %f", activeScale );
}
// ------------------------------------------------------------------------------------------------
// Imports the given file and returns the imported data.
aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile, IOSystem* pIOHandler) {
aiScene* BaseImporter::ReadFile(Importer* pImp, const std::string& pFile, IOSystem* pIOHandler) {
m_progress = pImp->GetProgressHandler();
if (nullptr == m_progress) {
return nullptr;
@ -100,6 +116,11 @@ aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile,
{
InternReadFile( pFile, sc.get(), &filter);
// Calculate import scale hook - required because pImp not available anywhere else
// passes scale into ScaleProcess
UpdateImporterScale(pImp);
} catch( const std::exception& err ) {
// extract error description
m_ErrorText = err.what();
@ -112,7 +133,7 @@ aiScene* BaseImporter::ReadFile(const Importer* pImp, const std::string& pFile,
}
// ------------------------------------------------------------------------------------------------
void BaseImporter::SetupProperties(const Importer* /*pImp*/)
void BaseImporter::SetupProperties(const Importer* pImp)
{
// the default implementation does nothing
}
@ -588,6 +609,8 @@ aiScene* BatchLoader::GetImport( unsigned int which )
return nullptr;
}
// ------------------------------------------------------------------------------------------------
void BatchLoader::LoadAll()
{

View file

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

View file

@ -430,10 +430,6 @@ private:
void ConvertGlobalSettings();
// ------------------------------------------------------------------------------------------------
// Will perform the conversion from a given unit to the requested unit.
void ConvertToUnitScale(FbxUnit unit);
// ------------------------------------------------------------------------------------------------
// copy generated meshes, animations, lights, cameras and textures to the output scene
void TransferDataToScene();
@ -470,9 +466,6 @@ private:
aiScene* const out;
const FBX::Document& doc;
bool mRemoveEmptyBones;
FbxUnit mCurrentUnit;
};

View file

@ -90,14 +90,6 @@ const Object* LazyObject::Get(bool dieOnError)
return object.get();
}
// if this is the root object, we return a dummy since there
// is no root object int he fbx file - it is just referenced
// with id 0.
if(id == 0L) {
object.reset(new Object(id, element, "Model::RootNode"));
return object.get();
}
const Token& key = element.KeyToken();
const TokenList& tokens = element.Tokens();

View file

@ -1706,8 +1706,7 @@ void FBXExporter::WriteObjects ()
}
if (end) { break; }
}
limbnodes.insert(parent);
skeleton.insert(parent);
// if it was the skeleton root we can finish here
if (end) { break; }
}
@ -1848,44 +1847,10 @@ void FBXExporter::WriteObjects ()
inverse_bone_xform.Inverse();
aiMatrix4x4 tr = inverse_bone_xform * mesh_xform;
// this should be the same as the bone's mOffsetMatrix.
// if it's not the same, the skeleton isn't in the bind pose.
float epsilon = 1e-4f; // some error is to be expected
float epsilon_custom = mProperties->GetPropertyFloat("BINDPOSE_EPSILON", -1);
if(epsilon_custom > 0)
epsilon = epsilon_custom;
bool bone_xform_okay = true;
if (b && ! tr.Equal(b->mOffsetMatrix, epsilon)) {
not_in_bind_pose.insert(b);
bone_xform_okay = false;
}
sdnode.AddChild("Transform", tr);
// if we have a bone we should use the mOffsetMatrix,
// otherwise try to just use the calculated transform.
if (b) {
sdnode.AddChild("Transform", b->mOffsetMatrix);
} else {
sdnode.AddChild("Transform", tr);
}
// note: it doesn't matter if we mix these,
// because if they disagree we'll throw an exception later.
// it could be that the skeleton is not in the bone pose
// but all bones are still defined,
// in which case this would use the mOffsetMatrix for everything
// and a correct skeleton would still be output.
// transformlink should be the position of the bone in world space.
// if the bone is in the bind pose (or nonexistent),
// we can just use the matrix we already calculated
if (bone_xform_okay) {
sdnode.AddChild("TransformLink", bone_xform);
// otherwise we can only work it out using the mesh position.
} else {
aiMatrix4x4 trl = b->mOffsetMatrix;
trl.Inverse();
trl *= mesh_xform;
sdnode.AddChild("TransformLink", trl);
}
sdnode.AddChild("TransformLink", bone_xform);
// note: this means we ALWAYS rely on the mesh node transform
// being unchanged from the time the skeleton was bound.
// there's not really any way around this at the moment.

View file

@ -189,8 +189,16 @@ void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOS
if (settings.convertToMeters) {
unit = FbxUnit::m;
}
// convert the FBX DOM to aiScene
ConvertToAssimpScene(pScene,doc, settings.removeEmptyBones, unit);
ConvertToAssimpScene(pScene, doc, settings.removeEmptyBones, unit);
// size relative to cm
float size_relative_to_cm = doc.GlobalSettings().UnitScaleFactor();
// Set FBX file scale is relative to CM must be converted to M for
// assimp universal format (M)
SetFileScale( size_relative_to_cm * 0.01f);
std::for_each(tokens.begin(),tokens.end(),Util::delete_fun<Token>());
}

View file

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

View file

@ -212,7 +212,7 @@ bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
// project tangent and bitangent into the plane formed by the vertex' normal
aiVector3D localTangent = tangent - meshNorm[p] * (tangent * meshNorm[p]);
aiVector3D localBitangent = bitangent - meshNorm[p] * (bitangent * meshNorm[p]);
localTangent.Normalize(); localBitangent.Normalize();
localTangent.NormalizeSafe(); localBitangent.NormalizeSafe();
// reconstruct tangent/bitangent according to normal and bitangent/tangent when it's infinite or NaN.
bool invalid_tangent = is_special_float(localTangent.x) || is_special_float(localTangent.y) || is_special_float(localTangent.z);
@ -220,10 +220,10 @@ bool CalcTangentsProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
if (invalid_tangent != invalid_bitangent) {
if (invalid_tangent) {
localTangent = meshNorm[p] ^ localBitangent;
localTangent.Normalize();
localTangent.NormalizeSafe();
} else {
localBitangent = localTangent ^ meshNorm[p];
localBitangent.Normalize();
localBitangent.NormalizeSafe();
}
}

View file

@ -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 <assimp/scene.h>
#include <assimp/postprocess.h>
#include <assimp/BaseImporter.h>
namespace Assimp {
ScaleProcess::ScaleProcess()
: BaseProcess()
, mScale( AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT ) {
// empty
}
ScaleProcess::~ScaleProcess() {
@ -71,10 +69,26 @@ bool ScaleProcess::IsActive( unsigned int pFlags ) const {
}
void ScaleProcess::SetupProperties( const Importer* pImp ) {
mScale = pImp->GetPropertyFloat( AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY, 0 );
// User scaling
mScale = pImp->GetPropertyFloat( AI_CONFIG_GLOBAL_SCALE_FACTOR_KEY, 1.0f );
// File scaling * Application Scaling
float importerScale = pImp->GetPropertyFloat( AI_CONFIG_APP_SCALE_KEY, 1.0f );
// apply scale to the scale
// helps prevent bugs with backward compatibility for anyone using normal scaling.
mScale *= importerScale;
}
void ScaleProcess::Execute( aiScene* pScene ) {
if(mScale == 1.0f) {
return; // nothing to scale
}
ai_assert( mScale != 0 );
ai_assert( nullptr != pScene );
ai_assert( nullptr != pScene->mRootNode );
if ( nullptr == pScene ) {
return;
}
@ -82,22 +96,113 @@ void ScaleProcess::Execute( aiScene* pScene ) {
if ( nullptr == pScene->mRootNode ) {
return;
}
// Process animations and update position transform to new unit system
for( unsigned int animationID = 0; animationID < pScene->mNumAnimations; animationID++ )
{
aiAnimation* animation = pScene->mAnimations[animationID];
for( unsigned int animationChannel = 0; animationChannel < animation->mNumChannels; animationChannel++)
{
aiNodeAnim* anim = animation->mChannels[animationChannel];
for( unsigned int posKey = 0; posKey < anim->mNumPositionKeys; posKey++)
{
aiVectorKey& vectorKey = anim->mPositionKeys[posKey];
vectorKey.mValue *= mScale;
}
}
}
for( unsigned int meshID = 0; meshID < pScene->mNumMeshes; meshID++)
{
aiMesh *mesh = pScene->mMeshes[meshID];
// Reconstruct mesh vertexes to the new unit system
for( unsigned int vertexID = 0; vertexID < mesh->mNumVertices; vertexID++)
{
aiVector3D& vertex = mesh->mVertices[vertexID];
vertex *= mScale;
}
// bone placement / scaling
for( unsigned int boneID = 0; boneID < mesh->mNumBones; boneID++)
{
// Reconstruct matrix by transform rather than by scale
// This prevent scale values being changed which can
// be meaningful in some cases
// like when you want the modeller to see 1:1 compatibility.
aiBone* bone = mesh->mBones[boneID];
aiVector3D pos, scale;
aiQuaternion rotation;
bone->mOffsetMatrix.Decompose( scale, rotation, pos);
aiMatrix4x4 translation;
aiMatrix4x4::Translation( pos * mScale, translation );
aiMatrix4x4 scaling;
aiMatrix4x4::Scaling( aiVector3D(scale), scaling );
aiMatrix4x4 RotMatrix = aiMatrix4x4 (rotation.GetMatrix());
bone->mOffsetMatrix = translation * RotMatrix * scaling;
}
// animation mesh processing
// convert by position rather than scale.
for( unsigned int animMeshID = 0; animMeshID < mesh->mNumAnimMeshes; animMeshID++)
{
aiAnimMesh * animMesh = mesh->mAnimMeshes[animMeshID];
for( unsigned int vertexID = 0; vertexID < animMesh->mNumVertices; vertexID++)
{
aiVector3D& vertex = animMesh->mVertices[vertexID];
vertex *= mScale;
}
}
}
traverseNodes( pScene->mRootNode );
}
void ScaleProcess::traverseNodes( aiNode *node ) {
void ScaleProcess::traverseNodes( aiNode *node, unsigned int nested_node_id ) {
applyScaling( node );
for( size_t i = 0; i < node->mNumChildren; i++)
{
// recurse into the tree until we are done!
traverseNodes( node->mChildren[i], nested_node_id+1 );
}
}
void ScaleProcess::applyScaling( aiNode *currentNode ) {
if ( nullptr != currentNode ) {
currentNode->mTransformation.a1 = currentNode->mTransformation.a1 * mScale;
currentNode->mTransformation.b2 = currentNode->mTransformation.b2 * mScale;
currentNode->mTransformation.c3 = currentNode->mTransformation.c3 * mScale;
// Reconstruct matrix by transform rather than by scale
// This prevent scale values being changed which can
// be meaningful in some cases
// like when you want the modeller to
// see 1:1 compatibility.
aiVector3D pos, scale;
aiQuaternion rotation;
currentNode->mTransformation.Decompose( scale, rotation, pos);
aiMatrix4x4 translation;
aiMatrix4x4::Translation( pos * mScale, translation );
aiMatrix4x4 scaling;
// note: we do not use mScale here, this is on purpose.
aiMatrix4x4::Scaling( scale, scaling );
aiMatrix4x4 RotMatrix = aiMatrix4x4 (rotation.GetMatrix());
currentNode->mTransformation = translation * RotMatrix * scaling;
}
}
} // Namespace Assimp
#endif // !! ASSIMP_BUILD_NO_GLOBALSCALE_PROCESS

View file

@ -39,7 +39,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
#pragma once
#ifndef SCALE_PROCESS_H_
#define SCALE_PROCESS_H_
#include "Common/BaseProcess.h"
@ -53,6 +54,11 @@ namespace Assimp {
// ---------------------------------------------------------------------------
/** ScaleProcess: Class to rescale the whole model.
* Now rescales animations, bones, and blend shapes properly.
* Please note this will not write to 'scale' transform it will rewrite mesh
* and matrixes so that your scale values
* from your model package are preserved, so this is completely intentional
* bugs should be reported as soon as they are found.
*/
class ASSIMP_API ScaleProcess : public BaseProcess {
public:
@ -78,7 +84,7 @@ public:
virtual void Execute( aiScene* pScene );
private:
void traverseNodes( aiNode *currentNode );
void traverseNodes( aiNode *currentNode, unsigned int nested_node_id = 0 );
void applyScaling( aiNode *currentNode );
private:
@ -86,3 +92,6 @@ private:
};
} // Namespace Assimp
#endif // SCALE_PROCESS_H_

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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

View file

@ -48,8 +48,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <vector>
#include <set>
#include <map>
#include <assimp/types.h>
#include <assimp/ProgressHandler.hpp>
#include <assimp/ai_assert.h>
struct aiScene;
struct aiImporterDesc;
@ -80,6 +82,10 @@ class IOStream;
class ASSIMP_API BaseImporter {
friend class Importer;
private:
/* Pushes state into importer for the importer scale */
virtual void UpdateImporterScale( Importer* pImp );
public:
/** Constructor to be privately used by #Importer */
@ -132,7 +138,7 @@ public:
* a suitable response to the caller.
*/
aiScene* ReadFile(
const Importer* pImp,
Importer* pImp,
const std::string& pFile,
IOSystem* pIOHandler
);
@ -161,14 +167,65 @@ public:
* some loader features. Importers must provide this information. */
virtual const aiImporterDesc* GetInfo() const = 0;
/**
* Will be called only by scale process when scaling is requested.
*/
virtual void SetFileScale(double scale)
{
fileScale = scale;
}
virtual double GetFileScale() const
{
return fileScale;
}
enum ImporterUnits {
M,
MM,
CM,
INCHES,
FEET
};
/**
* Assimp Importer
* unit conversions available
* if you need another measurment unit add it below.
* it's currently defined in assimp that we prefer meters.
* */
std::map<ImporterUnits, 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.
* Take the extension list contained in the structure returned by
* #GetInfo and insert all file extensions into the given set.
* @param extension set to collect file extensions in*/
void GetExtensionList(std::set<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

View file

@ -142,7 +142,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/** @brief Specifies the maximum angle that may be between two vertex tangents
* that their tangents and bi-tangents are smoothed.
*
* This applies to the CalcTangentSpace-Step. TFvhe angle is specified
* This applies to the CalcTangentSpace-Step. The angle is specified
* in degrees. The maximum value is 175.
* Property type: float. Default value: 45 degrees
*/
@ -999,6 +999,13 @@ enum aiComponent
# define AI_CONFIG_GLOBAL_SCALE_FACTOR_DEFAULT 1.0f
#endif // !! AI_DEBONE_THRESHOLD
#define AI_CONFIG_APP_SCALE_KEY "APP_SCALE_FACTOR"
#if (!defined AI_CONFIG_APP_SCALE_KEY)
# define AI_CONFIG_APP_SCALE_KEY 1.0
#endif // AI_CONFIG_APP_SCALE_KEY
// ---------- All the Build/Compile-time defines ------------
/** @brief Specifies if double precision is supported inside assimp

View file

@ -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

View file

@ -56,9 +56,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "material.h"
#include "anim.h"
#include "metadata.h"
#include <cstdlib>
#ifdef __cplusplus
# include <cstdlib>
extern "C" {
#endif