summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRevoluPowered <gordon@gordonite.tech>2019-11-05 17:54:34 +0000
committerRevoluPowered <gordon@gordonite.tech>2019-11-05 17:54:57 +0000
commit0bd877780fb7b344b5ed1d2fa7765078616f069b (patch)
tree46d359e6a6f1117015403ace17257915d088003f
parent245c99175c242bdc60a212cc84986b1a9ad5aa08 (diff)
FBX Importer Generation 3
Basic skin support Various fixes - Fixes bind mount id and mesh index - Fixed duplicate nodes being created - Prevented leak when instances being freed during re-import. - Improved camera and light transform import - skeleton handling and technical debt removal - ASSIMP: bone nodes were unlinked from bones by this code - bone_add working can distinguish between armatutes - Updated transform to be the correct offset - Added safety for state.root node errors - Fixed memory leak with leaf bones - Implemented children re-parenting for mesh template - import_animation fixes to basic skeleton data - Adds some more debug messages - Fixed Godot import segfault - Fix build failing on mono - Clear resources we use which are no longer required after import - Fixed bone duplication issue - Working skeleton_bone_map which can lookup armatures properly now. - Fixed stack being used up when mesh swapped & Fixed bone ID Additional notes: We use a mesh template which is a fake node to instance the initial mesh nodes . This is to ensure the entire tree can be built. We replace mesh node templates with the real mesh after the skeleton is available, since this makes it ensure that the fully built skeleton exists with all bones, all nodes, etc. The bone stack is a stack which pops when it finds bones, this overcomes duplicate bones with the same names. FBX has lots of these because animation armature has bone names like bone001 and another armature will also have bone001 Fixed errors in node path assignment Simple explanation: - Every mesh uses a node from the stack - Node stack was empties before completed - Every time node not found, stack must be rebuilt to maintain correct armature order :) Additional fixes: - Fixes destructor in assimp - Implements aiNode* mArmature in bone data - Implements aiNode* mParent in bone data - Fixes parent ID on bones. Implemented skeleton assignment in generate_mesh_indicies This is the only place we can safely do a lookup for the skeleton for the mesh.h I used a pointer reference so we can pass this back out, since the skeleton assignment happens inside the function. Added mesh re-parenting to the armature node this is a permanent feature and must be enforced, just like GLTF2 specification. Fixed import_animation spawning tracks per skin
-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;
}