summaryrefslogtreecommitdiff
path: root/modules
diff options
context:
space:
mode:
Diffstat (limited to 'modules')
-rw-r--r--modules/assimp/editor_scene_importer_assimp.cpp1595
-rw-r--r--modules/assimp/editor_scene_importer_assimp.h147
-rw-r--r--modules/assimp/import_state.h115
-rw-r--r--modules/assimp/import_utils.h448
4 files changed, 1144 insertions, 1161 deletions
diff --git a/modules/assimp/editor_scene_importer_assimp.cpp b/modules/assimp/editor_scene_importer_assimp.cpp
index 05f9120a07..e5439fd132 100644
--- a/modules/assimp/editor_scene_importer_assimp.cpp
+++ b/modules/assimp/editor_scene_importer_assimp.cpp
@@ -28,24 +28,13 @@
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
-#include "assimp/DefaultLogger.hpp"
-#include "assimp/Importer.hpp"
-#include "assimp/LogStream.hpp"
-#include "assimp/Logger.hpp"
-#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 "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/import/resource_importer_scene.h"
-#include "editor_scene_importer_assimp.h"
#include "editor_settings.h"
+#include "import_utils.h"
#include "scene/3d/camera.h"
#include "scene/3d/light.h"
#include "scene/3d/mesh_instance.h"
@@ -53,7 +42,19 @@
#include "scene/main/node.h"
#include "scene/resources/material.h"
#include "scene/resources/surface_tool.h"
-#include "zutil.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>
void EditorSceneImporterAssimp::get_extensions(List<String> *r_extensions) const {
@@ -92,18 +93,6 @@ uint32_t EditorSceneImporterAssimp::get_import_flags() const {
return IMPORT_SCENE;
}
-AssimpStream::AssimpStream() {
- // empty
-}
-
-AssimpStream::~AssimpStream() {
- // empty
-}
-
-void AssimpStream::write(const char *message) {
- print_verbose(String("Open Asset Import: ") + String(message).strip_edges());
-}
-
void EditorSceneImporterAssimp::_bind_methods() {
}
@@ -122,35 +111,36 @@ Node *EditorSceneImporterAssimp::import_scene(const String &p_path, uint32_t p_f
//}
importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT);
+
//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_FlipUVs |
- //aiProcess_FlipWindingOrder |
+ aiProcess_FlipWindingOrder | // very important for culling so that it is done in the correct order.
//aiProcess_DropNormals |
//aiProcess_GenSmoothNormals |
- aiProcess_JoinIdenticalVertices |
+ //aiProcess_JoinIdenticalVertices |
aiProcess_ImproveCacheLocality |
- aiProcess_LimitBoneWeights |
//aiProcess_RemoveRedundantMaterials | // Causes a crash
- aiProcess_SplitLargeMeshes |
+ //aiProcess_SplitLargeMeshes |
aiProcess_Triangulate |
aiProcess_GenUVCoords |
//aiProcess_FindDegenerates |
- aiProcess_SortByPType |
- aiProcess_FindInvalidData |
+ //aiProcess_SortByPType |
+ // aiProcess_FindInvalidData |
aiProcess_TransformUVCoords |
aiProcess_FindInstances |
//aiProcess_FixInfacingNormals |
- //aiProcess_ValidateDataStructure |
+ aiProcess_ValidateDataStructure |
aiProcess_OptimizeMeshes |
//aiProcess_OptimizeGraph |
//aiProcess_Debone |
- aiProcess_EmbedTextures |
- aiProcess_SplitByBoneCount |
+ // aiProcess_EmbedTextures |
+ //aiProcess_SplitByBoneCount |
0;
- const aiScene *scene = importer.ReadFile(s_path.c_str(),
- post_process_Steps);
- ERR_FAIL_COND_V_MSG(scene == NULL, NULL, String("Open Asset Import failed to open: ") + String(importer.GetErrorString()) + ".");
+ aiScene *scene = (aiScene *)importer.ReadFile(s_path.c_str(), post_process_Steps);
+ ERR_EXPLAIN(String("Open Asset Import failed to open: ") + String(importer.GetErrorString()));
+ ERR_FAIL_COND_V(scene == NULL, NULL);
return _generate_scene(p_path, scene, p_flags, p_bake_fps, max_bone_weights);
}
@@ -281,158 +271,7 @@ T EditorSceneImporterAssimp::_interpolate_track(const Vector<float> &p_times, co
ERR_FAIL_V(p_values[0]);
}
-void EditorSceneImporterAssimp::_generate_bone_groups(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<String, Transform> &bind_xforms) {
-
- Transform mesh_offset = _get_global_assimp_node_transform(p_assimp_node);
- //mesh_offset.basis = Basis();
- for (uint32_t i = 0; i < p_assimp_node->mNumMeshes; i++) {
- const aiMesh *mesh = state.assimp_scene->mMeshes[i];
- int owned_by = -1;
- for (uint32_t j = 0; j < mesh->mNumBones; j++) {
- const aiBone *bone = mesh->mBones[j];
- String name = _assimp_get_string(bone->mName);
-
- if (ownership.has(name)) {
- owned_by = ownership[name];
- break;
- }
- }
-
- if (owned_by == -1) { //no owned, create new unique id
- owned_by = 1;
- for (Map<String, int>::Element *E = ownership.front(); E; E = E->next()) {
- owned_by = MAX(E->get() + 1, owned_by);
- }
- }
-
- for (uint32_t j = 0; j < mesh->mNumBones; j++) {
- const aiBone *bone = mesh->mBones[j];
- String name = _assimp_get_string(bone->mName);
- ownership[name] = owned_by;
- //store the actual full path for the bone transform
- //when skeleton finds its place in the tree, it will be restored
- bind_xforms[name] = mesh_offset * _assimp_matrix_transform(bone->mOffsetMatrix);
- }
- }
-
- for (size_t i = 0; i < p_assimp_node->mNumChildren; i++) {
- _generate_bone_groups(state, p_assimp_node->mChildren[i], ownership, bind_xforms);
- }
-}
-
-void EditorSceneImporterAssimp::_fill_node_relationships(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<int, int> &skeleton_map, int p_skeleton_id, Skeleton *p_skeleton, const String &p_parent_name, int &holecount, const Vector<SkeletonHole> &p_holes, const Map<String, Transform> &bind_xforms) {
-
- String name = _assimp_get_string(p_assimp_node->mName);
- if (name == String()) {
- name = "AuxiliaryBone" + itos(holecount++);
- }
-
- Transform pose = _assimp_matrix_transform(p_assimp_node->mTransformation);
-
- if (!ownership.has(name)) {
- //not a bone, it's a hole
- Vector<SkeletonHole> holes = p_holes;
- SkeletonHole hole; //add a new one
- hole.name = name;
- hole.pose = pose;
- hole.node = p_assimp_node;
- hole.parent = p_parent_name;
- holes.push_back(hole);
-
- for (size_t i = 0; i < p_assimp_node->mNumChildren; i++) {
- _fill_node_relationships(state, p_assimp_node->mChildren[i], ownership, skeleton_map, p_skeleton_id, p_skeleton, name, holecount, holes, bind_xforms);
- }
-
- return;
- } else if (ownership[name] != p_skeleton_id) {
- //oh, it's from another skeleton? fine.. reparent all bones to this skeleton.
- int prev_owner = ownership[name];
- ERR_FAIL_COND_MSG(skeleton_map.has(prev_owner), "A previous skeleton exists for bone '" + name + "', this type of skeleton layout is unsupported.");
- for (Map<String, int>::Element *E = ownership.front(); E; E = E->next()) {
- if (E->get() == prev_owner) {
- E->get() = p_skeleton_id;
- }
- }
- }
-
- //valid bone, first fill holes if needed
- for (int i = 0; i < p_holes.size(); i++) {
-
- int bone_idx = p_skeleton->get_bone_count();
- p_skeleton->add_bone(p_holes[i].name);
- int parent_idx = p_skeleton->find_bone(p_holes[i].parent);
- if (parent_idx >= 0) {
- p_skeleton->set_bone_parent(bone_idx, parent_idx);
- }
-
- Transform pose_transform = _get_global_assimp_node_transform(p_holes[i].node);
- p_skeleton->set_bone_rest(bone_idx, pose_transform);
-
- state.bone_owners[p_holes[i].name] = skeleton_map[p_skeleton_id];
- }
-
- //finally fill bone
-
- int bone_idx = p_skeleton->get_bone_count();
- p_skeleton->add_bone(name);
- int parent_idx = p_skeleton->find_bone(p_parent_name);
- if (parent_idx >= 0) {
- p_skeleton->set_bone_parent(bone_idx, parent_idx);
- }
- //p_skeleton->set_bone_pose(bone_idx, pose);
- if (bind_xforms.has(name)) {
- //for now this is the full path to the bone in rest pose
- //when skeleton finds it's place in the tree, it will get fixed
- p_skeleton->set_bone_rest(bone_idx, bind_xforms[name]);
- }
- state.bone_owners[name] = skeleton_map[p_skeleton_id];
- //go to children
- for (size_t i = 0; i < p_assimp_node->mNumChildren; i++) {
- _fill_node_relationships(state, p_assimp_node->mChildren[i], ownership, skeleton_map, p_skeleton_id, p_skeleton, name, holecount, Vector<SkeletonHole>(), bind_xforms);
- }
-}
-
-void EditorSceneImporterAssimp::_generate_skeletons(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<int, int> &skeleton_map, const Map<String, Transform> &bind_xforms) {
-
- //find skeletons at this level, there may be multiple root nodes for each
- Map<int, List<aiNode *> > skeletons_found;
- for (size_t i = 0; i < p_assimp_node->mNumChildren; i++) {
- String name = _assimp_get_string(p_assimp_node->mChildren[i]->mName);
- if (ownership.has(name)) {
- int skeleton = ownership[name];
- if (!skeletons_found.has(skeleton)) {
- skeletons_found[skeleton] = List<aiNode *>();
- }
- skeletons_found[skeleton].push_back(p_assimp_node->mChildren[i]);
- }
- }
-
- //go via the potential skeletons found and generate the actual skeleton
- for (Map<int, List<aiNode *> >::Element *E = skeletons_found.front(); E; E = E->next()) {
- ERR_CONTINUE(skeleton_map.has(E->key())); //skeleton already exists? this can't be.. skip
- Skeleton *skeleton = memnew(Skeleton);
- //this the only way to reliably use multiple meshes with one skeleton, at the cost of less precision
- skeleton->set_use_bones_in_world_transform(true);
- skeleton_map[E->key()] = state.skeletons.size();
- state.skeletons.push_back(skeleton);
- int holecount = 1;
- //fill the bones and their relationships
- for (List<aiNode *>::Element *F = E->get().front(); F; F = F->next()) {
- _fill_node_relationships(state, F->get(), ownership, skeleton_map, E->key(), skeleton, "", holecount, Vector<SkeletonHole>(), bind_xforms);
- }
- }
-
- //go to the children
- for (uint32_t i = 0; i < p_assimp_node->mNumChildren; i++) {
- String name = _assimp_get_string(p_assimp_node->mChildren[i]->mName);
- if (ownership.has(name)) {
- continue; //a bone, so don't bother with this
- }
- _generate_skeletons(state, p_assimp_node->mChildren[i], ownership, skeleton_map, bind_xforms);
- }
-}
-
-Spatial *EditorSceneImporterAssimp::_generate_scene(const String &p_path, const aiScene *scene, const uint32_t p_flags, int p_bake_fps, const int32_t p_max_bone_weights) {
+Spatial *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;
@@ -443,60 +282,37 @@ Spatial *EditorSceneImporterAssimp::_generate_scene(const String &p_path, const
state.fbx = false;
state.animation_player = NULL;
- real_t scale_factor = 1.0f;
- {
- //handle scale
- String ext = p_path.get_file().get_extension().to_lower();
- if (ext == "fbx") {
- if (scene->mMetaData != NULL) {
- float factor = 1.0;
- scene->mMetaData->Get("UnitScaleFactor", factor);
- scale_factor = factor * 0.01f;
- }
- state.fbx = true;
- }
- }
-
- state.root->set_scale(Vector3(scale_factor, scale_factor, scale_factor));
-
//fill light map cache
for (size_t l = 0; l < scene->mNumLights; l++) {
aiLight *ai_light = scene->mLights[l];
ERR_CONTINUE(ai_light == NULL);
- state.light_cache[_assimp_get_string(ai_light->mName)] = l;
+ state.light_cache[AssimpUtils::get_assimp_string(ai_light->mName)] = l;
}
//fill camera cache
for (size_t c = 0; c < scene->mNumCameras; c++) {
aiCamera *ai_camera = scene->mCameras[c];
ERR_CONTINUE(ai_camera == NULL);
- state.camera_cache[_assimp_get_string(ai_camera->mName)] = c;
+ state.camera_cache[AssimpUtils::get_assimp_string(ai_camera->mName)] = c;
}
if (scene->mRootNode) {
- Map<String, Transform> bind_xforms; //temporary map to store bind transforms
- //guess the skeletons, since assimp does not really support them directly
- Map<String, int> ownership; //bone names to groups
- //fill this map with bone names and which group where they detected to, going mesh by mesh
- _generate_bone_groups(state, state.assimp_scene->mRootNode, ownership, bind_xforms);
- Map<int, int> skeleton_map; //maps previously created groups to actual skeletons
- //generates the skeletons when bones are found in the hierarchy, and follows them (including gaps/holes).
- _generate_skeletons(state, state.assimp_scene->mRootNode, ownership, skeleton_map, bind_xforms);
//generate nodes
for (uint32_t i = 0; i < scene->mRootNode->mNumChildren; i++) {
- _generate_node(state, scene->mRootNode->mChildren[i], state.root);
+ _generate_node(state, NULL, scene->mRootNode->mChildren[i], state.root);
}
- //assign skeletons to nodes
-
- for (Map<MeshInstance *, Skeleton *>::Element *E = state.mesh_skeletons.front(); E; E = E->next()) {
- MeshInstance *mesh = E->key();
- Skeleton *skeleton = E->get();
- NodePath skeleton_path = mesh->get_path_to(skeleton);
- mesh->set_skeleton_path(skeleton_path);
+ // 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();
}
+
+ print_verbose("generating mesh phase from skeletal mesh");
+ generate_mesh_phase_from_skeletal_mesh(state);
}
if (p_flags & IMPORT_ANIMATION && scene->mNumAnimations) {
@@ -601,12 +417,14 @@ void EditorSceneImporterAssimp::_insert_animation_track(ImportState &scene, cons
}
}
+// animation tracks are per bone
+
void EditorSceneImporterAssimp::_import_animation(ImportState &state, int p_animation_index, int p_bake_fps) {
ERR_FAIL_INDEX(p_animation_index, (int)state.assimp_scene->mNumAnimations);
const aiAnimation *anim = state.assimp_scene->mAnimations[p_animation_index];
- String name = _assimp_anim_string_to_string(anim->mName);
+ String name = AssimpUtils::get_anim_string_from_assimp(anim->mName);
if (name == String()) {
name = "Animation " + itos(p_animation_index + 1);
}
@@ -616,7 +434,7 @@ void EditorSceneImporterAssimp::_import_animation(ImportState &state, int p_anim
if (state.assimp_scene->mMetaData != NULL && Math::is_equal_approx(ticks_per_second, 0.0f)) {
int32_t time_mode = 0;
state.assimp_scene->mMetaData->Get("TimeMode", time_mode);
- ticks_per_second = _get_fbx_fps(time_mode, state.assimp_scene);
+ ticks_per_second = AssimpUtils::get_fbx_fps(time_mode, state.assimp_scene);
}
//?
@@ -637,36 +455,31 @@ void EditorSceneImporterAssimp::_import_animation(ImportState &state, int p_anim
for (size_t i = 0; i < anim->mNumChannels; i++) {
const aiNodeAnim *track = anim->mChannels[i];
- String node_name = _assimp_get_string(track->mNodeName);
- /*
- if (node_name.find(ASSIMP_FBX_KEY) != -1) {
- String p_track_type = node_name.get_slice(ASSIMP_FBX_KEY, 1);
- if (p_track_type == "_Translation" || p_track_type == "_Rotation" || p_track_type == "_Scaling") {
- continue;
- }
- }
-*/
+ String node_name = AssimpUtils::get_assimp_string(track->mNodeName);
+
if (track->mNumRotationKeys == 0 && track->mNumPositionKeys == 0 && track->mNumScalingKeys == 0) {
continue; //do not bother
}
- bool is_bone = state.bone_owners.has(node_name);
- NodePath node_path;
- Skeleton *skeleton = NULL;
+ 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();
- if (is_bone) {
- skeleton = state.skeletons[state.bone_owners[node_name]];
- String path = state.root->get_path_to(skeleton);
- path += ":" + node_name;
- node_path = path;
- } else {
+ bool is_bone = skeleton->find_bone(node_name) != -1;
+ //print_verbose("Bone " + node_name + " is bone? " + (is_bone ? "Yes" : "No"));
+ NodePath node_path;
- ERR_CONTINUE(!state.node_map.has(node_name));
- Node *node = state.node_map[node_name];
- node_path = state.root->get_path_to(node);
- }
+ 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);
+ }
- _insert_animation_track(state, anim, i, p_bake_fps, animation, ticks_per_second, skeleton, node_path, node_name);
+ _insert_animation_track(state, anim, i, p_bake_fps, animation, ticks_per_second, skeleton, node_path, node_name);
+ }
}
//blend shape tracks
@@ -675,7 +488,7 @@ void EditorSceneImporterAssimp::_import_animation(ImportState &state, int p_anim
const aiMeshMorphAnim *anim_mesh = anim->mMorphMeshChannels[i];
- const String prop_name = _assimp_get_string(anim_mesh->mName);
+ const String prop_name = AssimpUtils::get_assimp_string(anim_mesh->mName);
const String mesh_name = prop_name.split("*")[0];
ERR_CONTINUE(prop_name.split("*").size() != 2);
@@ -715,513 +528,22 @@ void EditorSceneImporterAssimp::_import_animation(ImportState &state, int p_anim
}
}
-float EditorSceneImporterAssimp::_get_fbx_fps(int32_t time_mode, const aiScene *p_scene) {
- switch (time_mode) {
- case AssetImportFbx::TIME_MODE_DEFAULT: return 24; //hack
- case AssetImportFbx::TIME_MODE_120: return 120;
- case AssetImportFbx::TIME_MODE_100: return 100;
- case AssetImportFbx::TIME_MODE_60: return 60;
- case AssetImportFbx::TIME_MODE_50: return 50;
- case AssetImportFbx::TIME_MODE_48: return 48;
- case AssetImportFbx::TIME_MODE_30: return 30;
- case AssetImportFbx::TIME_MODE_30_DROP: return 30;
- case AssetImportFbx::TIME_MODE_NTSC_DROP_FRAME: return 29.9700262f;
- case AssetImportFbx::TIME_MODE_NTSC_FULL_FRAME: return 29.9700262f;
- case AssetImportFbx::TIME_MODE_PAL: return 25;
- case AssetImportFbx::TIME_MODE_CINEMA: return 24;
- case AssetImportFbx::TIME_MODE_1000: return 1000;
- case AssetImportFbx::TIME_MODE_CINEMA_ND: return 23.976f;
- case AssetImportFbx::TIME_MODE_CUSTOM:
- int32_t frame_rate;
- p_scene->mMetaData->Get("FrameRate", frame_rate);
- return frame_rate;
- }
- return 0;
-}
-
-Transform EditorSceneImporterAssimp::_get_global_assimp_node_transform(const aiNode *p_current_node) {
- aiNode const *current_node = p_current_node;
- Transform xform;
- while (current_node != NULL) {
- xform = _assimp_matrix_transform(current_node->mTransformation) * xform;
- current_node = current_node->mParent;
- }
- return xform;
-}
-
-Ref<Texture> EditorSceneImporterAssimp::_load_texture(ImportState &state, String p_path) {
- Vector<String> split_path = p_path.get_basename().split("*");
- if (split_path.size() == 2) {
- size_t texture_idx = split_path[1].to_int();
- ERR_FAIL_COND_V(texture_idx >= state.assimp_scene->mNumTextures, Ref<Texture>());
- aiTexture *tex = state.assimp_scene->mTextures[texture_idx];
- String filename = _assimp_raw_string_to_string(tex->mFilename);
- filename = filename.get_file();
- print_verbose("Open Asset Import: Loading embedded texture " + filename);
- if (tex->mHeight == 0) {
- if (tex->CheckFormat("png")) {
- Ref<Image> img = Image::_png_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth);
- ERR_FAIL_COND_V(img.is_null(), Ref<Texture>());
-
- Ref<ImageTexture> t;
- t.instance();
- t->create_from_image(img);
- t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY);
- return t;
- } else if (tex->CheckFormat("jpg")) {
- Ref<Image> img = Image::_jpg_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth);
- ERR_FAIL_COND_V(img.is_null(), Ref<Texture>());
- Ref<ImageTexture> t;
- t.instance();
- t->create_from_image(img);
- t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY);
- return t;
- } else if (tex->CheckFormat("dds")) {
- ERR_FAIL_V_MSG(Ref<Texture>(), "Open Asset Import: Embedded dds not implemented.");
- //Ref<Image> img = Image::_dds_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth);
- //ERR_FAIL_COND_V(img.is_null(), Ref<Texture>());
- //Ref<ImageTexture> t;
- //t.instance();
- //t->create_from_image(img);
- //t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY);
- //return t;
- }
- } else {
- Ref<Image> img;
- img.instance();
- PoolByteArray arr;
- uint32_t size = tex->mWidth * tex->mHeight;
- arr.resize(size);
- memcpy(arr.write().ptr(), tex->pcData, size);
- ERR_FAIL_COND_V(arr.size() % 4 != 0, Ref<Texture>());
- //ARGB8888 to RGBA8888
- for (int32_t i = 0; i < arr.size() / 4; i++) {
- arr.write().ptr()[(4 * i) + 3] = arr[(4 * i) + 0];
- arr.write().ptr()[(4 * i) + 0] = arr[(4 * i) + 1];
- arr.write().ptr()[(4 * i) + 1] = arr[(4 * i) + 2];
- arr.write().ptr()[(4 * i) + 2] = arr[(4 * i) + 3];
- }
- img->create(tex->mWidth, tex->mHeight, true, Image::FORMAT_RGBA8, arr);
- ERR_FAIL_COND_V(img.is_null(), Ref<Texture>());
-
- Ref<ImageTexture> t;
- t.instance();
- t->create_from_image(img);
- t->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY);
- return t;
- }
- return Ref<Texture>();
- }
- Ref<Texture> p_texture = ResourceLoader::load(p_path, "Texture");
- return p_texture;
-}
-
-Ref<Material> EditorSceneImporterAssimp::_generate_material_from_index(ImportState &state, int p_index, bool p_double_sided) {
-
- ERR_FAIL_INDEX_V(p_index, (int)state.assimp_scene->mNumMaterials, Ref<Material>());
-
- aiMaterial *ai_material = state.assimp_scene->mMaterials[p_index];
- Ref<SpatialMaterial> mat;
- mat.instance();
-
- int32_t mat_two_sided = 0;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_TWOSIDED, mat_two_sided)) {
- if (mat_two_sided > 0) {
- mat->set_cull_mode(SpatialMaterial::CULL_DISABLED);
- }
- }
-
- //const String mesh_name = _assimp_get_string(ai_mesh->mName);
- aiString mat_name;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_NAME, mat_name)) {
- mat->set_name(_assimp_get_string(mat_name));
- }
-
- aiTextureType tex_normal = aiTextureType_NORMALS;
- {
- aiString ai_filename = aiString();
- String filename = "";
- aiTextureMapMode map_mode[2];
-
- if (AI_SUCCESS == ai_material->GetTexture(tex_normal, 0, &ai_filename, NULL, NULL, NULL, NULL, map_mode)) {
- filename = _assimp_raw_string_to_string(ai_filename);
- String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/"));
- bool found = false;
- _find_texture_path(state.path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(state, path);
-
- if (texture.is_valid()) {
- _set_texture_mapping_mode(map_mode, texture);
- mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true);
- mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture);
- }
- }
- }
- }
-
- {
- aiString ai_filename = aiString();
- String filename = "";
-
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_NORMAL_TEXTURE, ai_filename)) {
- filename = _assimp_raw_string_to_string(ai_filename);
- String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/"));
- bool found = false;
- _find_texture_path(state.path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(state, path);
- if (texture != NULL) {
- mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true);
- mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture);
- }
- }
- }
- }
-
- aiTextureType tex_emissive = aiTextureType_EMISSIVE;
-
- if (ai_material->GetTextureCount(tex_emissive) > 0) {
-
- aiString ai_filename = aiString();
- String filename = "";
- aiTextureMapMode map_mode[2];
-
- if (AI_SUCCESS == ai_material->GetTexture(tex_emissive, 0, &ai_filename, NULL, NULL, NULL, NULL, map_mode)) {
- filename = _assimp_raw_string_to_string(ai_filename);
- String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/"));
- bool found = false;
- _find_texture_path(state.path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(state, path);
- if (texture != NULL) {
- _set_texture_mapping_mode(map_mode, texture);
- mat->set_feature(SpatialMaterial::FEATURE_EMISSION, true);
- mat->set_texture(SpatialMaterial::TEXTURE_EMISSION, texture);
- }
- }
- }
- }
-
- aiTextureType tex_albedo = aiTextureType_DIFFUSE;
- if (ai_material->GetTextureCount(tex_albedo) > 0) {
-
- aiString ai_filename = aiString();
- String filename = "";
- aiTextureMapMode map_mode[2];
- if (AI_SUCCESS == ai_material->GetTexture(tex_albedo, 0, &ai_filename, NULL, NULL, NULL, NULL, map_mode)) {
- filename = _assimp_raw_string_to_string(ai_filename);
- String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/"));
- bool found = false;
- _find_texture_path(state.path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(state, path);
- if (texture != NULL) {
- if (texture->get_data()->detect_alpha() != Image::ALPHA_NONE) {
- _set_texture_mapping_mode(map_mode, texture);
- mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
- mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
- }
- mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture);
- }
- }
- }
- } else {
- aiColor4D clr_diffuse;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_COLOR_DIFFUSE, clr_diffuse)) {
- 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_albedo(Color(clr_diffuse.r, clr_diffuse.g, clr_diffuse.b, clr_diffuse.a));
- }
- }
-
- aiString tex_gltf_base_color_path = aiString();
- aiTextureMapMode map_mode[2];
- if (AI_SUCCESS == ai_material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_TEXTURE, &tex_gltf_base_color_path, NULL, NULL, NULL, NULL, map_mode)) {
- String filename = _assimp_raw_string_to_string(tex_gltf_base_color_path);
- String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/"));
- bool found = false;
- _find_texture_path(state.path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(state, path);
- _find_texture_path(state.path, path, found);
- if (texture != NULL) {
- if (texture->get_data()->detect_alpha() == Image::ALPHA_BLEND) {
- _set_texture_mapping_mode(map_mode, texture);
- mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
- mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
- }
- mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture);
- }
- }
- } else {
- aiColor4D pbr_base_color;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_BASE_COLOR_FACTOR, pbr_base_color)) {
- if (Math::is_equal_approx(pbr_base_color.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_albedo(Color(pbr_base_color.r, pbr_base_color.g, pbr_base_color.b, pbr_base_color.a));
- }
- }
- {
- aiString tex_fbx_pbs_base_color_path = aiString();
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_BASE_COLOR_TEXTURE, tex_fbx_pbs_base_color_path)) {
- String filename = _assimp_raw_string_to_string(tex_fbx_pbs_base_color_path);
- String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/"));
- bool found = false;
- _find_texture_path(state.path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(state, path);
- _find_texture_path(state.path, path, found);
- if (texture != NULL) {
- if (texture->get_data()->detect_alpha() == Image::ALPHA_BLEND) {
- mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
- mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
- }
- mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture);
- }
- }
- } else {
- aiColor4D pbr_base_color;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_BASE_COLOR_FACTOR, pbr_base_color)) {
- mat->set_albedo(Color(pbr_base_color.r, pbr_base_color.g, pbr_base_color.b, pbr_base_color.a));
- }
- }
-
- aiUVTransform pbr_base_color_uv_xform;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_BASE_COLOR_UV_XFORM, pbr_base_color_uv_xform)) {
- mat->set_uv1_offset(Vector3(pbr_base_color_uv_xform.mTranslation.x, pbr_base_color_uv_xform.mTranslation.y, 0.0f));
- mat->set_uv1_scale(Vector3(pbr_base_color_uv_xform.mScaling.x, pbr_base_color_uv_xform.mScaling.y, 1.0f));
- }
- }
-
- {
- aiString tex_fbx_pbs_normal_path = aiString();
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_NORMAL_TEXTURE, tex_fbx_pbs_normal_path)) {
- String filename = _assimp_raw_string_to_string(tex_fbx_pbs_normal_path);
- String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/"));
- bool found = false;
- _find_texture_path(state.path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(state, path);
- _find_texture_path(state.path, path, found);
- if (texture != NULL) {
- mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true);
- mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture);
- }
- }
- }
- }
-
- if (p_double_sided) {
- mat->set_cull_mode(SpatialMaterial::CULL_DISABLED);
- }
-
- {
- aiString tex_fbx_stingray_normal_path = aiString();
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_TEXTURE, tex_fbx_stingray_normal_path)) {
- String filename = _assimp_raw_string_to_string(tex_fbx_stingray_normal_path);
- String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/"));
- bool found = false;
- _find_texture_path(state.path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(state, path);
- _find_texture_path(state.path, path, found);
- if (texture != NULL) {
- mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true);
- mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, texture);
- }
- }
- }
- }
-
- {
- aiString tex_fbx_pbs_base_color_path = aiString();
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_TEXTURE, tex_fbx_pbs_base_color_path)) {
- String filename = _assimp_raw_string_to_string(tex_fbx_pbs_base_color_path);
- String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/"));
- bool found = false;
- _find_texture_path(state.path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(state, path);
- _find_texture_path(state.path, path, found);
- if (texture != NULL) {
- if (texture->get_data()->detect_alpha() == Image::ALPHA_BLEND) {
- mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
- mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
- }
- mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture);
- }
- }
- } else {
- aiColor4D pbr_base_color;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_BASE_COLOR_FACTOR, pbr_base_color)) {
- mat->set_albedo(Color(pbr_base_color.r, pbr_base_color.g, pbr_base_color.b, pbr_base_color.a));
- }
- }
-
- aiUVTransform pbr_base_color_uv_xform;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_UV_XFORM, pbr_base_color_uv_xform)) {
- mat->set_uv1_offset(Vector3(pbr_base_color_uv_xform.mTranslation.x, pbr_base_color_uv_xform.mTranslation.y, 0.0f));
- mat->set_uv1_scale(Vector3(pbr_base_color_uv_xform.mScaling.x, pbr_base_color_uv_xform.mScaling.y, 1.0f));
- }
- }
-
- {
- aiString tex_fbx_pbs_emissive_path = aiString();
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_TEXTURE, tex_fbx_pbs_emissive_path)) {
- String filename = _assimp_raw_string_to_string(tex_fbx_pbs_emissive_path);
- String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/"));
- bool found = false;
- _find_texture_path(state.path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(state, path);
- _find_texture_path(state.path, path, found);
- if (texture != NULL) {
- if (texture->get_data()->detect_alpha() == Image::ALPHA_BLEND) {
- mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
- mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
- }
- mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, texture);
- }
- }
- } else {
- aiColor4D pbr_emmissive_color;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_FACTOR, pbr_emmissive_color)) {
- mat->set_emission(Color(pbr_emmissive_color.r, pbr_emmissive_color.g, pbr_emmissive_color.b, pbr_emmissive_color.a));
- }
- }
-
- real_t pbr_emission_intensity;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_INTENSITY_FACTOR, pbr_emission_intensity)) {
- mat->set_emission_energy(pbr_emission_intensity);
- }
- }
-
- aiString tex_gltf_pbr_metallicroughness_path;
- if (AI_SUCCESS == ai_material->GetTexture(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLICROUGHNESS_TEXTURE, &tex_gltf_pbr_metallicroughness_path)) {
- String filename = _assimp_raw_string_to_string(tex_gltf_pbr_metallicroughness_path);
- String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/"));
- bool found = false;
- _find_texture_path(state.path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(state, path);
- if (texture != NULL) {
- mat->set_texture(SpatialMaterial::TEXTURE_METALLIC, texture);
- mat->set_metallic_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_BLUE);
- mat->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, texture);
- mat->set_roughness_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GREEN);
- }
- }
- } else {
- float pbr_roughness = 0.0f;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_ROUGHNESS_FACTOR, pbr_roughness)) {
- mat->set_roughness(pbr_roughness);
- }
- float pbr_metallic = 0.0f;
-
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_GLTF_PBRMETALLICROUGHNESS_METALLIC_FACTOR, pbr_metallic)) {
- mat->set_metallic(pbr_metallic);
- }
- }
- {
- aiString tex_fbx_pbs_metallic_path;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_TEXTURE, tex_fbx_pbs_metallic_path)) {
- String filename = _assimp_raw_string_to_string(tex_fbx_pbs_metallic_path);
- String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/"));
- bool found = false;
- _find_texture_path(state.path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(state, path);
- if (texture != NULL) {
- mat->set_texture(SpatialMaterial::TEXTURE_METALLIC, texture);
- mat->set_metallic_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE);
- }
- }
- } else {
- float pbr_metallic = 0.0f;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_FACTOR, pbr_metallic)) {
- mat->set_metallic(pbr_metallic);
- }
- }
-
- aiString tex_fbx_pbs_rough_path;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_TEXTURE, tex_fbx_pbs_rough_path)) {
- String filename = _assimp_raw_string_to_string(tex_fbx_pbs_rough_path);
- String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/"));
- bool found = false;
- _find_texture_path(state.path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(state, path);
- if (texture != NULL) {
- mat->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, texture);
- mat->set_roughness_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE);
- }
- }
- } else {
- float pbr_roughness = 0.04f;
-
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_FACTOR, pbr_roughness)) {
- mat->set_roughness(pbr_roughness);
- }
- }
- }
-
- {
- aiString tex_fbx_pbs_metallic_path;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_METALNESS_TEXTURE, tex_fbx_pbs_metallic_path)) {
- String filename = _assimp_raw_string_to_string(tex_fbx_pbs_metallic_path);
- String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/"));
- bool found = false;
- _find_texture_path(state.path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(state, path);
- if (texture != NULL) {
- mat->set_texture(SpatialMaterial::TEXTURE_METALLIC, texture);
- mat->set_metallic_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE);
- }
- }
- } else {
- float pbr_metallic = 0.0f;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_METALNESS_FACTOR, pbr_metallic)) {
- mat->set_metallic(pbr_metallic);
- }
- }
-
- aiString tex_fbx_pbs_rough_path;
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_TEXTURE, tex_fbx_pbs_rough_path)) {
- String filename = _assimp_raw_string_to_string(tex_fbx_pbs_rough_path);
- String path = state.path.get_base_dir().plus_file(filename.replace("\\", "/"));
- bool found = false;
- _find_texture_path(state.path, path, found);
- if (found) {
- Ref<Texture> texture = _load_texture(state, path);
- if (texture != NULL) {
- mat->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, texture);
- mat->set_roughness_texture_channel(SpatialMaterial::TEXTURE_CHANNEL_GRAYSCALE);
- }
- }
- } else {
- float pbr_roughness = 0.04f;
-
- if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_FACTOR, pbr_roughness)) {
- mat->set_roughness(pbr_roughness);
- }
- }
- }
-
- return mat;
-}
-
-Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportState &state, const Vector<int> &p_surface_indices, Skeleton *p_skeleton, bool p_double_sided_material) {
+//
+// Mesh Generation from indicies ? 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<ArrayMesh> mesh;
mesh.instance();
bool has_uvs = false;
+ //
+ // Process Vertex Weights
+ //
for (int i = 0; i < p_surface_indices.size(); i++) {
const unsigned int mesh_idx = p_surface_indices[i];
const aiMesh *ai_mesh = state.assimp_scene->mMeshes[mesh_idx];
@@ -1231,7 +553,7 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportS
if (p_skeleton) {
for (size_t b = 0; b < ai_mesh->mNumBones; b++) {
aiBone *bone = ai_mesh->mBones[b];
- String bone_name = _assimp_get_string(bone->mName);
+ 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.
@@ -1244,7 +566,6 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportS
uint32_t vertex_index = ai_weights.mVertexId;
bi.bone = bone_index;
bi.weight = ai_weights.mWeight;
- ;
if (!vertex_weights.has(vertex_index)) {
vertex_weights[vertex_index] = Vector<BoneInfo>();
@@ -1255,23 +576,34 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportS
}
}
+ //
+ // Create mesh from data from assimp
+ //
+
Ref<SurfaceTool> st;
st.instance();
st->begin(Mesh::PRIMITIVE_TRIANGLES);
for (size_t j = 0; j < ai_mesh->mNumVertices; j++) {
+
+ // Get the texture coordinates if they exist
if (ai_mesh->HasTextureCoords(0)) {
has_uvs = true;
st->add_uv(Vector2(ai_mesh->mTextureCoords[0][j].x, 1.0f - ai_mesh->mTextureCoords[0][j].y));
}
+
if (ai_mesh->HasTextureCoords(1)) {
has_uvs = true;
st->add_uv2(Vector2(ai_mesh->mTextureCoords[1][j].x, 1.0f - ai_mesh->mTextureCoords[1][j].y));
}
+
+ // 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);
st->add_color(color);
}
+
+ // Work out normal calculations? - this needs work it doesn't work properly on huestos
if (ai_mesh->mNormals != NULL) {
const aiVector3D normals = ai_mesh->mNormals[j];
const Vector3 godot_normal = Vector3(normals.x, normals.y, normals.z);
@@ -1286,6 +618,7 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportS
}
}
+ // We have vertex weights right?
if (vertex_weights.has(j)) {
Vector<BoneInfo> bone_info = vertex_weights[j];
@@ -1293,6 +626,8 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportS
bones.resize(bone_info.size());
Vector<float> weights;
weights.resize(bone_info.size());
+
+ // todo? do we really need to loop over all bones? - assimp may have helper to find all influences on this vertex.
for (int k = 0; k < bone_info.size(); k++) {
bones.write[k] = bone_info[k].bone;
weights.write[k] = bone_info[k].weight;
@@ -1302,30 +637,152 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportS
st->add_weights(weights);
}
+ // Assign vertex
const aiVector3D pos = ai_mesh->mVertices[j];
+
+ // note we must include node offset transform as this is relative to world space not local space.
Vector3 godot_pos = Vector3(pos.x, pos.y, pos.z);
st->add_vertex(godot_pos);
}
+ // fire replacement for face handling
for (size_t j = 0; j < ai_mesh->mNumFaces; j++) {
const aiFace face = ai_mesh->mFaces[j];
- ERR_CONTINUE(face.mNumIndices != 3);
- Vector<size_t> order;
- order.push_back(2);
- order.push_back(1);
- order.push_back(0);
- for (int32_t k = 0; k < order.size(); k++) {
- st->add_index(face.mIndices[order[k]]);
+ for (unsigned int k = 0; k < face.mNumIndices; k++) {
+ st->add_index(face.mIndices[k]);
}
}
+
if (ai_mesh->HasTangentsAndBitangents() == false && has_uvs) {
st->generate_tangents();
}
- Ref<Material> material;
+ aiMaterial *ai_material = state.assimp_scene->mMaterials[ai_mesh->mMaterialIndex];
+ Ref<SpatialMaterial> mat;
+ mat.instance();
+
+ int32_t mat_two_sided = 0;
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_TWOSIDED, mat_two_sided)) {
+ if (mat_two_sided > 0) {
+ mat->set_cull_mode(SpatialMaterial::CULL_DISABLED);
+ }
+ }
+
+ const String mesh_name = AssimpUtils::get_assimp_string(ai_mesh->mName);
+ aiString mat_name;
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_NAME, mat_name)) {
+ mat->set_name(AssimpUtils::get_assimp_string(mat_name));
+ }
+
+ // Culling handling for meshes
+
+ // cull all back faces
+ mat->set_cull_mode(SpatialMaterial::CULL_BACK);
+
+ // Now process materials
+ aiTextureType tex_diffuse = aiTextureType_DIFFUSE;
+ {
+ String filename, path;
+ AssimpImageData image_data;
+
+ if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_diffuse, filename, path, image_data)) {
+ AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
+
+ // anything transparent must be culled
+ 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_texture(SpatialMaterial::TEXTURE_ALBEDO, image_data.texture);
+ }
+
+ aiColor4D clr_diffuse;
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_COLOR_DIFFUSE, clr_diffuse)) {
+ 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_albedo(Color(clr_diffuse.r, clr_diffuse.g, clr_diffuse.b, clr_diffuse.a));
+ }
+ }
+
+ aiTextureType tex_normal = aiTextureType_NORMALS;
+ {
+ String filename, path;
+ Ref<ImageTexture> texture;
+ AssimpImageData image_data;
+
+ // Process texture normal map
+ if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_normal, filename, path, image_data)) {
+ AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
+ mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true);
+ mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, image_data.texture);
+ } else {
+ aiString texture_path;
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_NORMAL_TEXTURE, AI_PROPERTIES, texture_path)) {
+ if (AssimpUtils::CreateAssimpTexture(state, texture_path, filename, path, image_data)) {
+ mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true);
+ mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, image_data.texture);
+ }
+ }
+ }
+ }
+
+ aiTextureType tex_emissive = aiTextureType_EMISSIVE;
+ {
+ String filename = "";
+ String path = "";
+ Ref<Image> texture;
+ AssimpImageData image_data;
+
+ if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_emissive, filename, path, image_data)) {
+ AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
+ mat->set_feature(SpatialMaterial::FEATURE_EMISSION, true);
+ mat->set_texture(SpatialMaterial::TEXTURE_EMISSION, image_data.texture);
+ } 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 (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);
+ }
+ } else {
+ float pbr_emission = 0.0f;
+ if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_EMISSIVE_FACTOR, AI_NULL, pbr_emission)) {
+ mat->set_emission(Color(pbr_emission, pbr_emission, pbr_emission, 1.0f));
+ }
+ }
+ }
+ }
+
+ aiTextureType tex_specular = aiTextureType_SPECULAR;
+ {
+ String filename, path;
+ Ref<ImageTexture> texture;
+ AssimpImageData image_data;
+
+ // Process texture normal map
+ if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_specular, filename, path, image_data)) {
+ AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
+ mat->set_texture(SpatialMaterial::TEXTURE_METALLIC, image_data.texture);
+ }
+ }
- if (!state.material_cache.has(ai_mesh->mMaterialIndex)) {
- material = _generate_material_from_index(state, ai_mesh->mMaterialIndex, p_double_sided_material);
+ aiTextureType tex_roughness = aiTextureType_SHININESS;
+ {
+ String filename, path;
+ Ref<ImageTexture> texture;
+ AssimpImageData image_data;
+
+ // Process texture normal map
+ if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_roughness, filename, path, image_data)) {
+ AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
+ mat->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, image_data.texture);
+ }
}
Array array_mesh = st->commit_to_arrays();
@@ -1335,16 +792,13 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportS
Map<uint32_t, String> morph_mesh_idx_names;
for (size_t j = 0; j < ai_mesh->mNumAnimMeshes; j++) {
- if (i == 0) {
- //only do this the first time
- String ai_anim_mesh_name = _assimp_get_string(ai_mesh->mAnimMeshes[j]->mName);
- mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED);
- if (ai_anim_mesh_name.empty()) {
- ai_anim_mesh_name = String("morph_") + itos(j);
- }
- mesh->add_blend_shape(ai_anim_mesh_name);
+ String ai_anim_mesh_name = AssimpUtils::get_assimp_string(ai_mesh->mAnimMeshes[j]->mName);
+ mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED);
+ if (ai_anim_mesh_name.empty()) {
+ ai_anim_mesh_name = String("morph_") + itos(j);
}
-
+ mesh->add_blend_shape(ai_anim_mesh_name);
+ morph_mesh_idx_names.insert(j, ai_anim_mesh_name);
Array array_copy;
array_copy.resize(VisualServer::ARRAY_MAX);
@@ -1363,12 +817,11 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportS
vertices.write()[l] = position;
}
PoolVector3Array new_vertices = array_copy[VisualServer::ARRAY_VERTEX].duplicate(true);
-
- for (int32_t l = 0; l < vertices.size(); l++) {
+ ERR_CONTINUE(vertices.size() != new_vertices.size());
+ for (int32_t l = 0; l < new_vertices.size(); l++) {
PoolVector3Array::Write w = new_vertices.write();
w[l] = vertices[l];
}
- ERR_CONTINUE(vertices.size() != new_vertices.size());
array_copy[VisualServer::ARRAY_VERTEX] = new_vertices;
}
@@ -1382,7 +835,7 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportS
colors.write()[l] = color;
}
PoolColorArray new_colors = array_copy[VisualServer::ARRAY_COLOR].duplicate(true);
-
+ ERR_CONTINUE(colors.size() != new_colors.size());
for (int32_t l = 0; l < colors.size(); l++) {
PoolColorArray::Write w = new_colors.write();
w[l] = colors[l];
@@ -1394,12 +847,12 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportS
PoolVector3Array normals;
normals.resize(num_vertices);
for (size_t l = 0; l < num_vertices; l++) {
- const aiVector3D ai_normal = ai_mesh->mAnimMeshes[i]->mNormals[l];
+ const aiVector3D ai_normal = ai_mesh->mAnimMeshes[j]->mNormals[l];
Vector3 normal = Vector3(ai_normal.x, ai_normal.y, ai_normal.z);
normals.write()[l] = normal;
}
PoolVector3Array new_normals = array_copy[VisualServer::ARRAY_NORMAL].duplicate(true);
-
+ ERR_CONTINUE(normals.size() != new_normals.size());
for (int l = 0; l < normals.size(); l++) {
PoolVector3Array::Write w = new_normals.write();
w[l] = normals[l];
@@ -1412,7 +865,7 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportS
tangents.resize(num_vertices);
PoolColorArray::Write w = tangents.write();
for (size_t l = 0; l < num_vertices; l++) {
- _calc_tangent_from_mesh(ai_mesh, j, l, l, w);
+ AssimpUtils::calc_tangent_from_mesh(ai_mesh, j, l, l, w);
}
PoolRealArray new_tangents = array_copy[VisualServer::ARRAY_TANGENT].duplicate(true);
ERR_CONTINUE(new_tangents.size() != tangents.size() * 4);
@@ -1422,340 +875,388 @@ Ref<Mesh> EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportS
new_tangents.write()[l + 2] = tangents[l].b;
new_tangents.write()[l + 3] = tangents[l].a;
}
-
array_copy[VisualServer::ARRAY_TANGENT] = new_tangents;
}
morphs[j] = array_copy;
}
-
mesh->add_surface_from_arrays(primitive, array_mesh, morphs);
- mesh->surface_set_material(i, material);
- mesh->surface_set_name(i, _assimp_get_string(ai_mesh->mName));
+ mesh->surface_set_material(i, mat);
+ mesh->surface_set_name(i, AssimpUtils::get_assimp_string(ai_mesh->mName));
}
return mesh;
}
-void EditorSceneImporterAssimp::_generate_node(ImportState &state, const aiNode *p_assimp_node, Node *p_parent) {
+/* 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];
- Spatial *new_node = NULL;
- String node_name = _assimp_get_string(p_assimp_node->mName);
- Transform node_transform = _assimp_matrix_transform(p_assimp_node->mTransformation);
-
- if (p_assimp_node->mNumMeshes > 0) {
- /* MESH NODE */
- Ref<Mesh> mesh;
- Skeleton *skeleton = NULL;
- {
+ // iterate over all the bones on the mesh for this node only!
+ for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; boneIndex++) {
- //see if we have mesh cache for this.
- Vector<int> surface_indices;
- for (uint32_t i = 0; i < p_assimp_node->mNumMeshes; i++) {
- int mesh_index = p_assimp_node->mMeshes[i];
- surface_indices.push_back(mesh_index);
-
- //take the chance and attempt to find the skeleton from the bones
- if (!skeleton) {
- aiMesh *ai_mesh = state.assimp_scene->mMeshes[p_assimp_node->mMeshes[i]];
- for (uint32_t j = 0; j < ai_mesh->mNumBones; j++) {
- aiBone *bone = ai_mesh->mBones[j];
- String bone_name = _assimp_get_string(bone->mName);
- if (state.bone_owners.has(bone_name)) {
- skeleton = state.skeletons[state.bone_owners[bone_name]];
- break;
- }
- }
- }
- }
- surface_indices.sort();
- String mesh_key;
- for (int i = 0; i < surface_indices.size(); i++) {
- if (i > 0) {
- mesh_key += ":";
- }
- mesh_key += itos(surface_indices[i]);
+ aiBone *bone = mesh->mBones[boneIndex];
+ if (bone->mName == bone_name) {
+ printf("matched bone by name: %s\n", bone->mName.C_Str());
+ return bone;
}
+ }
+ }
- if (!state.mesh_cache.has(mesh_key)) {
- //adding cache
- aiString cull_mode; //cull is on mesh, which is kind of stupid tbh
- bool double_sided_material = false;
- if (p_assimp_node->mMetaData) {
- p_assimp_node->mMetaData->Get("Culling", cull_mode);
- }
- if (cull_mode.length != 0 && cull_mode == aiString("CullingOff")) {
- double_sided_material = true;
- }
+ return NULL;
+}
- mesh = _generate_mesh_from_surface_indices(state, surface_indices, skeleton, double_sided_material);
- state.mesh_cache[mesh_key] = mesh;
+/**
+ * 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) {
+ /* MESH NODE */
+ Ref<Mesh> mesh;
+ Skeleton *skeleton = NULL;
+ // 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();
+
+ 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.
}
-
- mesh = state.mesh_cache[mesh_key];
}
+ surface_indices.push_back(mesh_index);
+ }
- MeshInstance *mesh_node = memnew(MeshInstance);
- if (skeleton) {
- state.mesh_skeletons[mesh_node] = skeleton;
+ surface_indices.sort();
+ String mesh_key;
+ for (int i = 0; i < surface_indices.size(); i++) {
+ if (i > 0) {
+ mesh_key += ":";
}
- mesh_node->set_mesh(mesh);
- new_node = mesh_node;
-
- } else if (state.light_cache.has(node_name)) {
-
- Light *light = NULL;
- aiLight *ai_light = state.assimp_scene->mLights[state.light_cache[node_name]];
- ERR_FAIL_COND(!ai_light);
+ mesh_key += itos(surface_indices[i]);
+ }
- if (ai_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();
+ if (!state.mesh_cache.has(mesh_key)) {
+ mesh = _generate_mesh_from_surface_indices(state, surface_indices, assimp_node, skeleton);
+ state.mesh_cache[mesh_key] = mesh;
+ }
- Transform light_transform;
- light_transform.set_look_at(pos, pos + dir, up);
+ //Transform transform = recursive_state.node_transform;
- node_transform *= light_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();
+ }
- } else if (ai_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;
+ MeshInstance *mesh_node = memnew(MeshInstance);
+ mesh = state.mesh_cache[mesh_key];
+ mesh_node->set_mesh(mesh);
- node_transform *= xform;
+ attach_new_node(state,
+ mesh_node,
+ assimp_node,
+ parent_node,
+ node_name,
+ node_transform);
- light->set_transform(xform);
+ // set this once and for all
+ if (skeleton != NULL) {
+ // root must be informed of its new child
+ parent_node->add_child(skeleton);
- //light->set_param(Light::PARAM_ATTENUATION, 1);
- } else if (ai_light->mType == aiLightSource_SPOT) {
- light = memnew(SpotLight);
+ // owner must be set after adding to tree
+ skeleton->set_owner(state.root);
- 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();
+ skeleton->set_transform(node_transform);
- Transform light_transform;
- light_transform.set_look_at(pos, pos + dir, up);
- node_transform *= light_transform;
+ // must be done after added to tree
+ mesh_node->set_skeleton_path(mesh_node->get_path_to(skeleton));
+ }
+}
- //light->set_param(Light::PARAM_ATTENUATION, 0.0f);
- }
- ERR_FAIL_COND(light == NULL);
- light->set_color(Color(ai_light->mColorDiffuse.r, ai_light->mColorDiffuse.g, ai_light->mColorDiffuse.b));
- new_node = light;
- } else if (state.camera_cache.has(node_name)) {
+/** 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();
- aiCamera *ai_camera = state.assimp_scene->mCameras[state.camera_cache[node_name]];
- ERR_FAIL_COND(!ai_camera);
+ ERR_CONTINUE(assimp_node == NULL);
+ ERR_CONTINUE(parent_node == NULL);
- Camera *camera = memnew(Camera);
+ String node_name = AssimpUtils::get_assimp_string(assimp_node->mName);
+ Transform node_transform = AssimpUtils::assimp_matrix_transform(assimp_node->mTransformation);
- float near = ai_camera->mClipPlaneNear;
- if (Math::is_equal_approx(near, 0.0f)) {
- near = 0.1f;
+ if (assimp_node->mNumMeshes > 0) {
+ create_mesh(state, assimp_node, node_name, current_node, parent_node, node_transform);
}
- 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);
+/**
+ * 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;
+}
+/**
+ * Create a light for the scene
+ * Automatically caches lights for lookup later
+ */
+void EditorSceneImporterAssimp::create_light(ImportState &state, RecursiveState &recursive_state) {
+ Light *light = NULL;
+ aiLight *ai_light = state.assimp_scene->mLights[state.light_cache[recursive_state.node_name]];
+ ERR_FAIL_COND(!ai_light);
+
+ if (ai_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) {
+ light = memnew(OmniLight);
+ Vector3 pos = Vector3(ai_light->mPosition.x, ai_light->mPosition.y, ai_light->mPosition.z);
Transform xform;
- xform.set_look_at(pos, look_at, up);
-
- new_node = camera;
- } else if (state.bone_owners.has(node_name)) {
-
- //have to actually put the skeleton somewhere, you know.
- Skeleton *skeleton = state.skeletons[state.bone_owners[node_name]];
- if (skeleton->get_parent()) {
- //a bone for a skeleton already added..
- //could go downwards here to add meshes children of skeleton bones
- //but let's not support it for now.
- return;
- }
- //restore rest poses to local, now that we know where the skeleton finally is
- Transform skeleton_transform;
- if (p_assimp_node->mParent) {
- skeleton_transform = _get_global_assimp_node_transform(p_assimp_node->mParent);
- }
- for (int i = 0; i < skeleton->get_bone_count(); i++) {
- Transform rest = skeleton_transform.affine_inverse() * skeleton->get_bone_rest(i);
- skeleton->set_bone_rest(i, rest.affine_inverse());
- }
+ xform.origin = pos;
- skeleton->localize_rests();
- node_name = "Skeleton"; //don't use the bone root name
- node_transform = Transform(); //don't transform
+ recursive_state.node_transform *= xform;
- new_node = skeleton;
- } else {
- //generic node
- new_node = memnew(Spatial);
- }
+ light->set_transform(xform);
- {
+ //light->set_param(Light::PARAM_ATTENUATION, 1);
+ } else if (ai_light->mType == aiLightSource_SPOT) {
+ light = memnew(SpotLight);
- new_node->set_name(node_name);
- new_node->set_transform(node_transform);
- p_parent->add_child(new_node);
- new_node->set_owner(state.root);
- }
+ 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();
- state.node_map[node_name] = new_node;
+ Transform light_transform;
+ light_transform.set_look_at(pos, pos + dir, up);
+ recursive_state.node_transform *= light_transform;
- for (size_t i = 0; i < p_assimp_node->mNumChildren; i++) {
- _generate_node(state, p_assimp_node->mChildren[i], new_node);
+ //light->set_param(Light::PARAM_ATTENUATION, 0.0f);
}
-}
+ ERR_FAIL_COND(light == NULL);
-void EditorSceneImporterAssimp::_calc_tangent_from_mesh(const aiMesh *ai_mesh, int i, int tri_index, int index, PoolColorArray::Write &w) {
- const aiVector3D normals = ai_mesh->mAnimMeshes[i]->mNormals[tri_index];
- const Vector3 godot_normal = Vector3(normals.x, normals.y, normals.z);
- const aiVector3D tangent = ai_mesh->mAnimMeshes[i]->mTangents[tri_index];
- const Vector3 godot_tangent = Vector3(tangent.x, tangent.y, tangent.z);
- const aiVector3D bitangent = ai_mesh->mAnimMeshes[i]->mBitangents[tri_index];
- const Vector3 godot_bitangent = Vector3(bitangent.x, bitangent.y, bitangent.z);
- float d = godot_normal.cross(godot_tangent).dot(godot_bitangent) > 0.0f ? 1.0f : -1.0f;
- Color plane_tangent = Color(tangent.x, tangent.y, tangent.z, d);
- w[index] = plane_tangent;
+ light->set_color(Color(ai_light->mColorDiffuse.r, ai_light->mColorDiffuse.g, ai_light->mColorDiffuse.b));
+ recursive_state.new_node = light;
+
+ attach_new_node(state,
+ recursive_state.new_node,
+ recursive_state.assimp_node,
+ recursive_state.parent_node,
+ recursive_state.node_name,
+ recursive_state.node_transform);
}
-void EditorSceneImporterAssimp::_set_texture_mapping_mode(aiTextureMapMode *map_mode, Ref<Texture> texture) {
- ERR_FAIL_COND(map_mode == NULL);
- aiTextureMapMode tex_mode = aiTextureMapMode::aiTextureMapMode_Wrap;
- //for (size_t i = 0; i < 3; i++) {
- tex_mode = map_mode[0];
- //}
- int32_t flags = Texture::FLAGS_DEFAULT;
- if (tex_mode == aiTextureMapMode_Wrap) {
- //Default
- } else if (tex_mode == aiTextureMapMode_Clamp) {
- flags = flags & ~Texture::FLAG_REPEAT;
- } else if (tex_mode == aiTextureMapMode_Mirror) {
- flags = flags | Texture::FLAG_MIRRORED_REPEAT;
+/**
+ * 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;
+ if (Math::is_equal_approx(near, 0.0f)) {
+ near = 0.1f;
}
- texture->set_flags(flags);
-}
+ camera->set_perspective(Math::rad2deg(ai_camera->mHorizontalFOV) * 2.0f, near, ai_camera->mClipPlaneFar);
-void EditorSceneImporterAssimp::_find_texture_path(const String &r_p_path, String &r_path, bool &r_found) {
+ 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);
- _Directory dir;
+ Transform xform;
+ xform.set_look_at(pos, look_at, up);
- List<String> exts;
- ImageLoader::get_recognized_extensions(&exts);
+ recursive_state.new_node = camera;
- Vector<String> split_path = r_path.get_basename().split("*");
- if (split_path.size() == 2) {
- r_found = true;
- return;
- }
+ attach_new_node(state,
+ recursive_state.new_node,
+ recursive_state.assimp_node,
+ recursive_state.parent_node,
+ recursive_state.node_name,
+ recursive_state.node_transform);
+}
- if (dir.file_exists(r_p_path.get_base_dir() + r_path.get_file())) {
- r_path = r_p_path.get_base_dir() + r_path.get_file();
- r_found = true;
- return;
- }
+/**
+ * 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);
- for (int32_t i = 0; i < exts.size(); i++) {
- if (r_found) {
- return;
- }
- if (r_found == false) {
- _find_texture_path(r_p_path, dir, r_path, r_found, "." + exts[i]);
+ // set 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;
}
}
-}
-void EditorSceneImporterAssimp::_find_texture_path(const String &p_path, _Directory &dir, String &path, bool &found, String extension) {
- String name = path.get_basename() + extension;
- if (dir.file_exists(name)) {
- found = true;
- path = name;
- return;
- }
- String name_ignore_sub_directory = p_path.get_base_dir().plus_file(path.get_file().get_basename()) + extension;
- if (dir.file_exists(name_ignore_sub_directory)) {
- found = true;
- path = name_ignore_sub_directory;
- return;
- }
+ // 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);
- String name_find_texture_sub_directory = p_path.get_base_dir() + "/textures/" + path.get_file().get_basename() + extension;
- if (dir.file_exists(name_find_texture_sub_directory)) {
- found = true;
- path = name_find_texture_sub_directory;
- return;
- }
- String name_find_texture_upper_sub_directory = p_path.get_base_dir() + "/Textures/" + path.get_file().get_basename() + extension;
- if (dir.file_exists(name_find_texture_upper_sub_directory)) {
- found = true;
- path = name_find_texture_upper_sub_directory;
- return;
- }
- String name_find_texture_outside_sub_directory = p_path.get_base_dir() + "/../textures/" + path.get_file().get_basename() + extension;
- if (dir.file_exists(name_find_texture_outside_sub_directory)) {
- found = true;
- path = name_find_texture_outside_sub_directory;
- return;
- }
+ ERR_FAIL_COND(state.root == NULL);
+ ERR_FAIL_COND(recursive_state.skeleton == NULL);
- String name_find_upper_texture_outside_sub_directory = p_path.get_base_dir() + "/../Textures/" + path.get_file().get_basename() + extension;
- if (dir.file_exists(name_find_upper_texture_outside_sub_directory)) {
- found = true;
- path = name_find_upper_texture_outside_sub_directory;
- return;
- }
-}
+ 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));
-String EditorSceneImporterAssimp::_assimp_get_string(const aiString &p_string) const {
- //convert an assimp String to a Godot String
- String name;
- name.parse_utf8(p_string.C_Str() /*,p_string.length*/);
- if (name.find(":") != -1) {
- String replaced_name = name.split(":")[1];
- print_verbose("Replacing " + name + " containing : with " + replaced_name);
- name = replaced_name;
+ //skeleton->set_use_bones_in_world_transform(true);
+ print_verbose("Created new FBX skeleton for armature node");
}
- name = name.replace(".", ""); //can break things, specially bone names
+ ERR_FAIL_COND_MSG(recursive_state.skeleton == NULL, "Mesh has invalid armature detection - report this");
- return name;
-}
+ // this transform is a bone
+ recursive_state.skeleton->add_bone(recursive_state.node_name);
-String EditorSceneImporterAssimp::_assimp_anim_string_to_string(const aiString &p_string) const {
+ ERR_FAIL_COND(recursive_state.skeleton == NULL); // serious bug we must now exit.
+ //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);
- String name;
- name.parse_utf8(p_string.C_Str() /*,p_string.length*/);
- if (name.find(":") != -1) {
- String replaced_name = name.split(":")[1];
- print_verbose("Replacing " + name + " containing : with " + replaced_name);
- name = replaced_name;
+ 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());
+
+ // 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);
}
- return name;
}
-String EditorSceneImporterAssimp::_assimp_raw_string_to_string(const aiString &p_string) const {
- String name;
- name.parse_utf8(p_string.C_Str() /*,p_string.length*/);
- return name;
-}
+/**
+ * Generate node
+ * Recursive call to iterate over all nodes
+ */
+void EditorSceneImporterAssimp::_generate_node(
+ ImportState &state,
+ Skeleton *skeleton,
+ const aiNode *assimp_node, Node *parent_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);
-Ref<Animation> EditorSceneImporterAssimp::import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) {
- return Ref<Animation>();
-}
+ Spatial *new_node = NULL;
+ String node_name = AssimpUtils::get_assimp_string(assimp_node->mName);
+ Transform node_transform = AssimpUtils::assimp_matrix_transform(assimp_node->mTransformation);
-const Transform EditorSceneImporterAssimp::_assimp_matrix_transform(const aiMatrix4x4 p_matrix) {
- aiMatrix4x4 matrix = p_matrix;
- Transform xform;
- //xform.set(matrix.a1, matrix.b1, matrix.c1, matrix.a2, matrix.b2, matrix.c2, matrix.a3, matrix.b3, matrix.c3, matrix.a4, matrix.b4, matrix.c4);
- xform.set(matrix.a1, matrix.a2, matrix.a3, matrix.b1, matrix.b2, matrix.b3, matrix.c1, matrix.c2, matrix.c3, matrix.a4, matrix.b4, matrix.c4);
- return xform;
-}
+ // 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);
+ }
+
+ // 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);
+ }
+} \ No newline at end of file
diff --git a/modules/assimp/editor_scene_importer_assimp.h b/modules/assimp/editor_scene_importer_assimp.h
index 7a30816e3b..787376c9af 100644
--- a/modules/assimp/editor_scene_importer_assimp.h
+++ b/modules/assimp/editor_scene_importer_assimp.h
@@ -44,60 +44,31 @@
#include "scene/resources/animation.h"
#include "scene/resources/surface_tool.h"
-#include "assimp/DefaultLogger.hpp"
-#include "assimp/LogStream.hpp"
-#include "assimp/Logger.hpp"
-#include "assimp/matrix4x4.h"
-#include "assimp/scene.h"
-#include "assimp/types.h"
+#include <assimp/matrix4x4.h>
+#include <assimp/scene.h>
+#include <assimp/types.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/LogStream.hpp>
+#include <assimp/Logger.hpp>
+
+#include "import_state.h"
+#include "import_utils.h"
+
+using namespace AssimpImporter;
class AssimpStream : public Assimp::LogStream {
public:
// Constructor
- AssimpStream();
+ AssimpStream() {}
// Destructor
- ~AssimpStream();
+ ~AssimpStream() {}
// Write something using your own functionality
- void write(const char *message);
+ void write(const char *message) {
+ print_verbose(String("Open Asset Import: ") + String(message).strip_edges());
+ }
};
-#define AI_MATKEY_FBX_MAYA_BASE_COLOR_FACTOR "$raw.Maya|baseColor", 0, 0
-#define AI_MATKEY_FBX_MAYA_METALNESS_FACTOR "$raw.Maya|metalness", 0, 0
-#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_FACTOR "$raw.Maya|diffuseRoughness", 0, 0
-
-#define AI_MATKEY_FBX_MAYA_METALNESS_TEXTURE "$raw.Maya|metalness|file", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_METALNESS_UV_XFORM "$raw.Maya|metalness|uvtrafo", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_TEXTURE "$raw.Maya|diffuseRoughness|file", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_UV_XFORM "$raw.Maya|diffuseRoughness|uvtrafo", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_BASE_COLOR_TEXTURE "$raw.Maya|baseColor|file", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_BASE_COLOR_UV_XFORM "$raw.Maya|baseColor|uvtrafo", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_NORMAL_TEXTURE "$raw.Maya|normalCamera|file", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_NORMAL_UV_XFORM "$raw.Maya|normalCamera|uvtrafo", aiTextureType_UNKNOWN, 0
-
-#define AI_MATKEY_FBX_NORMAL_TEXTURE "$raw.Maya|normalCamera|file", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_NORMAL_UV_XFORM "$raw.Maya|normalCamera|uvtrafo", aiTextureType_UNKNOWN, 0
-
-#define AI_MATKEY_FBX_MAYA_STINGRAY_DISPLACEMENT_SCALING_FACTOR "$raw.Maya|displacementscaling", 0, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_BASE_COLOR_FACTOR "$raw.Maya|base_color", 0, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_FACTOR "$raw.Maya|emissive", 0, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_FACTOR "$raw.Maya|metallic", 0, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_FACTOR "$raw.Maya|roughness", 0, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_INTENSITY_FACTOR "$raw.Maya|emissive_intensity", 0, 0
-
-#define AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_TEXTURE "$raw.Maya|TEX_normal_map|file", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_UV_XFORM "$raw.Maya|TEX_normal_map|uvtrafo", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_TEXTURE "$raw.Maya|TEX_color_map|file", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_UV_XFORM "$raw.Maya|TEX_color_map|uvtrafo", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_TEXTURE "$raw.Maya|TEX_metallic_map|file", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_UV_XFORM "$raw.Maya|TEX_metallic_map|uvtrafo", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_TEXTURE "$raw.Maya|TEX_roughness_map|file", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_UV_XFORM "$raw.Maya|TEX_roughness_map|uvtrafo", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_TEXTURE "$raw.Maya|TEX_emissive_map|file", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_UV_XFORM "$raw.Maya|TEX_emissive_map|uvtrafo", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_AO_TEXTURE "$raw.Maya|TEX_ao_map|file", aiTextureType_UNKNOWN, 0
-#define AI_MATKEY_FBX_MAYA_STINGRAY_AO_UV_XFORM "$raw.Maya|TEX_ao_map|uvtrafo", aiTextureType_UNKNOWN, 0
-
class EditorSceneImporterAssimp : public EditorSceneImporter {
private:
GDCLASS(EditorSceneImporterAssimp, EditorSceneImporter);
@@ -112,59 +83,6 @@ private:
};
};
- struct AssetImportFbx {
- enum ETimeMode {
- TIME_MODE_DEFAULT = 0,
- TIME_MODE_120 = 1,
- TIME_MODE_100 = 2,
- TIME_MODE_60 = 3,
- TIME_MODE_50 = 4,
- TIME_MODE_48 = 5,
- TIME_MODE_30 = 6,
- TIME_MODE_30_DROP = 7,
- TIME_MODE_NTSC_DROP_FRAME = 8,
- TIME_MODE_NTSC_FULL_FRAME = 9,
- TIME_MODE_PAL = 10,
- TIME_MODE_CINEMA = 11,
- TIME_MODE_1000 = 12,
- TIME_MODE_CINEMA_ND = 13,
- TIME_MODE_CUSTOM = 14,
- TIME_MODE_TIME_MODE_COUNT = 15
- };
- enum UpAxis {
- UP_VECTOR_AXIS_X = 1,
- UP_VECTOR_AXIS_Y = 2,
- UP_VECTOR_AXIS_Z = 3
- };
- enum FrontAxis {
- FRONT_PARITY_EVEN = 1,
- FRONT_PARITY_ODD = 2,
- };
-
- enum CoordAxis {
- COORD_RIGHT = 0,
- COORD_LEFT = 1
- };
- };
-
- struct ImportState {
-
- String path;
- const aiScene *assimp_scene;
- uint32_t max_bone_weights;
- Spatial *root;
- Map<String, Ref<Mesh> > mesh_cache;
- Map<int, Ref<Material> > material_cache;
- Map<String, int> light_cache;
- Map<String, int> camera_cache;
- Vector<Skeleton *> skeletons;
- Map<String, int> bone_owners; //maps bones to skeleton index owned by
- Map<String, Node *> node_map;
- Map<MeshInstance *, Skeleton *> mesh_skeletons;
- bool fbx; //for some reason assimp does some things different for FBX
- AnimationPlayer *animation_player;
- };
-
struct BoneInfo {
uint32_t bone;
float weight;
@@ -177,28 +95,29 @@ private:
const aiNode *node;
};
- const Transform _assimp_matrix_transform(const aiMatrix4x4 p_matrix);
- String _assimp_get_string(const aiString &p_string) const;
- Transform _get_global_assimp_node_transform(const aiNode *p_current_node);
-
void _calc_tangent_from_mesh(const aiMesh *ai_mesh, int i, int tri_index, int index, PoolColorArray::Write &w);
void _set_texture_mapping_mode(aiTextureMapMode *map_mode, Ref<Texture> texture);
- void _find_texture_path(const String &p_path, String &path, bool &r_found);
- void _find_texture_path(const String &p_path, _Directory &dir, String &path, bool &found, String extension);
-
- Ref<Texture> _load_texture(ImportState &state, String p_path);
- Ref<Material> _generate_material_from_index(ImportState &state, int p_index, bool p_double_sided);
- Ref<Mesh> _generate_mesh_from_surface_indices(ImportState &state, const Vector<int> &p_surface_indices, Skeleton *p_skeleton = NULL, bool p_double_sided_material = false);
- void _generate_node(ImportState &state, const aiNode *p_assimp_node, Node *p_parent);
- void _generate_bone_groups(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<String, Transform> &bind_xforms);
- void _fill_node_relationships(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<int, int> &skeleton_map, int p_skeleton_id, Skeleton *p_skeleton, const String &p_parent_name, int &holecount, const Vector<SkeletonHole> &p_holes, const Map<String, Transform> &bind_xforms);
- void _generate_skeletons(ImportState &state, const aiNode *p_assimp_node, Map<String, int> &ownership, Map<int, int> &skeleton_map, const Map<String, Transform> &bind_xforms);
+ Ref<Mesh> _generate_mesh_from_surface_indices(ImportState &state, const Vector<int> &p_surface_indices, const aiNode *assimp_node, Skeleton *p_skeleton = NULL);
+
+ // utility for node creation
+ void attach_new_node(ImportState &state, Spatial *new_node, const aiNode *node, Node *parent_node, String Name, Transform &transform);
+ // simple object creation functions
+ void create_light(ImportState &state, RecursiveState &recursive_state);
+ void create_camera(ImportState &state, RecursiveState &recursive_state);
+ void create_bone(ImportState &state, RecursiveState &recursive_state);
+ // non recursive - linear so must not use recursive arguments
+ void create_mesh(ImportState &state, const aiNode *assimp_node, const String &node_name, Node *current_node, Node *parent_node, Transform node_transform);
+
+ // recursive node generator
+ void _generate_node(ImportState &state, Skeleton *skeleton, const aiNode *assimp_node, Node *parent_node);
+ // runs after _generate_node as it must then use pre-created godot skeleton.
+ void generate_mesh_phase_from_skeletal_mesh(ImportState &state);
void _insert_animation_track(ImportState &scene, const aiAnimation *assimp_anim, int p_track, int p_bake_fps, Ref<Animation> animation, float ticks_per_second, Skeleton *p_skeleton, const NodePath &p_path, const String &p_name);
void _import_animation(ImportState &state, int p_animation_index, int p_bake_fps);
- Spatial *_generate_scene(const String &p_path, const aiScene *scene, const uint32_t p_flags, int p_bake_fps, const int32_t p_max_bone_weights);
+ Spatial *_generate_scene(const String &p_path, aiScene *scene, const uint32_t p_flags, int p_bake_fps, const int32_t p_max_bone_weights);
String _assimp_anim_string_to_string(const aiString &p_string) const;
String _assimp_raw_string_to_string(const aiString &p_string) const;
@@ -228,7 +147,7 @@ public:
virtual void get_extensions(List<String> *r_extensions) const;
virtual uint32_t get_import_flags() const;
virtual Node *import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = NULL);
- virtual Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps);
+ Ref<Image> load_image(ImportState &state, const aiScene *p_scene, String p_path);
};
#endif
#endif
diff --git a/modules/assimp/import_state.h b/modules/assimp/import_state.h
new file mode 100644
index 0000000000..8d82cd3e39
--- /dev/null
+++ b/modules/assimp/import_state.h
@@ -0,0 +1,115 @@
+/*************************************************************************/
+/* import_state.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef EDITOR_SCENE_IMPORT_STATE_H
+#define EDITOR_SCENE_IMPORT_STATE_H
+
+#include "core/bind/core_bind.h"
+#include "core/io/resource_importer.h"
+#include "core/vector.h"
+#include "editor/import/resource_importer_scene.h"
+#include "editor/project_settings_editor.h"
+#include "scene/3d/mesh_instance.h"
+#include "scene/3d/skeleton.h"
+#include "scene/3d/spatial.h"
+#include "scene/animation/animation_player.h"
+#include "scene/resources/animation.h"
+#include "scene/resources/surface_tool.h"
+
+#include <assimp/matrix4x4.h>
+#include <assimp/scene.h>
+#include <assimp/types.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/LogStream.hpp>
+#include <assimp/Logger.hpp>
+
+namespace AssimpImporter {
+/** Import state is for global scene import data
+ * This makes the code simpler and contains useful lookups.
+ */
+struct ImportState {
+
+ String path;
+ const aiScene *assimp_scene;
+ uint32_t max_bone_weights;
+
+ Spatial *root;
+ Map<String, Ref<Mesh> > mesh_cache;
+ Map<int, Ref<Material> > material_cache;
+ Map<String, int> light_cache;
+ Map<String, int> camera_cache;
+ //Vector<Skeleton *> skeletons;
+ Map<Skeleton *, const Spatial *> armature_skeletons; // maps skeletons based on their armature nodes.
+ Map<const aiBone *, Skeleton *> bone_to_skeleton_lookup; // maps bones back into their skeleton
+ // very useful for when you need to ask assimp for the bone mesh
+ Map<String, Node *> node_map;
+ Map<const aiNode *, const Node *> assimp_node_map;
+ Map<String, Ref<Image> > path_to_image_cache;
+ bool fbx; //for some reason assimp does some things different for FBX
+ AnimationPlayer *animation_player;
+};
+
+struct AssimpImageData {
+ Ref<Image> raw_image;
+ Ref<ImageTexture> texture;
+ aiTextureMapMode *map_mode = NULL;
+};
+
+/** Recursive state is used to push state into functions instead of specifying them
+ * This makes the code easier to handle too and add extra arguments without breaking things
+ */
+struct RecursiveState {
+ RecursiveState(
+ Transform &_node_transform,
+ Skeleton *_skeleton,
+ Spatial *_new_node,
+ const String &_node_name,
+ const aiNode *_assimp_node,
+ Node *_parent_node,
+ const aiBone *_bone) :
+ node_transform(_node_transform),
+ skeleton(_skeleton),
+ new_node(_new_node),
+ node_name(_node_name),
+ assimp_node(_assimp_node),
+ parent_node(_parent_node),
+ bone(_bone) {}
+
+ Transform &node_transform;
+ Skeleton *skeleton;
+ Spatial *new_node;
+ const String &node_name;
+ const aiNode *assimp_node;
+ Node *parent_node;
+ const aiBone *bone;
+};
+} // namespace AssimpImporter
+
+#endif // EDITOR_SCENE_IMPORT_STATE_H
diff --git a/modules/assimp/import_utils.h b/modules/assimp/import_utils.h
new file mode 100644
index 0000000000..4be76ade0f
--- /dev/null
+++ b/modules/assimp/import_utils.h
@@ -0,0 +1,448 @@
+/*************************************************************************/
+/* import_utils.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef IMPORT_UTILS_IMPORTER_ASSIMP_H
+#define IMPORT_UTILS_IMPORTER_ASSIMP_H
+
+#include "core/io/image_loader.h"
+#include "import_state.h"
+
+#include <assimp/SceneCombiner.h>
+#include <assimp/cexport.h>
+#include <assimp/cimport.h>
+#include <assimp/matrix4x4.h>
+#include <assimp/pbrmaterial.h>
+#include <assimp/postprocess.h>
+#include <assimp/scene.h>
+#include <assimp/DefaultLogger.hpp>
+#include <assimp/Importer.hpp>
+#include <assimp/LogStream.hpp>
+#include <assimp/Logger.hpp>
+#include <string>
+
+using namespace AssimpImporter;
+
+#define AI_PROPERTIES aiTextureType_UNKNOWN, 0
+#define AI_NULL 0, 0
+#define AI_MATKEY_FBX_MAYA_BASE_COLOR_FACTOR "$raw.Maya|baseColor"
+#define AI_MATKEY_FBX_MAYA_METALNESS_FACTOR "$raw.Maya|metalness"
+#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_FACTOR "$raw.Maya|diffuseRoughness"
+
+#define AI_MATKEY_FBX_MAYA_EMISSION_TEXTURE "$raw.Maya|emissionColor|file"
+#define AI_MATKEY_FBX_MAYA_EMISSIVE_FACTOR "$raw.Maya|emission"
+#define AI_MATKEY_FBX_MAYA_METALNESS_TEXTURE "$raw.Maya|metalness|file"
+#define AI_MATKEY_FBX_MAYA_METALNESS_UV_XFORM "$raw.Maya|metalness|uvtrafo"
+#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_TEXTURE "$raw.Maya|diffuseRoughness|file"
+#define AI_MATKEY_FBX_MAYA_DIFFUSE_ROUGHNESS_UV_XFORM "$raw.Maya|diffuseRoughness|uvtrafo"
+#define AI_MATKEY_FBX_MAYA_BASE_COLOR_TEXTURE "$raw.Maya|baseColor|file"
+#define AI_MATKEY_FBX_MAYA_BASE_COLOR_UV_XFORM "$raw.Maya|baseColor|uvtrafo"
+#define AI_MATKEY_FBX_MAYA_NORMAL_TEXTURE "$raw.Maya|normalCamera|file"
+#define AI_MATKEY_FBX_MAYA_NORMAL_UV_XFORM "$raw.Maya|normalCamera|uvtrafo"
+
+#define AI_MATKEY_FBX_NORMAL_TEXTURE "$raw.Maya|normalCamera|file"
+#define AI_MATKEY_FBX_NORMAL_UV_XFORM "$raw.Maya|normalCamera|uvtrafo"
+
+#define AI_MATKEY_FBX_MAYA_STINGRAY_DISPLACEMENT_SCALING_FACTOR "$raw.Maya|displacementscaling"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_BASE_COLOR_FACTOR "$raw.Maya|base_color"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_FACTOR "$raw.Maya|emissive"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_FACTOR "$raw.Maya|metallic"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_FACTOR "$raw.Maya|roughness"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_INTENSITY_FACTOR "$raw.Maya|emissive_intensity"
+
+#define AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_TEXTURE "$raw.Maya|TEX_normal_map|file"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_NORMAL_UV_XFORM "$raw.Maya|TEX_normal_map|uvtrafo"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_TEXTURE "$raw.Maya|TEX_color_map|file"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_COLOR_UV_XFORM "$raw.Maya|TEX_color_map|uvtrafo"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_TEXTURE "$raw.Maya|TEX_metallic_map|file"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_METALLIC_UV_XFORM "$raw.Maya|TEX_metallic_map|uvtrafo"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_TEXTURE "$raw.Maya|TEX_roughness_map|file"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_ROUGHNESS_UV_XFORM "$raw.Maya|TEX_roughness_map|uvtrafo"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_TEXTURE "$raw.Maya|TEX_emissive_map|file"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_EMISSIVE_UV_XFORM "$raw.Maya|TEX_emissive_map|uvtrafo"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_AO_TEXTURE "$raw.Maya|TEX_ao_map|file"
+#define AI_MATKEY_FBX_MAYA_STINGRAY_AO_UV_XFORM "$raw.Maya|TEX_ao_map|uvtrafo"
+
+/**
+ * Assimp Utils
+ * Conversion tools / glue code to convert from assimp to godot
+*/
+class AssimpUtils {
+public:
+ /**
+ * calculate tangents for mesh data from assimp data
+ */
+ static void calc_tangent_from_mesh(const aiMesh *ai_mesh, int i, int tri_index, int index, PoolColorArray::Write &w) {
+ const aiVector3D normals = ai_mesh->mAnimMeshes[i]->mNormals[tri_index];
+ const Vector3 godot_normal = Vector3(normals.x, normals.y, normals.z);
+ const aiVector3D tangent = ai_mesh->mAnimMeshes[i]->mTangents[tri_index];
+ const Vector3 godot_tangent = Vector3(tangent.x, tangent.y, tangent.z);
+ const aiVector3D bitangent = ai_mesh->mAnimMeshes[i]->mBitangents[tri_index];
+ const Vector3 godot_bitangent = Vector3(bitangent.x, bitangent.y, bitangent.z);
+ float d = godot_normal.cross(godot_tangent).dot(godot_bitangent) > 0.0f ? 1.0f : -1.0f;
+ Color plane_tangent = Color(tangent.x, tangent.y, tangent.z, d);
+ w[index] = plane_tangent;
+ }
+
+ struct AssetImportFbx {
+ enum ETimeMode {
+ TIME_MODE_DEFAULT = 0,
+ TIME_MODE_120 = 1,
+ TIME_MODE_100 = 2,
+ TIME_MODE_60 = 3,
+ TIME_MODE_50 = 4,
+ TIME_MODE_48 = 5,
+ TIME_MODE_30 = 6,
+ TIME_MODE_30_DROP = 7,
+ TIME_MODE_NTSC_DROP_FRAME = 8,
+ TIME_MODE_NTSC_FULL_FRAME = 9,
+ TIME_MODE_PAL = 10,
+ TIME_MODE_CINEMA = 11,
+ TIME_MODE_1000 = 12,
+ TIME_MODE_CINEMA_ND = 13,
+ TIME_MODE_CUSTOM = 14,
+ TIME_MODE_TIME_MODE_COUNT = 15
+ };
+ enum UpAxis {
+ UP_VECTOR_AXIS_X = 1,
+ UP_VECTOR_AXIS_Y = 2,
+ UP_VECTOR_AXIS_Z = 3
+ };
+ enum FrontAxis {
+ FRONT_PARITY_EVEN = 1,
+ FRONT_PARITY_ODD = 2,
+ };
+
+ enum CoordAxis {
+ COORD_RIGHT = 0,
+ COORD_LEFT = 1
+ };
+ };
+
+ /** Get assimp string
+ * automatically filters the string data
+ */
+ static String get_assimp_string(const aiString &p_string) {
+ //convert an assimp String to a Godot String
+ String name;
+ name.parse_utf8(p_string.C_Str() /*,p_string.length*/);
+ if (name.find(":") != -1) {
+ String replaced_name = name.split(":")[1];
+ print_verbose("Replacing " + name + " containing : with " + replaced_name);
+ name = replaced_name;
+ }
+
+ return name;
+ }
+
+ static String get_anim_string_from_assimp(const aiString &p_string) {
+
+ String name;
+ name.parse_utf8(p_string.C_Str() /*,p_string.length*/);
+ if (name.find(":") != -1) {
+ String replaced_name = name.split(":")[1];
+ print_verbose("Replacing " + name + " containing : with " + replaced_name);
+ name = replaced_name;
+ }
+ return name;
+ }
+
+ /**
+ * No filter logic get_raw_string_from_assimp
+ * This just convers the aiString to a parsed utf8 string
+ * Without removing special chars etc
+ */
+ static String get_raw_string_from_assimp(const aiString &p_string) {
+ String name;
+ name.parse_utf8(p_string.C_Str() /*,p_string.length*/);
+ return name;
+ }
+
+ static Ref<Animation> import_animation(const String &p_path, uint32_t p_flags, int p_bake_fps) {
+ return Ref<Animation>();
+ }
+
+ /**
+ * Converts aiMatrix4x4 to godot Transform
+ */
+ static const Transform assimp_matrix_transform(const aiMatrix4x4 p_matrix) {
+ aiMatrix4x4 matrix = p_matrix;
+ Transform xform;
+ xform.set(matrix.a1, matrix.a2, matrix.a3, matrix.b1, matrix.b2, matrix.b3, matrix.c1, matrix.c2, matrix.c3, matrix.a4, matrix.b4, matrix.c4);
+ return xform;
+ }
+
+ /** Get fbx fps for time mode meta data
+ */
+ static float get_fbx_fps(int32_t time_mode, const aiScene *p_scene) {
+ switch (time_mode) {
+ case AssetImportFbx::TIME_MODE_DEFAULT: return 24; //hack
+ case AssetImportFbx::TIME_MODE_120: return 120;
+ case AssetImportFbx::TIME_MODE_100: return 100;
+ case AssetImportFbx::TIME_MODE_60: return 60;
+ case AssetImportFbx::TIME_MODE_50: return 50;
+ case AssetImportFbx::TIME_MODE_48: return 48;
+ case AssetImportFbx::TIME_MODE_30: return 30;
+ case AssetImportFbx::TIME_MODE_30_DROP: return 30;
+ case AssetImportFbx::TIME_MODE_NTSC_DROP_FRAME: return 29.9700262f;
+ case AssetImportFbx::TIME_MODE_NTSC_FULL_FRAME: return 29.9700262f;
+ case AssetImportFbx::TIME_MODE_PAL: return 25;
+ case AssetImportFbx::TIME_MODE_CINEMA: return 24;
+ case AssetImportFbx::TIME_MODE_1000: return 1000;
+ case AssetImportFbx::TIME_MODE_CINEMA_ND: return 23.976f;
+ case AssetImportFbx::TIME_MODE_CUSTOM:
+ int32_t frame_rate = -1;
+ p_scene->mMetaData->Get("FrameRate", frame_rate);
+ return frame_rate;
+ }
+ return 0;
+ }
+
+ /**
+ * Get global transform for the current node - so we can use world space rather than
+ * local space coordinates
+ * useful if you need global - although recommend using local wherever possible over global
+ * as you could break fbx scaling :)
+ */
+ static Transform _get_global_assimp_node_transform(const aiNode *p_current_node) {
+ aiNode const *current_node = p_current_node;
+ Transform xform;
+ while (current_node != NULL) {
+ xform = assimp_matrix_transform(current_node->mTransformation) * xform;
+ current_node = current_node->mParent;
+ }
+ return xform;
+ }
+
+ /**
+ * Find hardcoded textures from assimp which could be in many different directories
+ */
+ static void find_texture_path(const String &p_path, _Directory &dir, String &path, bool &found, String extension) {
+ Vector<String> paths;
+ paths.push_back(path.get_basename() + extension);
+ paths.push_back(path + extension);
+ paths.push_back(path);
+ paths.push_back(p_path.get_base_dir().plus_file(path.get_file().get_basename() + extension));
+ paths.push_back(p_path.get_base_dir().plus_file(path.get_file() + extension));
+ paths.push_back(p_path.get_base_dir().plus_file(path.get_file()));
+ paths.push_back(p_path.get_base_dir().plus_file("textures/" + path.get_file().get_basename() + extension));
+ paths.push_back(p_path.get_base_dir().plus_file("textures/" + path.get_file() + extension));
+ paths.push_back(p_path.get_base_dir().plus_file("textures/" + path.get_file()));
+ paths.push_back(p_path.get_base_dir().plus_file("Textures/" + path.get_file().get_basename() + extension));
+ paths.push_back(p_path.get_base_dir().plus_file("Textures/" + path.get_file() + extension));
+ paths.push_back(p_path.get_base_dir().plus_file("Textures/" + path.get_file()));
+ paths.push_back(p_path.get_base_dir().plus_file("../Textures/" + path.get_file() + extension));
+ paths.push_back(p_path.get_base_dir().plus_file("../Textures/" + path.get_file().get_basename() + extension));
+ paths.push_back(p_path.get_base_dir().plus_file("../Textures/" + path.get_file()));
+ paths.push_back(p_path.get_base_dir().plus_file("../textures/" + path.get_file().get_basename() + extension));
+ paths.push_back(p_path.get_base_dir().plus_file("../textures/" + path.get_file() + extension));
+ paths.push_back(p_path.get_base_dir().plus_file("../textures/" + path.get_file()));
+ paths.push_back(p_path.get_base_dir().plus_file("texture/" + path.get_file().get_basename() + extension));
+ paths.push_back(p_path.get_base_dir().plus_file("texture/" + path.get_file() + extension));
+ paths.push_back(p_path.get_base_dir().plus_file("texture/" + path.get_file()));
+ paths.push_back(p_path.get_base_dir().plus_file("Texture/" + path.get_file().get_basename() + extension));
+ paths.push_back(p_path.get_base_dir().plus_file("Texture/" + path.get_file() + extension));
+ paths.push_back(p_path.get_base_dir().plus_file("Texture/" + path.get_file()));
+ paths.push_back(p_path.get_base_dir().plus_file("../Texture/" + path.get_file() + extension));
+ paths.push_back(p_path.get_base_dir().plus_file("../Texture/" + path.get_file().get_basename() + extension));
+ paths.push_back(p_path.get_base_dir().plus_file("../Texture/" + path.get_file()));
+ paths.push_back(p_path.get_base_dir().plus_file("../texture/" + path.get_file().get_basename() + extension));
+ paths.push_back(p_path.get_base_dir().plus_file("../texture/" + path.get_file() + extension));
+ paths.push_back(p_path.get_base_dir().plus_file("../texture/" + path.get_file()));
+ for (int i = 0; i < paths.size(); i++) {
+ if (dir.file_exists(paths[i])) {
+ found = true;
+ path = paths[i];
+ return;
+ }
+ }
+ }
+
+ /** find the texture path for the supplied fbx path inside godot
+ * very simple lookup for subfolders etc for a texture which may or may not be in a directory
+ */
+ static void find_texture_path(const String &r_p_path, String &r_path, bool &r_found) {
+ _Directory dir;
+
+ List<String> exts;
+ ImageLoader::get_recognized_extensions(&exts);
+
+ Vector<String> split_path = r_path.get_basename().split("*");
+ if (split_path.size() == 2) {
+ r_found = true;
+ return;
+ }
+
+ if (dir.file_exists(r_p_path.get_base_dir() + r_path.get_file())) {
+ r_path = r_p_path.get_base_dir() + r_path.get_file();
+ r_found = true;
+ return;
+ }
+
+ for (int32_t i = 0; i < exts.size(); i++) {
+ if (r_found) {
+ return;
+ }
+ if (r_found == false) {
+ find_texture_path(r_p_path, dir, r_path, r_found, "." + exts[i]);
+ }
+ }
+ }
+
+ /**
+ * set_texture_mapping_mode
+ * Helper to check the mapping mode of the texture (repeat, clamp and mirror)
+ */
+ static void set_texture_mapping_mode(aiTextureMapMode *map_mode, Ref<ImageTexture> texture) {
+ ERR_FAIL_COND(texture.is_null());
+ ERR_FAIL_COND(map_mode == NULL);
+ aiTextureMapMode tex_mode = aiTextureMapMode::aiTextureMapMode_Wrap;
+
+ tex_mode = map_mode[0];
+
+ int32_t flags = Texture::FLAGS_DEFAULT;
+ if (tex_mode == aiTextureMapMode_Wrap) {
+ //Default
+ } else if (tex_mode == aiTextureMapMode_Clamp) {
+ flags = flags & ~Texture::FLAG_REPEAT;
+ } else if (tex_mode == aiTextureMapMode_Mirror) {
+ flags = flags | Texture::FLAG_MIRRORED_REPEAT;
+ }
+ texture->set_flags(flags);
+ }
+
+ /**
+ * Load or load from cache image :)
+ */
+ static Ref<Image> load_image(ImportState &state, const aiScene *p_scene, String p_path) {
+
+ Map<String, Ref<Image> >::Element *match = state.path_to_image_cache.find(p_path);
+
+ // if our cache contains this image then don't bother
+ if (match) {
+ return match->get();
+ }
+
+ Vector<String> split_path = p_path.get_basename().split("*");
+ if (split_path.size() == 2) {
+ size_t texture_idx = split_path[1].to_int();
+ ERR_FAIL_COND_V(texture_idx >= p_scene->mNumTextures, Ref<Image>());
+ aiTexture *tex = p_scene->mTextures[texture_idx];
+ String filename = AssimpUtils::get_raw_string_from_assimp(tex->mFilename);
+ filename = filename.get_file();
+ print_verbose("Open Asset Import: Loading embedded texture " + filename);
+ if (tex->mHeight == 0) {
+ if (tex->CheckFormat("png")) {
+ Ref<Image> img = Image::_png_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth);
+ ERR_FAIL_COND_V(img.is_null(), Ref<Image>());
+ state.path_to_image_cache.insert(p_path, img);
+ return img;
+ } else if (tex->CheckFormat("jpg")) {
+ Ref<Image> img = Image::_jpg_mem_loader_func((uint8_t *)tex->pcData, tex->mWidth);
+ ERR_FAIL_COND_V(img.is_null(), Ref<Image>());
+ state.path_to_image_cache.insert(p_path, img);
+ return img;
+ } else if (tex->CheckFormat("dds")) {
+ ERR_EXPLAIN("Open Asset Import: Embedded dds not implemented");
+ ERR_FAIL_COND_V(true, Ref<Image>());
+ }
+ } else {
+ Ref<Image> img;
+ img.instance();
+ PoolByteArray arr;
+ uint32_t size = tex->mWidth * tex->mHeight;
+ arr.resize(size);
+ memcpy(arr.write().ptr(), tex->pcData, size);
+ ERR_FAIL_COND_V(arr.size() % 4 != 0, Ref<Image>());
+ //ARGB8888 to RGBA8888
+ for (int32_t i = 0; i < arr.size() / 4; i++) {
+ arr.write().ptr()[(4 * i) + 3] = arr[(4 * i) + 0];
+ arr.write().ptr()[(4 * i) + 0] = arr[(4 * i) + 1];
+ arr.write().ptr()[(4 * i) + 1] = arr[(4 * i) + 2];
+ arr.write().ptr()[(4 * i) + 2] = arr[(4 * i) + 3];
+ }
+ img->create(tex->mWidth, tex->mHeight, true, Image::FORMAT_RGBA8, arr);
+ ERR_FAIL_COND_V(img.is_null(), Ref<Image>());
+ state.path_to_image_cache.insert(p_path, img);
+ return img;
+ }
+ return Ref<Image>();
+ } else {
+ Ref<Texture> texture = ResourceLoader::load(p_path);
+ Ref<Image> image = texture->get_data();
+ state.path_to_image_cache.insert(p_path, image);
+ return image;
+ }
+
+ return Ref<Image>();
+ }
+
+ /* create texture from assimp data, if found in path */
+ static bool CreateAssimpTexture(
+ AssimpImporter::ImportState &state,
+ aiString texture_path,
+ String &filename,
+ String &path,
+ AssimpImageData &image_state) {
+ filename = get_raw_string_from_assimp(texture_path);
+ path = state.path.get_base_dir().plus_file(filename.replace("\\", "/"));
+ bool found = false;
+ find_texture_path(state.path, path, found);
+ if (found) {
+ image_state.raw_image = AssimpUtils::load_image(state, state.assimp_scene, path);
+ if (image_state.raw_image.is_valid()) {
+ image_state.texture.instance();
+ image_state.texture->create_from_image(image_state.raw_image);
+ image_state.texture->set_storage(ImageTexture::STORAGE_COMPRESS_LOSSY);
+ return true;
+ }
+ }
+
+ return false;
+ }
+ /** GetAssimpTexture
+ * Designed to retrieve textures for you
+ */
+ static bool GetAssimpTexture(
+ AssimpImporter::ImportState &state,
+ aiMaterial *ai_material,
+ aiTextureType texture_type,
+ String &filename,
+ String &path,
+ AssimpImageData &image_state) {
+ aiString ai_filename = aiString();
+ if (AI_SUCCESS == ai_material->GetTexture(texture_type, 0, &ai_filename, NULL, NULL, NULL, NULL, image_state.map_mode)) {
+ return CreateAssimpTexture(state, ai_filename, filename, path, image_state);
+ }
+
+ return false;
+ }
+};
+
+#endif // IMPORT_UTILS_IMPORTER_ASSIMP_H