| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | /*************************************************************************/ | 
					
						
							|  |  |  | /*  editor_scene_importer_assimp.cpp                                     */ | 
					
						
							|  |  |  | /*************************************************************************/ | 
					
						
							|  |  |  | /*                       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.                */ | 
					
						
							|  |  |  | /*************************************************************************/ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | #include "editor_scene_importer_assimp.h"
 | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | #include "core/io/image_loader.h"
 | 
					
						
							|  |  |  | #include "editor/import/resource_importer_scene.h"
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | #include "import_utils.h"
 | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | #include "scene/3d/camera.h"
 | 
					
						
							|  |  |  | #include "scene/3d/light.h"
 | 
					
						
							|  |  |  | #include "scene/3d/mesh_instance.h"
 | 
					
						
							|  |  |  | #include "scene/main/node.h"
 | 
					
						
							|  |  |  | #include "scene/resources/material.h"
 | 
					
						
							|  |  |  | #include "scene/resources/surface_tool.h"
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include <assimp/matrix4x4.h>
 | 
					
						
							|  |  |  | #include <assimp/postprocess.h>
 | 
					
						
							|  |  |  | #include <assimp/scene.h>
 | 
					
						
							|  |  |  | #include <assimp/Importer.hpp>
 | 
					
						
							|  |  |  | #include <assimp/LogStream.hpp>
 | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | #include <string>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | // move into assimp
 | 
					
						
							|  |  |  | aiBone *get_bone_by_name(const aiScene *scene, aiString bone_name) { | 
					
						
							|  |  |  | 	for (unsigned int mesh_id = 0; mesh_id < scene->mNumMeshes; ++mesh_id) { | 
					
						
							|  |  |  | 		aiMesh *mesh = scene->mMeshes[mesh_id]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// iterate over all the bones on the mesh for this node only!
 | 
					
						
							|  |  |  | 		for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; boneIndex++) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			aiBone *bone = mesh->mBones[boneIndex]; | 
					
						
							|  |  |  | 			if (bone->mName == bone_name) { | 
					
						
							|  |  |  | 				printf("matched bone by name: %s\n", bone->mName.C_Str()); | 
					
						
							|  |  |  | 				return bone; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | void EditorSceneImporterAssimp::get_extensions(List<String> *r_extensions) const { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const String import_setting_string = "filesystem/import/open_asset_import/"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Map<String, ImportFormat> import_format; | 
					
						
							|  |  |  | 	{ | 
					
						
							|  |  |  | 		Vector<String> exts; | 
					
						
							|  |  |  | 		exts.push_back("fbx"); | 
					
						
							|  |  |  | 		ImportFormat import = { exts, true }; | 
					
						
							|  |  |  | 		import_format.insert("fbx", import); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for (Map<String, ImportFormat>::Element *E = import_format.front(); E; E = E->next()) { | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 		_register_project_setting_import(E->key(), import_setting_string, E->get().extensions, r_extensions, | 
					
						
							|  |  |  | 				E->get().is_default); | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | void EditorSceneImporterAssimp::_register_project_setting_import(const String generic, const String import_setting_string, | 
					
						
							|  |  |  | 		const Vector<String> &exts, List<String> *r_extensions, | 
					
						
							|  |  |  | 		const bool p_enabled) const { | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 	const String use_generic = "use_" + generic; | 
					
						
							|  |  |  | 	_GLOBAL_DEF(import_setting_string + use_generic, p_enabled, true); | 
					
						
							|  |  |  | 	if (ProjectSettings::get_singleton()->get(import_setting_string + use_generic)) { | 
					
						
							|  |  |  | 		for (int32_t i = 0; i < exts.size(); i++) { | 
					
						
							|  |  |  | 			r_extensions->push_back(exts[i]); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | uint32_t EditorSceneImporterAssimp::get_import_flags() const { | 
					
						
							|  |  |  | 	return IMPORT_SCENE; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void EditorSceneImporterAssimp::_bind_methods() { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | Node *EditorSceneImporterAssimp::import_scene(const String &p_path, uint32_t p_flags, int p_bake_fps, | 
					
						
							|  |  |  | 		List<String> *r_missing_deps, Error *r_err) { | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 	Assimp::Importer importer; | 
					
						
							|  |  |  | 	std::wstring w_path = ProjectSettings::get_singleton()->globalize_path(p_path).c_str(); | 
					
						
							|  |  |  | 	std::string s_path(w_path.begin(), w_path.end()); | 
					
						
							|  |  |  | 	importer.SetPropertyBool(AI_CONFIG_PP_FD_REMOVE, true); | 
					
						
							|  |  |  | 	// Cannot remove pivot points because the static mesh will be in the wrong place
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 	importer.SetPropertyBool(AI_CONFIG_IMPORT_FBX_PRESERVE_PIVOTS, false); | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 	int32_t max_bone_weights = 4; | 
					
						
							|  |  |  | 	//if (p_flags & IMPORT_ANIMATION_EIGHT_WEIGHTS) {
 | 
					
						
							|  |  |  | 	//	const int eight_bones = 8;
 | 
					
						
							|  |  |  | 	//	importer.SetPropertyBool(AI_CONFIG_PP_LBW_MAX_WEIGHTS, eight_bones);
 | 
					
						
							|  |  |  | 	//	max_bone_weights = eight_bones;
 | 
					
						
							|  |  |  | 	//}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	importer.SetPropertyInteger(AI_CONFIG_PP_SBP_REMOVE, aiPrimitiveType_LINE | aiPrimitiveType_POINT); | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 	//importer.SetPropertyFloat(AI_CONFIG_PP_DB_THRESHOLD, 1.0f);
 | 
					
						
							|  |  |  | 	int32_t post_process_Steps = aiProcess_CalcTangentSpace | | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 								 aiProcess_GlobalScale | | 
					
						
							|  |  |  | 								 // imports models and listens to their file scale for CM to M conversions
 | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 								 //aiProcess_FlipUVs |
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 								 aiProcess_FlipWindingOrder | | 
					
						
							|  |  |  | 								 // very important for culling so that it is done in the correct order.
 | 
					
						
							| 
									
										
										
										
											2019-04-17 10:48:54 -07:00
										 |  |  | 								 //aiProcess_DropNormals |
 | 
					
						
							|  |  |  | 								 //aiProcess_GenSmoothNormals |
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 								 //aiProcess_JoinIdenticalVertices |
 | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 								 aiProcess_ImproveCacheLocality | | 
					
						
							|  |  |  | 								 //aiProcess_RemoveRedundantMaterials | // Causes a crash
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 								 //aiProcess_SplitLargeMeshes |
 | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 								 aiProcess_Triangulate | | 
					
						
							|  |  |  | 								 aiProcess_GenUVCoords | | 
					
						
							|  |  |  | 								 //aiProcess_FindDegenerates |
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 								 //aiProcess_SortByPType |
 | 
					
						
							|  |  |  | 								 // aiProcess_FindInvalidData |
 | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 								 aiProcess_TransformUVCoords | | 
					
						
							|  |  |  | 								 aiProcess_FindInstances | | 
					
						
							|  |  |  | 								 //aiProcess_FixInfacingNormals |
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:06:06 +00:00
										 |  |  | 								 //aiProcess_ValidateDataStructure |
 | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 								 aiProcess_OptimizeMeshes | | 
					
						
							| 
									
										
										
										
											2019-11-05 17:06:06 +00:00
										 |  |  | 								 aiProcess_PopulateArmatureData | | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 								 //aiProcess_OptimizeGraph |
 | 
					
						
							|  |  |  | 								 //aiProcess_Debone |
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 								 // aiProcess_EmbedTextures |
 | 
					
						
							|  |  |  | 								 //aiProcess_SplitByBoneCount |
 | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 								 0; | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 	aiScene *scene = (aiScene *)importer.ReadFile(s_path.c_str(), post_process_Steps); | 
					
						
							| 
									
										
										
										
											2019-11-11 10:24:04 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ERR_FAIL_COND_V_MSG(scene == NULL, NULL, String("Open Asset Import failed to open: ") + String(importer.GetErrorString())); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 	return _generate_scene(p_path, scene, p_flags, p_bake_fps, max_bone_weights); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template <class T> | 
					
						
							|  |  |  | struct EditorSceneImporterAssetImportInterpolate { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	T lerp(const T &a, const T &b, float c) const { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return a + (b - a) * c; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	T catmull_rom(const T &p0, const T &p1, const T &p2, const T &p3, float t) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		float t2 = t * t; | 
					
						
							|  |  |  | 		float t3 = t2 * t; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 		return 0.5f * ((2.0f * p1) + (-p0 + p2) * t + (2.0f * p0 - 5.0f * p1 + 4 * p2 - p3) * t2 + | 
					
						
							|  |  |  | 							  (-p0 + 3.0f * p1 - 3.0f * p2 + p3) * t3); | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	T bezier(T start, T control_1, T control_2, T end, float t) { | 
					
						
							|  |  |  | 		/* Formula from Wikipedia article on Bezier curves. */ | 
					
						
							|  |  |  | 		real_t omt = (1.0 - t); | 
					
						
							|  |  |  | 		real_t omt2 = omt * omt; | 
					
						
							|  |  |  | 		real_t omt3 = omt2 * omt; | 
					
						
							|  |  |  | 		real_t t2 = t * t; | 
					
						
							|  |  |  | 		real_t t3 = t2 * t; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return start * omt3 + control_1 * omt2 * t * 3.0 + control_2 * omt * t2 * 3.0 + end * t3; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | //thank you for existing, partial specialization
 | 
					
						
							|  |  |  | template <> | 
					
						
							|  |  |  | struct EditorSceneImporterAssetImportInterpolate<Quat> { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Quat lerp(const Quat &a, const Quat &b, float c) const { | 
					
						
							|  |  |  | 		ERR_FAIL_COND_V(!a.is_normalized(), Quat()); | 
					
						
							|  |  |  | 		ERR_FAIL_COND_V(!b.is_normalized(), Quat()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return a.slerp(b, c).normalized(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Quat catmull_rom(const Quat &p0, const Quat &p1, const Quat &p2, const Quat &p3, float c) { | 
					
						
							|  |  |  | 		ERR_FAIL_COND_V(!p1.is_normalized(), Quat()); | 
					
						
							|  |  |  | 		ERR_FAIL_COND_V(!p2.is_normalized(), Quat()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return p1.slerp(p2, c).normalized(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Quat bezier(Quat start, Quat control_1, Quat control_2, Quat end, float t) { | 
					
						
							|  |  |  | 		ERR_FAIL_COND_V(!start.is_normalized(), Quat()); | 
					
						
							|  |  |  | 		ERR_FAIL_COND_V(!end.is_normalized(), Quat()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return start.slerp(end, t).normalized(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template <class T> | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | T EditorSceneImporterAssimp::_interpolate_track(const Vector<float> &p_times, const Vector<T> &p_values, float p_time, | 
					
						
							|  |  |  | 		AssetImportAnimation::Interpolation p_interp) { | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 	//could use binary search, worth it?
 | 
					
						
							|  |  |  | 	int idx = -1; | 
					
						
							|  |  |  | 	for (int i = 0; i < p_times.size(); i++) { | 
					
						
							|  |  |  | 		if (p_times[i] > p_time) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		idx++; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	EditorSceneImporterAssetImportInterpolate<T> interp; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (p_interp) { | 
					
						
							|  |  |  | 		case AssetImportAnimation::INTERP_LINEAR: { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (idx == -1) { | 
					
						
							|  |  |  | 				return p_values[0]; | 
					
						
							|  |  |  | 			} else if (idx >= p_times.size() - 1) { | 
					
						
							|  |  |  | 				return p_values[p_times.size() - 1]; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			return interp.lerp(p_values[idx], p_values[idx + 1], c); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		} break; | 
					
						
							|  |  |  | 		case AssetImportAnimation::INTERP_STEP: { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (idx == -1) { | 
					
						
							|  |  |  | 				return p_values[0]; | 
					
						
							|  |  |  | 			} else if (idx >= p_times.size() - 1) { | 
					
						
							|  |  |  | 				return p_values[p_times.size() - 1]; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			return p_values[idx]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		} break; | 
					
						
							|  |  |  | 		case AssetImportAnimation::INTERP_CATMULLROMSPLINE: { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (idx == -1) { | 
					
						
							|  |  |  | 				return p_values[1]; | 
					
						
							|  |  |  | 			} else if (idx >= p_times.size() - 1) { | 
					
						
							|  |  |  | 				return p_values[1 + p_times.size() - 1]; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			return interp.catmull_rom(p_values[idx - 1], p_values[idx], p_values[idx + 1], p_values[idx + 3], c); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		} break; | 
					
						
							|  |  |  | 		case AssetImportAnimation::INTERP_CUBIC_SPLINE: { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (idx == -1) { | 
					
						
							|  |  |  | 				return p_values[1]; | 
					
						
							|  |  |  | 			} else if (idx >= p_times.size() - 1) { | 
					
						
							|  |  |  | 				return p_values[(p_times.size() - 1) * 3 + 1]; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			float c = (p_time - p_times[idx]) / (p_times[idx + 1] - p_times[idx]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			T from = p_values[idx * 3 + 1]; | 
					
						
							|  |  |  | 			T c1 = from + p_values[idx * 3 + 2]; | 
					
						
							|  |  |  | 			T to = p_values[idx * 3 + 4]; | 
					
						
							|  |  |  | 			T c2 = to + p_values[idx * 3 + 3]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			return interp.bezier(from, c1, c2, to, c); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		} break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ERR_FAIL_V(p_values[0]); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | aiBone *EditorSceneImporterAssimp::get_bone_from_stack(ImportState &state, aiString name) { | 
					
						
							|  |  |  | 	List<aiBone *>::Element *iter; | 
					
						
							|  |  |  | 	aiBone *bone = NULL; | 
					
						
							|  |  |  | 	for (iter = state.bone_stack.front(); iter; iter = iter->next()) { | 
					
						
							|  |  |  | 		bone = (aiBone *)iter->get(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (bone && bone->mName == name) { | 
					
						
							|  |  |  | 			state.bone_stack.erase(bone); | 
					
						
							|  |  |  | 			return bone; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Spatial * | 
					
						
							|  |  |  | EditorSceneImporterAssimp::_generate_scene(const String &p_path, aiScene *scene, const uint32_t p_flags, int p_bake_fps, | 
					
						
							|  |  |  | 		const int32_t p_max_bone_weights) { | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 	ERR_FAIL_COND_V(scene == NULL, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ImportState state; | 
					
						
							|  |  |  | 	state.path = p_path; | 
					
						
							|  |  |  | 	state.assimp_scene = scene; | 
					
						
							|  |  |  | 	state.max_bone_weights = p_max_bone_weights; | 
					
						
							|  |  |  | 	state.animation_player = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	// populate light map
 | 
					
						
							|  |  |  | 	for (unsigned int l = 0; l < scene->mNumLights; l++) { | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 		aiLight *ai_light = scene->mLights[l]; | 
					
						
							|  |  |  | 		ERR_CONTINUE(ai_light == NULL); | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 		state.light_cache[AssimpUtils::get_assimp_string(ai_light->mName)] = l; | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	// fill camera cache
 | 
					
						
							|  |  |  | 	for (unsigned int c = 0; c < scene->mNumCameras; c++) { | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 		aiCamera *ai_camera = scene->mCameras[c]; | 
					
						
							|  |  |  | 		ERR_CONTINUE(ai_camera == NULL); | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 		state.camera_cache[AssimpUtils::get_assimp_string(ai_camera->mName)] = c; | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 	if (scene->mRootNode) { | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 		state.nodes.push_back(scene->mRootNode); | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 		// make flat node tree - in order to make processing deterministic
 | 
					
						
							|  |  |  | 		for (unsigned int i = 0; i < scene->mRootNode->mNumChildren; i++) { | 
					
						
							|  |  |  | 			_generate_node(state, scene->mRootNode->mChildren[i]); | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 		RegenerateBoneStack(state); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		Node *last_valid_parent = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		List<const aiNode *>::Element *iter; | 
					
						
							|  |  |  | 		for (iter = state.nodes.front(); iter; iter = iter->next()) { | 
					
						
							|  |  |  | 			const aiNode *element_assimp_node = iter->get(); | 
					
						
							|  |  |  | 			const aiNode *parent_assimp_node = element_assimp_node->mParent; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			String node_name = AssimpUtils::get_assimp_string(element_assimp_node->mName); | 
					
						
							|  |  |  | 			//print_verbose("node: " + node_name);
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			Spatial *spatial = NULL; | 
					
						
							|  |  |  | 			Transform transform = AssimpUtils::assimp_matrix_transform(element_assimp_node->mTransformation); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// retrieve this node bone
 | 
					
						
							|  |  |  | 			aiBone *bone = get_bone_from_stack(state, element_assimp_node->mName); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (state.light_cache.has(node_name)) { | 
					
						
							|  |  |  | 				spatial = create_light(state, node_name, transform); | 
					
						
							|  |  |  | 			} else if (state.camera_cache.has(node_name)) { | 
					
						
							|  |  |  | 				spatial = create_camera(state, node_name, transform); | 
					
						
							|  |  |  | 			} else if (state.armature_nodes.find(element_assimp_node)) { | 
					
						
							|  |  |  | 				// create skeleton
 | 
					
						
							|  |  |  | 				print_verbose("Making skeleton: " + node_name); | 
					
						
							|  |  |  | 				Skeleton *skeleton = memnew(Skeleton); | 
					
						
							|  |  |  | 				spatial = skeleton; | 
					
						
							|  |  |  | 				if (!state.armature_skeletons.has(element_assimp_node)) { | 
					
						
							|  |  |  | 					state.armature_skeletons.insert(element_assimp_node, skeleton); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else if (bone != NULL) { | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			} else if (element_assimp_node->mNumMeshes > 0) { | 
					
						
							|  |  |  | 				spatial = memnew(Spatial); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				spatial = memnew(Spatial); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			ERR_CONTINUE_MSG(spatial == NULL, "FBX Import - are we out of ram?"); | 
					
						
							|  |  |  | 			// we on purpose set the transform and name after creating the node.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			spatial->set_name(node_name); | 
					
						
							|  |  |  | 			spatial->set_global_transform(transform); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// first element is root
 | 
					
						
							|  |  |  | 			if (iter == state.nodes.front()) { | 
					
						
							|  |  |  | 				state.root = spatial; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// flat node map parent lookup tool
 | 
					
						
							|  |  |  | 			state.flat_node_map.insert(element_assimp_node, spatial); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			Map<const aiNode *, Spatial *>::Element *parent_lookup = state.flat_node_map.find(parent_assimp_node); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// note: this always fails on the root node :) keep that in mind this is by design
 | 
					
						
							|  |  |  | 			if (parent_lookup) { | 
					
						
							|  |  |  | 				Spatial *parent_node = parent_lookup->value(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				ERR_FAIL_COND_V_MSG(parent_node == NULL, state.root, | 
					
						
							|  |  |  | 						"Parent node invalid even though lookup successful, out of ram?") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if (parent_node && spatial != state.root) { | 
					
						
							|  |  |  | 					parent_node->add_child(spatial); | 
					
						
							|  |  |  | 					spatial->set_owner(state.root); | 
					
						
							|  |  |  | 				} else if (spatial == state.root) { | 
					
						
							|  |  |  | 					// required - think about it root never has a parent yet is valid, anything else without a parent is not valid.
 | 
					
						
							|  |  |  | 				} else // Safety for instances
 | 
					
						
							|  |  |  | 				{ | 
					
						
							|  |  |  | 					WARN_PRINT( | 
					
						
							|  |  |  | 							"Failed to find parent node instance after lookup, serious warning report to godot with model"); | 
					
						
							|  |  |  | 					memdelete(spatial); // this node is broken
 | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} else if (spatial != state.root) { | 
					
						
							|  |  |  | 				// if the ainode is not in the tree
 | 
					
						
							|  |  |  | 				// parent it to the last good parent found
 | 
					
						
							|  |  |  | 				if (last_valid_parent) { | 
					
						
							|  |  |  | 					last_valid_parent->add_child(spatial); | 
					
						
							|  |  |  | 					spatial->set_owner(state.root); | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					// this is a serious error?
 | 
					
						
							|  |  |  | 					memdelete(spatial); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// update last valid parent
 | 
					
						
							|  |  |  | 			last_valid_parent = spatial; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		print_verbose("node counts: " + itos(state.nodes.size())); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// make clean bone stack
 | 
					
						
							|  |  |  | 		RegenerateBoneStack(state); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		print_verbose("generating godot bone data"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		print_verbose("Godot bone stack count: " + itos(state.bone_stack.size())); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// This is a list of bones, duplicates are from other meshes and must be dealt with properly
 | 
					
						
							|  |  |  | 		for (List<aiBone *>::Element *element = state.bone_stack.front(); element; element = element->next()) { | 
					
						
							|  |  |  | 			aiBone *bone = element->get(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			ERR_CONTINUE_MSG(!bone, "invalid bone read from assimp?"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Utilities for armature lookup - for now only FBX makes these
 | 
					
						
							|  |  |  | 			aiNode *armature_for_bone = bone->mArmature; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Utilities for bone node lookup - for now only FBX makes these
 | 
					
						
							|  |  |  | 			aiNode *bone_node = bone->mNode; | 
					
						
							|  |  |  | 			aiNode *parent_node = bone_node->mParent; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			String bone_name = AssimpUtils::get_anim_string_from_assimp(bone->mName); | 
					
						
							|  |  |  | 			ERR_CONTINUE_MSG(armature_for_bone == NULL, "Armature for bone invalid: " + bone_name); | 
					
						
							|  |  |  | 			Skeleton *skeleton = state.armature_skeletons[armature_for_bone]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			state.skeleton_bone_map[bone] = skeleton; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (bone_name.empty()) { | 
					
						
							|  |  |  | 				bone_name = "untitled_bone_name"; | 
					
						
							|  |  |  | 				WARN_PRINT("Untitled bone name detected... report with file please"); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// todo: this is where skin support goes
 | 
					
						
							|  |  |  | 			if (skeleton && skeleton->find_bone(bone_name) == -1) { | 
					
						
							|  |  |  | 				print_verbose("[Godot Glue] Imported bone" + bone_name); | 
					
						
							|  |  |  | 				int boneIdx = skeleton->get_bone_count(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				Transform pform = AssimpUtils::assimp_matrix_transform(bone->mNode->mTransformation); | 
					
						
							|  |  |  | 				skeleton->add_bone(bone_name); | 
					
						
							|  |  |  | 				skeleton->set_bone_rest(boneIdx, pform); | 
					
						
							|  |  |  | 				skeleton->set_bone_pose(boneIdx, pform); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if (parent_node != NULL) { | 
					
						
							|  |  |  | 					int parent_bone_id = skeleton->find_bone(AssimpUtils::get_anim_string_from_assimp(parent_node->mName)); | 
					
						
							|  |  |  | 					int current_bone_id = boneIdx; | 
					
						
							|  |  |  | 					skeleton->set_bone_parent(current_bone_id, parent_bone_id); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		print_verbose("generating mesh phase from skeletal mesh"); | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		List<Spatial *> cleanup_template_nodes; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for (Map<const aiNode *, Spatial *>::Element *key_value_pair = state.flat_node_map.front(); key_value_pair; key_value_pair = key_value_pair->next()) { | 
					
						
							|  |  |  | 			const aiNode *assimp_node = key_value_pair->key(); | 
					
						
							|  |  |  | 			Spatial *mesh_template = key_value_pair->value(); | 
					
						
							|  |  |  | 			Node *parent_node = mesh_template->get_parent(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			ERR_CONTINUE(assimp_node == NULL); | 
					
						
							|  |  |  | 			ERR_CONTINUE(mesh_template == NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (mesh_template == state.root) { | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (parent_node == NULL) { | 
					
						
							|  |  |  | 				print_error("Found invalid parent node!"); | 
					
						
							|  |  |  | 				continue; // root node
 | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			String node_name = AssimpUtils::get_assimp_string(assimp_node->mName); | 
					
						
							|  |  |  | 			Transform node_transform = AssimpUtils::assimp_matrix_transform(assimp_node->mTransformation); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (assimp_node->mNumMeshes > 0) { | 
					
						
							|  |  |  | 				MeshInstance *mesh = create_mesh(state, assimp_node, node_name, parent_node, node_transform); | 
					
						
							|  |  |  | 				if (mesh) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					parent_node->remove_child(mesh_template); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					// re-parent children
 | 
					
						
							|  |  |  | 					List<Node *> children; | 
					
						
							|  |  |  | 					// re-parent all children to new node
 | 
					
						
							|  |  |  | 					// note: since get_child_count will change during execution we must build a list first to be safe.
 | 
					
						
							|  |  |  | 					for (int childId = 0; childId < mesh_template->get_child_count(); childId++) { | 
					
						
							|  |  |  | 						// get child
 | 
					
						
							|  |  |  | 						Node *child = mesh_template->get_child(childId); | 
					
						
							|  |  |  | 						children.push_back(child); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					for (List<Node *>::Element *element = children.front(); element; element = element->next()) { | 
					
						
							|  |  |  | 						// reparent the children to the real mesh node.
 | 
					
						
							|  |  |  | 						mesh_template->remove_child(element->get()); | 
					
						
							|  |  |  | 						mesh->add_child(element->get()); | 
					
						
							|  |  |  | 						element->get()->set_owner(state.root); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					// update mesh in list so that each mesh node is available
 | 
					
						
							|  |  |  | 					// this makes the template unavailable which is the desired behaviour
 | 
					
						
							|  |  |  | 					state.flat_node_map[assimp_node] = mesh; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					cleanup_template_nodes.push_back(mesh_template); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					// clean up this list we don't need it
 | 
					
						
							|  |  |  | 					children.clear(); | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for (List<Spatial *>::Element *element = cleanup_template_nodes.front(); element; element = element->next()) { | 
					
						
							|  |  |  | 			if (element->get()) { | 
					
						
							|  |  |  | 				memdelete(element->get()); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 	if (p_flags & IMPORT_ANIMATION && scene->mNumAnimations) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		state.animation_player = memnew(AnimationPlayer); | 
					
						
							|  |  |  | 		state.root->add_child(state.animation_player); | 
					
						
							|  |  |  | 		state.animation_player->set_owner(state.root); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for (uint32_t i = 0; i < scene->mNumAnimations; i++) { | 
					
						
							|  |  |  | 			_import_animation(state, i, p_bake_fps); | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// Cleanup operations
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	state.mesh_cache.clear(); | 
					
						
							|  |  |  | 	state.material_cache.clear(); | 
					
						
							|  |  |  | 	state.light_cache.clear(); | 
					
						
							|  |  |  | 	state.camera_cache.clear(); | 
					
						
							|  |  |  | 	state.assimp_node_map.clear(); | 
					
						
							|  |  |  | 	state.path_to_image_cache.clear(); | 
					
						
							|  |  |  | 	state.nodes.clear(); | 
					
						
							|  |  |  | 	state.flat_node_map.clear(); | 
					
						
							|  |  |  | 	state.armature_skeletons.clear(); | 
					
						
							|  |  |  | 	state.bone_stack.clear(); | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 	return state.root; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | void EditorSceneImporterAssimp::_insert_animation_track(ImportState &scene, const aiAnimation *assimp_anim, int track_id, | 
					
						
							|  |  |  | 		int anim_fps, Ref<Animation> animation, float ticks_per_second, | 
					
						
							|  |  |  | 		Skeleton *skeleton, const NodePath &node_path, | 
					
						
							|  |  |  | 		const String &node_name, aiBone *track_bone) { | 
					
						
							|  |  |  | 	const aiNodeAnim *assimp_track = assimp_anim->mChannels[track_id]; | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 	//make transform track
 | 
					
						
							|  |  |  | 	int track_idx = animation->get_track_count(); | 
					
						
							|  |  |  | 	animation->add_track(Animation::TYPE_TRANSFORM); | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	animation->track_set_path(track_idx, node_path); | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 	//first determine animation length
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	float increment = 1.0 / float(anim_fps); | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 	float time = 0.0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bool last = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 	Vector<Vector3> pos_values; | 
					
						
							|  |  |  | 	Vector<float> pos_times; | 
					
						
							|  |  |  | 	Vector<Vector3> scale_values; | 
					
						
							|  |  |  | 	Vector<float> scale_times; | 
					
						
							|  |  |  | 	Vector<Quat> rot_values; | 
					
						
							|  |  |  | 	Vector<float> rot_times; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 	for (size_t p = 0; p < assimp_track->mNumPositionKeys; p++) { | 
					
						
							|  |  |  | 		aiVector3D pos = assimp_track->mPositionKeys[p].mValue; | 
					
						
							|  |  |  | 		pos_values.push_back(Vector3(pos.x, pos.y, pos.z)); | 
					
						
							|  |  |  | 		pos_times.push_back(assimp_track->mPositionKeys[p].mTime / ticks_per_second); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 	for (size_t r = 0; r < assimp_track->mNumRotationKeys; r++) { | 
					
						
							|  |  |  | 		aiQuaternion quat = assimp_track->mRotationKeys[r].mValue; | 
					
						
							|  |  |  | 		rot_values.push_back(Quat(quat.x, quat.y, quat.z, quat.w).normalized()); | 
					
						
							|  |  |  | 		rot_times.push_back(assimp_track->mRotationKeys[r].mTime / ticks_per_second); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 	for (size_t sc = 0; sc < assimp_track->mNumScalingKeys; sc++) { | 
					
						
							|  |  |  | 		aiVector3D scale = assimp_track->mScalingKeys[sc].mValue; | 
					
						
							|  |  |  | 		scale_values.push_back(Vector3(scale.x, scale.y, scale.z)); | 
					
						
							|  |  |  | 		scale_times.push_back(assimp_track->mScalingKeys[sc].mTime / ticks_per_second); | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 	while (true) { | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 		Vector3 pos; | 
					
						
							|  |  |  | 		Quat rot; | 
					
						
							|  |  |  | 		Vector3 scale(1, 1, 1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (pos_values.size()) { | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 			pos = _interpolate_track<Vector3>(pos_times, pos_values, time, AssetImportAnimation::INTERP_LINEAR); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if (rot_values.size()) { | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 			rot = _interpolate_track<Quat>(rot_times, rot_values, time, | 
					
						
							|  |  |  | 					AssetImportAnimation::INTERP_LINEAR) | 
					
						
							|  |  |  | 						  .normalized(); | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if (scale_values.size()) { | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 			scale = _interpolate_track<Vector3>(scale_times, scale_values, time, AssetImportAnimation::INTERP_LINEAR); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 		if (skeleton) { | 
					
						
							|  |  |  | 			int skeleton_bone = skeleton->find_bone(node_name); | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 			if (skeleton_bone >= 0 && track_bone) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				Transform xform; | 
					
						
							|  |  |  | 				xform.basis.set_quat_scale(rot, scale); | 
					
						
							|  |  |  | 				xform.origin = pos; | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 				xform = skeleton->get_bone_pose(skeleton_bone).inverse() * xform; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				rot = xform.basis.get_rotation_quat(); | 
					
						
							|  |  |  | 				rot.normalize(); | 
					
						
							|  |  |  | 				scale = xform.basis.get_scale(); | 
					
						
							|  |  |  | 				pos = xform.origin; | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				ERR_FAIL_MSG("Skeleton bone lookup failed for skeleton: " + skeleton->get_name()); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 		animation->track_set_interpolation_type(track_idx, Animation::INTERPOLATION_LINEAR); | 
					
						
							|  |  |  | 		animation->transform_track_insert_key(track_idx, time, pos, rot, scale); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 		if (last) { //done this way so a key is always inserted past the end (for proper interpolation)
 | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		time += increment; | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 		if (time >= animation->get_length()) { | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 			last = true; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | // I really do not like this but need to figure out a better way of removing it later.
 | 
					
						
							|  |  |  | Node *EditorSceneImporterAssimp::get_node_by_name(ImportState &state, String name) { | 
					
						
							|  |  |  | 	for (Map<const aiNode *, Spatial *>::Element *key_value_pair = state.flat_node_map.front(); key_value_pair; key_value_pair = key_value_pair->next()) { | 
					
						
							|  |  |  | 		const aiNode *assimp_node = key_value_pair->key(); | 
					
						
							|  |  |  | 		Spatial *node = key_value_pair->value(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		String node_name = AssimpUtils::get_assimp_string(assimp_node->mName); | 
					
						
							|  |  |  | 		if (name == node_name && node) { | 
					
						
							|  |  |  | 			return node; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Bone stack is a fifo handler for multiple armatures since armatures aren't a thing in assimp (yet) */ | 
					
						
							|  |  |  | void EditorSceneImporterAssimp::RegenerateBoneStack(ImportState &state) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	state.bone_stack.clear(); | 
					
						
							|  |  |  | 	// build bone stack list
 | 
					
						
							|  |  |  | 	for (unsigned int mesh_id = 0; mesh_id < state.assimp_scene->mNumMeshes; ++mesh_id) { | 
					
						
							|  |  |  | 		aiMesh *mesh = state.assimp_scene->mMeshes[mesh_id]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// iterate over all the bones on the mesh for this node only!
 | 
					
						
							|  |  |  | 		for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; boneIndex++) { | 
					
						
							|  |  |  | 			aiBone *bone = mesh->mBones[boneIndex]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// doubtful this is required right now but best to check
 | 
					
						
							|  |  |  | 			if (!state.bone_stack.find(bone)) { | 
					
						
							|  |  |  | 				//print_verbose("[assimp] bone stack added: " + String(bone->mName.C_Str()) );
 | 
					
						
							|  |  |  | 				state.bone_stack.push_back(bone); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Bone stack is a fifo handler for multiple armatures since armatures aren't a thing in assimp (yet) */ | 
					
						
							|  |  |  | void EditorSceneImporterAssimp::RegenerateBoneStack(ImportState &state, aiMesh *mesh) { | 
					
						
							|  |  |  | 	state.bone_stack.clear(); | 
					
						
							|  |  |  | 	// iterate over all the bones on the mesh for this node only!
 | 
					
						
							|  |  |  | 	for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; boneIndex++) { | 
					
						
							|  |  |  | 		aiBone *bone = mesh->mBones[boneIndex]; | 
					
						
							|  |  |  | 		if (state.bone_stack.find(bone) == NULL) { | 
					
						
							|  |  |  | 			state.bone_stack.push_back(bone); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | // animation tracks are per bone
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 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]; | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 	String name = AssimpUtils::get_anim_string_from_assimp(anim->mName); | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 	if (name == String()) { | 
					
						
							|  |  |  | 		name = "Animation " + itos(p_animation_index + 1); | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	print_verbose("import animation: " + name); | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 	float ticks_per_second = anim->mTicksPerSecond; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	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); | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 		ticks_per_second = AssimpUtils::get_fbx_fps(time_mode, state.assimp_scene); | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 	//?
 | 
					
						
							|  |  |  | 	//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)) {
 | 
					
						
							|  |  |  | 	//	ticks_per_second = 1000.0f;
 | 
					
						
							|  |  |  | 	//}
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (Math::is_equal_approx(ticks_per_second, 0.0f)) { | 
					
						
							|  |  |  | 		ticks_per_second = 25.0f; | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 	Ref<Animation> animation; | 
					
						
							|  |  |  | 	animation.instance(); | 
					
						
							|  |  |  | 	animation->set_name(name); | 
					
						
							|  |  |  | 	animation->set_length(anim->mDuration / ticks_per_second); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	// generate bone stack for animation import
 | 
					
						
							|  |  |  | 	RegenerateBoneStack(state); | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	//regular tracks
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 	for (size_t i = 0; i < anim->mNumChannels; i++) { | 
					
						
							|  |  |  | 		const aiNodeAnim *track = anim->mChannels[i]; | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 		String node_name = AssimpUtils::get_assimp_string(track->mNodeName); | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 		print_verbose("track name import: " + node_name); | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 		if (track->mNumRotationKeys == 0 && track->mNumPositionKeys == 0 && track->mNumScalingKeys == 0) { | 
					
						
							|  |  |  | 			continue; //do not bother
 | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 		Skeleton *skeleton = NULL; | 
					
						
							|  |  |  | 		NodePath node_path; | 
					
						
							|  |  |  | 		aiBone *bone = NULL; | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 		// Import skeleton bone animation for this track
 | 
					
						
							|  |  |  | 		// Any bone will do, no point in processing more than just what is in the skeleton
 | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			bone = get_bone_from_stack(state, track->mNodeName); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (bone) { | 
					
						
							|  |  |  | 				// get skeleton by bone
 | 
					
						
							|  |  |  | 				skeleton = state.armature_skeletons[bone->mArmature]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				if (skeleton) { | 
					
						
							|  |  |  | 					String path = state.root->get_path_to(skeleton); | 
					
						
							|  |  |  | 					path += ":" + node_name; | 
					
						
							|  |  |  | 					node_path = path; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					if (node_path != NodePath()) { | 
					
						
							|  |  |  | 						_insert_animation_track(state, anim, i, p_bake_fps, animation, ticks_per_second, skeleton, | 
					
						
							|  |  |  | 								node_path, node_name, bone); | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						print_error("Failed to find valid node path for animation"); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 		// not a bone
 | 
					
						
							|  |  |  | 		// note this is flaky it uses node names which is unreliable
 | 
					
						
							|  |  |  | 		Node *allocated_node = get_node_by_name(state, node_name); | 
					
						
							|  |  |  | 		// todo: implement skeleton grabbing for node based animations too :)
 | 
					
						
							|  |  |  | 		// check if node exists, if it does then also apply animation track for node and bones above are all handled.
 | 
					
						
							|  |  |  | 		// this is now inclusive animation handling so that
 | 
					
						
							|  |  |  | 		// we import all the data and do not miss anything.
 | 
					
						
							|  |  |  | 		if (allocated_node) { | 
					
						
							|  |  |  | 			node_path = state.root->get_path_to(allocated_node); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (node_path != NodePath()) { | 
					
						
							|  |  |  | 				_insert_animation_track(state, anim, i, p_bake_fps, animation, ticks_per_second, skeleton, | 
					
						
							|  |  |  | 						node_path, node_name, nullptr); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 	//blend shape tracks
 | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 	for (size_t i = 0; i < anim->mNumMorphMeshChannels; i++) { | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 		const aiMeshMorphAnim *anim_mesh = anim->mMorphMeshChannels[i]; | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 		const String prop_name = AssimpUtils::get_assimp_string(anim_mesh->mName); | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 		const String mesh_name = prop_name.split("*")[0]; | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 		ERR_CONTINUE(prop_name.split("*").size() != 2); | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 		Node *item = get_node_by_name(state, mesh_name); | 
					
						
							|  |  |  | 		ERR_CONTINUE_MSG(!item, "failed to look up node by name"); | 
					
						
							|  |  |  | 		const MeshInstance *mesh_instance = Object::cast_to<MeshInstance>(item); | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 		ERR_CONTINUE(mesh_instance == NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		String base_path = state.root->get_path_to(mesh_instance); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		Ref<Mesh> mesh = mesh_instance->get_mesh(); | 
					
						
							|  |  |  | 		ERR_CONTINUE(mesh.is_null()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		//add the tracks for this mesh
 | 
					
						
							|  |  |  | 		int base_track = animation->get_track_count(); | 
					
						
							|  |  |  | 		for (int j = 0; j < mesh->get_blend_shape_count(); j++) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			animation->add_track(Animation::TYPE_VALUE); | 
					
						
							|  |  |  | 			animation->track_set_path(base_track + j, base_path + ":blend_shapes/" + mesh->get_blend_shape_name(j)); | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		for (size_t k = 0; k < anim_mesh->mNumKeys; k++) { | 
					
						
							|  |  |  | 			for (size_t j = 0; j < anim_mesh->mKeys[k].mNumValuesAndWeights; j++) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				float t = anim_mesh->mKeys[k].mTime / ticks_per_second; | 
					
						
							|  |  |  | 				float w = anim_mesh->mKeys[k].mWeights[j]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				animation->track_insert_key(base_track + j, t, w); | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 	if (animation->get_track_count()) { | 
					
						
							|  |  |  | 		state.animation_player->add_animation(name, animation); | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | //
 | 
					
						
							| 
									
										
										
											
												Fix misc. source comment typos
Found using `codespell -q 3 -S ./thirdparty,*.po -L ang,ba,cas,dof,doubleclick,fave,hist,leapyear,lod,nd,numer,ois,paket,seeked,sinc,switchs,te,uint -D ~/Projects/codespell/codespell_lib/data/dictionary.txt `
											
										 
											2019-09-19 14:36:39 -04:00
										 |  |  | // Mesh Generation from indices ? why do we need so much mesh code
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | // [debt needs looked into]
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | Ref<Mesh> | 
					
						
							|  |  |  | EditorSceneImporterAssimp::_generate_mesh_from_surface_indices(ImportState &state, const Vector<int> &p_surface_indices, | 
					
						
							|  |  |  | 		const aiNode *assimp_node, Ref<Skin> &skin, | 
					
						
							|  |  |  | 		Skeleton *&skeleton_assigned) { | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	Ref<ArrayMesh> mesh; | 
					
						
							|  |  |  | 	mesh.instance(); | 
					
						
							|  |  |  | 	bool has_uvs = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-05 22:22:03 +01:00
										 |  |  | 	Map<String, uint32_t> morph_mesh_string_lookup; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (int i = 0; i < p_surface_indices.size(); i++) { | 
					
						
							|  |  |  | 		const unsigned int mesh_idx = p_surface_indices[0]; | 
					
						
							|  |  |  | 		const aiMesh *ai_mesh = state.assimp_scene->mMeshes[mesh_idx]; | 
					
						
							|  |  |  | 		for (size_t j = 0; j < ai_mesh->mNumAnimMeshes; j++) { | 
					
						
							|  |  |  | 			String ai_anim_mesh_name = AssimpUtils::get_assimp_string(ai_mesh->mAnimMeshes[j]->mName); | 
					
						
							|  |  |  | 			if (!morph_mesh_string_lookup.has(ai_anim_mesh_name)) { | 
					
						
							|  |  |  | 				morph_mesh_string_lookup.insert(ai_anim_mesh_name, j); | 
					
						
							|  |  |  | 				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); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 	//
 | 
					
						
							|  |  |  | 	// Process Vertex Weights
 | 
					
						
							|  |  |  | 	//
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 	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]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		Map<uint32_t, Vector<BoneInfo> > vertex_weights; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 		if (ai_mesh->mNumBones > 0) { | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 			for (size_t b = 0; b < ai_mesh->mNumBones; b++) { | 
					
						
							|  |  |  | 				aiBone *bone = ai_mesh->mBones[b]; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 				if (!skeleton_assigned) { | 
					
						
							|  |  |  | 					print_verbose("Assigned mesh skeleton during mesh creation"); | 
					
						
							|  |  |  | 					skeleton_assigned = state.skeleton_bone_map[bone]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					if (!skin.is_valid()) { | 
					
						
							|  |  |  | 						print_verbose("Configured new skin"); | 
					
						
							|  |  |  | 						skin.instance(); | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						print_verbose("Reusing existing skin!"); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				//                skeleton_assigned =
 | 
					
						
							|  |  |  | 				String bone_name = AssimpUtils::get_assimp_string(bone->mName); | 
					
						
							|  |  |  | 				int bone_index = skeleton_assigned->find_bone(bone_name); | 
					
						
							|  |  |  | 				ERR_CONTINUE(bone_index == -1); | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 				for (size_t w = 0; w < bone->mNumWeights; w++) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 					aiVertexWeight ai_weights = bone->mWeights[w]; | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 					BoneInfo bi; | 
					
						
							|  |  |  | 					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>(); | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 					} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 					vertex_weights[vertex_index].push_back(bi); | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 		//
 | 
					
						
							|  |  |  | 		// Create mesh from data from assimp
 | 
					
						
							|  |  |  | 		//
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 		Ref<SurfaceTool> st; | 
					
						
							|  |  |  | 		st.instance(); | 
					
						
							|  |  |  | 		st->begin(Mesh::PRIMITIVE_TRIANGLES); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for (size_t j = 0; j < ai_mesh->mNumVertices; j++) { | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// Get the texture coordinates if they exist
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 			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)); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 			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)); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// Assign vertex colors
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 			if (ai_mesh->HasVertexColors(0)) { | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 				Color color = Color(ai_mesh->mColors[0]->r, ai_mesh->mColors[0]->g, ai_mesh->mColors[0]->b, | 
					
						
							|  |  |  | 						ai_mesh->mColors[0]->a); | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 				st->add_color(color); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// Work out normal calculations? - this needs work it doesn't work properly on huestos
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 			if (ai_mesh->mNormals != NULL) { | 
					
						
							|  |  |  | 				const aiVector3D normals = ai_mesh->mNormals[j]; | 
					
						
							|  |  |  | 				const Vector3 godot_normal = Vector3(normals.x, normals.y, normals.z); | 
					
						
							|  |  |  | 				st->add_normal(godot_normal); | 
					
						
							|  |  |  | 				if (ai_mesh->HasTangentsAndBitangents()) { | 
					
						
							|  |  |  | 					const aiVector3D tangents = ai_mesh->mTangents[j]; | 
					
						
							|  |  |  | 					const Vector3 godot_tangent = Vector3(tangents.x, tangents.y, tangents.z); | 
					
						
							|  |  |  | 					const aiVector3D bitangent = ai_mesh->mBitangents[j]; | 
					
						
							|  |  |  | 					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; | 
					
						
							|  |  |  | 					st->add_tangent(Plane(tangents.x, tangents.y, tangents.z, d)); | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 			// We have vertex weights right?
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 			if (vertex_weights.has(j)) { | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 				Vector<BoneInfo> bone_info = vertex_weights[j]; | 
					
						
							|  |  |  | 				Vector<int> bones; | 
					
						
							|  |  |  | 				bones.resize(bone_info.size()); | 
					
						
							|  |  |  | 				Vector<float> weights; | 
					
						
							|  |  |  | 				weights.resize(bone_info.size()); | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				// todo? do we really need to loop over all bones? - assimp may have helper to find all influences on this vertex.
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 				for (int k = 0; k < bone_info.size(); k++) { | 
					
						
							|  |  |  | 					bones.write[k] = bone_info[k].bone; | 
					
						
							|  |  |  | 					weights.write[k] = bone_info[k].weight; | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 				st->add_bones(bones); | 
					
						
							|  |  |  | 				st->add_weights(weights); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 			// Assign vertex
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 			const aiVector3D pos = ai_mesh->mVertices[j]; | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			// note we must include node offset transform as this is relative to world space not local space.
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 			Vector3 godot_pos = Vector3(pos.x, pos.y, pos.z); | 
					
						
							|  |  |  | 			st->add_vertex(godot_pos); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 		// fire replacement for face handling
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 		for (size_t j = 0; j < ai_mesh->mNumFaces; j++) { | 
					
						
							|  |  |  | 			const aiFace face = ai_mesh->mFaces[j]; | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 			for (unsigned int k = 0; k < face.mNumIndices; k++) { | 
					
						
							|  |  |  | 				st->add_index(face.mIndices[k]); | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 		if (ai_mesh->HasTangentsAndBitangents() == false && has_uvs) { | 
					
						
							|  |  |  | 			st->generate_tangents(); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 		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); | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 			} else { | 
					
						
							|  |  |  | 				mat->set_cull_mode(SpatialMaterial::CULL_BACK); | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		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
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 		mat->set_cull_mode(SpatialMaterial::CULL_DISABLED); | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Now process materials
 | 
					
						
							| 
									
										
										
										
											2019-09-06 21:52:00 +01:00
										 |  |  | 		aiTextureType base_color = aiTextureType_BASE_COLOR; | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			String filename, path; | 
					
						
							|  |  |  | 			AssimpImageData image_data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (AssimpUtils::GetAssimpTexture(state, ai_material, base_color, 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); | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 					mat->set_cull_mode( | 
					
						
							|  |  |  | 							SpatialMaterial::CULL_DISABLED); // since you can see both sides in transparent mode
 | 
					
						
							| 
									
										
										
										
											2019-09-06 21:52:00 +01:00
										 |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				mat->set_texture(SpatialMaterial::TEXTURE_ALBEDO, image_data.texture); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 		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); | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 					mat->set_cull_mode( | 
					
						
							|  |  |  | 							SpatialMaterial::CULL_DISABLED); // since you can see both sides in transparent mode
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				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); | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 					mat->set_cull_mode( | 
					
						
							|  |  |  | 							SpatialMaterial::CULL_DISABLED); // since you can see both sides in transparent mode
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				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); | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-06 21:52:00 +01:00
										 |  |  | 		aiTextureType tex_normal_camera = aiTextureType_NORMAL_CAMERA; | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			String filename, path; | 
					
						
							|  |  |  | 			Ref<ImageTexture> texture; | 
					
						
							|  |  |  | 			AssimpImageData image_data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Process texture normal map
 | 
					
						
							|  |  |  | 			if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_normal_camera, 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); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		aiTextureType tex_emission_color = aiTextureType_EMISSION_COLOR; | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			String filename, path; | 
					
						
							|  |  |  | 			Ref<ImageTexture> texture; | 
					
						
							|  |  |  | 			AssimpImageData image_data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Process texture normal map
 | 
					
						
							|  |  |  | 			if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_emission_color, 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); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		aiTextureType tex_metalness = aiTextureType_METALNESS; | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			String filename, path; | 
					
						
							|  |  |  | 			Ref<ImageTexture> texture; | 
					
						
							|  |  |  | 			AssimpImageData image_data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Process texture normal map
 | 
					
						
							|  |  |  | 			if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_metalness, 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); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		aiTextureType tex_roughness = aiTextureType_DIFFUSE_ROUGHNESS; | 
					
						
							|  |  |  | 		{ | 
					
						
							|  |  |  | 			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); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 		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; | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 				if (AI_SUCCESS == | 
					
						
							|  |  |  | 						ai_material->Get(AI_MATKEY_FBX_MAYA_EMISSION_TEXTURE, AI_PROPERTIES, texture_emissive_path)) { | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 					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); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-09-08 18:10:12 +01:00
										 |  |  | 		aiTextureType tex_ao_map = aiTextureType_AMBIENT_OCCLUSION; | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 		{ | 
					
						
							|  |  |  | 			String filename, path; | 
					
						
							|  |  |  | 			Ref<ImageTexture> texture; | 
					
						
							|  |  |  | 			AssimpImageData image_data; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// Process texture normal map
 | 
					
						
							| 
									
										
										
										
											2019-09-08 18:10:12 +01:00
										 |  |  | 			if (AssimpUtils::GetAssimpTexture(state, ai_material, tex_ao_map, filename, path, image_data)) { | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 				AssimpUtils::set_texture_mapping_mode(image_data.map_mode, image_data.texture); | 
					
						
							| 
									
										
										
										
											2019-09-08 18:10:12 +01:00
										 |  |  | 				mat->set_feature(SpatialMaterial::FEATURE_AMBIENT_OCCLUSION, true); | 
					
						
							|  |  |  | 				mat->set_texture(SpatialMaterial::TEXTURE_AMBIENT_OCCLUSION, image_data.texture); | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		Array array_mesh = st->commit_to_arrays(); | 
					
						
							|  |  |  | 		Array morphs; | 
					
						
							|  |  |  | 		morphs.resize(ai_mesh->mNumAnimMeshes); | 
					
						
							|  |  |  | 		Mesh::PrimitiveType primitive = Mesh::PRIMITIVE_TRIANGLES; | 
					
						
							| 
									
										
										
										
											2019-09-05 22:22:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 		for (size_t j = 0; j < ai_mesh->mNumAnimMeshes; j++) { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 			String ai_anim_mesh_name = AssimpUtils::get_assimp_string(ai_mesh->mAnimMeshes[j]->mName); | 
					
						
							| 
									
										
										
										
											2019-09-05 22:22:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 			if (ai_anim_mesh_name.empty()) { | 
					
						
							|  |  |  | 				ai_anim_mesh_name = String("morph_") + itos(j); | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2019-09-05 22:22:03 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 			Array array_copy; | 
					
						
							|  |  |  | 			array_copy.resize(VisualServer::ARRAY_MAX); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			for (int l = 0; l < VisualServer::ARRAY_MAX; l++) { | 
					
						
							|  |  |  | 				array_copy[l] = array_mesh[l].duplicate(true); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			const size_t num_vertices = ai_mesh->mAnimMeshes[j]->mNumVertices; | 
					
						
							|  |  |  | 			array_copy[Mesh::ARRAY_INDEX] = Variant(); | 
					
						
							|  |  |  | 			if (ai_mesh->mAnimMeshes[j]->HasPositions()) { | 
					
						
							|  |  |  | 				PoolVector3Array vertices; | 
					
						
							|  |  |  | 				vertices.resize(num_vertices); | 
					
						
							|  |  |  | 				for (size_t l = 0; l < num_vertices; l++) { | 
					
						
							|  |  |  | 					const aiVector3D ai_pos = ai_mesh->mAnimMeshes[j]->mVertices[l]; | 
					
						
							|  |  |  | 					Vector3 position = Vector3(ai_pos.x, ai_pos.y, ai_pos.z); | 
					
						
							|  |  |  | 					vertices.write()[l] = position; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				PoolVector3Array new_vertices = array_copy[VisualServer::ARRAY_VERTEX].duplicate(true); | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 				ERR_CONTINUE(vertices.size() != new_vertices.size()); | 
					
						
							|  |  |  | 				for (int32_t l = 0; l < new_vertices.size(); l++) { | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 					PoolVector3Array::Write w = new_vertices.write(); | 
					
						
							|  |  |  | 					w[l] = vertices[l]; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				array_copy[VisualServer::ARRAY_VERTEX] = new_vertices; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			int32_t color_set = 0; | 
					
						
							|  |  |  | 			if (ai_mesh->mAnimMeshes[j]->HasVertexColors(color_set)) { | 
					
						
							|  |  |  | 				PoolColorArray colors; | 
					
						
							|  |  |  | 				colors.resize(num_vertices); | 
					
						
							|  |  |  | 				for (size_t l = 0; l < num_vertices; l++) { | 
					
						
							|  |  |  | 					const aiColor4D ai_color = ai_mesh->mAnimMeshes[j]->mColors[color_set][l]; | 
					
						
							|  |  |  | 					Color color = Color(ai_color.r, ai_color.g, ai_color.b, ai_color.a); | 
					
						
							|  |  |  | 					colors.write()[l] = color; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				PoolColorArray new_colors = array_copy[VisualServer::ARRAY_COLOR].duplicate(true); | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 				ERR_CONTINUE(colors.size() != new_colors.size()); | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 				for (int32_t l = 0; l < colors.size(); l++) { | 
					
						
							|  |  |  | 					PoolColorArray::Write w = new_colors.write(); | 
					
						
							|  |  |  | 					w[l] = colors[l]; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				array_copy[VisualServer::ARRAY_COLOR] = new_colors; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (ai_mesh->mAnimMeshes[j]->HasNormals()) { | 
					
						
							|  |  |  | 				PoolVector3Array normals; | 
					
						
							|  |  |  | 				normals.resize(num_vertices); | 
					
						
							|  |  |  | 				for (size_t l = 0; l < num_vertices; l++) { | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 					const aiVector3D ai_normal = ai_mesh->mAnimMeshes[j]->mNormals[l]; | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 					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); | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 				ERR_CONTINUE(normals.size() != new_normals.size()); | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 				for (int l = 0; l < normals.size(); l++) { | 
					
						
							|  |  |  | 					PoolVector3Array::Write w = new_normals.write(); | 
					
						
							|  |  |  | 					w[l] = normals[l]; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				array_copy[VisualServer::ARRAY_NORMAL] = new_normals; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (ai_mesh->mAnimMeshes[j]->HasTangentsAndBitangents()) { | 
					
						
							|  |  |  | 				PoolColorArray tangents; | 
					
						
							|  |  |  | 				tangents.resize(num_vertices); | 
					
						
							|  |  |  | 				PoolColorArray::Write w = tangents.write(); | 
					
						
							|  |  |  | 				for (size_t l = 0; l < num_vertices; l++) { | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 					AssimpUtils::calc_tangent_from_mesh(ai_mesh, j, l, l, w); | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 				} | 
					
						
							|  |  |  | 				PoolRealArray new_tangents = array_copy[VisualServer::ARRAY_TANGENT].duplicate(true); | 
					
						
							|  |  |  | 				ERR_CONTINUE(new_tangents.size() != tangents.size() * 4); | 
					
						
							|  |  |  | 				for (int32_t l = 0; l < tangents.size(); l++) { | 
					
						
							|  |  |  | 					new_tangents.write()[l + 0] = tangents[l].r; | 
					
						
							|  |  |  | 					new_tangents.write()[l + 1] = tangents[l].g; | 
					
						
							|  |  |  | 					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); | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 		mesh->surface_set_material(i, mat); | 
					
						
							|  |  |  | 		mesh->surface_set_name(i, AssimpUtils::get_assimp_string(ai_mesh->mName)); | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return mesh; | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * Create a new mesh for the node supplied | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | MeshInstance * | 
					
						
							|  |  |  | EditorSceneImporterAssimp::create_mesh(ImportState &state, const aiNode *assimp_node, const String &node_name, Node *active_node, Transform node_transform) { | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 	/* MESH NODE */ | 
					
						
							|  |  |  | 	Ref<Mesh> mesh; | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	Ref<Skin> skin; | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 	// see if we have mesh cache for this.
 | 
					
						
							|  |  |  | 	Vector<int> surface_indices; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	RegenerateBoneStack(state); | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-22 08:35:03 +01:00
										 |  |  | 	// Configure indices
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	for (uint32_t i = 0; i < assimp_node->mNumMeshes; i++) { | 
					
						
							|  |  |  | 		int mesh_index = assimp_node->mMeshes[i]; | 
					
						
							|  |  |  | 		// create list of mesh indexes
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 		surface_indices.push_back(mesh_index); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	//surface_indices.sort();
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 	String mesh_key; | 
					
						
							|  |  |  | 	for (int i = 0; i < surface_indices.size(); i++) { | 
					
						
							|  |  |  | 		if (i > 0) { | 
					
						
							|  |  |  | 			mesh_key += ":"; | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 		mesh_key += itos(surface_indices[i]); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	Skeleton *skeleton = NULL; | 
					
						
							|  |  |  | 	aiNode *armature = NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 	if (!state.mesh_cache.has(mesh_key)) { | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 		mesh = _generate_mesh_from_surface_indices(state, surface_indices, assimp_node, skin, skeleton); | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 		state.mesh_cache[mesh_key] = mesh; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 	MeshInstance *mesh_node = memnew(MeshInstance); | 
					
						
							|  |  |  | 	mesh = state.mesh_cache[mesh_key]; | 
					
						
							|  |  |  | 	mesh_node->set_mesh(mesh); | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	// if we have a valid skeleton set it up
 | 
					
						
							|  |  |  | 	if (skin.is_valid()) { | 
					
						
							|  |  |  | 		for (uint32_t i = 0; i < assimp_node->mNumMeshes; i++) { | 
					
						
							|  |  |  | 			unsigned int mesh_index = assimp_node->mMeshes[i]; | 
					
						
							|  |  |  | 			const aiMesh *ai_mesh = state.assimp_scene->mMeshes[mesh_index]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			// please remember bone id relative to the skin is NOT the mesh relative index.
 | 
					
						
							|  |  |  | 			// it is the index relative to the skeleton that is why
 | 
					
						
							|  |  |  | 			// we have state.bone_id_map, it allows for duplicate bone id's too :)
 | 
					
						
							|  |  |  | 			// hope this makes sense
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			int bind_count = 0; | 
					
						
							|  |  |  | 			for (unsigned int boneId = 0; boneId < ai_mesh->mNumBones; ++boneId) { | 
					
						
							|  |  |  | 				aiBone *iterBone = ai_mesh->mBones[boneId]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				// used to reparent mesh to the correct armature later on if assigned.
 | 
					
						
							|  |  |  | 				if (!armature) { | 
					
						
							|  |  |  | 					print_verbose("Configured mesh armature, will reparent later to armature"); | 
					
						
							|  |  |  | 					armature = iterBone->mArmature; | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 				if (skeleton) { | 
					
						
							|  |  |  | 					int id = skeleton->find_bone(AssimpUtils::get_assimp_string(iterBone->mName)); | 
					
						
							|  |  |  | 					if (id != -1) { | 
					
						
							|  |  |  | 						print_verbose("Set bind bone: mesh: " + itos(mesh_index) + " bone index: " + itos(id)); | 
					
						
							|  |  |  | 						Transform t = AssimpUtils::assimp_matrix_transform(iterBone->mOffsetMatrix); | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 						skin->add_bind(bind_count, t); | 
					
						
							|  |  |  | 						skin->set_bind_bone(bind_count, id); | 
					
						
							|  |  |  | 						bind_count++; | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 		print_verbose("Finished configuring bind pose for skin mesh"); | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	// this code parents all meshes with bones to the armature they are for
 | 
					
						
							|  |  |  | 	// GLTF2 specification relies on this and we are enforcing it for FBX.
 | 
					
						
							|  |  |  | 	if (armature && state.flat_node_map[armature]) { | 
					
						
							|  |  |  | 		Node *armature_parent = state.flat_node_map[armature]; | 
					
						
							|  |  |  | 		print_verbose("Parented mesh " + node_name + " to armature " + armature_parent->get_name()); | 
					
						
							|  |  |  | 		// static mesh handling
 | 
					
						
							|  |  |  | 		armature_parent->add_child(mesh_node); | 
					
						
							|  |  |  | 		// transform must be identity
 | 
					
						
							|  |  |  | 		mesh_node->set_global_transform(Transform()); | 
					
						
							|  |  |  | 		mesh_node->set_name(node_name); | 
					
						
							|  |  |  | 		mesh_node->set_owner(state.root); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		// static mesh handling
 | 
					
						
							|  |  |  | 		active_node->add_child(mesh_node); | 
					
						
							|  |  |  | 		mesh_node->set_global_transform(node_transform); | 
					
						
							|  |  |  | 		mesh_node->set_name(node_name); | 
					
						
							|  |  |  | 		mesh_node->set_owner(state.root); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	if (skeleton) { | 
					
						
							|  |  |  | 		print_verbose("Attempted to set skeleton path!"); | 
					
						
							|  |  |  | 		mesh_node->set_skeleton_path(mesh_node->get_path_to(skeleton)); | 
					
						
							|  |  |  | 		mesh_node->set_skin(skin); | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	return mesh_node; | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2019-04-13 15:19:26 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * Create a light for the scene | 
					
						
							|  |  |  |  * Automatically caches lights for lookup later | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | Spatial *EditorSceneImporterAssimp::create_light( | 
					
						
							|  |  |  | 		ImportState &state, | 
					
						
							|  |  |  | 		const String &node_name, | 
					
						
							|  |  |  | 		Transform &look_at_transform) { | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 	Light *light = NULL; | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	aiLight *assimp_light = state.assimp_scene->mLights[state.light_cache[node_name]]; | 
					
						
							|  |  |  | 	ERR_FAIL_COND_V(!assimp_light, NULL); | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	if (assimp_light->mType == aiLightSource_DIRECTIONAL) { | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 		light = memnew(DirectionalLight); | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	} else if (assimp_light->mType == aiLightSource_POINT) { | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 		light = memnew(OmniLight); | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	} else if (assimp_light->mType == aiLightSource_SPOT) { | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 		light = memnew(SpotLight); | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	ERR_FAIL_COND_V(light == NULL, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (assimp_light->mType != aiLightSource_POINT) { | 
					
						
							|  |  |  | 		Vector3 pos = Vector3( | 
					
						
							|  |  |  | 				assimp_light->mPosition.x, | 
					
						
							|  |  |  | 				assimp_light->mPosition.y, | 
					
						
							|  |  |  | 				assimp_light->mPosition.z); | 
					
						
							|  |  |  | 		Vector3 look_at = Vector3( | 
					
						
							|  |  |  | 				assimp_light->mDirection.y, | 
					
						
							|  |  |  | 				assimp_light->mDirection.x, | 
					
						
							|  |  |  | 				assimp_light->mDirection.z) | 
					
						
							|  |  |  | 								  .normalized(); | 
					
						
							|  |  |  | 		Vector3 up = Vector3( | 
					
						
							|  |  |  | 				assimp_light->mUp.x, | 
					
						
							|  |  |  | 				assimp_light->mUp.y, | 
					
						
							|  |  |  | 				assimp_light->mUp.z); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		look_at_transform.set_look_at(pos, look_at, up); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// properties for light variables should be put here.
 | 
					
						
							|  |  |  | 	// not really hugely important yet but we will need them in the future
 | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	light->set_color( | 
					
						
							|  |  |  | 			Color(assimp_light->mColorDiffuse.r, assimp_light->mColorDiffuse.g, assimp_light->mColorDiffuse.b)); | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	return light; | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * Create camera for the scene | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | Spatial *EditorSceneImporterAssimp::create_camera( | 
					
						
							|  |  |  | 		ImportState &state, | 
					
						
							|  |  |  | 		const String &node_name, | 
					
						
							|  |  |  | 		Transform &look_at_transform) { | 
					
						
							|  |  |  | 	aiCamera *camera = state.assimp_scene->mCameras[state.camera_cache[node_name]]; | 
					
						
							|  |  |  | 	ERR_FAIL_COND_V(!camera, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Camera *camera_node = memnew(Camera); | 
					
						
							|  |  |  | 	ERR_FAIL_COND_V(!camera_node, NULL); | 
					
						
							|  |  |  | 	float near = camera->mClipPlaneNear; | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 	if (Math::is_equal_approx(near, 0.0f)) { | 
					
						
							|  |  |  | 		near = 0.1f; | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	camera_node->set_perspective(Math::rad2deg(camera->mHorizontalFOV) * 2.0f, near, camera->mClipPlaneFar); | 
					
						
							|  |  |  | 	Vector3 pos = Vector3(camera->mPosition.x, camera->mPosition.y, camera->mPosition.z); | 
					
						
							|  |  |  | 	Vector3 look_at = Vector3(camera->mLookAt.y, camera->mLookAt.x, camera->mLookAt.z).normalized(); | 
					
						
							|  |  |  | 	Vector3 up = Vector3(camera->mUp.x, camera->mUp.y, camera->mUp.z); | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	look_at_transform.set_look_at(pos + look_at_transform.origin, look_at, up); | 
					
						
							|  |  |  | 	return camera_node; | 
					
						
							| 
									
										
										
										
											2019-04-03 10:24:00 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * Generate node | 
					
						
							|  |  |  |  * Recursive call to iterate over all nodes | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | void EditorSceneImporterAssimp::_generate_node( | 
					
						
							|  |  |  | 		ImportState &state, | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 		const aiNode *assimp_node) { | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	ERR_FAIL_COND(assimp_node == NULL); | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	state.nodes.push_back(assimp_node); | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 	String node_name = AssimpUtils::get_assimp_string(assimp_node->mName); | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	String parent_name = AssimpUtils::get_assimp_string(assimp_node->mParent->mName); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// please note
 | 
					
						
							|  |  |  | 	// duplicate bone names exist
 | 
					
						
							|  |  |  | 	// this is why we only check if the bone exists
 | 
					
						
							|  |  |  | 	// so everything else is useless but the name
 | 
					
						
							|  |  |  | 	// please do not copy any other values from get_bone_by_name.
 | 
					
						
							|  |  |  | 	aiBone *parent_bone = get_bone_by_name(state.assimp_scene, assimp_node->mParent->mName); | 
					
						
							|  |  |  | 	aiBone *current_bone = get_bone_by_name(state.assimp_scene, assimp_node->mName); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// is this an armature
 | 
					
						
							|  |  |  | 	// parent null
 | 
					
						
							|  |  |  | 	// and this is the first bone :)
 | 
					
						
							|  |  |  | 	if (parent_bone == NULL && current_bone) { | 
					
						
							|  |  |  | 		state.armature_nodes.push_back(assimp_node->mParent); | 
					
						
							|  |  |  | 		print_verbose("found valid armature: " + parent_name); | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-11-05 17:54:34 +00:00
										 |  |  | 	for (size_t i = 0; i < assimp_node->mNumChildren; i++) { | 
					
						
							|  |  |  | 		_generate_node(state, assimp_node->mChildren[i]); | 
					
						
							| 
									
										
										
										
											2019-08-30 02:21:40 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2019-10-09 16:11:04 +02:00
										 |  |  | } |