Browse Source

Backport to 3.x "gltf export: Fix export of skeletons, skins and blend shapes."

Create GLTFSkeleton at the same time we create GLTFNode objects.
Create GLTFSkin at the same time we export MeshInstance3D
Fixes export of blend shape arrays for meshes with multiple surfaces.
Fixes array indexing issues in export of glTF morph target animations.

Converts BoneAttachment3D nodes during normal node creation: this avoids
special cases during mesh export, and especially exporting skeletons or meshes
which are children of BoneAttachment3D.

Co-authored-by: K. S. Ernest (iFire) Lee <[email protected]>
Lyuma 4 năm trước cách đây
mục cha
commit
3cdaaffb54

+ 335 - 342
modules/gltf/gltf_document.cpp

@@ -82,7 +82,10 @@
 Error GLTFDocument::serialize(Ref<GLTFState> state, Node *p_root, const String &p_path) {
 Error GLTFDocument::serialize(Ref<GLTFState> state, Node *p_root, const String &p_path) {
 	uint64_t begin_time = OS::get_singleton()->get_ticks_usec();
 	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()) {
 	if (!state->buffers.size()) {
 		state->buffers.push_back(Vector<uint8_t>());
 		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) {
 	if (err != OK) {
 		return Error::FAILED;
 		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) */
 	/* STEP 5 SERIALIZE MESHES (we have enough info now) */
 	err = _serialize_meshes(state);
 	err = _serialize_meshes(state);
 	if (err != OK) {
 	if (err != OK) {
@@ -255,30 +254,6 @@ Error GLTFDocument::_parse_json(const String &p_path, Ref<GLTFState> state) {
 	return OK;
 	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 GLTFDocument::_parse_glb(const String &p_path, Ref<GLTFState> state) {
 	Error err;
 	Error err;
 	FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err);
 	FileAccessRef f = FileAccess::open(p_path, FileAccess::READ, &err);
@@ -2313,11 +2288,14 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
 			continue;
 			continue;
 		}
 		}
 		Array primitives;
 		Array primitives;
-		Array targets;
 		Dictionary gltf_mesh;
 		Dictionary gltf_mesh;
 		Array target_names;
 		Array target_names;
 		Array weights;
 		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++) {
 		for (int surface_i = 0; surface_i < import_mesh->get_surface_count(); surface_i++) {
+			Array targets;
 			Dictionary primitive;
 			Dictionary primitive;
 			Mesh::PrimitiveType primitive_type = import_mesh->surface_get_primitive_type(surface_i);
 			Mesh::PrimitiveType primitive_type = import_mesh->surface_get_primitive_type(surface_i);
 			switch (primitive_type) {
 			switch (primitive_type) {
@@ -2443,10 +2421,10 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
 				const Array &a = array[Mesh::ARRAY_WEIGHTS];
 				const Array &a = array[Mesh::ARRAY_WEIGHTS];
 				const Vector<Vector3> &vertex_array = array[Mesh::ARRAY_VERTEX];
 				const Vector<Vector3> &vertex_array = array[Mesh::ARRAY_VERTEX];
 				if ((a.size() / JOINT_GROUP_SIZE) == vertex_array.size()) {
 				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;
 					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]);
 						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);
 					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);
 				Array array_morphs = import_mesh->surface_get_blend_shape_arrays(surface_i);
 				for (int morph_i = 0; morph_i < array_morphs.size(); morph_i++) {
 				for (int morph_i = 0; morph_i < array_morphs.size(); morph_i++) {
 					Array array_morph = array_morphs[morph_i];
 					Array array_morph = array_morphs[morph_i];
-					target_names.push_back(import_mesh->get_blend_shape_name(morph_i));
 					Dictionary t;
 					Dictionary t;
 					Vector<Vector3> varr = array_morph[Mesh::ARRAY_VERTEX];
 					Vector<Vector3> varr = array_morph[Mesh::ARRAY_VERTEX];
 					Array mesh_arrays = import_mesh->surface_get_arrays(surface_i);
 					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];
 					Vector<Vector3> narr = array_morph[Mesh::ARRAY_NORMAL];
-					if (varr.size()) {
+					if (narr.size()) {
 						t["NORMAL"] = _encode_accessor_as_vec3(state, narr, true);
 						t["NORMAL"] = _encode_accessor_as_vec3(state, narr, true);
 					}
 					}
 					Vector<real_t> tarr = array_morph[Mesh::ARRAY_TANGENT];
 					Vector<real_t> tarr = array_morph[Mesh::ARRAY_TANGENT];
 					if (tarr.size()) {
 					if (tarr.size()) {
 						const int ret_size = tarr.size() / 4;
 						const int ret_size = tarr.size() / 4;
-						Vector<Color> attribs;
+						Vector<Vector3> attribs;
 						attribs.resize(ret_size);
 						attribs.resize(ret_size);
 						for (int i = 0; i < ret_size; i++) {
 						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);
 					targets.push_back(t);
 				}
 				}
@@ -2579,12 +2555,13 @@ Error GLTFDocument::_serialize_meshes(Ref<GLTFState> state) {
 		Dictionary e;
 		Dictionary e;
 		e["targetNames"] = target_names;
 		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;
 			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()) {
 		if (weights.size()) {
 			gltf_mesh["weights"] = weights;
 			gltf_mesh["weights"] = weights;
@@ -4294,6 +4271,7 @@ Error GLTFDocument::_create_skeletons(Ref<GLTFState> state) {
 
 
 		Skeleton *skeleton = memnew(Skeleton);
 		Skeleton *skeleton = memnew(Skeleton);
 		gltf_skeleton->godot_skeleton = 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
 		// Make a unique name, no gltf node represents this skeleton
 		skeleton->set_name(_gen_unique_name(state, "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) {
 Error GLTFDocument::_serialize_skins(Ref<GLTFState> state) {
 	_remove_duplicate_skins(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;
 	return OK;
 }
 }
 
 
@@ -4757,30 +4745,74 @@ Error GLTFDocument::_serialize_animations(Ref<GLTFState> state) {
 				channels.push_back(t);
 				channels.push_back(t);
 			}
 			}
 			if (track.weight_tracks.size()) {
 			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;
 				Dictionary t;
 				t["sampler"] = samplers.size();
 				t["sampler"] = samplers.size();
 				Dictionary s;
 				Dictionary s;
-
 				Vector<real_t> times;
 				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++) {
 				for (int k = 0; k < track.weight_tracks.size(); k++) {
 					Vector<float> wdata = track.weight_tracks[k].values;
 					Vector<float> wdata = track.weight_tracks[k].values;
 					for (int l = 0; l < wdata.size(); l++) {
 					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["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);
 				samplers.push_back(s);
 
 
@@ -4918,7 +4950,7 @@ Error GLTFDocument::_parse_animations(Ref<GLTFState> state) {
 				track->weight_tracks.resize(wc);
 				track->weight_tracks.resize(wc);
 
 
 				const int expected_value_count = times.size() * output_count * 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;
 				const int wlen = weights.size() / wc;
 				for (int k = 0; k < wc; k++) { //separate tracks, having them together is not such a good idea
 				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;
 	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);
 	ERR_FAIL_NULL_V(p_mesh_instance, -1);
 	if (p_mesh_instance->get_mesh().is_null()) {
 	if (p_mesh_instance->get_mesh().is_null()) {
 		return -1;
 		return -1;
@@ -4998,35 +5030,32 @@ GLTFMeshIndex GLTFDocument::_convert_mesh_instance(Ref<GLTFState> state, MeshIns
 	if (godot_mesh.is_null()) {
 	if (godot_mesh.is_null()) {
 		return -1;
 		return -1;
 	}
 	}
-	Vector<float> blend_weights;
-	Vector<String> blend_names;
 	int32_t blend_count = godot_mesh->get_blend_shape_count();
 	int32_t blend_count = godot_mesh->get_blend_shape_count();
-	blend_names.resize(blend_count);
+	Vector<float> blend_weights;
 	blend_weights.resize(blend_count);
 	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++) {
 	for (int32_t blend_i = 0; blend_i < blend_count; blend_i++) {
 		blend_weights.write[blend_i] = 0.0f;
 		blend_weights.write[blend_i] = 0.0f;
@@ -5190,17 +5219,6 @@ GLTFLightIndex GLTFDocument::_convert_light(Ref<GLTFState> state, Light *p_light
 	return light_index;
 	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) {
 void GLTFDocument::_convert_spatial(Ref<GLTFState> state, Spatial *p_spatial, Ref<GLTFNode> p_node) {
 	Transform xform = p_spatial->get_transform();
 	Transform xform = p_spatial->get_transform();
 	p_node->scale = xform.basis.get_scale();
 	p_node->scale = xform.basis.get_scale();
@@ -5216,7 +5234,7 @@ Spatial *GLTFDocument::_generate_spatial(Ref<GLTFState> state, Node *scene_paren
 
 
 	return spatial;
 	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;
 	bool retflag = true;
 	_check_visibility(p_current, retflag);
 	_check_visibility(p_current, retflag);
 	if (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);
 		_convert_spatial(state, spatial, gltf_node);
 	}
 	}
 	if (cast_to<MeshInstance>(p_current)) {
 	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)) {
 	} 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;
 		return;
 	} else if (cast_to<Skeleton>(p_current)) {
 	} 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.
 		// We ignore the Godot Engine node that is the skeleton.
 		return;
 		return;
 	} else if (cast_to<MultiMeshInstance>(p_current)) {
 	} 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
 #ifdef MODULE_CSG_ENABLED
 	} else if (cast_to<CSGShape>(p_current)) {
 	} 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
 #endif // MODULE_CSG_ENABLED
 #ifdef MODULE_GRIDMAP_ENABLED
 #ifdef MODULE_GRIDMAP_ENABLED
 	} else if (cast_to<GridMap>(p_current)) {
 	} 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
 #endif // MODULE_GRIDMAP_ENABLED
 	} else if (cast_to<Camera>(p_current)) {
 	} else if (cast_to<Camera>(p_current)) {
 		Camera *camera = Object::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)) {
 	} else if (cast_to<Light>(p_current)) {
 		Light *light = Object::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)) {
 	} else if (cast_to<AnimationPlayer>(p_current)) {
 		AnimationPlayer *animation_player = Object::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 current_node_i = state->nodes.size();
 	GLTFNodeIndex gltf_root = p_gltf_root;
 	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);
 	_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++) {
 	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
 #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");
 	csg->call("_update_shape");
 	Array meshes = csg->get_meshes();
 	Array meshes = csg->get_meshes();
 	if (meshes.size() != 2) {
 	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) {
 		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->scene_nodes.insert(current_node_i, p_scene_parent);
 	state->nodes.push_back(gltf_node);
 	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) {
 	if (p_parent_node_index == -1) {
 		return;
 		return;
 	}
 	}
 	state->nodes.write[p_parent_node_index]->children.push_back(current_node_i);
 	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);
 	ERR_FAIL_COND(!animation_player);
 	state->animation_players.push_back(animation_player);
 	state->animation_players.push_back(animation_player);
 	print_verbose(String("glTF: Converting animation player: ") + animation_player->get_name());
 	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;
 	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);
 	ERR_FAIL_COND(!camera);
 	GLTFCameraIndex camera_index = _convert_camera(state, camera);
 	GLTFCameraIndex camera_index = _convert_camera(state, camera);
 	if (camera_index != -1) {
 	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);
 	ERR_FAIL_COND(!light);
 	GLTFLightIndex light_index = _convert_light(state, light);
 	GLTFLightIndex light_index = _convert_light(state, light);
 	if (light_index != -1) {
 	if (light_index != -1) {
@@ -5354,43 +5375,39 @@ void GLTFDocument::_convert_light_to_gltf(Light *light, Ref<GLTFState> state, Sp
 }
 }
 
 
 #ifdef MODULE_GRIDMAP_ENABLED
 #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++) {
 	for (int32_t k = 0; k < cells.size(); k++) {
 		GLTFNode *new_gltf_node = memnew(GLTFNode);
 		GLTFNode *new_gltf_node = memnew(GLTFNode);
 		gltf_node->children.push_back(state->nodes.size());
 		gltf_node->children.push_back(state->nodes.size());
 		state->nodes.push_back(new_gltf_node);
 		state->nodes.push_back(new_gltf_node);
 		Vector3 cell_location = cells[k];
 		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));
 				Vector3(cell_location.x, cell_location.y, cell_location.z));
 		MeshInstance *import_mesh_node = memnew(MeshInstance);
 		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;
 		Transform cell_xform;
 		cell_xform.basis.set_orthogonal_index(
 		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)));
 						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)));
 				Vector3(cell_location.x, cell_location.y, cell_location.z)));
 		Ref<GLTFMesh> gltf_mesh;
 		Ref<GLTFMesh> gltf_mesh;
 		gltf_mesh.instance();
 		gltf_mesh.instance();
 		gltf_mesh = import_mesh_node;
 		gltf_mesh = import_mesh_node;
 		new_gltf_node->mesh = state->meshes.size();
 		new_gltf_node->mesh = state->meshes.size();
 		state->meshes.push_back(gltf_mesh);
 		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
 #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()) {
 	if (multi_mesh.is_valid()) {
 		for (int32_t instance_i = 0; instance_i < multi_mesh->get_instance_count();
 		for (int32_t instance_i = 0; instance_i < multi_mesh->get_instance_count();
 				instance_i++) {
 				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,
 				transform.basis.set_quat_scale(quat,
 						Vector3(scale.x, 0, scale.y));
 						Vector3(scale.x, 0, scale.y));
 				transform =
 				transform =
-						multi_mesh_instance->get_transform() * transform;
+						p_multi_mesh_instance->get_transform() * transform;
 			} else if (multi_mesh->get_transform_format() == MultiMesh::TRANSFORM_3D) {
 			} 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);
 							multi_mesh->get_instance_transform(instance_i);
 			}
 			}
 			Ref<ArrayMesh> mm = multi_mesh->get_mesh();
 			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);
 				state->meshes.push_back(gltf_mesh);
 			}
 			}
 			new_gltf_node->xform = transform;
 			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());
 			gltf_node->children.push_back(state->nodes.size());
 			state->nodes.push_back(new_gltf_node);
 			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) {
 		if (node->mesh < 0) {
 			continue;
 			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);
 		Map<GLTFNodeIndex, Node *>::Element *mi_element = state->scene_nodes.find(mi_node_i);
 		if (!mi_element) {
 		if (!mi_element) {
 			continue;
 			continue;
@@ -5948,7 +6000,6 @@ void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> state) {
 		node->rotation = mi_xform.basis.get_rotation_quat();
 		node->rotation = mi_xform.basis.get_rotation_quat();
 		node->translation = mi_xform.origin;
 		node->translation = mi_xform.origin;
 
 
-		Dictionary json_skin;
 		Skeleton *skeleton = Object::cast_to<Skeleton>(mi->get_node(mi->get_skeleton_path()));
 		Skeleton *skeleton = Object::cast_to<Skeleton>(mi->get_node(mi->get_skeleton_path()));
 		if (!skeleton) {
 		if (!skeleton) {
 			continue;
 			continue;
@@ -5957,121 +6008,75 @@ void GLTFDocument::_convert_mesh_instances(Ref<GLTFState> state) {
 			continue;
 			continue;
 		}
 		}
 		Ref<Skin> skin = mi->get_skin();
 		Ref<Skin> skin = mi->get_skin();
-		if (skin.is_null()) {
-			skin = skeleton->register_skin(nullptr)->get_skin();
-		}
 		Ref<GLTFSkin> gltf_skin;
 		Ref<GLTFSkin> gltf_skin;
 		gltf_skin.instance();
 		gltf_skin.instance();
 		Array json_joints;
 		Array json_joints;
-		GLTFSkeletonIndex skeleton_gltf_i = -1;
 
 
 		NodePath skeleton_path = mi->get_skeleton_path();
 		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 {
 			} 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++) {
 	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);
 		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) {
 	if (track_type == Animation::TYPE_TRANSFORM) {
 		p_track.translation_track.times = times;
 		p_track.translation_track.times = times;
 		p_track.translation_track.interpolation = gltf_interpolation;
 		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 Vector<String> node_suffix = String(orig_track_path).split(":blend_shapes/");
 			const NodePath path = node_suffix[0];
 			const NodePath path = node_suffix[0];
 			const String suffix = node_suffix[1];
 			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;
 						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) {
 		} else if (String(orig_track_path).find(":") != -1) {
 			//Process skeleton
 			//Process skeleton
 			const Vector<String> node_suffix = String(orig_track_path).split(":");
 			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 GLTFNode;
 class GLTFSpecGloss;
 class GLTFSpecGloss;
 class GLTFSkeleton;
 class GLTFSkeleton;
+class CSGShape;
+class GridMap;
+class MultiMeshInstance;
 
 
 using GLTFAccessorIndex = int;
 using GLTFAccessorIndex = int;
 using GLTFAnimationIndex = int;
 using GLTFAnimationIndex = int;
@@ -69,6 +72,9 @@ class GLTFDocument : public Resource {
 	friend class GLTFSkin;
 	friend class GLTFSkin;
 	friend class GLTFSkeleton;
 	friend class GLTFSkeleton;
 
 
+private:
+	const float BAKE_FPS = 30.0f;
+
 public:
 public:
 	const int32_t JOINT_GROUP_SIZE = 4;
 	const int32_t JOINT_GROUP_SIZE = 4;
 	enum GLTFType {
 	enum GLTFType {
@@ -336,7 +342,6 @@ private:
 			GLTFNodeIndex p_node_i);
 			GLTFNodeIndex p_node_i);
 	Error _encode_buffer_bins(Ref<GLTFState> state, const String &p_path);
 	Error _encode_buffer_bins(Ref<GLTFState> state, const String &p_path);
 	Error _encode_buffer_glb(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_uv1(Ref<SpatialMaterial> p_material);
 	Dictionary _serialize_texture_transform_uv2(Ref<SpatialMaterial> p_material);
 	Dictionary _serialize_texture_transform_uv2(Ref<SpatialMaterial> p_material);
 	Error _serialize_version(Ref<GLTFState> state);
 	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 _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,
 	void _import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
 			const GLTFAnimationIndex index, const int bake_fps);
 			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);
 	void _convert_mesh_instances(Ref<GLTFState> state);
 	GLTFCameraIndex _convert_camera(Ref<GLTFState> state, Camera *p_camera);
 	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);
 	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_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_current,
 			const GLTFNodeIndex p_gltf_root);
 			const GLTFNodeIndex p_gltf_root);
 
 
 #ifdef MODULE_CSG_ENABLED
 #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
 #endif // MODULE_CSG_ENABLED
 
 
 	void _create_gltf_node(Ref<GLTFState> state,
 	void _create_gltf_node(Ref<GLTFState> state,
@@ -394,40 +396,39 @@ public:
 			Ref<GLTFNode> gltf_node);
 			Ref<GLTFNode> gltf_node);
 	void _convert_animation_player_to_gltf(
 	void _convert_animation_player_to_gltf(
 			AnimationPlayer *animation_player, Ref<GLTFState> state,
 			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 _check_visibility(Node *p_node, bool &retflag);
 	void _convert_camera_to_gltf(Camera *camera, Ref<GLTFState> state,
 	void _convert_camera_to_gltf(Camera *camera, Ref<GLTFState> state,
-			Spatial *spatial,
 			Ref<GLTFNode> gltf_node);
 			Ref<GLTFNode> gltf_node);
 #ifdef MODULE_GRIDMAP_ENABLED
 #ifdef MODULE_GRIDMAP_ENABLED
 	void _convert_grid_map_to_gltf(
 	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
 #endif // MODULE_GRIDMAP_ENABLED
 	void _convert_mult_mesh_instance_to_gltf(
 	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(
 	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<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);
 			Ref<GLTFNode> gltf_node);
+	GLTFMeshIndex _convert_mesh_to_gltf(Ref<GLTFState> state,
+			MeshInstance *p_mesh_instance);
 	void _convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
 	void _convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
 			String p_animation_track_name);
 			String p_animation_track_name);
 	Error serialize(Ref<GLTFState> state, Node *p_root, const String &p_path);
 	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_skeleton.h"
 #include "gltf_skin.h"
 #include "gltf_skin.h"
 #include "gltf_texture.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/animation/animation_player.h"
 #include "scene/resources/texture.h"
 #include "scene/resources/texture.h"
 
 
@@ -89,6 +94,9 @@ class GLTFState : public Resource {
 	Vector<Ref<GLTFAnimation>> animations;
 	Vector<Ref<GLTFAnimation>> animations;
 	Map<GLTFNodeIndex, Node *> scene_nodes;
 	Map<GLTFNodeIndex, Node *> scene_nodes;
 
 
+	Map<ObjectID, GLTFSkeletonIndex> skeleton3d_to_gltf_skeleton;
+	Map<ObjectID, Map<ObjectID, GLTFSkinIndex>> skin_and_skeleton3d_to_gltf_skin;
+
 protected:
 protected:
 	static void _bind_methods();
 	static void _bind_methods();