Merge pull request #68665 from TokageItLab/cut-unkeyed-gltf-anim

Add "Trimming" option to cut un-keyed timeline before first key in glTF animation
This commit is contained in:
Rémi Verschelde 2022-11-29 16:51:56 +01:00
commit 09e1db2148
No known key found for this signature in database
GPG key ID: C3336907360768E1
17 changed files with 110 additions and 95 deletions

View file

@ -39,7 +39,6 @@
<param index="0" name="path" type="String" /> <param index="0" name="path" type="String" />
<param index="1" name="flags" type="int" /> <param index="1" name="flags" type="int" />
<param index="2" name="options" type="Dictionary" /> <param index="2" name="options" type="Dictionary" />
<param index="3" name="bake_fps" type="int" />
<description> <description>
</description> </description>
</method> </method>

View file

@ -65,7 +65,7 @@ struct ColladaImport {
bool force_make_tangents = false; bool force_make_tangents = false;
bool apply_mesh_xform_to_vertices = true; bool apply_mesh_xform_to_vertices = true;
bool use_mesh_builtin_materials = false; bool use_mesh_builtin_materials = false;
float bake_fps = 15; float bake_fps = 30;
HashMap<String, NodeMap> node_map; //map from collada node to engine node HashMap<String, NodeMap> node_map; //map from collada node to engine node
HashMap<String, String> node_name_map; //map from collada node to engine node HashMap<String, String> node_name_map; //map from collada node to engine node
@ -1760,7 +1760,7 @@ void EditorSceneFormatImporterCollada::get_extensions(List<String> *r_extensions
r_extensions->push_back("dae"); r_extensions->push_back("dae");
} }
Node *EditorSceneFormatImporterCollada::import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) { Node *EditorSceneFormatImporterCollada::import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err) {
if (r_err) { if (r_err) {
*r_err = OK; *r_err = OK;
} }
@ -1771,7 +1771,7 @@ Node *EditorSceneFormatImporterCollada::import_scene(const String &p_path, uint3
} }
state.use_mesh_builtin_materials = true; state.use_mesh_builtin_materials = true;
state.bake_fps = p_bake_fps; state.bake_fps = (float)p_options["animation/fps"];
Error err = state.load(p_path, flags, p_flags & EditorSceneFormatImporter::IMPORT_GENERATE_TANGENT_ARRAYS, false); Error err = state.load(p_path, flags, p_flags & EditorSceneFormatImporter::IMPORT_GENERATE_TANGENT_ARRAYS, false);

View file

@ -39,7 +39,7 @@ class EditorSceneFormatImporterCollada : public EditorSceneFormatImporter {
public: public:
virtual uint32_t get_import_flags() const override; virtual uint32_t get_import_flags() const override;
virtual void get_extensions(List<String> *r_extensions) const override; virtual void get_extensions(List<String> *r_extensions) const override;
virtual Node *import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, int p_bake_fps, List<String> *r_missing_deps = nullptr, Error *r_err = nullptr) override; virtual Node *import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps = nullptr, Error *r_err = nullptr) override;
EditorSceneFormatImporterCollada(); EditorSceneFormatImporterCollada();
}; };

View file

@ -422,7 +422,7 @@ static Error _parse_obj(const String &p_path, List<Ref<Mesh>> &r_meshes, bool p_
return OK; return OK;
} }
Node *EditorOBJImporter::import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) { Node *EditorOBJImporter::import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err) {
List<Ref<Mesh>> meshes; List<Ref<Mesh>> meshes;
Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, false, Vector3(1, 1, 1), Vector3(0, 0, 0), r_missing_deps); Error err = _parse_obj(p_path, meshes, false, p_flags & IMPORT_GENERATE_TANGENT_ARRAYS, false, Vector3(1, 1, 1), Vector3(0, 0, 0), r_missing_deps);

View file

@ -39,7 +39,7 @@ class EditorOBJImporter : public EditorSceneFormatImporter {
public: public:
virtual uint32_t get_import_flags() const override; virtual uint32_t get_import_flags() const override;
virtual void get_extensions(List<String> *r_extensions) const override; virtual void get_extensions(List<String> *r_extensions) const override;
virtual Node *import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = nullptr) override; virtual Node *import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err = nullptr) override;
EditorOBJImporter(); EditorOBJImporter();
}; };

View file

