Browse Source

Merge pull request #53448 from lyuma/backport_gltf_skin_export

Rémi Verschelde 3 years ago
parent
commit
0a1cd0c316
3 changed files with 375 additions and 373 deletions
  1. 335 342
      modules/gltf/gltf_document.cpp
  2. 32 31
      modules/gltf/gltf_document.h
  3. 8 0
      modules/gltf/gltf_state.h

+ 335 - 342
modules/gltf/gltf_document.cpp

@@ -82,7 +82,10 @@
 Error GLTFDocument::serialize(Ref<GLTFState> state, Node *p_root, const String &p_path) {
 	uint64_t begin_time = OS::get_singleton()->get_ticks_usec();
 
-	_convert_scene_node(state, p_root, p_root, -1, -1);
+	state->skeleton3d_to_gltf_skeleton.clear();
+	state->skin_and_skeleton3d_to_gltf_skin.clear();
+
+	_convert_scene_node(state, p_root, -1, -1);
 	if (!state->buffers.size()) {
 		state->buffers.push_back(Vector<uint8_t>());
 	}
@@ -101,11 +104,7 @@ Error GLTFDocument::serialize(Ref<GLTFState> state, Node *p_root, const String &
 	if (err != OK) {
 		return Error::FAILED;
 	}
-	/* STEP 4 CREATE BONE ATTACHMENTS */
-	err = _serialize_bone_attachment(state);
-	if (err != OK) {
-		return Error::FAILED;
-	}
+
 	/* STEP 5 SERIALIZE MESHES (we have enough info now) */
 	err = _serialize_meshes(state);
 	if (err != OK) {
@@ -255,30 +254,6 @@ Error GLTFDocument::_parse_json(const String &p_path, Ref<GLTFState> state) {
 	return OK;
 }
 
-Error GLTFDocument::_serialize_bone_attachment(Ref<GLTFState> state) {
-	for (int skeleton_i = 0; skeleton_i < state->skeletons.size(); skeleton_i++) {
-		for (int attachment_i = 0; attachment_i < state->skeletons[skeleton_i]->bone_attachments.size(); attachment_i++) {
-			BoneAttachment *bone_attachment = state->skeletons[skeleton_i]->bone_attachments[attachment_i];
-			String bone_name = bone_attachment->get_bone_name();
-			bone_name = _sanitize_bone_name(state, bone_name);
-			int32_t bone = state->skeletons[skeleton_i]->godot_skeleton->find_bone(bone_name);
-			ERR_CONTINUE(bone == -1);
-			for (int skin_i = 0; skin_i < state->skins.size(); skin_i++) {
-				if (state->skins[skin_i]->skeleton != skeleton_i) {
-					continue;
-				}
-
-				for (int node_i = 0; node_i < bone_attachment->get_child_count(); node_i++) {
-					ERR_CONTINUE(bone >= state->skins[skin_i]->joints.size());
-					_convert_scene_node(state, bone_attachment->get_child(node_i), bone_attachment->get_owner(), state->skins[skin_i]->joints[bone], 0);
-				}
-				break;
-			}
-		}
-	}
-	return OK;
-}
-
 Error GLTFDocument::_parse_glb(const String &p_path, Ref<GLTFState> state) {
 	Error err;
 	FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err);
@@ -2313,11 +2288,14 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
 			continue;
 		}
 		Array primitives;
-		Array targets;
 		Dictionary gltf_mesh;
 		Array target_names;
 		Array weights;
+		for (int morph_i = 0; morph_i < import_mesh->get_blend_shape_count(); morph_i++) {
+			target_names.push_back(import_mesh->get_blend_shape_name(morph_i));
+		}
 		for (int surface_i = 0; surface_i < import_mesh->get_surface_count(); surface_i++) {
+			Array targets;
 			Dictionary primitive;
 			Mesh::PrimitiveType primitive_type = import_mesh->surface_get_primitive_type(surface_i);
 			switch (primitive_type) {
@@ -2443,10 +2421,10 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
 				const Array &a = array[Mesh::ARRAY_WEIGHTS];
 				const Vector<Vector3> &vertex_array = array[Mesh::ARRAY_VERTEX];
 				if ((a.size() / JOINT_GROUP_SIZE) == vertex_array.size()) {
-					const int ret_size = a.size() / JOINT_GROUP_SIZE;
+					int32_t vertex_count = vertex_array.size();
 					Vector<Color> attribs;
-					attribs.resize(ret_size);
-					for (int i = 0; i < ret_size; i++) {
+					attribs.resize(vertex_count);
+					for (int i = 0; i < vertex_count; i++) {
 						attribs.write[i] = Color(a[(i * JOINT_GROUP_SIZE) + 0], a[(i * JOINT_GROUP_SIZE) + 1], a[(i * JOINT_GROUP_SIZE) + 2], a[(i * JOINT_GROUP_SIZE) + 3]);
 					}
 					attributes["WEIGHTS_0"] = _encode_accessor_as_weights(state, attribs, true);
@@ -2518,7 +2496,6 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
 				Array array_morphs = import_mesh->surface_get_blend_shape_arrays(surface_i);
 				for (int morph_i = 0; morph_i < array_morphs.size(); morph_i++) {
 					Array array_morph = array_morphs[morph_i];
-					target_names.push_back(import_mesh->get_blend_shape_name(morph_i));
 					Dictionary t;
 					Vector<Vector3> varr = array_morph[Mesh::ARRAY_VERTEX];
 					Array mesh_arrays = import_mesh->surface_get_arrays(surface_i);
@@ -2535,22 +2512,21 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
 					}
 
 					Vector<Vector3> narr = array_morph[Mesh::ARRAY_NORMAL];
-					if (varr.size()) {
+					if (narr.size()) {
 						t["NORMAL"] = _encode_accessor_as_vec3(state, narr, true);
 					}
 					Vector<real_t> tarr = array_morph[Mesh::ARRAY_TANGENT];
 					if (tarr.size()) {
 						const int ret_size = tarr.size() / 4;
-						Vector<Color> attribs;
+						Vector<Vector3> attribs;
 						attribs.resize(ret_size);
 						for (int i = 0; i < ret_size; i++) {
-							Color tangent;
-							tangent.r = tarr[(i * 4) + 0];
-							tangent.g = tarr[(i * 4) + 1];
-							tangent.b = tarr[(i * 4) + 2];
-							tangent.a = tarr[(i * 4) + 3];
+							Vector3 vec3;
+							vec3.x = tarr[(i * 4) + 0];
+							vec3.y = tarr[(i * 4) + 1];
+							vec3.z = tarr[(i * 4) + 2];
 						}
-						t["TANGENT"] = _encode_accessor_as_color(state, attribs, true);
+						t["TANGENT"] = _encode_accessor_as_vec3(state, attribs, true);
 					}
 					targets.push_back(t);
 				}
@@ -2579,12 +2555,13 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
 		Dictionary e;
 		e["targetNames"] = target_names;
 
-		for (int j = 0; j < target_names.size(); j++) {
+		weights.resize(target_names.size());
+		for (int name_i = 0; name_i < target_names.size(); name_i++) {
 			real_t weight = 0.0;
-			if (j < state->meshes.write[gltf_mesh_i]->get_blend_weights().size()) {
-				weight = state->meshes.write[gltf_mesh_i]->get_blend_weights()[j];
+			if (name_i < state->meshes.write[gltf_mesh_i]->get_blend_weights().size()) {
+				weight = state->meshes.write[gltf_mesh_i]->get_blend_weights()[name_i];
 			}
-			weights.push_back(weight);
+			weights[name_i] = weight;
 		}
 		if (weights.size()) {
 			gltf_mesh["weights"] = weights;
@@ -4294,6 +4271,7 @@ Error GLTFDocument::_create_skeletons(Ref<GLTFState> state) {
 
 		Skeleton *skeleton = memnew(Skeleton);
 		gltf_skeleton->godot_skeleton = skeleton;
+		state->skeleton3d_to_gltf_skeleton[skeleton->get_instance_id()] = skel_i;
 
 		// Make a unique name, no gltf node represents this skeleton
 		skeleton->set_name(_gen_unique_name(state, "Skeleton"));
@@ -4379,6 +4357,16 @@ Error GLTFDocument::_map_skin_joints_indices_to_skeleton_bone_indices(Ref<GLTFSt
 
 Error GLTFDocument::_serialize_skins(Ref<GLTFState> state) {
 	_remove_duplicate_skins(state);
+	Array json_skins;
+	for (int skin_i = 0; skin_i < state->skins.size(); skin_i++) {
+		Ref<GLTFSkin> gltf_skin = state->skins[skin_i];
+		Dictionary json_skin;
+		json_skin["inverseBindMatrices"] = _encode_accessor_as_xform(state, gltf_skin->inverse_binds, false);
+		json_skin["joints"] = gltf_skin->get_joints();
+		json_skin["name"] = gltf_skin->get_name();
+		json_skins.push_back(json_skin);
+	}
+	state->json["skins"] = json_skins;
 	return OK;
 }
 
@@ -4757,30 +4745,74 @@ Error GLTFDocument::_serialize_animations(Ref<GLTFState> state) {
 				channels.push_back(t);
 			}
 			if (track.weight_tracks.size()) {
+				double length = 0.0f;
+
+				for (int32_t track_idx = 0; track_idx < track.weight_tracks.size(); track_idx++) {
+					int32_t last_time_index = track.weight_tracks[track_idx].times.size() - 1;
+					length = MAX(length, track.weight_tracks[track_idx].times[last_time_index]);
+				}
+
 				Dictionary t;
 				t["sampler"] = samplers.size();
 				Dictionary s;
-
 				Vector<real_t> times;
-				Vector<real_t> values;
+				const double increment = 1.0 / BAKE_FPS;
+				{
+					double time = 0.0;
+					bool last = false;
+					while (true) {
+						times.push_back(time);
+						if (last) {
+							break;
+						}
+						time += increment;
+						if (time >= length) {
+							last = true;
+							time = length;
+						}
+					}
+				}
 
-				for (int32_t times_i = 0; times_i < track.weight_tracks[0].times.size(); times_i++) {
-					real_t time = track.weight_tracks[0].times[times_i];
-					times.push_back(time);
+				for (int32_t track_idx = 0; track_idx < track.weight_tracks.size(); track_idx++) {
+					double time = 0.0;
+					bool last = false;
+					Vector<real_t> weight_track;
+					while (true) {
+						float weight = _interpolate_track<float>(track.weight_tracks[track_idx].times,
+								track.weight_tracks[track_idx].values,
+								time,
+								track.weight_tracks[track_idx].interpolation);
+						weight_track.push_back(weight);
+						if (last) {
+							break;
+						}
+						time += increment;
+						if (time >= length) {
+							last = true;
+							time = length;
+						}
+					}
+					track.weight_tracks.write[track_idx].times = times;
+					track.weight_tracks.write[track_idx].values = weight_track;
 				}
 
-				values.resize(times.size() * track.weight_tracks.size());
-				// TODO Sort by order in blend shapes
+				Vector<real_t> all_track_times = times;
+				Vector<real_t> all_track_values;
+				int32_t values_size = track.weight_tracks[0].values.size();
+				int32_t weight_tracks_size = track.weight_tracks.size();
+				all_track_values.resize(weight_tracks_size * values_size);
 				for (int k = 0; k < track.weight_tracks.size(); k++) {
 					Vector<float> wdata = track.weight_tracks[k].values;
 					for (int l = 0; l < wdata.size(); l++) {
-						values.write[l * track.weight_tracks.size() + k] = wdata.write[l];
+						int32_t index = l * weight_tracks_size + k;
+						ERR_BREAK(index >= all_track_values.size());
+						all_track_values.write[index] = wdata.write[l];
 					}
 				}
 
 				s["interpolation"] = interpolation_to_string(track.weight_tracks[track.weight_tracks.size() - 1].interpolation);
-				s["input"] = _encode_accessor_as_floats(state, times, false);
-				s["output"] = _encode_accessor_as_floats(state, values, false);
+				s["input"] = _encode_accessor_as_floats(state, all_track_times, false);
+				s["output"] = _encode_accessor_as_floats(state, all_track_values, false);
 
 				samplers.push_back(s);
 
@@ -4918,7 +4950,7 @@ Error GLTFDocument::_parse_animations(Ref<GLTFState> state) {
 				track->weight_tracks.resize(wc);
 
 				const int expected_value_count = times.size() * output_count * wc;
-				ERR_FAIL_COND_V_MSG(weights.size() != expected_value_count, ERR_PARSE_ERROR, "Invalid weight data, expected " + itos(expected_value_count) + " weight values, got " + itos(weights.size()) + " instead.");
+				ERR_CONTINUE_MSG(weights.size() != expected_value_count, "Invalid weight data, expected " + itos(expected_value_count) + " weight values, got " + itos(weights.size()) + " instead.");
 
 				const int wlen = weights.size() / wc;
 				for (int k = 0; k < wc; k++) { //separate tracks, having them together is not such a good idea
@@ -4987,7 +5019,7 @@ BoneAttachment *GLTFDocument::_generate_bone_attachment(Ref<GLTFState> state, Sk
 	return bone_attachment;
 }
 
-GLTFMeshIndex GLTFDocument::_convert_mesh_instance(Ref<GLTFState> state, MeshInstance *p_mesh_instance) {
+GLTFMeshIndex GLTFDocument::_convert_mesh_to_gltf(Ref<GLTFState> state, MeshInstance *p_mesh_instance) {
 	ERR_FAIL_NULL_V(p_mesh_instance, -1);
 	if (p_mesh_instance->get_mesh().is_null()) {
 		return -1;
@@ -4998,35 +5030,32 @@ GLTFMeshIndex GLTFDocument::_convert_mesh_instance(Ref<GLTFState> state, MeshIns
 	if (godot_mesh.is_null()) {
 		return -1;
 	}
-	Vector<float> blend_weights;
-	Vector<String> blend_names;
 	int32_t blend_count = godot_mesh->get_blend_shape_count();
-	blend_names.resize(blend_count);
+	Vector<float> blend_weights;
 	blend_weights.resize(blend_count);
-	for (int32_t blend_i = 0; blend_i < godot_mesh->get_blend_shape_count(); blend_i++) {
-		String blend_name = godot_mesh->get_blend_shape_name(blend_i);
-		blend_names.write[blend_i] = blend_name;
-		import_mesh->add_blend_shape(blend_name);
-	}
-	for (int32_t surface_i = 0; surface_i < godot_mesh->get_surface_count(); surface_i++) {
-		Mesh::PrimitiveType primitive_type = godot_mesh->surface_get_primitive_type(surface_i);
-		Array arrays = godot_mesh->surface_get_arrays(surface_i);
-		Array blend_shape_arrays = godot_mesh->surface_get_blend_shape_arrays(surface_i);
-		Ref<Material> mat = godot_mesh->surface_get_material(surface_i);
-		Ref<ArrayMesh> godot_array_mesh = godot_mesh;
-		String surface_name;
-		if (godot_array_mesh.is_valid()) {
-			surface_name = godot_array_mesh->surface_get_name(surface_i);
-		}
-		if (p_mesh_instance->get_surface_material(surface_i).is_valid()) {
-			mat = p_mesh_instance->get_surface_material(surface_i);
-		}
-		if (p_mesh_instance->get_material_override().is_valid()) {
-			mat = p_mesh_instance->get_material_override();
-		}
-		int32_t mat_idx = import_mesh->get_surface_count();
-		import_mesh->add_surface_from_arrays(primitive_type, arrays, blend_shape_arrays);
-		import_mesh->surface_set_material(mat_idx, mat);
+	Ref<ArrayMesh> am = godot_mesh;
+	if (am != nullptr) {
+		import_mesh = am;
+	} else {
+		for (int32_t surface_i = 0; surface_i < godot_mesh->get_surface_count(); surface_i++) {
+			Mesh::PrimitiveType primitive_type = godot_mesh->surface_get_primitive_type(surface_i);
+			Array arrays = godot_mesh->surface_get_arrays(surface_i);
+			Ref<Material> mat = godot_mesh->surface_get_material(surface_i);
+			Ref<ArrayMesh> godot_array_mesh = godot_mesh;
+			String surface_name;
+			if (godot_array_mesh.is_valid()) {
+				surface_name = godot_array_mesh->surface_get_name(surface_i);
+			}
+			if (p_mesh_instance->get_surface_material(surface_i).is_valid()) {
+				mat = p_mesh_instance->get_surface_material(surface_i);
+			}
+			if (p_mesh_instance->get_material_override().is_valid()) {
+				mat = p_mesh_instance->get_material_override();
+			}
+			int32_t mat_idx = import_mesh->get_surface_count();
+			import_mesh->add_surface_from_arrays(primitive_type, arrays);
+			import_mesh->surface_set_material(mat_idx, mat);
+		}
 	}
 	for (int32_t blend_i = 0; blend_i < blend_count; blend_i++) {
 		blend_weights.write[blend_i] = 0.0f;
@@ -5190,17 +5219,6 @@ GLTFLightIndex GLTFDocument::_convert_light(Ref<GLTFState> state, Light *p_light
 	return light_index;
 }
 
-GLTFSkeletonIndex GLTFDocument::_convert_skeleton(Ref<GLTFState> state, Skeleton *p_skeleton) {
-	print_verbose("glTF: Converting skeleton: " + p_skeleton->get_name());
-	Ref<GLTFSkeleton> gltf_skeleton;
-	gltf_skeleton.instance();
-	gltf_skeleton->set_name(_gen_unique_name(state, p_skeleton->get_name()));
-	gltf_skeleton->godot_skeleton = p_skeleton;
-	GLTFSkeletonIndex skeleton_i = state->skeletons.size();
-	state->skeletons.push_back(gltf_skeleton);
-	return skeleton_i;
-}
-
 void GLTFDocument::_convert_spatial(Ref<GLTFState> state, Spatial *p_spatial, Ref<GLTFNode> p_node) {
 	Transform xform = p_spatial->get_transform();
 	p_node->scale = xform.basis.get_scale();
@@ -5216,7 +5234,7 @@ Spatial *GLTFDocument::_generate_spatial(Ref<GLTFState> state, Node *scene_paren
 
 	return spatial;
 }
-void GLTFDocument::_convert_scene_node(Ref<GLTFState> state, Node *p_current, Node *p_root, const GLTFNodeIndex p_gltf_parent, const GLTFNodeIndex p_gltf_root) {
+void GLTFDocument::_convert_scene_node(Ref<GLTFState> state, Node *p_current, const GLTFNodeIndex p_gltf_parent, const GLTFNodeIndex p_gltf_root) {
 	bool retflag = true;
 	_check_visibility(p_current, retflag);
 	if (retflag) {
@@ -5230,37 +5248,41 @@ void GLTFDocument::_convert_scene_node(Ref<GLTFState> state, Node *p_current, No
 		_convert_spatial(state, spatial, gltf_node);
 	}
 	if (cast_to<MeshInstance>(p_current)) {
-		Spatial *spatial = cast_to<Spatial>(p_current);
-		_convert_mesh_to_gltf(p_current, state, spatial, gltf_node);
+		MeshInstance *mi = cast_to<MeshInstance>(p_current);
+		_convert_mesh_instance_to_gltf(mi, state, gltf_node);
 	} else if (cast_to<BoneAttachment>(p_current)) {
-		_convert_bone_attachment_to_gltf(p_current, state, gltf_node, retflag);
-		// TODO 2020-12-21 iFire Handle the case of objects under the bone attachment.
+		BoneAttachment *bone = cast_to<BoneAttachment>(p_current);
+		_convert_bone_attachment_to_gltf(bone, state, p_gltf_parent, p_gltf_root, gltf_node);
 		return;
 	} else if (cast_to<Skeleton>(p_current)) {
-		_convert_skeleton_to_gltf(p_current, state, p_gltf_parent, p_gltf_root, gltf_node, p_root);
+		Skeleton *skel = cast_to<Skeleton>(p_current);
+		_convert_skeleton_to_gltf(skel, state, p_gltf_parent, p_gltf_root, gltf_node);
 		// We ignore the Godot Engine node that is the skeleton.
 		return;
 	} else if (cast_to<MultiMeshInstance>(p_current)) {
-		_convert_mult_mesh_instance_to_gltf(p_current, p_gltf_parent, p_gltf_root, gltf_node, state, p_root);
+		MultiMeshInstance *multi = cast_to<MultiMeshInstance>(p_current);
+		_convert_mult_mesh_instance_to_gltf(multi, p_gltf_parent, p_gltf_root, gltf_node, state);
 #ifdef MODULE_CSG_ENABLED
 	} else if (cast_to<CSGShape>(p_current)) {
-		if (p_current->get_parent() && cast_to<CSGShape>(p_current)->is_root_shape()) {
-			_convert_csg_shape_to_gltf(p_current, p_gltf_parent, gltf_node, state);
+		CSGShape *shape = cast_to<CSGShape>(p_current);
+		if (shape->get_parent() && shape->is_root_shape()) {
+			_convert_csg_shape_to_gltf(shape, p_gltf_parent, gltf_node, state);
 		}
 #endif // MODULE_CSG_ENABLED
 #ifdef MODULE_GRIDMAP_ENABLED
 	} else if (cast_to<GridMap>(p_current)) {
-		_convert_grid_map_to_gltf(p_current, p_gltf_parent, p_gltf_root, gltf_node, state, p_root);
+		GridMap *gridmap = Object::cast_to<GridMap>(p_current);
+		_convert_grid_map_to_gltf(gridmap, p_gltf_parent, p_gltf_root, gltf_node, state);
 #endif // MODULE_GRIDMAP_ENABLED
 	} else if (cast_to<Camera>(p_current)) {
 		Camera *camera = Object::cast_to<Camera>(p_current);
-		_convert_camera_to_gltf(camera, state, camera, gltf_node);
+		_convert_camera_to_gltf(camera, state, gltf_node);
 	} else if (cast_to<Light>(p_current)) {
 		Light *light = Object::cast_to<Light>(p_current);
-		_convert_light_to_gltf(light, state, light, gltf_node);
+		_convert_light_to_gltf(light, state, gltf_node);
 	} else if (cast_to<AnimationPlayer>(p_current)) {
 		AnimationPlayer *animation_player = Object::cast_to<AnimationPlayer>(p_current);
-		_convert_animation_player_to_gltf(animation_player, state, p_gltf_parent, p_gltf_root, gltf_node, p_current, p_root);
+		_convert_animation_player_to_gltf(animation_player, state, p_gltf_parent, p_gltf_root, gltf_node, p_current);
 	}
 	GLTFNodeIndex current_node_i = state->nodes.size();
 	GLTFNodeIndex gltf_root = p_gltf_root;
@@ -5272,13 +5294,13 @@ void GLTFDocument::_convert_scene_node(Ref<GLTFState> state, Node *p_current, No
 	}
 	_create_gltf_node(state, p_current, current_node_i, p_gltf_parent, gltf_root, gltf_node);
 	for (int node_i = 0; node_i < p_current->get_child_count(); node_i++) {
-		_convert_scene_node(state, p_current->get_child(node_i), p_root, current_node_i, gltf_root);
+		_convert_scene_node(state, p_current->get_child(node_i), current_node_i, gltf_root);
 	}
 }
 
 #ifdef MODULE_CSG_ENABLED
-void GLTFDocument::_convert_csg_shape_to_gltf(Node *p_current, GLTFNodeIndex p_gltf_parent, Ref<GLTFNode> gltf_node, Ref<GLTFState> state) {
-	CSGShape *csg = Object::cast_to<CSGShape>(p_current);
+void GLTFDocument::_convert_csg_shape_to_gltf(CSGShape *p_current, GLTFNodeIndex p_gltf_parent, Ref<GLTFNode> gltf_node, Ref<GLTFState> state) {
+	CSGShape *csg = p_current;
 	csg->call("_update_shape");
 	Array meshes = csg->get_meshes();
 	if (meshes.size() != 2) {
@@ -5309,16 +5331,15 @@ void GLTFDocument::_create_gltf_node(Ref<GLTFState> state, Node *p_scene_parent,
 		GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_gltf_node, Ref<GLTFNode> gltf_node) {
 	state->scene_nodes.insert(current_node_i, p_scene_parent);
 	state->nodes.push_back(gltf_node);
-	if (current_node_i == p_parent_node_index) {
-		return;
-	}
+	ERR_FAIL_COND(current_node_i == p_parent_node_index);
+	state->nodes.write[current_node_i]->parent = p_parent_node_index;
 	if (p_parent_node_index == -1) {
 		return;
 	}
 	state->nodes.write[p_parent_node_index]->children.push_back(current_node_i);
 }
 
-void GLTFDocument::_convert_animation_player_to_gltf(AnimationPlayer *animation_player, Ref<GLTFState> state, const GLTFNodeIndex &p_gltf_current, const GLTFNodeIndex &p_gltf_root_index, Ref<GLTFNode> p_gltf_node, Node *p_scene_parent, Node *p_root) {
+void GLTFDocument::_convert_animation_player_to_gltf(AnimationPlayer *animation_player, Ref<GLTFState> state, GLTFNodeIndex p_gltf_current, GLTFNodeIndex p_gltf_root_index, Ref<GLTFNode> p_gltf_node, Node *p_scene_parent) {
 	ERR_FAIL_COND(!animation_player);
 	state->animation_players.push_back(animation_player);
 	print_verbose(String("glTF: Converting animation player: ") + animation_player->get_name());
@@ -5337,7 +5358,7 @@ void GLTFDocument::_check_visibility(Node *p_node, bool &retflag) {
 	retflag = false;
 }
 
-void GLTFDocument::_convert_camera_to_gltf(Camera *camera, Ref<GLTFState> state, Spatial *spatial, Ref<GLTFNode> gltf_node) {
+void GLTFDocument::_convert_camera_to_gltf(Camera *camera, Ref<GLTFState> state, Ref<GLTFNode> gltf_node) {
 	ERR_FAIL_COND(!camera);
 	GLTFCameraIndex camera_index = _convert_camera(state, camera);
 	if (camera_index != -1) {
@@ -5345,7 +5366,7 @@ void GLTFDocument::_convert_camera_to_gltf(Camera *camera, Ref<GLTFState> state,
 	}
 }
 
-void GLTFDocument::_convert_light_to_gltf(Light *light, Ref<GLTFState> state, Spatial *spatial, Ref<GLTFNode> gltf_node) {
+void GLTFDocument::_convert_light_to_gltf(Light *light, Ref<GLTFState> state, Ref<GLTFNode> gltf_node) {
 	ERR_FAIL_COND(!light);
 	GLTFLightIndex light_index = _convert_light(state, light);
 	if (light_index != -1) {
@@ -5354,43 +5375,39 @@ void GLTFDocument::_convert_light_to_gltf(Light *light, Ref<GLTFState> state, Sp
 }
 
 #ifdef MODULE_GRIDMAP_ENABLED
-void GLTFDocument::_convert_grid_map_to_gltf(Node *p_scene_parent, const GLTFNodeIndex &p_parent_node_index, const GLTFNodeIndex &p_root_node_index, Ref<GLTFNode> gltf_node, Ref<GLTFState> state, Node *p_root_node) {
-	GridMap *grid_map = Object::cast_to<GridMap>(p_scene_parent);
-	ERR_FAIL_COND(!grid_map);
-	Array cells = grid_map->get_used_cells();
+void GLTFDocument::_convert_grid_map_to_gltf(GridMap *p_grid_map, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref<GLTFNode> gltf_node, Ref<GLTFState> state) {
+	Array cells = p_grid_map->get_used_cells();
 	for (int32_t k = 0; k < cells.size(); k++) {
 		GLTFNode *new_gltf_node = memnew(GLTFNode);
 		gltf_node->children.push_back(state->nodes.size());
 		state->nodes.push_back(new_gltf_node);
 		Vector3 cell_location = cells[k];
-		int32_t cell = grid_map->get_cell_item(
+		int32_t cell = p_grid_map->get_cell_item(
 				Vector3(cell_location.x, cell_location.y, cell_location.z));
 		MeshInstance *import_mesh_node = memnew(MeshInstance);
-		import_mesh_node->set_mesh(grid_map->get_mesh_library()->get_item_mesh(cell));
+		import_mesh_node->set_mesh(p_grid_map->get_mesh_library()->get_item_mesh(cell));
 		Transform cell_xform;
 		cell_xform.basis.set_orthogonal_index(
-				grid_map->get_cell_item_orientation(
+				p_grid_map->get_cell_item_orientation(
 						Vector3(cell_location.x, cell_location.y, cell_location.z)));
-		cell_xform.basis.scale(Vector3(grid_map->get_cell_scale(),
-				grid_map->get_cell_scale(),
-				grid_map->get_cell_scale()));
-		cell_xform.set_origin(grid_map->map_to_world(
+		cell_xform.basis.scale(Vector3(p_grid_map->get_cell_scale(),
+				p_grid_map->get_cell_scale(),
+				p_grid_map->get_cell_scale()));
+		cell_xform.set_origin(p_grid_map->map_to_world(
 				Vector3(cell_location.x, cell_location.y, cell_location.z)));
 		Ref<GLTFMesh> gltf_mesh;
 		gltf_mesh.instance();
 		gltf_mesh = import_mesh_node;
 		new_gltf_node->mesh = state->meshes.size();
 		state->meshes.push_back(gltf_mesh);
-		new_gltf_node->xform = cell_xform * grid_map->get_transform();
-		new_gltf_node->set_name(_gen_unique_name(state, grid_map->get_mesh_library()->get_item_name(cell)));
+		new_gltf_node->xform = cell_xform * p_grid_map->get_transform();
+		new_gltf_node->set_name(_gen_unique_name(state, p_grid_map->get_mesh_library()->get_item_name(cell)));
 	}
 }
 #endif // MODULE_GRIDMAP_ENABLED
 
-void GLTFDocument::_convert_mult_mesh_instance_to_gltf(Node *p_scene_parent, const GLTFNodeIndex &p_parent_node_index, const GLTFNodeIndex &p_root_node_index, Ref<GLTFNode> gltf_node, Ref<GLTFState> state, Node *p_root_node) {
-	MultiMeshInstance *multi_mesh_instance = Object::cast_to<MultiMeshInstance>(p_scene_parent);
-	ERR_FAIL_COND(!multi_mesh_instance);
-	Ref<MultiMesh> multi_mesh = multi_mesh_instance->get_multimesh();
+void GLTFDocument::_convert_mult_mesh_instance_to_gltf(MultiMeshInstance *p_multi_mesh_instance, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref<GLTFNode> gltf_node, Ref<GLTFState> state) {
+	Ref<MultiMesh> multi_mesh = p_multi_mesh_instance->get_multimesh();
 	if (multi_mesh.is_valid()) {
 		for (int32_t instance_i = 0; instance_i < multi_mesh->get_instance_count();
 				instance_i++) {
@@ -5406,9 +5423,9 @@ void GLTFDocument::_convert_mult_mesh_instance_to_gltf(Node *p_scene_parent, con
 				transform.basis.set_quat_scale(quat,
 						Vector3(scale.x, 0, scale.y));
 				transform =
-						multi_mesh_instance->get_transform() * transform;
+						p_multi_mesh_instance->get_transform() * transform;
 			} else if (multi_mesh->get_transform_format() == MultiMesh::TRANSFORM_3D) {
-				transform = multi_mesh_instance->get_transform() *
+				transform = p_multi_mesh_instance->get_transform() *
 							multi_mesh->get_instance_transform(instance_i);
 			}
 			Ref<ArrayMesh> mm = multi_mesh->get_mesh();
@@ -5427,56 +5444,95 @@ void GLTFDocument::_convert_mult_mesh_instance_to_gltf(Node *p_scene_parent, con
 				state->meshes.push_back(gltf_mesh);
 			}
 			new_gltf_node->xform = transform;
-			new_gltf_node->set_name(_gen_unique_name(state, multi_mesh_instance->get_name()));
+			new_gltf_node->set_name(_gen_unique_name(state, p_multi_mesh_instance->get_name()));
 			gltf_node->children.push_back(state->nodes.size());
 			state->nodes.push_back(new_gltf_node);
 		}
 	}
 }
 
-void GLTFDocument::_convert_skeleton_to_gltf(Node *p_scene_parent, Ref<GLTFState> state, const GLTFNodeIndex &p_parent_node_index, const GLTFNodeIndex &p_root_node_index, Ref<GLTFNode> gltf_node, Node *p_root_node) {
-	Skeleton *skeleton = Object::cast_to<Skeleton>(p_scene_parent);
-	if (skeleton) {
-		// Remove placeholder skeleton3d node by not creating the gltf node
-		// Skins are per mesh
-		for (int node_i = 0; node_i < skeleton->get_child_count(); node_i++) {
-			_convert_scene_node(state, skeleton->get_child(node_i), p_root_node, p_parent_node_index, p_root_node_index);
+void GLTFDocument::_convert_skeleton_to_gltf(Skeleton *p_skeleton3d, Ref<GLTFState> state, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref<GLTFNode> gltf_node) {
+	Skeleton *skeleton = p_skeleton3d;
+	Ref<GLTFSkeleton> gltf_skeleton;
+	gltf_skeleton.instance();
+	// GLTFSkeleton is only used to hold internal state data. It will not be written to the document.
+	//
+	gltf_skeleton->godot_skeleton = skeleton;
+	GLTFSkeletonIndex skeleton_i = state->skeletons.size();
+	state->skeleton3d_to_gltf_skeleton[skeleton->get_instance_id()] = skeleton_i;
+	state->skeletons.push_back(gltf_skeleton);
+
+	BoneId bone_count = skeleton->get_bone_count();
+	for (BoneId bone_i = 0; bone_i < bone_count; bone_i++) {
+		Ref<GLTFNode> joint_node;
+		joint_node.instance();
+		// Note that we cannot use _gen_unique_bone_name here, because glTF spec requires all node
+		// names to be unique regardless of whether or not they are used as joints.
+		joint_node->set_name(_gen_unique_name(state, skeleton->get_bone_name(bone_i)));
+		Transform xform = skeleton->get_bone_rest(bone_i) * skeleton->get_bone_pose(bone_i);
+		joint_node->scale = xform.basis.get_scale();
+		joint_node->rotation = xform.basis.get_rotation_quat();
+		joint_node->translation = xform.origin;
+		joint_node->joint = true;
+		GLTFNodeIndex current_node_i = state->nodes.size();
+		state->scene_nodes.insert(current_node_i, skeleton);
+		state->nodes.push_back(joint_node);
+
+		gltf_skeleton->joints.push_back(current_node_i);
+		if (skeleton->get_bone_parent(bone_i) == -1) {
+			gltf_skeleton->roots.push_back(current_node_i);
+		}
+		gltf_skeleton->godot_bone_node.insert(bone_i, current_node_i);
+	}
+	for (BoneId bone_i = 0; bone_i < bone_count; bone_i++) {
+		GLTFNodeIndex current_node_i = gltf_skeleton->godot_bone_node[bone_i];
+		BoneId parent_bone_id = skeleton->get_bone_parent(bone_i);
+		if (parent_bone_id == -1) {
+			if (p_parent_node_index != -1) {
+				state->nodes.write[current_node_i]->parent = p_parent_node_index;
+				state->nodes.write[p_parent_node_index]->children.push_back(current_node_i);
+			}
+		} else {
+			GLTFNodeIndex parent_node_i = gltf_skeleton->godot_bone_node[parent_bone_id];
+			state->nodes.write[current_node_i]->parent = parent_node_i;
+			state->nodes.write[parent_node_i]->children.push_back(current_node_i);
 		}
 	}
+	// Remove placeholder skeleton3d node by not creating the gltf node
+	// Skins are per mesh
+	for (int node_i = 0; node_i < skeleton->get_child_count(); node_i++) {
+		_convert_scene_node(state, skeleton->get_child(node_i), p_parent_node_index, p_root_node_index);
+	}
 }
 
-void GLTFDocument::_convert_bone_attachment_to_gltf(Node *p_scene_parent, Ref<GLTFState> state, Ref<GLTFNode> gltf_node, bool &retflag) {
-	retflag = true;
-	BoneAttachment *bone_attachment = Object::cast_to<BoneAttachment>(p_scene_parent);
-	if (bone_attachment) {
-		Node *node = bone_attachment->get_parent();
-		while (node) {
-			Skeleton *bone_attachment_skeleton = Object::cast_to<Skeleton>(node);
-			if (bone_attachment_skeleton) {
-				for (GLTFSkeletonIndex skeleton_i = 0; skeleton_i < state->skeletons.size(); skeleton_i++) {
-					if (state->skeletons[skeleton_i]->godot_skeleton != bone_attachment_skeleton) {
-						continue;
-					}
-					state->skeletons.write[skeleton_i]->bone_attachments.push_back(bone_attachment);
-					break;
-				}
-				break;
-			}
-			node = node->get_parent();
-		}
-		gltf_node.unref();
-		return;
+void GLTFDocument::_convert_bone_attachment_to_gltf(BoneAttachment *p_bone_attachment, Ref<GLTFState> state, GLTFNodeIndex p_parent_node_index, GLTFNodeIndex p_root_node_index, Ref<GLTFNode> gltf_node) {
+	Skeleton *skeleton;
+	// Note that relative transforms to external skeletons and pose overrides are not supported.
+	skeleton = cast_to<Skeleton>(p_bone_attachment->get_parent());
+	GLTFSkeletonIndex skel_gltf_i = -1;
+	if (skeleton != nullptr && state->skeleton3d_to_gltf_skeleton.has(skeleton->get_instance_id())) {
+		skel_gltf_i = state->skeleton3d_to_gltf_skeleton[skeleton->get_instance_id()];
+	}
+	int bone_idx = -1;
+	if (skeleton != nullptr) {
+		bone_idx = skeleton->find_bone(p_bone_attachment->get_bone_name());
+	}
+	GLTFNodeIndex par_node_index = p_parent_node_index;
+	if (skeleton != nullptr && bone_idx != -1 && skel_gltf_i != -1) {
+		Ref<GLTFSkeleton> gltf_skeleton = state->skeletons.write[skel_gltf_i];
+		gltf_skeleton->bone_attachments.push_back(p_bone_attachment);
+		par_node_index = gltf_skeleton->joints[bone_idx];
+	}
+
+	for (int node_i = 0; node_i < p_bone_attachment->get_child_count(); node_i++) {
+		_convert_scene_node(state, p_bone_attachment->get_child(node_i), par_node_index, p_root_node_index);
 	}
-	retflag = false;
 }
 
-void GLTFDocument::_convert_mesh_to_gltf(Node *p_scene_parent, Ref<GLTFState> state, Spatial *spatial, Ref<GLTFNode> gltf_node) {
-	MeshInstance *mi = Object::cast_to<MeshInstance>(p_scene_parent);
-	if (mi) {
-		GLTFMeshIndex gltf_mesh_index = _convert_mesh_instance(state, mi);
-		if (gltf_mesh_index != -1) {
-			gltf_node->mesh = gltf_mesh_index;
-		}
+void GLTFDocument::_convert_mesh_instance_to_gltf(MeshInstance *p_scene_parent, Ref<GLTFState> state, Ref<GLTFNode> gltf_node) {
+	GLTFMeshIndex gltf_mesh_index = _convert_mesh_to_gltf(state, p_scene_parent);
+	if (gltf_mesh_index != -1) {
+		gltf_node->mesh = gltf_mesh_index;
 	}
 }
 
@@ -5933,10 +5989,6 @@ void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> state) {
 		if (node->mesh < 0) {
 			continue;
 		}
-		Array json_skins;
-		if (state->json.has("skins")) {
-			json_skins = state->json["skins"];
-		}
 		Map<GLTFNodeIndex, Node *>::Element *mi_element = state->scene_nodes.find(mi_node_i);
 		if (!mi_element) {
 			continue;
@@ -5948,7 +6000,6 @@ void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> state) {
 		node->rotation = mi_xform.basis.get_rotation_quat();
 		node->translation = mi_xform.origin;
 
-		Dictionary json_skin;
 		Skeleton *skeleton = Object::cast_to<Skeleton>(mi->get_node(mi->get_skeleton_path()));
 		if (!skeleton) {
 			continue;
@@ -5957,121 +6008,75 @@ void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> state) {
 			continue;
 		}
 		Ref<Skin> skin = mi->get_skin();
-		if (skin.is_null()) {
-			skin = skeleton->register_skin(nullptr)->get_skin();
-		}
 		Ref<GLTFSkin> gltf_skin;
 		gltf_skin.instance();
 		Array json_joints;
-		GLTFSkeletonIndex skeleton_gltf_i = -1;
 
 		NodePath skeleton_path = mi->get_skeleton_path();
-		bool is_unique = true;
-		for (int32_t skin_i = 0; skin_i < state->skins.size(); skin_i++) {
-			Ref<GLTFSkin> prev_gltf_skin = state->skins.write[skin_i];
-			if (gltf_skin.is_null()) {
-				continue;
-			}
-			GLTFSkeletonIndex prev_skeleton = prev_gltf_skin->get_skeleton();
-			if (prev_skeleton == -1 || prev_skeleton >= state->skeletons.size()) {
-				continue;
-			}
-			if (prev_gltf_skin->get_godot_skin() == skin && state->skeletons[prev_skeleton]->godot_skeleton == skeleton) {
-				node->skin = skin_i;
-				node->skeleton = prev_skeleton;
-				is_unique = false;
-				break;
-			}
-		}
-		if (!is_unique) {
-			continue;
-		}
-		GLTFSkeletonIndex skeleton_i = _convert_skeleton(state, skeleton);
-		skeleton_gltf_i = skeleton_i;
-		ERR_CONTINUE(skeleton_gltf_i == -1);
-		gltf_skin->skeleton = skeleton_gltf_i;
-		Ref<GLTFSkeleton> gltf_skeleton = state->skeletons.write[skeleton_gltf_i];
-		for (int32_t bind_i = 0; bind_i < skin->get_bind_count(); bind_i++) {
-			String godot_bone_name = skin->get_bind_name(bind_i);
-			if (godot_bone_name.empty()) {
-				int32_t bone = skin->get_bind_bone(bind_i);
-				godot_bone_name = skeleton->get_bone_name(bone);
-			}
-			if (skeleton->find_bone(godot_bone_name) == -1) {
-				godot_bone_name = skeleton->get_bone_name(0);
-			}
-			BoneId bone_index = skeleton->find_bone(godot_bone_name);
-			ERR_CONTINUE(bone_index == -1);
-			Ref<GLTFNode> joint_node;
-			joint_node.instance();
-			String gltf_bone_name = _gen_unique_bone_name(state, skeleton_gltf_i, godot_bone_name);
-			joint_node->set_name(gltf_bone_name);
-
-			Transform bone_rest_xform = skeleton->get_bone_rest(bone_index);
-			joint_node->scale = bone_rest_xform.basis.get_scale();
-			joint_node->rotation = bone_rest_xform.basis.get_rotation_quat();
-			joint_node->translation = bone_rest_xform.origin;
-			joint_node->joint = true;
-
-			int32_t joint_node_i = state->nodes.size();
-			state->nodes.push_back(joint_node);
-			gltf_skeleton->godot_bone_node.insert(bone_index, joint_node_i);
-			int32_t joint_index = gltf_skin->joints.size();
-			gltf_skin->joint_i_to_bone_i.insert(joint_index, bone_index);
-			gltf_skin->joints.push_back(joint_node_i);
-			gltf_skin->joints_original.push_back(joint_node_i);
-			gltf_skin->inverse_binds.push_back(skin->get_bind_pose(bind_i));
-			json_joints.push_back(joint_node_i);
-			for (Map<GLTFNodeIndex, Node *>::Element *skin_scene_node_i = state->scene_nodes.front(); skin_scene_node_i; skin_scene_node_i = skin_scene_node_i->next()) {
-				if (skin_scene_node_i->get() == skeleton) {
-					gltf_skin->skin_root = skin_scene_node_i->key();
-					json_skin["skeleton"] = skin_scene_node_i->key();
-				}
-			}
-			gltf_skin->godot_skin = skin;
-			gltf_skin->set_name(_gen_unique_name(state, skin->get_name()));
-		}
-		for (int32_t bind_i = 0; bind_i < skin->get_bind_count(); bind_i++) {
-			String bone_name = skeleton->get_bone_name(bind_i);
-			String godot_bone_name = skin->get_bind_name(bind_i);
-			int32_t bone = -1;
-			if (skin->get_bind_bone(bind_i) != -1) {
-				bone = skin->get_bind_bone(bind_i);
-				godot_bone_name = skeleton->get_bone_name(bone);
-			}
-			bone = skeleton->find_bone(godot_bone_name);
-			if (bone == -1) {
-				continue;
-			}
-			BoneId bone_parent = skeleton->get_bone_parent(bone);
-			GLTFNodeIndex joint_node_i = gltf_skeleton->godot_bone_node[bone];
-			ERR_CONTINUE(joint_node_i >= state->nodes.size());
-			if (bone_parent != -1) {
-				GLTFNodeIndex parent_joint_gltf_node = gltf_skin->joints[bone_parent];
-				Ref<GLTFNode> parent_joint_node = state->nodes.write[parent_joint_gltf_node];
-				parent_joint_node->children.push_back(joint_node_i);
+		Node *skel_node = mi->get_node_or_null(skeleton_path);
+		Skeleton *godot_skeleton = nullptr;
+		if (skel_node != nullptr) {
+			godot_skeleton = cast_to<Skeleton>(skel_node);
+		}
+		if (godot_skeleton != nullptr && state->skeleton3d_to_gltf_skeleton.has(godot_skeleton->get_instance_id())) {
+			// This is a skinned mesh. If the mesh has no ARRAY_WEIGHTS or ARRAY_BONES, it will be invisible.
+			const GLTFSkeletonIndex skeleton_gltf_i = state->skeleton3d_to_gltf_skeleton[godot_skeleton->get_instance_id()];
+			Ref<GLTFSkeleton> gltf_skeleton = state->skeletons[skeleton_gltf_i];
+			int bone_cnt = skeleton->get_bone_count();
+			ERR_FAIL_COND(bone_cnt != gltf_skeleton->joints.size());
+
+			ObjectID gltf_skin_key = skin->get_instance_id();
+			ObjectID gltf_skel_key = godot_skeleton->get_instance_id();
+			GLTFSkinIndex skin_gltf_i = -1;
+			GLTFNodeIndex root_gltf_i = -1;
+			if (!gltf_skeleton->roots.empty()) {
+				root_gltf_i = gltf_skeleton->roots[0];
+			}
+			if (state->skin_and_skeleton3d_to_gltf_skin.has(gltf_skin_key) && state->skin_and_skeleton3d_to_gltf_skin[gltf_skin_key].has(gltf_skel_key)) {
+				skin_gltf_i = state->skin_and_skeleton3d_to_gltf_skin[gltf_skin_key][gltf_skel_key];
 			} else {
-				Node *node_parent = skeleton->get_parent();
-				ERR_CONTINUE(!node_parent);
-				for (Map<GLTFNodeIndex, Node *>::Element *E = state->scene_nodes.front(); E; E = E->next()) {
-					if (E->get() == node_parent) {
-						GLTFNodeIndex gltf_node_i = E->key();
-						Ref<GLTFNode> gltf_node = state->nodes.write[gltf_node_i];
-						gltf_node->children.push_back(joint_node_i);
-						break;
+				if (skin.is_null()) {
+					// Note that gltf_skin_key should remain null, so these can share a reference.
+					skin = skeleton->register_skin(nullptr)->get_skin();
+				}
+				gltf_skin.instance();
+				gltf_skin->godot_skin = skin;
+				gltf_skin->set_name(skin->get_name());
+				gltf_skin->skeleton = skeleton_gltf_i;
+				gltf_skin->skin_root = root_gltf_i;
+				//gltf_state->godot_to_gltf_node[skel_node]
+				HashMap<StringName, int> bone_name_to_idx;
+				for (int bone_i = 0; bone_i < bone_cnt; bone_i++) {
+					bone_name_to_idx[skeleton->get_bone_name(bone_i)] = bone_i;
+				}
+				for (int bind_i = 0, cnt = skin->get_bind_count(); bind_i < cnt; bind_i++) {
+					int bone_i = skin->get_bind_bone(bind_i);
+					Transform bind_pose = skin->get_bind_pose(bind_i);
+					StringName bind_name = skin->get_bind_name(bind_i);
+					if (bind_name != StringName()) {
+						bone_i = bone_name_to_idx[bind_name];
+					}
+					ERR_CONTINUE(bone_i < 0 || bone_i >= bone_cnt);
+					if (bind_name == StringName()) {
+						bind_name = skeleton->get_bone_name(bone_i);
+					}
+					GLTFNodeIndex skeleton_bone_i = gltf_skeleton->joints[bone_i];
+					gltf_skin->joints_original.push_back(skeleton_bone_i);
+					gltf_skin->joints.push_back(skeleton_bone_i);
+					gltf_skin->inverse_binds.push_back(bind_pose);
+					if (skeleton->get_bone_parent(bone_i) == -1) {
+						gltf_skin->roots.push_back(skeleton_bone_i);
 					}
+					gltf_skin->joint_i_to_bone_i[bind_i] = bone_i;
+					gltf_skin->joint_i_to_name[bind_i] = bind_name;
 				}
+				skin_gltf_i = state->skins.size();
+				state->skins.push_back(gltf_skin);
+				state->skin_and_skeleton3d_to_gltf_skin[gltf_skin_key][gltf_skel_key] = skin_gltf_i;
 			}
+			node->skin = skin_gltf_i;
+			node->skeleton = skeleton_gltf_i;
 		}
-		_expand_skin(state, gltf_skin);
-		node->skin = state->skins.size();
-		state->skins.push_back(gltf_skin);
-
-		json_skin["inverseBindMatrices"] = _encode_accessor_as_xform(state, gltf_skin->inverse_binds, false);
-		json_skin["joints"] = json_joints;
-		json_skin["name"] = gltf_skin->get_name();
-		json_skins.push_back(json_skin);
-		state->json["skins"] = json_skins;
 	}
 }
 
@@ -6154,7 +6159,6 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> state
 	for (int32_t key_i = 0; key_i < key_count; key_i++) {
 		times.write[key_i] = p_animation->track_get_key_time(p_track_i, key_i);
 	}
-	const float BAKE_FPS = 30.0f;
 	if (track_type == Animation::TYPE_TRANSFORM) {
 		p_track.translation_track.times = times;
 		p_track.translation_track.interpolation = gltf_interpolation;
@@ -6396,69 +6400,58 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
 			const Vector<String> node_suffix = String(orig_track_path).split(":blend_shapes/");
 			const NodePath path = node_suffix[0];
 			const String suffix = node_suffix[1];
-			const Node *node = ap->get_parent()->get_node_or_null(path);
-			for (Map<GLTFNodeIndex, Node *>::Element *transform_track_i = state->scene_nodes.front(); transform_track_i; transform_track_i = transform_track_i->next()) {
-				if (transform_track_i->get() == node) {
-					const MeshInstance *mi = Object::cast_to<MeshInstance>(node);
-					if (!mi) {
-						continue;
-					}
-					Ref<ArrayMesh> array_mesh = mi->get_mesh();
-					if (array_mesh.is_null()) {
+			Node *node = ap->get_parent()->get_node_or_null(path);
+			MeshInstance *mi = cast_to<MeshInstance>(node);
+			Ref<Mesh> mesh = mi->get_mesh();
+			ERR_CONTINUE(mesh.is_null());
+			int32_t mesh_index = -1;
+			for (Map<GLTFNodeIndex, Node *>::Element *mesh_track_i = state->scene_nodes.front(); mesh_track_i; mesh_track_i = mesh_track_i->next()) {
+				if (mesh_track_i->get() == node) {
+					mesh_index = mesh_track_i->key();
+				}
+			}
+			ERR_CONTINUE(mesh_index == -1);
+			Map<int, GLTFAnimation::Track> &tracks = gltf_animation->get_tracks();
+			GLTFAnimation::Track track = gltf_animation->get_tracks().has(mesh_index) ? gltf_animation->get_tracks()[mesh_index] : GLTFAnimation::Track();
+			if (!tracks.has(mesh_index)) {
+				for (int32_t shape_i = 0; shape_i < mesh->get_blend_shape_count(); shape_i++) {
+					String shape_name = mesh->get_blend_shape_name(shape_i);
+					NodePath shape_path = String(path) + ":blend_shapes/" + shape_name;
+					int32_t shape_track_i = animation->find_track(shape_path);
+					if (shape_track_i == -1) {
+						GLTFAnimation::Channel<float> weight;
+						weight.interpolation = GLTFAnimation::INTERP_LINEAR;
+						weight.times.push_back(0.0f);
+						weight.times.push_back(0.0f);
+						weight.values.push_back(0.0f);
+						weight.values.push_back(0.0f);
+						track.weight_tracks.push_back(weight);
 						continue;
 					}
-					if (node_suffix.size() != 2) {
-						continue;
+					Animation::InterpolationType interpolation = animation->track_get_interpolation_type(track_i);
+					GLTFAnimation::Interpolation gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
+					if (interpolation == Animation::InterpolationType::INTERPOLATION_LINEAR) {
+						gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
+					} else if (interpolation == Animation::InterpolationType::INTERPOLATION_NEAREST) {
+						gltf_interpolation = GLTFAnimation::INTERP_STEP;
+					} else if (interpolation == Animation::InterpolationType::INTERPOLATION_CUBIC) {
+						gltf_interpolation = GLTFAnimation::INTERP_CUBIC_SPLINE;
 					}
-					GLTFNodeIndex mesh_index = -1;
-					for (GLTFNodeIndex node_i = 0; node_i < state->scene_nodes.size(); node_i++) {
-						if (state->scene_nodes[node_i] == node) {
-							mesh_index = node_i;
-							break;
-						}
+					int32_t key_count = animation->track_get_key_count(shape_track_i);
+					GLTFAnimation::Channel<float> weight;
+					weight.interpolation = gltf_interpolation;
+					weight.times.resize(key_count);
+					for (int32_t time_i = 0; time_i < key_count; time_i++) {
+						weight.times.write[time_i] = animation->track_get_key_time(shape_track_i, time_i);
 					}
-					ERR_CONTINUE(mesh_index == -1);
-					Ref<Mesh> mesh = mi->get_mesh();
-					ERR_CONTINUE(mesh.is_null());
-					for (int32_t shape_i = 0; shape_i < mesh->get_blend_shape_count(); shape_i++) {
-						if (mesh->get_blend_shape_name(shape_i) != suffix) {
-							continue;
-						}
-						GLTFAnimation::Track track;
-						Map<int, GLTFAnimation::Track>::Element *blend_shape_track_i = gltf_animation->get_tracks().find(mesh_index);
-						if (blend_shape_track_i) {
-							track = blend_shape_track_i->get();
-						}
-						Animation::InterpolationType interpolation = animation->track_get_interpolation_type(track_i);
-
-						GLTFAnimation::Interpolation gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
-						if (interpolation == Animation::InterpolationType::INTERPOLATION_LINEAR) {
-							gltf_interpolation = GLTFAnimation::INTERP_LINEAR;
-						} else if (interpolation == Animation::InterpolationType::INTERPOLATION_NEAREST) {
-							gltf_interpolation = GLTFAnimation::INTERP_STEP;
-						} else if (interpolation == Animation::InterpolationType::INTERPOLATION_CUBIC) {
-							gltf_interpolation = GLTFAnimation::INTERP_CUBIC_SPLINE;
-						}
-						Animation::TrackType track_type = animation->track_get_type(track_i);
-						if (track_type == Animation::TYPE_VALUE) {
-							int32_t key_count = animation->track_get_key_count(track_i);
-							GLTFAnimation::Channel<float> weight;
-							weight.interpolation = gltf_interpolation;
-							weight.times.resize(key_count);
-							for (int32_t time_i = 0; time_i < key_count; time_i++) {
-								weight.times.write[time_i] = animation->track_get_key_time(track_i, time_i);
-							}
-							weight.values.resize(key_count);
-							for (int32_t value_i = 0; value_i < key_count; value_i++) {
-								weight.values.write[value_i] = animation->track_get_key_value(track_i, value_i);
-							}
-							track.weight_tracks.push_back(weight);
-						}
-						gltf_animation->get_tracks()[mesh_index] = track;
+					weight.values.resize(key_count);
+					for (int32_t value_i = 0; value_i < key_count; value_i++) {
+						weight.values.write[value_i] = animation->track_get_key_value(shape_track_i, value_i);
 					}
+					track.weight_tracks.push_back(weight);
 				}
+				tracks[mesh_index] = track;
 			}
-
 		} else if (String(orig_track_path).find(":") != -1) {
 			//Process skeleton
 			const Vector<String> node_suffix = String(orig_track_path).split(":");

+ 32 - 31
modules/gltf/gltf_document.h

@@ -48,6 +48,9 @@ class GLTFSkin;
 class GLTFNode;
 class GLTFSpecGloss;
 class GLTFSkeleton;
+class CSGShape;
+class GridMap;
+class MultiMeshInstance;
 
 using GLTFAccessorIndex = int;
 using GLTFAnimationIndex = int;
@@ -69,6 +72,9 @@ class GLTFDocument : public Resource {
 	friend class GLTFSkin;
 	friend class GLTFSkeleton;
 
+private:
+	const float BAKE_FPS = 30.0f;
+
 public:
 	const int32_t JOINT_GROUP_SIZE = 4;
 	enum GLTFType {
@@ -336,7 +342,6 @@ private:
 			GLTFNodeIndex p_node_i);
 	Error _encode_buffer_bins(Ref<GLTFState> state, const String &p_path);
 	Error _encode_buffer_glb(Ref<GLTFState> state, const String &p_path);
-	Error _serialize_bone_attachment(Ref<GLTFState> state);
 	Dictionary _serialize_texture_transform_uv1(Ref<SpatialMaterial> p_material);
 	Dictionary _serialize_texture_transform_uv2(Ref<SpatialMaterial> p_material);
 	Error _serialize_version(Ref<GLTFState> state);
@@ -370,20 +375,17 @@ public:
 	void _generate_skeleton_bone_node(Ref<GLTFState> state, Node *scene_parent, Spatial *scene_root, const GLTFNodeIndex node_index);
 	void _import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
 			const GLTFAnimationIndex index, const int bake_fps);
-	GLTFMeshIndex _convert_mesh_instance(Ref<GLTFState> state,
-			MeshInstance *p_mesh_instance);
 	void _convert_mesh_instances(Ref<GLTFState> state);
 	GLTFCameraIndex _convert_camera(Ref<GLTFState> state, Camera *p_camera);
-	void _convert_light_to_gltf(Light *light, Ref<GLTFState> state, Spatial *spatial, Ref<GLTFNode> gltf_node);
+	void _convert_light_to_gltf(Light *light, Ref<GLTFState> state, Ref<GLTFNode> gltf_node);
 	GLTFLightIndex _convert_light(Ref<GLTFState> state, Light *p_light);
-	GLTFSkeletonIndex _convert_skeleton(Ref<GLTFState> state, Skeleton *p_skeleton);
 	void _convert_spatial(Ref<GLTFState> state, Spatial *p_spatial, Ref<GLTFNode> p_node);
-	void _convert_scene_node(Ref<GLTFState> state, Node *p_current, Node *p_root,
+	void _convert_scene_node(Ref<GLTFState> state, Node *p_current,
 			const GLTFNodeIndex p_gltf_current,
 			const GLTFNodeIndex p_gltf_root);
 
 #ifdef MODULE_CSG_ENABLED
-	void _convert_csg_shape_to_gltf(Node *p_current, GLTFNodeIndex p_gltf_parent, Ref<GLTFNode> gltf_node, Ref<GLTFState> state);
+	void _convert_csg_shape_to_gltf(CSGShape *p_current, GLTFNodeIndex p_gltf_parent, Ref<GLTFNode> gltf_node, Ref<GLTFState> state);
 #endif // MODULE_CSG_ENABLED
 
 	void _create_gltf_node(Ref<GLTFState> state,
@@ -394,40 +396,39 @@ public:
 			Ref<GLTFNode> gltf_node);
 	void _convert_animation_player_to_gltf(
 			AnimationPlayer *animation_player, Ref<GLTFState> state,
-			const GLTFNodeIndex &p_gltf_current,
-			const GLTFNodeIndex &p_gltf_root_index,
-			Ref<GLTFNode> p_gltf_node, Node *p_scene_parent,
-			Node *p_root);
+			GLTFNodeIndex p_gltf_current,
+			GLTFNodeIndex p_gltf_root_index,
+			Ref<GLTFNode> p_gltf_node, Node *p_scene_parent);
 	void _check_visibility(Node *p_node, bool &retflag);
 	void _convert_camera_to_gltf(Camera *camera, Ref<GLTFState> state,
-			Spatial *spatial,
 			Ref<GLTFNode> gltf_node);
 #ifdef MODULE_GRIDMAP_ENABLED
 	void _convert_grid_map_to_gltf(
-			Node *p_scene_parent,
-			const GLTFNodeIndex &p_parent_node_index,
-			const GLTFNodeIndex &p_root_node_index,
-			Ref<GLTFNode> gltf_node, Ref<GLTFState> state,
-			Node *p_root_node);
+			GridMap *p_grid_map,
+			GLTFNodeIndex p_parent_node_index,
+			GLTFNodeIndex p_root_node_index,
+			Ref<GLTFNode> gltf_node, Ref<GLTFState> state);
 #endif // MODULE_GRIDMAP_ENABLED
 	void _convert_mult_mesh_instance_to_gltf(
-			Node *p_scene_parent,
-			const GLTFNodeIndex &p_parent_node_index,
-			const GLTFNodeIndex &p_root_node_index,
-			Ref<GLTFNode> gltf_node, Ref<GLTFState> state,
-			Node *p_root_node);
+			MultiMeshInstance *p_scene_parent,
+			GLTFNodeIndex p_parent_node_index,
+			GLTFNodeIndex p_root_node_index,
+			Ref<GLTFNode> gltf_node, Ref<GLTFState> state);
 	void _convert_skeleton_to_gltf(
-			Node *p_scene_parent, Ref<GLTFState> state,
-			const GLTFNodeIndex &p_parent_node_index,
-			const GLTFNodeIndex &p_root_node_index,
-			Ref<GLTFNode> gltf_node, Node *p_root_node);
-	void _convert_bone_attachment_to_gltf(Node *p_scene_parent,
+			Skeleton *p_scene_parent, Ref<GLTFState> state,
+			GLTFNodeIndex p_parent_node_index,
+			GLTFNodeIndex p_root_node_index,
+			Ref<GLTFNode> gltf_node);
+	void _convert_bone_attachment_to_gltf(BoneAttachment *p_bone_attachment,
+			Ref<GLTFState> state,
+			GLTFNodeIndex p_parent_node_index,
+			GLTFNodeIndex p_root_node_index,
+			Ref<GLTFNode> gltf_node);
+	void _convert_mesh_instance_to_gltf(MeshInstance *p_mesh_instance,
 			Ref<GLTFState> state,
-			Ref<GLTFNode> gltf_node,
-			bool &retflag);
-	void _convert_mesh_to_gltf(Node *p_scene_parent,
-			Ref<GLTFState> state, Spatial *spatial,
 			Ref<GLTFNode> gltf_node);
+	GLTFMeshIndex _convert_mesh_to_gltf(Ref<GLTFState> state,
+			MeshInstance *p_mesh_instance);
 	void _convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
 			String p_animation_track_name);
 	Error serialize(Ref<GLTFState> state, Node *p_root, const String &p_path);

+ 8 - 0
modules/gltf/gltf_state.h

@@ -45,6 +45,11 @@
 #include "gltf_skeleton.h"
 #include "gltf_skin.h"
 #include "gltf_texture.h"
+
+#include "core/map.h"
+#include "core/pair.h"
+#include "core/resource.h"
+#include "core/vector.h"
 #include "scene/animation/animation_player.h"
 #include "scene/resources/texture.h"
 
@@ -89,6 +94,9 @@ class GLTFState : public Resource {
 	Vector<Ref<GLTFAnimation>> animations;
 	Map<GLTFNodeIndex, Node *> scene_nodes;
 
+	Map<ObjectID, GLTFSkeletonIndex> skeleton3d_to_gltf_skeleton;
+	Map<ObjectID, Map<ObjectID, GLTFSkinIndex>> skin_and_skeleton3d_to_gltf_skin;
+
 protected:
 	static void _bind_methods();