-Display on animation editor which keys are invalid and which tracks are unresolved

-Added a tool to clean up unresolved tracks and unused keys
This commit is contained in:
Juan Linietsky 2015-12-05 14:18:22 -03:00
parent 35fa048af5
commit 200b7bb87c
19 changed files with 381 additions and 20 deletions

View File

@ -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.
*/

View File

@ -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;
}

View File

@ -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;

View File

@ -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) {

View File

@ -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);

View File

@ -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) {

View File

@ -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; }

View File

@ -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;

View File

@ -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) {

View File

@ -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!

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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() {

View File

@ -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);

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

View File

@ -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;

View File

@ -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);