summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJuan Linietsky <reduzio@gmail.com>2015-12-05 14:18:22 -0300
committerJuan Linietsky <reduzio@gmail.com>2015-12-05 14:18:22 -0300
commit200b7bb87c3d8d8b2011b08ed4bd7b034ceb452f (patch)
treed9dc837018c4c2c7680491889316eedb9fe6b36c
parent35fa048af555e1f8411a2034706e9e452ce2f399 (diff)
-Display on animation editor which keys are invalid and which tracks are unresolved
-Added a tool to clean up unresolved tracks and unused keys
-rw-r--r--core/error_macros.h5
-rw-r--r--core/object.cpp27
-rw-r--r--core/object.h2
-rw-r--r--core/object_type_db.cpp39
-rw-r--r--core/object_type_db.h4
-rw-r--r--core/script_language.cpp14
-rw-r--r--core/script_language.h3
-rw-r--r--core/variant.h2
-rw-r--r--core/variant_call.cpp4
-rw-r--r--modules/gdscript/gd_script.cpp20
-rw-r--r--modules/gdscript/gd_script.h2
-rw-r--r--scene/animation/animation_player.cpp87
-rw-r--r--scene/animation/animation_player.h2
-rw-r--r--tools/editor/animation_editor.cpp171
-rw-r--r--tools/editor/animation_editor.h8
-rw-r--r--tools/editor/icons/icon_key_invalid.pngbin0 -> 239 bytes
-rw-r--r--tools/editor/icons/icon_key_invalid_hover.pngbin0 -> 239 bytes
-rw-r--r--tools/editor/plugins/animation_player_editor_plugin.cpp7
-rw-r--r--tools/editor/plugins/animation_player_editor_plugin.h4
19 files changed, 381 insertions, 20 deletions
diff --git a/core/error_macros.h b/core/error_macros.h
index 76da88287b..cc88686301 100644
--- a/core/error_macros.h
+++ b/core/error_macros.h
@@ -207,6 +207,11 @@ extern bool _err_error_exists;
_err_error_exists=false;\
} \
+#define ERR_PRINTS(m_string) \
+ { \
+ _err_print_error(FUNCTION_STR,__FILE__,__LINE__,String(m_string).utf8().get_data()); \
+ _err_error_exists=false;\
+ } \
/** Print a warning string.
*/
diff --git a/core/object.cpp b/core/object.cpp
index 3a4c06e7e7..96f0c86832 100644
--- a/core/object.cpp
+++ b/core/object.cpp
@@ -314,6 +314,7 @@ void Object::set(const StringName& p_name, const Variant& p_value, bool *r_valid
_edited=true;
#endif
+
if (script_instance) {
if (script_instance->set(p_name,p_value)) {
@@ -326,9 +327,9 @@ void Object::set(const StringName& p_name, const Variant& p_value, bool *r_valid
//try built-in setgetter
{
- if (ObjectTypeDB::set_property(this,p_name,p_value)) {
- if (r_valid)
- *r_valid=true;
+ if (ObjectTypeDB::set_property(this,p_name,p_value,r_valid)) {
+ //if (r_valid)
+ // *r_valid=true;
return;
}
}
@@ -1694,6 +1695,26 @@ void Object::get_translatable_strings(List<String> *p_strings) const {
}
+Variant::Type Object::get_static_property_type(const StringName& p_property, bool *r_valid) const {
+
+ bool valid;
+ Variant::Type t = ObjectTypeDB::get_property_type(get_type_name(),p_property,&valid);
+ if (valid) {
+ if (r_valid)
+ *r_valid=true;
+ return t;
+ }
+
+ if (get_script_instance()) {
+ return get_script_instance()->get_property_type(p_property,r_valid);
+ }
+ if (r_valid)
+ *r_valid=false;
+
+ return Variant::NIL;
+
+}
+
bool Object::is_queued_for_deletion() const {
return _is_queued_for_deletion;
}
diff --git a/core/object.h b/core/object.h
index 981a83958c..5b6361796f 100644
--- a/core/object.h
+++ b/core/object.h
@@ -606,6 +606,8 @@ public:
void set_block_signals(bool p_block);
bool is_blocking_signals() const;
+ Variant::Type get_static_property_type(const StringName& p_property,bool *r_valid=NULL) const;
+
virtual void get_translatable_strings(List<String> *p_strings) const;
virtual void get_argument_options(const StringName& p_function,int p_idx,List<String>*r_options) const;
diff --git a/core/object_type_db.cpp b/core/object_type_db.cpp
index a64b3d2715..f8ba0a9b15 100644
--- a/core/object_type_db.cpp
+++ b/core/object_type_db.cpp
@@ -612,6 +612,7 @@ void ObjectTypeDB::add_property(StringName p_type,const PropertyInfo& p_pinfo, c
psg._setptr=mb_set;
psg._getptr=mb_get;
psg.index=p_index;
+ psg.type=p_pinfo.type;
type->property_setget[p_pinfo.name]=psg;
@@ -634,7 +635,7 @@ void ObjectTypeDB::get_property_list(StringName p_type,List<PropertyInfo> *p_lis
}
}
-bool ObjectTypeDB::set_property(Object* p_object,const StringName& p_property, const Variant& p_value) {
+bool ObjectTypeDB::set_property(Object* p_object,const StringName& p_property, const Variant& p_value,bool *r_valid) {
TypeInfo *type=types.getptr(p_object->get_type_name());
@@ -643,13 +644,17 @@ bool ObjectTypeDB::set_property(Object* p_object,const StringName& p_property, c
const PropertySetGet *psg = check->property_setget.getptr(p_property);
if (psg) {
- if (!psg->setter)
+ if (!psg->setter) {
+ if (r_valid)
+ *r_valid=false;
return true; //return true but do nothing
+ }
+
+ Variant::CallError ce;
if (psg->index>=0) {
Variant index=psg->index;
const Variant* arg[2]={&index,&p_value};
- Variant::CallError ce;
// p_object->call(psg->setter,arg,2,ce);
if (psg->_setptr) {
psg->_setptr->call(p_object,arg,2,ce);
@@ -660,13 +665,16 @@ bool ObjectTypeDB::set_property(Object* p_object,const StringName& p_property, c
} else {
const Variant* arg[1]={&p_value};
- Variant::CallError ce;
if (psg->_setptr) {
psg->_setptr->call(p_object,arg,1,ce);
} else {
p_object->call(psg->setter,arg,1,ce);
}
}
+
+ if (r_valid)
+ *r_valid=ce.error==Variant::CallError::CALL_OK;
+
return true;
}
@@ -718,6 +726,29 @@ bool ObjectTypeDB::get_property(Object* p_object,const StringName& p_property, V
return false;
}
+Variant::Type ObjectTypeDB::get_property_type(const StringName& p_type, const StringName& p_property,bool *r_is_valid) {
+
+ TypeInfo *type=types.getptr(p_type);
+ TypeInfo *check=type;
+ while(check) {
+ const PropertySetGet *psg = check->property_setget.getptr(p_property);
+ if (psg) {
+
+ if (r_is_valid)
+ *r_is_valid=true;
+
+ return psg->type;
+ }
+
+ check=check->inherits_ptr;
+ }
+ if (r_is_valid)
+ *r_is_valid=false;
+
+ return Variant::NIL;
+
+}
+
void ObjectTypeDB::set_method_flags(StringName p_type,StringName p_method,int p_flags) {
diff --git a/core/object_type_db.h b/core/object_type_db.h
index bfa0f921e5..319e3ec02c 100644
--- a/core/object_type_db.h
+++ b/core/object_type_db.h
@@ -117,6 +117,7 @@ class ObjectTypeDB {
StringName getter;
MethodBind *_setptr;
MethodBind *_getptr;
+ Variant::Type type;
};
struct TypeInfo {
@@ -456,8 +457,9 @@ public:
static void add_property(StringName p_type,const PropertyInfo& p_pinfo, const StringName& p_setter, const StringName& p_getter, int p_index=-1);
static void get_property_list(StringName p_type,List<PropertyInfo> *p_list,bool p_no_inheritance=false);
- static bool set_property(Object* p_object,const StringName& p_property, const Variant& p_value);
+ static bool set_property(Object* p_object, const StringName& p_property, const Variant& p_value, bool *r_valid=NULL);
static bool get_property(Object* p_object,const StringName& p_property, Variant& r_value);
+ static Variant::Type get_property_type(const StringName& p_type, const StringName& p_property,bool *r_is_valid=NULL);
diff --git a/core/script_language.cpp b/core/script_language.cpp
index 35c50b1022..b7a0f579f4 100644
--- a/core/script_language.cpp
+++ b/core/script_language.cpp
@@ -267,6 +267,20 @@ void PlaceHolderScriptInstance::get_property_list(List<PropertyInfo> *p_properti
}
}
+Variant::Type PlaceHolderScriptInstance::get_property_type(const StringName& p_name,bool *r_is_valid) const {
+
+ if (values.has(p_name)) {
+ if (r_is_valid)
+ *r_is_valid=true;
+ return values[p_name].get_type();
+ }
+ if (r_is_valid)
+ *r_is_valid=false;
+
+ return Variant::NIL;
+}
+
+
void PlaceHolderScriptInstance::update(const List<PropertyInfo> &p_properties,const Map<StringName,Variant>& p_values) {
diff --git a/core/script_language.h b/core/script_language.h
index 5a0f673b94..9660f141c7 100644
--- a/core/script_language.h
+++ b/core/script_language.h
@@ -82,6 +82,7 @@ public:
virtual StringName get_instance_base_type() const=0; // this may not work in all scripts, will return empty if so
virtual ScriptInstance* instance_create(Object *p_this)=0;
virtual bool instance_has(const Object *p_this) const=0;
+
virtual bool has_source_code() const=0;
virtual String get_source_code() const=0;
@@ -109,6 +110,7 @@ public:
virtual bool set(const StringName& p_name, const Variant& p_value)=0;
virtual bool get(const StringName& p_name, Variant &r_ret) const=0;
virtual void get_property_list(List<PropertyInfo> *p_properties) const=0;
+ virtual Variant::Type get_property_type(const StringName& p_name,bool *r_is_valid=NULL) const=0;
virtual void get_method_list(List<MethodInfo> *p_list) const=0;
virtual bool has_method(const StringName& p_method) const=0;
@@ -208,6 +210,7 @@ public:
virtual bool set(const StringName& p_name, const Variant& p_value);
virtual bool get(const StringName& p_name, Variant &r_ret) const;
virtual void get_property_list(List<PropertyInfo> *p_properties) const;
+ virtual Variant::Type get_property_type(const StringName& p_name,bool *r_is_valid=NULL) const;
virtual void get_method_list(List<MethodInfo> *p_list) const {}
virtual bool has_method(const StringName& p_method) const { return false; }
diff --git a/core/variant.h b/core/variant.h
index 8fd9662c36..e75a2b1c92 100644
--- a/core/variant.h
+++ b/core/variant.h
@@ -390,7 +390,7 @@ public:
Variant call(const StringName& p_method,const Variant** p_args,int p_argcount,CallError &r_error);
Variant call(const StringName& p_method,const Variant& p_arg1=Variant(),const Variant& p_arg2=Variant(),const Variant& p_arg3=Variant(),const Variant& p_arg4=Variant(),const Variant& p_arg5=Variant());
- static Variant construct(const Variant::Type,const Variant** p_args,int p_argcount,CallError &r_error);
+ static Variant construct(const Variant::Type,const Variant** p_args,int p_argcount,CallError &r_error,bool p_strict=true);
void get_method_list(List<MethodInfo> *p_list) const;
bool has_method(const StringName& p_method) const;
diff --git a/core/variant_call.cpp b/core/variant_call.cpp
index 48c38f05f9..0d0248ef28 100644
--- a/core/variant_call.cpp
+++ b/core/variant_call.cpp
@@ -959,7 +959,7 @@ Variant Variant::call(const StringName& p_method,const Variant** p_args,int p_ar
#define VCALL(m_type,m_method) _VariantCall::_call_##m_type##_##m_method
-Variant Variant::construct(const Variant::Type p_type,const Variant** p_args,int p_argcount,CallError &r_error) {
+Variant Variant::construct(const Variant::Type p_type, const Variant** p_args, int p_argcount, CallError &r_error, bool p_strict) {
r_error.error=Variant::CallError::CALL_ERROR_INVALID_METHOD;
ERR_FAIL_INDEX_V(p_type,VARIANT_MAX,Variant());
@@ -1035,7 +1035,7 @@ Variant Variant::construct(const Variant::Type p_type,const Variant** p_args,int
} else if (p_argcount==1 && p_args[0]->type==p_type) {
return *p_args[0]; //copy construct
- } else if (p_argcount==1 && Variant::can_convert(p_args[0]->type,p_type)) {
+ } else if (p_argcount==1 && (!p_strict || Variant::can_convert(p_args[0]->type,p_type))) {
//near match construct
switch(p_type) {
diff --git a/modules/gdscript/gd_script.cpp b/modules/gdscript/gd_script.cpp
index 8746f92c9e..c3cc779bce 100644
--- a/modules/gdscript/gd_script.cpp
+++ b/modules/gdscript/gd_script.cpp
@@ -2274,6 +2274,26 @@ bool GDInstance::get(const StringName& p_name, Variant &r_ret) const {
return false;
}
+
+Variant::Type GDInstance::get_property_type(const StringName& p_name,bool *r_is_valid) const {
+
+
+ const GDScript *sptr=script.ptr();
+ while(sptr) {
+
+ if (sptr->member_info.has(p_name)) {
+ if (r_is_valid)
+ *r_is_valid=true;
+ return sptr->member_info[p_name].type;
+ }
+ sptr = sptr->_base;
+ }
+
+ if (r_is_valid)
+ *r_is_valid=false;
+ return Variant::NIL;
+}
+
void GDInstance::get_property_list(List<PropertyInfo> *p_properties) const {
// exported members, not doen yet!
diff --git a/modules/gdscript/gd_script.h b/modules/gdscript/gd_script.h
index 850ffec05f..3d16b59065 100644
--- a/modules/gdscript/gd_script.h
+++ b/modules/gdscript/gd_script.h
@@ -373,6 +373,8 @@ public:
virtual bool set(const StringName& p_name, const Variant& p_value);
virtual bool get(const StringName& p_name, Variant &r_ret) const;
virtual void get_property_list(List<PropertyInfo> *p_properties) const;
+ virtual Variant::Type get_property_type(const StringName& p_name,bool *r_is_valid=NULL) const;
+
virtual void get_method_list(List<MethodInfo> *p_list) const;
virtual bool has_method(const StringName& p_method) const;
diff --git a/scene/animation/animation_player.cpp b/scene/animation/animation_player.cpp
index c2ea1c8bb6..b040e59d16 100644
--- a/scene/animation/animation_player.cpp
+++ b/scene/animation/animation_player.cpp
@@ -268,6 +268,7 @@ void AnimationPlayer::_generate_node_caches(AnimationData* p_anim) {
TrackNodeCacheKey key;
key.id=id;
key.bone_idx=bone_idx;
+
if (node_cache_map.has(key)) {
@@ -278,6 +279,7 @@ void AnimationPlayer::_generate_node_caches(AnimationData* p_anim) {
node_cache_map[key]=TrackNodeCache();
p_anim->node_cache[i]=&node_cache_map[key];
+ p_anim->node_cache[i]->path=a->track_get_path(i);
p_anim->node_cache[i]->node=child;
p_anim->node_cache[i]->resource=resource;
p_anim->node_cache[i]->node_2d=child->cast_to<Node2D>();
@@ -320,6 +322,7 @@ void AnimationPlayer::_generate_node_caches(AnimationData* p_anim) {
pa.prop=property;
pa.object=resource.is_valid()?(Object*)resource.ptr():(Object*)child;
pa.special=SP_NONE;
+ pa.owner=p_anim->node_cache[i];
if (false && p_anim->node_cache[i]->node_2d) {
if (pa.prop==SceneStringNames::get_singleton()->transform_pos)
@@ -410,7 +413,7 @@ void AnimationPlayer::_animation_process_animation(AnimationData* p_anim,float p
TrackNodeCache::PropertyAnim *pa = &E->get();
- if (a->value_track_is_continuous(i) || p_delta==0) {
+ if (a->value_track_is_continuous(i) || p_delta==0) { //delta == 0 means seek
Variant value=a->value_track_interpolate(i,p_time);
@@ -436,10 +439,42 @@ void AnimationPlayer::_animation_process_animation(AnimationData* p_anim,float p
Variant value=a->track_get_key_value(i,F->get());
switch(pa->special) {
- case SP_NONE: pa->object->set(pa->prop,value); break; //you are not speshul
- case SP_NODE2D_POS: static_cast<Node2D*>(pa->object)->set_pos(value); break;
- case SP_NODE2D_ROT: static_cast<Node2D*>(pa->object)->set_rot(Math::deg2rad(value)); break;
- case SP_NODE2D_SCALE: static_cast<Node2D*>(pa->object)->set_scale(value); break;
+ case SP_NONE: {
+ bool valid;
+ pa->object->set(pa->prop,value,&valid); //you are not speshul
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ ERR_PRINTS("Failed setting track value '"+String(pa->owner->path)+"'. Check if property exists or the type of key is valid");
+ }
+#endif
+
+ } break;
+ case SP_NODE2D_POS: {
+#ifdef DEBUG_ENABLED
+ if (value.get_type()!=Variant::VECTOR2) {
+ ERR_PRINTS("Position key at time "+rtos(p_time)+" in Animation Track '"+String(pa->owner->path)+"' not of type Vector2()");
+ }
+#endif
+ static_cast<Node2D*>(pa->object)->set_pos(value);
+ } break;
+ case SP_NODE2D_ROT: {
+#ifdef DEBUG_ENABLED
+ if (value.is_num()) {
+ ERR_PRINTS("Rotation key at time "+rtos(p_time)+" in Animation Track '"+String(pa->owner->path)+"' not numerical");
+ }
+#endif
+
+ static_cast<Node2D*>(pa->object)->set_rot(Math::deg2rad(value));
+ } break;
+ case SP_NODE2D_SCALE: {
+#ifdef DEBUG_ENABLED
+ if (value.get_type()!=Variant::VECTOR2) {
+ ERR_PRINTS("Scale key at time "+rtos(p_time)+" in Animation Track '"+String(pa->owner->path)+"' not of type Vector2()");
+ }
+#endif
+
+ static_cast<Node2D*>(pa->object)->set_scale(value);
+ } break;
}
}
@@ -607,13 +642,53 @@ void AnimationPlayer::_animation_update_transforms() {
ERR_CONTINUE( pa->accum_pass!=accum_pass );
#if 1
- switch(pa->special) {
+/* switch(pa->special) {
case SP_NONE: pa->object->set(pa->prop,pa->value_accum); break; //you are not speshul
case SP_NODE2D_POS: static_cast<Node2D*>(pa->object)->set_pos(pa->value_accum); break;
case SP_NODE2D_ROT: static_cast<Node2D*>(pa->object)->set_rot(Math::deg2rad(pa->value_accum)); break;
case SP_NODE2D_SCALE: static_cast<Node2D*>(pa->object)->set_scale(pa->value_accum); break;
+ }*/
+
+ switch(pa->special) {
+
+ case SP_NONE: {
+ bool valid;
+ pa->object->set(pa->prop,pa->value_accum,&valid); //you are not speshul
+#ifdef DEBUG_ENABLED
+ if (!valid) {
+ ERR_PRINTS("Failed setting key at time "+rtos(playback.current.pos)+" in Animation '"+get_current_animation()+"', Track '"+String(pa->owner->path)+"'. Check if property exists or the type of key is right for the property");
+ }
+#endif
+
+ } break;
+ case SP_NODE2D_POS: {
+#ifdef DEBUG_ENABLED
+ if (pa->value_accum.get_type()!=Variant::VECTOR2) {
+ ERR_PRINTS("Position key at time "+rtos(playback.current.pos)+" in Animation '"+get_current_animation()+"', Track '"+String(pa->owner->path)+"' not of type Vector2()");
+ }
+#endif
+ static_cast<Node2D*>(pa->object)->set_pos(pa->value_accum);
+ } break;
+ case SP_NODE2D_ROT: {
+#ifdef DEBUG_ENABLED
+ if (pa->value_accum.is_num()) {
+ ERR_PRINTS("Rotation key at time "+rtos(playback.current.pos)+" in Animation '"+get_current_animation()+"', Track '"+String(pa->owner->path)+"' not numerical");
+ }
+#endif
+
+ static_cast<Node2D*>(pa->object)->set_rot(Math::deg2rad(pa->value_accum));
+ } break;
+ case SP_NODE2D_SCALE: {
+#ifdef DEBUG_ENABLED
+ if (pa->value_accum.get_type()!=Variant::VECTOR2) {
+ ERR_PRINTS("Scale key at time "+rtos(playback.current.pos)+" in Animation '"+get_current_animation()+"', Track '"+String(pa->owner->path)+"' not of type Vector2()");
+ }
+#endif
+
+ static_cast<Node2D*>(pa->object)->set_scale(pa->value_accum);
+ } break;
}
#else
diff --git a/scene/animation/animation_player.h b/scene/animation/animation_player.h
index 1e3c37c4d6..18cedee796 100644
--- a/scene/animation/animation_player.h
+++ b/scene/animation/animation_player.h
@@ -68,6 +68,7 @@ private:
struct TrackNodeCache {
+ NodePath path;
uint32_t id;
RES resource;
Node *node;
@@ -84,6 +85,7 @@ private:
struct PropertyAnim {
+ TrackNodeCache *owner;
SpecialProperty special; //small optimization
StringName prop;
Object *object;
diff --git a/tools/editor/animation_editor.cpp b/tools/editor/animation_editor.cpp
index ace6fda696..4ccb612a39 100644
--- a/tools/editor/animation_editor.cpp
+++ b/tools/editor/animation_editor.cpp
@@ -34,6 +34,7 @@
#include "pair.h"
#include "scene/gui/separator.h"
#include "editor_node.h"
+#include "tools/editor/plugins/animation_player_editor_plugin.h"
/* Missing to fix:
*Set
@@ -903,6 +904,23 @@ void AnimationKeyEditor::_menu_track(int p_type) {
optimize_dialog->popup_centered(Size2(250,180));
} break;
+ case TRACK_MENU_CLEAN_UP: {
+
+ cleanup_dialog->popup_centered_minsize(Size2(300,0));
+ } break;
+ case TRACK_MENU_CLEAN_UP_CONFIRM: {
+
+ if (cleanup_all->is_pressed()) {
+ List<StringName> names;
+ AnimationPlayerEditor::singleton->get_player()->get_animation_list(&names);
+ for (List<StringName>::Element *E=names.front();E;E=E->next()) {
+ _cleanup_animation(AnimationPlayerEditor::singleton->get_player()->get_animation(E->get()));
+ }
+ } else {
+ _cleanup_animation(animation);
+
+ }
+ } break;
case CURVE_SET_LINEAR: {
curve_edit->force_transition(1.0);
@@ -933,6 +951,57 @@ void AnimationKeyEditor::_menu_track(int p_type) {
}
+void AnimationKeyEditor::_cleanup_animation(Ref<Animation> p_animation) {
+
+
+ for(int i=0;i<p_animation->get_track_count();i++) {
+
+ bool prop_exists=false;
+ Variant::Type valid_type=Variant::NIL;
+ Object *obj=NULL;
+
+ RES res;
+ Node *node = root->get_node_and_resource(animation->track_get_path(i),res);
+
+ if (res.is_valid()) {
+ obj=res.ptr();
+ } else if (node) {
+ obj=node;
+ }
+
+ if (obj && animation->track_get_type(i)==Animation::TYPE_VALUE) {
+ valid_type=obj->get_static_property_type(animation->track_get_path(i).get_property(),&prop_exists);
+ }
+
+ if (!obj && cleanup_tracks->is_pressed()) {
+
+ animation->remove_track(i);
+ i--;
+ continue;
+ }
+
+ if (!prop_exists || animation->track_get_type(i)!=Animation::TYPE_VALUE || cleanup_keys->is_pressed()==false)
+ continue;
+
+ for(int j=0;j<animation->track_get_key_count(i);j++) {
+
+ Variant v = animation->track_get_key_value(i,j);
+
+ if (!Variant::can_convert(v.get_type(),valid_type)) {
+ animation->track_remove_key(i,j);
+ j--;
+ }
+ }
+
+ if (animation->track_get_key_count(i)==0 && cleanup_tracks->is_pressed()) {
+ animation->remove_track(i);
+ i--;
+ }
+ }
+
+ undo_redo->clear_history();
+ _update_paths();
+}
void AnimationKeyEditor::_animation_optimize() {
@@ -1042,6 +1111,7 @@ void AnimationKeyEditor::_track_editor_draw() {
timecolor = Color::html("ff4a414f");
Color hover_color = Color(1,1,1,0.05);
Color select_color = Color(1,1,1,0.1);
+ Color invalid_path_color = Color(1,0.6,0.4,0.5);
Color track_select_color =Color::html("ffbd8e8e");
Ref<Texture> remove_icon = get_icon("Remove","EditorIcons");
@@ -1068,6 +1138,9 @@ void AnimationKeyEditor::_track_editor_draw() {
get_icon("KeyCall","EditorIcons")
};
+ Ref<Texture> invalid_icon = get_icon("KeyInvalid","EditorIcons");
+ Ref<Texture> invalid_icon_hover = get_icon("KeyInvalidHover","EditorIcons");
+
Ref<Texture> hsize_icon = get_icon("Hsize","EditorIcons");
Ref<Texture> type_hover=get_icon("KeyHover","EditorIcons");
@@ -1254,6 +1327,23 @@ void AnimationKeyEditor::_track_editor_draw() {
break;
int y = h+i*h+sep;
+ bool prop_exists=false;
+ Variant::Type valid_type=Variant::NIL;
+ Object *obj=NULL;
+
+ RES res;
+ Node *node = root->get_node_and_resource(animation->track_get_path(idx),res);
+
+ if (res.is_valid()) {
+ obj=res.ptr();
+ } else if (node) {
+ obj=node;
+ }
+
+ if (obj && animation->track_get_type(idx)==Animation::TYPE_VALUE) {
+ valid_type=obj->get_static_property_type(animation->track_get_path(idx).get_property(),&prop_exists);
+ }
+
if (/*mouse_over.over!=MouseOver::OVER_NONE &&*/ idx==mouse_over.track) {
Color sepc=hover_color;
@@ -1274,6 +1364,8 @@ void AnimationKeyEditor::_track_editor_draw() {
ncol=track_select_color;
te->draw_string(font,Point2(ofs+Point2(type_icon[0]->get_width()+sep,y+font->get_ascent()+(sep/2))).floor(),np,ncol,name_limit-(type_icon[0]->get_width()+sep)-5);
+ if (!obj)
+ te->draw_line(ofs+Point2(0,y+h/2),ofs+Point2(name_limit,y+h/2),invalid_path_color);
te->draw_line(ofs+Point2(0,y+h),ofs+Point2(size.width,y+h),sepcolor);
@@ -1339,6 +1431,8 @@ void AnimationKeyEditor::_track_editor_draw() {
int kc=animation->track_get_key_count(idx);
bool first=true;
+
+
for(int i=0;i<kc;i++) {
@@ -1386,7 +1480,21 @@ void AnimationKeyEditor::_track_editor_draw() {
}
- te->draw_texture(tex,ofs+Point2(x,y+key_vofs).floor());
+ if (prop_exists && !Variant::can_convert(value.get_type(),valid_type)) {
+ te->draw_texture(invalid_icon,ofs+Point2(x,y+key_vofs).floor());
+ }
+
+ if (prop_exists && !Variant::can_convert(value.get_type(),valid_type)) {
+ if (tex==type_hover)
+ te->draw_texture(invalid_icon_hover,ofs+Point2(x,y+key_vofs).floor());
+ else
+ te->draw_texture(invalid_icon,ofs+Point2(x,y+key_vofs).floor());
+ } else {
+
+ te->draw_texture(tex,ofs+Point2(x,y+key_vofs).floor());
+ }
+
+
first=false;
}
@@ -2555,6 +2663,8 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
String text;
text="time: "+rtos(animation->track_get_key_time(idx,mouse_over.over_key))+"\n";
+
+
switch(animation->track_get_type(idx)) {
case Animation::TYPE_TRANSFORM: {
@@ -2569,8 +2679,33 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
} break;
case Animation::TYPE_VALUE: {
- Variant v = animation->track_get_key_value(idx,mouse_over.over_key);
- text+="value: "+String(v)+"\n";
+ Variant v = animation->track_get_key_value(idx,mouse_over.over_key);;
+ //text+="value: "+String(v)+"\n";
+
+ bool prop_exists=false;
+ Variant::Type valid_type=Variant::NIL;
+ Object *obj=NULL;
+
+ RES res;
+ Node *node = root->get_node_and_resource(animation->track_get_path(idx),res);
+
+ if (res.is_valid()) {
+ obj=res.ptr();
+ } else if (node) {
+ obj=node;
+ }
+
+ if (obj) {
+ valid_type=obj->get_static_property_type(animation->track_get_path(idx).get_property(),&prop_exists);
+ }
+
+ text+="type: "+Variant::get_type_name(v.get_type())+"\n";
+ if (prop_exists && !Variant::can_convert(v.get_type(),valid_type)) {
+ text+="value: "+String(v)+" (Invalid, expected type: "+Variant::get_type_name(valid_type)+")\n";
+ } else {
+ text+="value: "+String(v)+"\n";
+ }
+
} break;
case Animation::TYPE_METHOD: {
@@ -2593,6 +2728,9 @@ void AnimationKeyEditor::_track_editor_input_event(const InputEvent& p_input) {
} break;
}
text+="easing: "+rtos(animation->track_get_key_transition(idx,mouse_over.over_key));
+
+
+
track_editor->set_tooltip(text);
return;
@@ -2703,6 +2841,7 @@ void AnimationKeyEditor::_notification(int p_what) {
//menu_track->get_popup()->add_submenu_item("Set Transitions..","Transitions");
//menu_track->get_popup()->add_separator();
menu_track->get_popup()->add_item("Optimize Animation",TRACK_MENU_OPTIMIZE);
+ menu_track->get_popup()->add_item("Clean-Up Animation",TRACK_MENU_CLEAN_UP);
curve_linear->set_icon(get_icon("CurveLinear","EditorIcons"));
curve_in->set_icon(get_icon("CurveIn","EditorIcons"));
@@ -3862,6 +4001,32 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h
add_child(call_select);
call_select->set_title("Call Functions in Which Node?");
+ cleanup_dialog = memnew( ConfirmationDialog );
+ add_child(cleanup_dialog);
+ VBoxContainer *cleanup_vb = memnew( VBoxContainer );
+ cleanup_dialog->add_child(cleanup_vb);
+ cleanup_dialog->set_child_rect(cleanup_vb);
+ cleanup_keys = memnew( CheckButton );
+ cleanup_keys->set_text("Remove invalid keys");
+ cleanup_keys->set_pressed(true);
+ cleanup_vb->add_child(cleanup_keys);
+
+ cleanup_tracks = memnew( CheckButton );
+ cleanup_tracks->set_text("Remove unresolved and empty tracks");
+ cleanup_tracks->set_pressed(true);
+ cleanup_vb->add_child(cleanup_tracks);
+
+ cleanup_all = memnew( CheckButton );
+ cleanup_all->set_text("Clean-Up all animations");
+ cleanup_vb->add_child(cleanup_all);
+
+ cleanup_dialog->set_title("Clean up Animation(s) (NO UNDO!)");
+ cleanup_dialog->get_ok()->set_text("Clean-Up");
+
+ cleanup_dialog->connect("confirmed",this,"_menu_track",varray(TRACK_MENU_CLEAN_UP_CONFIRM));
+
+
+
}
AnimationKeyEditor::~AnimationKeyEditor() {
diff --git a/tools/editor/animation_editor.h b/tools/editor/animation_editor.h
index 629377d78e..743242fe94 100644
--- a/tools/editor/animation_editor.h
+++ b/tools/editor/animation_editor.h
@@ -89,6 +89,8 @@ class AnimationKeyEditor : public VBoxContainer {
TRACK_MENU_NEXT_STEP,
TRACK_MENU_PREV_STEP,
TRACK_MENU_OPTIMIZE,
+ TRACK_MENU_CLEAN_UP,
+ TRACK_MENU_CLEAN_UP_CONFIRM,
CURVE_SET_LINEAR,
CURVE_SET_IN,
CURVE_SET_OUT,
@@ -190,6 +192,11 @@ class AnimationKeyEditor : public VBoxContainer {
SpinBox *optimize_angular_error;
SpinBox *optimize_max_angle;
+ ConfirmationDialog *cleanup_dialog;
+ CheckButton *cleanup_keys;
+ CheckButton *cleanup_tracks;
+ CheckButton *cleanup_all;
+
SpinBox *step;
MenuButton *menu_add_track;
@@ -284,6 +291,7 @@ class AnimationKeyEditor : public VBoxContainer {
void _animation_changed();
void _animation_optimize();
+ void _cleanup_animation(Ref<Animation> p_animation);
void _scroll_changed(double);
diff --git a/tools/editor/icons/icon_key_invalid.png b/tools/editor/icons/icon_key_invalid.png
new file mode 100644
index 0000000000..e8e6c87180
--- /dev/null
+++ b/tools/editor/icons/icon_key_invalid.png
Binary files differ
diff --git a/tools/editor/icons/icon_key_invalid_hover.png b/tools/editor/icons/icon_key_invalid_hover.png
new file mode 100644
index 0000000000..6f0396d96a
--- /dev/null
+++ b/tools/editor/icons/icon_key_invalid_hover.png
Binary files differ
diff --git a/tools/editor/plugins/animation_player_editor_plugin.cpp b/tools/editor/plugins/animation_player_editor_plugin.cpp
index 6542fc8b4a..dc2c241d2f 100644
--- a/tools/editor/plugins/animation_player_editor_plugin.cpp
+++ b/tools/editor/plugins/animation_player_editor_plugin.cpp
@@ -1250,8 +1250,15 @@ void AnimationPlayerEditor::_bind_methods() {
}
+AnimationPlayerEditor *AnimationPlayerEditor::singleton=NULL;
+
+AnimationPlayer *AnimationPlayerEditor::get_player() const {
+
+ return player;
+}
AnimationPlayerEditor::AnimationPlayerEditor(EditorNode *p_editor) {
editor=p_editor;
+ singleton=this;
updating=false;
diff --git a/tools/editor/plugins/animation_player_editor_plugin.h b/tools/editor/plugins/animation_player_editor_plugin.h
index ac4d1ab6ba..839df3ddcc 100644
--- a/tools/editor/plugins/animation_player_editor_plugin.h
+++ b/tools/editor/plugins/animation_player_editor_plugin.h
@@ -162,6 +162,7 @@ class AnimationPlayerEditor : public VBoxContainer {
void _animation_tool_menu(int p_option);
void _animation_save_menu(int p_option);
+
AnimationPlayerEditor();
protected:
@@ -171,6 +172,9 @@ protected:
static void _bind_methods();
public:
+ AnimationPlayer *get_player() const;
+ static AnimationPlayerEditor *singleton;
+
Dictionary get_state() const;
void set_state(const Dictionary& p_state);