Add bake_fps for FBXDocument, GLTFDocument and both import-export.

This commit is contained in:
K. S. Ernest (iFire) Lee 2024-05-23 08:40:26 -07:00
parent b947c53ddc
commit 24f56008ac
13 changed files with 76 additions and 35 deletions

View file

@ -80,6 +80,7 @@ Node *EditorSceneFormatImporterUFBX::import_scene(const String &p_path, uint32_t
state->set_import_as_skeleton_bones(true);
}
p_flags |= EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS;
state->set_bake_fps(p_options["animation/fps"]);
Error err = fbx->append_from_file(path, state, p_flags, p_path.get_base_dir());
if (err != OK) {
if (r_err) {
@ -87,7 +88,7 @@ Node *EditorSceneFormatImporterUFBX::import_scene(const String &p_path, uint32_t
}
return nullptr;
}
return fbx->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"], false);
return fbx->generate_scene(state, state->get_bake_fps(), (bool)p_options["animation/trimming"], false);
}
Variant EditorSceneFormatImporterUFBX::get_option_visibility(const String &p_path, bool p_for_animation,

View file

@ -1381,6 +1381,10 @@ Error FBXDocument::_parse_animations(Ref<FBXState> p_state) {
additional_data["time_end"] = fbx_anim_stack->time_end;
animation->set_additional_data("GODOT_animation_time_begin_time_end", additional_data);
ufbx_bake_opts opts = {};
opts.resample_rate = p_state->get_bake_fps();
opts.minimum_sample_rate = p_state->get_bake_fps();
opts.max_keyframe_segments = 1024;
ufbx_error error;
ufbx_unique_ptr<ufbx_baked_anim> fbx_baked_anim{ ufbx_bake_anim(fbx_scene, fbx_anim_stack->anim, &opts, &error) };
if (!fbx_baked_anim) {
@ -1759,7 +1763,7 @@ void FBXDocument::_generate_skeleton_bone_node(Ref<FBXState> p_state, const GLTF
}
}
void FBXDocument::_import_animation(Ref<FBXState> p_state, AnimationPlayer *p_animation_player, const GLTFAnimationIndex p_index, const float p_bake_fps, const bool p_trimming, const bool p_remove_immutable_tracks) {
void FBXDocument::_import_animation(Ref<FBXState> p_state, AnimationPlayer *p_animation_player, const GLTFAnimationIndex p_index, const bool p_trimming, const bool p_remove_immutable_tracks) {
Ref<GLTFAnimation> anim = p_state->animations[p_index];
String anim_name = anim->get_name();
@ -1771,7 +1775,7 @@ void FBXDocument::_import_animation(Ref<FBXState> p_state, AnimationPlayer *p_an
Ref<Animation> animation;
animation.instantiate();
animation->set_name(anim_name);
animation->set_step(1.0 / p_bake_fps);
animation->set_step(1.0 / p_state->get_bake_fps());
if (anim->get_loop()) {
animation->set_loop_mode(Animation::LOOP_LINEAR);
@ -2118,6 +2122,7 @@ Node *FBXDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, bool
ERR_FAIL_COND_V(state.is_null(), nullptr);
ERR_FAIL_NULL_V(state, nullptr);
ERR_FAIL_INDEX_V(0, state->root_nodes.size(), nullptr);
p_state->set_bake_fps(p_bake_fps);
GLTFNodeIndex fbx_root = state->root_nodes.write[0];
Node *fbx_root_node = state->get_scene_node(fbx_root);
Node *root = fbx_root_node;
@ -2131,7 +2136,7 @@ Node *FBXDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, bool
root->add_child(ap, true);
ap->set_owner(root);
for (int i = 0; i < state->animations.size(); i++) {
_import_animation(state, ap, i, p_bake_fps, p_trimming, p_remove_immutable_tracks);
_import_animation(state, ap, i, p_trimming, p_remove_immutable_tracks);
}
}
ERR_FAIL_NULL_V(root, nullptr);

View file

@ -99,7 +99,7 @@ public:
void _generate_scene_node(Ref<FBXState> p_state, const GLTFNodeIndex p_node_index, Node *p_scene_parent, Node *p_scene_root);
void _generate_skeleton_bone_node(Ref<FBXState> p_state, const GLTFNodeIndex p_node_index, Node *p_scene_parent, Node *p_scene_root);
void _import_animation(Ref<FBXState> p_state, AnimationPlayer *p_animation_player,
const GLTFAnimationIndex p_index, const float p_bake_fps, const bool p_trimming, const bool p_remove_immutable_tracks);
const GLTFAnimationIndex p_index, const bool p_trimming, const bool p_remove_immutable_tracks);
Error _parse(Ref<FBXState> p_state, String p_path, Ref<FileAccess> p_file);
};

View file

@ -60,6 +60,7 @@
<param index="3" name="remove_immutable_tracks" type="bool" default="true" />
<description>
Takes a [GLTFState] object through the [param state] parameter and returns a Godot Engine scene node.
The [param bake_fps] parameter overrides the bake_fps in [param state].
</description>
</method>
<method name="register_gltf_document_extension" qualifiers="static">

View file

@ -275,6 +275,9 @@
</method>
</methods>
<members>
<member name="bake_fps" type="float" setter="set_bake_fps" getter="get_bake_fps" default="30.0">
The baking fps of the animation for either import or export.
</member>
<member name="base_path" type="String" setter="set_base_path" getter="get_base_path" default="&quot;&quot;">
The folder path associated with this GLTF data. This is used to find other files the GLTF file references, like images or binary buffers. This will be set during import when appending from a file, and will be set during export when writing to a file.
</member>

View file

@ -107,6 +107,7 @@ void SceneExporterGLTFPlugin::_export_scene_as_gltf(const String &p_file_path) {
state->set_copyright(_export_settings->get_copyright());
int32_t flags = 0;
flags |= EditorSceneFormatImporter::IMPORT_USE_NAMED_SKIN_BINDS;
state->set_bake_fps(_export_settings->get_bake_fps());
Error err = _gltf_document->append_from_scene(root, state, flags);
if (err != OK) {
ERR_PRINT(vformat("glTF2 save scene error %s.", itos(err)));

View file

@ -182,4 +182,16 @@ void EditorSceneExporterGLTFSettings::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_copyright"), &EditorSceneExporterGLTFSettings::get_copyright);
ClassDB::bind_method(D_METHOD("set_copyright", "copyright"), &EditorSceneExporterGLTFSettings::set_copyright);
ADD_PROPERTY(PropertyInfo(Variant::STRING, "copyright", PROPERTY_HINT_PLACEHOLDER_TEXT, "Example: 2014 Godette"), "set_copyright", "get_copyright");
ClassDB::bind_method(D_METHOD("get_bake_fps"), &EditorSceneExporterGLTFSettings::get_bake_fps);
ClassDB::bind_method(D_METHOD("set_bake_fps", "bake_fps"), &EditorSceneExporterGLTFSettings::set_bake_fps);
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_fps"), "set_bake_fps", "get_bake_fps");
}
double EditorSceneExporterGLTFSettings::get_bake_fps() const {
return _bake_fps;
}
void EditorSceneExporterGLTFSettings::set_bake_fps(const double p_bake_fps) {
_bake_fps = p_bake_fps;
}

View file

@ -42,6 +42,7 @@ class EditorSceneExporterGLTFSettings : public RefCounted {
HashMap<String, Ref<GLTFDocumentExtension>> _config_name_to_extension_map;
String _copyright;
double _bake_fps = 30.0;
protected:
static void _bind_methods();
@ -58,6 +59,9 @@ public:
String get_copyright() const;
void set_copyright(const String &p_copyright);
double get_bake_fps() const;
void set_bake_fps(const double p_bake_fps);
};
#endif // TOOLS_ENABLED

View file

@ -62,6 +62,7 @@ Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t
if (p_options.has(SNAME("nodes/import_as_skeleton_bones")) ? (bool)p_options[SNAME("nodes/import_as_skeleton_bones")] : false) {
state->set_import_as_skeleton_bones(true);
}
state->set_bake_fps(p_options["animation/fps"]);
Error err = gltf->append_from_file(p_path, state, p_flags);
if (err != OK) {
if (r_err) {
@ -75,9 +76,9 @@ Node *EditorSceneFormatImporterGLTF::import_scene(const String &p_path, uint32_t
#ifndef DISABLE_DEPRECATED
bool trimming = p_options.has("animation/trimming") ? (bool)p_options["animation/trimming"] : false;
return gltf->generate_scene(state, (float)p_options["animation/fps"], trimming, false);
return gltf->generate_scene(state, state->get_bake_fps(), trimming, false);
#else
return gltf->generate_scene(state, (float)p_options["animation/fps"], (bool)p_options["animation/trimming"], false);
return gltf->generate_scene(state, state->get_bake_fps(), (bool)p_options["animation/trimming"], false);
#endif
}

View file

@ -4860,7 +4860,7 @@ Error GLTFDocument::_serialize_animations(Ref<GLTFState> p_state) {
t["sampler"] = samplers.size();
Dictionary s;
Vector<real_t> times;
const double increment = 1.0 / BAKE_FPS;
const double increment = 1.0 / p_state->get_bake_fps();
{
double time = 0.0;
bool last = false;
@ -5902,7 +5902,8 @@ T GLTFDocument::_interpolate_track(const Vector<real_t> &p_times, const Vector<T
ERR_FAIL_V(p_values[0]);
}
void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_animation_player, const GLTFAnimationIndex p_index, const float p_bake_fps, const bool p_trimming, const bool p_remove_immutable_tracks) {
void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_animation_player, const GLTFAnimationIndex p_index, const bool p_trimming, const bool p_remove_immutable_tracks) {
ERR_FAIL_COND(p_state.is_null());
Ref<GLTFAnimation> anim = p_state->animations[p_index];
String anim_name = anim->get_name();
@ -5914,7 +5915,7 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_
Ref<Animation> animation;
animation.instantiate();
animation->set_name(anim_name);
animation->set_step(1.0 / p_bake_fps);
animation->set_step(1.0 / p_state->get_bake_fps());
if (anim->get_loop()) {
animation->set_loop_mode(Animation::LOOP_LINEAR);
@ -6081,7 +6082,7 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_
}
}
const double increment = 1.0 / p_bake_fps;
const double increment = 1.0 / p_state->get_bake_fps();
double time = anim_start;
Vector3 base_pos;
@ -6158,7 +6159,7 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_
}
} else {
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
const double increment = 1.0 / p_bake_fps;
const double increment = 1.0 / p_state->get_bake_fps();
double time = 0.0;
bool last = false;
while (true) {
@ -6372,7 +6373,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
p_track.scale_track.times.clear();
p_track.scale_track.values.clear();
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
const double increment = 1.0 / BAKE_FPS;
const double increment = 1.0 / p_state->get_bake_fps();
double time = 0.0;
bool last = false;
while (true) {
@ -6407,7 +6408,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
p_track.position_track.times.clear();
p_track.position_track.values.clear();
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
const double increment = 1.0 / BAKE_FPS;
const double increment = 1.0 / p_state->get_bake_fps();
double time = 0.0;
bool last = false;
while (true) {
@ -6442,7 +6443,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
p_track.rotation_track.times.clear();
p_track.rotation_track.values.clear();
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
const double increment = 1.0 / BAKE_FPS;
const double increment = 1.0 / p_state->get_bake_fps();
double time = 0.0;
bool last = false;
while (true) {
@ -6482,7 +6483,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
p_track.position_track.times.clear();
p_track.position_track.values.clear();
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
const double increment = 1.0 / BAKE_FPS;
const double increment = 1.0 / p_state->get_bake_fps();
double time = 0.0;
bool last = false;
while (true) {
@ -6515,7 +6516,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
p_track.rotation_track.times.clear();
p_track.rotation_track.values.clear();
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
const double increment = 1.0 / BAKE_FPS;
const double increment = 1.0 / p_state->get_bake_fps();
double time = 0.0;
bool last = false;
while (true) {
@ -6551,7 +6552,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
p_track.scale_track.times.clear();
p_track.scale_track.values.clear();
// CATMULLROMSPLINE or CUBIC_SPLINE have to be baked, apologies.
const double increment = 1.0 / BAKE_FPS;
const double increment = 1.0 / p_state->get_bake_fps();
double time = 0.0;
bool last = false;
while (true) {
@ -6577,14 +6578,14 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
}
}
} else if (track_type == Animation::TYPE_BEZIER) {
const int32_t keys = anim_end * BAKE_FPS;
const int32_t keys = anim_end * p_state->get_bake_fps();
if (path.contains(":scale")) {
if (!p_track.scale_track.times.size()) {
p_track.scale_track.interpolation = gltf_interpolation;
Vector<real_t> new_times;
new_times.resize(keys);
for (int32_t key_i = 0; key_i < keys; key_i++) {
new_times.write[key_i] = key_i / BAKE_FPS;
new_times.write[key_i] = key_i / p_state->get_bake_fps();
}
p_track.scale_track.times = new_times;
@ -6597,11 +6598,11 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
for (int32_t key_i = 0; key_i < keys; key_i++) {
Vector3 bezier_track = p_track.scale_track.values[key_i];
if (path.contains(":scale:x")) {
bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
} else if (path.contains(":scale:y")) {
bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
} else if (path.contains(":scale:z")) {
bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
}
p_track.scale_track.values.write[key_i] = bezier_track;
}
@ -6612,7 +6613,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
Vector<real_t> new_times;
new_times.resize(keys);
for (int32_t key_i = 0; key_i < keys; key_i++) {
new_times.write[key_i] = key_i / BAKE_FPS;
new_times.write[key_i] = key_i / p_state->get_bake_fps();
}
p_track.position_track.times = new_times;
@ -6622,11 +6623,11 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
for (int32_t key_i = 0; key_i < keys; key_i++) {
Vector3 bezier_track = p_track.position_track.values[key_i];
if (path.contains(":position:x")) {
bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
} else if (path.contains(":position:y")) {
bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
} else if (path.contains(":position:z")) {
bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
}
p_track.position_track.values.write[key_i] = bezier_track;
}
@ -6636,7 +6637,7 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
Vector<real_t> new_times;
new_times.resize(keys);
for (int32_t key_i = 0; key_i < keys; key_i++) {
new_times.write[key_i] = key_i / BAKE_FPS;
new_times.write[key_i] = key_i / p_state->get_bake_fps();
}
p_track.rotation_track.times = new_times;
@ -6645,13 +6646,13 @@ GLTFAnimation::Track GLTFDocument::_convert_animation_track(Ref<GLTFState> p_sta
for (int32_t key_i = 0; key_i < keys; key_i++) {
Quaternion bezier_track = p_track.rotation_track.values[key_i];
if (path.contains(":rotation:x")) {
bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
bezier_track.x = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
} else if (path.contains(":rotation:y")) {
bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
bezier_track.y = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
} else if (path.contains(":rotation:z")) {
bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
bezier_track.z = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
} else if (path.contains(":rotation:w")) {
bezier_track.w = p_animation->bezier_track_interpolate(p_track_i, key_i / BAKE_FPS);
bezier_track.w = p_animation->bezier_track_interpolate(p_track_i, key_i / p_state->get_bake_fps());
}
p_track.rotation_track.values.write[key_i] = bezier_track;
}
@ -7305,6 +7306,7 @@ Node *GLTFDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, boo
ERR_FAIL_NULL_V(state, nullptr);
ERR_FAIL_INDEX_V(0, state->root_nodes.size(), nullptr);
Error err = OK;
p_state->set_bake_fps(p_bake_fps);
Node *root = _generate_scene_node_tree(state);
ERR_FAIL_NULL_V(root, nullptr);
_process_mesh_instances(state, root);
@ -7313,7 +7315,7 @@ Node *GLTFDocument::generate_scene(Ref<GLTFState> p_state, float p_bake_fps, boo
root->add_child(ap, true);
ap->set_owner(root);
for (int i = 0; i < state->animations.size(); i++) {
_import_animation(state, ap, i, p_bake_fps, p_trimming, p_remove_immutable_tracks);
_import_animation(state, ap, i, p_trimming, p_remove_immutable_tracks);
}
}
for (KeyValue<GLTFNodeIndex, Node *> E : state->scene_nodes) {

View file

@ -75,7 +75,6 @@ public:
};
private:
const float BAKE_FPS = 30.0f;
int _naming_version = 1;
String _image_format = "PNG";
float _lossy_quality = 0.75f;
@ -328,7 +327,7 @@ public:
void _generate_scene_node(Ref<GLTFState> p_state, const GLTFNodeIndex p_node_index, Node *p_scene_parent, Node *p_scene_root);
void _generate_skeleton_bone_node(Ref<GLTFState> p_state, const GLTFNodeIndex p_node_index, Node *p_scene_parent, Node *p_scene_root);
void _import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_animation_player,
const GLTFAnimationIndex p_index, const float p_bake_fps, const bool p_trimming, const bool p_remove_immutable_tracks);
const GLTFAnimationIndex p_index, const bool p_trimming, const bool p_remove_immutable_tracks);
void _convert_mesh_instances(Ref<GLTFState> p_state);
GLTFCameraIndex _convert_camera(Ref<GLTFState> p_state, Camera3D *p_camera);
void _convert_light_to_gltf(Light3D *p_light, Ref<GLTFState> p_state, Ref<GLTFNode> p_gltf_node);

View file

@ -100,6 +100,8 @@ void GLTFState::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_additional_data", "extension_name", "additional_data"), &GLTFState::set_additional_data);
ClassDB::bind_method(D_METHOD("get_handle_binary_image"), &GLTFState::get_handle_binary_image);
ClassDB::bind_method(D_METHOD("set_handle_binary_image", "method"), &GLTFState::set_handle_binary_image);
ClassDB::bind_method(D_METHOD("set_bake_fps", "value"), &GLTFState::set_bake_fps);
ClassDB::bind_method(D_METHOD("get_bake_fps"), &GLTFState::get_bake_fps);
ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "json"), "set_json", "get_json"); // Dictionary
ADD_PROPERTY(PropertyInfo(Variant::INT, "major_version"), "set_major_version", "get_major_version"); // int
@ -130,6 +132,7 @@ void GLTFState::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "import_as_skeleton_bones"), "set_import_as_skeleton_bones", "get_import_as_skeleton_bones"); // bool
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "animations", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_animations", "get_animations"); // Vector<Ref<GLTFAnimation>>
ADD_PROPERTY(PropertyInfo(Variant::INT, "handle_binary_image", PROPERTY_HINT_ENUM, "Discard All Textures,Extract Textures,Embed as Basis Universal,Embed as Uncompressed", PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_EDITOR), "set_handle_binary_image", "get_handle_binary_image"); // enum
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bake_fps"), "set_bake_fps", "get_bake_fps");
BIND_CONSTANT(HANDLE_BINARY_DISCARD_TEXTURES);
BIND_CONSTANT(HANDLE_BINARY_EXTRACT_TEXTURES);

View file

@ -57,6 +57,7 @@ protected:
int minor_version = 0;
String copyright;
Vector<uint8_t> glb_data;
double bake_fps = 30.0;
bool use_named_skin_binds = false;
bool use_khr_texture_transform = false;
@ -108,6 +109,14 @@ protected:
static void _bind_methods();
public:
double get_bake_fps() const {
return bake_fps;
}
void set_bake_fps(double value) {
bake_fps = value;
}
void add_used_extension(const String &p_extension, bool p_required = false);
GLTFBufferViewIndex append_data_to_buffers(const Vector<uint8_t> &p_data, const bool p_deduplication);