mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-03-26 22:49:01 +08:00
[godot] Resolves #3036, adds GDExtension implementation of animation mixes inspector panel
This commit is contained in:
parent
2fe33f5760
commit
06b114b3b5
@ -32,6 +32,7 @@
|
|||||||
|
|
||||||
#ifdef SPINE_GODOT_EXTENSION
|
#ifdef SPINE_GODOT_EXTENSION
|
||||||
#include <godot_cpp/core/version.hpp>
|
#include <godot_cpp/core/version.hpp>
|
||||||
|
#include <godot_cpp/classes/ref.hpp>
|
||||||
|
|
||||||
// When running scons with deprecated=no, these are not defined in version.h in Godot 4.5.1
|
// When running scons with deprecated=no, these are not defined in version.h in Godot 4.5.1
|
||||||
// but our code for older versions of Godot relies on them.
|
// but our code for older versions of Godot relies on them.
|
||||||
|
|||||||
@ -35,6 +35,11 @@
|
|||||||
#if VERSION_MAJOR > 3
|
#if VERSION_MAJOR > 3
|
||||||
#ifdef SPINE_GODOT_EXTENSION
|
#ifdef SPINE_GODOT_EXTENSION
|
||||||
#include <godot_cpp/classes/editor_undo_redo_manager.hpp>
|
#include <godot_cpp/classes/editor_undo_redo_manager.hpp>
|
||||||
|
#include <godot_cpp/classes/editor_interface.hpp>
|
||||||
|
#include <godot_cpp/classes/h_box_container.hpp>
|
||||||
|
#include <godot_cpp/classes/option_button.hpp>
|
||||||
|
#include <godot_cpp/classes/spin_box.hpp>
|
||||||
|
#include <godot_cpp/classes/button.hpp>
|
||||||
#else
|
#else
|
||||||
#include "editor/editor_undo_redo_manager.h"
|
#include "editor/editor_undo_redo_manager.h"
|
||||||
#endif
|
#endif
|
||||||
@ -225,7 +230,6 @@ bool SpineSkeletonDataResourceInspectorPlugin::parse_property(Object *object, co
|
|||||||
bool SpineSkeletonDataResourceInspectorPlugin::parse_property(Object *object, Variant::Type type, const String &path,
|
bool SpineSkeletonDataResourceInspectorPlugin::parse_property(Object *object, Variant::Type type, const String &path,
|
||||||
PropertyHint hint, const String &hint_text, int usage) {
|
PropertyHint hint, const String &hint_text, int usage) {
|
||||||
#endif
|
#endif
|
||||||
// FIXME can't do this in godot-cpp
|
|
||||||
#ifndef SPINE_GODOT_EXTENSION
|
#ifndef SPINE_GODOT_EXTENSION
|
||||||
if (path == "animation_mixes") {
|
if (path == "animation_mixes") {
|
||||||
Ref<SpineSkeletonDataResource> skeleton_data = Object::cast_to<SpineSkeletonDataResource>(object);
|
Ref<SpineSkeletonDataResource> skeleton_data = Object::cast_to<SpineSkeletonDataResource>(object);
|
||||||
@ -235,11 +239,19 @@ bool SpineSkeletonDataResourceInspectorPlugin::parse_property(Object *object, Va
|
|||||||
add_property_editor(path, mixes_property);
|
add_property_editor(path, mixes_property);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
if (path == "animation_mixes") {
|
||||||
|
Ref<SpineSkeletonDataResource> skeleton_data = Object::cast_to<SpineSkeletonDataResource>(object);
|
||||||
|
if (!skeleton_data.is_valid() || !skeleton_data->is_skeleton_data_loaded()) return true;
|
||||||
|
auto mixes_property = memnew(SpineEditorPropertyAnimationMixes);
|
||||||
|
mixes_property->setup(skeleton_data);
|
||||||
|
add_property_editor(path, mixes_property);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME can't do this in godot-cpp
|
|
||||||
#ifndef SPINE_GODOT_EXTENSION
|
#ifndef SPINE_GODOT_EXTENSION
|
||||||
SpineEditorPropertyAnimationMixes::SpineEditorPropertyAnimationMixes() : skeleton_data(nullptr), container(nullptr), updating(false) {
|
SpineEditorPropertyAnimationMixes::SpineEditorPropertyAnimationMixes() : skeleton_data(nullptr), container(nullptr), updating(false) {
|
||||||
INSTANTIATE(array_object);
|
INSTANTIATE(array_object);
|
||||||
@ -471,6 +483,198 @@ void SpineEditorPropertyAnimationMix::update_property() {
|
|||||||
|
|
||||||
updating = false;
|
updating = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// *** NEW: GDExtension implementation of the animation mixes editor. ***
|
||||||
|
// Uses plain Godot Controls (OptionButton, SpinBox, Button) instead of
|
||||||
|
// internal editor classes (EditorPropertyTextEnum, EditorPropertyFloat,
|
||||||
|
// EditorPropertyArrayObject) which are not exposed to GDExtension.
|
||||||
|
// Undo/redo is supported via EditorUndoRedoManager, which IS available in godot-cpp.
|
||||||
|
#else
|
||||||
|
SpineEditorPropertyAnimationMixes::SpineEditorPropertyAnimationMixes() : container(nullptr), updating(false) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpineEditorPropertyAnimationMixes::_bind_methods() {
|
||||||
|
ClassDB::bind_method(D_METHOD("rebuild_ui"), &SpineEditorPropertyAnimationMixes::rebuild_ui);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpineEditorPropertyAnimationMixes::setup(const Ref<SpineSkeletonDataResource> &_skeleton_data) {
|
||||||
|
this->skeleton_data = _skeleton_data;
|
||||||
|
rebuild_ui();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpineEditorPropertyAnimationMixes::_update_property() {
|
||||||
|
if (updating) return;
|
||||||
|
rebuild_ui();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpineEditorPropertyAnimationMixes::rebuild_ui() {
|
||||||
|
updating = true;
|
||||||
|
|
||||||
|
if (container) {
|
||||||
|
remove_child(container);
|
||||||
|
memdelete(container);
|
||||||
|
container = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!skeleton_data.is_valid() || !skeleton_data->is_skeleton_data_loaded()) {
|
||||||
|
updating = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
container = memnew(VBoxContainer);
|
||||||
|
add_child(container);
|
||||||
|
set_bottom_editor(container);
|
||||||
|
|
||||||
|
Array mixes = skeleton_data->get_animation_mixes();
|
||||||
|
PackedStringArray animation_names;
|
||||||
|
skeleton_data->get_animation_names(animation_names);
|
||||||
|
|
||||||
|
for (int i = 0; i < mixes.size(); i++) {
|
||||||
|
Ref<SpineAnimationMix> mix = mixes[i];
|
||||||
|
if (mix.is_null()) continue;
|
||||||
|
|
||||||
|
auto hbox = memnew(HBoxContainer);
|
||||||
|
hbox->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||||
|
container->add_child(hbox);
|
||||||
|
|
||||||
|
auto from_option = memnew(OptionButton);
|
||||||
|
from_option->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||||
|
for (int j = 0; j < animation_names.size(); j++) {
|
||||||
|
from_option->add_item(animation_names[j]);
|
||||||
|
if (animation_names[j] == mix->get_from()) {
|
||||||
|
from_option->select(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
from_option->connect("item_selected", callable_mp(this, &SpineEditorPropertyAnimationMixes::on_from_changed).bind(i), CONNECT_DEFERRED);
|
||||||
|
hbox->add_child(from_option);
|
||||||
|
|
||||||
|
auto to_option = memnew(OptionButton);
|
||||||
|
to_option->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||||
|
for (int j = 0; j < animation_names.size(); j++) {
|
||||||
|
to_option->add_item(animation_names[j]);
|
||||||
|
if (animation_names[j] == mix->get_to()) {
|
||||||
|
to_option->select(j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
to_option->connect("item_selected", callable_mp(this, &SpineEditorPropertyAnimationMixes::on_to_changed).bind(i), CONNECT_DEFERRED);
|
||||||
|
hbox->add_child(to_option);
|
||||||
|
|
||||||
|
auto spin_box = memnew(SpinBox);
|
||||||
|
spin_box->set_h_size_flags(SIZE_EXPAND_FILL);
|
||||||
|
spin_box->set_min(0.0);
|
||||||
|
spin_box->set_max(9999.0);
|
||||||
|
spin_box->set_step(0.01);
|
||||||
|
spin_box->set_value(mix->get_mix());
|
||||||
|
spin_box->connect("value_changed", callable_mp(this, &SpineEditorPropertyAnimationMixes::on_mix_value_changed).bind(i), CONNECT_DEFERRED);
|
||||||
|
hbox->add_child(spin_box);
|
||||||
|
|
||||||
|
auto delete_button = memnew(Button);
|
||||||
|
delete_button->set_text("Remove");
|
||||||
|
delete_button->connect("pressed", callable_mp(this, &SpineEditorPropertyAnimationMixes::delete_mix).bind(i), CONNECT_DEFERRED);
|
||||||
|
hbox->add_child(delete_button);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto add_mix_button = memnew(Button);
|
||||||
|
add_mix_button->set_text("Add mix");
|
||||||
|
add_mix_button->connect("pressed", callable_mp(this, &SpineEditorPropertyAnimationMixes::add_mix), CONNECT_DEFERRED);
|
||||||
|
container->add_child(add_mix_button);
|
||||||
|
|
||||||
|
updating = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpineEditorPropertyAnimationMixes::add_mix() {
|
||||||
|
if (updating || !skeleton_data.is_valid() || !skeleton_data->is_skeleton_data_loaded()) return;
|
||||||
|
|
||||||
|
PackedStringArray animation_names;
|
||||||
|
skeleton_data->get_animation_names(animation_names);
|
||||||
|
|
||||||
|
Ref<SpineAnimationMix> mix = Ref<SpineAnimationMix>(memnew(SpineAnimationMix));
|
||||||
|
mix->set_from(animation_names[0]);
|
||||||
|
mix->set_to(animation_names[0]);
|
||||||
|
mix->set_mix(0);
|
||||||
|
|
||||||
|
Array mixes = skeleton_data->get_animation_mixes().duplicate();
|
||||||
|
mixes.push_back(mix);
|
||||||
|
emit_changed(get_edited_property(), mixes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpineEditorPropertyAnimationMixes::delete_mix(int idx) {
|
||||||
|
if (updating || !skeleton_data.is_valid() || !skeleton_data->is_skeleton_data_loaded()) return;
|
||||||
|
|
||||||
|
Array mixes = skeleton_data->get_animation_mixes().duplicate();
|
||||||
|
mixes.remove_at(idx);
|
||||||
|
emit_changed(get_edited_property(), mixes);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpineEditorPropertyAnimationMixes::on_from_changed(int option_idx, int mix_idx) {
|
||||||
|
if (updating) return;
|
||||||
|
|
||||||
|
PackedStringArray animation_names;
|
||||||
|
skeleton_data->get_animation_names(animation_names);
|
||||||
|
String new_value = animation_names[option_idx];
|
||||||
|
|
||||||
|
Array mixes = skeleton_data->get_animation_mixes();
|
||||||
|
Ref<SpineAnimationMix> mix = mixes[mix_idx];
|
||||||
|
String old_value = mix->get_from();
|
||||||
|
|
||||||
|
auto undo_redo = EditorInterface::get_singleton()->get_editor_undo_redo();
|
||||||
|
undo_redo->create_action("Set mix from animation");
|
||||||
|
undo_redo->add_do_property(mix.ptr(), "from", new_value);
|
||||||
|
undo_redo->add_undo_property(mix.ptr(), "from", old_value);
|
||||||
|
undo_redo->add_do_method(this, "rebuild_ui");
|
||||||
|
undo_redo->add_undo_method(this, "rebuild_ui");
|
||||||
|
updating = true;
|
||||||
|
undo_redo->commit_action();
|
||||||
|
updating = false;
|
||||||
|
|
||||||
|
emit_changed(get_edited_property(), skeleton_data->get_animation_mixes());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpineEditorPropertyAnimationMixes::on_to_changed(int option_idx, int mix_idx) {
|
||||||
|
if (updating) return;
|
||||||
|
|
||||||
|
PackedStringArray animation_names;
|
||||||
|
skeleton_data->get_animation_names(animation_names);
|
||||||
|
String new_value = animation_names[option_idx];
|
||||||
|
|
||||||
|
Array mixes = skeleton_data->get_animation_mixes();
|
||||||
|
Ref<SpineAnimationMix> mix = mixes[mix_idx];
|
||||||
|
String old_value = mix->get_to();
|
||||||
|
|
||||||
|
auto undo_redo = EditorInterface::get_singleton()->get_editor_undo_redo();
|
||||||
|
undo_redo->create_action("Set mix to animation");
|
||||||
|
undo_redo->add_do_property(mix.ptr(), "to", new_value);
|
||||||
|
undo_redo->add_undo_property(mix.ptr(), "to", old_value);
|
||||||
|
undo_redo->add_do_method(this, "rebuild_ui");
|
||||||
|
undo_redo->add_undo_method(this, "rebuild_ui");
|
||||||
|
updating = true;
|
||||||
|
undo_redo->commit_action();
|
||||||
|
updating = false;
|
||||||
|
|
||||||
|
emit_changed(get_edited_property(), skeleton_data->get_animation_mixes());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SpineEditorPropertyAnimationMixes::on_mix_value_changed(float value, int mix_idx) {
|
||||||
|
if (updating) return;
|
||||||
|
|
||||||
|
Array mixes = skeleton_data->get_animation_mixes();
|
||||||
|
Ref<SpineAnimationMix> mix = mixes[mix_idx];
|
||||||
|
float old_value = mix->get_mix();
|
||||||
|
|
||||||
|
auto undo_redo = EditorInterface::get_singleton()->get_editor_undo_redo();
|
||||||
|
undo_redo->create_action("Set mix duration");
|
||||||
|
undo_redo->add_do_property(mix.ptr(), "mix", value);
|
||||||
|
undo_redo->add_undo_property(mix.ptr(), "mix", old_value);
|
||||||
|
undo_redo->add_do_method(this, "rebuild_ui");
|
||||||
|
undo_redo->add_undo_method(this, "rebuild_ui");
|
||||||
|
updating = true;
|
||||||
|
undo_redo->commit_action();
|
||||||
|
updating = false;
|
||||||
|
|
||||||
|
emit_changed(get_edited_property(), skeleton_data->get_animation_mixes());
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void SpineSpriteInspectorPlugin::_bind_methods() {
|
void SpineSpriteInspectorPlugin::_bind_methods() {
|
||||||
@ -498,4 +702,4 @@ void SpineSpriteInspectorPlugin::parse_begin(Object *object) {
|
|||||||
if (!sprite->get_skeleton_data_res().is_valid() || !sprite->get_skeleton_data_res()->is_skeleton_data_loaded()) return;
|
if (!sprite->get_skeleton_data_res().is_valid() || !sprite->get_skeleton_data_res()->is_skeleton_data_loaded()) return;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@ -34,6 +34,7 @@
|
|||||||
#if VERSION_MAJOR > 3
|
#if VERSION_MAJOR > 3
|
||||||
#ifdef SPINE_GODOT_EXTENSION
|
#ifdef SPINE_GODOT_EXTENSION
|
||||||
#include <godot_cpp/classes/editor_import_plugin.hpp>
|
#include <godot_cpp/classes/editor_import_plugin.hpp>
|
||||||
|
#include <godot_cpp/classes/v_box_container.hpp>
|
||||||
#else
|
#else
|
||||||
#include "editor/import/editor_import_plugin.h"
|
#include "editor/import/editor_import_plugin.h"
|
||||||
#endif
|
#endif
|
||||||
@ -371,6 +372,27 @@ public:
|
|||||||
void setup(SpineEditorPropertyAnimationMixes *mixes_property, const Ref<SpineSkeletonDataResource> &skeleton_data, int index);
|
void setup(SpineEditorPropertyAnimationMixes *mixes_property, const Ref<SpineSkeletonDataResource> &skeleton_data, int index);
|
||||||
void update_property() override;
|
void update_property() override;
|
||||||
};
|
};
|
||||||
|
#else
|
||||||
|
class SpineEditorPropertyAnimationMixes : public EditorProperty {
|
||||||
|
GDCLASS(SpineEditorPropertyAnimationMixes, EditorProperty)
|
||||||
|
|
||||||
|
Ref<SpineSkeletonDataResource> skeleton_data;
|
||||||
|
VBoxContainer *container;
|
||||||
|
bool updating;
|
||||||
|
|
||||||
|
static void _bind_methods();
|
||||||
|
void rebuild_ui();
|
||||||
|
void add_mix();
|
||||||
|
void delete_mix(int idx);
|
||||||
|
void on_from_changed(int option_idx, int mix_idx);
|
||||||
|
void on_to_changed(int option_idx, int mix_idx);
|
||||||
|
void on_mix_value_changed(float value, int mix_idx);
|
||||||
|
|
||||||
|
public:
|
||||||
|
SpineEditorPropertyAnimationMixes();
|
||||||
|
void setup(const Ref<SpineSkeletonDataResource> &skeletonData);
|
||||||
|
void _update_property() override;
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
class SpineSpriteInspectorPlugin : public EditorInspectorPlugin {
|
class SpineSpriteInspectorPlugin : public EditorInspectorPlugin {
|
||||||
|
|||||||
@ -505,6 +505,7 @@ void SpineSkeletonDataResource::update_mixes() {
|
|||||||
animation_state_data->setDefaultMix(default_mix);
|
animation_state_data->setDefaultMix(default_mix);
|
||||||
for (int i = 0; i < animation_mixes.size(); i++) {
|
for (int i = 0; i < animation_mixes.size(); i++) {
|
||||||
Ref<SpineAnimationMix> mix = animation_mixes[i];
|
Ref<SpineAnimationMix> mix = animation_mixes[i];
|
||||||
|
if (mix.is_null()) continue;
|
||||||
spine::Animation *from =
|
spine::Animation *from =
|
||||||
skeleton_data->findAnimation(mix->get_from().utf8().ptr());
|
skeleton_data->findAnimation(mix->get_from().utf8().ptr());
|
||||||
spine::Animation *to =
|
spine::Animation *to =
|
||||||
|
|||||||
@ -84,6 +84,7 @@ void initialize_spine_godot_module(ModuleInitializationLevel level) {
|
|||||||
GDREGISTER_CLASS(SpineJsonResourceImportPlugin);
|
GDREGISTER_CLASS(SpineJsonResourceImportPlugin);
|
||||||
GDREGISTER_CLASS(SpineBinaryResourceImportPlugin);
|
GDREGISTER_CLASS(SpineBinaryResourceImportPlugin);
|
||||||
GDREGISTER_CLASS(SpineSkeletonDataResourceInspectorPlugin);
|
GDREGISTER_CLASS(SpineSkeletonDataResourceInspectorPlugin);
|
||||||
|
GDREGISTER_CLASS(SpineEditorPropertyAnimationMixes);
|
||||||
GDREGISTER_CLASS(SpineEditorPlugin);
|
GDREGISTER_CLASS(SpineEditorPlugin);
|
||||||
EditorPlugins::add_plugin_class(StringName("SpineEditorPlugin"));
|
EditorPlugins::add_plugin_class(StringName("SpineEditorPlugin"));
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user