diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index a448575b5261..d52473fa9e29 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -2439,7 +2439,7 @@ Error GLTFDocument::_serialize_meshes(Ref state) { //blend shapes print_verbose("glTF: Mesh has targets"); - if (import_mesh->get_blend_shape_count()) { + if (import_mesh->get_blend_shape_count() && surface_i == 0) { ArrayMesh::BlendShapeMode shape_mode = import_mesh->get_blend_shape_mode(); for (int morph_i = 0; morph_i < import_mesh->get_blend_shape_count(); morph_i++) { Array array_morph = import_mesh->get_surface_blend_shape_arrays(surface_i, morph_i); @@ -2463,6 +2463,20 @@ Error GLTFDocument::_serialize_meshes(Ref state) { if (varr.size()) { t["NORMAL"] = _encode_accessor_as_vec3(state, narr, true); } + Vector tarr = array_morph[Mesh::ARRAY_TANGENT]; + if (tarr.size()) { + const int ret_size = tarr.size() / 4; + Vector attribs; + attribs.resize(ret_size); + for (int i = 0; i < ret_size; i++) { + Vector3 vec3; + vec3.x = tarr[(i * 4) + 0]; + vec3.y = tarr[(i * 4) + 1]; + vec3.z = tarr[(i * 4) + 2]; + vec3 *= tarr[(i * 4) + 3] * -1; + } + t["TANGENT"] = _encode_accessor_as_vec3(state, attribs, true); + } targets.push_back(t); } } @@ -2490,12 +2504,13 @@ Error GLTFDocument::_serialize_meshes(Ref 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; @@ -4644,8 +4659,8 @@ Error GLTFDocument::_serialize_animations(Ref state) { Array channels; Array samplers; - for (Map::Element *track_i = gltf_animation->get_tracks().front(); track_i; track_i = track_i->next()) { - GLTFAnimation::Track track = track_i->get(); + for (Map::Element *anim_track_i = gltf_animation->get_tracks().front(); anim_track_i; anim_track_i = anim_track_i->next()) { + GLTFAnimation::Track track = anim_track_i->get(); if (track.translation_track.times.size()) { Dictionary t; t["sampler"] = samplers.size(); @@ -4661,7 +4676,7 @@ Error GLTFDocument::_serialize_animations(Ref state) { Dictionary target; target["path"] = "translation"; - target["node"] = track_i->key(); + target["node"] = anim_track_i->key(); t["target"] = target; channels.push_back(t); @@ -4681,7 +4696,7 @@ Error GLTFDocument::_serialize_animations(Ref state) { Dictionary target; target["path"] = "rotation"; - target["node"] = track_i->key(); + target["node"] = anim_track_i->key(); t["target"] = target; channels.push_back(t); @@ -4701,42 +4716,86 @@ Error GLTFDocument::_serialize_animations(Ref state) { Dictionary target; target["path"] = "scale"; - target["node"] = track_i->key(); + target["node"] = anim_track_i->key(); t["target"] = target; channels.push_back(t); } if (track.weight_tracks.size()) { + double length = 0.0f; + + for (int32_t weight_track_i = 0; weight_track_i < track.weight_tracks.size(); weight_track_i++) { + int32_t last_time_index = track.weight_tracks[weight_track_i].times.size() - 1; + length = MAX(length, track.weight_tracks[weight_track_i].times[last_time_index]); + } + Dictionary t; t["sampler"] = samplers.size(); Dictionary s; - Vector times; - Vector 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 interp_weight_track_i = 0; interp_weight_track_i < track.weight_tracks.size(); interp_weight_track_i++) { + double time = 0.0; + bool last = false; + Vector weight_track; + while (true) { + float weight = _interpolate_track(track.weight_tracks[interp_weight_track_i].times, + track.weight_tracks[interp_weight_track_i].values, + time, + track.weight_tracks[interp_weight_track_i].interpolation); + weight_track.push_back(weight); + if (last) { + break; + } + time += increment; + if (time >= length) { + last = true; + time = length; + } + } + track.weight_tracks.write[interp_weight_track_i].times = times; + track.weight_tracks.write[interp_weight_track_i].values = weight_track; } - values.resize(times.size() * track.weight_tracks.size()); - // TODO Sort by order in blend shapes + Vector all_track_times = times; + Vector 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 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 = k * weight_tracks_size + l; + 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); Dictionary target; target["path"] = "weights"; - target["node"] = track_i->key(); + target["node"] = anim_track_i->key(); t["target"] = target; channels.push_back(t); @@ -4864,7 +4923,7 @@ Error GLTFDocument::_parse_animations(Ref 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 @@ -6071,7 +6130,6 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref 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_TRANSFORM3D) { p_track.translation_track.times = times; p_track.translation_track.interpolation = gltf_interpolation; @@ -6309,69 +6367,56 @@ void GLTFDocument::_convert_animation(Ref state, AnimationPlayer *ap, const Vector 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::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 MeshInstance3D *mi = Object::cast_to(node); - if (!mi) { - continue; - } - Ref array_mesh = mi->get_mesh(); - if (array_mesh.is_null()) { - continue; - } - if (node_suffix.size() != 2) { - continue; - } - 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; - } - } - ERR_CONTINUE(mesh_index == -1); - Ref 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::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 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; - } + Node *node = ap->get_parent()->get_node_or_null(path); + MeshInstance3D *mi = cast_to(node); + Ref mesh = mi->get_mesh(); + ERR_CONTINUE(mesh.is_null()); + int32_t mesh_index = -1; + for (Map::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); + GLTFAnimation::Track track = gltf_animation->get_tracks().has(mesh_index) ? gltf_animation->get_tracks()[mesh_index] : GLTFAnimation::Track(); + 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 weight; + weight.interpolation = GLTFAnimation::INTERP_LINEAR; + weight.times.push_back(0.0f); + weight.values.push_back(0.0f); + track.weight_tracks.push_back(weight); + 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; + } + int32_t key_count = animation->track_get_key_count(shape_track_i); + GLTFAnimation::Channel 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); + } + 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); + } + weight.times.push_back(0.0f); + weight.values.push_back(0.0f); + track.weight_tracks.push_back(weight); + } + Map &tracks = gltf_animation->get_tracks(); + tracks[mesh_index] = track; } else if (String(orig_track_path).find(":") != -1) { //Process skeleton const Vector node_suffix = String(orig_track_path).split(":"); diff --git a/modules/gltf/gltf_document.h b/modules/gltf/gltf_document.h index efb4c0f0249a..5bb4d135b49d 100644 --- a/modules/gltf/gltf_document.h +++ b/modules/gltf/gltf_document.h @@ -103,6 +103,7 @@ class GLTFDocument : public Resource { }; private: + const float BAKE_FPS = 30.0f; template static Array to_array(const Vector &p_inp) { Array ret;