summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/assimp/editor_scene_importer_assimp.cpp962
-rw-r--r--modules/assimp/editor_scene_importer_assimp.h50
-rw-r--r--modules/assimp/import_state.h53
-rw-r--r--thirdparty/assimp/code/Common/BaseImporter.cpp2
-rw-r--r--thirdparty/assimp/code/Common/DefaultIOSystem.cpp161
-rw-r--r--thirdparty/assimp/code/Common/Exporter.cpp35
-rw-r--r--thirdparty/assimp/code/Common/SceneCombiner.cpp29
-rw-r--r--thirdparty/assimp/code/FBX/FBXConverter.cpp463
-rw-r--r--thirdparty/assimp/code/FBX/FBXConverter.h80
-rw-r--r--thirdparty/assimp/code/FBX/FBXExportProperty.cpp6
-rw-r--r--thirdparty/assimp/code/FBX/FBXExporter.cpp120
-rw-r--r--thirdparty/assimp/code/FBX/FBXExporter.h2
-rw-r--r--thirdparty/assimp/code/FBX/FBXImporter.cpp217
-rw-r--r--thirdparty/assimp/code/FBX/FBXMeshGeometry.cpp20
-rw-r--r--thirdparty/assimp/code/PostProcessing/JoinVerticesProcess.cpp25
-rw-r--r--thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.cpp29
-rw-r--r--thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.h7
-rw-r--r--thirdparty/assimp/include/assimp/MathFunctions.h29
-rw-r--r--thirdparty/assimp/include/assimp/SceneCombiner.h2
-rw-r--r--thirdparty/assimp/include/assimp/camera.h2
-rw-r--r--thirdparty/assimp/include/assimp/matrix4x4.inl9
-rw-r--r--thirdparty/assimp/include/assimp/mesh.h18
22 files changed, 1399 insertions, 922 deletions
diff --git a/modules/assimp/editor_scene_importer_assimp.cpp b/modules/assimp/editor_scene_importer_assimp.cpp
index 1ea9399c02..4580d8f3d2 100644
--- a/modules/assimp/editor_scene_importer_assimp.cpp
+++ b/modules/assimp/editor_scene_importer_assimp.cpp
@@ -29,35 +29,42 @@
/*************************************************************************/
#include "editor_scene_importer_assimp.h"
-
-#include "core/bind/core_bind.h"
#include "core/io/image_loader.h"
-#include "editor/editor_file_system.h"
-#include "editor/editor_settings.h"
#include "editor/import/resource_importer_scene.h"
#include "import_utils.h"
#include "scene/3d/camera.h"
#include "scene/3d/light.h"
#include "scene/3d/mesh_instance.h"
-#include "scene/animation/animation_player.h"
#include "scene/main/node.h"
#include "scene/resources/material.h"
#include "scene/resources/surface_tool.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 <zutil.h>
-#include <assimp/DefaultLogger.hpp>
#include <assimp/Importer.hpp>
#include <assimp/LogStream.hpp>
-#include <assimp/Logger.hpp>
#include <string>
+// move into assimp
+aiBone *get_bone_by_name(const aiScene *scene, aiString bone_name) {
+ for (unsigned int mesh_id = 0; mesh_id < scene->mNumMeshes; ++mesh_id) {
+ aiMesh *mesh = scene->mMeshes[mesh_id];
+
+ // iterate over all the bones on the mesh for this node only!
+ for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; boneIndex++) {
+
+ aiBone *bone = mesh->mBones[boneIndex];
+ if (bone->mName == bone_name) {
+ printf("matched bone by name: %s\n", bone->mName.C_Str());
+ return bone;
+ }
+ }
+ }
+
+ return NULL;
+}
+
void EditorSceneImporterAssimp::get_extensions(List<String> *r_extensions) const {
const String import_setting_string = "filesystem/import/open_asset_import/";
@@ -76,11 +83,14 @@ void EditorSceneImporterAssimp::get_extensions(List<String> *r_extensions) const
import_format.insert("mmd", import);
}
for (Map<String, ImportFormat>::Element *E = import_format.front(); E; E = E->next()) {
- _register_project_setting_import(E->key(), import_setting_string, E->get().extensions, r_extensions, E->get().is_default);
+ _register_project_setting_import(E->key(), import_setting_string, E->get().extensions, r_extensions,
+ E->get().is_default);
}
}
-void EditorSceneImporterAssimp::_register_project_setting_import(const String generic, const String import_setting_string, const Vector<String> &exts, List<String> *r_extensions, const bool p_enabled) const {
+void EditorSceneImporterAssimp::_register_project_setting_import(const String generic, const String import_setting_string,
+ const Vector<String> &exts, List<String> *r_extensions,
+ const bool p_enabled) const {
const String use_generic = "use_" + generic;
_GLOBAL_DEF(import_setting_string + use_generic, p_enabled, true);
if (ProjectSettings::get_singleton()->get(import_setting_string + use_generic)) {
@@ -97,7 +107,8 @@ uint32_t EditorSceneImporterAssimp::get_import_flags() const {
void EditorSceneImporterAssimp::_bind_methods() {
}
-Node *EditorSceneImporterAssimp::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) {
+Node *EditorSceneImporterAssimp::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps,
+ List<String> *r_missing_deps, Error *r_err) {
Assimp::Importer importer;
std::wstring w_path = ProjectSettings::get_singleton()->globalize_path(p_path).c_str();
std::string s_path(w_path.begin(), w_path.end());
@@ -115,9 +126,11 @@ Node *EditorSceneImporterAssimp::import_scene(const String &p_path, uint32_t p_f
//importer.SetPropertyFloat(AI_CONFIG_PP_DB_THRESHOLD, 1.0f);
int32_t post_process_Steps = aiProcess_CalcTangentSpace |
- aiProcess_GlobalScale | // imports models and listens to their file scale for CM to M conversions
+ aiProcess_GlobalScale |
+ // imports models and listens to their file scale for CM to M conversions
//aiProcess_FlipUVs |
- aiProcess_FlipWindingOrder | // very important for culling so that it is done in the correct order.
+ aiProcess_FlipWindingOrder |
+ // very important for culling so that it is done in the correct order.
//aiProcess_DropNormals |
//aiProcess_GenSmoothNormals |
//aiProcess_JoinIdenticalVertices |
@@ -158,7 +171,8 @@ struct EditorSceneImporterAssetImportInterpolate {
float t2 = t * t;
float t3 = t2 * t;
- return 0.5f * ((2.0f * p1) + (-p0 + p2) * t + (2.0f * p0 - 5.0f * p1 + 4 * p2 - p3) * t2 + (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3);
+ return 0.5f * ((2.0f * p1) + (-p0 + p2) * t + (2.0f * p0 - 5.0f * p1 + 4 * p2 - p3) * t2 +
+ (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3);
}
T bezier(T start, T control_1, T control_2, T end, float t) {
@@ -200,7 +214,8 @@ struct EditorSceneImporterAssetImportInterpolate<Quat> {
};
template <class T>
-T EditorSceneImporterAssimp::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time, AssetImportAnimation::Interpolation p_interp) {
+T EditorSceneImporterAssimp::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time,
+ AssetImportAnimation::Interpolation p_interp) {
//could use binary search, worth it?
int idx = -1;
for (int i = 0; i < p_times.size(); i++) {
@@ -272,48 +287,257 @@ T EditorSceneImporterAssimp::_interpolate_track(const Vector<float> &p_times, co
ERR_FAIL_V(p_values[0]);
}
-Spatial *EditorSceneImporterAssimp::_generate_scene(const String &p_path, aiScene *scene, const uint32_t p_flags, int p_bake_fps, const int32_t p_max_bone_weights) {
+aiBone *EditorSceneImporterAssimp::get_bone_from_stack(ImportState &state, aiString name) {
+ List<aiBone *>::Element *iter;
+ aiBone *bone = NULL;
+ for (iter = state.bone_stack.front(); iter; iter = iter->next()) {
+ bone = (aiBone *)iter->get();
+
+ if (bone && bone->mName == name) {
+ state.bone_stack.erase(bone);
+ return bone;
+ }
+ }
+
+ return NULL;
+}
+
+Spatial *
+EditorSceneImporterAssimp::_generate_scene(const String &p_path, aiScene *scene, const uint32_t p_flags, int p_bake_fps,
+ const int32_t p_max_bone_weights) {
ERR_FAIL_COND_V(scene == NULL, NULL);
ImportState state;
state.path = p_path;
state.assimp_scene = scene;
state.max_bone_weights = p_max_bone_weights;
- state.root = memnew(Spatial);
- state.fbx = false;
state.animation_player = NULL;
- //fill light map cache
- for (size_t l = 0; l < scene->mNumLights; l++) {
+ // populate light map
+ for (unsigned int l = 0; l < scene->mNumLights; l++) {
aiLight *ai_light = scene->mLights[l];
ERR_CONTINUE(ai_light == NULL);
state.light_cache[AssimpUtils::get_assimp_string(ai_light->mName)] = l;
}
- //fill camera cache
- for (size_t c = 0; c < scene->mNumCameras; c++) {
+ // fill camera cache
+ for (unsigned int c = 0; c < scene->mNumCameras; c++) {
aiCamera *ai_camera = scene->mCameras[c];
ERR_CONTINUE(ai_camera == NULL);
state.camera_cache[AssimpUtils::get_assimp_string(ai_camera->mName)] = c;
}
if (scene->mRootNode) {
+ state.nodes.push_back(scene->mRootNode);
- //generate nodes
- for (uint32_t i = 0; i < scene->mRootNode->mNumChildren; i++) {
- _generate_node(state, NULL, scene->mRootNode->mChildren[i], state.root);
+ // make flat node tree - in order to make processing deterministic
+ for (unsigned int i = 0; i < scene->mRootNode->mNumChildren; i++) {
+ _generate_node(state, scene->mRootNode->mChildren[i]);
}
- // finalize skeleton
- for (Map<Skeleton *, const Spatial *>::Element *key_value_pair = state.armature_skeletons.front(); key_value_pair; key_value_pair = key_value_pair->next()) {
- Skeleton *skeleton = key_value_pair->key();
- // convert world to local for skeleton bone rests
- skeleton->localize_rests();
+ RegenerateBoneStack(state);
+
+ Node *last_valid_parent = NULL;
+
+ List<const aiNode *>::Element *iter;
+ for (iter = state.nodes.front(); iter; iter = iter->next()) {
+ const aiNode *element_assimp_node = iter->get();
+ const aiNode *parent_assimp_node = element_assimp_node->mParent;
+
+ String node_name = AssimpUtils::get_assimp_string(element_assimp_node->mName);
+ //print_verbose("node: " + node_name);
+
+ Spatial *spatial = NULL;
+ Transform transform = AssimpUtils::assimp_matrix_transform(element_assimp_node->mTransformation);
+
+ // retrieve this node bone
+ aiBone *bone = get_bone_from_stack(state, element_assimp_node->mName);
+
+ if (state.light_cache.has(node_name)) {
+ spatial = create_light(state, node_name, transform);
+ } else if (state.camera_cache.has(node_name)) {
+ spatial = create_camera(state, node_name, transform);
+ } else if (state.armature_nodes.find(element_assimp_node)) {
+ // create skeleton
+ print_verbose("Making skeleton: " + node_name);
+ Skeleton *skeleton = memnew(Skeleton);
+ spatial = skeleton;
+ if (!state.armature_skeletons.has(element_assimp_node)) {
+ state.armature_skeletons.insert(element_assimp_node, skeleton);
+ }
+ } else if (bone != NULL) {
+ continue;
+ } else if (element_assimp_node->mNumMeshes > 0) {
+ spatial = memnew(Spatial);
+ } else {
+ spatial = memnew(Spatial);
+ }
+
+ ERR_CONTINUE_MSG(spatial == NULL, "FBX Import - are we out of ram?");
+ // we on purpose set the transform and name after creating the node.
+
+ spatial->set_name(node_name);
+ spatial->set_global_transform(transform);
+
+ // first element is root
+ if (iter == state.nodes.front()) {
+ state.root = spatial;
+ }
+
+ // flat node map parent lookup tool
+ state.flat_node_map.insert(element_assimp_node, spatial);
+
+ Map<const aiNode *, Spatial *>::Element *parent_lookup = state.flat_node_map.find(parent_assimp_node);
+
+ // note: this always fails on the root node :) keep that in mind this is by design
+ if (parent_lookup) {
+ Spatial *parent_node = parent_lookup->value();
+
+ ERR_FAIL_COND_V_MSG(parent_node == NULL, state.root,
+ "Parent node invalid even though lookup successful, out of ram?")
+
+ if (parent_node && spatial != state.root) {
+ parent_node->add_child(spatial);
+ spatial->set_owner(state.root);
+ } else if (spatial == state.root) {
+ // required - think about it root never has a parent yet is valid, anything else without a parent is not valid.
+ } else // Safety for instances
+ {
+ WARN_PRINT(
+ "Failed to find parent node instance after lookup, serious warning report to godot with model");
+ memdelete(spatial); // this node is broken
+ }
+ } else if (spatial != state.root) {
+ // if the ainode is not in the tree
+ // parent it to the last good parent found
+ if (last_valid_parent) {
+ last_valid_parent->add_child(spatial);
+ spatial->set_owner(state.root);
+ } else {
+ // this is a serious error?
+ memdelete(spatial);
+ }
+ }
+
+ // update last valid parent
+ last_valid_parent = spatial;
+ }
+ print_verbose("node counts: " + itos(state.nodes.size()));
+
+ // make clean bone stack
+ RegenerateBoneStack(state);
+
+ print_verbose("generating godot bone data");
+
+ print_verbose("Godot bone stack count: " + itos(state.bone_stack.size()));
+
+ // This is a list of bones, duplicates are from other meshes and must be dealt with properly
+ for (List<aiBone *>::Element *element = state.bone_stack.front(); element; element = element->next()) {
+ aiBone *bone = element->get();
+
+ ERR_CONTINUE_MSG(!bone, "invalid bone read from assimp?");
+
+ // Utilities for armature lookup - for now only FBX makes these
+ aiNode *armature_for_bone = bone->mArmature;
+
+ // Utilities for bone node lookup - for now only FBX makes these
+ aiNode *bone_node = bone->mNode;
+ aiNode *parent_node = bone_node->mParent;
+
+ String bone_name = AssimpUtils::get_anim_string_from_assimp(bone->mName);
+ ERR_CONTINUE_MSG(armature_for_bone == NULL, "Armature for bone invalid: " + bone_name);
+ Skeleton *skeleton = state.armature_skeletons[armature_for_bone];
+
+ state.skeleton_bone_map[bone] = skeleton;
+
+ if (bone_name.empty()) {
+ bone_name = "untitled_bone_name";
+ WARN_PRINT("Untitled bone name detected... report with file please");
+ }
+
+ // todo: this is where skin support goes
+ if (skeleton && skeleton->find_bone(bone_name) == -1) {
+ print_verbose("[Godot Glue] Imported bone" + bone_name);
+ int boneIdx = skeleton->get_bone_count();
+
+ Transform pform = AssimpUtils::assimp_matrix_transform(bone->mNode->mTransformation);
+ skeleton->add_bone(bone_name);
+ skeleton->set_bone_rest(boneIdx, pform);
+ skeleton->set_bone_pose(boneIdx, pform);
+
+ if (parent_node != NULL) {
+ int parent_bone_id = skeleton->find_bone(AssimpUtils::get_anim_string_from_assimp(parent_node->mName));
+ int current_bone_id = boneIdx;
+ skeleton->set_bone_parent(current_bone_id, parent_bone_id);
+ }
+ }
}
print_verbose("generating mesh phase from skeletal mesh");
- generate_mesh_phase_from_skeletal_mesh(state);
+
+ List<Spatial *> cleanup_template_nodes;
+
+ for (Map<const aiNode *, Spatial *>::Element *key_value_pair = state.flat_node_map.front(); key_value_pair; key_value_pair = key_value_pair->next()) {
+ const aiNode *assimp_node = key_value_pair->key();
+ Spatial *mesh_template = key_value_pair->value();
+ Node *parent_node = mesh_template->get_parent();
+
+ ERR_CONTINUE(assimp_node == NULL);
+ ERR_CONTINUE(mesh_template == NULL);
+
+ if (mesh_template == state.root) {
+ continue;
+ }
+
+ if (parent_node == NULL) {
+ print_error("Found invalid parent node!");
+ continue; // root node
+ }
+
+ String node_name = AssimpUtils::get_assimp_string(assimp_node->mName);
+ Transform node_transform = AssimpUtils::assimp_matrix_transform(assimp_node->mTransformation);
+
+ if (assimp_node->mNumMeshes > 0) {
+ MeshInstance *mesh = create_mesh(state, assimp_node, node_name, parent_node, node_transform);
+ if (mesh) {
+
+ parent_node->remove_child(mesh_template);
+
+ // re-parent children
+ List<Node *> children;
+ // re-parent all children to new node
+ // note: since get_child_count will change during execution we must build a list first to be safe.
+ for (int childId = 0; childId < mesh_template->get_child_count(); childId++) {
+ // get child
+ Node *child = mesh_template->get_child(childId);
+ children.push_back(child);
+ }
+
+ for (List<Node *>::Element *element = children.front(); element; element = element->next()) {
+ // reparent the children to the real mesh node.
+ mesh_template->remove_child(element->get());
+ mesh->add_child(element->get());
+ element->get()->set_owner(state.root);
+ }
+
+ // update mesh in list so that each mesh node is available
+ // this makes the template unavailable which is the desired behaviour
+ state.flat_node_map[assimp_node] = mesh;
+
+ cleanup_template_nodes.push_back(mesh_template);
+
+ // clean up this list we don't need it
+ children.clear();
+ }
+ }
+ }
+
+ for (List<Spatial *>::Element *element = cleanup_template_nodes.front(); element; element = element->next()) {
+ if (element->get()) {
+ memdelete(element->get());
+ }
+ }
}
if (p_flags & IMPORT_ANIMATION && scene->mNumAnimations) {
@@ -327,29 +551,39 @@ Spatial *EditorSceneImporterAssimp::_generate_scene(const String &p_path, aiScen
}
}
+ //
+ // Cleanup operations
+ //
+
+ state.mesh_cache.clear();
+ state.material_cache.clear();
+ state.light_cache.clear();
+ state.camera_cache.clear();
+ state.assimp_node_map.clear();
+ state.path_to_image_cache.clear();
+ state.nodes.clear();
+ state.flat_node_map.clear();
+ state.armature_skeletons.clear();
+ state.bone_stack.clear();
return state.root;
}
-void EditorSceneImporterAssimp::_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) {
-
- const aiNodeAnim *assimp_track = assimp_anim->mChannels[p_track];
+void EditorSceneImporterAssimp::_insert_animation_track(ImportState &scene, const aiAnimation *assimp_anim, int track_id,
+ int anim_fps, Ref<Animation> animation, float ticks_per_second,
+ Skeleton *skeleton, const NodePath &node_path,
+ const String &node_name, aiBone *track_bone) {
+ const aiNodeAnim *assimp_track = assimp_anim->mChannels[track_id];
//make transform track
int track_idx = animation->get_track_count();
animation->add_track(Animation::TYPE_TRANSFORM);
- animation->track_set_path(track_idx, p_path);
+ animation->track_set_path(track_idx, node_path);
//first determine animation length
- float increment = 1.0 / float(p_bake_fps);
+ float increment = 1.0 / float(anim_fps);
float time = 0.0;
bool last = false;
- int skeleton_bone = -1;
-
- if (p_skeleton) {
- skeleton_bone = p_skeleton->find_bone(p_name);
- }
-
Vector<Vector3> pos_values;
Vector<float> pos_times;
Vector<Vector3> scale_values;
@@ -374,6 +608,7 @@ void EditorSceneImporterAssimp::_insert_animation_track(ImportState &scene, cons
scale_values.push_back(Vector3(scale.x, scale.y, scale.z));
scale_times.push_back(assimp_track->mScalingKeys[sc].mTime / ticks_per_second);
}
+
while (true) {
Vector3 pos;
Quat rot;
@@ -384,26 +619,34 @@ void EditorSceneImporterAssimp::_insert_animation_track(ImportState &scene, cons
}
if (rot_values.size()) {
- rot = _interpolate_track<Quat>(rot_times, rot_values, time, AssetImportAnimation::INTERP_LINEAR).normalized();
+ rot = _interpolate_track<Quat>(rot_times, rot_values, time,
+ AssetImportAnimation::INTERP_LINEAR)
+ .normalized();
}
if (scale_values.size()) {
scale = _interpolate_track<Vector3>(scale_times, scale_values, time, AssetImportAnimation::INTERP_LINEAR);
}
- if (skeleton_bone >= 0) {
- Transform xform;
- xform.basis.set_quat_scale(rot, scale);
- xform.origin = pos;
+ if (skeleton) {
+ int skeleton_bone = skeleton->find_bone(node_name);
- Transform rest_xform = p_skeleton->get_bone_rest(skeleton_bone);
- xform = rest_xform.affine_inverse() * xform;
- rot = xform.basis.get_rotation_quat();
- scale = xform.basis.get_scale();
- pos = xform.origin;
- }
+ if (skeleton_bone >= 0 && track_bone) {
+
+ Transform xform;
+ xform.basis.set_quat_scale(rot, scale);
+ xform.origin = pos;
- rot.normalize();
+ xform = skeleton->get_bone_pose(skeleton_bone).inverse() * xform;
+
+ rot = xform.basis.get_rotation_quat();
+ rot.normalize();
+ scale = xform.basis.get_scale();
+ pos = xform.origin;
+ } else {
+ ERR_FAIL_MSG("Skeleton bone lookup failed for skeleton: " + skeleton->get_name());
+ }
+ }
animation->track_set_interpolation_type(track_idx, Animation::INTERPOLATION_LINEAR);
animation->transform_track_insert_key(track_idx, time, pos, rot, scale);
@@ -418,6 +661,53 @@ void EditorSceneImporterAssimp::_insert_animation_track(ImportState &scene, cons
}
}
+// I really do not like this but need to figure out a better way of removing it later.
+Node *EditorSceneImporterAssimp::get_node_by_name(ImportState &state, String name) {
+ for (Map<const aiNode *, Spatial *>::Element *key_value_pair = state.flat_node_map.front(); key_value_pair; key_value_pair = key_value_pair->next()) {
+ const aiNode *assimp_node = key_value_pair->key();
+ Spatial *node = key_value_pair->value();
+
+ String node_name = AssimpUtils::get_assimp_string(assimp_node->mName);
+ if (name == node_name && node) {
+ return node;
+ }
+ }
+ return NULL;
+}
+
+/* Bone stack is a fifo handler for multiple armatures since armatures aren't a thing in assimp (yet) */
+void EditorSceneImporterAssimp::RegenerateBoneStack(ImportState &state) {
+
+ state.bone_stack.clear();
+ // build bone stack list
+ for (unsigned int mesh_id = 0; mesh_id < state.assimp_scene->mNumMeshes; ++mesh_id) {
+ aiMesh *mesh = state.assimp_scene->mMeshes[mesh_id];
+
+ // iterate over all the bones on the mesh for this node only!
+ for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; boneIndex++) {
+ aiBone *bone = mesh->mBones[boneIndex];
+
+ // doubtful this is required right now but best to check
+ if (!state.bone_stack.find(bone)) {
+ //print_verbose("[assimp] bone stack added: " + String(bone->mName.C_Str()) );
+ state.bone_stack.push_back(bone);
+ }
+ }
+ }
+}
+
+/* Bone stack is a fifo handler for multiple armatures since armatures aren't a thing in assimp (yet) */
+void EditorSceneImporterAssimp::RegenerateBoneStack(ImportState &state, aiMesh *mesh) {
+ state.bone_stack.clear();
+ // iterate over all the bones on the mesh for this node only!
+ for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; boneIndex++) {
+ aiBone *bone = mesh->mBones[boneIndex];
+ if (state.bone_stack.find(bone) == NULL) {
+ state.bone_stack.push_back(bone);
+ }
+ }
+}
+
// animation tracks are per bone
void EditorSceneImporterAssimp::_import_animation(ImportState &state, int p_animation_index, int p_bake_fps) {
@@ -429,7 +719,7 @@ void EditorSceneImporterAssimp::_import_animation(ImportState &state, int p_anim
if (name == String()) {
name = "Animation " + itos(p_animation_index + 1);
}
-
+ print_verbose("import animation: " + name);
float ticks_per_second = anim->mTicksPerSecond;
if (state.assimp_scene->mMetaData != NULL && Math::is_equal_approx(ticks_per_second, 0.0f)) {
@@ -452,34 +742,60 @@ void EditorSceneImporterAssimp::_import_animation(ImportState &state, int p_anim
animation->set_name(name);
animation->set_length(anim->mDuration / ticks_per_second);
- //regular tracks
+ // generate bone stack for animation import
+ RegenerateBoneStack(state);
+ //regular tracks
for (size_t i = 0; i < anim->mNumChannels; i++) {
const aiNodeAnim *track = anim->mChannels[i];
String node_name = AssimpUtils::get_assimp_string(track->mNodeName);
-
+ print_verbose("track name import: " + node_name);
if (track->mNumRotationKeys == 0 && track->mNumPositionKeys == 0 && track->mNumScalingKeys == 0) {
continue; //do not bother
}
- for (Map<Skeleton *, const Spatial *>::Element *key_value_pair = state.armature_skeletons.front(); key_value_pair; key_value_pair = key_value_pair->next()) {
- Skeleton *skeleton = key_value_pair->key();
-
- bool is_bone = skeleton->find_bone(node_name) != -1;
- //print_verbose("Bone " + node_name + " is bone? " + (is_bone ? "Yes" : "No"));
- NodePath node_path;
+ Skeleton *skeleton = NULL;
+ NodePath node_path;
+ aiBone *bone = NULL;
- if (is_bone) {
- String path = state.root->get_path_to(skeleton);
- path += ":" + node_name;
- node_path = path;
- } else {
- ERR_CONTINUE(!state.node_map.has(node_name));
- Node *node = state.node_map[node_name];
- node_path = state.root->get_path_to(node);
+ // Import skeleton bone animation for this track
+ // Any bone will do, no point in processing more than just what is in the skeleton
+ {
+ bone = get_bone_from_stack(state, track->mNodeName);
+
+ if (bone) {
+ // get skeleton by bone
+ skeleton = state.armature_skeletons[bone->mArmature];
+
+ if (skeleton) {
+ String path = state.root->get_path_to(skeleton);
+ path += ":" + node_name;
+ node_path = path;
+
+ if (node_path != NodePath()) {
+ _insert_animation_track(state, anim, i, p_bake_fps, animation, ticks_per_second, skeleton,
+ node_path, node_name, bone);
+ } else {
+ print_error("Failed to find valid node path for animation");
+ }
+ }
}
+ }
- _insert_animation_track(state, anim, i, p_bake_fps, animation, ticks_per_second, skeleton, node_path, node_name);
+ // not a bone
+ // note this is flaky it uses node names which is unreliable
+ Node *allocated_node = get_node_by_name(state, node_name);
+ // todo: implement skeleton grabbing for node based animations too :)
+ // check if node exists, if it does then also apply animation track for node and bones above are all handled.
+ // this is now inclusive animation handling so that
+ // we import all the data and do not miss anything.
+ if (allocated_node) {
+ node_path = state.root->get_path_to(allocated_node);
+
+ if (node_path != NodePath()) {
+ _insert_animation_track(state, anim, i, p_bake_fps, animation, ticks_per_second, skeleton,
+ node_path, node_name, nullptr);
+ }
}
}
@@ -494,10 +810,9 @@ void EditorSceneImporterAssimp::_import_animation(ImportState &state, int p_anim
ERR_CONTINUE(prop_name.split("*").size() != 2);
- ERR_CONTINUE(!state.node_map.has(mesh_name));
-
- const MeshInstance *mesh_instance = Object::cast_to<MeshInstance>(state.node_map[mesh_name]);
-
+ Node *item = get_node_by_name(state, mesh_name);
+ ERR_CONTINUE_MSG(!item, "failed to look up node by name");
+ const MeshInstance *mesh_instance = Object::cast_to<MeshInstance>(item);
ERR_CONTINUE(mesh_instance == NULL);
String base_path = state.root->get_path_to(mesh_instance);
@@ -528,15 +843,13 @@ void EditorSceneImporterAssimp::_import_animation(ImportState &state, int p_anim
state.animation_player->add_animation(name, animation);
}
}
-
//
// Mesh Generation from indices ? why do we need so much mesh code
// [debt needs looked into]
-Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(
- ImportState &state,
- const Vector<int> &p_surface_indices,
- const aiNode *assimp_node,
- Skeleton *p_skeleton) {
+Ref<Mesh>
+EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportState &state, const Vector<int> &p_surface_indices,
+ const aiNode *assimp_node, Ref<Skin> &skin,
+ Skeleton *&skeleton_assigned) {
Ref<ArrayMesh> mesh;
mesh.instance();
@@ -548,7 +861,6 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(
const unsigned int mesh_idx = p_surface_indices[0];
const aiMesh *ai_mesh = state.assimp_scene->mMeshes[mesh_idx];
for (size_t j = 0; j < ai_mesh->mNumAnimMeshes; j++) {
-
String ai_anim_mesh_name = AssimpUtils::get_assimp_string(ai_mesh->mAnimMeshes[j]->mName);
if (!morph_mesh_string_lookup.has(ai_anim_mesh_name)) {
morph_mesh_string_lookup.insert(ai_anim_mesh_name, j);
@@ -560,7 +872,6 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(
}
}
}
-
//
// Process Vertex Weights
//
@@ -570,19 +881,30 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(
Map<uint32_t, Vector<BoneInfo> > vertex_weights;
- if (p_skeleton) {
+ if (ai_mesh->mNumBones > 0) {
for (size_t b = 0; b < ai_mesh->mNumBones; b++) {
aiBone *bone = ai_mesh->mBones[b];
- String bone_name = AssimpUtils::get_assimp_string(bone->mName);
- int bone_index = p_skeleton->find_bone(bone_name);
- ERR_CONTINUE(bone_index == -1); //bone refers to an unexisting index, wtf.
+ if (!skeleton_assigned) {
+ print_verbose("Assigned mesh skeleton during mesh creation");
+ skeleton_assigned = state.skeleton_bone_map[bone];
+
+ if (!skin.is_valid()) {
+ print_verbose("Configured new skin");
+ skin.instance();
+ } else {
+ print_verbose("Reusing existing skin!");
+ }
+ }
+ // skeleton_assigned =
+ String bone_name = AssimpUtils::get_assimp_string(bone->mName);
+ int bone_index = skeleton_assigned->find_bone(bone_name);
+ ERR_CONTINUE(bone_index == -1);
for (size_t w = 0; w < bone->mNumWeights; w++) {
aiVertexWeight ai_weights = bone->mWeights[w];
BoneInfo bi;
-
uint32_t vertex_index = ai_weights.mVertexId;
bi.bone = bone_index;
bi.weight = ai_weights.mWeight;
@@ -619,7 +941,8 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(
// Assign vertex colors
if (ai_mesh->HasVertexColors(0)) {
- Color color = Color(ai_mesh->mColors[0]->r, ai_mesh->mColors[0]->g, ai_mesh->mColors[0]->b, ai_mesh->mColors[0]->a);
+ Color color = Color(ai_mesh->mColors[0]->r, ai_mesh->mColors[0]->g, ai_mesh->mColors[0]->b,
+ ai_mesh->mColors[0]->a);
st->add_color(color);
}
@@ -685,6 +1008,8 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(
if (AI_SUCCESS == ai_material->Get(AI_MATKEY_TWOSIDED, mat_two_sided)) {
if (mat_two_sided > 0) {
mat->set_cull_mode(SpatialMaterial::CULL_DISABLED);
+ } else {
+ mat->set_cull_mode(SpatialMaterial::CULL_BACK);
}
}
@@ -697,7 +1022,7 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(
// Culling handling for meshes
// cull all back faces
- mat->set_cull_mode(SpatialMaterial::CULL_BACK);
+ mat->set_cull_mode(SpatialMaterial::CULL_DISABLED);
// Now process materials
aiTextureType base_color = aiTextureType_BASE_COLOR;
@@ -712,7 +1037,8 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(
if (image_data.raw_image->detect_alpha() != Image::ALPHA_NONE) {
mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
- mat->set_cull_mode(SpatialMaterial::CULL_DISABLED); // since you can see both sides in transparent mode
+ mat->set_cull_mode(
+ SpatialMaterial::CULL_DISABLED); // since you can see both sides in transparent mode
}
mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, image_data.texture);
@@ -731,7 +1057,8 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(
if (image_data.raw_image->detect_alpha() != Image::ALPHA_NONE) {
mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
- mat->set_cull_mode(SpatialMaterial::CULL_DISABLED); // since you can see both sides in transparent mode
+ mat->set_cull_mode(
+ SpatialMaterial::CULL_DISABLED); // since you can see both sides in transparent mode
}
mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, image_data.texture);
@@ -742,7 +1069,8 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(
if (Math::is_equal_approx(clr_diffuse.a, 1.0f) == false) {
mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
- mat->set_cull_mode(SpatialMaterial::CULL_DISABLED); // since you can see both sides in transparent mode
+ mat->set_cull_mode(
+ SpatialMaterial::CULL_DISABLED); // since you can see both sides in transparent mode
}
mat->set_albedo(Color(clr_diffuse.r, clr_diffuse.g, clr_diffuse.b, clr_diffuse.a));
}
@@ -838,7 +1166,8 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(
} else {
// Process emission textures
aiString texture_emissive_path;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_EMISSION_TEXTURE, AI_PROPERTIES, texture_emissive_path)) {
+ if (AI_SUCCESS ==
+ ai_material->Get(AI_MATKEY_FBX_MAYA_EMISSION_TEXTURE, AI_PROPERTIES, texture_emissive_path)) {
if (AssimpUtils::CreateAssimpTexture(state, texture_emissive_path, filename, path, image_data)) {
mat->set_feature(SpatialMaterial::FEATURE_EMISSION, true);
mat->set_texture(SpatialMaterial::TEXTURE_EMISSION, image_data.texture);
@@ -981,62 +1310,27 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(
return mesh;
}
-/* to be moved into assimp */
-aiBone *get_bone_by_name(const aiScene *scene, aiString bone_name) {
- for (unsigned int mesh_id = 0; mesh_id < scene->mNumMeshes; ++mesh_id) {
- aiMesh *mesh = scene->mMeshes[mesh_id];
-
- // iterate over all the bones on the mesh for this node only!
- for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; boneIndex++) {
-
- aiBone *bone = mesh->mBones[boneIndex];
- if (bone->mName == bone_name) {
- printf("matched bone by name: %s\n", bone->mName.C_Str());
- return bone;
- }
- }
- }
-
- return NULL;
-}
-
/**
* Create a new mesh for the node supplied
*/
-void EditorSceneImporterAssimp::create_mesh(ImportState &state, const aiNode *assimp_node, const String &node_name, Node *current_node, Node *parent_node, Transform node_transform) {
+MeshInstance *
+EditorSceneImporterAssimp::create_mesh(ImportState &state, const aiNode *assimp_node, const String &node_name, Node *active_node, Transform node_transform) {
/* MESH NODE */
Ref<Mesh> mesh;
- Skeleton *skeleton = NULL;
+ Ref<Skin> skin;
// see if we have mesh cache for this.
Vector<int> surface_indices;
- for (uint32_t i = 0; i < assimp_node->mNumMeshes; i++) {
- int mesh_index = assimp_node->mMeshes[i];
- aiMesh *ai_mesh = state.assimp_scene->mMeshes[assimp_node->mMeshes[i]];
-
- // Map<aiBone*, Skeleton*> // this is what we need
- if (ai_mesh->mNumBones > 0) {
- // we only need the first bone to retrieve the skeleton
- const aiBone *first = ai_mesh->mBones[0];
-
- ERR_FAIL_COND(first == NULL);
- Map<const aiBone *, Skeleton *>::Element *match = state.bone_to_skeleton_lookup.find(first);
- if (match != NULL) {
- skeleton = match->value();
+ RegenerateBoneStack(state);
- if (skeleton == NULL) {
- print_error("failed to find bone skeleton for bone: " + AssimpUtils::get_assimp_string(first->mName));
- } else {
- print_verbose("successfully found skeleton for first bone on mesh, can properly handle animations now!");
- }
- // I really need the skeleton and bone to be known as this is something flaky in model exporters.
- ERR_FAIL_COND(skeleton == NULL); // should not happen if bone was successfully created in previous step.
- }
- }
+ // Configure indicies
+ for (uint32_t i = 0; i < assimp_node->mNumMeshes; i++) {
+ int mesh_index = assimp_node->mMeshes[i];
+ // create list of mesh indexes
surface_indices.push_back(mesh_index);
}
- surface_indices.sort();
+ //surface_indices.sort();
String mesh_key;
for (int i = 0; i < surface_indices.size(); i++) {
if (i > 0) {
@@ -1045,262 +1339,154 @@ void EditorSceneImporterAssimp::create_mesh(ImportState &state, const aiNode *as
mesh_key += itos(surface_indices[i]);
}
+ Skeleton *skeleton = NULL;
+ aiNode *armature = NULL;
+
if (!state.mesh_cache.has(mesh_key)) {
- mesh = _generate_mesh_from_surface_indices(state, surface_indices, assimp_node, skeleton);
+ mesh = _generate_mesh_from_surface_indices(state, surface_indices, assimp_node, skin, skeleton);
state.mesh_cache[mesh_key] = mesh;
}
- //Transform transform = recursive_state.node_transform;
-
- // we must unfortunately overwrite mesh and skeleton transform with armature data
- if (skeleton != NULL) {
- print_verbose("Applying mesh and skeleton to armature");
- // required for blender, maya etc
- Map<Skeleton *, const Spatial *>::Element *match = state.armature_skeletons.find(skeleton);
- node_transform = match->value()->get_transform();
- }
-
MeshInstance *mesh_node = memnew(MeshInstance);
mesh = state.mesh_cache[mesh_key];
mesh_node->set_mesh(mesh);
- attach_new_node(state,
- mesh_node,
- assimp_node,
- parent_node,
- node_name,
- node_transform);
-
- // set this once and for all
- if (skeleton != NULL) {
- // root must be informed of its new child
- parent_node->add_child(skeleton);
+ // if we have a valid skeleton set it up
+ if (skin.is_valid()) {
+ for (uint32_t i = 0; i < assimp_node->mNumMeshes; i++) {
+ unsigned int mesh_index = assimp_node->mMeshes[i];
+ const aiMesh *ai_mesh = state.assimp_scene->mMeshes[mesh_index];
+
+ // please remember bone id relative to the skin is NOT the mesh relative index.
+ // it is the index relative to the skeleton that is why
+ // we have state.bone_id_map, it allows for duplicate bone id's too :)
+ // hope this makes sense
+
+ int bind_count = 0;
+ for (unsigned int boneId = 0; boneId < ai_mesh->mNumBones; ++boneId) {
+ aiBone *iterBone = ai_mesh->mBones[boneId];
+
+ // used to reparent mesh to the correct armature later on if assigned.
+ if (!armature) {
+ print_verbose("Configured mesh armature, will reparent later to armature");
+ armature = iterBone->mArmature;
+ }
- // owner must be set after adding to tree
- skeleton->set_owner(state.root);
+ if (skeleton) {
+ int id = skeleton->find_bone(AssimpUtils::get_assimp_string(iterBone->mName));
+ if (id != -1) {
+ print_verbose("Set bind bone: mesh: " + itos(mesh_index) + " bone index: " + itos(id));
+ Transform t = AssimpUtils::assimp_matrix_transform(iterBone->mOffsetMatrix);
- skeleton->set_transform(node_transform);
+ skin->add_bind(bind_count, t);
+ skin->set_bind_bone(bind_count, id);
+ bind_count++;
+ }
+ }
+ }
+ }
- // must be done after added to tree
- mesh_node->set_skeleton_path(mesh_node->get_path_to(skeleton));
+ print_verbose("Finished configuring bind pose for skin mesh");
}
-}
-
-/** generate_mesh_phase_from_skeletal_mesh
- * This must be executed after generate_nodes because the skeleton doesn't exist until that has completed the first pass
- */
-void EditorSceneImporterAssimp::generate_mesh_phase_from_skeletal_mesh(ImportState &state) {
- // prevent more than one skeleton existing per mesh
- // * multiple root bones have this
- // * this simply filters the node out if it has already been added then references the skeleton so we know the actual skeleton for this node
- for (Map<const aiNode *, const Node *>::Element *key_value_pair = state.assimp_node_map.front(); key_value_pair; key_value_pair = key_value_pair->next()) {
- const aiNode *assimp_node = key_value_pair->key();
- Node *current_node = (Node *)key_value_pair->value();
- Node *parent_node = current_node->get_parent();
-
- ERR_CONTINUE(assimp_node == NULL);
- ERR_CONTINUE(parent_node == NULL);
- String node_name = AssimpUtils::get_assimp_string(assimp_node->mName);
- Transform node_transform = AssimpUtils::assimp_matrix_transform(assimp_node->mTransformation);
+ // this code parents all meshes with bones to the armature they are for
+ // GLTF2 specification relies on this and we are enforcing it for FBX.
+ if (armature && state.flat_node_map[armature]) {
+ Node *armature_parent = state.flat_node_map[armature];
+ print_verbose("Parented mesh " + node_name + " to armature " + armature_parent->get_name());
+ // static mesh handling
+ armature_parent->add_child(mesh_node);
+ // transform must be identity
+ mesh_node->set_global_transform(Transform());
+ mesh_node->set_name(node_name);
+ mesh_node->set_owner(state.root);
+ } else {
+ // static mesh handling
+ active_node->add_child(mesh_node);
+ mesh_node->set_global_transform(node_transform);
+ mesh_node->set_name(node_name);
+ mesh_node->set_owner(state.root);
+ }
- if (assimp_node->mNumMeshes > 0) {
- create_mesh(state, assimp_node, node_name, current_node, parent_node, node_transform);
- }
+ if (skeleton) {
+ print_verbose("Attempted to set skeleton path!");
+ mesh_node->set_skeleton_path(mesh_node->get_path_to(skeleton));
+ mesh_node->set_skin(skin);
}
-}
-/**
- * attach_new_node
- * configures node, assigns parent node
-**/
-void EditorSceneImporterAssimp::attach_new_node(ImportState &state, Spatial *new_node, const aiNode *node, Node *parent_node, String Name, Transform &transform) {
- ERR_FAIL_COND(new_node == NULL);
- ERR_FAIL_COND(node == NULL);
- ERR_FAIL_COND(parent_node == NULL);
- ERR_FAIL_COND(state.root == NULL);
-
- // assign properties to new godot note
- new_node->set_name(Name);
- new_node->set_transform(transform);
-
- // add element as child to parent
- parent_node->add_child(new_node);
-
- // owner must be set after
- new_node->set_owner(state.root);
-
- // cache node mapping results by name and then by aiNode*
- state.node_map[Name] = new_node;
- state.assimp_node_map[node] = new_node;
+ return mesh_node;
}
/**
* Create a light for the scene
* Automatically caches lights for lookup later
*/
-void EditorSceneImporterAssimp::create_light(ImportState &state, RecursiveState &recursive_state) {
+Spatial *EditorSceneImporterAssimp::create_light(
+ ImportState &state,
+ const String &node_name,
+ Transform &look_at_transform) {
Light *light = NULL;
- aiLight *ai_light = state.assimp_scene->mLights[state.light_cache[recursive_state.node_name]];
- ERR_FAIL_COND(!ai_light);
+ aiLight *assimp_light = state.assimp_scene->mLights[state.light_cache[node_name]];
+ ERR_FAIL_COND_V(!assimp_light, NULL);
- if (ai_light->mType == aiLightSource_DIRECTIONAL) {
+ if (assimp_light->mType == aiLightSource_DIRECTIONAL) {
light = memnew(DirectionalLight);
- Vector3 dir = Vector3(ai_light->mDirection.y, ai_light->mDirection.x, ai_light->mDirection.z);
- dir.normalize();
- Vector3 pos = Vector3(ai_light->mPosition.x, ai_light->mPosition.y, ai_light->mPosition.z);
- Vector3 up = Vector3(ai_light->mUp.x, ai_light->mUp.y, ai_light->mUp.z);
- up.normalize();
-
- Transform light_transform;
- light_transform.set_look_at(pos, pos + dir, up);
-
- recursive_state.node_transform *= light_transform;
-
- } else if (ai_light->mType == aiLightSource_POINT) {
+ } else if (assimp_light->mType == aiLightSource_POINT) {
light = memnew(OmniLight);
- Vector3 pos = Vector3(ai_light->mPosition.x, ai_light->mPosition.y, ai_light->mPosition.z);
- Transform xform;
- xform.origin = pos;
-
- recursive_state.node_transform *= xform;
-
- light->set_transform(xform);
-
- //light->set_param(Light::PARAM_ATTENUATION, 1);
- } else if (ai_light->mType == aiLightSource_SPOT) {
+ } else if (assimp_light->mType == aiLightSource_SPOT) {
light = memnew(SpotLight);
-
- Vector3 dir = Vector3(ai_light->mDirection.y, ai_light->mDirection.x, ai_light->mDirection.z);
- dir.normalize();
- Vector3 pos = Vector3(ai_light->mPosition.x, ai_light->mPosition.y, ai_light->mPosition.z);
- Vector3 up = Vector3(ai_light->mUp.x, ai_light->mUp.y, ai_light->mUp.z);
- up.normalize();
-
- Transform light_transform;
- light_transform.set_look_at(pos, pos + dir, up);
- recursive_state.node_transform *= light_transform;
-
- //light->set_param(Light::PARAM_ATTENUATION, 0.0f);
}
- ERR_FAIL_COND(light == NULL);
+ ERR_FAIL_COND_V(light == NULL, NULL);
+
+ if (assimp_light->mType != aiLightSource_POINT) {
+ Vector3 pos = Vector3(
+ assimp_light->mPosition.x,
+ assimp_light->mPosition.y,
+ assimp_light->mPosition.z);
+ Vector3 look_at = Vector3(
+ assimp_light->mDirection.y,
+ assimp_light->mDirection.x,
+ assimp_light->mDirection.z)
+ .normalized();
+ Vector3 up = Vector3(
+ assimp_light->mUp.x,
+ assimp_light->mUp.y,
+ assimp_light->mUp.z);
+
+ look_at_transform.set_look_at(pos, look_at, up);
+ }
+ // properties for light variables should be put here.
+ // not really hugely important yet but we will need them in the future
- light->set_color(Color(ai_light->mColorDiffuse.r, ai_light->mColorDiffuse.g, ai_light->mColorDiffuse.b));
- recursive_state.new_node = light;
+ light->set_color(
+ Color(assimp_light->mColorDiffuse.r, assimp_light->mColorDiffuse.g, assimp_light->mColorDiffuse.b));
- attach_new_node(state,
- recursive_state.new_node,
- recursive_state.assimp_node,
- recursive_state.parent_node,
- recursive_state.node_name,
- recursive_state.node_transform);
+ return light;
}
/**
* Create camera for the scene
*/
-void EditorSceneImporterAssimp::create_camera(ImportState &state, RecursiveState &recursive_state) {
- aiCamera *ai_camera = state.assimp_scene->mCameras[state.camera_cache[recursive_state.node_name]];
- ERR_FAIL_COND(!ai_camera);
-
- Camera *camera = memnew(Camera);
-
- float near = ai_camera->mClipPlaneNear;
+Spatial *EditorSceneImporterAssimp::create_camera(
+ ImportState &state,
+ const String &node_name,
+ Transform &look_at_transform) {
+ aiCamera *camera = state.assimp_scene->mCameras[state.camera_cache[node_name]];
+ ERR_FAIL_COND_V(!camera, NULL);
+
+ Camera *camera_node = memnew(Camera);
+ ERR_FAIL_COND_V(!camera_node, NULL);
+ float near = camera->mClipPlaneNear;
if (Math::is_equal_approx(near, 0.0f)) {
near = 0.1f;
}
- camera->set_perspective(Math::rad2deg(ai_camera->mHorizontalFOV) * 2.0f, near, ai_camera->mClipPlaneFar);
-
- Vector3 pos = Vector3(ai_camera->mPosition.x, ai_camera->mPosition.y, ai_camera->mPosition.z);
- Vector3 look_at = Vector3(ai_camera->mLookAt.y, ai_camera->mLookAt.x, ai_camera->mLookAt.z).normalized();
- Vector3 up = Vector3(ai_camera->mUp.x, ai_camera->mUp.y, ai_camera->mUp.z);
-
- Transform xform;
- xform.set_look_at(pos, look_at, up);
-
- recursive_state.new_node = camera;
-
- attach_new_node(state,
- recursive_state.new_node,
- recursive_state.assimp_node,
- recursive_state.parent_node,
- recursive_state.node_name,
- recursive_state.node_transform);
-}
-
-/**
- * Create Bone
- * Create a bone in the scene
- */
-void EditorSceneImporterAssimp::create_bone(ImportState &state, RecursiveState &recursive_state) {
- // for each armature node we must make a new skeleton but ensure it
- // has a bone in the child to ensure we don't make too many
- // the reason you must do this is because a skeleton exists per mesh?
- // and duplicate bone names are very bad for determining what is going on.
- aiBone *parent_bone_assimp = get_bone_by_name(state.assimp_scene, recursive_state.assimp_node->mParent->mName);
-
- // set to true when you want to use skeleton reference from cache.
- bool do_not_create_armature = false;
-
- // prevent more than one skeleton existing per mesh
- // * multiple root bones have this
- // * this simply filters the node out if it has already been added then references the skeleton so we know the actual skeleton for this node
- for (Map<Skeleton *, const Spatial *>::Element *key_value_pair = state.armature_skeletons.front(); key_value_pair; key_value_pair = key_value_pair->next()) {
- if (key_value_pair->value() == recursive_state.parent_node) {
- // apply the skeleton for this mesh
- recursive_state.skeleton = key_value_pair->key();
-
- // force this off
- do_not_create_armature = true;
- }
- }
-
- // check if parent was a bone
- // if parent was not a bone this is the first bone.
- // therefore parent is the 'armature'?
- // also for multi root bone support make sure we don't already have the skeleton cached.
- // if we do we must merge them - as this is all godot supports right now.
- if (!parent_bone_assimp && recursive_state.skeleton == NULL && !do_not_create_armature) {
- // create new skeleton on the root.
- recursive_state.skeleton = memnew(Skeleton);
-
- ERR_FAIL_COND(state.root == NULL);
- ERR_FAIL_COND(recursive_state.skeleton == NULL);
-
- print_verbose("Parent armature node is called " + recursive_state.parent_node->get_name());
- // store root node for this skeleton / used in animation playback and bone detection.
-
- state.armature_skeletons.insert(recursive_state.skeleton, Object::cast_to<Spatial>(recursive_state.parent_node));
-
- //skeleton->set_use_bones_in_world_transform(true);
- print_verbose("Created new FBX skeleton for armature node");
- }
-
- ERR_FAIL_COND_MSG(recursive_state.skeleton == NULL, "Mesh has invalid armature detection - report this");
-
- // this transform is a bone
- recursive_state.skeleton->add_bone(recursive_state.node_name);
-
- //ERR_FAIL_COND(recursive_state.skeleton->get_name() == "");
- print_verbose("Bone added to lookup: " + AssimpUtils::get_assimp_string(recursive_state.bone->mName));
- print_verbose("Skeleton attached to: " + recursive_state.skeleton->get_name());
- // make sure to write the bone lookup inverse so we can retrieve the mesh for this bone later
- state.bone_to_skeleton_lookup.insert(recursive_state.bone, recursive_state.skeleton);
-
- Transform xform = AssimpUtils::assimp_matrix_transform(recursive_state.bone->mOffsetMatrix);
- recursive_state.skeleton->set_bone_rest(recursive_state.skeleton->get_bone_count() - 1, xform.affine_inverse());
+ camera_node->set_perspective(Math::rad2deg(camera->mHorizontalFOV) * 2.0f, near, camera->mClipPlaneFar);
+ Vector3 pos = Vector3(camera->mPosition.x, camera->mPosition.y, camera->mPosition.z);
+ Vector3 look_at = Vector3(camera->mLookAt.y, camera->mLookAt.x, camera->mLookAt.z).normalized();
+ Vector3 up = Vector3(camera->mUp.x, camera->mUp.y, camera->mUp.z);
- // get parent node of assimp node
- const aiNode *parent_node_assimp = recursive_state.assimp_node->mParent;
-
- // ensure we have a parent
- if (parent_node_assimp != NULL) {
- int parent_bone_id = recursive_state.skeleton->find_bone(AssimpUtils::get_assimp_string(parent_node_assimp->mName));
- int current_bone_id = recursive_state.skeleton->find_bone(recursive_state.node_name);
- print_verbose("Parent bone id " + itos(parent_bone_id) + " current bone id" + itos(current_bone_id));
- print_verbose("Bone debug: " + AssimpUtils::get_assimp_string(parent_node_assimp->mName));
- recursive_state.skeleton->set_bone_parent(current_bone_id, parent_bone_id);
- }
+ look_at_transform.set_look_at(pos + look_at_transform.origin, look_at, up);
+ return camera_node;
}
/**
@@ -1309,46 +1495,30 @@ void EditorSceneImporterAssimp::create_bone(ImportState &state, RecursiveState &
*/
void EditorSceneImporterAssimp::_generate_node(
ImportState &state,
- Skeleton *skeleton,
- const aiNode *assimp_node, Node *parent_node) {
+ const aiNode *assimp_node) {
- // sanity check
- ERR_FAIL_COND(state.root == NULL);
- ERR_FAIL_COND(state.assimp_scene == NULL);
ERR_FAIL_COND(assimp_node == NULL);
- ERR_FAIL_COND(parent_node == NULL);
-
- Spatial *new_node = NULL;
+ state.nodes.push_back(assimp_node);
String node_name = AssimpUtils::get_assimp_string(assimp_node->mName);
- Transform node_transform = AssimpUtils::assimp_matrix_transform(assimp_node->mTransformation);
-
- // can safely return null - is this node a bone?
- aiBone *bone = get_bone_by_name(state.assimp_scene, assimp_node->mName);
-
- // out arguments helper - for pushing state down into creation functions
- RecursiveState recursive_state(node_transform, skeleton, new_node, node_name, assimp_node, parent_node, bone);
-
- // Creation code
- if (state.light_cache.has(node_name)) {
- create_light(state, recursive_state);
- } else if (state.camera_cache.has(node_name)) {
- create_camera(state, recursive_state);
- } else if (bone != NULL) {
- create_bone(state, recursive_state);
- } else {
- //generic node
- recursive_state.new_node = memnew(Spatial);
- attach_new_node(state,
- recursive_state.new_node,
- recursive_state.assimp_node,
- recursive_state.parent_node,
- recursive_state.node_name,
- recursive_state.node_transform);
+ String parent_name = AssimpUtils::get_assimp_string(assimp_node->mParent->mName);
+
+ // please note
+ // duplicate bone names exist
+ // this is why we only check if the bone exists
+ // so everything else is useless but the name
+ // please do not copy any other values from get_bone_by_name.
+ aiBone *parent_bone = get_bone_by_name(state.assimp_scene, assimp_node->mParent->mName);
+ aiBone *current_bone = get_bone_by_name(state.assimp_scene, assimp_node->mName);
+
+ // is this an armature
+ // parent null
+ // and this is the first bone :)
+ if (parent_bone == NULL && current_bone) {
+ state.armature_nodes.push_back(assimp_node->mParent);
+ print_verbose("found valid armature: " + parent_name);
}
- // recurse into all child elements
- for (size_t i = 0; i < recursive_state.assimp_node->mNumChildren; i++) {
- _generate_node(state, recursive_state.skeleton, recursive_state.assimp_node->mChildren[i],
- recursive_state.new_node != NULL ? recursive_state.new_node : recursive_state.parent_node);
+ for (size_t i = 0; i < assimp_node->mNumChildren; i++) {
+ _generate_node(state, assimp_node->mChildren[i]);
}
}
diff --git a/modules/assimp/editor_scene_importer_assimp.h b/modules/assimp/editor_scene_importer_assimp.h
index 787376c9af..a47d7ac46e 100644
--- a/modules/assimp/editor_scene_importer_assimp.h
+++ b/modules/assimp/editor_scene_importer_assimp.h
@@ -50,6 +50,7 @@
#include <assimp/DefaultLogger.hpp>
#include <assimp/LogStream.hpp>
#include <assimp/Logger.hpp>
+#include <map>
#include "import_state.h"
#include "import_utils.h"
@@ -72,7 +73,6 @@ public:
class EditorSceneImporterAssimp : public EditorSceneImporter {
private:
GDCLASS(EditorSceneImporterAssimp, EditorSceneImporter);
- const String ASSIMP_FBX_KEY = "_$AssimpFbx$";
struct AssetImportAnimation {
enum Interpolation {
@@ -88,40 +88,32 @@ private:
float weight;
};
- struct SkeletonHole { //nodes may be part of the skeleton by used by vertex
- String name;
- String parent;
- Transform pose;
- const aiNode *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);
+ Ref<Mesh> _generate_mesh_from_surface_indices(ImportState &state, const Vector<int> &p_surface_indices,
+ const aiNode *assimp_node, Ref<Skin> &skin,
+ Skeleton *&skeleton_assigned);
- 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);
+ Spatial *create_light(ImportState &state,
+ const String &node_name,
+ Transform &look_at_transform);
+ Spatial *create_camera(
+ ImportState &state,
+ const String &node_name,
+ Transform &look_at_transform);
// 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);
-
+ MeshInstance *create_mesh(ImportState &state, const aiNode *assimp_node, const String &node_name, Node *active_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 _generate_node(ImportState &state, const aiNode *assimp_node);
+ void _insert_animation_track(ImportState &scene, const aiAnimation *assimp_anim, int track_id,
+ int anim_fps, Ref<Animation> animation, float ticks_per_second,
+ Skeleton *skeleton, const NodePath &node_path,
+ const String &node_name, aiBone *track_bone);
void _import_animation(ImportState &state, int p_animation_index, int p_bake_fps);
-
+ Node *get_node_by_name(ImportState &state, String name);
+ aiBone *get_bone_from_stack(ImportState &state, aiString name);
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;
- float _get_fbx_fps(int32_t time_mode, const aiScene *p_scene);
template <class T>
T _interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time, AssetImportAnimation::Interpolation p_interp);
void _register_project_setting_import(const String generic, const String import_setting_string, const Vector<String> &exts, List<String> *r_extensions, const bool p_enabled) const;
@@ -148,6 +140,10 @@ public:
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);
Ref<Image> load_image(ImportState &state, const aiScene *p_scene, String p_path);
+
+ static void RegenerateBoneStack(ImportState &state);
+
+ void RegenerateBoneStack(ImportState &state, aiMesh *mesh);
};
#endif
#endif
diff --git a/modules/assimp/import_state.h b/modules/assimp/import_state.h
index 56d89ffea7..9859a88c1c 100644
--- a/modules/assimp/import_state.h
+++ b/modules/assimp/import_state.h
@@ -52,28 +52,42 @@
namespace AssimpImporter {
/** Import state is for global scene import data
- * This makes the code simpler and contains useful lookups.
- */
+ * This makes the code simpler and contains useful lookups.
+ */
struct ImportState {
String path;
+ Spatial *root;
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<const aiNode *, Node *> assimp_node_map;
Map<String, Ref<Image> > path_to_image_cache;
- bool fbx; //for some reason assimp does some things different for FBX
+
+ // Generation 3 - determinisitic iteration
+ // to lower potential recursion errors
+ List<const aiNode *> nodes;
+ Map<const aiNode *, Spatial *> flat_node_map;
AnimationPlayer *animation_player;
+
+ // Generation 3 - deterministic armatures
+ // list of armature nodes - flat and simple to parse
+ // assimp node, node in godot
+ List<aiNode *> armature_nodes;
+ Map<const aiNode *, Skeleton *> armature_skeletons;
+ Map<aiBone *, Skeleton *> skeleton_bone_map;
+ // Generation 3 - deterministic bone handling
+ // bones from the stack are popped when found
+ // this means we can detect
+ // what bones are for other armatures
+ List<aiBone *> bone_stack;
};
struct AssimpImageData {
@@ -86,14 +100,15 @@ struct AssimpImageData {
* This makes the code easier to handle too and add extra arguments without breaking things
*/
struct RecursiveState {
+ RecursiveState() {} // do not construct :)
RecursiveState(
Transform &_node_transform,
Skeleton *_skeleton,
Spatial *_new_node,
- const String &_node_name,
- const aiNode *_assimp_node,
+ String &_node_name,
+ aiNode *_assimp_node,
Node *_parent_node,
- const aiBone *_bone) :
+ aiBone *_bone) :
node_transform(_node_transform),
skeleton(_skeleton),
new_node(_new_node),
@@ -102,13 +117,13 @@ struct RecursiveState {
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;
+ Transform node_transform;
+ Skeleton *skeleton = NULL;
+ Spatial *new_node = NULL;
+ String node_name;
+ aiNode *assimp_node = NULL;
+ Node *parent_node = NULL;
+ aiBone *bone = NULL;
};
} // namespace AssimpImporter
diff --git a/thirdparty/assimp/code/Common/BaseImporter.cpp b/thirdparty/assimp/code/Common/BaseImporter.cpp
index de5018a250..b77bbfe23c 100644
--- a/thirdparty/assimp/code/Common/BaseImporter.cpp
+++ b/thirdparty/assimp/code/Common/BaseImporter.cpp
@@ -85,7 +85,7 @@ void BaseImporter::UpdateImporterScale( Importer* pImp )
double activeScale = importerScale * fileScale;
// Set active scaling
- pImp->SetPropertyFloat( AI_CONFIG_APP_SCALE_KEY, activeScale);
+ pImp->SetPropertyFloat( AI_CONFIG_APP_SCALE_KEY, static_cast<float>( activeScale) );
ASSIMP_LOG_DEBUG_F("UpdateImporterScale scale set: %f", activeScale );
}
diff --git a/thirdparty/assimp/code/Common/DefaultIOSystem.cpp b/thirdparty/assimp/code/Common/DefaultIOSystem.cpp
index d40b67de32..6fdc24dd80 100644
--- a/thirdparty/assimp/code/Common/DefaultIOSystem.cpp
+++ b/thirdparty/assimp/code/Common/DefaultIOSystem.cpp
@@ -61,83 +61,66 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp;
-// maximum path length
-// XXX http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
-#ifdef PATH_MAX
-# define PATHLIMIT PATH_MAX
-#else
-# define PATHLIMIT 4096
+#ifdef _WIN32
+static std::wstring Utf8ToWide(const char* in)
+{
+ int size = MultiByteToWideChar(CP_UTF8, 0, in, -1, nullptr, 0);
+ // size includes terminating null; std::wstring adds null automatically
+ std::wstring out(static_cast<size_t>(size) - 1, L'\0');
+ MultiByteToWideChar(CP_UTF8, 0, in, -1, &out[0], size);
+ return out;
+}
+
+static std::string WideToUtf8(const wchar_t* in)
+{
+ int size = WideCharToMultiByte(CP_UTF8, 0, in, -1, nullptr, 0, nullptr, nullptr);
+ // size includes terminating null; std::string adds null automatically
+ std::string out(static_cast<size_t>(size) - 1, '\0');
+ WideCharToMultiByte(CP_UTF8, 0, in, -1, &out[0], size, nullptr, nullptr);
+ return out;
+}
#endif
// ------------------------------------------------------------------------------------------------
// Tests for the existence of a file at the given path.
-bool DefaultIOSystem::Exists( const char* pFile) const
+bool DefaultIOSystem::Exists(const char* pFile) const
{
#ifdef _WIN32
- wchar_t fileName16[PATHLIMIT];
-
-#ifndef WindowsStore
- bool isUnicode = IsTextUnicode(pFile, static_cast<int>(strlen(pFile)), NULL) != 0;
- if (isUnicode) {
-
- MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, pFile, -1, fileName16, PATHLIMIT);
- struct __stat64 filestat;
- if (0 != _wstat64(fileName16, &filestat)) {
- return false;
- }
- } else {
-#endif
- FILE* file = ::fopen(pFile, "rb");
- if (!file)
- return false;
-
- ::fclose(file);
-#ifndef WindowsStore
+ struct __stat64 filestat;
+ if (_wstat64(Utf8ToWide(pFile).c_str(), &filestat) != 0) {
+ return false;
}
-#endif
#else
- FILE* file = ::fopen( pFile, "rb");
- if( !file)
+ FILE* file = ::fopen(pFile, "rb");
+ if (!file)
return false;
- ::fclose( file);
+ ::fclose(file);
#endif
return true;
}
// ------------------------------------------------------------------------------------------------
// Open a new file with a given path.
-IOStream* DefaultIOSystem::Open( const char* strFile, const char* strMode)
+IOStream* DefaultIOSystem::Open(const char* strFile, const char* strMode)
{
- ai_assert(NULL != strFile);
- ai_assert(NULL != strMode);
+ ai_assert(strFile != nullptr);
+ ai_assert(strMode != nullptr);
FILE* file;
#ifdef _WIN32
- wchar_t fileName16[PATHLIMIT];
-#ifndef WindowsStore
- bool isUnicode = IsTextUnicode(strFile, static_cast<int>(strlen(strFile)), NULL) != 0;
- if (isUnicode) {
- MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, strFile, -1, fileName16, PATHLIMIT);
- std::string mode8(strMode);
- file = ::_wfopen(fileName16, std::wstring(mode8.begin(), mode8.end()).c_str());
- } else {
-#endif
- file = ::fopen(strFile, strMode);
-#ifndef WindowsStore
- }
-#endif
+ file = ::_wfopen(Utf8ToWide(strFile).c_str(), Utf8ToWide(strMode).c_str());
#else
file = ::fopen(strFile, strMode);
#endif
- if (nullptr == file)
+ if (!file)
return nullptr;
- return new DefaultIOStream(file, (std::string) strFile);
+ return new DefaultIOStream(file, strFile);
}
// ------------------------------------------------------------------------------------------------
// Closes the given file and releases all resources associated with it.
-void DefaultIOSystem::Close( IOStream* pFile)
+void DefaultIOSystem::Close(IOStream* pFile)
{
delete pFile;
}
@@ -155,78 +138,56 @@ char DefaultIOSystem::getOsSeparator() const
// ------------------------------------------------------------------------------------------------
// IOSystem default implementation (ComparePaths isn't a pure virtual function)
-bool IOSystem::ComparePaths (const char* one, const char* second) const
+bool IOSystem::ComparePaths(const char* one, const char* second) const
{
- return !ASSIMP_stricmp(one,second);
+ return !ASSIMP_stricmp(one, second);
}
// ------------------------------------------------------------------------------------------------
// Convert a relative path into an absolute path
-inline static void MakeAbsolutePath (const char* in, char* _out)
+inline static std::string MakeAbsolutePath(const char* in)
{
- ai_assert(in && _out);
-#if defined( _MSC_VER ) || defined( __MINGW32__ )
-#ifndef WindowsStore
- bool isUnicode = IsTextUnicode(in, static_cast<int>(strlen(in)), NULL) != 0;
- if (isUnicode) {
- wchar_t out16[PATHLIMIT];
- wchar_t in16[PATHLIMIT];
- MultiByteToWideChar(CP_UTF8, MB_PRECOMPOSED, in, -1, out16, PATHLIMIT);
- wchar_t* ret = ::_wfullpath(out16, in16, PATHLIMIT);
- if (ret) {
- WideCharToMultiByte(CP_UTF8, MB_PRECOMPOSED, out16, -1, _out, PATHLIMIT, nullptr, nullptr);
- }
- if (!ret) {
- // preserve the input path, maybe someone else is able to fix
- // the path before it is accessed (e.g. our file system filter)
- ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in));
- strcpy(_out, in);
- }
-
- } else {
-#endif
- char* ret = :: _fullpath(_out, in, PATHLIMIT);
- if (!ret) {
- // preserve the input path, maybe someone else is able to fix
- // the path before it is accessed (e.g. our file system filter)
- ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in));
- strcpy(_out, in);
- }
-#ifndef WindowsStore
+ ai_assert(in);
+ std::string out;
+#ifdef _WIN32
+ wchar_t* ret = ::_wfullpath(nullptr, Utf8ToWide(in).c_str(), 0);
+ if (ret) {
+ out = WideToUtf8(ret);
+ free(ret);
}
-#endif
#else
- // use realpath
- char* ret = realpath(in, _out);
- if(!ret) {
+ char* ret = realpath(in, nullptr);
+ if (ret) {
+ out = ret;
+ free(ret);
+ }
+#endif
+ if (!ret) {
// preserve the input path, maybe someone else is able to fix
// the path before it is accessed (e.g. our file system filter)
ASSIMP_LOG_WARN_F("Invalid path: ", std::string(in));
- strcpy(_out,in);
+ out = in;
}
-#endif
+ return out;
}
// ------------------------------------------------------------------------------------------------
// DefaultIOSystem's more specialized implementation
-bool DefaultIOSystem::ComparePaths (const char* one, const char* second) const
+bool DefaultIOSystem::ComparePaths(const char* one, const char* second) const
{
// chances are quite good both paths are formatted identically,
// so we can hopefully return here already
- if( !ASSIMP_stricmp(one,second) )
+ if (!ASSIMP_stricmp(one, second))
return true;
- char temp1[PATHLIMIT];
- char temp2[PATHLIMIT];
-
- MakeAbsolutePath (one, temp1);
- MakeAbsolutePath (second, temp2);
+ std::string temp1 = MakeAbsolutePath(one);
+ std::string temp2 = MakeAbsolutePath(second);
- return !ASSIMP_stricmp(temp1,temp2);
+ return !ASSIMP_stricmp(temp1, temp2);
}
// ------------------------------------------------------------------------------------------------
-std::string DefaultIOSystem::fileName( const std::string &path )
+std::string DefaultIOSystem::fileName(const std::string& path)
{
std::string ret = path;
std::size_t last = ret.find_last_of("\\/");
@@ -235,16 +196,16 @@ std::string DefaultIOSystem::fileName( const std::string &path )
}
// ------------------------------------------------------------------------------------------------
-std::string DefaultIOSystem::completeBaseName( const std::string &path )
+std::string DefaultIOSystem::completeBaseName(const std::string& path)
{
std::string ret = fileName(path);
std::size_t pos = ret.find_last_of('.');
- if(pos != ret.npos) ret = ret.substr(0, pos);
+ if (pos != std::string::npos) ret = ret.substr(0, pos);
return ret;
}
// ------------------------------------------------------------------------------------------------
-std::string DefaultIOSystem::absolutePath( const std::string &path )
+std::string DefaultIOSystem::absolutePath(const std::string& path)
{
std::string ret = path;
std::size_t last = ret.find_last_of("\\/");
@@ -253,5 +214,3 @@ std::string DefaultIOSystem::absolutePath( const std::string &path )
}
// ------------------------------------------------------------------------------------------------
-
-#undef PATHLIMIT
diff --git a/thirdparty/assimp/code/Common/Exporter.cpp b/thirdparty/assimp/code/Common/Exporter.cpp
index 090b561ae0..34d49c472a 100644
--- a/thirdparty/assimp/code/Common/Exporter.cpp
+++ b/thirdparty/assimp/code/Common/Exporter.cpp
@@ -316,34 +316,6 @@ const aiExportDataBlob* Exporter::ExportToBlob( const aiScene* pScene, const cha
}
// ------------------------------------------------------------------------------------------------
-bool IsVerboseFormat(const aiMesh* mesh) {
- // avoid slow vector<bool> specialization
- std::vector<unsigned int> seen(mesh->mNumVertices,0);
- for(unsigned int i = 0; i < mesh->mNumFaces; ++i) {
- const aiFace& f = mesh->mFaces[i];
- for(unsigned int j = 0; j < f.mNumIndices; ++j) {
- if(++seen[f.mIndices[j]] == 2) {
- // found a duplicate index
- return false;
- }
- }
- }
-
- return true;
-}
-
-// ------------------------------------------------------------------------------------------------
-bool IsVerboseFormat(const aiScene* pScene) {
- for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
- if(!IsVerboseFormat(pScene->mMeshes[i])) {
- return false;
- }
- }
-
- return true;
-}
-
-// ------------------------------------------------------------------------------------------------
aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const char* pPath,
unsigned int pPreprocessing, const ExportProperties* pProperties) {
ASSIMP_BEGIN_EXCEPTION_REGION();
@@ -352,7 +324,7 @@ aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const c
// format. They will likely not be aware that there is a flag in the scene to indicate
// this, however. To avoid surprises and bug reports, we check for duplicates in
// meshes upfront.
- const bool is_verbose_format = !(pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) || IsVerboseFormat(pScene);
+ const bool is_verbose_format = !(pScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT) || MakeVerboseFormatProcess::IsVerboseFormat(pScene);
pimpl->mProgressHandler->UpdateFileWrite(0, 4);
@@ -472,7 +444,10 @@ aiReturn Exporter::Export( const aiScene* pScene, const char* pFormatId, const c
}
ExportProperties emptyProperties; // Never pass NULL ExportProperties so Exporters don't have to worry.
- exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProperties ? pProperties : &emptyProperties);
+ ExportProperties* pProp = pProperties ? (ExportProperties*)pProperties : &emptyProperties;
+ pProp->SetPropertyBool("bJoinIdenticalVertices", must_join_again);
+ exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProp);
+ exp.mExportFunction(pPath,pimpl->mIOSystem.get(),scenecopy.get(), pProp);
pimpl->mProgressHandler->UpdateFileWrite(4, 4);
} catch (DeadlyExportError& err) {
diff --git a/thirdparty/assimp/code/Common/SceneCombiner.cpp b/thirdparty/assimp/code/Common/SceneCombiner.cpp
index e445bd7434..4e6bc5b475 100644
--- a/thirdparty/assimp/code/Common/SceneCombiner.cpp
+++ b/thirdparty/assimp/code/Common/SceneCombiner.cpp
@@ -1091,6 +1091,35 @@ void SceneCombiner::Copy( aiMesh** _dest, const aiMesh* src ) {
aiFace& f = dest->mFaces[i];
GetArrayCopy(f.mIndices,f.mNumIndices);
}
+
+ // make a deep copy of all blend shapes
+ CopyPtrArray(dest->mAnimMeshes, dest->mAnimMeshes, dest->mNumAnimMeshes);
+}
+
+// ------------------------------------------------------------------------------------------------
+void SceneCombiner::Copy(aiAnimMesh** _dest, const aiAnimMesh* src) {
+ if (nullptr == _dest || nullptr == src) {
+ return;
+ }
+
+ aiAnimMesh* dest = *_dest = new aiAnimMesh();
+
+ // get a flat copy
+ ::memcpy(dest, src, sizeof(aiAnimMesh));
+
+ // and reallocate all arrays
+ GetArrayCopy(dest->mVertices, dest->mNumVertices);
+ GetArrayCopy(dest->mNormals, dest->mNumVertices);
+ GetArrayCopy(dest->mTangents, dest->mNumVertices);
+ GetArrayCopy(dest->mBitangents, dest->mNumVertices);
+
+ unsigned int n = 0;
+ while (dest->HasTextureCoords(n))
+ GetArrayCopy(dest->mTextureCoords[n++], dest->mNumVertices);
+
+ n = 0;
+ while (dest->HasVertexColors(n))
+ GetArrayCopy(dest->mColors[n++], dest->mNumVertices);
}
// ------------------------------------------------------------------------------------------------
diff --git a/thirdparty/assimp/code/FBX/FBXConverter.cpp b/thirdparty/assimp/code/FBX/FBXConverter.cpp
index 3f64016ea4..bf51884f79 100644
--- a/thirdparty/assimp/code/FBX/FBXConverter.cpp
+++ b/thirdparty/assimp/code/FBX/FBXConverter.cpp
@@ -55,6 +55,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "FBXImporter.h"
#include <assimp/StringComparison.h>
+#include <assimp/MathFunctions.h>
#include <assimp/scene.h>
@@ -67,7 +68,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <sstream>
#include <iomanip>
#include <cstdint>
-
+#include <iostream>
+#include <stdlib.h>
namespace Assimp {
namespace FBX {
@@ -119,6 +121,46 @@ namespace Assimp {
ConvertGlobalSettings();
TransferDataToScene();
+ // Now convert all bone positions to the correct mOffsetMatrix
+ std::vector<aiBone*> bones;
+ std::vector<aiNode*> nodes;
+ std::map<aiBone*, aiNode*> bone_stack;
+ BuildBoneList(out->mRootNode, out->mRootNode, out, bones);
+ BuildNodeList(out->mRootNode, nodes );
+
+
+ BuildBoneStack(out->mRootNode, out->mRootNode, out, bones, bone_stack, nodes);
+
+ std::cout << "Bone stack size: " << bone_stack.size() << std::endl;
+
+ for( std::pair<aiBone*, aiNode*> kvp : bone_stack )
+ {
+ aiBone *bone = kvp.first;
+ aiNode *bone_node = kvp.second;
+ std::cout << "active node lookup: " << bone->mName.C_Str() << std::endl;
+ // lcl transform grab - done in generate_nodes :)
+
+ //bone->mOffsetMatrix = bone_node->mTransformation;
+ aiNode * armature = GetArmatureRoot(bone_node, bones);
+
+ ai_assert(armature);
+
+ // set up bone armature id
+ bone->mArmature = armature;
+
+ // set this bone node to be referenced properly
+ ai_assert(bone_node);
+ bone->mNode = bone_node;
+
+ // apply full hierarchy to transform for basic offset
+ while( bone_node->mParent )
+ {
+ bone->mRestMatrix = bone_node->mTransformation * bone->mRestMatrix;
+ bone_node = bone_node->mParent;
+ }
+ }
+
+
// if we didn't read any meshes set the AI_SCENE_FLAGS_INCOMPLETE
// to make sure the scene passes assimp's validation. FBX files
// need not contain geometry (i.e. camera animations, raw armatures).
@@ -137,6 +179,167 @@ namespace Assimp {
std::for_each(textures.begin(), textures.end(), Util::delete_fun<aiTexture>());
}
+ /* Returns the armature root node */
+ /* This is required to be detected for a bone initially, it will recurse up until it cannot find another
+ * bone and return the node
+ * No known failure points. (yet)
+ */
+ aiNode * FBXConverter::GetArmatureRoot(aiNode *bone_node, std::vector<aiBone*> &bone_list)
+ {
+ while(bone_node)
+ {
+ if(!IsBoneNode(bone_node->mName, bone_list))
+ {
+ std::cout << "Found valid armature: " << bone_node->mName.C_Str() << std::endl;
+ return bone_node;
+ }
+
+ bone_node = bone_node->mParent;
+ }
+
+ std::cout << "can't find armature! node: " << bone_node << std::endl;
+
+ return NULL;
+ }
+
+ /* Simple IsBoneNode check if this could be a bone */
+ bool FBXConverter::IsBoneNode(const aiString &bone_name, std::vector<aiBone*>& bones )
+ {
+ for( aiBone *bone : bones)
+ {
+ if(bone->mName == bone_name)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ /* Pop this node by name from the stack if found */
+ /* Used in multiple armature situations with duplicate node / bone names */
+ /* Known flaw: cannot have nodes with bone names, will be fixed in later release */
+ /* (serious to be fixed) Known flaw: nodes which have more than one bone could be prematurely dropped from stack */
+ aiNode* FBXConverter::GetNodeFromStack(const aiString &node_name, std::vector<aiNode*> &nodes)
+ {
+ std::vector<aiNode*>::iterator iter;
+ aiNode *found = NULL;
+ for( iter = nodes.begin(); iter < nodes.end(); ++iter )
+ {
+ aiNode *element = *iter;
+ ai_assert(element);
+ // node valid and node name matches
+ if(element->mName == node_name)
+ {
+ found = element;
+ break;
+ }
+ }
+
+ if(found != NULL) {
+ // now pop the element from the node list
+ nodes.erase(iter);
+
+ return found;
+ }
+ return NULL;
+ }
+
+ /* Prepare flat node list which can be used for non recursive lookups later */
+ void FBXConverter::BuildNodeList(aiNode *current_node, std::vector<aiNode *> &nodes)
+ {
+ assert(current_node);
+
+ for( unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId)
+ {
+ aiNode *child = current_node->mChildren[nodeId];
+ assert(child);
+
+ nodes.push_back(child);
+
+ BuildNodeList(child, nodes);
+ }
+ }
+
+ /* Reprocess all nodes to calculate bone transforms properly based on the REAL mOffsetMatrix not the local. */
+ /* Before this would use mesh transforms which is wrong for bone transforms */
+ /* Before this would work for simple character skeletons but not complex meshes with multiple origins */
+ /* Source: sketch fab log cutter fbx */
+ void FBXConverter::BuildBoneList(aiNode *current_node, const aiNode * root_node, const aiScene *scene, std::vector<aiBone*> &bones )
+ {
+ assert(scene);
+ for( unsigned int nodeId = 0; nodeId < current_node->mNumChildren; ++nodeId)
+ {
+ aiNode *child = current_node->mChildren[nodeId];
+ assert(child);
+
+ // check for bones
+ for( unsigned int meshId = 0; meshId < child->mNumMeshes; ++meshId)
+ {
+ assert(child->mMeshes);
+ unsigned int mesh_index = child->mMeshes[meshId];
+ aiMesh *mesh = scene->mMeshes[ mesh_index ];
+ assert(mesh);
+
+ for( unsigned int boneId = 0; boneId < mesh->mNumBones; ++boneId)
+ {
+ aiBone *bone = mesh->mBones[boneId];
+ ai_assert(bone);
+
+ // duplicate meshes exist with the same bones sometimes :)
+ // so this must be detected
+ if( std::find(bones.begin(), bones.end(), bone) == bones.end() )
+ {
+ // add the element once
+ bones.push_back(bone);
+ }
+ }
+
+ // find mesh and get bones
+ // then do recursive lookup for bones in root node hierarchy
+ }
+
+ BuildBoneList(child, root_node, scene, bones);
+ }
+ }
+
+ /* A bone stack allows us to have multiple armatures, with the same bone names
+ * A bone stack allows us also to retrieve bones true transform even with duplicate names :)
+ */
+ void FBXConverter::BuildBoneStack(aiNode *current_node, const aiNode *root_node, const aiScene *scene,
+ const std::vector<aiBone *> &bones,
+ std::map<aiBone *, aiNode *> &bone_stack,
+ std::vector<aiNode*> &node_stack )
+ {
+ ai_assert(scene);
+ ai_assert(root_node);
+ ai_assert(!node_stack.empty());
+
+ for( aiBone * bone : bones)
+ {
+ ai_assert(bone);
+ aiNode* node = GetNodeFromStack(bone->mName, node_stack);
+ if(node == NULL)
+ {
+ node_stack.clear();
+ BuildNodeList(out->mRootNode, node_stack );
+ std::cout << "Resetting bone stack: null element " << bone->mName.C_Str() << std::endl;
+
+ node = GetNodeFromStack(bone->mName, node_stack);
+
+ if(!node) {
+ std::cout << "serious import issue armature failed to be detected?" << std::endl;
+ continue;
+ }
+ }
+
+ std::cout << "Successfully added bone to stack and have valid armature: " << bone->mName.C_Str() << std::endl;
+
+ bone_stack.insert(std::pair<aiBone*, aiNode*>(bone, node));
+ }
+ }
+
void FBXConverter::ConvertRootNode() {
out->mRootNode = new aiNode();
std::string unique_name;
@@ -144,7 +347,7 @@ namespace Assimp {
out->mRootNode->mName.Set(unique_name);
// root has ID 0
- ConvertNodes(0L, *out->mRootNode);
+ ConvertNodes(0L, out->mRootNode, out->mRootNode);
}
static std::string getAncestorBaseName(const aiNode* node)
@@ -178,8 +381,11 @@ namespace Assimp {
GetUniqueName(original_name, unique_name);
return unique_name;
}
-
- void FBXConverter::ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform) {
+ /// todo: pre-build node hierarchy
+ /// todo: get bone from stack
+ /// todo: make map of aiBone* to aiNode*
+ /// then update convert clusters to the new format
+ void FBXConverter::ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node) {
const std::vector<const Connection*>& conns = doc.GetConnectionsByDestinationSequenced(id, "Model");
std::vector<aiNode*> nodes;
@@ -190,62 +396,69 @@ namespace Assimp {
try {
for (const Connection* con : conns) {
-
// ignore object-property links
if (con->PropertyName().length()) {
- continue;
+ // really important we document why this is ignored.
+ FBXImporter::LogInfo("ignoring property link - no docs on why this is ignored");
+ continue; //?
}
+ // convert connection source object into Object base class
const Object* const object = con->SourceObject();
if (nullptr == object) {
- FBXImporter::LogWarn("failed to convert source object for Model link");
+ FBXImporter::LogError("failed to convert source object for Model link");
continue;
}
+ // FBX Model::Cube, Model::Bone001, etc elements
+ // This detects if we can cast the object into this model structure.
const Model* const model = dynamic_cast<const Model*>(object);
if (nullptr != model) {
nodes_chain.clear();
post_nodes_chain.clear();
- aiMatrix4x4 new_abs_transform = parent_transform;
-
- std::string unique_name = MakeUniqueNodeName(model, parent);
-
+ aiMatrix4x4 new_abs_transform = parent->mTransformation;
+ std::string node_name = FixNodeName(model->Name());
// even though there is only a single input node, the design of
// assimp (or rather: the complicated transformation chain that
// is employed by fbx) means that we may need multiple aiNode's
// to represent a fbx node's transformation.
- const bool need_additional_node = GenerateTransformationNodeChain(*model, unique_name, nodes_chain, post_nodes_chain);
+
+ // generate node transforms - this includes pivot data
+ // if need_additional_node is true then you t
+ const bool need_additional_node = GenerateTransformationNodeChain(*model, node_name, nodes_chain, post_nodes_chain);
+
+ // assert that for the current node we must have at least a single transform
ai_assert(nodes_chain.size());
if (need_additional_node) {
- nodes_chain.push_back(new aiNode(unique_name));
+ nodes_chain.push_back(new aiNode(node_name));
}
//setup metadata on newest node
SetupNodeMetadata(*model, *nodes_chain.back());
// link all nodes in a row
- aiNode* last_parent = &parent;
- for (aiNode* prenode : nodes_chain) {
- ai_assert(prenode);
+ aiNode* last_parent = parent;
+ for (aiNode* child : nodes_chain) {
+ ai_assert(child);
- if (last_parent != &parent) {
+ if (last_parent != parent) {
last_parent->mNumChildren = 1;
last_parent->mChildren = new aiNode*[1];
- last_parent->mChildren[0] = prenode;
+ last_parent->mChildren[0] = child;
}
- prenode->mParent = last_parent;
- last_parent = prenode;
+ child->mParent = last_parent;
+ last_parent = child;
- new_abs_transform *= prenode->mTransformation;
+ new_abs_transform *= child->mTransformation;
}
// attach geometry
- ConvertModel(*model, *nodes_chain.back(), new_abs_transform);
+ ConvertModel(*model, nodes_chain.back(), root_node, new_abs_transform);
// check if there will be any child nodes
const std::vector<const Connection*>& child_conns
@@ -257,7 +470,7 @@ namespace Assimp {
for (aiNode* postnode : post_nodes_chain) {
ai_assert(postnode);
- if (last_parent != &parent) {
+ if (last_parent != parent) {
last_parent->mNumChildren = 1;
last_parent->mChildren = new aiNode*[1];
last_parent->mChildren[0] = postnode;
@@ -279,15 +492,15 @@ namespace Assimp {
);
}
- // attach sub-nodes (if any)
- ConvertNodes(model->ID(), *last_parent, new_abs_transform);
+ // recursion call - child nodes
+ ConvertNodes(model->ID(), last_parent, root_node);
if (doc.Settings().readLights) {
- ConvertLights(*model, unique_name);
+ ConvertLights(*model, node_name);
}
if (doc.Settings().readCameras) {
- ConvertCameras(*model, unique_name);
+ ConvertCameras(*model, node_name);
}
nodes.push_back(nodes_chain.front());
@@ -296,10 +509,10 @@ namespace Assimp {
}
if (nodes.size()) {
- parent.mChildren = new aiNode*[nodes.size()]();
- parent.mNumChildren = static_cast<unsigned int>(nodes.size());
+ parent->mChildren = new aiNode*[nodes.size()]();
+ parent->mNumChildren = static_cast<unsigned int>(nodes.size());
- std::swap_ranges(nodes.begin(), nodes.end(), parent.mChildren);
+ std::swap_ranges(nodes.begin(), nodes.end(), parent->mChildren);
}
}
catch (std::exception&) {
@@ -553,7 +766,7 @@ namespace Assimp {
return;
}
- const float angle_epsilon = 1e-6f;
+ const float angle_epsilon = Math::getEpsilon<float>();
out = aiMatrix4x4();
@@ -694,7 +907,7 @@ namespace Assimp {
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 float zero_epsilon = Math::getEpsilon<float>();
const aiVector3D all_ones(1.0f, 1.0f, 1.0f);
const aiVector3D& PreRotation = PropertyGet<aiVector3D>(props, "PreRotation", ok);
@@ -802,7 +1015,7 @@ 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) == ((chainBits & chainMaskComplex) != 0));
+ //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
@@ -904,7 +1117,8 @@ namespace Assimp {
}
}
- void FBXConverter::ConvertModel(const Model& model, aiNode& nd, const aiMatrix4x4& node_global_transform)
+ void FBXConverter::ConvertModel(const Model &model, aiNode *parent, aiNode *root_node,
+ const aiMatrix4x4 &absolute_transform)
{
const std::vector<const Geometry*>& geos = model.GetGeometry();
@@ -916,11 +1130,12 @@ namespace Assimp {
const MeshGeometry* const mesh = dynamic_cast<const MeshGeometry*>(geo);
const LineGeometry* const line = dynamic_cast<const LineGeometry*>(geo);
if (mesh) {
- const std::vector<unsigned int>& indices = ConvertMesh(*mesh, model, node_global_transform, nd);
+ const std::vector<unsigned int>& indices = ConvertMesh(*mesh, model, parent, root_node,
+ absolute_transform);
std::copy(indices.begin(), indices.end(), std::back_inserter(meshes));
}
else if (line) {
- const std::vector<unsigned int>& indices = ConvertLine(*line, model, node_global_transform, nd);
+ const std::vector<unsigned int>& indices = ConvertLine(*line, model, parent, root_node);
std::copy(indices.begin(), indices.end(), std::back_inserter(meshes));
}
else {
@@ -929,15 +1144,16 @@ namespace Assimp {
}
if (meshes.size()) {
- nd.mMeshes = new unsigned int[meshes.size()]();
- nd.mNumMeshes = static_cast<unsigned int>(meshes.size());
+ parent->mMeshes = new unsigned int[meshes.size()]();
+ parent->mNumMeshes = static_cast<unsigned int>(meshes.size());
- std::swap_ranges(meshes.begin(), meshes.end(), nd.mMeshes);
+ std::swap_ranges(meshes.begin(), meshes.end(), parent->mMeshes);
}
}
- std::vector<unsigned int> FBXConverter::ConvertMesh(const MeshGeometry& mesh, const Model& model,
- const aiMatrix4x4& node_global_transform, aiNode& nd)
+ std::vector<unsigned int>
+ FBXConverter::ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node,
+ const aiMatrix4x4 &absolute_transform)
{
std::vector<unsigned int> temp;
@@ -961,18 +1177,18 @@ namespace Assimp {
const MatIndexArray::value_type base = mindices[0];
for (MatIndexArray::value_type index : mindices) {
if (index != base) {
- return ConvertMeshMultiMaterial(mesh, model, node_global_transform, nd);
+ return ConvertMeshMultiMaterial(mesh, model, parent, root_node, absolute_transform);
}
}
}
// faster code-path, just copy the data
- temp.push_back(ConvertMeshSingleMaterial(mesh, model, node_global_transform, nd));
+ temp.push_back(ConvertMeshSingleMaterial(mesh, model, absolute_transform, parent, root_node));
return temp;
}
std::vector<unsigned int> FBXConverter::ConvertLine(const LineGeometry& line, const Model& model,
- const aiMatrix4x4& node_global_transform, aiNode& nd)
+ aiNode *parent, aiNode *root_node)
{
std::vector<unsigned int> temp;
@@ -983,7 +1199,7 @@ namespace Assimp {
return temp;
}
- aiMesh* const out_mesh = SetupEmptyMesh(line, nd);
+ aiMesh* const out_mesh = SetupEmptyMesh(line, root_node);
out_mesh->mPrimitiveTypes |= aiPrimitiveType_LINE;
// copy vertices
@@ -1018,7 +1234,7 @@ namespace Assimp {
return temp;
}
- aiMesh* FBXConverter::SetupEmptyMesh(const Geometry& mesh, aiNode& nd)
+ aiMesh* FBXConverter::SetupEmptyMesh(const Geometry& mesh, aiNode *parent)
{
aiMesh* const out_mesh = new aiMesh();
meshes.push_back(out_mesh);
@@ -1035,17 +1251,18 @@ namespace Assimp {
}
else
{
- out_mesh->mName = nd.mName;
+ out_mesh->mName = parent->mName;
}
return out_mesh;
}
- unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry& mesh, const Model& model,
- const aiMatrix4x4& node_global_transform, aiNode& nd)
+ unsigned int FBXConverter::ConvertMeshSingleMaterial(const MeshGeometry &mesh, const Model &model,
+ const aiMatrix4x4 &absolute_transform, aiNode *parent,
+ aiNode *root_node)
{
const MatIndexArray& mindices = mesh.GetMaterialIndices();
- aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd);
+ aiMesh* const out_mesh = SetupEmptyMesh(mesh, parent);
const std::vector<aiVector3D>& vertices = mesh.GetVertices();
const std::vector<unsigned int>& faces = mesh.GetFaceIndexCounts();
@@ -1163,7 +1380,8 @@ namespace Assimp {
}
if (doc.Settings().readWeights && mesh.DeformerSkin() != NULL) {
- ConvertWeights(out_mesh, model, mesh, node_global_transform, NO_MATERIAL_SEPARATION);
+ ConvertWeights(out_mesh, model, mesh, absolute_transform, parent, root_node, NO_MATERIAL_SEPARATION,
+ nullptr);
}
std::vector<aiAnimMesh*> animMeshes;
@@ -1208,8 +1426,10 @@ namespace Assimp {
return static_cast<unsigned int>(meshes.size() - 1);
}
- std::vector<unsigned int> FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model,
- const aiMatrix4x4& node_global_transform, aiNode& nd)
+ std::vector<unsigned int>
+ FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, aiNode *parent,
+ aiNode *root_node,
+ const aiMatrix4x4 &absolute_transform)
{
const MatIndexArray& mindices = mesh.GetMaterialIndices();
ai_assert(mindices.size());
@@ -1220,7 +1440,7 @@ namespace Assimp {
for (MatIndexArray::value_type index : mindices) {
if (had.find(index) == had.end()) {
- indices.push_back(ConvertMeshMultiMaterial(mesh, model, index, node_global_transform, nd));
+ indices.push_back(ConvertMeshMultiMaterial(mesh, model, index, parent, root_node, absolute_transform));
had.insert(index);
}
}
@@ -1228,12 +1448,12 @@ namespace Assimp {
return indices;
}
- unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model,
- MatIndexArray::value_type index,
- const aiMatrix4x4& node_global_transform,
- aiNode& nd)
+ unsigned int FBXConverter::ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model,
+ MatIndexArray::value_type index,
+ aiNode *parent, aiNode *root_node,
+ const aiMatrix4x4 &absolute_transform)
{
- aiMesh* const out_mesh = SetupEmptyMesh(mesh, nd);
+ aiMesh* const out_mesh = SetupEmptyMesh(mesh, parent);
const MatIndexArray& mindices = mesh.GetMaterialIndices();
const std::vector<aiVector3D>& vertices = mesh.GetVertices();
@@ -1398,7 +1618,7 @@ namespace Assimp {
ConvertMaterialForMesh(out_mesh, model, mesh, index);
if (process_weights) {
- ConvertWeights(out_mesh, model, mesh, node_global_transform, index, &reverseMapping);
+ ConvertWeights(out_mesh, model, mesh, absolute_transform, parent, root_node, index, &reverseMapping);
}
std::vector<aiAnimMesh*> animMeshes;
@@ -1448,10 +1668,10 @@ namespace Assimp {
return static_cast<unsigned int>(meshes.size() - 1);
}
- void FBXConverter::ConvertWeights(aiMesh* out, const Model& model, const MeshGeometry& geo,
- const aiMatrix4x4& node_global_transform,
- unsigned int materialIndex,
- std::vector<unsigned int>* outputVertStartIndices)
+ void FBXConverter::ConvertWeights(aiMesh *out, const Model &model, const MeshGeometry &geo,
+ const aiMatrix4x4 &absolute_transform,
+ aiNode *parent, aiNode *root_node, unsigned int materialIndex,
+ std::vector<unsigned int> *outputVertStartIndices)
{
ai_assert(geo.DeformerSkin());
@@ -1462,13 +1682,12 @@ namespace Assimp {
const Skin& sk = *geo.DeformerSkin();
std::vector<aiBone*> bones;
- bones.reserve(sk.Clusters().size());
const bool no_mat_check = materialIndex == NO_MATERIAL_SEPARATION;
ai_assert(no_mat_check || outputVertStartIndices);
try {
-
+ // iterate over the sub deformers
for (const Cluster* cluster : sk.Clusters()) {
ai_assert(cluster);
@@ -1482,6 +1701,7 @@ namespace Assimp {
index_out_indices.clear();
out_indices.clear();
+
// now check if *any* of these weights is contained in the output mesh,
// taking notes so we don't need to do it twice.
for (WeightIndexArray::value_type index : indices) {
@@ -1519,68 +1739,107 @@ 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.
- ConvertCluster(bones, model, *cluster, out_indices, index_out_indices,
- count_out_indices, node_global_transform);
+ ConvertCluster(bones, cluster, out_indices, index_out_indices,
+ count_out_indices, absolute_transform, parent, root_node);
}
+
+ bone_map.clear();
}
- catch (std::exception&) {
+ catch (std::exception&e) {
std::for_each(bones.begin(), bones.end(), Util::delete_fun<aiBone>());
throw;
}
if (bones.empty()) {
+ out->mBones = nullptr;
+ out->mNumBones = 0;
return;
- }
-
- out->mBones = new aiBone*[bones.size()]();
- out->mNumBones = static_cast<unsigned int>(bones.size());
+ } else {
+ out->mBones = new aiBone *[bones.size()]();
+ out->mNumBones = static_cast<unsigned int>(bones.size());
- std::swap_ranges(bones.begin(), bones.end(), out->mBones);
+ std::swap_ranges(bones.begin(), bones.end(), out->mBones);
+ }
}
- void FBXConverter::ConvertCluster(std::vector<aiBone*>& bones, const Model& /*model*/, const Cluster& cl,
- std::vector<size_t>& out_indices,
- std::vector<size_t>& index_out_indices,
- std::vector<size_t>& count_out_indices,
- const aiMatrix4x4& node_global_transform)
+ const aiNode* FBXConverter::GetNodeByName( const aiString& name, aiNode *current_node )
{
+ aiNode * iter = current_node;
+ //printf("Child count: %d", iter->mNumChildren);
+ return iter;
+ }
- aiBone* const bone = new aiBone();
- bones.push_back(bone);
+ void FBXConverter::ConvertCluster(std::vector<aiBone *> &local_mesh_bones, const Cluster *cl,
+ std::vector<size_t> &out_indices, std::vector<size_t> &index_out_indices,
+ std::vector<size_t> &count_out_indices, const aiMatrix4x4 &absolute_transform,
+ aiNode *parent, aiNode *root_node) {
+ assert(cl); // make sure cluster valid
+ std::string deformer_name = cl->TargetNode()->Name();
+ aiString bone_name = aiString(FixNodeName(deformer_name));
- bone->mName = FixNodeName(cl.TargetNode()->Name());
+ aiBone *bone = NULL;
- bone->mOffsetMatrix = cl.TransformLink();
- bone->mOffsetMatrix.Inverse();
+ if (bone_map.count(deformer_name)) {
+ std::cout << "retrieved bone from lookup " << bone_name.C_Str() << ". Deformer: " << deformer_name
+ << std::endl;
+ bone = bone_map[deformer_name];
+ } else {
+ std::cout << "created new bone " << bone_name.C_Str() << ". Deformer: " << deformer_name << std::endl;
+ bone = new aiBone();
+ bone->mName = bone_name;
- bone->mOffsetMatrix = bone->mOffsetMatrix * node_global_transform;
+ // store local transform link for post processing
+ bone->mOffsetMatrix = cl->TransformLink();
+ bone->mOffsetMatrix.Inverse();
- bone->mNumWeights = static_cast<unsigned int>(out_indices.size());
- aiVertexWeight* cursor = bone->mWeights = new aiVertexWeight[out_indices.size()];
+ aiMatrix4x4 matrix = (aiMatrix4x4)absolute_transform;
- const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
- const WeightArray& weights = cl.GetWeights();
+ bone->mOffsetMatrix = bone->mOffsetMatrix * matrix; // * mesh_offset
- const size_t c = index_out_indices.size();
- for (size_t i = 0; i < c; ++i) {
- const size_t index_index = index_out_indices[i];
- if (index_index == no_index_sentinel) {
- continue;
- }
+ //
+ // Now calculate the aiVertexWeights
+ //
+
+ aiVertexWeight *cursor = nullptr;
+
+ bone->mNumWeights = static_cast<unsigned int>(out_indices.size());
+ cursor = bone->mWeights = new aiVertexWeight[out_indices.size()];
+
+ const size_t no_index_sentinel = std::numeric_limits<size_t>::max();
+ const WeightArray& weights = cl->GetWeights();
+
+ const size_t c = index_out_indices.size();
+ for (size_t i = 0; i < c; ++i) {
+ const size_t index_index = index_out_indices[i];
- const size_t cc = count_out_indices[i];
- for (size_t j = 0; j < cc; ++j) {
- aiVertexWeight& out_weight = *cursor++;
+ if (index_index == no_index_sentinel) {
+ continue;
+ }
- out_weight.mVertexId = static_cast<unsigned int>(out_indices[index_index + j]);
- out_weight.mWeight = weights[i];
+ const size_t cc = count_out_indices[i];
+ for (size_t j = 0; j < cc; ++j) {
+ // cursor runs from first element relative to the start
+ // or relative to the start of the next indexes.
+ aiVertexWeight& out_weight = *cursor++;
+
+ out_weight.mVertexId = static_cast<unsigned int>(out_indices[index_index + j]);
+ out_weight.mWeight = weights[i];
+ }
}
+
+ bone_map.insert(std::pair<const std::string, aiBone *>(deformer_name, bone));
}
+
+ std::cout << "bone research: Indicies size: " << out_indices.size() << std::endl;
+
+ // lookup must be populated in case something goes wrong
+ // this also allocates bones to mesh instance outside
+ local_mesh_bones.push_back(bone);
}
void FBXConverter::ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo,
@@ -2967,7 +3226,7 @@ void FBXConverter::SetShadingPropertiesRaw(aiMaterial* out_mat, const PropertyTa
TransformationCompDefaultValue(comp)
);
- const float epsilon = 1e-6f;
+ const float epsilon = Math::getEpsilon<float>();
return (dyn_val - static_val).SquareLength() < epsilon;
}
diff --git a/thirdparty/assimp/code/FBX/FBXConverter.h b/thirdparty/assimp/code/FBX/FBXConverter.h
index ab610058a4..619da92c17 100644
--- a/thirdparty/assimp/code/FBX/FBXConverter.h
+++ b/thirdparty/assimp/code/FBX/FBXConverter.h
@@ -76,16 +76,6 @@ namespace Assimp {
namespace FBX {
class Document;
-
-enum class FbxUnit {
- cm = 0,
- m,
- km,
- NumUnits,
-
- Undefined
-};
-
/**
* Convert a FBX #Document to #aiScene
* @param out Empty scene to be populated
@@ -133,7 +123,7 @@ private:
// ------------------------------------------------------------------------------------------------
// collect and assign child nodes
- void ConvertNodes(uint64_t id, aiNode& parent, const aiMatrix4x4& parent_transform = aiMatrix4x4());
+ void ConvertNodes(uint64_t id, aiNode *parent, aiNode *root_node);
// ------------------------------------------------------------------------------------------------
void ConvertLights(const Model& model, const std::string &orig_name );
@@ -189,32 +179,35 @@ private:
void SetupNodeMetadata(const Model& model, aiNode& nd);
// ------------------------------------------------------------------------------------------------
- void ConvertModel(const Model& model, aiNode& nd, const aiMatrix4x4& node_global_transform);
+ void ConvertModel(const Model &model, aiNode *parent, aiNode *root_node,
+ const aiMatrix4x4 &absolute_transform);
// ------------------------------------------------------------------------------------------------
// MeshGeometry -> aiMesh, return mesh index + 1 or 0 if the conversion failed
- std::vector<unsigned int> ConvertMesh(const MeshGeometry& mesh, const Model& model,
- const aiMatrix4x4& node_global_transform, aiNode& nd);
+ std::vector<unsigned int>
+ ConvertMesh(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node,
+ const aiMatrix4x4 &absolute_transform);
// ------------------------------------------------------------------------------------------------
std::vector<unsigned int> ConvertLine(const LineGeometry& line, const Model& model,
- const aiMatrix4x4& node_global_transform, aiNode& nd);
+ aiNode *parent, aiNode *root_node);
// ------------------------------------------------------------------------------------------------
- aiMesh* SetupEmptyMesh(const Geometry& mesh, aiNode& nd);
+ aiMesh* SetupEmptyMesh(const Geometry& mesh, aiNode *parent);
// ------------------------------------------------------------------------------------------------
- unsigned int ConvertMeshSingleMaterial(const MeshGeometry& mesh, const Model& model,
- const aiMatrix4x4& node_global_transform, aiNode& nd);
+ unsigned int ConvertMeshSingleMaterial(const MeshGeometry &mesh, const Model &model,
+ const aiMatrix4x4 &absolute_transform, aiNode *parent,
+ aiNode *root_node);
// ------------------------------------------------------------------------------------------------
- std::vector<unsigned int> ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model,
- const aiMatrix4x4& node_global_transform, aiNode& nd);
+ std::vector<unsigned int>
+ ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, aiNode *parent, aiNode *root_node,
+ const aiMatrix4x4 &absolute_transform);
// ------------------------------------------------------------------------------------------------
- unsigned int ConvertMeshMultiMaterial(const MeshGeometry& mesh, const Model& model,
- MatIndexArray::value_type index,
- const aiMatrix4x4& node_global_transform, aiNode& nd);
+ unsigned int ConvertMeshMultiMaterial(const MeshGeometry &mesh, const Model &model, MatIndexArray::value_type index,
+ aiNode *parent, aiNode *root_node, const aiMatrix4x4 &absolute_transform);
// ------------------------------------------------------------------------------------------------
static const unsigned int NO_MATERIAL_SEPARATION = /* std::numeric_limits<unsigned int>::max() */
@@ -227,17 +220,17 @@ private:
* - outputVertStartIndices is only used when a material index is specified, it gives for
* each output vertex the DOM index it maps to.
*/
- void ConvertWeights(aiMesh* out, const Model& model, const MeshGeometry& geo,
- const aiMatrix4x4& node_global_transform = aiMatrix4x4(),
- unsigned int materialIndex = NO_MATERIAL_SEPARATION,
- std::vector<unsigned int>* outputVertStartIndices = NULL);
-
+ void ConvertWeights(aiMesh *out, const Model &model, const MeshGeometry &geo, const aiMatrix4x4 &absolute_transform,
+ aiNode *parent = NULL, aiNode *root_node = NULL,
+ unsigned int materialIndex = NO_MATERIAL_SEPARATION,
+ std::vector<unsigned int> *outputVertStartIndices = NULL);
+ // lookup
+ static const aiNode* GetNodeByName( const aiString& name, aiNode *current_node );
// ------------------------------------------------------------------------------------------------
- void ConvertCluster(std::vector<aiBone*>& bones, const Model& /*model*/, const Cluster& cl,
- std::vector<size_t>& out_indices,
- std::vector<size_t>& index_out_indices,
- std::vector<size_t>& count_out_indices,
- const aiMatrix4x4& node_global_transform);
+ void ConvertCluster(std::vector<aiBone *> &local_mesh_bones, const Cluster *cl,
+ std::vector<size_t> &out_indices, std::vector<size_t> &index_out_indices,
+ std::vector<size_t> &count_out_indices, const aiMatrix4x4 &absolute_transform,
+ aiNode *parent, aiNode *root_node);
// ------------------------------------------------------------------------------------------------
void ConvertMaterialForMesh(aiMesh* out, const Model& model, const MeshGeometry& geo,
@@ -462,11 +455,30 @@ private:
using NodeNameCache = std::unordered_map<std::string, unsigned int>;
NodeNameCache mNodeNames;
+ // Deformer name is not the same as a bone name - it does contain the bone name though :)
+ // Deformer names in FBX are always unique in an FBX file.
+ std::map<const std::string, aiBone *> bone_map;
+
double anim_fps;
aiScene* const out;
const FBX::Document& doc;
- FbxUnit mCurrentUnit;
+
+ static void BuildBoneList(aiNode *current_node, const aiNode *root_node, const aiScene *scene,
+ std::vector<aiBone*>& bones);
+
+ void BuildBoneStack(aiNode *current_node, const aiNode *root_node, const aiScene *scene,
+ const std::vector<aiBone *> &bones,
+ std::map<aiBone *, aiNode *> &bone_stack,
+ std::vector<aiNode*> &node_stack );
+
+ static void BuildNodeList(aiNode *current_node, std::vector<aiNode *> &nodes);
+
+ static aiNode *GetNodeFromStack(const aiString &node_name, std::vector<aiNode *> &nodes);
+
+ static aiNode *GetArmatureRoot(aiNode *bone_node, std::vector<aiBone*> &bone_list);
+
+ static bool IsBoneNode(const aiString &bone_name, std::vector<aiBone *> &bones);
};
}
diff --git a/thirdparty/assimp/code/FBX/FBXExportProperty.cpp b/thirdparty/assimp/code/FBX/FBXExportProperty.cpp
index f8593e6295..f2a63b72b9 100644
--- a/thirdparty/assimp/code/FBX/FBXExportProperty.cpp
+++ b/thirdparty/assimp/code/FBX/FBXExportProperty.cpp
@@ -59,11 +59,7 @@ namespace FBX {
FBXExportProperty::FBXExportProperty(bool v)
: type('C')
-, data(1) {
- data = {
- uint8_t(v)
- };
-}
+, data(1, uint8_t(v)) {}
FBXExportProperty::FBXExportProperty(int16_t v)
: type('Y')
diff --git a/thirdparty/assimp/code/FBX/FBXExporter.cpp b/thirdparty/assimp/code/FBX/FBXExporter.cpp
index 8ebc8555a2..25d057df1c 100644
--- a/thirdparty/assimp/code/FBX/FBXExporter.cpp
+++ b/thirdparty/assimp/code/FBX/FBXExporter.cpp
@@ -67,6 +67,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <vector>
#include <array>
#include <unordered_set>
+#include <numeric>
// RESOURCES:
// https://code.blender.org/2013/08/fbx-binary-file-format-specification/
@@ -1005,6 +1006,9 @@ void FBXExporter::WriteObjects ()
object_node.EndProperties(outstream, binary, indent);
object_node.BeginChildren(outstream, binary, indent);
+ bool bJoinIdenticalVertices = mProperties->GetPropertyBool("bJoinIdenticalVertices", true);
+ std::vector<std::vector<int32_t>> vVertexIndice;//save vertex_indices as it is needed later
+
// geometry (aiMesh)
mesh_uids.clear();
indent = 1;
@@ -1031,21 +1035,35 @@ void FBXExporter::WriteObjects ()
std::vector<int32_t> vertex_indices;
// map of vertex value to its index in the data vector
std::map<aiVector3D,size_t> index_by_vertex_value;
- int32_t index = 0;
- for (size_t vi = 0; vi < m->mNumVertices; ++vi) {
- aiVector3D vtx = m->mVertices[vi];
- auto elem = index_by_vertex_value.find(vtx);
- if (elem == index_by_vertex_value.end()) {
- vertex_indices.push_back(index);
- index_by_vertex_value[vtx] = index;
- flattened_vertices.push_back(vtx[0]);
- flattened_vertices.push_back(vtx[1]);
- flattened_vertices.push_back(vtx[2]);
- ++index;
- } else {
- vertex_indices.push_back(int32_t(elem->second));
+ if(bJoinIdenticalVertices){
+ int32_t index = 0;
+ for (size_t vi = 0; vi < m->mNumVertices; ++vi) {
+ aiVector3D vtx = m->mVertices[vi];
+ auto elem = index_by_vertex_value.find(vtx);
+ if (elem == index_by_vertex_value.end()) {
+ vertex_indices.push_back(index);
+ index_by_vertex_value[vtx] = index;
+ flattened_vertices.push_back(vtx[0]);
+ flattened_vertices.push_back(vtx[1]);
+ flattened_vertices.push_back(vtx[2]);
+ ++index;
+ } else {
+ vertex_indices.push_back(int32_t(elem->second));
+ }
+ }
+ }
+ else { // do not join vertex, respect the export flag
+ vertex_indices.resize(m->mNumVertices);
+ std::iota(vertex_indices.begin(), vertex_indices.end(), 0);
+ for(unsigned int v = 0; v < m->mNumVertices; ++ v) {
+ aiVector3D vtx = m->mVertices[v];
+ flattened_vertices.push_back(vtx.x);
+ flattened_vertices.push_back(vtx.y);
+ flattened_vertices.push_back(vtx.z);
}
}
+ vVertexIndice.push_back(vertex_indices);
+
FBX::Node::WritePropertyNode(
"Vertices", flattened_vertices, outstream, binary, indent
);
@@ -1116,6 +1134,51 @@ void FBXExporter::WriteObjects ()
normals.End(outstream, binary, indent, true);
}
+ // colors, if any
+ // TODO only one color channel currently
+ const int32_t colorChannelIndex = 0;
+ if (m->HasVertexColors(colorChannelIndex)) {
+ FBX::Node vertexcolors("LayerElementColor", int32_t(colorChannelIndex));
+ vertexcolors.Begin(outstream, binary, indent);
+ vertexcolors.DumpProperties(outstream, binary, indent);
+ vertexcolors.EndProperties(outstream, binary, indent);
+ vertexcolors.BeginChildren(outstream, binary, indent);
+ indent = 3;
+ FBX::Node::WritePropertyNode(
+ "Version", int32_t(101), outstream, binary, indent
+ );
+ char layerName[8];
+ sprintf(layerName, "COLOR_%d", colorChannelIndex);
+ FBX::Node::WritePropertyNode(
+ "Name", (const char*)layerName, outstream, binary, indent
+ );
+ FBX::Node::WritePropertyNode(
+ "MappingInformationType", "ByPolygonVertex",
+ outstream, binary, indent
+ );
+ FBX::Node::WritePropertyNode(
+ "ReferenceInformationType", "Direct",
+ outstream, binary, indent
+ );
+ std::vector<double> color_data;
+ color_data.reserve(4 * polygon_data.size());
+ for (size_t fi = 0; fi < m->mNumFaces; ++fi) {
+ const aiFace &f = m->mFaces[fi];
+ for (size_t pvi = 0; pvi < f.mNumIndices; ++pvi) {
+ const aiColor4D &c = m->mColors[colorChannelIndex][f.mIndices[pvi]];
+ color_data.push_back(c.r);
+ color_data.push_back(c.g);
+ color_data.push_back(c.b);
+ color_data.push_back(c.a);
+ }
+ }
+ FBX::Node::WritePropertyNode(
+ "Colors", color_data, outstream, binary, indent
+ );
+ indent = 2;
+ vertexcolors.End(outstream, binary, indent, true);
+ }
+
// uvs, if any
for (size_t uvi = 0; uvi < m->GetNumUVChannels(); ++uvi) {
if (m->mNumUVComponents[uvi] > 2) {
@@ -1209,6 +1272,11 @@ void FBXExporter::WriteObjects ()
le.AddChild("Type", "LayerElementNormal");
le.AddChild("TypedIndex", int32_t(0));
layer.AddChild(le);
+ // TODO only 1 color channel currently
+ le = FBX::Node("LayerElement");
+ le.AddChild("Type", "LayerElementColor");
+ le.AddChild("TypedIndex", int32_t(0));
+ layer.AddChild(le);
le = FBX::Node("LayerElement");
le.AddChild("Type", "LayerElementMaterial");
le.AddChild("TypedIndex", int32_t(0));
@@ -1748,28 +1816,8 @@ void FBXExporter::WriteObjects ()
// connect it
connections.emplace_back("C", "OO", deformer_uid, mesh_uids[mi]);
- // we will be indexing by vertex...
- // but there might be a different number of "vertices"
- // between assimp and our output FBX.
- // this code is cut-and-pasted from the geometry section above...
- // ideally this should not be so.
- // ---
- // index of original vertex in vertex data vector
- std::vector<int32_t> vertex_indices;
- // map of vertex value to its index in the data vector
- std::map<aiVector3D,size_t> index_by_vertex_value;
- int32_t index = 0;
- for (size_t vi = 0; vi < m->mNumVertices; ++vi) {
- aiVector3D vtx = m->mVertices[vi];
- auto elem = index_by_vertex_value.find(vtx);
- if (elem == index_by_vertex_value.end()) {
- vertex_indices.push_back(index);
- index_by_vertex_value[vtx] = index;
- ++index;
- } else {
- vertex_indices.push_back(int32_t(elem->second));
- }
- }
+ //computed before
+ std::vector<int32_t>& vertex_indices = vVertexIndice[mi];
// TODO, FIXME: this won't work if anything is not in the bind pose.
// for now if such a situation is detected, we throw an exception.
@@ -2435,7 +2483,7 @@ void FBXExporter::WriteModelNodes(
void FBXExporter::WriteAnimationCurveNode(
StreamWriterLE& outstream,
int64_t uid,
- std::string name, // "T", "R", or "S"
+ const std::string& name, // "T", "R", or "S"
aiVector3D default_value,
std::string property_name, // "Lcl Translation" etc
int64_t layer_uid,
diff --git a/thirdparty/assimp/code/FBX/FBXExporter.h b/thirdparty/assimp/code/FBX/FBXExporter.h
index 71fb55c57f..1ae727eda9 100644
--- a/thirdparty/assimp/code/FBX/FBXExporter.h
+++ b/thirdparty/assimp/code/FBX/FBXExporter.h
@@ -156,7 +156,7 @@ namespace Assimp
void WriteAnimationCurveNode(
StreamWriterLE& outstream,
int64_t uid,
- std::string name, // "T", "R", or "S"
+ const std::string& name, // "T", "R", or "S"
aiVector3D default_value,
std::string property_name, // "Lcl Translation" etc
int64_t animation_layer_uid,
diff --git a/thirdparty/assimp/code/FBX/FBXImporter.cpp b/thirdparty/assimp/code/FBX/FBXImporter.cpp
index 271935a568..afcc1ddc78 100644
--- a/thirdparty/assimp/code/FBX/FBXImporter.cpp
+++ b/thirdparty/assimp/code/FBX/FBXImporter.cpp
@@ -48,26 +48,26 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "FBXImporter.h"
-#include "FBXTokenizer.h"
+#include "FBXConverter.h"
+#include "FBXDocument.h"
#include "FBXParser.h"
+#include "FBXTokenizer.h"
#include "FBXUtil.h"
-#include "FBXDocument.h"
-#include "FBXConverter.h"
-#include <assimp/StreamReader.h>
#include <assimp/MemoryIOWrapper.h>
-#include <assimp/Importer.hpp>
+#include <assimp/StreamReader.h>
#include <assimp/importerdesc.h>
+#include <assimp/Importer.hpp>
namespace Assimp {
-template<>
-const char* LogFunctions<FBXImporter>::Prefix() {
- static auto prefix = "FBX: ";
- return prefix;
+template <>
+const char *LogFunctions<FBXImporter>::Prefix() {
+ static auto prefix = "FBX: ";
+ return prefix;
}
-}
+} // namespace Assimp
using namespace Assimp;
using namespace Assimp::Formatter;
@@ -76,136 +76,123 @@ using namespace Assimp::FBX;
namespace {
static const aiImporterDesc desc = {
- "Autodesk FBX Importer",
- "",
- "",
- "",
- aiImporterFlags_SupportTextFlavour,
- 0,
- 0,
- 0,
- 0,
- "fbx"
+ "Autodesk FBX Importer",
+ "",
+ "",
+ "",
+ aiImporterFlags_SupportTextFlavour,
+ 0,
+ 0,
+ 0,
+ 0,
+ "fbx"
};
}
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by #Importer
-FBXImporter::FBXImporter()
-{
+FBXImporter::FBXImporter() {
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
-FBXImporter::~FBXImporter()
-{
+FBXImporter::~FBXImporter() {
}
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
-bool FBXImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler, bool checkSig) const
-{
- const std::string& extension = GetExtension(pFile);
- if (extension == std::string( desc.mFileExtensions ) ) {
- return true;
- }
-
- else if ((!extension.length() || checkSig) && pIOHandler) {
- // at least ASCII-FBX files usually have a 'FBX' somewhere in their head
- const char* tokens[] = {"fbx"};
- return SearchFileHeaderForToken(pIOHandler,pFile,tokens,1);
- }
- return false;
+bool FBXImporter::CanRead(const std::string &pFile, IOSystem *pIOHandler, bool checkSig) const {
+ const std::string &extension = GetExtension(pFile);
+ if (extension == std::string(desc.mFileExtensions)) {
+ return true;
+ }
+
+ else if ((!extension.length() || checkSig) && pIOHandler) {
+ // at least ASCII-FBX files usually have a 'FBX' somewhere in their head
+ const char *tokens[] = { "fbx" };
+ return SearchFileHeaderForToken(pIOHandler, pFile, tokens, 1);
+ }
+ return false;
}
// ------------------------------------------------------------------------------------------------
// List all extensions handled by this loader
-const aiImporterDesc* FBXImporter::GetInfo () const
-{
- return &desc;
+const aiImporterDesc *FBXImporter::GetInfo() const {
+ return &desc;
}
// ------------------------------------------------------------------------------------------------
// Setup configuration properties for the loader
-void FBXImporter::SetupProperties(const Importer* pImp)
-{
- settings.readAllLayers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, true);
- settings.readAllMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, false);
- settings.readMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true);
- settings.readTextures = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_TEXTURES, true);
- settings.readCameras = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_CAMERAS, true);
- settings.readLights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_LIGHTS, true);
- settings.readAnimations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS, true);
- settings.strictMode = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_STRICT_MODE, false);
- settings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true);
- settings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true);
- settings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false);
- settings.removeEmptyBones = pImp->GetPropertyBool(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true);
- settings.convertToMeters = pImp->GetPropertyBool(AI_CONFIG_FBX_CONVERT_TO_M, false);
+void FBXImporter::SetupProperties(const Importer *pImp) {
+ settings.readAllLayers = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_GEOMETRY_LAYERS, true);
+ settings.readAllMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ALL_MATERIALS, false);
+ settings.readMaterials = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_MATERIALS, true);
+ settings.readTextures = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_TEXTURES, true);
+ settings.readCameras = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_CAMERAS, true);
+ settings.readLights = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_LIGHTS, true);
+ settings.readAnimations = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_READ_ANIMATIONS, true);
+ settings.strictMode = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_STRICT_MODE, false);
+ settings.preservePivots = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, true);
+ settings.optimizeEmptyAnimationCurves = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_OPTIMIZE_EMPTY_ANIMATION_CURVES, true);
+ settings.useLegacyEmbeddedTextureNaming = pImp->GetPropertyBool(AI_CONFIG_IMPORT_FBX_EMBEDDED_TEXTURES_LEGACY_NAMING, false);
+ settings.removeEmptyBones = pImp->GetPropertyBool(AI_CONFIG_IMPORT_REMOVE_EMPTY_BONES, true);
+ settings.convertToMeters = pImp->GetPropertyBool(AI_CONFIG_FBX_CONVERT_TO_M, false);
}
// ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure.
-void FBXImporter::InternReadFile( const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
-{
- std::unique_ptr<IOStream> stream(pIOHandler->Open(pFile,"rb"));
- if (!stream) {
- ThrowException("Could not open file for reading");
- }
-
- // read entire file into memory - no streaming for this, fbx
- // files can grow large, but the assimp output data structure
- // then becomes very large, too. Assimp doesn't support
- // streaming for its output data structures so the net win with
- // streaming input data would be very low.
- std::vector<char> contents;
- contents.resize(stream->FileSize()+1);
- stream->Read( &*contents.begin(), 1, contents.size()-1 );
- contents[ contents.size() - 1 ] = 0;
- const char* const begin = &*contents.begin();
-
- // broadphase tokenizing pass in which we identify the core
- // syntax elements of FBX (brackets, commas, key:value mappings)
- TokenList tokens;
- try {
-
- bool is_binary = false;
- if (!strncmp(begin,"Kaydara FBX Binary",18)) {
- is_binary = true;
- TokenizeBinary(tokens,begin,contents.size());
- }
- else {
- Tokenize(tokens,begin);
- }
-
- // use this information to construct a very rudimentary
- // parse-tree representing the FBX scope structure
- Parser parser(tokens, is_binary);
-
- // take the raw parse-tree and convert it to a FBX DOM
- Document doc(parser,settings);
-
- FbxUnit unit(FbxUnit::cm);
- if (settings.convertToMeters) {
- unit = FbxUnit::m;
- }
-
- // convert the FBX DOM to aiScene
- ConvertToAssimpScene(pScene, doc, settings.removeEmptyBones);
-
- // 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>());
- }
- catch(std::exception&) {
- std::for_each(tokens.begin(),tokens.end(),Util::delete_fun<Token>());
- throw;
- }
+void FBXImporter::InternReadFile(const std::string &pFile, aiScene *pScene, IOSystem *pIOHandler) {
+ std::unique_ptr<IOStream> stream(pIOHandler->Open(pFile, "rb"));
+ if (!stream) {
+ ThrowException("Could not open file for reading");
+ }
+
+ // read entire file into memory - no streaming for this, fbx
+ // files can grow large, but the assimp output data structure
+ // then becomes very large, too. Assimp doesn't support
+ // streaming for its output data structures so the net win with
+ // streaming input data would be very low.
+ std::vector<char> contents;
+ contents.resize(stream->FileSize() + 1);
+ stream->Read(&*contents.begin(), 1, contents.size() - 1);
+ contents[contents.size() - 1] = 0;
+ const char *const begin = &*contents.begin();
+
+ // broadphase tokenizing pass in which we identify the core
+ // syntax elements of FBX (brackets, commas, key:value mappings)
+ TokenList tokens;
+ try {
+
+ bool is_binary = false;
+ if (!strncmp(begin, "Kaydara FBX Binary", 18)) {
+ is_binary = true;
+ TokenizeBinary(tokens, begin, contents.size());
+ } else {
+ Tokenize(tokens, begin);
+ }
+
+ // use this information to construct a very rudimentary
+ // parse-tree representing the FBX scope structure
+ Parser parser(tokens, is_binary);
+
+ // take the raw parse-tree and convert it to a FBX DOM
+ Document doc(parser, settings);
+
+ // convert the FBX DOM to aiScene
+ ConvertToAssimpScene(pScene, doc, settings.removeEmptyBones);
+
+ // 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>());
+ } catch (std::exception &) {
+ std::for_each(tokens.begin(), tokens.end(), Util::delete_fun<Token>());
+ throw;
+ }
}
#endif // !ASSIMP_BUILD_NO_FBX_IMPORTER
diff --git a/thirdparty/assimp/code/FBX/FBXMeshGeometry.cpp b/thirdparty/assimp/code/FBX/FBXMeshGeometry.cpp
index 5c9a0e309d..1386e2383c 100644
--- a/thirdparty/assimp/code/FBX/FBXMeshGeometry.cpp
+++ b/thirdparty/assimp/code/FBX/FBXMeshGeometry.cpp
@@ -610,11 +610,11 @@ void MeshGeometry::ReadVertexDataMaterials(std::vector<int>& materials_out, cons
const std::string& ReferenceInformationType)
{
const size_t face_count = m_faces.size();
- if(face_count <= 0)
+ if( 0 == face_count )
{
return;
}
-
+
// materials are handled separately. First of all, they are assigned per-face
// and not per polyvert. Secondly, ReferenceInformationType=IndexToDirect
// has a slightly different meaning for materials.
@@ -625,16 +625,14 @@ void MeshGeometry::ReadVertexDataMaterials(std::vector<int>& materials_out, cons
if (materials_out.empty()) {
FBXImporter::LogError(Formatter::format("expected material index, ignoring"));
return;
- }
- else if (materials_out.size() > 1) {
+ } else if (materials_out.size() > 1) {
FBXImporter::LogWarn(Formatter::format("expected only a single material index, ignoring all except the first one"));
materials_out.clear();
}
materials_out.resize(m_vertices.size());
std::fill(materials_out.begin(), materials_out.end(), materials_out.at(0));
- }
- else if (MappingInformationType == "ByPolygon" && ReferenceInformationType == "IndexToDirect") {
+ } else if (MappingInformationType == "ByPolygon" && ReferenceInformationType == "IndexToDirect") {
materials_out.resize(face_count);
if(materials_out.size() != face_count) {
@@ -643,18 +641,16 @@ void MeshGeometry::ReadVertexDataMaterials(std::vector<int>& materials_out, cons
);
return;
}
- }
- else {
+ } else {
FBXImporter::LogError(Formatter::format("ignoring material assignments, access type not implemented: ")
<< MappingInformationType << "," << ReferenceInformationType);
}
}
// ------------------------------------------------------------------------------------------------
ShapeGeometry::ShapeGeometry(uint64_t id, const Element& element, const std::string& name, const Document& doc)
- : Geometry(id, element, name, doc)
-{
- const Scope* sc = element.Compound();
- if (!sc) {
+: Geometry(id, element, name, doc) {
+ const Scope *sc = element.Compound();
+ if (nullptr == sc) {
DOMError("failed to read Geometry object (class: Shape), no data scope found");
}
const Element& Indexes = GetRequiredElement(*sc, "Indexes", &element);
diff --git a/thirdparty/assimp/code/PostProcessing/JoinVerticesProcess.cpp b/thirdparty/assimp/code/PostProcessing/JoinVerticesProcess.cpp
index 914ec05b46..f121fc60d3 100644
--- a/thirdparty/assimp/code/PostProcessing/JoinVerticesProcess.cpp
+++ b/thirdparty/assimp/code/PostProcessing/JoinVerticesProcess.cpp
@@ -431,31 +431,6 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
bone->mWeights = new aiVertexWeight[bone->mNumWeights];
memcpy( bone->mWeights, &newWeights[0], bone->mNumWeights * sizeof( aiVertexWeight));
}
- else {
-
- /* NOTE:
- *
- * In the algorithm above we're assuming that there are no vertices
- * with a different bone weight setup at the same position. That wouldn't
- * make sense, but it is not absolutely impossible. SkeletonMeshBuilder
- * for example generates such input data if two skeleton points
- * share the same position. Again this doesn't make sense but is
- * reality for some model formats (MD5 for example uses these special
- * nodes as attachment tags for its weapons).
- *
- * Then it is possible that a bone has no weights anymore .... as a quick
- * workaround, we're just removing these bones. If they're animated,
- * model geometry might be modified but at least there's no risk of a crash.
- */
- delete bone;
- --pMesh->mNumBones;
- for (unsigned int n = a; n < pMesh->mNumBones; ++n) {
- pMesh->mBones[n] = pMesh->mBones[n+1];
- }
-
- --a;
- ASSIMP_LOG_WARN("Removing bone -> no weights remaining");
- }
}
return pMesh->mNumVertices;
}
diff --git a/thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.cpp b/thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.cpp
index 50ff5ed93d..41f50a5ba5 100644
--- a/thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.cpp
+++ b/thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.cpp
@@ -224,3 +224,32 @@ bool MakeVerboseFormatProcess::MakeVerboseFormat(aiMesh* pcMesh)
}
return (pcMesh->mNumVertices != iOldNumVertices);
}
+
+
+// ------------------------------------------------------------------------------------------------
+bool IsMeshInVerboseFormat(const aiMesh* mesh) {
+ // avoid slow vector<bool> specialization
+ std::vector<unsigned int> seen(mesh->mNumVertices,0);
+ for(unsigned int i = 0; i < mesh->mNumFaces; ++i) {
+ const aiFace& f = mesh->mFaces[i];
+ for(unsigned int j = 0; j < f.mNumIndices; ++j) {
+ if(++seen[f.mIndices[j]] == 2) {
+ // found a duplicate index
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+// ------------------------------------------------------------------------------------------------
+bool MakeVerboseFormatProcess::IsVerboseFormat(const aiScene* pScene) {
+ for(unsigned int i = 0; i < pScene->mNumMeshes; ++i) {
+ if(!IsMeshInVerboseFormat(pScene->mMeshes[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.h b/thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.h
index 1adf8e2f69..8565d5933a 100644
--- a/thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.h
+++ b/thirdparty/assimp/code/PostProcessing/MakeVerboseFormat.h
@@ -94,6 +94,13 @@ public:
* @param pScene The imported data to work at. */
void Execute( aiScene* pScene);
+public:
+
+ // -------------------------------------------------------------------
+ /** Checks whether the scene is already in verbose format.
+ * @param pScene The data to check.
+ * @return true if the scene is already in verbose format. */
+ static bool IsVerboseFormat(const aiScene* pScene);
private:
diff --git a/thirdparty/assimp/include/assimp/MathFunctions.h b/thirdparty/assimp/include/assimp/MathFunctions.h
index cb3b696076..f49273bb34 100644
--- a/thirdparty/assimp/include/assimp/MathFunctions.h
+++ b/thirdparty/assimp/include/assimp/MathFunctions.h
@@ -39,22 +39,24 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
+#pragma once
+
/** @file MathFunctions.h
- * @brief Implementation of the math functions (gcd and lcm)
+* @brief Implementation of math utility functions.
*
- * Copied from BoostWorkaround/math
- */
+*/
+
+#include <limits>
namespace Assimp {
namespace Math {
// TODO: use binary GCD for unsigned integers ....
template < typename IntegerType >
-IntegerType gcd( IntegerType a, IntegerType b )
-{
+inline
+IntegerType gcd( IntegerType a, IntegerType b ) {
const IntegerType zero = (IntegerType)0;
- while ( true )
- {
+ while ( true ) {
if ( a == zero )
return b;
b %= a;
@@ -66,12 +68,19 @@ IntegerType gcd( IntegerType a, IntegerType b )
}
template < typename IntegerType >
-IntegerType lcm( IntegerType a, IntegerType b )
-{
+inline
+IntegerType lcm( IntegerType a, IntegerType b ) {
const IntegerType t = gcd (a,b);
- if (!t)return t;
+ if (!t)
+ return t;
return a / t * b;
}
+template<class T>
+inline
+T getEpsilon() {
+ return std::numeric_limits<T>::epsilon();
+}
+
}
}
diff --git a/thirdparty/assimp/include/assimp/SceneCombiner.h b/thirdparty/assimp/include/assimp/SceneCombiner.h
index 679a2acea4..f69a25f43b 100644
--- a/thirdparty/assimp/include/assimp/SceneCombiner.h
+++ b/thirdparty/assimp/include/assimp/SceneCombiner.h
@@ -65,6 +65,7 @@ struct aiLight;
struct aiMetadata;
struct aiBone;
struct aiMesh;
+struct aiAnimMesh;
struct aiAnimation;
struct aiNodeAnim;
@@ -363,6 +364,7 @@ public:
static void Copy (aiMesh** dest, const aiMesh* src);
// similar to Copy():
+ static void Copy (aiAnimMesh** dest, const aiAnimMesh* src);
static void Copy (aiMaterial** dest, const aiMaterial* src);
static void Copy (aiTexture** dest, const aiTexture* src);
static void Copy (aiAnimation** dest, const aiAnimation* src);
diff --git a/thirdparty/assimp/include/assimp/camera.h b/thirdparty/assimp/include/assimp/camera.h
index e573eea5d1..a9a9f76533 100644
--- a/thirdparty/assimp/include/assimp/camera.h
+++ b/thirdparty/assimp/include/assimp/camera.h
@@ -113,7 +113,6 @@ struct aiCamera
*/
C_STRUCT aiVector3D mPosition;
-
/** 'Up' - vector of the camera coordinate system relative to
* the coordinate space defined by the corresponding node.
*
@@ -134,7 +133,6 @@ struct aiCamera
*/
C_STRUCT aiVector3D mLookAt;
-
/** Half horizontal field of view angle, in radians.
*
* The field of view angle is the angle between the center
diff --git a/thirdparty/assimp/include/assimp/matrix4x4.inl b/thirdparty/assimp/include/assimp/matrix4x4.inl
index ebc67a06ec..19e684917b 100644
--- a/thirdparty/assimp/include/assimp/matrix4x4.inl
+++ b/thirdparty/assimp/include/assimp/matrix4x4.inl
@@ -5,8 +5,6 @@ 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,
@@ -53,6 +51,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "matrix4x4.h"
#include "matrix3x3.h"
#include "quaternion.h"
+#include "MathFunctions.h"
#include <algorithm>
#include <limits>
@@ -420,8 +419,8 @@ inline void aiMatrix4x4t<TReal>::Decompose (aiVector3t<TReal>& pScaling, aiQuate
}
template <typename TReal>
-inline void aiMatrix4x4t<TReal>::Decompose(aiVector3t<TReal>& pScaling, aiVector3t<TReal>& pRotation, aiVector3t<TReal>& pPosition) const
-{
+inline
+void aiMatrix4x4t<TReal>::Decompose(aiVector3t<TReal>& pScaling, aiVector3t<TReal>& pRotation, aiVector3t<TReal>& pPosition) const {
ASSIMP_MATRIX4_4_DECOMPOSE_PART;
/*
@@ -442,7 +441,7 @@ inline void aiMatrix4x4t<TReal>::Decompose(aiVector3t<TReal>& pScaling, aiVector
*/
// Use a small epsilon to solve floating-point inaccuracies
- const TReal epsilon = 10e-3f;
+ const TReal epsilon = Assimp::Math::getEpsilon<TReal>();
pRotation.y = std::asin(-vCols[0].z);// D. Angle around oY.
diff --git a/thirdparty/assimp/include/assimp/mesh.h b/thirdparty/assimp/include/assimp/mesh.h
index f1628f1f54..3e67aa7ce2 100644
--- a/thirdparty/assimp/include/assimp/mesh.h
+++ b/thirdparty/assimp/include/assimp/mesh.h
@@ -51,6 +51,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <assimp/types.h>
#include <assimp/aabb.h>
+struct aiNode;
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -264,6 +266,12 @@ struct aiBone {
//! The maximum value for this member is #AI_MAX_BONE_WEIGHTS.
unsigned int mNumWeights;
+ // The bone armature node - used for skeleton conversion
+ aiNode* mArmature;
+
+ // The bone node in the scene - used for skeleton conversion
+ aiNode* mNode;
+
//! The influence weights of this bone, by vertex index.
C_STRUCT aiVertexWeight* mWeights;
@@ -280,6 +288,11 @@ struct aiBone {
*/
C_STRUCT aiMatrix4x4 mOffsetMatrix;
+ /** Matrix used for the global rest transform
+ * This tells you directly the rest without extending as required in most game engine implementations
+ * */
+ C_STRUCT aiMatrix4x4 mRestMatrix;
+
#ifdef __cplusplus
//! Default constructor
@@ -769,7 +782,10 @@ struct aiMesh
// DO NOT REMOVE THIS ADDITIONAL CHECK
if (mNumBones && mBones) {
for( unsigned int a = 0; a < mNumBones; a++) {
- delete mBones[a];
+ if(mBones[a])
+ {
+ delete mBones[a];
+ }
}
delete [] mBones;
}