-Added AnimationGraphPlayer (still missing features)
-Added ability to edit resources from built-in inspector (wip, needs testing and feedback)
This commit is contained in:
parent
5c5aafabec
commit
0a1c1c660f
@ -187,7 +187,6 @@ Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, Map<Ref<Res
|
||||
|
||||
void Resource::configure_for_local_scene(Node *p_for_scene, Map<Ref<Resource>, Ref<Resource> > &remap_cache) {
|
||||
|
||||
print_line("configure for local: " + get_class());
|
||||
List<PropertyInfo> plist;
|
||||
get_property_list(&plist);
|
||||
|
||||
|
@ -78,7 +78,7 @@ void EditorHistory::cleanup_history() {
|
||||
current = history.size() - 1;
|
||||
}
|
||||
|
||||
void EditorHistory::_add_object(ObjectID p_object, const String &p_property, int p_level_change) {
|
||||
void EditorHistory::_add_object(ObjectID p_object, const String &p_property, int p_level_change, bool p_inspector_only) {
|
||||
|
||||
Object *obj = ObjectDB::get_instance(p_object);
|
||||
ERR_FAIL_COND(!obj);
|
||||
@ -88,6 +88,7 @@ void EditorHistory::_add_object(ObjectID p_object, const String &p_property, int
|
||||
o.ref = REF(r);
|
||||
o.object = p_object;
|
||||
o.property = p_property;
|
||||
o.inspector_only = p_inspector_only;
|
||||
|
||||
History h;
|
||||
|
||||
@ -120,6 +121,11 @@ void EditorHistory::_add_object(ObjectID p_object, const String &p_property, int
|
||||
current++;
|
||||
}
|
||||
|
||||
void EditorHistory::add_object_inspector_only(ObjectID p_object) {
|
||||
|
||||
_add_object(p_object, "", -1, true);
|
||||
}
|
||||
|
||||
void EditorHistory::add_object(ObjectID p_object) {
|
||||
|
||||
_add_object(p_object, "", -1);
|
||||
@ -142,6 +148,13 @@ int EditorHistory::get_history_pos() {
|
||||
return current;
|
||||
}
|
||||
|
||||
bool EditorHistory::is_history_obj_inspector_only(int p_obj) const {
|
||||
|
||||
ERR_FAIL_INDEX_V(p_obj, history.size(), false);
|
||||
ERR_FAIL_INDEX_V(history[p_obj].level, history[p_obj].path.size(), false);
|
||||
return history[p_obj].path[history[p_obj].level].inspector_only;
|
||||
}
|
||||
|
||||
ObjectID EditorHistory::get_history_obj(int p_obj) const {
|
||||
ERR_FAIL_INDEX_V(p_obj, history.size(), 0);
|
||||
ERR_FAIL_INDEX_V(history[p_obj].level, history[p_obj].path.size(), 0);
|
||||
@ -180,6 +193,14 @@ bool EditorHistory::previous() {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool EditorHistory::is_current_inspector_only() const {
|
||||
|
||||
if (current < 0 || current >= history.size())
|
||||
return false;
|
||||
|
||||
const History &h = history[current];
|
||||
return h.path[h.level].inspector_only;
|
||||
}
|
||||
ObjectID EditorHistory::get_current() {
|
||||
|
||||
if (current < 0 || current >= history.size())
|
||||
|
@ -50,6 +50,7 @@ class EditorHistory {
|
||||
REF ref;
|
||||
ObjectID object;
|
||||
String property;
|
||||
bool inspector_only;
|
||||
};
|
||||
|
||||
struct History {
|
||||
@ -70,7 +71,7 @@ class EditorHistory {
|
||||
Variant value;
|
||||
};
|
||||
|
||||
void _add_object(ObjectID p_object, const String &p_property, int p_level_change);
|
||||
void _add_object(ObjectID p_object, const String &p_property, int p_level_change, bool p_inspector_only = false);
|
||||
|
||||
public:
|
||||
void cleanup_history();
|
||||
@ -78,6 +79,7 @@ public:
|
||||
bool is_at_beginning() const;
|
||||
bool is_at_end() const;
|
||||
|
||||
void add_object_inspector_only(ObjectID p_object);
|
||||
void add_object(ObjectID p_object);
|
||||
void add_object(ObjectID p_object, const String &p_subprop);
|
||||
void add_object(ObjectID p_object, int p_relevel);
|
||||
@ -85,10 +87,12 @@ public:
|
||||
int get_history_len();
|
||||
int get_history_pos();
|
||||
ObjectID get_history_obj(int p_obj) const;
|
||||
bool is_history_obj_inspector_only(int p_obj) const;
|
||||
|
||||
bool next();
|
||||
bool previous();
|
||||
ObjectID get_current();
|
||||
bool is_current_inspector_only() const;
|
||||
|
||||
int get_path_size() const;
|
||||
ObjectID get_path_object(int p_index) const;
|
||||
|
@ -1477,6 +1477,9 @@ void EditorInspector::update_tree() {
|
||||
|
||||
ep->object = object;
|
||||
ep->connect("property_changed", this, "_property_changed");
|
||||
if (p.usage & PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED) {
|
||||
ep->connect("property_changed", this, "_property_changed_update_all", varray(), CONNECT_DEFERRED);
|
||||
}
|
||||
ep->connect("property_keyed", this, "_property_keyed");
|
||||
ep->connect("property_keyed_with_value", this, "_property_keyed_with_value");
|
||||
ep->connect("property_checked", this, "_property_checked");
|
||||
@ -1782,6 +1785,10 @@ void EditorInspector::_property_changed(const String &p_path, const Variant &p_v
|
||||
_edit_set(p_path, p_value, false, "");
|
||||
}
|
||||
|
||||
void EditorInspector::_property_changed_update_all(const String &p_path, const Variant &p_value) {
|
||||
update_tree();
|
||||
}
|
||||
|
||||
void EditorInspector::_multiple_properties_changed(Vector<String> p_paths, Array p_values) {
|
||||
|
||||
ERR_FAIL_COND(p_paths.size() == 0 || p_values.size() == 0);
|
||||
@ -1951,6 +1958,8 @@ void EditorInspector::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method("_multiple_properties_changed", &EditorInspector::_multiple_properties_changed);
|
||||
ClassDB::bind_method("_property_changed", &EditorInspector::_property_changed);
|
||||
ClassDB::bind_method("_property_changed_update_all", &EditorInspector::_property_changed_update_all);
|
||||
|
||||
ClassDB::bind_method("_edit_request_change", &EditorInspector::_edit_request_change);
|
||||
ClassDB::bind_method("_node_removed", &EditorInspector::_node_removed);
|
||||
ClassDB::bind_method("_filter_changed", &EditorInspector::_filter_changed);
|
||||
|
@ -257,6 +257,7 @@ class EditorInspector : public ScrollContainer {
|
||||
void _edit_set(const String &p_name, const Variant &p_value, bool p_refresh_all, const String &p_changed_field);
|
||||
|
||||
void _property_changed(const String &p_path, const Variant &p_value);
|
||||
void _property_changed_update_all(const String &p_path, const Variant &p_value);
|
||||
void _multiple_properties_changed(Vector<String> p_paths, Array p_values);
|
||||
void _property_keyed(const String &p_path);
|
||||
void _property_keyed_with_value(const String &p_path, const Variant &p_value);
|
||||
|
@ -66,6 +66,7 @@
|
||||
#include "editor/import/resource_importer_scene.h"
|
||||
#include "editor/import/resource_importer_texture.h"
|
||||
#include "editor/import/resource_importer_wav.h"
|
||||
#include "editor/plugins/animation_blend_tree_editor_plugin.h"
|
||||
#include "editor/plugins/animation_player_editor_plugin.h"
|
||||
#include "editor/plugins/animation_tree_editor_plugin.h"
|
||||
#include "editor/plugins/asset_library_editor_plugin.h"
|
||||
@ -1300,7 +1301,25 @@ void EditorNode::_dialog_action(String p_file) {
|
||||
}
|
||||
}
|
||||
|
||||
void EditorNode::push_item(Object *p_object, const String &p_property) {
|
||||
bool EditorNode::item_has_editor(Object *p_object) {
|
||||
|
||||
return editor_data.get_subeditors(p_object).size() > 0;
|
||||
}
|
||||
|
||||
void EditorNode::edit_item(Object *p_object) {
|
||||
|
||||
Vector<EditorPlugin *> sub_plugins = editor_data.get_subeditors(p_object);
|
||||
|
||||
if (!sub_plugins.empty()) {
|
||||
_display_top_editors(false);
|
||||
|
||||
_set_top_editors(sub_plugins);
|
||||
_set_editing_top_editors(p_object);
|
||||
_display_top_editors(true);
|
||||
}
|
||||
}
|
||||
|
||||
void EditorNode::push_item(Object *p_object, const String &p_property, bool p_inspector_only) {
|
||||
|
||||
if (!p_object) {
|
||||
get_inspector()->edit(NULL);
|
||||
@ -1312,7 +1331,9 @@ void EditorNode::push_item(Object *p_object, const String &p_property) {
|
||||
uint32_t id = p_object->get_instance_id();
|
||||
if (id != editor_history.get_current()) {
|
||||
|
||||
if (p_property == "")
|
||||
if (p_inspector_only) {
|
||||
editor_history.add_object_inspector_only(id);
|
||||
} else if (p_property == "")
|
||||
editor_history.add_object(id);
|
||||
else
|
||||
editor_history.add_object(id, p_property);
|
||||
@ -1366,6 +1387,7 @@ void EditorNode::_edit_current() {
|
||||
|
||||
uint32_t current = editor_history.get_current();
|
||||
Object *current_obj = current > 0 ? ObjectDB::get_instance(current) : NULL;
|
||||
bool inspector_only = editor_history.is_current_inspector_only();
|
||||
|
||||
this->current = current_obj;
|
||||
|
||||
@ -1457,57 +1479,60 @@ void EditorNode::_edit_current() {
|
||||
|
||||
/* Take care of PLUGIN EDITOR */
|
||||
|
||||
EditorPlugin *main_plugin = editor_data.get_editor(current_obj);
|
||||
if (!inspector_only) {
|
||||
|
||||
if (main_plugin) {
|
||||
EditorPlugin *main_plugin = editor_data.get_editor(current_obj);
|
||||
|
||||
// special case if use of external editor is true
|
||||
if (main_plugin->get_name() == "Script" && (bool(EditorSettings::get_singleton()->get("text_editor/external/use_external_editor")) || overrides_external_editor(current_obj))) {
|
||||
if (!changing_scene)
|
||||
main_plugin->edit(current_obj);
|
||||
}
|
||||
if (main_plugin) {
|
||||
|
||||
else if (main_plugin != editor_plugin_screen && (!ScriptEditor::get_singleton() || !ScriptEditor::get_singleton()->is_visible_in_tree() || ScriptEditor::get_singleton()->can_take_away_focus())) {
|
||||
// update screen main_plugin
|
||||
|
||||
if (!changing_scene) {
|
||||
|
||||
if (editor_plugin_screen)
|
||||
editor_plugin_screen->make_visible(false);
|
||||
editor_plugin_screen = main_plugin;
|
||||
editor_plugin_screen->edit(current_obj);
|
||||
|
||||
editor_plugin_screen->make_visible(true);
|
||||
|
||||
int plugin_count = editor_data.get_editor_plugin_count();
|
||||
for (int i = 0; i < plugin_count; i++) {
|
||||
editor_data.get_editor_plugin(i)->notify_main_screen_changed(editor_plugin_screen->get_name());
|
||||
}
|
||||
|
||||
for (int i = 0; i < editor_table.size(); i++) {
|
||||
|
||||
main_editor_buttons[i]->set_pressed(editor_table[i] == main_plugin);
|
||||
}
|
||||
// special case if use of external editor is true
|
||||
if (main_plugin->get_name() == "Script" && (bool(EditorSettings::get_singleton()->get("text_editor/external/use_external_editor")) || overrides_external_editor(current_obj))) {
|
||||
if (!changing_scene)
|
||||
main_plugin->edit(current_obj);
|
||||
}
|
||||
|
||||
} else {
|
||||
else if (main_plugin != editor_plugin_screen && (!ScriptEditor::get_singleton() || !ScriptEditor::get_singleton()->is_visible_in_tree() || ScriptEditor::get_singleton()->can_take_away_focus())) {
|
||||
// update screen main_plugin
|
||||
|
||||
editor_plugin_screen->edit(current_obj);
|
||||
if (!changing_scene) {
|
||||
|
||||
if (editor_plugin_screen)
|
||||
editor_plugin_screen->make_visible(false);
|
||||
editor_plugin_screen = main_plugin;
|
||||
editor_plugin_screen->edit(current_obj);
|
||||
|
||||
editor_plugin_screen->make_visible(true);
|
||||
|
||||
int plugin_count = editor_data.get_editor_plugin_count();
|
||||
for (int i = 0; i < plugin_count; i++) {
|
||||
editor_data.get_editor_plugin(i)->notify_main_screen_changed(editor_plugin_screen->get_name());
|
||||
}
|
||||
|
||||
for (int i = 0; i < editor_table.size(); i++) {
|
||||
|
||||
main_editor_buttons[i]->set_pressed(editor_table[i] == main_plugin);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
editor_plugin_screen->edit(current_obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector<EditorPlugin *> sub_plugins = editor_data.get_subeditors(current_obj);
|
||||
Vector<EditorPlugin *> sub_plugins = editor_data.get_subeditors(current_obj);
|
||||
|
||||
if (!sub_plugins.empty()) {
|
||||
_display_top_editors(false);
|
||||
if (!sub_plugins.empty()) {
|
||||
_display_top_editors(false);
|
||||
|
||||
_set_top_editors(sub_plugins);
|
||||
_set_editing_top_editors(current_obj);
|
||||
_display_top_editors(true);
|
||||
_set_top_editors(sub_plugins);
|
||||
_set_editing_top_editors(current_obj);
|
||||
_display_top_editors(true);
|
||||
|
||||
} else if (!editor_plugins_over->get_plugins_list().empty()) {
|
||||
} else if (!editor_plugins_over->get_plugins_list().empty()) {
|
||||
|
||||
_hide_top_editors();
|
||||
_hide_top_editors();
|
||||
}
|
||||
}
|
||||
|
||||
inspector_dock->update(current_obj);
|
||||
@ -5346,6 +5371,8 @@ EditorNode::EditorNode() {
|
||||
add_editor_plugin(memnew(SpatialEditorPlugin(this)));
|
||||
add_editor_plugin(memnew(ScriptEditorPlugin(this)));
|
||||
|
||||
add_editor_plugin(memnew(AnimationNodeBlendTreeEditorPlugin(this)));
|
||||
|
||||
EditorAudioBuses *audio_bus_editor = EditorAudioBuses::register_editor();
|
||||
|
||||
ScriptTextEditor::register_editor(); //register one for text scripts
|
||||
|
@ -634,7 +634,9 @@ public:
|
||||
|
||||
static HBoxContainer *get_menu_hb() { return singleton->menu_hb; }
|
||||
|
||||
void push_item(Object *p_object, const String &p_property = "");
|
||||
void push_item(Object *p_object, const String &p_property = "", bool p_inspector_only = false);
|
||||
void edit_item(Object *p_object);
|
||||
bool item_has_editor(Object *p_object);
|
||||
|
||||
void open_request(const String &p_path);
|
||||
|
||||
|
@ -1986,6 +1986,13 @@ void EditorPropertyResource::_sub_inspector_object_id_selected(int p_id) {
|
||||
emit_signal("object_id_selected", get_edited_property(), p_id);
|
||||
}
|
||||
|
||||
void EditorPropertyResource::_open_editor_pressed() {
|
||||
RES res = get_edited_object()->get(get_edited_property());
|
||||
if (res.is_valid()) {
|
||||
EditorNode::get_singleton()->edit_item(res.ptr());
|
||||
}
|
||||
}
|
||||
|
||||
void EditorPropertyResource::update_property() {
|
||||
|
||||
RES res = get_edited_object()->get(get_edited_property());
|
||||
@ -2009,9 +2016,29 @@ void EditorPropertyResource::update_property() {
|
||||
sub_inspector->set_read_only(is_read_only());
|
||||
sub_inspector->set_use_folding(is_using_folding());
|
||||
|
||||
add_child(sub_inspector);
|
||||
set_bottom_editor(sub_inspector);
|
||||
sub_inspector_vbox = memnew(VBoxContainer);
|
||||
add_child(sub_inspector_vbox);
|
||||
set_bottom_editor(sub_inspector_vbox);
|
||||
|
||||
sub_inspector_vbox->add_child(sub_inspector);
|
||||
assign->set_pressed(true);
|
||||
|
||||
bool use_editor = false;
|
||||
for (int i = 0; i < EditorNode::get_singleton()->get_editor_data().get_editor_plugin_count(); i++) {
|
||||
EditorPlugin *ep = EditorNode::get_singleton()->get_editor_data().get_editor_plugin(i);
|
||||
if (ep->handles(res.ptr())) {
|
||||
use_editor = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (use_editor) {
|
||||
Button *open_in_editor = memnew(Button);
|
||||
open_in_editor->set_text(TTR("Open Editor"));
|
||||
open_in_editor->set_icon(get_icon("Edit", "EditorIcons"));
|
||||
sub_inspector_vbox->add_child(open_in_editor);
|
||||
open_in_editor->connect("pressed", this, "_open_editor_pressed");
|
||||
open_in_editor->set_h_size_flags(SIZE_SHRINK_CENTER);
|
||||
}
|
||||
}
|
||||
|
||||
if (res.ptr() != sub_inspector->get_edited_object()) {
|
||||
@ -2021,8 +2048,9 @@ void EditorPropertyResource::update_property() {
|
||||
} else {
|
||||
if (sub_inspector) {
|
||||
set_bottom_editor(NULL);
|
||||
memdelete(sub_inspector);
|
||||
memdelete(sub_inspector_vbox);
|
||||
sub_inspector = NULL;
|
||||
sub_inspector_vbox = NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -2242,11 +2270,13 @@ void EditorPropertyResource::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("can_drop_data_fw"), &EditorPropertyResource::can_drop_data_fw);
|
||||
ClassDB::bind_method(D_METHOD("drop_data_fw"), &EditorPropertyResource::drop_data_fw);
|
||||
ClassDB::bind_method(D_METHOD("_button_draw"), &EditorPropertyResource::_button_draw);
|
||||
ClassDB::bind_method(D_METHOD("_open_editor_pressed"), &EditorPropertyResource::_open_editor_pressed);
|
||||
}
|
||||
|
||||
EditorPropertyResource::EditorPropertyResource() {
|
||||
|
||||
sub_inspector = NULL;
|
||||
sub_inspector_vbox = NULL;
|
||||
use_sub_inspector = !bool(EDITOR_GET("interface/inspector/open_resources_in_new_inspector"));
|
||||
HBoxContainer *hbc = memnew(HBoxContainer);
|
||||
add_child(hbc);
|
||||
|
@ -491,6 +491,7 @@ class EditorPropertyResource : public EditorProperty {
|
||||
EditorFileDialog *file;
|
||||
Vector<String> inheritors_array;
|
||||
EditorInspector *sub_inspector;
|
||||
VBoxContainer *sub_inspector_vbox;
|
||||
|
||||
bool use_sub_inspector;
|
||||
bool dropping;
|
||||
@ -516,6 +517,8 @@ class EditorPropertyResource : public EditorProperty {
|
||||
bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const;
|
||||
void drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from);
|
||||
|
||||
void _open_editor_pressed();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _notification(int p_what);
|
||||
|
@ -948,6 +948,7 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) {
|
||||
theme->set_stylebox("bg", "GraphEdit", style_tree_bg);
|
||||
theme->set_color("grid_major", "GraphEdit", grid_major_color);
|
||||
theme->set_color("grid_minor", "GraphEdit", grid_minor_color);
|
||||
theme->set_color("activity", "GraphEdit", accent_color);
|
||||
theme->set_icon("minus", "GraphEdit", theme->get_icon("ZoomLess", "EditorIcons"));
|
||||
theme->set_icon("more", "GraphEdit", theme->get_icon("ZoomMore", "EditorIcons"));
|
||||
theme->set_icon("reset", "GraphEdit", theme->get_icon("ZoomReset", "EditorIcons"));
|
||||
|
829
editor/plugins/animation_blend_tree_editor_plugin.cpp
Normal file
829
editor/plugins/animation_blend_tree_editor_plugin.cpp
Normal file
@ -0,0 +1,829 @@
|
||||
#include "animation_blend_tree_editor_plugin.h"
|
||||
|
||||
#include "core/io/resource_loader.h"
|
||||
#include "core/project_settings.h"
|
||||
#include "os/input.h"
|
||||
#include "os/keyboard.h"
|
||||
#include "scene/animation/animation_player.h"
|
||||
#include "scene/gui/menu_button.h"
|
||||
#include "scene/gui/panel.h"
|
||||
#include "scene/main/viewport.h"
|
||||
|
||||
void AnimationNodeBlendTreeEditor::edit(AnimationNodeBlendTree *p_blend_tree) {
|
||||
|
||||
blend_tree = p_blend_tree;
|
||||
|
||||
if (!blend_tree) {
|
||||
hide();
|
||||
} else {
|
||||
_update_graph();
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationNodeBlendTreeEditor::add_custom_type(const String &p_name, const Ref<Script> &p_script) {
|
||||
|
||||
for (int i = 0; i < add_options.size(); i++) {
|
||||
ERR_FAIL_COND(add_options[i].script == p_script);
|
||||
}
|
||||
|
||||
AddOption ao;
|
||||
ao.name = p_name;
|
||||
ao.script = p_script;
|
||||
add_options.push_back(ao);
|
||||
|
||||
_update_options_menu();
|
||||
}
|
||||
|
||||
void AnimationNodeBlendTreeEditor::remove_custom_type(const Ref<Script> &p_script) {
|
||||
|
||||
for (int i = 0; i < add_options.size(); i++) {
|
||||
if (add_options[i].script == p_script) {
|
||||
add_options.remove(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
_update_options_menu();
|
||||
}
|
||||
|
||||
void AnimationNodeBlendTreeEditor::_update_options_menu() {
|
||||
|
||||
add_node->get_popup()->clear();
|
||||
for (int i = 0; i < add_options.size(); i++) {
|
||||
add_node->get_popup()->add_item(add_options[i].name);
|
||||
}
|
||||
}
|
||||
|
||||
Size2 AnimationNodeBlendTreeEditor::get_minimum_size() const {
|
||||
|
||||
return Size2(10, 200);
|
||||
}
|
||||
|
||||
void AnimationNodeBlendTreeEditor::_update_graph() {
|
||||
|
||||
if (updating)
|
||||
return;
|
||||
|
||||
graph->set_scroll_ofs(blend_tree->get_graph_offset() * EDSCALE);
|
||||
|
||||
if (blend_tree->get_tree().is_valid()) {
|
||||
goto_parent->show();
|
||||
} else {
|
||||
goto_parent->hide();
|
||||
}
|
||||
graph->clear_connections();
|
||||
//erase all nodes
|
||||
for (int i = 0; i < graph->get_child_count(); i++) {
|
||||
|
||||
if (Object::cast_to<GraphNode>(graph->get_child(i))) {
|
||||
memdelete(graph->get_child(i));
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
animations.clear();
|
||||
|
||||
List<StringName> nodes;
|
||||
blend_tree->get_node_list(&nodes);
|
||||
|
||||
for (List<StringName>::Element *E = nodes.front(); E; E = E->next()) {
|
||||
|
||||
GraphNode *node = memnew(GraphNode);
|
||||
graph->add_child(node);
|
||||
|
||||
Ref<AnimationNode> agnode = blend_tree->get_node(E->get());
|
||||
|
||||
if (!agnode->is_connected("changed", this, "_node_changed")) {
|
||||
agnode->connect("changed", this, "_node_changed", varray(agnode->get_instance_id()), CONNECT_DEFERRED);
|
||||
}
|
||||
|
||||
node->set_offset(agnode->get_position() * EDSCALE);
|
||||
|
||||
node->set_title(agnode->get_caption());
|
||||
node->set_name(E->get());
|
||||
|
||||
int base = 0;
|
||||
if (String(E->get()) != "output") {
|
||||
LineEdit *name = memnew(LineEdit);
|
||||
name->set_text(E->get());
|
||||
name->set_expand_to_text_length(true);
|
||||
node->add_child(name);
|
||||
node->set_slot(0, false, 0, Color(), true, 0, get_color("font_color", "Label"));
|
||||
name->connect("text_entered", this, "_node_renamed", varray(agnode));
|
||||
name->connect("focus_exited", this, "_node_renamed_focus_out", varray(name, agnode));
|
||||
base = 1;
|
||||
node->set_show_close_button(true);
|
||||
node->connect("close_request", this, "_delete_request", varray(E->get()), CONNECT_DEFERRED);
|
||||
}
|
||||
|
||||
for (int i = 0; i < agnode->get_input_count(); i++) {
|
||||
Label *in_name = memnew(Label);
|
||||
node->add_child(in_name);
|
||||
in_name->set_text(agnode->get_input_name(i));
|
||||
node->set_slot(base + i, true, 0, get_color("font_color", "Label"), false, 0, Color());
|
||||
}
|
||||
|
||||
node->connect("dragged", this, "_node_dragged", varray(agnode));
|
||||
|
||||
if (EditorNode::get_singleton()->item_has_editor(agnode.ptr())) {
|
||||
node->add_child(memnew(HSeparator));
|
||||
Button *open_in_editor = memnew(Button);
|
||||
open_in_editor->set_text(TTR("Open Editor"));
|
||||
open_in_editor->set_icon(get_icon("Edit", "EditorIcons"));
|
||||
node->add_child(open_in_editor);
|
||||
open_in_editor->connect("pressed", this, "_open_in_editor", varray(E->get()), CONNECT_DEFERRED);
|
||||
open_in_editor->set_h_size_flags(SIZE_SHRINK_CENTER);
|
||||
}
|
||||
|
||||
if (agnode->has_filter()) {
|
||||
|
||||
node->add_child(memnew(HSeparator));
|
||||
Button *edit_filters = memnew(Button);
|
||||
edit_filters->set_text(TTR("Edit Filters"));
|
||||
edit_filters->set_icon(get_icon("AnimationFilter", "EditorIcons"));
|
||||
node->add_child(edit_filters);
|
||||
edit_filters->connect("pressed", this, "_edit_filters", varray(E->get()), CONNECT_DEFERRED);
|
||||
edit_filters->set_h_size_flags(SIZE_SHRINK_CENTER);
|
||||
}
|
||||
|
||||
Ref<AnimationNodeAnimation> anim = agnode;
|
||||
if (anim.is_valid()) {
|
||||
|
||||
MenuButton *mb = memnew(MenuButton);
|
||||
mb->set_text(anim->get_animation());
|
||||
mb->set_icon(get_icon("Animation", "EditorIcons"));
|
||||
Array options;
|
||||
|
||||
node->add_child(memnew(HSeparator));
|
||||
node->add_child(mb);
|
||||
|
||||
ProgressBar *pb = memnew(ProgressBar);
|
||||
|
||||
AnimationGraphPlayer *player = anim->get_graph_player();
|
||||
if (player->has_node(player->get_animation_player())) {
|
||||
AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(player->get_node(player->get_animation_player()));
|
||||
if (ap) {
|
||||
List<StringName> anims;
|
||||
ap->get_animation_list(&anims);
|
||||
|
||||
for (List<StringName>::Element *F = anims.front(); F; F = F->next()) {
|
||||
mb->get_popup()->add_item(F->get());
|
||||
options.push_back(F->get());
|
||||
}
|
||||
|
||||
if (ap->has_animation(anim->get_animation())) {
|
||||
pb->set_max(ap->get_animation(anim->get_animation())->get_length());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pb->set_percent_visible(false);
|
||||
animations[E->get()] = pb;
|
||||
node->add_child(pb);
|
||||
|
||||
mb->get_popup()->connect("index_pressed", this, "_anim_selected", varray(options, E->get()), CONNECT_DEFERRED);
|
||||
}
|
||||
|
||||
Ref<AnimationNodeOneShot> oneshot = agnode;
|
||||
if (oneshot.is_valid()) {
|
||||
|
||||
HBoxContainer *play_stop = memnew(HBoxContainer);
|
||||
play_stop->add_spacer();
|
||||
Button *play = memnew(Button);
|
||||
play->set_icon(get_icon("Play", "EditorIcons"));
|
||||
play->connect("pressed", this, "_oneshot_start", varray(E->get()), CONNECT_DEFERRED);
|
||||
play_stop->add_child(play);
|
||||
Button *stop = memnew(Button);
|
||||
stop->set_icon(get_icon("Stop", "EditorIcons"));
|
||||
stop->connect("pressed", this, "_oneshot_stop", varray(E->get()), CONNECT_DEFERRED);
|
||||
play_stop->add_child(stop);
|
||||
play_stop->add_spacer();
|
||||
node->add_child(play_stop);
|
||||
}
|
||||
}
|
||||
|
||||
List<AnimationNodeBlendTree::NodeConnection> connections;
|
||||
blend_tree->get_node_connections(&connections);
|
||||
|
||||
for (List<AnimationNodeBlendTree::NodeConnection>::Element *E = connections.front(); E; E = E->next()) {
|
||||
|
||||
StringName from = E->get().output_node;
|
||||
StringName to = E->get().input_node;
|
||||
int to_idx = E->get().input_index;
|
||||
|
||||
graph->connect_node(from, 0, to, to_idx);
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationNodeBlendTreeEditor::_add_node(int p_idx) {
|
||||
|
||||
ERR_FAIL_INDEX(p_idx, add_options.size());
|
||||
|
||||
Ref<AnimationNode> anode;
|
||||
|
||||
if (add_options[p_idx].type != String()) {
|
||||
AnimationNode *an = Object::cast_to<AnimationNode>(ClassDB::instance(add_options[p_idx].type));
|
||||
ERR_FAIL_COND(!an);
|
||||
anode = Ref<AnimationNode>(an);
|
||||
} else {
|
||||
ERR_FAIL_COND(add_options[p_idx].script.is_null());
|
||||
String base_type = add_options[p_idx].script->get_instance_base_type();
|
||||
AnimationNode *an = Object::cast_to<AnimationNode>(ClassDB::instance(base_type));
|
||||
ERR_FAIL_COND(!an);
|
||||
anode = Ref<AnimationNode>(an);
|
||||
anode->set_script(add_options[p_idx].script.get_ref_ptr());
|
||||
}
|
||||
|
||||
Point2 instance_pos = graph->get_scroll_ofs() + graph->get_size() * 0.5;
|
||||
|
||||
anode->set_position(instance_pos);
|
||||
|
||||
String base_name = add_options[p_idx].name;
|
||||
int base = 1;
|
||||
String name = base_name;
|
||||
while (blend_tree->has_node(name)) {
|
||||
base++;
|
||||
name = base_name + " " + itos(base);
|
||||
}
|
||||
|
||||
undo_redo->create_action("Add Node to BlendTree");
|
||||
undo_redo->add_do_method(blend_tree, "add_node", name, anode);
|
||||
undo_redo->add_undo_method(blend_tree, "remove_node", name);
|
||||
undo_redo->add_do_method(this, "_update_graph");
|
||||
undo_redo->add_undo_method(this, "_update_graph");
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
|
||||
void AnimationNodeBlendTreeEditor::_node_dragged(const Vector2 &p_from, const Vector2 &p_to, Ref<AnimationNode> p_node) {
|
||||
|
||||
updating = true;
|
||||
undo_redo->create_action("Node Moved");
|
||||
undo_redo->add_do_method(p_node.ptr(), "set_position", p_to / EDSCALE);
|
||||
undo_redo->add_undo_method(p_node.ptr(), "set_position", p_from / EDSCALE);
|
||||
undo_redo->add_do_method(this, "_update_graph");
|
||||
undo_redo->add_undo_method(this, "_update_graph");
|
||||
undo_redo->commit_action();
|
||||
updating = false;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendTreeEditor::_connection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index) {
|
||||
|
||||
AnimationNodeBlendTree::ConnectionError err = blend_tree->can_connect_node(p_to, p_to_index, p_from);
|
||||
|
||||
if (err != AnimationNodeBlendTree::CONNECTION_OK) {
|
||||
EditorNode::get_singleton()->show_warning(TTR("Unable to connect, port may be in use or connection may be invalid."));
|
||||
return;
|
||||
}
|
||||
|
||||
undo_redo->create_action("Nodes Connected");
|
||||
undo_redo->add_do_method(blend_tree, "connect_node", p_to, p_to_index, p_from);
|
||||
undo_redo->add_undo_method(blend_tree, "disconnect_node", p_to, p_to_index, p_from);
|
||||
undo_redo->add_do_method(this, "_update_graph");
|
||||
undo_redo->add_undo_method(this, "_update_graph");
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
|
||||
void AnimationNodeBlendTreeEditor::_disconnection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index) {
|
||||
|
||||
graph->disconnect_node(p_from, p_from_index, p_to, p_to_index);
|
||||
|
||||
updating = true;
|
||||
undo_redo->create_action("Nodes Disconnected");
|
||||
undo_redo->add_do_method(blend_tree, "disconnect_node", p_to, p_to_index);
|
||||
undo_redo->add_undo_method(blend_tree, "connect_node", p_to, p_to_index, p_from);
|
||||
undo_redo->add_do_method(this, "_update_graph");
|
||||
undo_redo->add_undo_method(this, "_update_graph");
|
||||
undo_redo->commit_action();
|
||||
updating = false;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendTreeEditor::_anim_selected(int p_index, Array p_options, const String &p_node) {
|
||||
|
||||
String option = p_options[p_index];
|
||||
|
||||
Ref<AnimationNodeAnimation> anim = blend_tree->get_node(p_node);
|
||||
ERR_FAIL_COND(!anim.is_valid());
|
||||
|
||||
undo_redo->create_action("Set Animation");
|
||||
undo_redo->add_do_method(anim.ptr(), "set_animation", option);
|
||||
undo_redo->add_undo_method(anim.ptr(), "set_animation", anim->get_animation());
|
||||
undo_redo->add_do_method(this, "_update_graph");
|
||||
undo_redo->add_undo_method(this, "_update_graph");
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
|
||||
void AnimationNodeBlendTreeEditor::_delete_request(const String &p_which) {
|
||||
|
||||
undo_redo->create_action("Delete Node");
|
||||
undo_redo->add_do_method(blend_tree, "remove_node", p_which);
|
||||
undo_redo->add_undo_method(blend_tree, "add_node", p_which, blend_tree->get_node(p_which));
|
||||
|
||||
List<AnimationNodeBlendTree::NodeConnection> conns;
|
||||
blend_tree->get_node_connections(&conns);
|
||||
|
||||
for (List<AnimationNodeBlendTree::NodeConnection>::Element *E = conns.front(); E; E = E->next()) {
|
||||
if (E->get().output_node == p_which || E->get().input_node == p_which) {
|
||||
undo_redo->add_undo_method(blend_tree, "connect_node", E->get().input_node, E->get().input_index, E->get().output_node);
|
||||
}
|
||||
}
|
||||
|
||||
undo_redo->add_do_method(this, "_update_graph");
|
||||
undo_redo->add_undo_method(this, "_update_graph");
|
||||
undo_redo->commit_action();
|
||||
}
|
||||
|
||||
void AnimationNodeBlendTreeEditor::_oneshot_start(const StringName &p_name) {
|
||||
|
||||
Ref<AnimationNodeOneShot> os = blend_tree->get_node(p_name);
|
||||
ERR_FAIL_COND(!os.is_valid());
|
||||
os->start();
|
||||
}
|
||||
|
||||
void AnimationNodeBlendTreeEditor::_oneshot_stop(const StringName &p_name) {
|
||||
|
||||
Ref<AnimationNodeOneShot> os = blend_tree->get_node(p_name);
|
||||
ERR_FAIL_COND(!os.is_valid());
|
||||
os->stop();
|
||||
}
|
||||
|
||||
void AnimationNodeBlendTreeEditor::_node_selected(Object *p_node) {
|
||||
|
||||
GraphNode *gn = Object::cast_to<GraphNode>(p_node);
|
||||
ERR_FAIL_COND(!gn);
|
||||
|
||||
String name = gn->get_name();
|
||||
|
||||
Ref<AnimationNode> anode = blend_tree->get_node(name);
|
||||
ERR_FAIL_COND(!anode.is_valid());
|
||||
|
||||
EditorNode::get_singleton()->push_item(anode.ptr(), "", true);
|
||||
}
|
||||
|
||||
void AnimationNodeBlendTreeEditor::_open_in_editor(const String &p_which) {
|
||||
|
||||
Ref<AnimationNode> an = blend_tree->get_node(p_which);
|
||||
ERR_FAIL_COND(!an.is_valid())
|
||||
EditorNode::get_singleton()->edit_item(an.ptr());
|
||||
}
|
||||
|
||||
void AnimationNodeBlendTreeEditor::_open_parent() {
|
||||
if (blend_tree->get_tree().is_valid()) {
|
||||
EditorNode::get_singleton()->edit_item(blend_tree->get_tree().ptr());
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationNodeBlendTreeEditor::_filter_toggled() {
|
||||
|
||||
updating = true;
|
||||
undo_redo->create_action("Toggle filter on/off");
|
||||
undo_redo->add_do_method(_filter_edit.ptr(), "set_filter_enabled", filter_enabled->is_pressed());
|
||||
undo_redo->add_undo_method(_filter_edit.ptr(), "set_filter_enabled", _filter_edit->is_filter_enabled());
|
||||
undo_redo->add_do_method(this, "_update_filters", _filter_edit);
|
||||
undo_redo->add_undo_method(this, "_update_filters", _filter_edit);
|
||||
undo_redo->commit_action();
|
||||
updating = false;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendTreeEditor::_filter_edited() {
|
||||
|
||||
TreeItem *edited = filters->get_edited();
|
||||
ERR_FAIL_COND(!edited);
|
||||
|
||||
NodePath edited_path = edited->get_metadata(0);
|
||||
bool filtered = edited->is_checked(0);
|
||||
|
||||
updating = true;
|
||||
undo_redo->create_action("Change filter");
|
||||
undo_redo->add_do_method(_filter_edit.ptr(), "set_filter_path", edited_path, filtered);
|
||||
undo_redo->add_undo_method(_filter_edit.ptr(), "set_filter_path", edited_path, _filter_edit->is_path_filtered(edited_path));
|
||||
undo_redo->add_do_method(this, "_update_filters", _filter_edit);
|
||||
undo_redo->add_undo_method(this, "_update_filters", _filter_edit);
|
||||
undo_redo->commit_action();
|
||||
updating = false;
|
||||
}
|
||||
|
||||
bool AnimationNodeBlendTreeEditor::_update_filters(const Ref<AnimationNode> &anode) {
|
||||
|
||||
if (updating || _filter_edit != anode)
|
||||
return false;
|
||||
|
||||
NodePath player_path = anode->get_graph_player()->get_animation_player();
|
||||
|
||||
if (!anode->get_graph_player()->has_node(player_path)) {
|
||||
EditorNode::get_singleton()->show_warning(TTR("No animation player set, so unable to retrieve track names."));
|
||||
return false;
|
||||
}
|
||||
|
||||
AnimationPlayer *player = Object::cast_to<AnimationPlayer>(anode->get_graph_player()->get_node(player_path));
|
||||
if (!player) {
|
||||
EditorNode::get_singleton()->show_warning(TTR("Player path set is invalid, so unable to retrieve track names."));
|
||||
return false;
|
||||
}
|
||||
|
||||
Node *base = player->get_node(player->get_root());
|
||||
|
||||
if (!base) {
|
||||
EditorNode::get_singleton()->show_warning(TTR("Animation player has no valid root node path, so unable to retrieve track names."));
|
||||
return false;
|
||||
}
|
||||
|
||||
updating = true;
|
||||
|
||||
Set<String> paths;
|
||||
{
|
||||
List<StringName> animations;
|
||||
player->get_animation_list(&animations);
|
||||
|
||||
for (List<StringName>::Element *E = animations.front(); E; E = E->next()) {
|
||||
|
||||
Ref<Animation> anim = player->get_animation(E->get());
|
||||
for (int i = 0; i < anim->get_track_count(); i++) {
|
||||
paths.insert(anim->track_get_path(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
filter_enabled->set_pressed(anode->is_filter_enabled());
|
||||
filters->clear();
|
||||
TreeItem *root = filters->create_item();
|
||||
|
||||
Map<String, TreeItem *> parenthood;
|
||||
|
||||
for (Set<String>::Element *E = paths.front(); E; E = E->next()) {
|
||||
|
||||
NodePath path = E->get();
|
||||
TreeItem *ti = NULL;
|
||||
String accum;
|
||||
for (int i = 0; i < path.get_name_count(); i++) {
|
||||
String name = path.get_name(i);
|
||||
if (accum != String()) {
|
||||
accum += "/";
|
||||
}
|
||||
accum += name;
|
||||
if (!parenthood.has(accum)) {
|
||||
if (ti) {
|
||||
ti = filters->create_item(ti);
|
||||
} else {
|
||||
ti = filters->create_item(root);
|
||||
}
|
||||
parenthood[accum] = ti;
|
||||
ti->set_text(0, name);
|
||||
ti->set_selectable(0, false);
|
||||
ti->set_editable(0, false);
|
||||
|
||||
if (base->has_node(accum)) {
|
||||
Node *node = base->get_node(accum);
|
||||
if (has_icon(node->get_class(), "EditorIcons")) {
|
||||
ti->set_icon(0, get_icon(node->get_class(), "EditorIcons"));
|
||||
} else {
|
||||
ti->set_icon(0, get_icon("Node", "EditorIcons"));
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
ti = parenthood[accum];
|
||||
}
|
||||
}
|
||||
|
||||
Node *node = NULL;
|
||||
if (base->has_node(accum)) {
|
||||
node = base->get_node(accum);
|
||||
}
|
||||
if (!node)
|
||||
continue; //no node, cant edit
|
||||
|
||||
if (path.get_subname_count()) {
|
||||
|
||||
String concat = path.get_concatenated_subnames();
|
||||
|
||||
Skeleton *skeleton = Object::cast_to<Skeleton>(node);
|
||||
if (skeleton && skeleton->find_bone(concat) != -1) {
|
||||
//path in skeleton
|
||||
String bone = concat;
|
||||
int idx = skeleton->find_bone(bone);
|
||||
List<String> bone_path;
|
||||
while (idx != -1) {
|
||||
bone_path.push_front(skeleton->get_bone_name(idx));
|
||||
idx = skeleton->get_bone_parent(idx);
|
||||
}
|
||||
|
||||
accum += ":";
|
||||
for (List<String>::Element *F = bone_path.front(); F; F = F->next()) {
|
||||
if (F != bone_path.front()) {
|
||||
accum += "/";
|
||||
}
|
||||
|
||||
accum += F->get();
|
||||
if (!parenthood.has(accum)) {
|
||||
ti = filters->create_item(ti);
|
||||
parenthood[accum] = ti;
|
||||
ti->set_text(0, F->get());
|
||||
ti->set_selectable(0, false);
|
||||
ti->set_editable(0, false);
|
||||
ti->set_icon(0, get_icon("BoneAttachment", "EditorIcons"));
|
||||
} else {
|
||||
ti = parenthood[accum];
|
||||
}
|
||||
}
|
||||
|
||||
ti->set_editable(0, true);
|
||||
ti->set_selectable(0, true);
|
||||
ti->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
|
||||
ti->set_text(0, concat);
|
||||
ti->set_checked(0, anode->is_path_filtered(path));
|
||||
ti->set_icon(0, get_icon("BoneAttachment", "EditorIcons"));
|
||||
ti->set_metadata(0, path);
|
||||
|
||||
} else {
|
||||
//just a property
|
||||
ti = filters->create_item(ti);
|
||||
ti->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
|
||||
ti->set_text(0, concat);
|
||||
ti->set_editable(0, true);
|
||||
ti->set_selectable(0, true);
|
||||
ti->set_checked(0, anode->is_path_filtered(path));
|
||||
ti->set_metadata(0, path);
|
||||
}
|
||||
} else {
|
||||
if (ti) {
|
||||
//just a node, likely call or animation track
|
||||
ti->set_editable(0, true);
|
||||
ti->set_selectable(0, true);
|
||||
ti->set_cell_mode(0, TreeItem::CELL_MODE_CHECK);
|
||||
ti->set_checked(0, anode->is_path_filtered(path));
|
||||
ti->set_metadata(0, path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updating = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendTreeEditor::_edit_filters(const String &p_which) {
|
||||
|
||||
Ref<AnimationNode> anode = blend_tree->get_node(p_which);
|
||||
ERR_FAIL_COND(!anode.is_valid());
|
||||
|
||||
_filter_edit = anode;
|
||||
if (!_update_filters(anode))
|
||||
return;
|
||||
|
||||
filter_dialog->popup_centered_minsize(Size2(500, 500) * EDSCALE);
|
||||
}
|
||||
|
||||
void AnimationNodeBlendTreeEditor::_notification(int p_what) {
|
||||
|
||||
if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) {
|
||||
|
||||
goto_parent->set_icon(get_icon("MoveUp", "EditorIcons"));
|
||||
|
||||
error_panel->add_style_override("panel", get_stylebox("bg", "Tree"));
|
||||
error_label->add_color_override("font_color", get_color("error_color", "Editor"));
|
||||
}
|
||||
|
||||
if (p_what == NOTIFICATION_PROCESS) {
|
||||
|
||||
String error;
|
||||
|
||||
if (!blend_tree->get_graph_player()) {
|
||||
error = TTR("BlendTree does not belong to an AnimationGraphPlayer node.");
|
||||
} else if (!blend_tree->get_graph_player()->is_active()) {
|
||||
error = TTR("AnimationGraphPlayer is inactive.\nActivate to enable playback, check node warnings if activation fails.");
|
||||
} else if (blend_tree->get_graph_player()->is_state_invalid()) {
|
||||
error = blend_tree->get_graph_player()->get_invalid_state_reason();
|
||||
}
|
||||
|
||||
if (error != error_label->get_text()) {
|
||||
error_label->set_text(error);
|
||||
if (error != String()) {
|
||||
error_panel->show();
|
||||
} else {
|
||||
error_panel->hide();
|
||||
}
|
||||
}
|
||||
|
||||
List<AnimationNodeBlendTree::NodeConnection> conns;
|
||||
blend_tree->get_node_connections(&conns);
|
||||
for (List<AnimationNodeBlendTree::NodeConnection>::Element *E = conns.front(); E; E = E->next()) {
|
||||
float activity = 0;
|
||||
if (blend_tree->get_graph_player() && !blend_tree->get_graph_player()->is_state_invalid()) {
|
||||
activity = blend_tree->get_connection_activity(E->get().input_node, E->get().input_index);
|
||||
}
|
||||
graph->set_connection_activity(E->get().output_node, 0, E->get().input_node, E->get().input_index, activity);
|
||||
}
|
||||
|
||||
AnimationGraphPlayer *graph_player = blend_tree->get_graph_player();
|
||||
AnimationPlayer *player = NULL;
|
||||
if (graph_player->has_node(graph_player->get_animation_player())) {
|
||||
player = Object::cast_to<AnimationPlayer>(graph_player->get_node(graph_player->get_animation_player()));
|
||||
}
|
||||
|
||||
if (player) {
|
||||
for (Map<StringName, ProgressBar *>::Element *E = animations.front(); E; E = E->next()) {
|
||||
Ref<AnimationNodeAnimation> an = blend_tree->get_node(E->key());
|
||||
if (an.is_valid()) {
|
||||
if (player->has_animation(an->get_animation())) {
|
||||
Ref<Animation> anim = player->get_animation(an->get_animation());
|
||||
if (anim.is_valid()) {
|
||||
E->get()->set_max(anim->get_length());
|
||||
E->get()->set_value(an->get_playback_time());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationNodeBlendTreeEditor::_scroll_changed(const Vector2 &p_scroll) {
|
||||
if (updating)
|
||||
return;
|
||||
updating = true;
|
||||
blend_tree->set_graph_offset(p_scroll / EDSCALE);
|
||||
updating = false;
|
||||
}
|
||||
|
||||
void AnimationNodeBlendTreeEditor::_node_changed(ObjectID p_node) {
|
||||
|
||||
AnimationNode *an = Object::cast_to<AnimationNode>(ObjectDB::get_instance(p_node));
|
||||
if (an && an->get_tree() == blend_tree) {
|
||||
_update_graph();
|
||||
}
|
||||
}
|
||||
|
||||
void AnimationNodeBlendTreeEditor::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method("_update_graph", &AnimationNodeBlendTreeEditor::_update_graph);
|
||||
ClassDB::bind_method("_add_node", &AnimationNodeBlendTreeEditor::_add_node);
|
||||
ClassDB::bind_method("_node_dragged", &AnimationNodeBlendTreeEditor::_node_dragged);
|
||||
ClassDB::bind_method("_node_renamed", &AnimationNodeBlendTreeEditor::_node_renamed);
|
||||
ClassDB::bind_method("_node_renamed_focus_out", &AnimationNodeBlendTreeEditor::_node_renamed_focus_out);
|
||||
ClassDB::bind_method("_connection_request", &AnimationNodeBlendTreeEditor::_connection_request);
|
||||
ClassDB::bind_method("_disconnection_request", &AnimationNodeBlendTreeEditor::_disconnection_request);
|
||||
ClassDB::bind_method("_node_selected", &AnimationNodeBlendTreeEditor::_node_selected);
|
||||
ClassDB::bind_method("_open_in_editor", &AnimationNodeBlendTreeEditor::_open_in_editor);
|
||||
ClassDB::bind_method("_open_parent", &AnimationNodeBlendTreeEditor::_open_parent);
|
||||
ClassDB::bind_method("_scroll_changed", &AnimationNodeBlendTreeEditor::_scroll_changed);
|
||||
ClassDB::bind_method("_delete_request", &AnimationNodeBlendTreeEditor::_delete_request);
|
||||
ClassDB::bind_method("_edit_filters", &AnimationNodeBlendTreeEditor::_edit_filters);
|
||||
ClassDB::bind_method("_update_filters", &AnimationNodeBlendTreeEditor::_update_filters);
|
||||
ClassDB::bind_method("_filter_edited", &AnimationNodeBlendTreeEditor::_filter_edited);
|
||||
ClassDB::bind_method("_filter_toggled", &AnimationNodeBlendTreeEditor::_filter_toggled);
|
||||
ClassDB::bind_method("_oneshot_start", &AnimationNodeBlendTreeEditor::_oneshot_start);
|
||||
ClassDB::bind_method("_oneshot_stop", &AnimationNodeBlendTreeEditor::_oneshot_stop);
|
||||
ClassDB::bind_method("_node_changed", &AnimationNodeBlendTreeEditor::_node_changed);
|
||||
|
||||
ClassDB::bind_method("_anim_selected", &AnimationNodeBlendTreeEditor::_anim_selected);
|
||||
}
|
||||
|
||||
AnimationNodeBlendTreeEditor *AnimationNodeBlendTreeEditor::singleton = NULL;
|
||||
|
||||
void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<AnimationNode> p_node) {
|
||||
|
||||
String prev_name = blend_tree->get_node_name(p_node);
|
||||
ERR_FAIL_COND(prev_name == String());
|
||||
GraphNode *gn = Object::cast_to<GraphNode>(graph->get_node(prev_name));
|
||||
ERR_FAIL_COND(!gn);
|
||||
|
||||
String new_name = p_text;
|
||||
|
||||
ERR_FAIL_COND(new_name == "" || new_name.find(".") != -1 || new_name.find("/") != -1)
|
||||
|
||||
ERR_FAIL_COND(new_name == prev_name);
|
||||
|
||||
String base_name = new_name;
|
||||
int base = 1;
|
||||
String name = base_name;
|
||||
while (blend_tree->has_node(name)) {
|
||||
base++;
|
||||
name = base_name + " " + itos(base);
|
||||
}
|
||||
|
||||
updating = true;
|
||||
undo_redo->create_action("Node Renamed");
|
||||
undo_redo->add_do_method(blend_tree, "rename_node", prev_name, name);
|
||||
undo_redo->add_undo_method(blend_tree, "rename_node", name, prev_name);
|
||||
undo_redo->add_do_method(this, "_update_graph");
|
||||
undo_redo->add_undo_method(this, "_update_graph");
|
||||
undo_redo->commit_action();
|
||||
updating = false;
|
||||
gn->set_name(new_name);
|
||||
gn->set_size(gn->get_minimum_size());
|
||||
}
|
||||
|
||||
void AnimationNodeBlendTreeEditor::_node_renamed_focus_out(Node *le, Ref<AnimationNode> p_node) {
|
||||
_node_renamed(le->call("get_text"), p_node);
|
||||
}
|
||||
|
||||
AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() {
|
||||
|
||||
singleton = this;
|
||||
updating = false;
|
||||
|
||||
blend_tree = NULL;
|
||||
graph = memnew(GraphEdit);
|
||||
add_child(graph);
|
||||
graph->add_valid_right_disconnect_type(0);
|
||||
graph->add_valid_left_disconnect_type(0);
|
||||
graph->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
graph->connect("connection_request", this, "_connection_request", varray(), CONNECT_DEFERRED);
|
||||
graph->connect("disconnection_request", this, "_disconnection_request", varray(), CONNECT_DEFERRED);
|
||||
graph->connect("node_selected", this, "_node_selected");
|
||||
graph->connect("scroll_offset_changed", this, "_scroll_changed");
|
||||
|
||||
VSeparator *vs = memnew(VSeparator);
|
||||
graph->get_zoom_hbox()->add_child(vs);
|
||||
graph->get_zoom_hbox()->move_child(vs, 0);
|
||||
|
||||
goto_parent = memnew(Button);
|
||||
goto_parent->set_text(TTR("Goto Parent"));
|
||||
graph->get_zoom_hbox()->add_child(goto_parent);
|
||||
graph->get_zoom_hbox()->move_child(goto_parent, 0);
|
||||
goto_parent->hide();
|
||||
goto_parent->connect("pressed", this, "_open_parent");
|
||||
|
||||
add_node = memnew(MenuButton);
|
||||
graph->get_zoom_hbox()->add_child(add_node);
|
||||
add_node->set_text(TTR("Add Node.."));
|
||||
graph->get_zoom_hbox()->move_child(add_node, 0);
|
||||
add_node->get_popup()->connect("index_pressed", this, "_add_node");
|
||||
|
||||
add_options.push_back(AddOption("Animation", "AnimationNodeAnimation"));
|
||||
add_options.push_back(AddOption("OneShot", "AnimationNodeOneShot"));
|
||||
add_options.push_back(AddOption("Add", "AnimationNodeAdd"));
|
||||
add_options.push_back(AddOption("Blend2", "AnimationNodeBlend2"));
|
||||
add_options.push_back(AddOption("Blend3", "AnimationNodeBlend3"));
|
||||
add_options.push_back(AddOption("Seek", "AnimationNodeTimeSeek"));
|
||||
add_options.push_back(AddOption("TimeScale", "AnimationNodeTimeScale"));
|
||||
add_options.push_back(AddOption("Transition", "AnimationNodeTransition"));
|
||||
add_options.push_back(AddOption("BlendTree", "AnimationNodeBlendTree"));
|
||||
_update_options_menu();
|
||||
|
||||
error_panel = memnew(PanelContainer);
|
||||
add_child(error_panel);
|
||||
error_label = memnew(Label);
|
||||
error_panel->add_child(error_label);
|
||||
error_label->set_text("eh");
|
||||
|
||||
filter_dialog = memnew(AcceptDialog);
|
||||
add_child(filter_dialog);
|
||||
filter_dialog->set_title(TTR("Edit Filtered Tracks:"));
|
||||
|
||||
VBoxContainer *filter_vbox = memnew(VBoxContainer);
|
||||
filter_dialog->add_child(filter_vbox);
|
||||
|
||||
filter_enabled = memnew(CheckBox);
|
||||
filter_enabled->set_text(TTR("Enable filtering"));
|
||||
filter_enabled->connect("pressed", this, "_filter_toggled");
|
||||
filter_vbox->add_child(filter_enabled);
|
||||
|
||||
filters = memnew(Tree);
|
||||
filter_vbox->add_child(filters);
|
||||
filters->set_v_size_flags(SIZE_EXPAND_FILL);
|
||||
filters->set_hide_root(true);
|
||||
filters->connect("item_edited", this, "_filter_edited");
|
||||
|
||||
undo_redo = EditorNode::get_singleton()->get_undo_redo();
|
||||
}
|
||||
|
||||
void AnimationNodeBlendTreeEditorPlugin::edit(Object *p_object) {
|
||||
|
||||
anim_tree_editor->edit(Object::cast_to<AnimationNodeBlendTree>(p_object));
|
||||
}
|
||||
|
||||
bool AnimationNodeBlendTreeEditorPlugin::handles(Object *p_object) const {
|
||||
|
||||
return p_object->is_class("AnimationNodeBlendTree");
|
||||
}
|
||||
|
||||
void AnimationNodeBlendTreeEditorPlugin::make_visible(bool p_visible) {
|
||||
|
||||
if (p_visible) {
|
||||
//editor->hide_animation_player_editors();
|
||||
//editor->animation_panel_make_visible(true);
|
||||
button->show();
|
||||
editor->make_bottom_panel_item_visible(anim_tree_editor);
|
||||
anim_tree_editor->set_process(true);
|
||||
} else {
|
||||
|
||||
if (anim_tree_editor->is_visible_in_tree())
|
||||
editor->hide_bottom_panel();
|
||||
button->hide();
|
||||
anim_tree_editor->set_process(false);
|
||||
}
|
||||
}
|
||||
|
||||
AnimationNodeBlendTreeEditorPlugin::AnimationNodeBlendTreeEditorPlugin(EditorNode *p_node) {
|
||||
|
||||
editor = p_node;
|
||||
anim_tree_editor = memnew(AnimationNodeBlendTreeEditor);
|
||||
anim_tree_editor->set_custom_minimum_size(Size2(0, 300));
|
||||
|
||||
button = editor->add_bottom_panel_item(TTR("BlendTree"), anim_tree_editor);
|
||||
button->hide();
|
||||
}
|
||||
|
||||
AnimationNodeBlendTreeEditorPlugin::~AnimationNodeBlendTreeEditorPlugin() {
|
||||
}
|
115
editor/plugins/animation_blend_tree_editor_plugin.h
Normal file
115
editor/plugins/animation_blend_tree_editor_plugin.h
Normal file
@ -0,0 +1,115 @@
|
||||
#ifndef ANIMATION_BLEND_TREE_EDITOR_PLUGIN_H
|
||||
#define ANIMATION_BLEND_TREE_EDITOR_PLUGIN_H
|
||||
|
||||
#include "editor/editor_node.h"
|
||||
#include "editor/editor_plugin.h"
|
||||
#include "editor/property_editor.h"
|
||||
#include "scene/animation/animation_blend_tree.h"
|
||||
#include "scene/gui/button.h"
|
||||
#include "scene/gui/graph_edit.h"
|
||||
#include "scene/gui/popup.h"
|
||||
#include "scene/gui/tree.h"
|
||||
/**
|
||||
@author Juan Linietsky <reduzio@gmail.com>
|
||||
*/
|
||||
|
||||
class AnimationNodeBlendTreeEditor : public VBoxContainer {
|
||||
|
||||
GDCLASS(AnimationNodeBlendTreeEditor, VBoxContainer);
|
||||
|
||||
AnimationNodeBlendTree *blend_tree;
|
||||
GraphEdit *graph;
|
||||
MenuButton *add_node;
|
||||
Button *goto_parent;
|
||||
|
||||
PanelContainer *error_panel;
|
||||
Label *error_label;
|
||||
|
||||
UndoRedo *undo_redo;
|
||||
|
||||
AcceptDialog *filter_dialog;
|
||||
Tree *filters;
|
||||
CheckBox *filter_enabled;
|
||||
|
||||
Map<StringName, ProgressBar *> animations;
|
||||
|
||||
void _update_graph();
|
||||
|
||||
struct AddOption {
|
||||
String name;
|
||||
String type;
|
||||
Ref<Script> script;
|
||||
AddOption(const String &p_name = String(), const String &p_type = String()) {
|
||||
name = p_name;
|
||||
type = p_type;
|
||||
}
|
||||
};
|
||||
|
||||
Vector<AddOption> add_options;
|
||||
|
||||
void _add_node(int p_idx);
|
||||
void _update_options_menu();
|
||||
|
||||
static AnimationNodeBlendTreeEditor *singleton;
|
||||
|
||||
void _node_dragged(const Vector2 &p_from, const Vector2 &p_to, Ref<AnimationNode> p_node);
|
||||
void _node_renamed(const String &p_text, Ref<AnimationNode> p_node);
|
||||
void _node_renamed_focus_out(Node *le, Ref<AnimationNode> p_node);
|
||||
|
||||
bool updating;
|
||||
|
||||
void _connection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index);
|
||||
void _disconnection_request(const String &p_from, int p_from_index, const String &p_to, int p_to_index);
|
||||
|
||||
void _scroll_changed(const Vector2 &p_scroll);
|
||||
void _node_selected(Object *p_node);
|
||||
void _open_in_editor(const String &p_which);
|
||||
void _open_parent();
|
||||
void _anim_selected(int p_index, Array p_options, const String &p_node);
|
||||
void _delete_request(const String &p_which);
|
||||
void _oneshot_start(const StringName &p_name);
|
||||
void _oneshot_stop(const StringName &p_name);
|
||||
|
||||
bool _update_filters(const Ref<AnimationNode> &anode);
|
||||
void _edit_filters(const String &p_which);
|
||||
void _filter_edited();
|
||||
void _filter_toggled();
|
||||
Ref<AnimationNode> _filter_edit;
|
||||
|
||||
void _node_changed(ObjectID p_node);
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
static AnimationNodeBlendTreeEditor *get_singleton() { return singleton; }
|
||||
|
||||
void add_custom_type(const String &p_name, const Ref<Script> &p_script);
|
||||
void remove_custom_type(const Ref<Script> &p_script);
|
||||
|
||||
virtual Size2 get_minimum_size() const;
|
||||
void edit(AnimationNodeBlendTree *p_blend_tree);
|
||||
AnimationNodeBlendTreeEditor();
|
||||
};
|
||||
|
||||
class AnimationNodeBlendTreeEditorPlugin : public EditorPlugin {
|
||||
|
||||
GDCLASS(AnimationNodeBlendTreeEditorPlugin, EditorPlugin);
|
||||
|
||||
AnimationNodeBlendTreeEditor *anim_tree_editor;
|
||||
EditorNode *editor;
|
||||
Button *button;
|
||||
|
||||
public:
|
||||
virtual String get_name() const { return "BlendTree"; }
|
||||
bool has_main_screen() const { return false; }
|
||||
virtual void edit(Object *p_object);
|
||||
virtual bool handles(Object *p_object) const;
|
||||
virtual void make_visible(bool p_visible);
|
||||
|
||||
AnimationNodeBlendTreeEditorPlugin(EditorNode *p_node);
|
||||
~AnimationNodeBlendTreeEditorPlugin();
|
||||
};
|
||||
|
||||
#endif // ANIMATION_BLEND_TREE_EDITOR_PLUGIN_H
|
1105
scene/animation/animation_blend_tree.cpp
Normal file
1105
scene/animation/animation_blend_tree.cpp
Normal file
File diff suppressed because it is too large
Load Diff
328
scene/animation/animation_blend_tree.h
Normal file
328
scene/animation/animation_blend_tree.h
Normal file
@ -0,0 +1,328 @@
|
||||
#ifndef ANIMATION_BLEND_TREE_H
|
||||
#define ANIMATION_BLEND_TREE_H
|
||||
|
||||
#include "scene/animation/animation_graph_player.h"
|
||||
|
||||
class AnimationNodeAnimation : public AnimationNode {
|
||||
|
||||
GDCLASS(AnimationNodeAnimation, AnimationNode);
|
||||
|
||||
StringName animation;
|
||||
|
||||
uint64_t last_version;
|
||||
float time;
|
||||
float step;
|
||||
bool skip;
|
||||
|
||||
protected:
|
||||
void _validate_property(PropertyInfo &property) const;
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual String get_caption() const;
|
||||
virtual float process(float p_time, bool p_seek);
|
||||
|
||||
void set_animation(const StringName &p_name);
|
||||
StringName get_animation() const;
|
||||
|
||||
float get_playback_time() const;
|
||||
|
||||
AnimationNodeAnimation();
|
||||
};
|
||||
|
||||
class AnimationNodeOneShot : public AnimationNode {
|
||||
GDCLASS(AnimationNodeOneShot, AnimationNode);
|
||||
|
||||
public:
|
||||
enum MixMode {
|
||||
MIX_MODE_BLEND,
|
||||
MIX_MODE_ADD
|
||||
};
|
||||
|
||||
private:
|
||||
bool active;
|
||||
bool do_start;
|
||||
float fade_in;
|
||||
float fade_out;
|
||||
|
||||
bool autorestart;
|
||||
float autorestart_delay;
|
||||
float autorestart_random_delay;
|
||||
MixMode mix;
|
||||
|
||||
float time;
|
||||
float remaining;
|
||||
float autorestart_remaining;
|
||||
bool sync;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual String get_caption() const;
|
||||
|
||||
void set_fadein_time(float p_time);
|
||||
void set_fadeout_time(float p_time);
|
||||
|
||||
float get_fadein_time() const;
|
||||
float get_fadeout_time() const;
|
||||
|
||||
void set_autorestart(bool p_active);
|
||||
void set_autorestart_delay(float p_time);
|
||||
void set_autorestart_random_delay(float p_time);
|
||||
|
||||
bool has_autorestart() const;
|
||||
float get_autorestart_delay() const;
|
||||
float get_autorestart_random_delay() const;
|
||||
|
||||
void set_mix_mode(MixMode p_mix);
|
||||
MixMode get_mix_mode() const;
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
bool is_active() const;
|
||||
|
||||
void set_use_sync(bool p_sync);
|
||||
bool is_using_sync() const;
|
||||
|
||||
virtual bool has_filter() const;
|
||||
virtual float process(float p_time, bool p_seek);
|
||||
|
||||
AnimationNodeOneShot();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(AnimationNodeOneShot::MixMode)
|
||||
|
||||
class AnimationNodeAdd : public AnimationNode {
|
||||
GDCLASS(AnimationNodeAdd, AnimationNode);
|
||||
|
||||
float amount;
|
||||
bool sync;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual String get_caption() const;
|
||||
|
||||
void set_amount(float p_amount);
|
||||
float get_amount() const;
|
||||
|
||||
void set_use_sync(bool p_sync);
|
||||
bool is_using_sync() const;
|
||||
|
||||
virtual bool has_filter() const;
|
||||
virtual float process(float p_time, bool p_seek);
|
||||
|
||||
AnimationNodeAdd();
|
||||
};
|
||||
|
||||
class AnimationNodeBlend2 : public AnimationNode {
|
||||
GDCLASS(AnimationNodeBlend2, AnimationNode);
|
||||
|
||||
float amount;
|
||||
bool sync;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual String get_caption() const;
|
||||
virtual float process(float p_time, bool p_seek);
|
||||
|
||||
void set_amount(float p_amount);
|
||||
float get_amount() const;
|
||||
|
||||
void set_use_sync(bool p_sync);
|
||||
bool is_using_sync() const;
|
||||
|
||||
virtual bool has_filter() const;
|
||||
AnimationNodeBlend2();
|
||||
};
|
||||
|
||||
class AnimationNodeBlend3 : public AnimationNode {
|
||||
GDCLASS(AnimationNodeBlend3, AnimationNode);
|
||||
|
||||
float amount;
|
||||
bool sync;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual String get_caption() const;
|
||||
|
||||
void set_amount(float p_amount);
|
||||
float get_amount() const;
|
||||
|
||||
void set_use_sync(bool p_sync);
|
||||
bool is_using_sync() const;
|
||||
|
||||
float process(float p_time, bool p_seek);
|
||||
AnimationNodeBlend3();
|
||||
};
|
||||
|
||||
class AnimationNodeTimeScale : public AnimationNode {
|
||||
GDCLASS(AnimationNodeTimeScale, AnimationNode);
|
||||
|
||||
float scale;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual String get_caption() const;
|
||||
|
||||
void set_scale(float p_scale);
|
||||
float get_scale() const;
|
||||
|
||||
float process(float p_time, bool p_seek);
|
||||
|
||||
AnimationNodeTimeScale();
|
||||
};
|
||||
|
||||
class AnimationNodeTimeSeek : public AnimationNode {
|
||||
GDCLASS(AnimationNodeTimeSeek, AnimationNode);
|
||||
|
||||
float seek_pos;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
virtual String get_caption() const;
|
||||
|
||||
void set_seek_pos(float p_sec);
|
||||
float get_seek_pos() const;
|
||||
|
||||
float process(float p_time, bool p_seek);
|
||||
|
||||
AnimationNodeTimeSeek();
|
||||
};
|
||||
|
||||
class AnimationNodeTransition : public AnimationNode {
|
||||
GDCLASS(AnimationNodeTransition, AnimationNode);
|
||||
|
||||
enum {
|
||||
MAX_INPUTS = 32
|
||||
};
|
||||
struct InputData {
|
||||
|
||||
String name;
|
||||
bool auto_advance;
|
||||
InputData() { auto_advance = false; }
|
||||
};
|
||||
|
||||
InputData inputs[MAX_INPUTS];
|
||||
int enabled_inputs;
|
||||
|
||||
float prev_time;
|
||||
float prev_xfading;
|
||||
int prev;
|
||||
bool switched;
|
||||
|
||||
float time;
|
||||
int current;
|
||||
|
||||
float xfade;
|
||||
|
||||
void _update_inputs();
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
void _validate_property(PropertyInfo &property) const;
|
||||
|
||||
public:
|
||||
virtual String get_caption() const;
|
||||
|
||||
void set_enabled_inputs(int p_inputs);
|
||||
int get_enabled_inputs();
|
||||
|
||||
void set_input_as_auto_advance(int p_input, bool p_enable);
|
||||
bool is_input_set_as_auto_advance(int p_input) const;
|
||||
|
||||
void set_input_caption(int p_input, const String &p_name);
|
||||
String get_input_caption(int p_input) const;
|
||||
|
||||
void set_current(int p_current);
|
||||
int get_current() const;
|
||||
|
||||
void set_cross_fade_time(float p_fade);
|
||||
float get_cross_fade_time() const;
|
||||
|
||||
float process(float p_time, bool p_seek);
|
||||
|
||||
AnimationNodeTransition();
|
||||
};
|
||||
|
||||
class AnimationNodeOutput : public AnimationNode {
|
||||
GDCLASS(AnimationNodeOutput, AnimationNode)
|
||||
public:
|
||||
virtual String get_caption() const;
|
||||
virtual float process(float p_time, bool p_seek);
|
||||
AnimationNodeOutput();
|
||||
};
|
||||
|
||||
/////
|
||||
|
||||
class AnimationNodeBlendTree : public AnimationNode {
|
||||
GDCLASS(AnimationNodeBlendTree, AnimationNode)
|
||||
|
||||
Map<StringName, Ref<AnimationNode> > nodes;
|
||||
|
||||
Vector2 graph_offset;
|
||||
|
||||
protected:
|
||||
static void _bind_methods();
|
||||
bool _set(const StringName &p_name, const Variant &p_value);
|
||||
bool _get(const StringName &p_name, Variant &r_ret) const;
|
||||
void _get_property_list(List<PropertyInfo> *p_list) const;
|
||||
|
||||
public:
|
||||
enum ConnectionError {
|
||||
CONNECTION_OK,
|
||||
CONNECTION_ERROR_NO_INPUT,
|
||||
CONNECTION_ERROR_NO_INPUT_INDEX,
|
||||
CONNECTION_ERROR_NO_OUTPUT,
|
||||
CONNECTION_ERROR_SAME_NODE,
|
||||
CONNECTION_ERROR_CONNECTION_EXISTS,
|
||||
//no need to check for cycles due to tree topology
|
||||
};
|
||||
|
||||
void add_node(const StringName &p_name, Ref<AnimationNode> p_node);
|
||||
Ref<AnimationNode> get_node(const StringName &p_name) const;
|
||||
void remove_node(const StringName &p_name);
|
||||
void rename_node(const StringName &p_name, const StringName &p_new_name);
|
||||
bool has_node(const StringName &p_name) const;
|
||||
StringName get_node_name(const Ref<AnimationNode> &p_node) const;
|
||||
|
||||
void connect_node(const StringName &p_input_node, int p_input_index, const StringName &p_output_node);
|
||||
void disconnect_node(const StringName &p_node, int p_input_index);
|
||||
float get_connection_activity(const StringName &p_input_node, int p_input_index) const;
|
||||
|
||||
struct NodeConnection {
|
||||
StringName input_node;
|
||||
int input_index;
|
||||
StringName output_node;
|
||||
};
|
||||
|
||||
ConnectionError can_connect_node(const StringName &p_input_node, int p_input_index, const StringName &p_output_node) const;
|
||||
void get_node_connections(List<NodeConnection> *r_connections) const;
|
||||
|
||||
virtual String get_caption() const;
|
||||
virtual float process(float p_time, bool p_seek);
|
||||
|
||||
void get_node_list(List<StringName> *r_list);
|
||||
|
||||
void set_graph_offset(const Vector2 &p_graph_offset);
|
||||
Vector2 get_graph_offset() const;
|
||||
|
||||
virtual void set_graph_player(AnimationGraphPlayer *p_player);
|
||||
AnimationNodeBlendTree();
|
||||
~AnimationNodeBlendTree();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(AnimationNodeBlendTree::ConnectionError)
|
||||
|
||||
#endif // ANIMATION_BLEND_TREE_H
|
1194
scene/animation/animation_graph_player.cpp
Normal file
1194
scene/animation/animation_graph_player.cpp
Normal file
File diff suppressed because it is too large
Load Diff
261
scene/animation/animation_graph_player.h
Normal file
261
scene/animation/animation_graph_player.h
Normal file
@ -0,0 +1,261 @@
|
||||
#ifndef ANIMATION_GRAPH_PLAYER_H
|
||||
#define ANIMATION_GRAPH_PLAYER_H
|
||||
|
||||
#include "animation_player.h"
|
||||
#include "scene/3d/skeleton.h"
|
||||
#include "scene/3d/spatial.h"
|
||||
#include "scene/resources/animation.h"
|
||||
|
||||
class AnimationNodeBlendTree;
|
||||
class AnimationPlayer;
|
||||
class AnimationGraphPlayer;
|
||||
|
||||
class AnimationNode : public Resource {
|
||||
GDCLASS(AnimationNode, Resource)
|
||||
public:
|
||||
enum FilterAction {
|
||||
FILTER_IGNORE,
|
||||
FILTER_PASS,
|
||||
FILTER_STOP,
|
||||
FILTER_BLEND
|
||||
};
|
||||
|
||||
struct Input {
|
||||
|
||||
String name;
|
||||
StringName connected_to;
|
||||
float activity;
|
||||
uint64_t last_pass;
|
||||
};
|
||||
|
||||
Vector<Input> inputs;
|
||||
|
||||
float process_input(int p_input, float p_time, bool p_seek, float p_blend);
|
||||
|
||||
friend class AnimationGraphPlayer;
|
||||
|
||||
struct AnimationState {
|
||||
|
||||
Ref<Animation> animation;
|
||||
float time;
|
||||
float delta;
|
||||
const Vector<float> *track_blends;
|
||||
float blend;
|
||||
bool seeked;
|
||||
};
|
||||
|
||||
struct State {
|
||||
|
||||
int track_count;
|
||||
HashMap<NodePath, int> track_map;
|
||||
List<AnimationState> animation_states;
|
||||
bool valid;
|
||||
AnimationPlayer *player;
|
||||
String invalid_reasons;
|
||||
uint64_t last_pass;
|
||||
};
|
||||
|
||||
Vector<float> blends;
|
||||
State *state;
|
||||
float _pre_process(State *p_state, float p_time, bool p_seek);
|
||||
void _pre_update_animations(HashMap<NodePath, int> *track_map);
|
||||
Vector2 position;
|
||||
|
||||
friend class AnimationNodeBlendTree;
|
||||
AnimationNodeBlendTree *tree;
|
||||
AnimationGraphPlayer *player;
|
||||
|
||||
float _blend_node(Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true, float *r_max = NULL);
|
||||
|
||||
HashMap<NodePath, bool> filter;
|
||||
bool filter_enabled;
|
||||
|
||||
Array _get_filters() const;
|
||||
void _set_filters(const Array &p_filters);
|
||||
|
||||
protected:
|
||||
void blend_animation(const StringName &p_animation, float p_time, float p_delta, bool p_seeked, float p_blend);
|
||||
float blend_node(Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
|
||||
float blend_input(int p_input, float p_time, bool p_seek, float p_blend, FilterAction p_filter = FILTER_IGNORE, bool p_optimize = true);
|
||||
void make_invalid(const String &p_reason);
|
||||
|
||||
static void _bind_methods();
|
||||
|
||||
void _validate_property(PropertyInfo &property) const;
|
||||
|
||||
public:
|
||||
Ref<AnimationNodeBlendTree> get_tree() const;
|
||||
virtual void set_graph_player(AnimationGraphPlayer *p_player);
|
||||
AnimationGraphPlayer *get_graph_player() const;
|
||||
AnimationPlayer *get_player() const;
|
||||
|
||||
virtual float process(float p_time, bool p_seek);
|
||||
|
||||
int get_input_count() const;
|
||||
String get_input_name(int p_input);
|
||||
StringName get_input_connection(int p_input);
|
||||
void set_input_connection(int p_input, const StringName &p_connection);
|
||||
float get_input_activity(int p_input) const;
|
||||
|
||||
void add_input(const String &p_name);
|
||||
void set_input_name(int p_input, const String &p_name);
|
||||
void remove_input(int p_index);
|
||||
|
||||
virtual String get_caption() const;
|
||||
|
||||
void set_filter_path(const NodePath &p_path, bool p_enable);
|
||||
bool is_path_filtered(const NodePath &p_path) const;
|
||||
|
||||
void set_filter_enabled(bool p_enable);
|
||||
bool is_filter_enabled() const;
|
||||
|
||||
virtual bool has_filter() const;
|
||||
|
||||
void set_position(const Vector2 &p_position);
|
||||
Vector2 get_position() const;
|
||||
|
||||
AnimationNode();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(AnimationNode::FilterAction)
|
||||
|
||||
class AnimationGraphPlayer : public Node {
|
||||
GDCLASS(AnimationGraphPlayer, Node)
|
||||
public:
|
||||
enum AnimationProcessMode {
|
||||
ANIMATION_PROCESS_PHYSICS,
|
||||
ANIMATION_PROCESS_IDLE,
|
||||
};
|
||||
|
||||
private:
|
||||
struct TrackCache {
|
||||
uint64_t setup_pass;
|
||||
uint64_t process_pass;
|
||||
Animation::TrackType type;
|
||||
Object *object;
|
||||
ObjectID object_id;
|
||||
|
||||
TrackCache() {
|
||||
setup_pass = 0;
|
||||
process_pass = 0;
|
||||
object = NULL;
|
||||
object_id = 0;
|
||||
}
|
||||
virtual ~TrackCache() {}
|
||||
};
|
||||
|
||||
struct TrackCacheTransform : public TrackCache {
|
||||
Spatial *spatial;
|
||||
Skeleton *skeleton;
|
||||
int bone_idx;
|
||||
Vector3 loc;
|
||||
Quat rot;
|
||||
Vector3 scale;
|
||||
|
||||
TrackCacheTransform() {
|
||||
type = Animation::TYPE_TRANSFORM;
|
||||
spatial = NULL;
|
||||
bone_idx = -1;
|
||||
skeleton = NULL;
|
||||
}
|
||||
};
|
||||
|
||||
struct TrackCacheValue : public TrackCache {
|
||||
|
||||
Variant value;
|
||||
Vector<StringName> subpath;
|
||||
TrackCacheValue() { type = Animation::TYPE_VALUE; }
|
||||
};
|
||||
|
||||
struct TrackCacheMethod : public TrackCache {
|
||||
|
||||
TrackCacheMethod() { type = Animation::TYPE_METHOD; }
|
||||
};
|
||||
|
||||
struct TrackCacheBezier : public TrackCache {
|
||||
|
||||
float value;
|
||||
Vector<StringName> subpath;
|
||||
TrackCacheBezier() {
|
||||
type = Animation::TYPE_BEZIER;
|
||||
value = 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct TrackCacheAudio : public TrackCache {
|
||||
|
||||
bool playing;
|
||||
float start;
|
||||
float len;
|
||||
|
||||
TrackCacheAudio() {
|
||||
type = Animation::TYPE_AUDIO;
|
||||
playing = false;
|
||||
start = 0;
|
||||
len = 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct TrackCacheAnimation : public TrackCache {
|
||||
|
||||
bool playing;
|
||||
|
||||
TrackCacheAnimation() {
|
||||
type = Animation::TYPE_ANIMATION;
|
||||
playing = false;
|
||||
}
|
||||
};
|
||||
|
||||
HashMap<NodePath, TrackCache *> track_cache;
|
||||
Set<TrackCache *> playing_caches;
|
||||
|
||||
Ref<AnimationNode> root;
|
||||
|
||||
AnimationProcessMode process_mode;
|
||||
bool active;
|
||||
NodePath animation_player;
|
||||
|
||||
AnimationNode::State state;
|
||||
bool cache_valid;
|
||||
void _node_removed(Node *p_node);
|
||||
void _caches_cleared();
|
||||
|
||||
void _clear_caches();
|
||||
bool _update_caches(AnimationPlayer *player);
|
||||
void _process_graph(float p_delta);
|
||||
|
||||
uint64_t setup_pass;
|
||||
uint64_t process_pass;
|
||||
|
||||
bool started;
|
||||
|
||||
protected:
|
||||
void _notification(int p_what);
|
||||
static void _bind_methods();
|
||||
|
||||
public:
|
||||
void set_graph_root(const Ref<AnimationNode> &p_root);
|
||||
Ref<AnimationNode> get_graph_root() const;
|
||||
|
||||
void set_active(bool p_active);
|
||||
bool is_active() const;
|
||||
|
||||
void set_process_mode(AnimationProcessMode p_mode);
|
||||
AnimationProcessMode get_process_mode() const;
|
||||
|
||||
void set_animation_player(const NodePath &p_player);
|
||||
NodePath get_animation_player() const;
|
||||
|
||||
virtual String get_configuration_warning() const;
|
||||
|
||||
bool is_state_invalid() const;
|
||||
String get_invalid_state_reason() const;
|
||||
|
||||
uint64_t get_last_process_pass() const;
|
||||
AnimationGraphPlayer();
|
||||
~AnimationGraphPlayer();
|
||||
};
|
||||
|
||||
VARIANT_ENUM_CAST(AnimationGraphPlayer::AnimationProcessMode)
|
||||
|
||||
#endif // ANIMATION_GRAPH_PLAYER_H
|
@ -1327,6 +1327,7 @@ float AnimationPlayer::get_current_animation_length() const {
|
||||
void AnimationPlayer::_animation_changed() {
|
||||
|
||||
clear_caches();
|
||||
emit_signal("caches_cleared");
|
||||
}
|
||||
|
||||
void AnimationPlayer::_stop_playing_caches() {
|
||||
@ -1622,6 +1623,7 @@ void AnimationPlayer::_bind_methods() {
|
||||
ADD_SIGNAL(MethodInfo("animation_finished", PropertyInfo(Variant::STRING, "anim_name")));
|
||||
ADD_SIGNAL(MethodInfo("animation_changed", PropertyInfo(Variant::STRING, "old_name"), PropertyInfo(Variant::STRING, "new_name")));
|
||||
ADD_SIGNAL(MethodInfo("animation_started", PropertyInfo(Variant::STRING, "anim_name")));
|
||||
ADD_SIGNAL(MethodInfo("caches_cleared"));
|
||||
|
||||
BIND_ENUM_CONSTANT(ANIMATION_PROCESS_PHYSICS);
|
||||
BIND_ENUM_CONSTANT(ANIMATION_PROCESS_IDLE);
|
||||
|
@ -58,6 +58,7 @@ Error GraphEdit::connect_node(const StringName &p_from, int p_from_port, const S
|
||||
c.from_port = p_from_port;
|
||||
c.to = p_to;
|
||||
c.to_port = p_to_port;
|
||||
c.activity = 0;
|
||||
connections.push_back(c);
|
||||
top_layer->update();
|
||||
update();
|
||||
@ -624,6 +625,7 @@ void GraphEdit::_draw_cos_line(CanvasItem *p_where, const Vector2 &p_from, const
|
||||
|
||||
void GraphEdit::_connections_layer_draw() {
|
||||
|
||||
Color activity_color = get_color("activity");
|
||||
//draw connections
|
||||
List<List<Connection>::Element *> to_erase;
|
||||
for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {
|
||||
@ -661,6 +663,11 @@ void GraphEdit::_connections_layer_draw() {
|
||||
Color color = gfrom->get_connection_output_color(E->get().from_port);
|
||||
Vector2 topos = gto->get_connection_input_position(E->get().to_port) + gto->get_offset() * zoom;
|
||||
Color tocolor = gto->get_connection_input_color(E->get().to_port);
|
||||
|
||||
if (E->get().activity > 0) {
|
||||
color = color.linear_interpolate(activity_color, E->get().activity);
|
||||
tocolor = tocolor.linear_interpolate(activity_color, E->get().activity);
|
||||
}
|
||||
_draw_cos_line(connections_layer, frompos, topos, color, tocolor);
|
||||
}
|
||||
|
||||
@ -980,6 +987,23 @@ void GraphEdit::_gui_input(const Ref<InputEvent> &p_ev) {
|
||||
}
|
||||
}
|
||||
|
||||
void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity) {
|
||||
|
||||
for (List<Connection>::Element *E = connections.front(); E; E = E->next()) {
|
||||
|
||||
if (E->get().from == p_from && E->get().from_port == p_from_port && E->get().to == p_to && E->get().to_port == p_to_port) {
|
||||
|
||||
if (ABS(E->get().activity != p_activity)) {
|
||||
//update only if changed
|
||||
top_layer->update();
|
||||
connections_layer->update();
|
||||
}
|
||||
E->get().activity = p_activity;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GraphEdit::clear_connections() {
|
||||
|
||||
connections.clear();
|
||||
@ -1141,11 +1165,16 @@ void GraphEdit::_snap_value_changed(double) {
|
||||
update();
|
||||
}
|
||||
|
||||
HBoxContainer *GraphEdit::get_zoom_hbox() {
|
||||
return zoom_hb;
|
||||
}
|
||||
|
||||
void GraphEdit::_bind_methods() {
|
||||
|
||||
ClassDB::bind_method(D_METHOD("connect_node", "from", "from_port", "to", "to_port"), &GraphEdit::connect_node);
|
||||
ClassDB::bind_method(D_METHOD("is_node_connected", "from", "from_port", "to", "to_port"), &GraphEdit::is_node_connected);
|
||||
ClassDB::bind_method(D_METHOD("disconnect_node", "from", "from_port", "to", "to_port"), &GraphEdit::disconnect_node);
|
||||
ClassDB::bind_method(D_METHOD("set_connection_activity", "from", "from_port", "to", "to_port", "amount"), &GraphEdit::set_connection_activity);
|
||||
ClassDB::bind_method(D_METHOD("get_connection_list"), &GraphEdit::_get_connection_list);
|
||||
ClassDB::bind_method(D_METHOD("clear_connections"), &GraphEdit::clear_connections);
|
||||
ClassDB::bind_method(D_METHOD("get_scroll_ofs"), &GraphEdit::get_scroll_ofs);
|
||||
@ -1187,6 +1216,8 @@ void GraphEdit::_bind_methods() {
|
||||
ClassDB::bind_method(D_METHOD("_update_scroll_offset"), &GraphEdit::_update_scroll_offset);
|
||||
ClassDB::bind_method(D_METHOD("_connections_layer_draw"), &GraphEdit::_connections_layer_draw);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("get_zoom_hbox"), &GraphEdit::get_zoom_hbox);
|
||||
|
||||
ClassDB::bind_method(D_METHOD("set_selected", "node"), &GraphEdit::set_selected);
|
||||
|
||||
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "right_disconnects"), "set_right_disconnects", "is_right_disconnects_enabled");
|
||||
@ -1253,7 +1284,7 @@ GraphEdit::GraphEdit() {
|
||||
|
||||
zoom = 1;
|
||||
|
||||
HBoxContainer *zoom_hb = memnew(HBoxContainer);
|
||||
zoom_hb = memnew(HBoxContainer);
|
||||
top_layer->add_child(zoom_hb);
|
||||
zoom_hb->set_position(Vector2(10, 10));
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
#ifndef GRAPH_EDIT_H
|
||||
#define GRAPH_EDIT_H
|
||||
|
||||
#include "scene/gui/box_container.h"
|
||||
#include "scene/gui/graph_node.h"
|
||||
#include "scene/gui/scroll_bar.h"
|
||||
#include "scene/gui/slider.h"
|
||||
@ -62,6 +63,7 @@ public:
|
||||
StringName to;
|
||||
int from_port;
|
||||
int to_port;
|
||||
float activity;
|
||||
};
|
||||
|
||||
private:
|
||||
@ -157,6 +159,8 @@ private:
|
||||
Set<int> valid_left_disconnect_types;
|
||||
Set<int> valid_right_disconnect_types;
|
||||
|
||||
HBoxContainer *zoom_hb;
|
||||
|
||||
friend class GraphEditFilter;
|
||||
bool _filter_input(const Point2 &p_point);
|
||||
void _snap_toggled();
|
||||
@ -175,6 +179,8 @@ public:
|
||||
void disconnect_node(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port);
|
||||
void clear_connections();
|
||||
|
||||
void set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity);
|
||||
|
||||
void add_valid_connection_type(int p_type, int p_with_type);
|
||||
void remove_valid_connection_type(int p_type, int p_with_type);
|
||||
bool is_valid_connection_type(int p_type, int p_with_type) const;
|
||||
@ -206,6 +212,8 @@ public:
|
||||
int get_snap() const;
|
||||
void set_snap(int p_snap);
|
||||
|
||||
HBoxContainer *get_zoom_hbox();
|
||||
|
||||
GraphEdit();
|
||||
};
|
||||
|
||||
|
@ -565,6 +565,9 @@ void LineEdit::_notification(int p_what) {
|
||||
#endif
|
||||
case NOTIFICATION_RESIZED: {
|
||||
|
||||
if (expand_to_text_length) {
|
||||
window_pos = 0; //force scroll back since it's expanding to text length
|
||||
}
|
||||
set_cursor_position(get_cursor_position());
|
||||
|
||||
} break;
|
||||
@ -1098,11 +1101,12 @@ void LineEdit::set_cursor_position(int p_pos) {
|
||||
for (int i = cursor_pos; i >= window_pos; i--) {
|
||||
|
||||
if (i >= text.length()) {
|
||||
accum_width = font->get_char_size(' ').width; //anything should do
|
||||
//do not do this, because if the cursor is at the end, its just fine that it takes no space
|
||||
//accum_width = font->get_char_size(' ').width; //anything should do
|
||||
} else {
|
||||
accum_width += font->get_char_size(text[i], i + 1 < text.length() ? text[i + 1] : 0).width; //anything should do
|
||||
}
|
||||
if (accum_width >= window_width)
|
||||
if (accum_width > window_width)
|
||||
break;
|
||||
|
||||
wp = i;
|
||||
@ -1169,7 +1173,7 @@ Size2 LineEdit::get_minimum_size() const {
|
||||
int mstext = get_constant("minimum_spaces") * space_size;
|
||||
|
||||
if (expand_to_text_length) {
|
||||
mstext = MAX(mstext, font->get_string_size(text).x + space_size); //add a spce because some fonts are too exact
|
||||
mstext = MAX(mstext, font->get_string_size(text).x + space_size); //add a spce because some fonts are too exact, and because cursor needs a bit more when at the end
|
||||
}
|
||||
|
||||
min.width += mstext;
|
||||
|
@ -39,9 +39,9 @@ Size2 ProgressBar::get_minimum_size() const {
|
||||
Size2 minimum_size = bg->get_minimum_size();
|
||||
minimum_size.height = MAX(minimum_size.height, fg->get_minimum_size().height);
|
||||
minimum_size.width = MAX(minimum_size.width, fg->get_minimum_size().width);
|
||||
if (percent_visible) {
|
||||
minimum_size.height = MAX(minimum_size.height, bg->get_minimum_size().height + font->get_height());
|
||||
}
|
||||
//if (percent_visible) { this is needed, else the progressbar will collapse
|
||||
minimum_size.height = MAX(minimum_size.height, bg->get_minimum_size().height + font->get_height());
|
||||
//}
|
||||
return minimum_size;
|
||||
}
|
||||
|
||||
|
@ -63,6 +63,8 @@
|
||||
#include "scene/2d/tile_map.h"
|
||||
#include "scene/2d/visibility_notifier_2d.h"
|
||||
#include "scene/2d/y_sort.h"
|
||||
#include "scene/animation/animation_blend_tree.h"
|
||||
#include "scene/animation/animation_graph_player.h"
|
||||
#include "scene/animation/animation_player.h"
|
||||
#include "scene/animation/animation_tree_player.h"
|
||||
#include "scene/animation/tween.h"
|
||||
@ -382,6 +384,19 @@ void register_scene_types() {
|
||||
ClassDB::register_class<NavigationMesh>();
|
||||
ClassDB::register_class<Navigation>();
|
||||
|
||||
ClassDB::register_class<AnimationGraphPlayer>();
|
||||
ClassDB::register_class<AnimationNode>();
|
||||
ClassDB::register_class<AnimationNodeBlendTree>();
|
||||
ClassDB::register_class<AnimationNodeOutput>();
|
||||
ClassDB::register_class<AnimationNodeOneShot>();
|
||||
ClassDB::register_class<AnimationNodeAnimation>();
|
||||
ClassDB::register_class<AnimationNodeAdd>();
|
||||
ClassDB::register_class<AnimationNodeBlend2>();
|
||||
ClassDB::register_class<AnimationNodeBlend3>();
|
||||
ClassDB::register_class<AnimationNodeTimeScale>();
|
||||
ClassDB::register_class<AnimationNodeTimeSeek>();
|
||||
ClassDB::register_class<AnimationNodeTransition>();
|
||||
|
||||
OS::get_singleton()->yield(); //may take time to init
|
||||
|
||||
ClassDB::register_virtual_class<CollisionObject>();
|
||||
|
@ -874,6 +874,7 @@ void fill_default_theme(Ref<Theme> &theme, const Ref<Font> &default_font, const
|
||||
theme->set_stylebox("bg", "GraphEdit", make_stylebox(tree_bg_png, 4, 4, 4, 5));
|
||||
theme->set_color("grid_minor", "GraphEdit", Color(1, 1, 1, 0.05));
|
||||
theme->set_color("grid_major", "GraphEdit", Color(1, 1, 1, 0.2));
|
||||
theme->set_color("activity", "GraphEdit", Color(1, 1, 1));
|
||||
theme->set_constant("bezier_len_pos", "GraphEdit", 80 * scale);
|
||||
theme->set_constant("bezier_len_neg", "GraphEdit", 160 * scale);
|
||||
|
||||
|
@ -187,6 +187,8 @@ SceneStringNames::SceneStringNames() {
|
||||
|
||||
node_configuration_warning_changed = StaticCString::create("node_configuration_warning_changed");
|
||||
|
||||
output = StaticCString::create("output");
|
||||
|
||||
path_pp = NodePath("..");
|
||||
|
||||
_default = StaticCString::create("default");
|
||||
|
@ -199,6 +199,8 @@ public:
|
||||
|
||||
StringName node_configuration_warning_changed;
|
||||
|
||||
StringName output;
|
||||
|
||||
enum {
|
||||
MAX_MATERIALS = 32
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user