@ -74,13 +74,13 @@ void EditorSceneFormatImporter::get_extensions(List<String> *r_extensions) const
ERR_FAIL(); ERR_FAIL();
} }
Node *EditorSceneFormatImporter::import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) { Node *EditorSceneFormatImporter::import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err) {
Dictionary options_dict; Dictionary options_dict;
for (const KeyValue<StringName, Variant> &elem : p_options) { for (const KeyValue<StringName, Variant> &elem : p_options) {
options_dict[elem.key] = elem.value; options_dict[elem.key] = elem.value;
} }
Object *ret = nullptr; Object *ret = nullptr;
if (GDVIRTUAL_CALL(_import_scene, p_path, p_flags, options_dict, p_bake_fps, ret)) { if (GDVIRTUAL_CALL(_import_scene, p_path, p_flags, options_dict, ret)) {
return Object::cast_to<Node>(ret); return Object::cast_to<Node>(ret);
} }
@ -100,7 +100,7 @@ Variant EditorSceneFormatImporter::get_option_visibility(const String &p_path, b
void EditorSceneFormatImporter::_bind_methods() { void EditorSceneFormatImporter::_bind_methods() {
GDVIRTUAL_BIND(_get_import_flags); GDVIRTUAL_BIND(_get_import_flags);
GDVIRTUAL_BIND(_get_extensions); GDVIRTUAL_BIND(_get_extensions);
GDVIRTUAL_BIND(_import_scene, "path", "flags", "options", "bake_fps"); GDVIRTUAL_BIND(_import_scene, "path", "flags", "options");
GDVIRTUAL_BIND(_get_import_options, "path"); GDVIRTUAL_BIND(_get_import_options, "path");
GDVIRTUAL_BIND(_get_option_visibility, "path", "for_animation", "option"); GDVIRTUAL_BIND(_get_option_visibility, "path", "for_animation", "option");
@ -1864,6 +1864,7 @@ void ResourceImporterScene::get_import_options(const String &p_path, List<Import
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "skins/use_named_skins"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import"), true)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/import"), true));
r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 30)); r_options->push_back(ImportOption(PropertyInfo(Variant::FLOAT, "animation/fps", PROPERTY_HINT_RANGE, "1,120,1"), 30));
r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "animation/trimming"), false));
r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "import_script/path", PROPERTY_HINT_FILE, script_ext_hint), "")); r_options->push_back(ImportOption(PropertyInfo(Variant::STRING, "import_script/path", PROPERTY_HINT_FILE, script_ext_hint), ""));
r_options->push_back(ImportOption(PropertyInfo(Variant::DICTIONARY, "_subresources", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), Dictionary())); r_options->push_back(ImportOption(PropertyInfo(Variant::DICTIONARY, "_subresources", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NO_EDITOR), Dictionary()));
@ -2281,13 +2282,8 @@ Node *ResourceImporterScene::pre_import(const String &p_source_file, const HashM
ERR_FAIL_COND_V(!importer.is_valid(), nullptr); ERR_FAIL_COND_V(!importer.is_valid(), nullptr);
int bake_fps = 30;
if (p_options.has(SNAME("animation/fps"))) {
bake_fps = p_options[SNAME("animation/fps")];
}
Error err = OK; Error err = OK;
Node *scene = importer->import_scene(p_source_file, EditorSceneFormatImporter::IMPORT_ANIMATION | EditorSceneFormatImporter::IMPORT_GENERATE_TANGENT_ARRAYS, p_options, bake_fps, nullptr, &err); Node *scene = importer->import_scene(p_source_file, EditorSceneFormatImporter::IMPORT_ANIMATION | EditorSceneFormatImporter::IMPORT_GENERATE_TANGENT_ARRAYS, p_options, nullptr, &err);
if (!scene || err != OK) { if (!scene || err != OK) {
return nullptr; return nullptr;
} }
@ -2326,8 +2322,6 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
ERR_FAIL_COND_V(!importer.is_valid(), ERR_FILE_UNRECOGNIZED); ERR_FAIL_COND_V(!importer.is_valid(), ERR_FILE_UNRECOGNIZED);
float fps = p_options["animation/fps"];
int import_flags = 0; int import_flags = 0;
if (animation_importer) { if (animation_importer) {
@ -2350,7 +2344,7 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
Error err = OK; Error err = OK;
List<String> missing_deps; // for now, not much will be done with this List<String> missing_deps; // for now, not much will be done with this
Node *scene = importer->import_scene(src_path, import_flags, p_options, fps, &missing_deps, &err); Node *scene = importer->import_scene(src_path, import_flags, p_options, &missing_deps, &err);
if (!scene || err != OK) { if (!scene || err != OK) {
return err; return err;
} }
@ -2398,6 +2392,10 @@ Error ResourceImporterScene::import(const String &p_source_file, const String &p
post_importer_plugins.write[i]->pre_process(scene, p_options); post_importer_plugins.write[i]->pre_process(scene, p_options);
} }
float fps = 30;
if (p_options.has(SNAME("animation/fps"))) {
fps = (float)p_options[SNAME("animation/fps")];
}
_pre_fix_animations(scene, scene, node_data, animation_data, fps); _pre_fix_animations(scene, scene, node_data, animation_data, fps);
_post_fix_node(scene, scene, collision_map, occluder_arrays, scanned_meshes, node_data, material_data, animation_data, fps); _post_fix_node(scene, scene, collision_map, occluder_arrays, scanned_meshes, node_data, material_data, animation_data, fps);
_post_fix_animations(scene, scene, node_data, animation_data, fps); _post_fix_animations(scene, scene, node_data, animation_data, fps);
@ -2612,7 +2610,7 @@ void EditorSceneFormatImporterESCN::get_extensions(List<String> *r_extensions) c
r_extensions->push_back("escn"); r_extensions->push_back("escn");
} }
Node *EditorSceneFormatImporterESCN::import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, int p_bake_fps, List<String> *r_missing_deps, Error *r_err) { Node *EditorSceneFormatImporterESCN::import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err) {
Error error; Error error;
Ref<PackedScene> ps = ResourceFormatLoaderText::singleton->load(p_path, p_path, &error); Ref<PackedScene> ps = ResourceFormatLoaderText::singleton->load(p_path, p_path, &error);
ERR_FAIL_COND_V_MSG(!ps.is_valid(), nullptr, "Cannot load scene as text resource from path '" + p_path + "'."); ERR_FAIL_COND_V_MSG(!ps.is_valid(), nullptr, "Cannot load scene as text resource from path '" + p_path + "'.");

View file

@ -53,12 +53,12 @@ class EditorSceneFormatImporter : public RefCounted {
protected: protected:
static void _bind_methods(); static void _bind_methods();
Node *import_scene_wrapper(const String &p_path, uint32_t p_flags, Dictionary p_options, int p_bake_fps); Node *import_scene_wrapper(const String &p_path, uint32_t p_flags, Dictionary p_options);
Ref<Animation> import_animation_wrapper(const String &p_path, uint32_t p_flags, Dictionary p_options, int p_bake_fps); Ref<Animation> import_animation_wrapper(const String &p_path, uint32_t p_flags, Dictionary p_options);
GDVIRTUAL0RC(int, _get_import_flags) GDVIRTUAL0RC(int, _get_import_flags)
GDVIRTUAL0RC(Vector<String>, _get_extensions) GDVIRTUAL0RC(Vector<String>, _get_extensions)
GDVIRTUAL4R(Object *, _import_scene, String, uint32_t, Dictionary, uint32_t) GDVIRTUAL3R(Object *, _import_scene, String, uint32_t, Dictionary)
GDVIRTUAL1(_get_import_options, String) GDVIRTUAL1(_get_import_options, String)
GDVIRTUAL3RC(Variant, _get_option_visibility, String, bool, String) GDVIRTUAL3RC(Variant, _get_option_visibility, String, bool, String)
@ -74,7 +74,7 @@ public:
virtual uint32_t get_import_flags() const; virtual uint32_t get_import_flags() const;
virtual void get_extensions(List<String> *r_extensions) const; virtual void get_extensions(List<String> *r_extensions) const;
virtual Node *import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = nullptr); virtual Node *import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err = nullptr);
virtual void get_import_options(const String &p_path, List<ResourceImporter::ImportOption> *r_options); virtual void get_import_options(const String &p_path, List<ResourceImporter::ImportOption> *r_options);
virtual Variant get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option, const HashMap<StringName, Variant> &p_options); virtual Variant get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option, const HashMap<StringName, Variant> &p_options);
@ -310,7 +310,7 @@ class EditorSceneFormatImporterESCN : public EditorSceneFormatImporter {
public: public:
virtual uint32_t get_import_flags() const override; virtual uint32_t get_import_flags() const override;
virtual void get_extensions(List<String> *r_extensions) const override; virtual void get_extensions(List<String> *r_extensions) const override;
virtual Node *import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, int p_bake_fps, List<String> *r_missing_deps, Error *r_err = nullptr) override; virtual Node *import_scene(const String &p_path, uint32_t p_flags, const HashMap<StringName, Variant> &p_options, List<String> *r_missing_deps, Error *r_err = nullptr) override;
}; };
template <class M> template <class M>

