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:
parent
a5e0aa32d9
commit
ad214c0356
File diff suppressed because it is too large
Load Diff
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
|
@ -1510,7 +1517,6 @@ namespace Assimp {
|
|||
}
|
||||
|
||||
++count_out_indices.back();
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1518,12 +1524,10 @@ 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,
|
||||
count_out_indices, node_global_transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (std::exception&) {
|
||||
std::for_each(bones.begin(), bones.end(), Util::delete_fun<aiBone>());
|
||||
throw;
|
||||
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
// 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.
|
||||
|
|
|
@ -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>());
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
@ -83,21 +97,112 @@ void ScaleProcess::Execute( aiScene* pScene ) {
|
|||
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
|
||||
|
|
|
@ -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_
|
|
@ -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
|
@ -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
|
|
@ -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,6 +167,52 @@ 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
|
||||
|
@ -169,6 +221,11 @@ public:
|
|||
void GetExtensionList(std::set<std::string>& extensions);
|
||||
|
||||
protected:
|
||||
ImporterUnits applicationUnits = ImporterUnits::M;
|
||||
double importerScale = 1.0;
|
||||
double fileScale = 1.0;
|
||||
|
||||
|
||||
|
||||
// -------------------------------------------------------------------
|
||||
/** Imports the given file into the given scene structure. The
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue