1 /*************************************************************************/
2 /* editor_scene_importer_assimp.cpp */
3 /*************************************************************************/
4 /* This file is part of: */
5 /* GODOT ENGINE */
6 /* https://godotengine.org */
7 /*************************************************************************/
8 /* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
9 /* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
10 /* */
11 /* Permission is hereby granted, free of charge, to any person obtaining */
12 /* a copy of this software and associated documentation files (the */
13 /* "Software"), to deal in the Software without restriction, including */
14 /* without limitation the rights to use, copy, modify, merge, publish, */
15 /* distribute, sublicense, and/or sell copies of the Software, and to */
16 /* permit persons to whom the Software is furnished to do so, subject to */
17 /* the following conditions: */
18 /* */
19 /* The above copyright notice and this permission notice shall be */
20 /* included in all copies or substantial portions of the Software. */
21 /* */
22 /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23 /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
25 /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26 /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27 /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28 /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29 /*************************************************************************/
30
31 #include "editor_scene_importer_assimp.h"
32 #include "core/io/image_loader.h"
33 #include "editor/import/resource_importer_scene.h"
34 #include "import_utils.h"
35 #include "scene/3d/camera.h"
36 #include "scene/3d/light.h"
37 #include "scene/3d/mesh_instance.h"
38 #include "scene/main/node.h"
39 #include "scene/resources/material.h"
40 #include "scene/resources/surface_tool.h"
41
42 #include <assimp/matrix4x4.h>
43 #include <assimp/postprocess.h>
44 #include <assimp/scene.h>
45 #include <assimp/Importer.hpp>
46 #include <assimp/LogStream.hpp>
47
48 // move into assimp
get_bone_by_name(const aiScene * scene,aiString bone_name)49 aiBone *get_bone_by_name(const aiScene *scene, aiString bone_name) {
50 for (unsigned int mesh_id = 0; mesh_id < scene->mNumMeshes; ++mesh_id) {
51 aiMesh *mesh = scene->mMeshes[mesh_id];
52
53 // iterate over all the bones on the mesh for this node only!
54 for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; boneIndex++) {
55
56 aiBone *bone = mesh->mBones[boneIndex];
57 if (bone->mName == bone_name) {
58 printf("matched bone by name: %s\n", bone->mName.C_Str());
59 return bone;
60 }
61 }
62 }
63
64 return NULL;
65 }
66
get_extensions(List<String> * r_extensions) const67 void EditorSceneImporterAssimp::get_extensions(List<String> *r_extensions) const {
68
69 const String import_setting_string = "filesystem/import/open_asset_import/";
70
71 Map<String, ImportFormat> import_format;
72 {
73 Vector<String> exts;
74 exts.push_back("fbx");
75 ImportFormat import = { exts, true };
76 import_format.insert("fbx", import);
77 }
78 for (Map<String, ImportFormat>::Element *E = import_format.front(); E; E = E->next()) {
79 _register_project_setting_import(E->key(), import_setting_string, E->get().extensions, r_extensions,
80 E->get().is_default);
81 }
82 }
83
_register_project_setting_import(const String generic,const String import_setting_string,const Vector<String> & exts,List<String> * r_extensions,const bool p_enabled) const84 void EditorSceneImporterAssimp::_register_project_setting_import(const String generic, const String import_setting_string,
85 const Vector<String> &exts, List<String> *r_extensions,
86 const bool p_enabled) const {
87 const String use_generic = "use_" + generic;
88 _GLOBAL_DEF(import_setting_string + use_generic, p_enabled, true);
89 if (ProjectSettings::get_singleton()->get(import_setting_string + use_generic)) {
90 for (int32_t i = 0; i < exts.size(); i++) {
91 r_extensions->push_back(exts[i]);
92 }
93 }
94 }
95
get_import_flags() const96 uint32_t EditorSceneImporterAssimp::get_import_flags() const {
97 return IMPORT_SCENE;
98 }
99
_bind_methods()100 void EditorSceneImporterAssimp::_bind_methods() {
101 }
102
import_scene(const String & p_path,uint32_t p_flags,int p_bake_fps,List<String> * r_missing_deps,Error * r_err)103 Node *EditorSceneImporterAssimp::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps,
104 List<String> *r_missing_deps, Error *r_err) {
105 Assimp::Importer importer;
106 importer.SetPropertyBool(AI_CONFIG_PP_FD_REMOVE, true);
107 // Cannot remove pivot points because the static mesh will be in the wrong place
108 importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, false);
109 int32_t max_bone_weights = 4;
110 //if (p_flags & IMPORT_ANIMATION_EIGHT_WEIGHTS) {
111 // const int eight_bones = 8;
112 // importer.SetPropertyBool(AI_CONFIG_PP_LBW_MAX_WEIGHTS, eight_bones);
113 // max_bone_weights = eight_bones;
114 //}
115
116 importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT);
117
118 //importer.SetPropertyFloat(AI_CONFIG_PP_DB_THRESHOLD, 1.0f);
119 int32_t post_process_Steps = aiProcess_CalcTangentSpace |
120 aiProcess_GlobalScale |
121 // imports models and listens to their file scale for CM to M conversions
122 //aiProcess_FlipUVs |
123 aiProcess_FlipWindingOrder |
124 // very important for culling so that it is done in the correct order.
125 //aiProcess_DropNormals |
126 //aiProcess_GenSmoothNormals |
127 //aiProcess_JoinIdenticalVertices |
128 aiProcess_ImproveCacheLocality |
129 //aiProcess_RemoveRedundantMaterials | // Causes a crash
130 //aiProcess_SplitLargeMeshes |
131 aiProcess_Triangulate |
132 aiProcess_GenUVCoords |
133 //aiProcess_FindDegenerates |
134 //aiProcess_SortByPType |
135 // aiProcess_FindInvalidData |
136 aiProcess_TransformUVCoords |
137 aiProcess_FindInstances |
138 //aiProcess_FixInfacingNormals |
139 //aiProcess_ValidateDataStructure |
140 aiProcess_OptimizeMeshes |
141 aiProcess_PopulateArmatureData |
142 //aiProcess_OptimizeGraph |
143 //aiProcess_Debone |
144 // aiProcess_EmbedTextures |
145 //aiProcess_SplitByBoneCount |
146 0;
147 String g_path = ProjectSettings::get_singleton()->globalize_path(p_path);
148 aiScene *scene = (aiScene *)importer.ReadFile(g_path.utf8().ptr(), post_process_Steps);
149
150 ERR_FAIL_COND_V_MSG(scene == NULL, NULL, String("Open Asset Import failed to open: ") + String(importer.GetErrorString()));
151
152 return _generate_scene(p_path, scene, p_flags, p_bake_fps, max_bone_weights);
153 }
154
155 template <class T>
156 struct EditorSceneImporterAssetImportInterpolate {
157
lerpEditorSceneImporterAssetImportInterpolate158 T lerp(const T &a, const T &b, float c) const {
159
160 return a + (b - a) * c;
161 }
162
catmull_romEditorSceneImporterAssetImportInterpolate163 T catmull_rom(const T &p0, const T &p1, const T &p2, const T &p3, float t) {
164
165 float t2 = t * t;
166 float t3 = t2 * t;
167
168 return 0.5f * ((2.0f * p1) + (-p0 + p2) * t + (2.0f * p0 - 5.0f * p1 + 4 * p2 - p3) * t2 +
169 (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3);
170 }
171
bezierEditorSceneImporterAssetImportInterpolate172 T bezier(T start, T control_1, T control_2, T end, float t) {
173 /* Formula from Wikipedia article on Bezier curves. */
174 real_t omt = (1.0 - t);
175 real_t omt2 = omt * omt;
176 real_t omt3 = omt2 * omt;
177 real_t t2 = t * t;
178 real_t t3 = t2 * t;
179
180 return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3;
181 }
182 };
183
184 //thank you for existing, partial specialization
185 template <>
186 struct EditorSceneImporterAssetImportInterpolate<Quat> {
187
lerpEditorSceneImporterAssetImportInterpolate188 Quat lerp(const Quat &a, const Quat &b, float c) const {
189 ERR_FAIL_COND_V_MSG(!a.is_normalized(), Quat(), "The quaternion \"a\" must be normalized.");
190 ERR_FAIL_COND_V_MSG(!b.is_normalized(), Quat(), "The quaternion \"b\" must be normalized.");
191
192 return a.slerp(b, c).normalized();
193 }
194
catmull_romEditorSceneImporterAssetImportInterpolate195 Quat catmull_rom(const Quat &p0, const Quat &p1, const Quat &p2, const Quat &p3, float c) {
196 ERR_FAIL_COND_V_MSG(!p1.is_normalized(), Quat(), "The quaternion \"p1\" must be normalized.");
197 ERR_FAIL_COND_V_MSG(!p2.is_normalized(), Quat(), "The quaternion \"p2\" must be normalized.");
198
199 return p1.slerp(p2, c).normalized();
200 }
201
bezierEditorSceneImporterAssetImportInterpolate202 Quat bezier(Quat start, Quat control_1, Quat control_2, Quat end, float t) {
203 ERR_FAIL_COND_V_MSG(!start.is_normalized(), Quat(), "The start quaternion must be normalized.");
204 ERR_FAIL_COND_V_MSG(!end.is_normalized(), Quat(), "The end quaternion must be normalized.");
205
206 return start.slerp(end, t).normalized();
207 }
208 };
209
210 template <class T>
_interpolate_track(const Vector<float> & p_times,const Vector<T> & p_values,float p_time,AssetImportAnimation::Interpolation p_interp)211 T EditorSceneImporterAssimp::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time,
212 AssetImportAnimation::Interpolation p_interp) {
213 //could use binary search, worth it?
214 int idx = -1;
215 for (int i = 0; i < p_times.size(); i++) {
216 if (p_times[i] > p_time)
217 break;
218 idx++;
219 }
220
221 EditorSceneImporterAssetImportInterpolate<T> interp;
222
223 switch (p_interp) {
224 case AssetImportAnimation::INTERP_LINEAR: {
225
226 if (idx == -1) {
227 return p_values[0];
228 } else if (idx >= p_times.size() - 1) {
229 return p_values[p_times.size() - 1];
230 }
231
232 float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]);
233
234 return interp.lerp(p_values[idx], p_values[idx + 1], c);
235
236 } break;
237 case AssetImportAnimation::INTERP_STEP: {
238
239 if (idx == -1) {
240 return p_values[0];
241 } else if (idx >= p_times.size() - 1) {
242 return p_values[p_times.size() - 1];
243 }
244
245 return p_values[idx];
246
247 } break;
248 case AssetImportAnimation::INTERP_CATMULLROMSPLINE: {
249
250 if (idx == -1) {
251 return p_values[1];
252 } else if (idx >= p_times.size() - 1) {
253 return p_values[1 + p_times.size() - 1];
254 }
255
256 float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]);
257
258 return interp.catmull_rom(p_values[idx - 1], p_values[idx], p_values[idx + 1], p_values[idx + 3], c);
259
260 } break;
261 case AssetImportAnimation::INTERP_CUBIC_SPLINE: {
262
263 if (idx == -1) {
264 return p_values[1];
265 } else if (idx >= p_times.size() - 1) {
266 return p_values[(p_times.size() - 1) * 3 + 1];
267 }
268
269 float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]);
270
271 T from = p_values[idx * 3 + 1];
272 T c1 = from + p_values[idx * 3 + 2];
273 T to = p_values[idx * 3 + 4];
274 T c2 = to + p_values[idx * 3 + 3];
275
276 return interp.bezier(from, c1, c2, to, c);
277
278 } break;
279 }
280
281 ERR_FAIL_V(p_values[0]);
282 }
283
get_bone_from_stack(ImportState & state,aiString name)284 aiBone *EditorSceneImporterAssimp::get_bone_from_stack(ImportState &state, aiString name) {
285 List<aiBone *>::Element *iter;
286 aiBone *bone = NULL;
287 for (iter = state.bone_stack.front(); iter; iter = iter->next()) {
288 bone = (aiBone *)iter->get();
289
290 if (bone && bone->mName == name) {
291 state.bone_stack.erase(bone);
292 return bone;
293 }
294 }
295
296 return NULL;
297 }
298
299 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)300 EditorSceneImporterAssimp::_generate_scene(const String &p_path, aiScene *scene, const uint32_t p_flags, int p_bake_fps,
301 const int32_t p_max_bone_weights) {
302 ERR_FAIL_COND_V(scene == NULL, NULL);
303
304 ImportState state;
305 state.path = p_path;
306 state.assimp_scene = scene;
307 state.max_bone_weights = p_max_bone_weights;
308 state.animation_player = NULL;
309 state.import_flags = p_flags;
310
311 // populate light map
312 for (unsigned int l = 0; l < scene->mNumLights; l++) {
313
314 aiLight *ai_light = scene->mLights[l];
315 ERR_CONTINUE(ai_light == NULL);
316 state.light_cache[AssimpUtils::get_assimp_string(ai_light->mName)] = l;
317 }
318
319 // fill camera cache
320 for (unsigned int c = 0; c < scene->mNumCameras; c++) {
321 aiCamera *ai_camera = scene->mCameras[c];
322 ERR_CONTINUE(ai_camera == NULL);
323 state.camera_cache[AssimpUtils::get_assimp_string(ai_camera->mName)] = c;
324 }
325
326 if (scene->mRootNode) {
327 state.nodes.push_back(scene->mRootNode);
328
329 // make flat node tree - in order to make processing deterministic
330 for (unsigned int i = 0; i < scene->mRootNode->mNumChildren; i++) {
331 _generate_node(state, scene->mRootNode->mChildren[i]);
332 }
333
334 RegenerateBoneStack(state);
335
336 Node *last_valid_parent = NULL;
337
338 List<const aiNode *>::Element *iter;
339 for (iter = state.nodes.front(); iter; iter = iter->next()) {
340 const aiNode *element_assimp_node = iter->get();
341 const aiNode *parent_assimp_node = element_assimp_node->mParent;
342
343 String node_name = AssimpUtils::get_assimp_string(element_assimp_node->mName);
344 //print_verbose("node: " + node_name);
345
346 Spatial *spatial = NULL;
347 Transform transform = AssimpUtils::assimp_matrix_transform(element_assimp_node->mTransformation);
348
349 // retrieve this node bone
350 aiBone *bone = get_bone_from_stack(state, element_assimp_node->mName);
351
352 if (state.light_cache.has(node_name)) {
353 spatial = create_light(state, node_name, transform);
354 } else if (state.camera_cache.has(node_name)) {
355 spatial = create_camera(state, node_name, transform);
356 } else if (state.armature_nodes.find(element_assimp_node)) {
357 // create skeleton
358 print_verbose("Making skeleton: " + node_name);
359 Skeleton *skeleton = memnew(Skeleton);
360 spatial = skeleton;
361 if (!state.armature_skeletons.has(element_assimp_node)) {
362 state.armature_skeletons.insert(element_assimp_node, skeleton);
363 }
364 } else if (bone != NULL) {
365 continue;
366 } else {
367 spatial = memnew(Spatial);
368 }
369
370 ERR_CONTINUE_MSG(spatial == NULL, "FBX Import - are we out of ram?");
371 // we on purpose set the transform and name after creating the node.
372
373 spatial->set_name(node_name);
374 spatial->set_global_transform(transform);
375
376 // first element is root
377 if (iter == state.nodes.front()) {
378 state.root = spatial;
379 }
380
381 // flat node map parent lookup tool
382 state.flat_node_map.insert(element_assimp_node, spatial);
383
384 Map<const aiNode *, Spatial *>::Element *parent_lookup = state.flat_node_map.find(parent_assimp_node);
385
386 // note: this always fails on the root node :) keep that in mind this is by design
387 if (parent_lookup) {
388 Spatial *parent_node = parent_lookup->value();
389
390 ERR_FAIL_COND_V_MSG(parent_node == NULL, state.root,
391 "Parent node invalid even though lookup successful, out of ram?")
392
393 if (spatial != state.root) {
394 parent_node->add_child(spatial);
395 spatial->set_owner(state.root);
396 } else {
397 // required - think about it root never has a parent yet is valid, anything else without a parent is not valid.
398 }
399 } else if (spatial != state.root) {
400 // if the ainode is not in the tree
401 // parent it to the last good parent found
402 if (last_valid_parent) {
403 last_valid_parent->add_child(spatial);
404 spatial->set_owner(state.root);
405 } else {
406 // this is a serious error?
407 memdelete(spatial);
408 }
409 }
410
411 // update last valid parent
412 last_valid_parent = spatial;
413 }
414 print_verbose("node counts: " + itos(state.nodes.size()));
415
416 // make clean bone stack
417 RegenerateBoneStack(state);
418
419 print_verbose("generating godot bone data");
420
421 print_verbose("Godot bone stack count: " + itos(state.bone_stack.size()));
422
423 // This is a list of bones, duplicates are from other meshes and must be dealt with properly
424 for (List<aiBone *>::Element *element = state.bone_stack.front(); element; element = element->next()) {
425 aiBone *bone = element->get();
426
427 ERR_CONTINUE_MSG(!bone, "invalid bone read from assimp?");
428
429 // Utilities for armature lookup - for now only FBX makes these
430 aiNode *armature_for_bone = bone->mArmature;
431
432 // Utilities for bone node lookup - for now only FBX makes these
433 aiNode *bone_node = bone->mNode;
434 aiNode *parent_node = bone_node->mParent;
435
436 String bone_name = AssimpUtils::get_anim_string_from_assimp(bone->mName);
437 ERR_CONTINUE_MSG(armature_for_bone == NULL, "Armature for bone invalid: " + bone_name);
438 Skeleton *skeleton = state.armature_skeletons[armature_for_bone];
439
440 state.skeleton_bone_map[bone] = skeleton;
441
442 if (bone_name.empty()) {
443 bone_name = "untitled_bone_name";
444 WARN_PRINT("Untitled bone name detected... report with file please");
445 }
446
447 // todo: this is where skin support goes
448 if (skeleton && skeleton->find_bone(bone_name) == -1) {
449 print_verbose("[Godot Glue] Imported bone" + bone_name);
450 int boneIdx = skeleton->get_bone_count();
451
452 Transform pform = AssimpUtils::assimp_matrix_transform(bone->mNode->mTransformation);
453 skeleton->add_bone(bone_name);
454 skeleton->set_bone_rest(boneIdx, pform);
455 skeleton->set_bone_pose(boneIdx, pform);
456
457 if (parent_node != NULL) {
458 int parent_bone_id = skeleton->find_bone(AssimpUtils::get_anim_string_from_assimp(parent_node->mName));
459 int current_bone_id = boneIdx;
460 skeleton->set_bone_parent(current_bone_id, parent_bone_id);
461 }
462 }
463 }
464
465 print_verbose("generating mesh phase from skeletal mesh");
466
467 List<Spatial *> cleanup_template_nodes;
468
469 for (Map<const aiNode *, Spatial *>::Element *key_value_pair = state.flat_node_map.front(); key_value_pair; key_value_pair = key_value_pair->next()) {
470 const aiNode *assimp_node = key_value_pair->key();
471 Spatial *mesh_template = key_value_pair->value();
472
473 ERR_CONTINUE(assimp_node == NULL);
474 ERR_CONTINUE(mesh_template == NULL);
475
476 Node *parent_node = mesh_template->get_parent();
477
478 if (mesh_template == state.root) {
479 continue;
480 }
481
482 if (parent_node == NULL) {
483 print_error("Found invalid parent node!");
484 continue; // root node
485 }
486
487 String node_name = AssimpUtils::get_assimp_string(assimp_node->mName);
488 Transform node_transform = AssimpUtils::assimp_matrix_transform(assimp_node->mTransformation);
489
490 if (assimp_node->mNumMeshes > 0) {
491 MeshInstance *mesh = create_mesh(state, assimp_node, node_name, parent_node, node_transform);
492 if (mesh) {
493
494 parent_node->remove_child(mesh_template);
495
496 // re-parent children
497 List<Node *> children;
498 // re-parent all children to new node
499 // note: since get_child_count will change during execution we must build a list first to be safe.
500 for (int childId = 0; childId < mesh_template->get_child_count(); childId++) {
501 // get child
502 Node *child = mesh_template->get_child(childId);
503 children.push_back(child);
504 }
505
506 for (List<Node *>::Element *element = children.front(); element; element = element->next()) {
507 // reparent the children to the real mesh node.
508 mesh_template->remove_child(element->get());
509 mesh->add_child(element->get());
510 element->get()->set_owner(state.root);
511 }
512
513 // update mesh in list so that each mesh node is available
514 // this makes the template unavailable which is the desired behaviour
515 state.flat_node_map[assimp_node] = mesh;
516
517 cleanup_template_nodes.push_back(mesh_template);
518
519 // clean up this list we don't need it
520 children.clear();
521 }
522 }
523 }
524
525 for (List<Spatial *>::Element *element = cleanup_template_nodes.front(); element; element = element->next()) {
526 if (element->get()) {
527 memdelete(element->get());
528 }
529 }
530 }
531
532 if (p_flags & IMPORT_ANIMATION && scene->mNumAnimations) {
533
534 state.animation_player = memnew(AnimationPlayer);
535 state.root->add_child(state.animation_player);
536 state.animation_player->set_owner(state.root);
537
538 for (uint32_t i = 0; i < scene->mNumAnimations; i++) {
539 _import_animation(state, i, p_bake_fps);
540 }
541 }
542
543 //
544 // Cleanup operations
545 //
546
547 state.mesh_cache.clear();
548 state.material_cache.clear();
549 state.light_cache.clear();
550 state.camera_cache.clear();
551 state.assimp_node_map.clear();
552 state.path_to_image_cache.clear();
553 state.nodes.clear();
554 state.flat_node_map.clear();
555 state.armature_skeletons.clear();
556 state.bone_stack.clear();
557 return state.root;
558 }
559
_insert_animation_track(ImportState & scene,const aiAnimation * assimp_anim,int track_id,int anim_fps,Ref<Animation> animation,float ticks_per_second,Skeleton * skeleton,const NodePath & node_path,const String & node_name,aiBone * track_bone)560 void EditorSceneImporterAssimp::_insert_animation_track(ImportState &scene, const aiAnimation *assimp_anim, int track_id,
561 int anim_fps, Ref<Animation> animation, float ticks_per_second,
562 Skeleton *skeleton, const NodePath &node_path,
563 const String &node_name, aiBone *track_bone) {
564 const aiNodeAnim *assimp_track = assimp_anim->mChannels[track_id];
565 //make transform track
566 int track_idx = animation->get_track_count();
567 animation->add_track(Animation::TYPE_TRANSFORM);
568 animation->track_set_path(track_idx, node_path);
569 //first determine animation length
570
571 float increment = 1.0 / float(anim_fps);
572 float time = 0.0;
573
574 bool last = false;
575
576 Vector<Vector3> pos_values;
577 Vector<float> pos_times;
578 Vector<Vector3> scale_values;
579 Vector<float> scale_times;
580 Vector<Quat> rot_values;
581 Vector<float> rot_times;
582
583 for (size_t p = 0; p < assimp_track->mNumPositionKeys; p++) {
584 aiVector3D pos = assimp_track->mPositionKeys[p].mValue;
585 pos_values.push_back(Vector3(pos.x, pos.y, pos.z));
586 pos_times.push_back(assimp_track->mPositionKeys[p].mTime / ticks_per_second);
587 }
588
589 for (size_t r = 0; r < assimp_track->mNumRotationKeys; r++) {
590 aiQuaternion quat = assimp_track->mRotationKeys[r].mValue;
591 rot_values.push_back(Quat(quat.x, quat.y, quat.z, quat.w).normalized());
592 rot_times.push_back(assimp_track->mRotationKeys[r].mTime / ticks_per_second);
593 }
594
595 for (size_t sc = 0; sc < assimp_track->mNumScalingKeys; sc++) {
596 aiVector3D scale = assimp_track->mScalingKeys[sc].mValue;
597 scale_values.push_back(Vector3(scale.x, scale.y, scale.z));
598 scale_times.push_back(assimp_track->mScalingKeys[sc].mTime / ticks_per_second);
599 }
600
601 while (true) {
602 Vector3 pos;
603 Quat rot;
604 Vector3 scale(1, 1, 1);
605
606 if (pos_values.size()) {
607 pos = _interpolate_track<Vector3>(pos_times, pos_values, time, AssetImportAnimation::INTERP_LINEAR);
608 }
609
610 if (rot_values.size()) {
611 rot = _interpolate_track<Quat>(rot_times, rot_values, time,
612 AssetImportAnimation::INTERP_LINEAR)
613 .normalized();
614 }
615
616 if (scale_values.size()) {
617 scale = _interpolate_track<Vector3>(scale_times, scale_values, time, AssetImportAnimation::INTERP_LINEAR);
618 }
619
620 if (skeleton) {
621 int skeleton_bone = skeleton->find_bone(node_name);
622
623 if (skeleton_bone >= 0 && track_bone) {
624
625 Transform xform;
626 xform.basis.set_quat_scale(rot, scale);
627 xform.origin = pos;
628
629 xform = skeleton->get_bone_pose(skeleton_bone).inverse() * xform;
630
631 rot = xform.basis.get_rotation_quat();
632 rot.normalize();
633 scale = xform.basis.get_scale();
634 pos = xform.origin;
635 } else {
636 ERR_FAIL_MSG("Skeleton bone lookup failed for skeleton: " + skeleton->get_name());
637 }
638 }
639
640 animation->track_set_interpolation_type(track_idx, Animation::INTERPOLATION_LINEAR);
641 animation->transform_track_insert_key(track_idx, time, pos, rot, scale);
642
643 if (last) { //done this way so a key is always inserted past the end (for proper interpolation)
644 break;
645 }
646 time += increment;
647 if (time >= animation->get_length()) {
648 last = true;
649 }
650 }
651 }
652
653 // I really do not like this but need to figure out a better way of removing it later.
get_node_by_name(ImportState & state,String name)654 Node *EditorSceneImporterAssimp::get_node_by_name(ImportState &state, String name) {
655 for (Map<const aiNode *, Spatial *>::Element *key_value_pair = state.flat_node_map.front(); key_value_pair; key_value_pair = key_value_pair->next()) {
656 const aiNode *assimp_node = key_value_pair->key();
657 Spatial *node = key_value_pair->value();
658
659 String node_name = AssimpUtils::get_assimp_string(assimp_node->mName);
660 if (name == node_name && node) {
661 return node;
662 }
663 }
664 return NULL;
665 }
666
667 /* Bone stack is a fifo handler for multiple armatures since armatures aren't a thing in assimp (yet) */
RegenerateBoneStack(ImportState & state)668 void EditorSceneImporterAssimp::RegenerateBoneStack(ImportState &state) {
669
670 state.bone_stack.clear();
671 // build bone stack list
672 for (unsigned int mesh_id = 0; mesh_id < state.assimp_scene->mNumMeshes; ++mesh_id) {
673 aiMesh *mesh = state.assimp_scene->mMeshes[mesh_id];
674
675 // iterate over all the bones on the mesh for this node only!
676 for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; boneIndex++) {
677 aiBone *bone = mesh->mBones[boneIndex];
678
679 // doubtful this is required right now but best to check
680 if (!state.bone_stack.find(bone)) {
681 //print_verbose("[assimp] bone stack added: " + String(bone->mName.C_Str()) );
682 state.bone_stack.push_back(bone);
683 }
684 }
685 }
686 }
687
688 /* Bone stack is a fifo handler for multiple armatures since armatures aren't a thing in assimp (yet) */
RegenerateBoneStack(ImportState & state,aiMesh * mesh)689 void EditorSceneImporterAssimp::RegenerateBoneStack(ImportState &state, aiMesh *mesh) {
690 state.bone_stack.clear();
691 // iterate over all the bones on the mesh for this node only!
692 for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; boneIndex++) {
693 aiBone *bone = mesh->mBones[boneIndex];
694 if (state.bone_stack.find(bone) == NULL) {
695 state.bone_stack.push_back(bone);
696 }
697 }
698 }
699
700 // animation tracks are per bone
701
_import_animation(ImportState & state,int p_animation_index,int p_bake_fps)702 void EditorSceneImporterAssimp::_import_animation(ImportState &state, int p_animation_index, int p_bake_fps) {
703
704 ERR_FAIL_INDEX(p_animation_index, (int)state.assimp_scene->mNumAnimations);
705
706 const aiAnimation *anim = state.assimp_scene->mAnimations[p_animation_index];
707 String name = AssimpUtils::get_anim_string_from_assimp(anim->mName);
708 if (name == String()) {
709 name = "Animation " + itos(p_animation_index + 1);
710 }
711 print_verbose("import animation: " + name);
712 float ticks_per_second = anim->mTicksPerSecond;
713
714 if (state.assimp_scene->mMetaData != NULL && Math::is_equal_approx(ticks_per_second, 0.0f)) {
715 int32_t time_mode = 0;
716 state.assimp_scene->mMetaData->Get("TimeMode", time_mode);
717 ticks_per_second = AssimpUtils::get_fbx_fps(time_mode, state.assimp_scene);
718 }
719
720 //?
721 //if ((p_path.get_file().get_extension().to_lower() == "glb" || p_path.get_file().get_extension().to_lower() == "gltf") && Math::is_equal_approx(ticks_per_second, 0.0f)) {
722 // ticks_per_second = 1000.0f;
723 //}
724
725 if (Math::is_equal_approx(ticks_per_second, 0.0f)) {
726 ticks_per_second = 25.0f;
727 }
728
729 Ref<Animation> animation;
730 animation.instance();
731 animation->set_name(name);
732 animation->set_length(anim->mDuration / ticks_per_second);
733
734 if (name.begins_with("loop") || name.ends_with("loop") || name.begins_with("cycle") || name.ends_with("cycle")) {
735 animation->set_loop(true);
736 }
737
738 // generate bone stack for animation import
739 RegenerateBoneStack(state);
740
741 //regular tracks
742 for (size_t i = 0; i < anim->mNumChannels; i++) {
743 const aiNodeAnim *track = anim->mChannels[i];
744 String node_name = AssimpUtils::get_assimp_string(track->mNodeName);
745 print_verbose("track name import: " + node_name);
746 if (track->mNumRotationKeys == 0 && track->mNumPositionKeys == 0 && track->mNumScalingKeys == 0) {
747 continue; //do not bother
748 }
749
750 Skeleton *skeleton = NULL;
751 NodePath node_path;
752 aiBone *bone = NULL;
753
754 // Import skeleton bone animation for this track
755 // Any bone will do, no point in processing more than just what is in the skeleton
756 {
757 bone = get_bone_from_stack(state, track->mNodeName);
758
759 if (bone) {
760 // get skeleton by bone
761 skeleton = state.armature_skeletons[bone->mArmature];
762
763 if (skeleton) {
764 String path = state.root->get_path_to(skeleton);
765 path += ":" + node_name;
766 node_path = path;
767
768 if (node_path != NodePath()) {
769 _insert_animation_track(state, anim, i, p_bake_fps, animation, ticks_per_second, skeleton,
770 node_path, node_name, bone);
771 } else {
772 print_error("Failed to find valid node path for animation");
773 }
774 }
775 }
776 }
777
778 // not a bone
779 // note this is flaky it uses node names which is unreliable
780 Node *allocated_node = get_node_by_name(state, node_name);
781 // todo: implement skeleton grabbing for node based animations too :)
782 // check if node exists, if it does then also apply animation track for node and bones above are all handled.
783 // this is now inclusive animation handling so that
784 // we import all the data and do not miss anything.
785 if (allocated_node) {
786 node_path = state.root->get_path_to(allocated_node);
787
788 if (node_path != NodePath()) {
789 _insert_animation_track(state, anim, i, p_bake_fps, animation, ticks_per_second, skeleton,
790 node_path, node_name, nullptr);
791 }
792 }
793 }
794
795 //blend shape tracks
796
797 for (size_t i = 0; i < anim->mNumMorphMeshChannels; i++) {
798
799 const aiMeshMorphAnim *anim_mesh = anim->mMorphMeshChannels[i];
800
801 const String prop_name = AssimpUtils::get_assimp_string(anim_mesh->mName);
802 const String mesh_name = prop_name.split("*")[0];
803
804 ERR_CONTINUE(prop_name.split("*").size() != 2);
805
806 Node *item = get_node_by_name(state, mesh_name);
807 ERR_CONTINUE_MSG(!item, "failed to look up node by name");
808 const MeshInstance *mesh_instance = Object::cast_to<MeshInstance>(item);
809 ERR_CONTINUE(mesh_instance == NULL);
810
811 String base_path = state.root->get_path_to(mesh_instance);
812
813 Ref<Mesh> mesh = mesh_instance->get_mesh();
814 ERR_CONTINUE(mesh.is_null());
815
816 //add the tracks for this mesh
817 int base_track = animation->get_track_count();
818 for (int j = 0; j < mesh->get_blend_shape_count(); j++) {
819
820 animation->add_track(Animation::TYPE_VALUE);
821 animation->track_set_path(base_track + j, base_path + ":blend_shapes/" + mesh->get_blend_shape_name(j));
822 }
823
824 for (size_t k = 0; k < anim_mesh->mNumKeys; k++) {
825 for (size_t j = 0; j < anim_mesh->mKeys[k].mNumValuesAndWeights; j++) {
826
827 float t = anim_mesh->mKeys[k].mTime / ticks_per_second;
828 float w = anim_mesh->mKeys[k].mWeights[j];
829
830 animation->track_insert_key(base_track + j, t, w);
831 }
832 }
833 }
834
835 if (animation->get_track_count()) {
836 state.animation_player->add_animation(name, animation);
837 }
838 }
839 //
840 // Mesh Generation from indices ? why do we need so much mesh code
841 // [debt needs looked into]
842 Ref<Mesh>
_generate_mesh_from_surface_indices(ImportState & state,const Vector<int> & p_surface_indices,const aiNode * assimp_node,Ref<Skin> & skin,Skeleton * & skeleton_assigned)843 EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportState &state, const Vector<int> &p_surface_indices,
844 const aiNode *assimp_node, Ref<Skin> &skin,
845 Skeleton *&skeleton_assigned) {
846
847 Ref<ArrayMesh> mesh;
848 mesh.instance();
849 bool has_uvs = false;
850 bool compress_vert_data = state.import_flags & IMPORT_USE_COMPRESSION;
851 uint32_t mesh_flags = compress_vert_data ? Mesh::ARRAY_COMPRESS_DEFAULT : 0;
852
853 Map<String, uint32_t> morph_mesh_string_lookup;
854
855 for (int i = 0; i < p_surface_indices.size(); i++) {
856 const unsigned int mesh_idx = p_surface_indices[0];
857 const aiMesh *ai_mesh = state.assimp_scene->mMeshes[mesh_idx];
858 for (size_t j = 0; j < ai_mesh->mNumAnimMeshes; j++) {
859 String ai_anim_mesh_name = AssimpUtils::get_assimp_string(ai_mesh->mAnimMeshes[j]->mName);
860 if (!morph_mesh_string_lookup.has(ai_anim_mesh_name)) {
861 morph_mesh_string_lookup.insert(ai_anim_mesh_name, j);
862 mesh->set_blend_shape_mode(Mesh::BLEND_SHAPE_MODE_NORMALIZED);
863 if (ai_anim_mesh_name.empty()) {
864 ai_anim_mesh_name = String("morph_") + itos(j);
865 }
866 mesh->add_blend_shape(ai_anim_mesh_name);
867 }
868 }
869 }
870 //
871 // Process Vertex Weights
872 //
873 for (int i = 0; i < p_surface_indices.size(); i++) {
874 const unsigned int mesh_idx = p_surface_indices[i];
875 const aiMesh *ai_mesh = state.assimp_scene->mMeshes[mesh_idx];
876
877 Map<uint32_t, Vector<BoneInfo> > vertex_weights;
878
879 if (ai_mesh->mNumBones > 0) {
880 for (size_t b = 0; b < ai_mesh->mNumBones; b++) {
881 aiBone *bone = ai_mesh->mBones[b];
882
883 if (!skeleton_assigned) {
884 print_verbose("Assigned mesh skeleton during mesh creation");
885 skeleton_assigned = state.skeleton_bone_map[bone];
886
887 if (!skin.is_valid()) {
888 print_verbose("Configured new skin");
889 skin.instance();
890 } else {
891 print_verbose("Reusing existing skin!");
892 }
893 }
894 // skeleton_assigned =
895 String bone_name = AssimpUtils::get_assimp_string(bone->mName);
896 int bone_index = skeleton_assigned->find_bone(bone_name);
897 ERR_CONTINUE(bone_index == -1);
898 for (size_t w = 0; w < bone->mNumWeights; w++) {
899
900 aiVertexWeight ai_weights = bone->mWeights[w];
901
902 BoneInfo bi;
903 uint32_t vertex_index = ai_weights.mVertexId;
904 bi.bone = bone_index;
905 bi.weight = ai_weights.mWeight;
906
907 if (!vertex_weights.has(vertex_index)) {
908 vertex_weights[vertex_index] = Vector<BoneInfo>();
909 }
910
911 vertex_weights[vertex_index].push_back(bi);
912 }
913 }
914 }
915
916 //
917 // Create mesh from data from assimp
918 //
919
920 Ref<SurfaceTool> st;
921 st.instance();
922 st->begin(Mesh::PRIMITIVE_TRIANGLES);
923
924 for (size_t j = 0; j < ai_mesh->mNumVertices; j++) {
925
926 // Get the texture coordinates if they exist
927 if (ai_mesh->HasTextureCoords(0)) {
928 has_uvs = true;
929 st->add_uv(Vector2(ai_mesh->mTextureCoords[0][j].x, 1.0f - ai_mesh->mTextureCoords[0][j].y));
930 }
931
932 if (ai_mesh->HasTextureCoords(1)) {
933 has_uvs = true;
934 st->add_uv2(Vector2(ai_mesh->mTextureCoords[1][j].x, 1.0f - ai_mesh->mTextureCoords[1][j].y));
935 }
936
937 // Assign vertex colors
938 if (ai_mesh->HasVertexColors(0)) {
939 Color color = Color(ai_mesh->mColors[0]->r, ai_mesh->mColors[0]->g, ai_mesh->mColors[0]->b,
940 ai_mesh->mColors[0]->a);
941 st->add_color(color);
942 }
943
944 // Work out normal calculations? - this needs work it doesn't work properly on huestos
945 if (ai_mesh->mNormals != NULL) {
946 const aiVector3D normals = ai_mesh->mNormals[j];
947 const Vector3 godot_normal = Vector3(normals.x, normals.y, normals.z);
948 st->add_normal(godot_normal);
949 if (ai_mesh->HasTangentsAndBitangents()) {
950 const aiVector3D tangents = ai_mesh->mTangents[j];
951 const Vector3 godot_tangent = Vector3(tangents.x, tangents.y, tangents.z);
952 const aiVector3D bitangent = ai_mesh->mBitangents[j];
953 const Vector3 godot_bitangent = Vector3(bitangent.x, bitangent.y, bitangent.z);
954 float d = godot_normal.cross(godot_tangent).dot(godot_bitangent) > 0.0f ? 1.0f : -1.0f;
955 st->add_tangent(Plane(tangents.x, tangents.y, tangents.z, d));
956 }
957 }
958
959 // We have vertex weights right?
960 if (vertex_weights.has(j)) {
961
962 Vector<BoneInfo> bone_info = vertex_weights[j];
963 Vector<int> bones;
964 bones.resize(bone_info.size());
965 Vector<float> weights;
966 weights.resize(bone_info.size());
967
968 // todo? do we really need to loop over all bones? - assimp may have helper to find all influences on this vertex.
969 for (int k = 0; k < bone_info.size(); k++) {
970 bones.write[k] = bone_info[k].bone;
971 weights.write[k] = bone_info[k].weight;
972 }
973
974 st->add_bones(bones);
975 st->add_weights(weights);
976 }
977
978 // Assign vertex
979 const aiVector3D pos = ai_mesh->mVertices[j];
980
981 // note we must include node offset transform as this is relative to world space not local space.
982 Vector3 godot_pos = Vector3(pos.x, pos.y, pos.z);
983 st->add_vertex(godot_pos);
984 }
985
986 // fire replacement for face handling
987 for (size_t j = 0; j < ai_mesh->mNumFaces; j++) {
988 const aiFace face = ai_mesh->mFaces[j];
989 for (unsigned int k = 0; k < face.mNumIndices; k++) {
990 st->add_index(face.mIndices[k]);
991 }
992 }
993
994 if (ai_mesh->HasTangentsAndBitangents() == false && has_uvs) {
995 st->generate_tangents();
996 }
997
998 aiMaterial *ai_material = state.assimp_scene->mMaterials[ai_mesh->mMaterialIndex];
999 Ref<SpatialMaterial> mat;
1000 mat.instance();
1001
1002 int32_t mat_two_sided = 0;
1003 if (AI_SUCCESS == ai_material->Get(AI_MATKEY_TWOSIDED, mat_two_sided)) {
1004 if (mat_two_sided > 0) {
1005 mat->set_cull_mode(SpatialMaterial::CULL_DISABLED);
1006 } else {
1007 mat->set_cull_mode(SpatialMaterial::CULL_BACK);
1008 }
1009 }
1010
1011 aiString mat_name;
1012 if (AI_SUCCESS == ai_material->Get(AI_MATKEY_NAME, mat_name)) {
1013 mat->set_name(AssimpUtils::get_assimp_string(mat_name));
1014 }
1015
1016 // Culling handling for meshes
1017
1018 // cull all back faces
1019 mat->set_cull_mode(SpatialMaterial::CULL_DISABLED);
1020
1021 // Now process materials
1022 aiTextureType base_color = aiTextureType_BASE_COLOR;
1023 {
1024 String filename, path;
1025 AssimpImageData image_data;
1026
1027 if (AssimpUtils::GetAssimpTexture(state, ai_material, base_color, filename, path, image_data)) {
1028 AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
1029
1030 // anything transparent must be culled
1031 if (image_data.raw_image->detect_alpha() != Image::ALPHA_NONE) {
1032 mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
1033 mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
1034 mat->set_cull_mode(
1035 SpatialMaterial::CULL_DISABLED); // since you can see both sides in transparent mode
1036 }
1037
1038 mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, image_data.texture);
1039 }
1040 }
1041
1042 aiTextureType tex_diffuse = aiTextureType_DIFFUSE;
1043 {
1044 String filename, path;
1045 AssimpImageData image_data;
1046
1047 if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_diffuse, filename, path, image_data)) {
1048 AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
1049
1050 // anything transparent must be culled
1051 if (image_data.raw_image->detect_alpha() != Image::ALPHA_NONE) {
1052 mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
1053 mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
1054 mat->set_cull_mode(
1055 SpatialMaterial::CULL_DISABLED); // since you can see both sides in transparent mode
1056 }
1057
1058 mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, image_data.texture);
1059 }
1060
1061 aiColor4D clr_diffuse;
1062 if (AI_SUCCESS == ai_material->Get(AI_MATKEY_COLOR_DIFFUSE, clr_diffuse)) {
1063 if (Math::is_equal_approx(clr_diffuse.a, 1.0f) == false) {
1064 mat->set_feature(SpatialMaterial::FEATURE_TRANSPARENT, true);
1065 mat->set_depth_draw_mode(SpatialMaterial::DepthDrawMode::DEPTH_DRAW_ALPHA_OPAQUE_PREPASS);
1066 mat->set_cull_mode(
1067 SpatialMaterial::CULL_DISABLED); // since you can see both sides in transparent mode
1068 }
1069 mat->set_albedo(Color(clr_diffuse.r, clr_diffuse.g, clr_diffuse.b, clr_diffuse.a));
1070 }
1071 }
1072
1073 aiTextureType tex_normal = aiTextureType_NORMALS;
1074 {
1075 String filename, path;
1076 Ref<ImageTexture> texture;
1077 AssimpImageData image_data;
1078
1079 // Process texture normal map
1080 if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_normal, filename, path, image_data)) {
1081 AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
1082 mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true);
1083 mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, image_data.texture);
1084 } else {
1085 aiString texture_path;
1086 if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_NORMAL_TEXTURE, AI_PROPERTIES, texture_path)) {
1087 if (AssimpUtils::CreateAssimpTexture(state, texture_path, filename, path, image_data)) {
1088 mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true);
1089 mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, image_data.texture);
1090 }
1091 }
1092 }
1093 }
1094
1095 aiTextureType tex_normal_camera = aiTextureType_NORMAL_CAMERA;
1096 {
1097 String filename, path;
1098 Ref<ImageTexture> texture;
1099 AssimpImageData image_data;
1100
1101 // Process texture normal map
1102 if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_normal_camera, filename, path, image_data)) {
1103 AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
1104 mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true);
1105 mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, image_data.texture);
1106 }
1107 }
1108
1109 aiTextureType tex_emission_color = aiTextureType_EMISSION_COLOR;
1110 {
1111 String filename, path;
1112 Ref<ImageTexture> texture;
1113 AssimpImageData image_data;
1114
1115 // Process texture normal map
1116 if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_emission_color, filename, path, image_data)) {
1117 AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
1118 mat->set_feature(SpatialMaterial::Feature::FEATURE_NORMAL_MAPPING, true);
1119 mat->set_texture(SpatialMaterial::TEXTURE_NORMAL, image_data.texture);
1120 }
1121 }
1122
1123 aiTextureType tex_metalness = aiTextureType_METALNESS;
1124 {
1125 String filename, path;
1126 Ref<ImageTexture> texture;
1127 AssimpImageData image_data;
1128
1129 // Process texture normal map
1130 if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_metalness, filename, path, image_data)) {
1131 AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
1132 mat->set_texture(SpatialMaterial::TEXTURE_METALLIC, image_data.texture);
1133 }
1134 }
1135
1136 aiTextureType tex_roughness = aiTextureType_DIFFUSE_ROUGHNESS;
1137 {
1138 String filename, path;
1139 Ref<ImageTexture> texture;
1140 AssimpImageData image_data;
1141
1142 // Process texture normal map
1143 if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_roughness, filename, path, image_data)) {
1144 AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
1145 mat->set_texture(SpatialMaterial::TEXTURE_ROUGHNESS, image_data.texture);
1146 }
1147 }
1148
1149 aiTextureType tex_emissive = aiTextureType_EMISSIVE;
1150 {
1151 String filename = "";
1152 String path = "";
1153 Ref<Image> texture;
1154 AssimpImageData image_data;
1155
1156 if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_emissive, filename, path, image_data)) {
1157 AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
1158 mat->set_feature(SpatialMaterial::FEATURE_EMISSION, true);
1159 mat->set_texture(SpatialMaterial::TEXTURE_EMISSION, image_data.texture);
1160 } else {
1161 // Process emission textures
1162 aiString texture_emissive_path;
1163 if (AI_SUCCESS ==
1164 ai_material->Get(AI_MATKEY_FBX_MAYA_EMISSION_TEXTURE, AI_PROPERTIES, texture_emissive_path)) {
1165 if (AssimpUtils::CreateAssimpTexture(state, texture_emissive_path, filename, path, image_data)) {
1166 mat->set_feature(SpatialMaterial::FEATURE_EMISSION, true);
1167 mat->set_texture(SpatialMaterial::TEXTURE_EMISSION, image_data.texture);
1168 }
1169 } else {
1170 float pbr_emission = 0.0f;
1171 if (AI_SUCCESS == ai_material->Get(AI_MATKEY_FBX_MAYA_EMISSIVE_FACTOR, AI_NULL, pbr_emission)) {
1172 mat->set_emission(Color(pbr_emission, pbr_emission, pbr_emission, 1.0f));
1173 }
1174 }
1175 }
1176 }
1177
1178 aiTextureType tex_specular = aiTextureType_SPECULAR;
1179 {
1180 String filename, path;
1181 Ref<ImageTexture> texture;
1182 AssimpImageData image_data;
1183
1184 // Process texture normal map
1185 if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_specular, filename, path, image_data)) {
1186 AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
1187 mat->set_texture(SpatialMaterial::TEXTURE_METALLIC, image_data.texture);
1188 }
1189 }
1190
1191 aiTextureType tex_ao_map = aiTextureType_AMBIENT_OCCLUSION;
1192 {
1193 String filename, path;
1194 Ref<ImageTexture> texture;
1195 AssimpImageData image_data;
1196
1197 // Process texture normal map
1198 if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_ao_map, filename, path, image_data)) {
1199 AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture);
1200 mat->set_feature(SpatialMaterial::FEATURE_AMBIENT_OCCLUSION, true);
1201 mat->set_texture(SpatialMaterial::TEXTURE_AMBIENT_OCCLUSION, image_data.texture);
1202 }
1203 }
1204
1205 Array array_mesh = st->commit_to_arrays();
1206 Array morphs;
1207 morphs.resize(ai_mesh->mNumAnimMeshes);
1208 Mesh::PrimitiveType primitive = Mesh::PRIMITIVE_TRIANGLES;
1209
1210 for (size_t j = 0; j < ai_mesh->mNumAnimMeshes; j++) {
1211
1212 String ai_anim_mesh_name = AssimpUtils::get_assimp_string(ai_mesh->mAnimMeshes[j]->mName);
1213
1214 if (ai_anim_mesh_name.empty()) {
1215 ai_anim_mesh_name = String("morph_") + itos(j);
1216 }
1217
1218 Array array_copy;
1219 array_copy.resize(VisualServer::ARRAY_MAX);
1220
1221 for (int l = 0; l < VisualServer::ARRAY_MAX; l++) {
1222 array_copy[l] = array_mesh[l].duplicate(true);
1223 }
1224
1225 const size_t num_vertices = ai_mesh->mAnimMeshes[j]->mNumVertices;
1226 array_copy[Mesh::ARRAY_INDEX] = Variant();
1227 if (ai_mesh->mAnimMeshes[j]->HasPositions()) {
1228 PoolVector3Array vertices;
1229 vertices.resize(num_vertices);
1230 for (size_t l = 0; l < num_vertices; l++) {
1231 const aiVector3D ai_pos = ai_mesh->mAnimMeshes[j]->mVertices[l];
1232 Vector3 position = Vector3(ai_pos.x, ai_pos.y, ai_pos.z);
1233 vertices.write()[l] = position;
1234 }
1235 PoolVector3Array new_vertices = array_copy[VisualServer::ARRAY_VERTEX].duplicate(true);
1236 ERR_CONTINUE(vertices.size() != new_vertices.size());
1237 for (int32_t l = 0; l < new_vertices.size(); l++) {
1238 PoolVector3Array::Write w = new_vertices.write();
1239 w[l] = vertices[l];
1240 }
1241 array_copy[VisualServer::ARRAY_VERTEX] = new_vertices;
1242 }
1243
1244 int32_t color_set = 0;
1245 if (ai_mesh->mAnimMeshes[j]->HasVertexColors(color_set)) {
1246 PoolColorArray colors;
1247 colors.resize(num_vertices);
1248 for (size_t l = 0; l < num_vertices; l++) {
1249 const aiColor4D ai_color = ai_mesh->mAnimMeshes[j]->mColors[color_set][l];
1250 Color color = Color(ai_color.r, ai_color.g, ai_color.b, ai_color.a);
1251 colors.write()[l] = color;
1252 }
1253 PoolColorArray new_colors = array_copy[VisualServer::ARRAY_COLOR].duplicate(true);
1254 ERR_CONTINUE(colors.size() != new_colors.size());
1255 for (int32_t l = 0; l < colors.size(); l++) {
1256 PoolColorArray::Write w = new_colors.write();
1257 w[l] = colors[l];
1258 }
1259 array_copy[VisualServer::ARRAY_COLOR] = new_colors;
1260 }
1261
1262 if (ai_mesh->mAnimMeshes[j]->HasNormals()) {
1263 PoolVector3Array normals;
1264 normals.resize(num_vertices);
1265 for (size_t l = 0; l < num_vertices; l++) {
1266 const aiVector3D ai_normal = ai_mesh->mAnimMeshes[j]->mNormals[l];
1267 Vector3 normal = Vector3(ai_normal.x, ai_normal.y, ai_normal.z);
1268 normals.write()[l] = normal;
1269 }
1270 PoolVector3Array new_normals = array_copy[VisualServer::ARRAY_NORMAL].duplicate(true);
1271 ERR_CONTINUE(normals.size() != new_normals.size());
1272 for (int l = 0; l < normals.size(); l++) {
1273 PoolVector3Array::Write w = new_normals.write();
1274 w[l] = normals[l];
1275 }
1276 array_copy[VisualServer::ARRAY_NORMAL] = new_normals;
1277 }
1278
1279 if (ai_mesh->mAnimMeshes[j]->HasTangentsAndBitangents()) {
1280 PoolColorArray tangents;
1281 tangents.resize(num_vertices);
1282 PoolColorArray::Write w = tangents.write();
1283 for (size_t l = 0; l < num_vertices; l++) {
1284 AssimpUtils::calc_tangent_from_mesh(ai_mesh, j, l, l, w);
1285 }
1286 PoolRealArray new_tangents = array_copy[VisualServer::ARRAY_TANGENT].duplicate(true);
1287 ERR_CONTINUE(new_tangents.size() != tangents.size() * 4);
1288 for (int32_t l = 0; l < tangents.size(); l++) {
1289 new_tangents.write()[l + 0] = tangents[l].r;
1290 new_tangents.write()[l + 1] = tangents[l].g;
1291 new_tangents.write()[l + 2] = tangents[l].b;
1292 new_tangents.write()[l + 3] = tangents[l].a;
1293 }
1294 array_copy[VisualServer::ARRAY_TANGENT] = new_tangents;
1295 }
1296
1297 morphs[j] = array_copy;
1298 }
1299 mesh->add_surface_from_arrays(primitive, array_mesh, morphs, mesh_flags);
1300 mesh->surface_set_material(i, mat);
1301 mesh->surface_set_name(i, AssimpUtils::get_assimp_string(ai_mesh->mName));
1302 }
1303
1304 return mesh;
1305 }
1306
1307 /**
1308 * Create a new mesh for the node supplied
1309 */
1310 MeshInstance *
create_mesh(ImportState & state,const aiNode * assimp_node,const String & node_name,Node * active_node,Transform node_transform)1311 EditorSceneImporterAssimp::create_mesh(ImportState &state, const aiNode *assimp_node, const String &node_name, Node *active_node, Transform node_transform) {
1312 /* MESH NODE */
1313 Ref<Mesh> mesh;
1314 Ref<Skin> skin;
1315 // see if we have mesh cache for this.
1316 Vector<int> surface_indices;
1317
1318 RegenerateBoneStack(state);
1319
1320 // Configure indices
1321 for (uint32_t i = 0; i < assimp_node->mNumMeshes; i++) {
1322 int mesh_index = assimp_node->mMeshes[i];
1323 // create list of mesh indexes
1324 surface_indices.push_back(mesh_index);
1325 }
1326
1327 //surface_indices.sort();
1328 String mesh_key;
1329 for (int i = 0; i < surface_indices.size(); i++) {
1330 if (i > 0) {
1331 mesh_key += ":";
1332 }
1333 mesh_key += itos(surface_indices[i]);
1334 }
1335
1336 Skeleton *skeleton = NULL;
1337 aiNode *armature = NULL;
1338
1339 if (!state.mesh_cache.has(mesh_key)) {
1340 mesh = _generate_mesh_from_surface_indices(state, surface_indices, assimp_node, skin, skeleton);
1341 state.mesh_cache[mesh_key] = mesh;
1342 }
1343
1344 MeshInstance *mesh_node = memnew(MeshInstance);
1345 mesh = state.mesh_cache[mesh_key];
1346 mesh_node->set_mesh(mesh);
1347
1348 // if we have a valid skeleton set it up
1349 if (skin.is_valid()) {
1350 for (uint32_t i = 0; i < assimp_node->mNumMeshes; i++) {
1351 unsigned int mesh_index = assimp_node->mMeshes[i];
1352 const aiMesh *ai_mesh = state.assimp_scene->mMeshes[mesh_index];
1353
1354 // please remember bone id relative to the skin is NOT the mesh relative index.
1355 // it is the index relative to the skeleton that is why
1356 // we have state.bone_id_map, it allows for duplicate bone id's too :)
1357 // hope this makes sense
1358
1359 int bind_count = 0;
1360 for (unsigned int boneId = 0; boneId < ai_mesh->mNumBones; ++boneId) {
1361 aiBone *iterBone = ai_mesh->mBones[boneId];
1362
1363 // used to reparent mesh to the correct armature later on if assigned.
1364 if (!armature) {
1365 print_verbose("Configured mesh armature, will reparent later to armature");
1366 armature = iterBone->mArmature;
1367 }
1368
1369 if (skeleton) {
1370 int id = skeleton->find_bone(AssimpUtils::get_assimp_string(iterBone->mName));
1371 if (id != -1) {
1372 print_verbose("Set bind bone: mesh: " + itos(mesh_index) + " bone index: " + itos(id));
1373 Transform t = AssimpUtils::assimp_matrix_transform(iterBone->mOffsetMatrix);
1374
1375 skin->add_bind(bind_count, t);
1376 skin->set_bind_bone(bind_count, id);
1377 bind_count++;
1378 }
1379 }
1380 }
1381 }
1382
1383 print_verbose("Finished configuring bind pose for skin mesh");
1384 }
1385
1386 // this code parents all meshes with bones to the armature they are for
1387 // GLTF2 specification relies on this and we are enforcing it for FBX.
1388 if (armature && state.flat_node_map[armature]) {
1389 Node *armature_parent = state.flat_node_map[armature];
1390 print_verbose("Parented mesh " + node_name + " to armature " + armature_parent->get_name());
1391 // static mesh handling
1392 armature_parent->add_child(mesh_node);
1393 // transform must be identity
1394 mesh_node->set_global_transform(Transform());
1395 mesh_node->set_name(node_name);
1396 mesh_node->set_owner(state.root);
1397 } else {
1398 // static mesh handling
1399 active_node->add_child(mesh_node);
1400 mesh_node->set_global_transform(node_transform);
1401 mesh_node->set_name(node_name);
1402 mesh_node->set_owner(state.root);
1403 }
1404
1405 if (skeleton) {
1406 print_verbose("Attempted to set skeleton path!");
1407 mesh_node->set_skeleton_path(mesh_node->get_path_to(skeleton));
1408 mesh_node->set_skin(skin);
1409 }
1410
1411 return mesh_node;
1412 }
1413
1414 /**
1415 * Create a light for the scene
1416 * Automatically caches lights for lookup later
1417 */
create_light(ImportState & state,const String & node_name,Transform & look_at_transform)1418 Spatial *EditorSceneImporterAssimp::create_light(
1419 ImportState &state,
1420 const String &node_name,
1421 Transform &look_at_transform) {
1422 Light *light = NULL;
1423 aiLight *assimp_light = state.assimp_scene->mLights[state.light_cache[node_name]];
1424 ERR_FAIL_COND_V(!assimp_light, NULL);
1425
1426 if (assimp_light->mType == aiLightSource_DIRECTIONAL) {
1427 light = memnew(DirectionalLight);
1428 } else if (assimp_light->mType == aiLightSource_POINT) {
1429 light = memnew(OmniLight);
1430 } else if (assimp_light->mType == aiLightSource_SPOT) {
1431 light = memnew(SpotLight);
1432 }
1433 ERR_FAIL_COND_V(light == NULL, NULL);
1434
1435 if (assimp_light->mType != aiLightSource_POINT) {
1436 Vector3 pos = Vector3(
1437 assimp_light->mPosition.x,
1438 assimp_light->mPosition.y,
1439 assimp_light->mPosition.z);
1440 Vector3 look_at = Vector3(
1441 assimp_light->mDirection.y,
1442 assimp_light->mDirection.x,
1443 assimp_light->mDirection.z)
1444 .normalized();
1445 Vector3 up = Vector3(
1446 assimp_light->mUp.x,
1447 assimp_light->mUp.y,
1448 assimp_light->mUp.z);
1449
1450 look_at_transform.set_look_at(pos, look_at, up);
1451 }
1452 // properties for light variables should be put here.
1453 // not really hugely important yet but we will need them in the future
1454
1455 light->set_color(
1456 Color(assimp_light->mColorDiffuse.r, assimp_light->mColorDiffuse.g, assimp_light->mColorDiffuse.b));
1457
1458 return light;
1459 }
1460
1461 /**
1462 * Create camera for the scene
1463 */
create_camera(ImportState & state,const String & node_name,Transform & look_at_transform)1464 Spatial *EditorSceneImporterAssimp::create_camera(
1465 ImportState &state,
1466 const String &node_name,
1467 Transform &look_at_transform) {
1468 aiCamera *camera = state.assimp_scene->mCameras[state.camera_cache[node_name]];
1469 ERR_FAIL_COND_V(!camera, NULL);
1470
1471 Camera *camera_node = memnew(Camera);
1472 ERR_FAIL_COND_V(!camera_node, NULL);
1473 float near = camera->mClipPlaneNear;
1474 if (Math::is_equal_approx(near, 0.0f)) {
1475 near = 0.1f;
1476 }
1477 camera_node->set_perspective(Math::rad2deg(camera->mHorizontalFOV) * 2.0f, near, camera->mClipPlaneFar);
1478 Vector3 pos = Vector3(camera->mPosition.x, camera->mPosition.y, camera->mPosition.z);
1479 Vector3 look_at = Vector3(camera->mLookAt.y, camera->mLookAt.x, camera->mLookAt.z).normalized();
1480 Vector3 up = Vector3(camera->mUp.x, camera->mUp.y, camera->mUp.z);
1481
1482 look_at_transform.set_look_at(pos + look_at_transform.origin, look_at, up);
1483 return camera_node;
1484 }
1485
1486 /**
1487 * Generate node
1488 * Recursive call to iterate over all nodes
1489 */
_generate_node(ImportState & state,const aiNode * assimp_node)1490 void EditorSceneImporterAssimp::_generate_node(
1491 ImportState &state,
1492 const aiNode *assimp_node) {
1493
1494 ERR_FAIL_COND(assimp_node == NULL);
1495 state.nodes.push_back(assimp_node);
1496 String parent_name = AssimpUtils::get_assimp_string(assimp_node->mParent->mName);
1497
1498 // please note
1499 // duplicate bone names exist
1500 // this is why we only check if the bone exists
1501 // so everything else is useless but the name
1502 // please do not copy any other values from get_bone_by_name.
1503 aiBone *parent_bone = get_bone_by_name(state.assimp_scene, assimp_node->mParent->mName);
1504 aiBone *current_bone = get_bone_by_name(state.assimp_scene, assimp_node->mName);
1505
1506 // is this an armature
1507 // parent null
1508 // and this is the first bone :)
1509 if (parent_bone == NULL && current_bone) {
1510 state.armature_nodes.push_back(assimp_node->mParent);
1511 print_verbose("found valid armature: " + parent_name);
1512 }
1513
1514 for (size_t i = 0; i < assimp_node->mNumChildren; i++) {
1515 _generate_node(state, assimp_node->mChildren[i]);
1516 }
1517 }
1518