View file

@ -14,7 +14,6 @@
<param index="1" name="base_path" type="String" /> <param index="1" name="base_path" type="String" />
<param index="2" name="state" type="GLTFState" /> <param index="2" name="state" type="GLTFState" />
<param index="3" name="flags" type="int" default="0" /> <param index="3" name="flags" type="int" default="0" />
<param index="4" name="bake_fps" type="int" default="30" />
<description> <description>
Takes a [PackedByteArray] defining a gLTF and returns a [GLTFState] object through the [param state] parameter. Takes a [PackedByteArray] defining a gLTF and returns a [GLTFState] object through the [param state] parameter.
[b]Note:[/b] The [param base_path] tells [method append_from_buffer] where to find dependencies and can be empty. [b]Note:[/b] The [param base_path] tells [method append_from_buffer] where to find dependencies and can be empty.
@ -25,8 +24,7 @@
<param index="0" name="path" type="String" /> <param index="0" name="path" type="String" />
<param index="1" name="state" type="GLTFState" /> <param index="1" name="state" type="GLTFState" />
<param index="2" name="flags" type="int" default="0" /> <param index="2" name="flags" type="int" default="0" />
<param index="3" name="bake_fps" type="int" default="30" /> <param index="3" name="base_path" type="String" default="&quot;&quot;" />
<param index="4" name="base_path" type="String" default="&quot;&quot;" />
<description> <description>
Takes a path to a gLTF file and returns a [GLTFState] object through the [param state] parameter. Takes a path to a gLTF file and returns a [GLTFState] object through the [param state] parameter.
[b]Note:[/b] The [param base_path] tells [method append_from_file] where to find dependencies and can be empty. [b]Note:[/b] The [param base_path] tells [method append_from_file] where to find dependencies and can be empty.
@ -37,7 +35,6 @@
<param index="0" name="node" type="Node" /> <param index="0" name="node" type="Node" />
<param index="1" name="state" type="GLTFState" /> <param index="1" name="state" type="GLTFState" />
<param index="2" name="flags" type="int" default="0" /> <param index="2" name="flags" type="int" default="0" />
<param index="3" name="bake_fps" type="int" default="30" />
<description> <description>
Takes a Godot Engine scene node and returns a [GLTFState] object through the [param state] parameter. Takes a Godot Engine scene node and returns a [GLTFState] object through the [param state] parameter.
</description> </description>
@ -52,7 +49,8 @@
<method name="generate_scene"> <method name="generate_scene">
<return type="Node" /> <return type="Node" />
<param index="0" name="state" type="GLTFState" /> <param index="0" name="state" type="GLTFState" />
<param index="1" name="bake_fps" type="int" default="30" /> <param index="1" name="bake_fps" type="float" default="30" />
<param index="2" name="trimming" type="bool" default="false" />
<description> <description>
Takes a [GLTFState] object through the [param state] parameter and returns a Godot Engine scene node. Takes a [GLTFState] object through the [param state] parameter and returns a Godot Engine scene node.
</description> </description>

