mirror of
https://github.com/Relintai/pandemonium_engine.git
synced 2024-12-27 06:07:14 +01:00
Backported from Godot 4: Fixed animation insertion in SkeletonEditor - TokageItLab
653e2a550c
This commit is contained in:
parent
2bc7355910
commit
e3918692a3
@ -205,6 +205,7 @@
|
||||
<method name="find_track" qualifiers="const">
|
||||
<return type="int" />
|
||||
<argument index="0" name="path" type="NodePath" />
|
||||
<argument index="1" name="type" type="int" enum="Animation.TrackType" />
|
||||
<description>
|
||||
Returns the index of the specified track. If the track is not found, return -1.
|
||||
</description>
|
||||
|
@ -3494,7 +3494,7 @@ void AnimationTrackEditor::_query_insert(const InsertData &p_id) {
|
||||
|
||||
for (List<InsertData>::Element *E = insert_data.front(); E; E = E->next()) {
|
||||
//prevent insertion of multiple tracks
|
||||
if (E->get().path == p_id.path) {
|
||||
if (E->get().path == p_id.path && E->get().type == p_id.type) {
|
||||
return; //already inserted a track for this on this frame
|
||||
}
|
||||
}
|
||||
@ -3623,7 +3623,11 @@ void AnimationTrackEditor::_insert_delay(bool p_create_reset, bool p_create_bezi
|
||||
insert_queue = false;
|
||||
}
|
||||
|
||||
void AnimationTrackEditor::insert_transform_key(Spatial *p_node, const String &p_sub, const Transform &p_xform) {
|
||||
void AnimationTrackEditor::insert_transform_key(Spatial *p_node, const String &p_sub, const Animation::TrackType p_type, const Variant p_value) {
|
||||
ERR_FAIL_COND(!root);
|
||||
ERR_FAIL_COND_MSG(
|
||||
(p_type != Animation::TYPE_POSITION_3D && p_type != Animation::TYPE_ROTATION_3D && p_type != Animation::TYPE_SCALE_3D),
|
||||
"Track type must be Position/Rotation/Scale 3D.");
|
||||
if (!keying) {
|
||||
return;
|
||||
}
|
||||
@ -3631,7 +3635,6 @@ void AnimationTrackEditor::insert_transform_key(Spatial *p_node, const String &p
|
||||
return;
|
||||
}
|
||||
|
||||
ERR_FAIL_COND(!root);
|
||||
//let's build a node path
|
||||
String path = root->get_path_to(p_node);
|
||||
if (p_sub != "") {
|
||||
@ -3640,24 +3643,18 @@ void AnimationTrackEditor::insert_transform_key(Spatial *p_node, const String &p
|
||||
|
||||
NodePath np = path;
|
||||
|
||||
int position_idx = -1;
|
||||
int rotation_idx = -1;
|
||||
int scale_idx = -1;
|
||||
int track_idx = -1;
|
||||
|
||||
for (int i = 0; i < animation->get_track_count(); i++) {
|
||||
if (animation->track_get_path(i) != np) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (animation->track_get_type(i) == Animation::TYPE_POSITION_3D) {
|
||||
position_idx = i;
|
||||
}
|
||||
if (animation->track_get_type(i) == Animation::TYPE_ROTATION_3D) {
|
||||
rotation_idx = i;
|
||||
}
|
||||
if (animation->track_get_type(i) == Animation::TYPE_SCALE_3D) {
|
||||
scale_idx = i;
|
||||
if (animation->track_get_type(i) != p_type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
track_idx = i;
|
||||
}
|
||||
|
||||
InsertData id;
|
||||
@ -3667,28 +3664,39 @@ void AnimationTrackEditor::insert_transform_key(Spatial *p_node, const String &p
|
||||
id.query = vformat(TTR("node '%s'"), p_node->get_name());
|
||||
id.advance = false;
|
||||
|
||||
//dialog insert
|
||||
|
||||
{
|
||||
id.track_idx = position_idx;
|
||||
id.value = p_xform.origin;
|
||||
id.type = Animation::TYPE_POSITION_3D;
|
||||
id.track_idx = track_idx;
|
||||
id.value = p_value;
|
||||
id.type = p_type;
|
||||
_query_insert(id);
|
||||
}
|
||||
{
|
||||
id.track_idx = rotation_idx;
|
||||
id.value = p_xform.basis.get_rotation_quat();
|
||||
id.type = Animation::TYPE_ROTATION_3D;
|
||||
_query_insert(id);
|
||||
}
|
||||
{
|
||||
id.track_idx = scale_idx;
|
||||
id.value = p_xform.basis.get_scale();
|
||||
id.type = Animation::TYPE_SCALE_3D;
|
||||
_query_insert(id);
|
||||
}
|
||||
}
|
||||
|
||||
bool AnimationTrackEditor::has_track(Spatial *p_node, const String &p_sub, const Animation::TrackType p_type) {
|
||||
ERR_FAIL_COND_V(!root, false);
|
||||
|
||||
if (!keying) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!animation.is_valid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//let's build a node path
|
||||
String path = root->get_path_to(p_node);
|
||||
if (p_sub != "") {
|
||||
path += ":" + p_sub;
|
||||
}
|
||||
|
||||
int track_id = animation->find_track(path, p_type);
|
||||
|
||||
if (track_id >= 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void AnimationTrackEditor::_insert_animation_key(NodePath p_path, const Variant &p_value) {
|
||||
String path = p_path;
|
||||
|
||||
@ -6385,49 +6393,3 @@ AnimationTrackEditor::~AnimationTrackEditor() {
|
||||
}
|
||||
}
|
||||
|
||||
bool AnimationTrackEditor::has_transform_key(Spatial *p_node, const String &p_sub) {
|
||||
if (!keying)
|
||||
return false;
|
||||
if (!animation.is_valid())
|
||||
return false;
|
||||
if (!root)
|
||||
return false;
|
||||
|
||||
//let's build a node path
|
||||
String path = root->get_path_to(p_node);
|
||||
if (p_sub != "")
|
||||
path += ":" + p_sub;
|
||||
|
||||
if (animation->find_track(path) >= 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool AnimationTrackEditor::has_transform_track(Spatial *p_node, const String &p_sub) {
|
||||
if (!keying) {
|
||||
return false;
|
||||
}
|
||||
if (!animation.is_valid()) {
|
||||
return false;
|
||||
}
|
||||
if (!root) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//let's build a node path
|
||||
String path = root->get_path_to(p_node);
|
||||
if (p_sub != "") {
|
||||
path += ":" + p_sub;
|
||||
}
|
||||
int track_id = animation->find_track(path);
|
||||
if (track_id >= 0) {
|
||||
//TODO
|
||||
//if (animation->track_get_type(track_id) == Animation::TYPE_TRANSFORM) {
|
||||
// return true;
|
||||
//}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -563,7 +563,8 @@ public:
|
||||
void set_anim_pos(float p_pos);
|
||||
void insert_node_value_key(Node *p_node, const String &p_property, const Variant &p_value, bool p_only_if_exists = false);
|
||||
void insert_value_key(const String &p_property, const Variant &p_value, bool p_advance);
|
||||
void insert_transform_key(Spatial *p_node, const String &p_sub, const Transform &p_xform);
|
||||
void insert_transform_key(Spatial *p_node, const String &p_sub, const Animation::TrackType p_type, const Variant p_value);
|
||||
bool has_track(Spatial *p_node, const String &p_sub, const Animation::TrackType p_type);
|
||||
|
||||
void show_select_node_warning(bool p_show);
|
||||
|
||||
@ -585,9 +586,6 @@ public:
|
||||
|
||||
AnimationTrackEditor();
|
||||
~AnimationTrackEditor();
|
||||
|
||||
bool has_transform_key(Spatial *p_node, const String &p_sub);
|
||||
bool has_transform_track(Spatial *p_node, const String &p_sub);
|
||||
};
|
||||
|
||||
#endif // ANIMATION_TRACK_EDITOR_H
|
||||
|
@ -419,7 +419,7 @@ Rect2 AnimationTrackEditSpriteFrame::get_key_rect(int p_index, float p_pixels_se
|
||||
// Go through other track to find if animation is set
|
||||
String animation_path = get_animation()->track_get_path(get_track());
|
||||
animation_path = animation_path.replace(":frame", ":animation");
|
||||
int animation_track = get_animation()->find_track(animation_path);
|
||||
int animation_track = get_animation()->find_track(animation_path, get_animation()->track_get_type(get_track()));
|
||||
float track_time = get_animation()->track_get_key_time(get_track(), p_index);
|
||||
int animaiton_index = get_animation()->track_find_key(animation_track, track_time);
|
||||
animation = get_animation()->track_get_key_value(animation_track, animaiton_index);
|
||||
@ -509,7 +509,7 @@ void AnimationTrackEditSpriteFrame::draw_key(int p_index, float p_pixels_sec, in
|
||||
// Go through other track to find if animation is set
|
||||
String animation_path = get_animation()->track_get_path(get_track());
|
||||
animation_path = animation_path.replace(":frame", ":animation");
|
||||
int animation_track = get_animation()->find_track(animation_path);
|
||||
int animation_track = get_animation()->find_track(animation_path, get_animation()->track_get_type(get_track()));
|
||||
float track_time = get_animation()->track_get_key_time(get_track(), p_index);
|
||||
int animaiton_index = get_animation()->track_find_key(animation_track, track_time);
|
||||
animation = get_animation()->track_get_key_value(animation_track, animaiton_index);
|
||||
|
1
editor/icons/icon_new_key.svg
Normal file
1
editor/icons/icon_new_key.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg enable-background="new 0 0 16 16" height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#e0e0e0" fill-opacity=".9961"><path d="m13 9h-2v2h-2v2h2v2h2v-2h2v-2h-2z"/><path d="m10 9.723c-.596-.347-1-.985-1-1.723 0-1.104.896-2 2-2s2 .896 2 2h1v2h.445c.344-.591.555-1.268.555-2 0-2.209-1.791-4-4-4-1.822.002-3.414 1.235-3.869 3h-6.131v2h1v2h3v-2h2.133c.16.62.466 1.169.867 1.627v-.627h2z"/></g></svg>
|
After Width: | Height: | Size: 433 B |
@ -30,10 +30,6 @@
|
||||
|
||||
#include "inspector_dock.h"
|
||||
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/plugins/animation_player_editor_plugin.h"
|
||||
#include "scene/3d/spatial.h"
|
||||
#include "core/class_db.h"
|
||||
#include "core/error_macros.h"
|
||||
#include "core/io/resource_loader.h"
|
||||
@ -51,7 +47,11 @@
|
||||
#include "editor/editor_data.h"
|
||||
#include "editor/editor_file_dialog.h"
|
||||
#include "editor/editor_inspector.h"
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_path.h"
|
||||
#include "editor/editor_settings.h"
|
||||
#include "editor/plugins/animation_player_editor_plugin.h"
|
||||
#include "scene/3d/spatial.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/control.h"
|
||||
#include "scene/gui/dialogs.h"
|
||||
@ -352,10 +352,14 @@ void InspectorDock::_property_keyed(const String &p_keyed, const Variant &p_valu
|
||||
|
||||
void InspectorDock::_transform_keyed(Object *sp, const String &p_sub, const Transform &p_key) {
|
||||
Spatial *s = Object::cast_to<Spatial>(sp);
|
||||
|
||||
if (!s) {
|
||||
return;
|
||||
}
|
||||
AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_transform_key(s, p_sub, p_key);
|
||||
|
||||
AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_transform_key(s, p_sub, Animation::TYPE_POSITION_3D, p_key.origin);
|
||||
AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_transform_key(s, p_sub, Animation::TYPE_ROTATION_3D, p_key.basis.get_rotation_quat());
|
||||
AnimationPlayerEditor::get_singleton()->get_track_editor()->insert_transform_key(s, p_sub, Animation::TYPE_SCALE_3D, p_key.basis.get_scale());
|
||||
}
|
||||
|
||||
void InspectorDock::_warning_pressed() {
|
||||
|
@ -6450,7 +6450,7 @@ void GLTFDocument::_convert_animation(Ref<GLTFState> state, AnimationPlayer *ap,
|
||||
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);
|
||||
int32_t shape_track_i = animation->find_track(shape_path, Animation::TYPE_VALUE);
|
||||
if (shape_track_i == -1) {
|
||||
GLTFAnimation::Channel<float> weight;
|
||||
weight.interpolation = GLTFAnimation::INTERP_LINEAR;
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "skeleton_editor_plugin.h"
|
||||
|
||||
#include "core/io/resource_saver.h"
|
||||
#include "core/os/keyboard.h"
|
||||
#include "editor/animation_track_editor.h"
|
||||
#include "editor/editor_file_dialog.h"
|
||||
#include "editor/editor_inspector.h"
|
||||
@ -94,6 +95,7 @@ void BoneTransformEditor::create_editors() {
|
||||
|
||||
enabled_checkbox = memnew(EditorPropertyCheck());
|
||||
enabled_checkbox->set_label("Pose Enabled");
|
||||
enabled_checkbox->set_selectable(false);
|
||||
enabled_checkbox->connect("property_changed", this, "_value_changed");
|
||||
section->get_vbox()->add_child(enabled_checkbox);
|
||||
|
||||
@ -101,23 +103,27 @@ void BoneTransformEditor::create_editors() {
|
||||
position_property = memnew(EditorPropertyVector3());
|
||||
position_property->setup(-10000, 10000, 0.001f, true);
|
||||
position_property->set_label("Position");
|
||||
position_property->set_selectable(false);
|
||||
position_property->connect("property_changed", this, "_value_changed");
|
||||
position_property->connect("property_keyed", this, "_property_keyed");
|
||||
section->get_vbox()->add_child(position_property);
|
||||
|
||||
// Rotation property
|
||||
rotation_property = memnew(EditorPropertyQuat());
|
||||
rotation_property->setup(-10000, 10000, 0.001f, true);
|
||||
rotation_property->set_label("Rotation");
|
||||
rotation_property->set_selectable(false);
|
||||
rotation_property->connect("property_changed", this, "_value_changed");
|
||||
rotation_property->connect("property_keyed", this, "_property_keyed");
|
||||
section->get_vbox()->add_child(rotation_property);
|
||||
|
||||
// Scale property
|
||||
scale_property = memnew(EditorPropertyVector3());
|
||||
scale_property->setup(-10000, 10000, 0.001f, true);
|
||||
scale_property->set_label("Scale");
|
||||
scale_property->set_use_folding(true);
|
||||
scale_property->set_read_only(false);
|
||||
scale_property->set_selectable(false);
|
||||
scale_property->connect("property_changed", this, "_value_changed_vector3");
|
||||
scale_property->connect("property_keyed", this, "_property_keyed");
|
||||
section->get_vbox()->add_child(scale_property);
|
||||
|
||||
// Transform/Matrix section
|
||||
@ -129,6 +135,7 @@ void BoneTransformEditor::create_editors() {
|
||||
rest_matrix = memnew(EditorPropertyTransform());
|
||||
rest_matrix->setup(-10000, 10000, 0.001f, true);
|
||||
rest_matrix->set_label("Transform");
|
||||
rest_matrix->set_selectable(false);
|
||||
rest_section->get_vbox()->add_child(rest_matrix);
|
||||
}
|
||||
|
||||
@ -155,18 +162,24 @@ void BoneTransformEditor::_value_changed(const String &p_path, const Variant &p_
|
||||
|
||||
void BoneTransformEditor::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_value_changed"), &BoneTransformEditor::_value_changed);
|
||||
ClassDB::bind_method(D_METHOD("_property_keyed"), &BoneTransformEditor::_property_keyed);
|
||||
//ClassDB::bind_method(D_METHOD("update_joint_tree"), &BoneTransformEditor::update_joint_tree);
|
||||
}
|
||||
|
||||
BoneTransformEditor::BoneTransformEditor(Skeleton *p_skeleton) {
|
||||
skeleton = p_skeleton;
|
||||
enabled_checkbox = nullptr;
|
||||
keyable = false;
|
||||
toggle_enabled = false;
|
||||
updating = false;
|
||||
undo_redo = EditorNode::get_undo_redo();
|
||||
}
|
||||
|
||||
void BoneTransformEditor::set_keyable(const bool p_keyable) {
|
||||
position_property->set_keying(p_keyable);
|
||||
rotation_property->set_keying(p_keyable);
|
||||
scale_property->set_keying(p_keyable);
|
||||
}
|
||||
|
||||
void BoneTransformEditor::set_target(const String &p_prop) {
|
||||
enabled_checkbox->set_object_and_property(skeleton, p_prop + "enabled");
|
||||
enabled_checkbox->update_property();
|
||||
@ -184,6 +197,23 @@ void BoneTransformEditor::set_target(const String &p_prop) {
|
||||
rest_matrix->update_property();
|
||||
}
|
||||
|
||||
void BoneTransformEditor::_property_keyed(const String &p_path, bool p_advance) {
|
||||
AnimationTrackEditor *te = AnimationPlayerEditor::get_singleton()->get_track_editor();
|
||||
Vector<String> split = p_path.split("/");
|
||||
if (split.size() == 3 && split[0] == "bones") {
|
||||
int bone_idx = split[1].to_int();
|
||||
if (split[2] == "position") {
|
||||
te->insert_transform_key(skeleton, skeleton->get_bone_name(bone_idx), Animation::TYPE_POSITION_3D, skeleton->get(p_path));
|
||||
}
|
||||
if (split[2] == "rotation") {
|
||||
te->insert_transform_key(skeleton, skeleton->get_bone_name(bone_idx), Animation::TYPE_ROTATION_3D, skeleton->get(p_path));
|
||||
}
|
||||
if (split[2] == "scale") {
|
||||
te->insert_transform_key(skeleton, skeleton->get_bone_name(bone_idx), Animation::TYPE_SCALE_3D, skeleton->get(p_path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BoneTransformEditor::_update_properties() {
|
||||
/*
|
||||
if (!skeleton) {
|
||||
@ -233,12 +263,17 @@ SkeletonEditor *SkeletonEditor::singleton = nullptr;
|
||||
|
||||
void SkeletonEditor::set_keyable(const bool p_keyable) {
|
||||
keyable = p_keyable;
|
||||
skeleton_options->get_popup()->set_item_disabled(SKELETON_OPTION_INSERT_KEYS, !p_keyable);
|
||||
skeleton_options->get_popup()->set_item_disabled(SKELETON_OPTION_INSERT_KEYS_EXISTED, !p_keyable);
|
||||
|
||||
if (p_keyable) {
|
||||
animation_hb->show();
|
||||
} else {
|
||||
animation_hb->hide();
|
||||
}
|
||||
};
|
||||
|
||||
void SkeletonEditor::set_rest_options_enabled(const bool p_rest_options_enabled) {
|
||||
rest_options->get_popup()->set_item_disabled(REST_OPTION_POSE_TO_REST, !p_rest_options_enabled);
|
||||
void SkeletonEditor::set_bone_options_enabled(const bool p_bone_options_enabled) {
|
||||
skeleton_options->get_popup()->set_item_disabled(SKELETON_OPTION_INIT_SELECTED_POSES, !p_bone_options_enabled);
|
||||
skeleton_options->get_popup()->set_item_disabled(SKELETON_OPTION_SELECTED_POSES_TO_RESTS, !p_bone_options_enabled);
|
||||
};
|
||||
|
||||
void SkeletonEditor::_on_click_skeleton_option(int p_skeleton_option) {
|
||||
@ -247,22 +282,26 @@ void SkeletonEditor::_on_click_skeleton_option(int p_skeleton_option) {
|
||||
}
|
||||
|
||||
switch (p_skeleton_option) {
|
||||
case SKELETON_OPTION_INIT_ALL_POSES: {
|
||||
init_pose(true);
|
||||
break;
|
||||
}
|
||||
case SKELETON_OPTION_INIT_SELECTED_POSES: {
|
||||
init_pose(false);
|
||||
break;
|
||||
}
|
||||
case SKELETON_OPTION_ALL_POSES_TO_RESTS: {
|
||||
pose_to_rest(true);
|
||||
break;
|
||||
}
|
||||
case SKELETON_OPTION_SELECTED_POSES_TO_RESTS: {
|
||||
pose_to_rest(false);
|
||||
break;
|
||||
}
|
||||
case SKELETON_OPTION_CREATE_PHYSICAL_SKELETON: {
|
||||
create_physical_skeleton();
|
||||
break;
|
||||
}
|
||||
case SKELETON_OPTION_INIT_POSE: {
|
||||
init_pose();
|
||||
break;
|
||||
}
|
||||
case SKELETON_OPTION_INSERT_KEYS: {
|
||||
insert_keys(true);
|
||||
break;
|
||||
}
|
||||
case SKELETON_OPTION_INSERT_KEYS_EXISTED: {
|
||||
insert_keys(false);
|
||||
break;
|
||||
}
|
||||
case SKELETON_OPTION_ADD_BONE: {
|
||||
add_bone();
|
||||
} break;
|
||||
@ -275,26 +314,19 @@ void SkeletonEditor::_on_click_skeleton_option(int p_skeleton_option) {
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonEditor::_on_click_rest_option(int p_rest_option) {
|
||||
void SkeletonEditor::init_pose(const bool p_all_bones) {
|
||||
if (!skeleton) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (p_rest_option) {
|
||||
case REST_OPTION_POSE_TO_REST: {
|
||||
pose_to_rest();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SkeletonEditor::init_pose() {
|
||||
const int bone_len = skeleton->get_bone_count();
|
||||
if (!bone_len) {
|
||||
return;
|
||||
}
|
||||
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
|
||||
ur->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
|
||||
|
||||
if (p_all_bones) {
|
||||
for (int i = 0; i < bone_len; i++) {
|
||||
Transform rest = skeleton->get_bone_rest(i);
|
||||
ur->add_do_method(skeleton, "set_bone_pose_position", i, rest.origin);
|
||||
@ -304,14 +336,33 @@ void SkeletonEditor::init_pose() {
|
||||
ur->add_undo_method(skeleton, "set_bone_pose_rotation", i, skeleton->get_bone_pose_rotation(i));
|
||||
ur->add_undo_method(skeleton, "set_bone_pose_scale", i, skeleton->get_bone_pose_scale(i));
|
||||
}
|
||||
} else {
|
||||
// Todo: Do method with multiple bone selection.
|
||||
if (selected_bone == -1) {
|
||||
ur->commit_action();
|
||||
return;
|
||||
}
|
||||
Transform rest = skeleton->get_bone_rest(selected_bone);
|
||||
ur->add_do_method(skeleton, "set_bone_pose_position", selected_bone, rest.origin);
|
||||
ur->add_do_method(skeleton, "set_bone_pose_rotation", selected_bone, rest.basis.get_rotation_quat());
|
||||
ur->add_do_method(skeleton, "set_bone_pose_scale", selected_bone, rest.basis.get_scale());
|
||||
ur->add_undo_method(skeleton, "set_bone_pose_position", selected_bone, skeleton->get_bone_pose_position(selected_bone));
|
||||
ur->add_undo_method(skeleton, "set_bone_pose_rotation", selected_bone, skeleton->get_bone_pose_rotation(selected_bone));
|
||||
ur->add_undo_method(skeleton, "set_bone_pose_scale", selected_bone, skeleton->get_bone_pose_scale(selected_bone));
|
||||
}
|
||||
|
||||
ur->commit_action();
|
||||
}
|
||||
|
||||
void SkeletonEditor::insert_keys(bool p_all_bones) {
|
||||
void SkeletonEditor::insert_keys(const bool p_all_bones) {
|
||||
if (!skeleton) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool pos_enabled = key_loc_button->is_pressed();
|
||||
bool rot_enabled = key_rot_button->is_pressed();
|
||||
bool scl_enabled = key_scale_button->is_pressed();
|
||||
|
||||
int bone_len = skeleton->get_bone_count();
|
||||
Node *root = EditorNode::get_singleton()->get_tree()->get_root();
|
||||
String path = root->get_path_to(skeleton);
|
||||
@ -324,50 +375,50 @@ void SkeletonEditor::insert_keys(bool p_all_bones) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!p_all_bones && !te->has_transform_track(skeleton, name)) {
|
||||
continue;
|
||||
if (pos_enabled && (p_all_bones || te->has_track(skeleton, name, Animation::TYPE_POSITION_3D))) {
|
||||
te->insert_transform_key(skeleton, name, Animation::TYPE_POSITION_3D, skeleton->get_bone_pose_position(i));
|
||||
}
|
||||
if (rot_enabled && (p_all_bones || te->has_track(skeleton, name, Animation::TYPE_ROTATION_3D))) {
|
||||
te->insert_transform_key(skeleton, name, Animation::TYPE_ROTATION_3D, skeleton->get_bone_pose_rotation(i));
|
||||
}
|
||||
if (scl_enabled && (p_all_bones || te->has_track(skeleton, name, Animation::TYPE_SCALE_3D))) {
|
||||
te->insert_transform_key(skeleton, name, Animation::TYPE_SCALE_3D, skeleton->get_bone_pose_scale(i));
|
||||
}
|
||||
}
|
||||
|
||||
// Need to normalize the basis before you key it
|
||||
Transform tform = skeleton->get_bone_pose(i);
|
||||
tform.orthonormalize();
|
||||
te->insert_transform_key(skeleton, name, tform);
|
||||
}
|
||||
//te->commit_insert_queue();
|
||||
}
|
||||
|
||||
void SkeletonEditor::pose_to_rest() {
|
||||
void SkeletonEditor::pose_to_rest(const bool p_all_bones) {
|
||||
if (!skeleton) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Todo: Do method with multiple bone selection.
|
||||
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
|
||||
|
||||
ur->create_action(TTR("Set Bone Rest"), UndoRedo::MERGE_ENDS);
|
||||
ur->add_do_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_pose(selected_bone));
|
||||
ur->add_undo_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_rest(selected_bone));
|
||||
|
||||
ur->commit_action();
|
||||
}
|
||||
/*
|
||||
void SkeletonEditor::pose_to_rest() {
|
||||
const int bone_len = skeleton->get_bone_count();
|
||||
if (!bone_len) {
|
||||
return;
|
||||
}
|
||||
|
||||
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
|
||||
ur->create_action(TTR("Set Bone Transform"), UndoRedo::MERGE_ENDS);
|
||||
|
||||
ur->create_action(TTR("Set Bone Rest"), UndoRedo::MERGE_ENDS);
|
||||
|
||||
if (p_all_bones) {
|
||||
for (int i = 0; i < bone_len; i++) {
|
||||
ur->add_do_method(skeleton, "set_bone_pose", i, Transform());
|
||||
ur->add_undo_method(skeleton, "set_bone_pose", i, skeleton->get_bone_pose(i));
|
||||
ur->add_do_method(skeleton, "set_bone_custom_pose", i, Transform());
|
||||
ur->add_undo_method(skeleton, "set_bone_custom_pose", i, skeleton->get_bone_custom_pose(i));
|
||||
ur->add_do_method(skeleton, "set_bone_rest", i, skeleton->get_bone_rest(i) * skeleton->get_bone_custom_pose(i) * skeleton->get_bone_pose(i));
|
||||
ur->add_do_method(skeleton, "set_bone_rest", i, skeleton->get_bone_pose(i));
|
||||
ur->add_undo_method(skeleton, "set_bone_rest", i, skeleton->get_bone_rest(i));
|
||||
}
|
||||
} else {
|
||||
// Todo: Do method with multiple bone selection.
|
||||
if (selected_bone == -1) {
|
||||
ur->commit_action();
|
||||
return;
|
||||
}
|
||||
ur->add_do_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_pose(selected_bone));
|
||||
ur->add_undo_method(skeleton, "set_bone_rest", selected_bone, skeleton->get_bone_rest(selected_bone));
|
||||
}
|
||||
ur->commit_action();
|
||||
}
|
||||
*/
|
||||
|
||||
void SkeletonEditor::create_physical_skeleton() {
|
||||
UndoRedo *ur = EditorNode::get_singleton()->get_undo_redo();
|
||||
@ -559,6 +610,7 @@ void SkeletonEditor::_joint_tree_selection_changed() {
|
||||
const String bone_path = "bones/" + itos(b_idx) + "/";
|
||||
|
||||
pose_editor->set_target(bone_path);
|
||||
pose_editor->set_keyable(keyable);
|
||||
|
||||
selected_bone = b_idx;
|
||||
}
|
||||
@ -566,7 +618,7 @@ void SkeletonEditor::_joint_tree_selection_changed() {
|
||||
|
||||
pose_editor->set_visible(selected);
|
||||
|
||||
set_rest_options_enabled(selected);
|
||||
set_bone_options_enabled(selected);
|
||||
_update_properties();
|
||||
_update_gizmo_visible();
|
||||
}
|
||||
@ -690,45 +742,85 @@ void SkeletonEditor::create_editors() {
|
||||
skeleton_options->set_text(TTR("Skeleton"));
|
||||
skeleton_options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("Skeleton", "EditorIcons"));
|
||||
|
||||
skeleton_options->get_popup()->add_item(TTR("Init pose"), SKELETON_OPTION_INIT_POSE);
|
||||
skeleton_options->get_popup()->add_item(TTR("Insert key of all bone poses"), SKELETON_OPTION_INSERT_KEYS);
|
||||
skeleton_options->get_popup()->add_item(TTR("Insert key of bone poses already exist track"), SKELETON_OPTION_INSERT_KEYS_EXISTED);
|
||||
skeleton_options->get_popup()->add_item(TTR("Create physical skeleton"), SKELETON_OPTION_CREATE_PHYSICAL_SKELETON);
|
||||
skeleton_options->get_popup()->add_item(TTR("Add bone"), SKELETON_OPTION_ADD_BONE);
|
||||
skeleton_options->get_popup()->add_item(TTR("Rename bone"), SKELETON_OPTION_RENAME_BONE);
|
||||
skeleton_options->get_popup()->add_item(TTR("Remove bone"), SKELETON_OPTION_REMOVE_BONE);
|
||||
PopupMenu *p = skeleton_options->get_popup();
|
||||
p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/init_all_poses", TTR("Init all Poses")), SKELETON_OPTION_INIT_ALL_POSES);
|
||||
p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/init_selected_poses", TTR("Init selected Poses")), SKELETON_OPTION_INIT_SELECTED_POSES);
|
||||
p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/all_poses_to_rests", TTR("Apply all poses to rests")), SKELETON_OPTION_ALL_POSES_TO_RESTS);
|
||||
p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/selected_poses_to_rests", TTR("Apply selected poses to rests")), SKELETON_OPTION_SELECTED_POSES_TO_RESTS);
|
||||
p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/create_physical_skeleton", TTR("Create physical skeleton")), SKELETON_OPTION_CREATE_PHYSICAL_SKELETON);
|
||||
p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/add_bone", TTR("Add bone")), SKELETON_OPTION_ADD_BONE);
|
||||
p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/rename_bone", TTR("Rename bone")), SKELETON_OPTION_RENAME_BONE);
|
||||
p->add_shortcut(ED_SHORTCUT("skeleton_3d_editor/remove_bone", TTR("Remove bone")), SKELETON_OPTION_REMOVE_BONE);
|
||||
|
||||
skeleton_options->get_popup()->connect("id_pressed", this, "_on_click_skeleton_option");
|
||||
|
||||
// Create Rest Option in Top Menu Bar.
|
||||
rest_options = memnew(MenuButton);
|
||||
ne->add_control_to_menu_panel(rest_options);
|
||||
|
||||
rest_options->set_text(TTR("Edit Rest"));
|
||||
rest_options->set_icon(EditorNode::get_singleton()->get_gui_base()->get_icon("BoneAttachment", "EditorIcons"));
|
||||
|
||||
rest_options->get_popup()->add_item(TTR("Apply current pose to rest"), REST_OPTION_POSE_TO_REST);
|
||||
rest_options->get_popup()->connect("id_pressed", this, "_on_click_rest_option");
|
||||
set_rest_options_enabled(false);
|
||||
set_bone_options_enabled(false);
|
||||
|
||||
Vector<Variant> button_binds;
|
||||
button_binds.resize(1);
|
||||
|
||||
edit_mode_button = memnew(Button);
|
||||
ne->add_control_to_menu_panel(edit_mode_button);
|
||||
edit_mode_button->set_tooltip(TTR("Edit Mode\nShow buttons on joints."));
|
||||
edit_mode_button->set_toggle_mode(true);
|
||||
edit_mode_button->set_flat(true);
|
||||
edit_mode_button->set_toggle_mode(true);
|
||||
edit_mode_button->set_focus_mode(FOCUS_NONE);
|
||||
edit_mode_button->set_tooltip(TTR("Edit Mode\nShow buttons on joints."));
|
||||
edit_mode_button->connect("toggled", this, "edit_mode_toggled");
|
||||
|
||||
edit_mode = false;
|
||||
|
||||
set_keyable(te->has_keying());
|
||||
|
||||
if (skeleton) {
|
||||
skeleton->add_child(handles_mesh_instance);
|
||||
handles_mesh_instance->set_skeleton_path(NodePath(""));
|
||||
}
|
||||
|
||||
// Keying buttons.
|
||||
animation_hb = memnew(HBoxContainer);
|
||||
ne->add_control_to_menu_panel(animation_hb);
|
||||
animation_hb->add_child(memnew(VSeparator));
|
||||
animation_hb->hide();
|
||||
|
||||
key_loc_button = memnew(Button);
|
||||
key_loc_button->set_flat(true);
|
||||
key_loc_button->set_toggle_mode(true);
|
||||
key_loc_button->set_pressed(false);
|
||||
key_loc_button->set_focus_mode(FOCUS_NONE);
|
||||
key_loc_button->set_tooltip(TTR("Translation mask for inserting keys."));
|
||||
animation_hb->add_child(key_loc_button);
|
||||
|
||||
key_rot_button = memnew(Button);
|
||||
key_rot_button->set_flat(true);
|
||||
key_rot_button->set_toggle_mode(true);
|
||||
key_rot_button->set_pressed(true);
|
||||
key_rot_button->set_focus_mode(FOCUS_NONE);
|
||||
key_rot_button->set_tooltip(TTR("Rotation mask for inserting keys."));
|
||||
animation_hb->add_child(key_rot_button);
|
||||
|
||||
key_scale_button = memnew(Button);
|
||||
key_scale_button->set_flat(true);
|
||||
key_scale_button->set_toggle_mode(true);
|
||||
key_scale_button->set_pressed(false);
|
||||
key_scale_button->set_focus_mode(FOCUS_NONE);
|
||||
key_scale_button->set_tooltip(TTR("Scale mask for inserting keys."));
|
||||
animation_hb->add_child(key_scale_button);
|
||||
|
||||
key_insert_button = memnew(Button);
|
||||
key_insert_button->set_flat(true);
|
||||
key_insert_button->set_focus_mode(FOCUS_NONE);
|
||||
key_insert_button->connect("pressed", this, "insert_keys", varray(false));
|
||||
key_insert_button->set_tooltip(TTR("Insert key of bone poses already exist track."));
|
||||
key_insert_button->set_shortcut(ED_SHORTCUT("skeleton_3d_editor/insert_key_to_existing_tracks", TTR("Insert Key (Existing Tracks)"), KEY_INSERT));
|
||||
animation_hb->add_child(key_insert_button);
|
||||
|
||||
key_insert_all_button = memnew(Button);
|
||||
key_insert_all_button->set_flat(true);
|
||||
key_insert_all_button->set_focus_mode(FOCUS_NONE);
|
||||
key_insert_all_button->connect("pressed", this, "insert_keys", varray(true));
|
||||
key_insert_all_button->set_tooltip(TTR("Insert key of all bone poses."));
|
||||
key_insert_all_button->set_shortcut(ED_SHORTCUT("skeleton_3d_editor/insert_key_of_all_bones", TTR("Insert Key (All Bones)"), KEY_MASK_CMD + KEY_INSERT));
|
||||
animation_hb->add_child(key_insert_all_button);
|
||||
|
||||
// Bone tree.
|
||||
|
||||
const Color section_color = get_color("prop_subsection", "Editor");
|
||||
|
||||
EditorInspectorSection *bones_section = memnew(EditorInspectorSection);
|
||||
@ -756,12 +848,20 @@ void SkeletonEditor::create_editors() {
|
||||
pose_editor->set_label(TTR("Bone Transform"));
|
||||
pose_editor->set_visible(false);
|
||||
add_child(pose_editor);
|
||||
|
||||
set_keyable(te->has_keying());
|
||||
}
|
||||
|
||||
void SkeletonEditor::_notification(int p_what) {
|
||||
switch (p_what) {
|
||||
case NOTIFICATION_READY: {
|
||||
edit_mode_button->set_icon(get_icon("ToolBoneSelect", "EditorIcons"));
|
||||
key_loc_button->set_icon(get_icon("KeyPosition", "EditorIcons"));
|
||||
key_rot_button->set_icon(get_icon("KeyRotation", "EditorIcons"));
|
||||
key_scale_button->set_icon(get_icon("KeyScale", "EditorIcons"));
|
||||
key_insert_button->set_icon(get_icon("Key", "EditorIcons"));
|
||||
key_insert_all_button->set_icon(get_icon("NewKey", "EditorIcons"));
|
||||
|
||||
get_tree()->connect("node_removed", this, "_node_removed", Vector<Variant>(), Object::CONNECT_ONESHOT);
|
||||
break;
|
||||
} break;
|
||||
@ -787,7 +887,6 @@ void SkeletonEditor::_node_removed(Node *p_node) {
|
||||
if (skeleton && p_node == skeleton) {
|
||||
skeleton = nullptr;
|
||||
skeleton_options->hide();
|
||||
rest_options->hide();
|
||||
}
|
||||
|
||||
_update_properties();
|
||||
@ -799,7 +898,7 @@ void SkeletonEditor::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_joint_tree_rmb_select"), &SkeletonEditor::_joint_tree_rmb_select);
|
||||
ClassDB::bind_method(D_METHOD("_update_properties"), &SkeletonEditor::_update_properties);
|
||||
ClassDB::bind_method(D_METHOD("_on_click_skeleton_option"), &SkeletonEditor::_on_click_skeleton_option);
|
||||
ClassDB::bind_method(D_METHOD("_on_click_rest_option"), &SkeletonEditor::_on_click_rest_option);
|
||||
ClassDB::bind_method(D_METHOD("insert_keys"), &SkeletonEditor::insert_keys);
|
||||
ClassDB::bind_method(D_METHOD("edit_mode_toggled"), &SkeletonEditor::edit_mode_toggled);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_drag_data_fw"), &SkeletonEditor::get_drag_data_fw);
|
||||
@ -831,7 +930,6 @@ SkeletonEditor::SkeletonEditor(EditorInspectorPluginSkeleton *e_plugin, EditorNo
|
||||
pose_editor = nullptr;
|
||||
|
||||
skeleton_options = nullptr;
|
||||
rest_options = nullptr;
|
||||
|
||||
edit_mode = false;
|
||||
|
||||
@ -937,6 +1035,11 @@ SkeletonEditor::~SkeletonEditor() {
|
||||
|
||||
SpatialEditor *ne = SpatialEditor::get_singleton();
|
||||
|
||||
if (animation_hb) {
|
||||
ne->remove_control_from_menu_panel(animation_hb);
|
||||
memdelete(animation_hb);
|
||||
}
|
||||
|
||||
if (separator) {
|
||||
ne->remove_control_from_menu_panel(separator);
|
||||
memdelete(separator);
|
||||
@ -947,11 +1050,6 @@ SkeletonEditor::~SkeletonEditor() {
|
||||
memdelete(skeleton_options);
|
||||
}
|
||||
|
||||
if (rest_options) {
|
||||
ne->remove_control_from_menu_panel(rest_options);
|
||||
memdelete(rest_options);
|
||||
}
|
||||
|
||||
if (edit_mode_button) {
|
||||
ne->remove_control_from_menu_panel(edit_mode_button);
|
||||
memdelete(edit_mode_button);
|
||||
|
@ -79,7 +79,6 @@ class BoneTransformEditor : public VBoxContainer {
|
||||
|
||||
UndoRedo *undo_redo;
|
||||
|
||||
bool keyable;
|
||||
bool toggle_enabled;
|
||||
bool updating;
|
||||
|
||||
@ -89,6 +88,8 @@ class BoneTransformEditor : public VBoxContainer {
|
||||
|
||||
void _value_changed(const String &p_path, const Variant &p_value, const String &p_name = "", bool changing = false);
|
||||
|
||||
void _property_keyed(const String &p_path, bool p_advance);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
@ -99,6 +100,7 @@ public:
|
||||
// Which transform target to modify
|
||||
void set_target(const String &p_prop);
|
||||
void set_label(const String &p_label) { label = p_label; }
|
||||
void set_keyable(const bool p_keyable);
|
||||
|
||||
void _update_properties();
|
||||
};
|
||||
@ -109,19 +111,16 @@ class SkeletonEditor : public VBoxContainer {
|
||||
friend class SkeletonEditorPlugin;
|
||||
|
||||
enum SkeletonOption {
|
||||
SKELETON_OPTION_INIT_POSE,
|
||||
SKELETON_OPTION_INSERT_KEYS,
|
||||
SKELETON_OPTION_INSERT_KEYS_EXISTED,
|
||||
SKELETON_OPTION_INIT_ALL_POSES,
|
||||
SKELETON_OPTION_INIT_SELECTED_POSES,
|
||||
SKELETON_OPTION_ALL_POSES_TO_RESTS,
|
||||
SKELETON_OPTION_SELECTED_POSES_TO_RESTS,
|
||||
SKELETON_OPTION_CREATE_PHYSICAL_SKELETON,
|
||||
SKELETON_OPTION_ADD_BONE,
|
||||
SKELETON_OPTION_REMOVE_BONE,
|
||||
SKELETON_OPTION_RENAME_BONE
|
||||
};
|
||||
|
||||
enum RestOption {
|
||||
REST_OPTION_POSE_TO_REST
|
||||
};
|
||||
|
||||
struct BoneInfo {
|
||||
PhysicalBone *physical_bone;
|
||||
Transform relative_rest; // Relative to skeleton node
|
||||
@ -142,11 +141,17 @@ class SkeletonEditor : public VBoxContainer {
|
||||
|
||||
VSeparator *separator;
|
||||
MenuButton *skeleton_options;
|
||||
MenuButton *rest_options;
|
||||
Button *edit_mode_button;
|
||||
|
||||
bool edit_mode;
|
||||
|
||||
HBoxContainer *animation_hb;
|
||||
Button *key_loc_button;
|
||||
Button *key_rot_button;
|
||||
Button *key_scale_button;
|
||||
Button *key_insert_button;
|
||||
Button *key_insert_all_button;
|
||||
|
||||
EditorFileDialog *file_dialog;
|
||||
|
||||
bool keyable;
|
||||
@ -154,7 +159,6 @@ class SkeletonEditor : public VBoxContainer {
|
||||
static SkeletonEditor *singleton;
|
||||
|
||||
void _on_click_skeleton_option(int p_skeleton_option);
|
||||
void _on_click_rest_option(int p_rest_option);
|
||||
void _file_selected(const String &p_file);
|
||||
TreeItem *_find(TreeItem *p_node, const NodePath &p_path);
|
||||
void edit_mode_toggled(const bool pressed);
|
||||
@ -166,9 +170,10 @@ class SkeletonEditor : public VBoxContainer {
|
||||
|
||||
void create_editors();
|
||||
|
||||
void init_pose();
|
||||
void insert_keys(bool p_all_bones);
|
||||
void pose_to_rest();
|
||||
void init_pose(const bool p_all_bones);
|
||||
void pose_to_rest(const bool p_all_bones);
|
||||
|
||||
void insert_keys(const bool p_all_bones);
|
||||
|
||||
void create_physical_skeleton();
|
||||
PhysicalBone *create_physical_bone(int bone_id, int bone_child_id, const Vector<BoneInfo> &bones_infos);
|
||||
@ -178,7 +183,7 @@ class SkeletonEditor : public VBoxContainer {
|
||||
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
|
||||
|
||||
void set_keyable(const bool p_keyable);
|
||||
void set_rest_options_enabled(const bool p_rest_options_enabled);
|
||||
void set_bone_options_enabled(const bool p_bone_options_enabled);
|
||||
|
||||
MeshInstance *handles_mesh_instance;
|
||||
Ref<ImmediateMesh> handles_mesh;
|
||||
|
@ -799,9 +799,9 @@ NodePath Animation::track_get_path(int p_track) const {
|
||||
return tracks[p_track]->path;
|
||||
}
|
||||
|
||||
int Animation::find_track(const NodePath &p_path) const {
|
||||
int Animation::find_track(const NodePath &p_path, const TrackType p_type) const {
|
||||
for (int i = 0; i < tracks.size(); i++) {
|
||||
if (tracks[i]->path == p_path) {
|
||||
if (tracks[i]->path == p_path && tracks[i]->type == p_type) {
|
||||
return i;
|
||||
}
|
||||
};
|
||||
@ -3076,7 +3076,7 @@ void Animation::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("track_get_type", "track_idx"), &Animation::track_get_type);
|
||||
ClassDB::bind_method(D_METHOD("track_get_path", "track_idx"), &Animation::track_get_path);
|
||||
ClassDB::bind_method(D_METHOD("track_set_path", "track_idx", "path"), &Animation::track_set_path);
|
||||
ClassDB::bind_method(D_METHOD("find_track", "path"), &Animation::find_track);
|
||||
ClassDB::bind_method(D_METHOD("find_track", "path", "type"), &Animation::find_track);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("track_move_up", "track_idx"), &Animation::track_move_up);
|
||||
ClassDB::bind_method(D_METHOD("track_move_down", "track_idx"), &Animation::track_move_down);
|
||||
|
@ -280,8 +280,7 @@ public:
|
||||
|
||||
void track_set_path(int p_track, const NodePath &p_path);
|
||||
NodePath track_get_path(int p_track) const;
|
||||
int find_track(const NodePath &p_path) const;
|
||||
// transform
|
||||
int find_track(const NodePath &p_path, const TrackType p_type) const;
|
||||
|
||||
void track_move_up(int p_track);
|
||||
void track_move_down(int p_track);
|
||||
|
Loading…
Reference in New Issue
Block a user