View file

@ -85,7 +85,7 @@ void SceneExporterGLTFPlugin::_gltf2_dialog_action(String p_file) {
state.instantiate(); state.instantiate();
int32_t flags = 0; int32_t flags = 0;
flags |= EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS; flags |= EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS;
Error err = doc->append_from_scene(root, state, flags, 30.0f); Error err = doc->append_from_scene(root, state, flags);
if (err != OK) { if (err != OK) {
ERR_PRINT(vformat("glTF2 save scene error %s.", itos(err))); ERR_PRINT(vformat("glTF2 save scene error %s.", itos(err)));
} }

View file

@ -59,7 +59,7 @@ void EditorSceneFormatImporterBlend::get_extensions(List<String> *r_extensions)
} }
Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_t p_flags, Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_t p_flags,
const HashMap<StringName, Variant> &p_options, int p_bake_fps, const HashMap<StringName, Variant> &p_options,
List<String> *r_missing_deps, Error *r_err) { List<String> *r_missing_deps, Error *r_err) {
// Get global paths for source and sink. // Get global paths for source and sink.
@ -228,14 +228,14 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_
if (p_options.has(SNAME("blender/materials/unpack_enabled")) && p_options[SNAME("blender/materials/unpack_enabled")]) { if (p_options.has(SNAME("blender/materials/unpack_enabled")) && p_options[SNAME("blender/materials/unpack_enabled")]) {
base_dir = sink.get_base_dir(); base_dir = sink.get_base_dir();
} }
Error err = gltf->append_from_file(sink.get_basename() + ".gltf", state, p_flags, p_bake_fps, base_dir); Error err = gltf->append_from_file(sink.get_basename() + ".gltf", state, p_flags, base_dir);
if (err != OK) { if (err != OK) {
if (r_err) { if (r_err) {
*r_err = FAILED; *r_err = FAILED;
} }
return nullptr; return nullptr;
} }
return gltf->generate_scene(state, p_bake_fps); return gltf->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"]);
} }
Variant EditorSceneFormatImporterBlend::get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option, Variant EditorSceneFormatImporterBlend::get_option_visibility(const String &p_path, bool p_for_animation, const String &p_option,

View file

@ -66,7 +66,7 @@ public:
virtual uint32_t get_import_flags() const override; virtual uint32_t get_import_flags() const override;
virtual void get_extensions(List<String> *r_extensions) const override; virtual void get_extensions(List<String> *r_extensions) const override;
virtual Node *import_scene(const String &p_path, uint32_t p_flags, virtual Node *import_scene(const String &p_path, uint32_t p_flags,
const HashMap<StringName, Variant> &p_options, int p_bake_fps, const HashMap<StringName, Variant> &p_options,
List<String> *r_missing_deps, Error *r_err = nullptr) override; List<String> *r_missing_deps, Error *r_err = nullptr) override;
virtual void get_import_options(const String &p_path, virtual void get_import_options(const String &p_path,
List<ResourceImporter::ImportOption> *r_options) override; List<ResourceImporter::ImportOption> *r_options) override;

View file

@ -49,7 +49,7 @@ void EditorSceneFormatImporterFBX::get_extensions(List<String> *r_extensions) co
} }
Node *EditorSceneFormatImporterFBX::import_scene(const String &p_path, uint32_t p_flags, Node *EditorSceneFormatImporterFBX::import_scene(const String &p_path, uint32_t p_flags,
const HashMap<StringName, Variant> &p_options, int p_bake_fps, const HashMap<StringName, Variant> &p_options,
List<String> *r_missing_deps, Error *r_err) { List<String> *r_missing_deps, Error *r_err) {
// Get global paths for source and sink. // Get global paths for source and sink.
@ -95,14 +95,14 @@ Node *EditorSceneFormatImporterFBX::import_scene(const String &p_path, uint32_t
Ref<GLTFState> state; Ref<GLTFState> state;
state.instantiate(); state.instantiate();
print_verbose(vformat("glTF path: %s", sink)); print_verbose(vformat("glTF path: %s", sink));
Error err = gltf->append_from_file(sink, state, p_flags, p_bake_fps); Error err = gltf->append_from_file(sink, state, p_flags);
if (err != OK) { if (err != OK) {
if (r_err) { if (r_err) {
*r_err = FAILED; *r_err = FAILED;
} }
return nullptr; return nullptr;
} }
return gltf->generate_scene(state, p_bake_fps); return gltf->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"]);
} }
Variant EditorSceneFormatImporterFBX::get_option_visibility(const String &p_path, bool p_for_animation, Variant EditorSceneFormatImporterFBX::get_option_visibility(const String &p_path, bool p_for_animation,

View file

@ -45,7 +45,7 @@ public:
virtual uint32_t get_import_flags() const override; virtual uint32_t get_import_flags() const override;
virtual void get_extensions(List<String> *r_extensions) const override; virtual void get_extensions(List<String> *r_extensions) const override;
virtual Node *import_scene(const String &p_path, uint32_t p_flags, virtual Node *import_scene(const String &p_path, uint32_t p_flags,
const HashMap<StringName, Variant> &p_options, int p_bake_fps, const HashMap<StringName, Variant> &p_options,
List<String> *r_missing_deps, Error *r_err = nullptr) override; List<String> *r_missing_deps, Error *r_err = nullptr) override;
virtual void get_import_options(const String &p_path, virtual void get_import_options(const String &p_path,
List<ResourceImporter::ImportOption> *r_options) override; List<ResourceImporter::ImportOption> *r_options) override;

View file

@ -47,13 +47,13 @@ void EditorSceneFormatImporterGLTF::get_extensions(List<String> *r_extensions) c
} }
Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t p_flags, Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t p_flags,
const HashMap<StringName, Variant> &p_options, int p_bake_fps, const HashMap<StringName, Variant> &p_options,
List<String> *r_missing_deps, Error *r_err) { List<String> *r_missing_deps, Error *r_err) {
Ref<GLTFDocument> doc; Ref<GLTFDocument> doc;
doc.instantiate(); doc.instantiate();
Ref<GLTFState> state; Ref<GLTFState> state;
state.instantiate(); state.instantiate();
Error err = doc->append_from_file(p_path, state, p_flags, p_bake_fps); Error err = doc->append_from_file(p_path, state, p_flags);
if (err != OK) { if (err != OK) {
if (r_err) { if (r_err) {
*r_err = err; *r_err = err;
@ -63,7 +63,7 @@ Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t
if (p_options.has("animation/import")) { if (p_options.has("animation/import")) {
state->set_create_animations(bool(p_options["animation/import"])); state->set_create_animations(bool(p_options["animation/import"]));
} }
return doc->generate_scene(state, p_bake_fps); return doc->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"]);
} }
#endif // TOOLS_ENABLED #endif // TOOLS_ENABLED

View file

@ -45,7 +45,7 @@ public:
virtual uint32_t get_import_flags() const override; virtual uint32_t get_import_flags() const override;
virtual void get_extensions(List<String> *r_extensions) const override; virtual void get_extensions(List<String> *r_extensions) const override;
virtual Node *import_scene(const String &p_path, uint32_t p_flags, virtual Node *import_scene(const String &p_path, uint32_t p_flags,
const HashMap<StringName, Variant> &p_options, int p_bake_fps, const HashMap<StringName, Variant> &p_options,
List<String> *r_missing_deps, Error *r_err = nullptr) override; List<String> *r_missing_deps, Error *r_err = nullptr) override;
}; };

View file

@ -5860,7 +5860,7 @@ T GLTFDocument::_interpolate_track(const Vector<real_t> &p_times, const Vector<T
ERR_FAIL_V(p_values[0]); ERR_FAIL_V(p_values[0]);
} }
void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap, const GLTFAnimationIndex index, const int bake_fps) { void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap, const GLTFAnimationIndex index, const float bake_fps, const bool trimming) {
Ref<GLTFAnimation> anim = state->animations[index]; Ref<GLTFAnimation> anim = state->animations[index];
String anim_name = anim->get_name(); String anim_name = anim->get_name();
@ -5877,7 +5877,8 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
animation->set_loop_mode(Animation::LOOP_LINEAR); animation->set_loop_mode(Animation::LOOP_LINEAR);
} }
float length = 0.0; double anim_start = trimming ? INFINITY : 0.0;
double anim_end = 0.0;
for (const KeyValue<int, GLTFAnimation::Track> &track_i : anim->get_tracks()) { for (const KeyValue<int, GLTFAnimation::Track> &track_i : anim->get_tracks()) {
const GLTFAnimation::Track &track = track_i.value; const GLTFAnimation::Track &track = track_i.value;
@ -5907,19 +5908,40 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
transform_node_path = node_path; transform_node_path = node_path;
} }
for (int i = 0; i < track.rotation_track.times.size(); i++) { if (trimming) {
length = MAX(length, track.rotation_track.times[i]); for (int i = 0; i < track.rotation_track.times.size(); i++) {
} anim_start = MIN(anim_start, track.rotation_track.times[i]);
for (int i = 0; i < track.position_track.times.size(); i++) { anim_end = MAX(anim_end, track.rotation_track.times[i]);
length = MAX(length, track.position_track.times[i]); }
} for (int i = 0; i < track.position_track.times.size(); i++) {
for (int i = 0; i < track.scale_track.times.size(); i++) { anim_start = MIN(anim_start, track.position_track.times[i]);
length = MAX(length, track.scale_track.times[i]); anim_end = MAX(anim_end, track.position_track.times[i]);
} }
for (int i = 0; i < track.scale_track.times.size(); i++) {
for (int i = 0; i < track.weight_tracks.size(); i++) { anim_start = MIN(anim_start, track.scale_track.times[i]);
for (int j = 0; j < track.weight_tracks[i].times.size(); j++) { anim_end = MAX(anim_end, track.scale_track.times[i]);
length = MAX(length, track.weight_tracks[i].times[j]); }
for (int i = 0; i < track.weight_tracks.size(); i++) {
for (int j = 0; j < track.weight_tracks[i].times.size(); j++) {
anim_start = MIN(anim_start, track.weight_tracks[i].times[j]);
anim_end = MAX(anim_end, track.weight_tracks[i].times[j]);
}
}
} else {
// If you don't use trimming and the first key time is not at 0.0, fake keys will be inserted.
for (int i = 0; i < track.rotation_track.times.size(); i++) {
anim_end = MAX(anim_end, track.rotation_track.times[i]);
}
for (int i = 0; i < track.position_track.times.size(); i++) {
anim_end = MAX(anim_end, track.position_track.times[i]);
}
for (int i = 0; i < track.scale_track.times.size(); i++) {
anim_end = MAX(anim_end, track.scale_track.times[i]);
}
for (int i = 0; i < track.weight_tracks.size(); i++) {
for (int j = 0; j < track.weight_tracks[i].times.size(); j++) {
anim_end = MAX(anim_end, track.weight_tracks[i].times[j]);
}
} }
} }
@ -5988,10 +6010,8 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
} }
} }
//first determine animation length
const double increment = 1.0 / bake_fps; const double increment = 1.0 / bake_fps;
double time = 0.0; double time = anim_start;
Vector3 base_pos; Vector3 base_pos;
Quaternion base_rot; Quaternion base_rot;
@ -6017,26 +6037,26 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
if (position_idx >= 0) { if (position_idx >= 0) {
pos = _interpolate_track<Vector3>(track.position_track.times, track.position_track.values, time, track.position_track.interpolation); pos = _interpolate_track<Vector3>(track.position_track.times, track.position_track.values, time, track.position_track.interpolation);
animation->position_track_insert_key(position_idx, time, pos); animation->position_track_insert_key(position_idx, time - anim_start, pos);
} }
if (rotation_idx >= 0) { if (rotation_idx >= 0) {
rot = _interpolate_track<Quaternion>(track.rotation_track.times, track.rotation_track.values, time, track.rotation_track.interpolation); rot = _interpolate_track<Quaternion>(track.rotation_track.times, track.rotation_track.values, time, track.rotation_track.interpolation);
animation->rotation_track_insert_key(rotation_idx, time, rot); animation->rotation_track_insert_key(rotation_idx, time - anim_start, rot);
} }
if (scale_idx >= 0) { if (scale_idx >= 0) {
scale = _interpolate_track<Vector3>(track.scale_track.times, track.scale_track.values, time, track.scale_track.interpolation); scale = _interpolate_track<Vector3>(track.scale_track.times, track.scale_track.values, time, track.scale_track.interpolation);
animation->scale_track_insert_key(scale_idx, time, scale); animation->scale_track_insert_key(scale_idx, time - anim_start, scale);
} }
if (last) { if (last) {
break; break;
} }
time += increment; time += increment;
if (time >= length) { if (time >= anim_end) {
last = true; last = true;
time = length; time = anim_end;
} }
} }
} }
@ -6071,21 +6091,21 @@ void GLTFDocument::_import_animation(Ref<GLTFState> state, AnimationPlayer *ap,
bool last = false; bool last = false;
while (true) { while (true) {
real_t blend = _interpolate_track<real_t>(track.weight_tracks[i].times, track.weight_tracks[i].values, time, gltf_interp); real_t blend = _interpolate_track<real_t>(track.weight_tracks[i].times, track.weight_tracks[i].values, time, gltf_interp);
animation->blend_shape_track_insert_key(track_idx, time, blend); animation->blend_shape_track_insert_key(track_idx, time - anim_start, blend);
if (last) { if (last) {
break; break;
} }
time += increment; time += increment;
if (time >= length) { if (time >= anim_end) {
last = true; last = true;
time = length; time = anim_end;
} }
} }
} }
} }
} }
animation->set_length(length); animation->set_length(anim_end - anim_start);
Ref<AnimationLibrary> library; Ref<AnimationLibrary> library;
if (!ap->has_animation_library("")) { if (!ap->has_animation_library("")) {
@ -6585,7 +6605,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
} }
} }
Error GLTFDocument::_parse(Ref<GLTFState> state, String p_path, Ref<FileAccess> f, int p_bake_fps) { Error GLTFDocument::_parse(Ref<GLTFState> state, String p_path, Ref<FileAccess> f) {
Error err; Error err;
if (f.is_null()) { if (f.is_null()) {
return FAILED; return FAILED;
@ -6636,7 +6656,7 @@ Error GLTFDocument::_parse(Ref<GLTFState> state, String p_path, Ref<FileAccess>
} }
} }
err = _parse_gltf_state(state, p_path, p_bake_fps); err = _parse_gltf_state(state, p_path);
ERR_FAIL_COND_V(err != OK, err); ERR_FAIL_COND_V(err != OK, err);
return OK; return OK;
@ -6754,14 +6774,14 @@ Error GLTFDocument::_serialize_file(Ref<GLTFState> state, const String p_path) {
} }
void GLTFDocument::_bind_methods() { void GLTFDocument::_bind_methods() {
ClassDB::bind_method(D_METHOD("append_from_file", "path", "state", "flags", "bake_fps", "base_path"), ClassDB::bind_method(D_METHOD("append_from_file", "path", "state", "flags", "base_path"),
&GLTFDocument::append_from_file, DEFVAL(0), DEFVAL(30), DEFVAL(String())); &GLTFDocument::append_from_file, DEFVAL(0), DEFVAL(String()));
ClassDB::bind_method(D_METHOD("append_from_buffer", "bytes", "base_path", "state", "flags", "bake_fps"), ClassDB::bind_method(D_METHOD("append_from_buffer", "bytes", "base_path", "state", "flags"),
&GLTFDocument::append_from_buffer, DEFVAL(0), DEFVAL(30)); &GLTFDocument::append_from_buffer, DEFVAL(0));
ClassDB::bind_method(D_METHOD("append_from_scene", "node", "state", "flags", "bake_fps"), ClassDB::bind_method(D_METHOD("append_from_scene", "node", "state", "flags"),
&GLTFDocument::append_from_scene, DEFVAL(0), DEFVAL(30)); &GLTFDocument::append_from_scene, DEFVAL(0));
ClassDB::bind_method(D_METHOD("generate_scene", "state", "bake_fps"), ClassDB::bind_method(D_METHOD("generate_scene", "state", "bake_fps", "trimming"),
&GLTFDocument::generate_scene, DEFVAL(30)); &GLTFDocument::generate_scene, DEFVAL(30), DEFVAL(false));
ClassDB::bind_method(D_METHOD("generate_buffer", "state"), ClassDB::bind_method(D_METHOD("generate_buffer", "state"),
&GLTFDocument::generate_buffer); &GLTFDocument::generate_buffer);
ClassDB::bind_method(D_METHOD("write_to_filesystem", "state", "path"), ClassDB::bind_method(D_METHOD("write_to_filesystem", "state", "path"),
@ -6870,7 +6890,7 @@ Error GLTFDocument::write_to_filesystem(Ref<GLTFState> state, const String &p_pa
return OK; return OK;
} }
Node *GLTFDocument::generate_scene(Ref<GLTFState> state, int32_t p_bake_fps) { Node *GLTFDocument::generate_scene(Ref<GLTFState> state, float p_bake_fps, bool p_trimming) {
ERR_FAIL_NULL_V(state, nullptr); ERR_FAIL_NULL_V(state, nullptr);
ERR_FAIL_INDEX_V(0, state->root_nodes.size(), nullptr); ERR_FAIL_INDEX_V(0, state->root_nodes.size(), nullptr);
Error err = OK; Error err = OK;
@ -6884,7 +6904,7 @@ Node *GLTFDocument::generate_scene(Ref<GLTFState> state, int32_t p_bake_fps) {
root->add_child(ap, true); root->add_child(ap, true);
ap->set_owner(root); ap->set_owner(root);
for (int i = 0; i < state->animations.size(); i++) { for (int i = 0; i < state->animations.size(); i++) {
_import_animation(state, ap, i, p_bake_fps); _import_animation(state, ap, i, p_bake_fps, p_trimming);
} }
} }
for (KeyValue<GLTFNodeIndex, Node *> E : state->scene_nodes) { for (KeyValue<GLTFNodeIndex, Node *> E : state->scene_nodes) {
@ -6910,7 +6930,7 @@ Node *GLTFDocument::generate_scene(Ref<GLTFState> state, int32_t p_bake_fps) {
return root; return root;
} }
Error GLTFDocument::append_from_scene(Node *p_node, Ref<GLTFState> state, uint32_t p_flags, int32_t p_bake_fps) { Error GLTFDocument::append_from_scene(Node *p_node, Ref<GLTFState> state, uint32_t p_flags) {
ERR_FAIL_COND_V(state.is_null(), FAILED); ERR_FAIL_COND_V(state.is_null(), FAILED);
state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS; state->use_named_skin_binds = p_flags & GLTF_IMPORT_USE_NAMED_SKIN_BINDS;
state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS; state->discard_meshes_and_materials = p_flags & GLTF_IMPORT_DISCARD_MESHES_AND_MATERIALS;
@ -6930,7 +6950,7 @@ Error GLTFDocument::append_from_scene(Node *p_node, Ref<GLTFState> state, uint32
return OK; return OK;
} }
Error GLTFDocument::append_from_buffer(PackedByteArray p_bytes, String p_base_path, Ref<GLTFState> state, uint32_t p_flags, int32_t p_bake_fps) { Error GLTFDocument::append_from_buffer(PackedByteArray p_bytes, String p_base_path, Ref<GLTFState> state, uint32_t p_flags) {
ERR_FAIL_COND_V(state.is_null(), FAILED); ERR_FAIL_COND_V(state.is_null(), FAILED);
// TODO Add missing texture and missing .bin file paths to r_missing_deps 2021-09-10 fire // TODO Add missing texture and missing .bin file paths to r_missing_deps 2021-09-10 fire
Error err = FAILED; Error err = FAILED;
@ -6941,7 +6961,7 @@ Error GLTFDocument::append_from_buffer(PackedByteArray p_bytes, String p_base_pa
file_access.instantiate(); file_access.instantiate();
file_access->open_custom(p_bytes.ptr(), p_bytes.size()); file_access->open_custom(p_bytes.ptr(), p_bytes.size());
state->base_path = p_base_path.get_base_dir(); state->base_path = p_base_path.get_base_dir();
err = _parse(state, state->base_path, file_access, p_bake_fps); err = _parse(state, state->base_path, file_access);
ERR_FAIL_COND_V(err != OK, err); ERR_FAIL_COND_V(err != OK, err);
for (Ref<GLTFDocumentExtension> ext : document_extensions) { for (Ref<GLTFDocumentExtension> ext : document_extensions) {
ERR_CONTINUE(ext.is_null()); ERR_CONTINUE(ext.is_null());
@ -6951,7 +6971,7 @@ Error GLTFDocument::append_from_buffer(PackedByteArray p_bytes, String p_base_pa
return OK; return OK;
} }
Error GLTFDocument::_parse_gltf_state(Ref<GLTFState> state, const String &p_search_path, float p_bake_fps) { Error GLTFDocument::_parse_gltf_state(Ref<GLTFState> state, const String &p_search_path) {
Error err; Error err;
/* PARSE EXTENSIONS */ /* PARSE EXTENSIONS */
@ -7047,7 +7067,7 @@ Error GLTFDocument::_parse_gltf_state(Ref<GLTFState> state, const String &p_sear
return OK; return OK;
} }
Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> r_state, uint32_t p_flags, int32_t p_bake_fps, String p_base_path) { Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> r_state, uint32_t p_flags, String p_base_path) {
// TODO Add missing texture and missing .bin file paths to r_missing_deps 2021-09-10 fire // TODO Add missing texture and missing .bin file paths to r_missing_deps 2021-09-10 fire
if (r_state == Ref<GLTFState>()) { if (r_state == Ref<GLTFState>()) {
r_state.instantiate(); r_state.instantiate();
@ -7064,7 +7084,7 @@ Error GLTFDocument::append_from_file(String p_path, Ref<GLTFState> r_state, uint
base_path = p_path.get_base_dir(); base_path = p_path.get_base_dir();
} }
r_state->base_path = base_path; r_state->base_path = base_path;
err = _parse(r_state, base_path, f, p_bake_fps); err = _parse(r_state, base_path, f);
ERR_FAIL_COND_V(err != OK, err); ERR_FAIL_COND_V(err != OK, err);
for (Ref<GLTFDocumentExtension> ext : document_extensions) { for (Ref<GLTFDocumentExtension> ext : document_extensions) {
ERR_CONTINUE(ext.is_null()); ERR_CONTINUE(ext.is_null());

View file

@ -292,17 +292,17 @@ private:
static float get_max_component(const Color &p_color); static float get_max_component(const Color &p_color);
public: public:
Error append_from_file(String p_path, Ref<GLTFState> r_state, uint32_t p_flags = 0, int32_t p_bake_fps = 30, String p_base_path = String()); Error append_from_file(String p_path, Ref<GLTFState> r_state, uint32_t p_flags = 0, String p_base_path = String());
Error append_from_buffer(PackedByteArray p_bytes, String p_base_path, Ref<GLTFState> r_state, uint32_t p_flags = 0, int32_t p_bake_fps = 30); Error append_from_buffer(PackedByteArray p_bytes, String p_base_path, Ref<GLTFState> r_state, uint32_t p_flags = 0);
Error append_from_scene(Node *p_node, Ref<GLTFState> r_state, uint32_t p_flags = 0, int32_t p_bake_fps = 30); Error append_from_scene(Node *p_node, Ref<GLTFState> r_state, uint32_t p_flags = 0);
public: public:
Node *generate_scene(Ref<GLTFState> state, int32_t p_bake_fps = 30.0f); Node *generate_scene(Ref<GLTFState> state, float p_bake_fps = 30.0f, bool p_trimming = false);
PackedByteArray generate_buffer(Ref<GLTFState> state); PackedByteArray generate_buffer(Ref<GLTFState> state);
Error write_to_filesystem(Ref<GLTFState> state, const String &p_path); Error write_to_filesystem(Ref<GLTFState> state, const String &p_path);
public: public:
Error _parse_gltf_state(Ref<GLTFState> state, const String &p_search_path, float p_bake_fps); Error _parse_gltf_state(Ref<GLTFState> state, const String &p_search_path);
Error _parse_gltf_extensions(Ref<GLTFState> state); Error _parse_gltf_extensions(Ref<GLTFState> state);
void _process_mesh_instances(Ref<GLTFState> state, Node *scene_root); void _process_mesh_instances(Ref<GLTFState> state, Node *scene_root);
void _generate_scene_node(Ref<GLTFState> state, Node *scene_parent, void _generate_scene_node(Ref<GLTFState> state, Node *scene_parent,
@ -310,7 +310,7 @@ public:
const GLTFNodeIndex node_index); const GLTFNodeIndex node_index);
void _generate_skeleton_bone_node(Ref<GLTFState> state, Node *scene_parent, Node3D *scene_root, const GLTFNodeIndex node_index); void _generate_skeleton_bone_node(Ref<GLTFState> state, Node *scene_parent, Node3D *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 float bake_fps, const bool trimming);
void _convert_mesh_instances(Ref<GLTFState> state); void _convert_mesh_instances(Ref<GLTFState> state);
GLTFCameraIndex _convert_camera(Ref<GLTFState> state, Camera3D *p_camera); GLTFCameraIndex _convert_camera(Ref<GLTFState> state, Camera3D *p_camera);
void _convert_light_to_gltf(Light3D *light, Ref<GLTFState> state, Ref<GLTFNode> gltf_node); void _convert_light_to_gltf(Light3D *light, Ref<GLTFState> state, Ref<GLTFNode> gltf_node);
@ -368,7 +368,7 @@ public:
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, const String &p_path); Error _serialize(Ref<GLTFState> state, const String &p_path);
Error _parse(Ref<GLTFState> state, String p_path, Ref<FileAccess> f, int p_bake_fps); Error _parse(Ref<GLTFState> state, String p_path, Ref<FileAccess> f);
}; };
#endif // GLTF_DOCUMENT_H #endif // GLTF_DOCUMENT_H