diff options
100 files changed, 4719 insertions, 3235 deletions
diff --git a/core/global_constants.cpp b/core/global_constants.cpp index 187813f9d0..962881e720 100644 --- a/core/global_constants.cpp +++ b/core/global_constants.cpp @@ -532,6 +532,7 @@ void register_global_constants() { BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_GLOBAL_DIR); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_RESOURCE_TYPE); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_MULTILINE_TEXT); + BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_PLACEHOLDER_TEXT); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_COLOR_NO_ALPHA); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_IMAGE_COMPRESS_LOSSY); BIND_GLOBAL_ENUM_CONSTANT(PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS); diff --git a/core/math/quick_hull.cpp b/core/math/quick_hull.cpp index cb923d264e..45c106102e 100644 --- a/core/math/quick_hull.cpp +++ b/core/math/quick_hull.cpp @@ -397,7 +397,6 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me Map<Edge, RetFaceConnect>::Element *F = ret_edges.find(e); ERR_CONTINUE(!F); - List<Geometry::MeshData::Face>::Element *O = F->get().left == E ? F->get().right : F->get().left; ERR_CONTINUE(O == E); ERR_CONTINUE(O == NULL); @@ -426,7 +425,6 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me Edge e2(idx, idxn); Map<Edge, RetFaceConnect>::Element *F2 = ret_edges.find(e2); - ERR_CONTINUE(!F2); //change faceconnect, point to this face instead if (F2->get().left == O) @@ -439,6 +437,15 @@ Error QuickHull::build(const Vector<Vector3> &p_points, Geometry::MeshData &r_me } } + // remove all edge connections to this face + for (Map<Edge, RetFaceConnect>::Element *E = ret_edges.front(); E; E = E->next()) { + if (E->get().left == O) + E->get().left = NULL; + + if (E->get().right == O) + E->get().right = NULL; + } + ret_edges.erase(F); //remove the edge ret_faces.erase(O); //remove the face } diff --git a/core/object.cpp b/core/object.cpp index ba8b710a84..4b2869b373 100644 --- a/core/object.cpp +++ b/core/object.cpp @@ -1476,8 +1476,13 @@ Error Object::connect(const StringName &p_signal, Object *p_to_object, const Str Signal::Target target(p_to_object->get_instance_id(), p_to_method); if (s->slot_map.has(target)) { - ERR_EXPLAIN("Signal '" + p_signal + "' is already connected to given method '" + p_to_method + "' in that object."); - ERR_FAIL_COND_V(s->slot_map.has(target), ERR_INVALID_PARAMETER); + if (p_flags & CONNECT_REFERENCE_COUNTED) { + s->slot_map[target].reference_count++; + return OK; + } else { + ERR_EXPLAIN("Signal '" + p_signal + "' is already connected to given method '" + p_to_method + "' in that object."); + ERR_FAIL_COND_V(s->slot_map.has(target), ERR_INVALID_PARAMETER); + } } Signal::Slot slot; @@ -1491,6 +1496,10 @@ Error Object::connect(const StringName &p_signal, Object *p_to_object, const Str conn.binds = p_binds; slot.conn = conn; slot.cE = p_to_object->connections.push_back(conn); + if (p_flags & CONNECT_REFERENCE_COUNTED) { + slot.reference_count = 1; + } + s->slot_map[target] = slot; return OK; @@ -1539,7 +1548,14 @@ void Object::disconnect(const StringName &p_signal, Object *p_to_object, const S ERR_FAIL(); } - p_to_object->connections.erase(s->slot_map[target].cE); + Signal::Slot *slot = &s->slot_map[target]; + + slot->reference_count--; // by default is zero, if it was not referenced it will go below it + if (slot->reference_count >= 0) { + return; + } + + p_to_object->connections.erase(slot->cE); s->slot_map.erase(target); if (s->slot_map.empty() && ClassDB::has_signal(get_class_name(), p_signal)) { @@ -1761,6 +1777,7 @@ void Object::_bind_methods() { BIND_ENUM_CONSTANT(CONNECT_DEFERRED); BIND_ENUM_CONSTANT(CONNECT_PERSIST); BIND_ENUM_CONSTANT(CONNECT_ONESHOT); + BIND_ENUM_CONSTANT(CONNECT_REFERENCE_COUNTED); } void Object::call_deferred(const StringName &p_method, VARIANT_ARG_DECLARE) { diff --git a/core/object.h b/core/object.h index 8dc3426d1d..52c9c509ab 100644 --- a/core/object.h +++ b/core/object.h @@ -71,6 +71,7 @@ enum PropertyHint { PROPERTY_HINT_GLOBAL_DIR, ///< a directory path must be passed PROPERTY_HINT_RESOURCE_TYPE, ///< a resource object type PROPERTY_HINT_MULTILINE_TEXT, ///< used for string properties that can contain multiple lines + PROPERTY_HINT_PLACEHOLDER_TEXT, ///< used to set a placeholder text for string properties PROPERTY_HINT_COLOR_NO_ALPHA, ///< used for ignoring alpha component when editing a color PROPERTY_HINT_IMAGE_COMPRESS_LOSSY, PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS, @@ -391,7 +392,8 @@ public: CONNECT_DEFERRED = 1, CONNECT_PERSIST = 2, // hint for scene to save this connection - CONNECT_ONESHOT = 4 + CONNECT_ONESHOT = 4, + CONNECT_REFERENCE_COUNTED = 8, }; struct Connection { @@ -442,8 +444,10 @@ private: struct Slot { + int reference_count; Connection conn; List<Connection>::Element *cE; + Slot() { reference_count = 0; } }; MethodInfo user; diff --git a/doc/classes/BakedLightmap.xml b/doc/classes/BakedLightmap.xml index 77895249e5..966b6dd7c4 100644 --- a/doc/classes/BakedLightmap.xml +++ b/doc/classes/BakedLightmap.xml @@ -1,8 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="BakedLightmap" inherits="VisualInstance" category="Core" version="3.1"> <brief_description> + Prerendered indirect light map for a scene. </brief_description> <description> + Baked lightmaps are an alternative workflow for adding indirect (or baked) lighting to a scene. Unlike the [GIProbe] approach, baked lightmaps work fine on low-end PCs and mobile devices as they consume almost no resources in run-time. </description> <tutorials> <link>http://docs.godotengine.org/en/3.0/tutorials/3d/baked_lightmaps.html</link> @@ -29,36 +31,49 @@ </methods> <members> <member name="bake_cell_size" type="float" setter="set_bake_cell_size" getter="get_bake_cell_size"> + Grid subdivision size for lightmapper calculation. Default value of [code]0.25[/code] will work for most cases. Increase for better lighting on small details or if your scene is very large. </member> <member name="bake_energy" type="float" setter="set_energy" getter="get_energy"> </member> <member name="bake_extents" type="Vector3" setter="set_extents" getter="get_extents"> + Size of affected area. </member> <member name="bake_hdr" type="bool" setter="set_hdr" getter="is_hdr"> + If [code]true[/code] lightmap can capture light values greater than [code]1.0[/code]. Turning this off will result in a smaller lightmap. Default value:[code]false[/code]. </member> <member name="bake_mode" type="int" setter="set_bake_mode" getter="get_bake_mode" enum="BakedLightmap.BakeMode"> + Lightmapping mode. See [enum BakeMode]. </member> <member name="bake_propagation" type="float" setter="set_propagation" getter="get_propagation"> </member> <member name="bake_quality" type="int" setter="set_bake_quality" getter="get_bake_quality" enum="BakedLightmap.BakeQuality"> + Three quality modes are available. Higher quality requires more rendering time. See [enum BakeQuality]. </member> <member name="capture_cell_size" type="float" setter="set_capture_cell_size" getter="get_capture_cell_size"> + Grid size used for real-time capture information on dynamic objects. Cannot be larger than [member bake_cell_size]. </member> <member name="image_path" type="String" setter="set_image_path" getter="get_image_path"> + Location where lightmaps will be saved. </member> <member name="light_data" type="BakedLightmapData" setter="set_light_data" getter="get_light_data"> + The calculated light data. </member> </members> <constants> <constant name="BAKE_QUALITY_LOW" value="0" enum="BakeQuality"> + Lowest bake quality mode. Fastest to calculate. </constant> <constant name="BAKE_QUALITY_MEDIUM" value="1" enum="BakeQuality"> + Default bake quality mode. </constant> <constant name="BAKE_QUALITY_HIGH" value="2" enum="BakeQuality"> + Highest bake quality mode. Takes longer to calculate. </constant> <constant name="BAKE_MODE_CONE_TRACE" value="0" enum="BakeMode"> + Less precise but faster bake mode. </constant> <constant name="BAKE_MODE_RAY_TRACE" value="1" enum="BakeMode"> + More precise bake mode but can take considerably longer to bake. </constant> <constant name="BAKE_ERROR_OK" value="0" enum="BakeError"> </constant> diff --git a/doc/classes/ColorPicker.xml b/doc/classes/ColorPicker.xml index 232357f822..4d52eacba8 100644 --- a/doc/classes/ColorPicker.xml +++ b/doc/classes/ColorPicker.xml @@ -4,7 +4,7 @@ Color picker control. </brief_description> <description> - This is a simple color picker [Control]. It's useful for selecting a color from an RGB/RGBA colorspace. + [Control] node displaying a color picker widget. It's useful for selecting a color from an RGB/RGBA colorspace. </description> <tutorials> </tutorials> @@ -17,7 +17,7 @@ <argument index="0" name="color" type="Color"> </argument> <description> - Adds the current selected to color to a list of colors (presets), the presets will be displayed in the color picker and the user will be able to select them, notice that the presets list is only for this color picker. + Adds the given color to a list of color presets. The presets are displayed in the color picker and the user will be able to select them. Note: the presets list is only for [i]this[/i] color picker. </description> </method> </methods> @@ -26,13 +26,13 @@ The currently selected color. </member> <member name="deferred_mode" type="bool" setter="set_deferred_mode" getter="is_deferred_mode"> - If [code]true[/code], the color will apply only after user releases mouse button, otherwise it will apply immediatly even in mouse motion event (which can cause performance issues). + If [code]true[/code] the color will apply only after the user releases the mouse button, otherwise it will apply immediatly even in mouse motion event (which can cause performance issues). </member> <member name="edit_alpha" type="bool" setter="set_edit_alpha" getter="is_editing_alpha"> - If [code]true[/code], shows an alpha channel slider (transparency). + If [code]true[/code] shows an alpha channel slider (transparency). </member> <member name="raw_mode" type="bool" setter="set_raw_mode" getter="is_raw_mode"> - If [code]true[/code], allows the color R, G, B component values to go beyond 1.0, which can be used for certain special operations that require it (like tinting without darkening or rendering sprites in HDR). + If [code]true[/code] allows the color R, G, B component values to go beyond 1.0, which can be used for certain special operations that require it (like tinting without darkening or rendering sprites in HDR). </member> </members> <signals> diff --git a/doc/classes/ColorPickerButton.xml b/doc/classes/ColorPickerButton.xml index d049e936a8..6ac2911c11 100644 --- a/doc/classes/ColorPickerButton.xml +++ b/doc/classes/ColorPickerButton.xml @@ -4,7 +4,7 @@ Button that pops out a [ColorPicker]. </brief_description> <description> - Encapsulates a [ColorPicker] making it accesible by pressing a button, pressing the button will toggle the [ColorPicker] visibility + Encapsulates a [ColorPicker] making it accesible by pressing a button. Pressing the button will toggle the [ColorPicker] visibility. </description> <tutorials> </tutorials> @@ -15,14 +15,14 @@ <return type="ColorPicker"> </return> <description> - Returns the [code]ColorPicker[/code] that this [code]ColorPickerButton[/code] toggles. + Returns the [ColorPicker] that this node toggles. </description> </method> <method name="get_popup"> <return type="PopupPanel"> </return> <description> - Returns the control's [PopupPanel] which allows you to connect to Popup Signals. This allows you to handle events when the ColorPicker is shown or hidden. + Returns the control's [PopupPanel] which allows you to connect to popup signals. This allows you to handle events when the ColorPicker is shown or hidden. </description> </method> </methods> diff --git a/doc/classes/ColorRect.xml b/doc/classes/ColorRect.xml index 69a70cfa39..e1bffb719e 100644 --- a/doc/classes/ColorRect.xml +++ b/doc/classes/ColorRect.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="ColorRect" inherits="Control" category="Core" version="3.1"> <brief_description> - Colored rect for canvas. + Colored rectangle. </brief_description> <description> - An object that is represented on the canvas as a rect with color. [Color] is used to set or get color info for the rect. + Displays a colored rectangle. </description> <tutorials> </tutorials> @@ -14,9 +14,9 @@ </methods> <members> <member name="color" type="Color" setter="set_frame_color" getter="get_frame_color"> - The color to fill the [code]ColorRect[/code]. + The fill color. [codeblock] - $ColorRect.color = Color(1, 0, 0, 1) # Set ColorRect node's color to red + $ColorRect.color = Color(1, 0, 0, 1) # Set ColorRect's color to red. [/codeblock] </member> </members> diff --git a/doc/classes/Control.xml b/doc/classes/Control.xml index d11b369e68..2efb529f31 100644 --- a/doc/classes/Control.xml +++ b/doc/classes/Control.xml @@ -1,7 +1,7 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="Control" inherits="CanvasItem" category="Core" version="3.1"> <brief_description> - All User Interface nodes inherit from Control. Features anchors and margins to adapt its position and size to its parent. + All User Interface nodes inherit from Control. A control's anchors and margins adapt its position and size relative to its parent. </brief_description> <description> Base class for all User Interface or [i]UI[/i] related nodes. [code]Control[/code] features a bounding rectangle that defines its extents, an anchor position relative to its parent and margins that represent an offset to the anchor. The margins update automatically when the node, any of its parents, or the screen size change. @@ -23,7 +23,7 @@ <return type="Vector2"> </return> <description> - Returns the minimum size this Control can shrink to. The node can never be smaller than this minimum size. + Returns the minimum size for this control. See [member rect_min_size]. </description> </method> <method name="_gui_input" qualifiers="virtual"> @@ -129,7 +129,7 @@ This method should only be used to test the data. Process the data in [method drop_data]. [codeblock] extends Control - + func can_drop_data(position, data): # check position if it is relevant to you # otherwise just check data @@ -148,10 +148,10 @@ Godot calls this method to pass you the [code]data[/code] from a control's [method get_drag_data] result. Godot first calls [method can_drop_data] to test if [code]data[/code] is allowed to drop at [code]position[/code] where [code]position[/code] is local to this control. [codeblock] extends ColorRect - + func can_drop_data(position, data): return typeof(data) == TYPE_DICTIONARY and data.has('color') - + func drop_data(position, data): color = data['color'] [/codeblock] @@ -173,6 +173,7 @@ <return type="Vector2"> </return> <description> + Returns [member margin_left] and [member margin_top]. See also [member rect_position]. </description> </method> <method name="get_color" qualifiers="const"> @@ -207,7 +208,7 @@ <argument index="0" name="position" type="Vector2" default="Vector2( 0, 0 )"> </argument> <description> - Returns the mouse cursor shape the control displays on mouse hover, one of the [code]CURSOR_*[/code] constants. + Returns the mouse cursor shape the control displays on mouse hover. See [enum CursorShape]. </description> </method> <method name="get_drag_data" qualifiers="virtual"> @@ -220,7 +221,7 @@ A preview that will follow the mouse that should represent the data can be set with [method set_drag_preview]. A good time to set the preview is in this method. [codeblock] extends Control - + func get_drag_data(position): var mydata = make_data() set_drag_preview(make_preview(mydata)) @@ -232,14 +233,14 @@ <return type="Vector2"> </return> <description> - Returns MARGIN_LEFT and MARGIN_TOP at the same time. This is a helper (see [method set_margin]). + Returns [member margin_right] and [member margin_bottom]. </description> </method> <method name="get_focus_owner" qualifiers="const"> <return type="Control"> </return> <description> - Return which control is owning the keyboard focus, or null if no one. + Returns the control that has the keyboard focus or [code]null[/code] if none. </description> </method> <method name="get_font" qualifiers="const"> @@ -256,7 +257,7 @@ <return type="Rect2"> </return> <description> - Return position and size of the Control, relative to the top-left corner of the [i]window[/i] Control. This is a helper (see [method get_global_position], [method get_size]). + Returns the position and size of the control relative to the top-left corner of the screen. See [member rect_position] and [member rect_size]. </description> </method> <method name="get_icon" qualifiers="const"> @@ -273,33 +274,35 @@ <return type="Vector2"> </return> <description> - Return the minimum size this Control can shrink to. A control will never be displayed or resized smaller than its minimum size. + Returns the minimum size for this control. See [member rect_min_size]. </description> </method> <method name="get_parent_area_size" qualifiers="const"> <return type="Vector2"> </return> <description> + Returns the width/height occupied in the parent control. </description> </method> <method name="get_parent_control" qualifiers="const"> <return type="Control"> </return> <description> + Returns the parent control node. </description> </method> <method name="get_rect" qualifiers="const"> <return type="Rect2"> </return> <description> - Return position and size of the Control, relative to the top-left corner of the parent Control. This is a helper (see [method get_position], [method get_size]). + Returns the position and size of the control relative to the top-left corner of the parent Control. See [member rect_position] and [member rect_size]. </description> </method> <method name="get_rotation" qualifiers="const"> <return type="float"> </return> <description> - Return the rotation (in radians) + Returns the rotation (in radians). </description> </method> <method name="get_stylebox" qualifiers="const"> @@ -318,7 +321,7 @@ <argument index="0" name="at_position" type="Vector2" default="Vector2( 0, 0 )"> </argument> <description> - Return the tooltip, which will appear when the cursor is resting over this control. + Returns the tooltip, which will appear when the cursor is resting over this control. </description> </method> <method name="grab_click_focus"> @@ -374,7 +377,7 @@ <return type="bool"> </return> <description> - Return whether the Control is the current focused control (see [method set_focus_mode]). + Returns [code]true[/code] if this is the current focused control. See [member focus_mode]. </description> </method> <method name="has_font" qualifiers="const"> @@ -457,7 +460,7 @@ <return type="void"> </return> <description> - Give up the focus, no other control will be able to receive keyboard input. + Give up the focus. No other control will be able to receive keyboard input. </description> </method> <method name="set_anchor"> @@ -516,7 +519,7 @@ <argument index="0" name="position" type="Vector2"> </argument> <description> - Sets MARGIN_LEFT and MARGIN_TOP at the same time. This is a helper (see [method set_margin]). + Sets [member margin_left] and [member margin_top] at the same time. </description> </method> <method name="set_drag_forwarding"> @@ -534,15 +537,15 @@ extends Control func _ready(): set_drag_forwarding(target_control) - + # TargetControl.gd extends Control func can_drop_data_fw(position, data, from_control): return true - + func drop_data_fw(position, data, from_control): my_handle_data(data) - + func get_drag_data_fw(position, from_control): set_drag_preview(my_preview) return my_data() @@ -564,7 +567,7 @@ <argument index="0" name="position" type="Vector2"> </argument> <description> - Sets MARGIN_RIGHT and MARGIN_BOTTOM at the same time. This is a helper (see [method set_margin]). + Sets [member margin_right] and [member margin_bottom] at the same time. </description> </method> <method name="set_margins_preset"> @@ -585,7 +588,7 @@ <argument index="0" name="radians" type="float"> </argument> <description> - Set the rotation (in radians). + Sets the rotation (in radians). </description> </method> <method name="show_modal"> @@ -594,7 +597,7 @@ <argument index="0" name="exclusive" type="bool" default="false"> </argument> <description> - Display a Control as modal. Control must be a subwindow. Modal controls capture the input signals until closed or the area outside them is accessed. When a modal control loses focus, or the ESC key is pressed, they automatically hide. Modal controls are used extensively for popup dialogs and menus. + Displays a control as modal. Control must be a subwindow. Modal controls capture the input signals until closed or the area outside them is accessed. When a modal control loses focus, or the ESC key is pressed, they automatically hide. Modal controls are used extensively for popup dialogs and menus. </description> </method> <method name="warp_mouse"> @@ -753,22 +756,22 @@ </signals> <constants> <constant name="FOCUS_NONE" value="0" enum="FocusMode"> - The node cannot grab focus. Use with [member set_focus_mode]. + The node cannot grab focus. Use with [member focus_mode]. </constant> <constant name="FOCUS_CLICK" value="1" enum="FocusMode"> - The node can only grab focus on mouse clicks. Use with [member set_focus_mode]. + The node can only grab focus on mouse clicks. Use with [member focus_mode]. </constant> <constant name="FOCUS_ALL" value="2" enum="FocusMode"> - The node can grab focus on mouse click or using the arrows and the Tab keys on the keyboard. Use with [member set_focus_mode]. + The node can grab focus on mouse click or using the arrows and the Tab keys on the keyboard. Use with [member focus_mode]. </constant> <constant name="NOTIFICATION_RESIZED" value="40"> Sent when the node changes size. Use [member rect_size] to get the new size. </constant> <constant name="NOTIFICATION_MOUSE_ENTER" value="41"> - Sent when the mouse pointer enters the node's [code]Rect[/code] area. + Sent when the mouse pointer enters the node. </constant> <constant name="NOTIFICATION_MOUSE_EXIT" value="42"> - Sent when the mouse pointer exits the node's [code]Rect[/code] area. + Sent when the mouse pointer exits the node. </constant> <constant name="NOTIFICATION_FOCUS_ENTER" value="43"> Sent when the node grabs focus. @@ -777,7 +780,7 @@ Sent when the node loses focus. </constant> <constant name="NOTIFICATION_THEME_CHANGED" value="45"> - Sent when the node's [member theme] changes, right before Godot redraws the [code]Control[/code]. Happens when you call one of the [code]add_*_override[/code] + Sent when the node's [member theme] changes, right before Godot redraws the control. Happens when you call one of the [code]add_*_override[/code] </constant> <constant name="NOTIFICATION_MODAL_CLOSE" value="46"> Sent when an open modal dialog closes. See [member show_modal]. @@ -903,7 +906,7 @@ Sets the node's size flags to both fill and expand. See the 2 constants above for more information. </constant> <constant name="SIZE_SHRINK_CENTER" value="4" enum="SizeFlags"> - Tells the parent [Container] to center the node in itself. It centers the [code]Control[/code] based on its bounding box, so it doesn't work with the fill or expand size flags. Use with [member size_flags_horizontal] and [member size_flags_vertical]. + Tells the parent [Container] to center the node in itself. It centers the control based on its bounding box, so it doesn't work with the fill or expand size flags. Use with [member size_flags_horizontal] and [member size_flags_vertical]. </constant> <constant name="SIZE_SHRINK_END" value="8" enum="SizeFlags"> Tells the parent [Container] to align the node with its end, either the bottom or the right edge. It doesn't work with the fill or expand size flags. Use with [member size_flags_horizontal] and [member size_flags_vertical]. diff --git a/doc/classes/DirectionalLight.xml b/doc/classes/DirectionalLight.xml index ef75182811..2dc522083d 100644 --- a/doc/classes/DirectionalLight.xml +++ b/doc/classes/DirectionalLight.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="DirectionalLight" inherits="Light" category="Core" version="3.1"> <brief_description> - Directional Light, such as the Sun or the Moon. + Directional light from a distance, as from the Sun. </brief_description> <description> - A DirectionalLight is a type of [Light] node that emits light constantly in one direction (the negative z axis of the node). It is used lights with strong intensity that are located far away from the scene to model sunlight or moonlight. The worldspace location of the DirectionalLight transform (origin) is ignored, only the basis is used do determine light direction. + A directional light is a type of [Light] node that models an infinite number of parallel rays covering the entire scene. It is used for lights with strong intensity that are located far away from the scene to model sunlight or moonlight. The worldspace location of the DirectionalLight transform (origin) is ignored. Only the basis is used do determine light direction. </description> <tutorials> <link>http://docs.godotengine.org/en/3.0/tutorials/3d/lights_and_shadows.html</link> @@ -15,34 +15,48 @@ </methods> <members> <member name="directional_shadow_bias_split_scale" type="float" setter="set_param" getter="get_param"> + Amount of extra bias for shadow splits that are far away. If self shadowing occurs only on the splits far away, this value can fix them. </member> <member name="directional_shadow_blend_splits" type="bool" setter="set_blend_splits" getter="is_blend_splits_enabled"> + If [code]true[/code] shadow detail is sacrificed in exchange for smoother transitions between splits. Default value:[code]false[/code]. </member> <member name="directional_shadow_depth_range" type="int" setter="set_shadow_depth_range" getter="get_shadow_depth_range" enum="DirectionalLight.ShadowDepthRange"> + Optimizes shadow rendering for detail versus movement. See [enum ShadowDepthRange]. </member> <member name="directional_shadow_max_distance" type="float" setter="set_param" getter="get_param"> + The maximum distance for shadow splits. </member> <member name="directional_shadow_mode" type="int" setter="set_shadow_mode" getter="get_shadow_mode" enum="DirectionalLight.ShadowMode"> + The light's shadow rendering algorithm. See [enum ShadowMode]. </member> <member name="directional_shadow_normal_bias" type="float" setter="set_param" getter="get_param"> + Can be used to fix special cases of self shadowing when objects are perpendicular to the light. </member> <member name="directional_shadow_split_1" type="float" setter="set_param" getter="get_param"> + The distance from camera to shadow split 1. Relative to [member directional_shadow_max_distance]. Only used in [enum directional_shadow_mode] SHADOW_PARALLEL_*_SPLITS. </member> <member name="directional_shadow_split_2" type="float" setter="set_param" getter="get_param"> + The distance from shadow split 1 to split 2. Relative to [member directional_shadow_max_distance]. Only used in [enum directional_shadow_mode] SHADOW_PARALLEL_*_SPLITS. </member> <member name="directional_shadow_split_3" type="float" setter="set_param" getter="get_param"> + The distance from shadow split 2 to split 3. Relative to [member directional_shadow_max_distance]. Only used in [enum directional_shadow_mode] SHADOW_PARALLEL_4_SPLITS. </member> </members> <constants> <constant name="SHADOW_ORTHOGONAL" value="0" enum="ShadowMode"> + Renders the entire scene's shadow map from an orthogonal point of view. May result in blockier shadows on close objects. </constant> <constant name="SHADOW_PARALLEL_2_SPLITS" value="1" enum="ShadowMode"> + Splits the view frustum in 2 areas, each with its own shadow map. </constant> <constant name="SHADOW_PARALLEL_4_SPLITS" value="2" enum="ShadowMode"> + Splits the view frustum in 4 areas, each with its own shadow map. </constant> <constant name="SHADOW_DEPTH_RANGE_STABLE" value="0" enum="ShadowDepthRange"> + Keeps the shadow stable when the camera moves, at the cost of lower effective shadow resolution. Default value. </constant> <constant name="SHADOW_DEPTH_RANGE_OPTIMIZED" value="1" enum="ShadowDepthRange"> + Tries to achieve maximum shadow resolution. May result in saw effect on shadow edges. </constant> </constants> </class> diff --git a/doc/classes/InputEventKey.xml b/doc/classes/InputEventKey.xml index 7503e53188..a013ee6266 100644 --- a/doc/classes/InputEventKey.xml +++ b/doc/classes/InputEventKey.xml @@ -16,6 +16,7 @@ <return type="int"> </return> <description> + Returns the scancode combined with modifier keys such as [code]Shift[/code] or [code]Alt[/code]. See also [InputEventWithModifiers]. </description> </method> </methods> diff --git a/doc/classes/InputMap.xml b/doc/classes/InputMap.xml index 2f5fb49dba..cbcff1dd75 100644 --- a/doc/classes/InputMap.xml +++ b/doc/classes/InputMap.xml @@ -40,6 +40,7 @@ <argument index="0" name="action" type="String"> </argument> <description> + Removes all events from an action. </description> </method> <method name="action_has_event"> @@ -50,7 +51,7 @@ <argument index="1" name="event" type="InputEvent"> </argument> <description> - Returns [true] if an action has an [InputEvent] associated with it. + Returns [code]true[/code] if the action has the given [InputEvent] associated with it. </description> </method> <method name="action_set_deadzone"> @@ -71,7 +72,7 @@ <argument index="1" name="deadzone" type="float" default="0.5"> </argument> <description> - Adds an (empty) action to the [code]InputMap[/code], with a configurable [code]deadzone[/code]. + Adds an empty action to the [code]InputMap[/code] with a configurable [code]deadzone[/code]. An [InputEvent] can then be added to this action with [method action_add_event]. </description> </method> diff --git a/doc/classes/ItemList.xml b/doc/classes/ItemList.xml index 48ca0ddc01..4723cf8ee4 100644 --- a/doc/classes/ItemList.xml +++ b/doc/classes/ItemList.xml @@ -77,6 +77,14 @@ <description> </description> </method> + <method name="get_item_custom_fg_color" qualifiers="const"> + <return type="Color"> + </return> + <argument index="0" name="idx" type="int"> + </argument> + <description> + </description> + </method> <method name="get_item_icon" qualifiers="const"> <return type="Texture"> </return> @@ -227,6 +235,16 @@ <description> </description> </method> + <method name="set_item_custom_fg_color"> + <return type="void"> + </return> + <argument index="0" name="idx" type="int"> + </argument> + <argument index="1" name="custom_fg_color" type="Color"> + </argument> + <description> + </description> + </method> <method name="set_item_disabled"> <return type="void"> </return> diff --git a/doc/classes/KinematicBody2D.xml b/doc/classes/KinematicBody2D.xml index fdc974630f..cdb1b0aa68 100644 --- a/doc/classes/KinematicBody2D.xml +++ b/doc/classes/KinematicBody2D.xml @@ -115,6 +115,8 @@ <argument index="6" name="floor_max_angle" type="float" default="0.785398"> </argument> <description> + Moves the body while keeping it attached to slopes. Similar to [me + As long as the [code]snap[/code] vector is in contact with the ground, the body will remain attached to the surface. This means you must disable snap in order to jump, for example. You can do this by setting[code]snap[/code] to[code](0, 0)[/code] or by using [method move_and_slide] instead. </description> </method> <method name="test_move"> @@ -136,6 +138,7 @@ If the body is at least this close to another body, this body will consider them to be colliding. </member> <member name="motion/sync_to_physics" type="bool" setter="set_sync_to_physics" getter="is_sync_to_physics_enabled"> + If [code]true[/code] the body's movement will be synchronized to the physics frame. This is useful when animating movement via [AnimationPlayer], for example on moving platforms. </member> </members> <constants> diff --git a/doc/classes/Light.xml b/doc/classes/Light.xml index e9b36c2f9d..04191136a8 100644 --- a/doc/classes/Light.xml +++ b/doc/classes/Light.xml @@ -15,28 +15,40 @@ </methods> <members> <member name="editor_only" type="bool" setter="set_editor_only" getter="is_editor_only"> + If [code]true[/code] the light only appears in the editor and will not be visible at runtime. Default value:[code]false[/code]. </member> <member name="light_bake_mode" type="int" setter="set_bake_mode" getter="get_bake_mode" enum="Light.BakeMode"> + The light's bake mode. See [enum BakeMode]. </member> <member name="light_color" type="Color" setter="set_color" getter="get_color"> + The light's color. </member> <member name="light_cull_mask" type="int" setter="set_cull_mask" getter="get_cull_mask"> + The light will affect objects in the selected layers. </member> <member name="light_energy" type="float" setter="set_param" getter="get_param"> + The light's strength multiplier. </member> <member name="light_indirect_energy" type="float" setter="set_param" getter="get_param"> + Secondary multiplier used with indirect light (light bounces). This works in baked light or GIProbe. </member> <member name="light_negative" type="bool" setter="set_negative" getter="is_negative"> + If [code]true[/code] the light's effect is reversed, darkening areas and casting bright shadows. Default value: [code]false[/code]. </member> <member name="light_specular" type="float" setter="set_param" getter="get_param"> + The intensity of the specular blob in objects affected by the light. At [code]0[/code] the light becomes a pure diffuse light. </member> <member name="shadow_bias" type="float" setter="set_param" getter="get_param"> + Used to adjust shadow appearance. Too small a value results in self shadowing, while too large a value causes shadows to separate from casters. Adjust as needed. </member> <member name="shadow_color" type="Color" setter="set_shadow_color" getter="get_shadow_color"> + The color of shadows cast by this light. </member> <member name="shadow_contact" type="float" setter="set_param" getter="get_param"> + Attempts to reduce [member shadow_bias] gap. </member> <member name="shadow_enabled" type="bool" setter="set_shadow" getter="has_shadow"> + If [code]true[/code] the light will cast shadows. Default value: [code]false[/code]. </member> <member name="shadow_reverse_cull_face" type="bool" setter="set_shadow_reverse_cull_face" getter="get_shadow_reverse_cull_face"> </member> @@ -75,10 +87,13 @@ <constant name="PARAM_MAX" value="15" enum="Param"> </constant> <constant name="BAKE_DISABLED" value="0" enum="BakeMode"> + Light is ignored when baking. Note: hiding a light does [i]not[/i] affect baking. </constant> <constant name="BAKE_INDIRECT" value="1" enum="BakeMode"> + Only indirect lighting will be baked. Default value. </constant> <constant name="BAKE_ALL" value="2" enum="BakeMode"> + Both direct and indirect light will be baked. Note: you should hide the light if you don't want it to appear twice (dynamic and baked). </constant> </constants> </class> diff --git a/doc/classes/OmniLight.xml b/doc/classes/OmniLight.xml index ff2e77ffbe..5ed058bb06 100644 --- a/doc/classes/OmniLight.xml +++ b/doc/classes/OmniLight.xml @@ -1,10 +1,10 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="OmniLight" inherits="Light" category="Core" version="3.1"> <brief_description> - OmniDirectional Light, such as a light bulb or a candle. + Omnidirectional light, such as a light bulb or a candle. </brief_description> <description> - An OmniDirectional light is a type of [Light] node that emits lights in all directions. The light is attenuated through the distance and this attenuation can be configured by changing the energy, radius and attenuation parameters of [Light]. + An Omnidirectional light is a type of [Light] that emits light in all directions. The light is attenuated by distance and this attenuation can be configured by changing its energy, radius, and attenuation parameters. </description> <tutorials> <link>http://docs.godotengine.org/en/3.0/tutorials/3d/lights_and_shadows.html</link> @@ -15,12 +15,16 @@ </methods> <members> <member name="omni_attenuation" type="float" setter="set_param" getter="get_param"> + The light's attenuation (drop-off) curve. A number of presets are available in the Inspector. </member> <member name="omni_range" type="float" setter="set_param" getter="get_param"> + Maximum distance the light affects. </member> <member name="omni_shadow_detail" type="int" setter="set_shadow_detail" getter="get_shadow_detail" enum="OmniLight.ShadowDetail"> + See [enum ShadowDetail]. </member> <member name="omni_shadow_mode" type="int" setter="set_shadow_mode" getter="get_shadow_mode" enum="OmniLight.ShadowMode"> + See [enum ShadowMode]. </member> </members> <constants> diff --git a/doc/classes/ProgressBar.xml b/doc/classes/ProgressBar.xml index 919ecd7c86..0f03b7b80a 100644 --- a/doc/classes/ProgressBar.xml +++ b/doc/classes/ProgressBar.xml @@ -14,6 +14,7 @@ </methods> <members> <member name="percent_visible" type="bool" setter="set_percent_visible" getter="is_percent_visible"> + If [code]true[/code] the fill percentage is displayed on the bar. Default value: [code]true[/code]. </member> </members> <constants> diff --git a/doc/classes/Range.xml b/doc/classes/Range.xml index fa7e20eff6..46a6132b94 100644 --- a/doc/classes/Range.xml +++ b/doc/classes/Range.xml @@ -17,30 +17,32 @@ <argument index="0" name="with" type="Node"> </argument> <description> - Binds two Ranges together along with any Ranges previously grouped with either of them. When any of Range's member variables change, it will share the new value with all other Ranges in its group. + Binds two ranges together along with any ranges previously grouped with either of them. When any of range's member variables change, it will share the new value with all other ranges in its group. </description> </method> <method name="unshare"> <return type="void"> </return> <description> - Stop Range from sharing its member variables with any other Range. + Stop range from sharing its member variables with any other. </description> </method> </methods> <members> <member name="allow_greater" type="bool" setter="set_allow_greater" getter="is_greater_allowed"> + If [code]true[/code] [member value] may be greater than [member max_value]. Default value: [code]false[/code]. </member> <member name="allow_lesser" type="bool" setter="set_allow_lesser" getter="is_lesser_allowed"> + If [code]true[/code] [member value] may be less than [member min_value]. Default value: [code]false[/code]. </member> <member name="exp_edit" type="bool" setter="set_exp_ratio" getter="is_ratio_exp"> If [code]true[/code] and [code]min_value[/code] is greater than 0, [code]value[/code] will be represented exponentially rather than linearly. </member> <member name="max_value" type="float" setter="set_max" getter="get_max"> - Maximum value. Range is clamped if [code]value[/code] is greater than [code]max_value[/code]. Default value: 100. + Maximum value. Range is clamped if [code]value[/code] is greater than [code]max_value[/code]. Default value: [code]100[/code]. </member> <member name="min_value" type="float" setter="set_min" getter="get_min"> - Minimum value. Range is clamped if [code]value[/code] is less than [code]min_value[/code]. Default value: 0. + Minimum value. Range is clamped if [code]value[/code] is less than [code]min_value[/code]. Default value: [code]0[/code]. </member> <member name="page" type="float" setter="set_page" getter="get_page"> Page size. Used mainly for [ScrollBar]. ScrollBar's length is its size multiplied by [code]page[/code] over the difference between [code]min_value[/code] and [code]max_value[/code]. @@ -49,7 +51,7 @@ The value mapped between 0 and 1. </member> <member name="rounded" type="bool" setter="set_use_rounded_values" getter="is_using_rounded_values"> - If [code]true[/code], [code]value[/code] will always be rounded to the nearest integer. + If [code]true[/code] [code]value[/code] will always be rounded to the nearest integer. Default value: [code]false[/code]. </member> <member name="step" type="float" setter="set_step" getter="get_step"> If greater than 0, [code]value[/code] will always be rounded to a multiple of [code]step[/code]. If [code]rounded[/code] is also [code]true[/code], [code]value[/code] will first be rounded to a multiple of [code]step[/code] then rounded to the nearest integer. @@ -61,14 +63,14 @@ <signals> <signal name="changed"> <description> - This signal is emitted when min, max, range or step change. + Emitted when [member min_value], [member max_value], [member page], or [member step] change. </description> </signal> <signal name="value_changed"> <argument index="0" name="value" type="float"> </argument> <description> - This signal is emitted when value changes. + Emitted when [member value] changes. </description> </signal> </signals> diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml index 7121bc8b9c..dadc7c5ffa 100644 --- a/doc/classes/TileSet.xml +++ b/doc/classes/TileSet.xml @@ -58,7 +58,7 @@ <return type="void"> </return> <description> - Clear all tiles. + Clears all tiles. </description> </method> <method name="create_tile"> @@ -67,7 +67,7 @@ <argument index="0" name="id" type="int"> </argument> <description> - Create a new tile which will be referenced by the given ID. + Creates a new tile which will be referenced by the given ID. </description> </method> <method name="find_tile_by_name" qualifiers="const"> @@ -76,21 +76,21 @@ <argument index="0" name="name" type="String"> </argument> <description> - Find the first tile matching the given name. + Returns the first tile matching the given name. </description> </method> <method name="get_last_unused_tile_id" qualifiers="const"> <return type="int"> </return> <description> - Return the ID following the last currently used ID, useful when creating a new tile. + Returns the ID following the last currently used ID, useful when creating a new tile. </description> </method> <method name="get_tiles_ids" qualifiers="const"> <return type="Array"> </return> <description> - Return an array of all currently used tile IDs. + Returns an array of all currently used tile IDs. </description> </method> <method name="remove_tile"> @@ -99,7 +99,7 @@ <argument index="0" name="id" type="int"> </argument> <description> - Remove the tile referenced by the given ID. + Removes the tile referenced by the given ID. </description> </method> <method name="tile_add_shape"> @@ -124,7 +124,7 @@ <argument index="0" name="id" type="int"> </argument> <description> - Return the light occluder of the tile. + Returns the light occluder of the tile. </description> </method> <method name="tile_get_material" qualifiers="const"> @@ -133,7 +133,7 @@ <argument index="0" name="id" type="int"> </argument> <description> - Return the material of the tile. + Returns the material of the tile. </description> </method> <method name="tile_get_modulate" qualifiers="const"> @@ -150,7 +150,7 @@ <argument index="0" name="id" type="int"> </argument> <description> - Return the name of the tile. + Returns the name of the tile. </description> </method> <method name="tile_get_navigation_polygon" qualifiers="const"> @@ -159,7 +159,7 @@ <argument index="0" name="id" type="int"> </argument> <description> - Return the navigation polygon of the tile. + Returns the navigation polygon of the tile. </description> </method> <method name="tile_get_navigation_polygon_offset" qualifiers="const"> @@ -168,7 +168,7 @@ <argument index="0" name="id" type="int"> </argument> <description> - Return the offset of the tile's navigation polygon. + Returns the offset of the tile's navigation polygon. </description> </method> <method name="tile_get_normal_map" qualifiers="const"> @@ -185,7 +185,7 @@ <argument index="0" name="id" type="int"> </argument> <description> - Return the offset of the tile's light occluder. + Returns the offset of the tile's light occluder. </description> </method> <method name="tile_get_region" qualifiers="const"> @@ -194,7 +194,7 @@ <argument index="0" name="id" type="int"> </argument> <description> - Return the tile sub-region in the texture. + Returns the tile sub-region in the texture. </description> </method> <method name="tile_get_shape" qualifiers="const"> @@ -241,7 +241,7 @@ <argument index="0" name="id" type="int"> </argument> <description> - Return the array of shapes of the tile. + Returns the array of shapes of the tile. </description> </method> <method name="tile_get_texture" qualifiers="const"> @@ -250,7 +250,7 @@ <argument index="0" name="id" type="int"> </argument> <description> - Return the texture of the tile. + Returns the texture of the tile. </description> </method> <method name="tile_get_texture_offset" qualifiers="const"> @@ -259,7 +259,7 @@ <argument index="0" name="id" type="int"> </argument> <description> - Return the texture offset of the tile. + Returns the texture offset of the tile. </description> </method> <method name="tile_get_tile_mode" qualifiers="const"> @@ -286,7 +286,7 @@ <argument index="1" name="light_occluder" type="OccluderPolygon2D"> </argument> <description> - Set a light occluder for the tile. + Sets a light occluder for the tile. </description> </method> <method name="tile_set_material"> @@ -297,7 +297,7 @@ <argument index="1" name="material" type="ShaderMaterial"> </argument> <description> - Set the material of the tile. + Sets the tile's material. </description> </method> <method name="tile_set_modulate"> @@ -308,6 +308,7 @@ <argument index="1" name="color" type="Color"> </argument> <description> + Sets the tile's modulation color. </description> </method> <method name="tile_set_name"> @@ -318,7 +319,7 @@ <argument index="1" name="name" type="String"> </argument> <description> - Set the name of the tile, for descriptive purposes. + Sets the tile's name. </description> </method> <method name="tile_set_navigation_polygon"> @@ -329,7 +330,7 @@ <argument index="1" name="navigation_polygon" type="NavigationPolygon"> </argument> <description> - Set a navigation polygon for the tile. + Sets the tile's navigation polygon. </description> </method> <method name="tile_set_navigation_polygon_offset"> @@ -340,7 +341,7 @@ <argument index="1" name="navigation_polygon_offset" type="Vector2"> </argument> <description> - Set an offset for the tile's navigation polygon. + Sets an offset for the tile's navigation polygon. </description> </method> <method name="tile_set_normal_map"> @@ -351,6 +352,7 @@ <argument index="1" name="normal_map" type="Texture"> </argument> <description> + Sets the tile's normal map texture. </description> </method> <method name="tile_set_occluder_offset"> @@ -372,7 +374,7 @@ <argument index="1" name="region" type="Rect2"> </argument> <description> - Set the tile sub-region in the texture. This is common in texture atlases. + Set the tile's sub-region in the texture. This is common in texture atlases. </description> </method> <method name="tile_set_shape"> @@ -419,7 +421,7 @@ <argument index="1" name="shapes" type="Array"> </argument> <description> - Set an array of shapes for the tile, enabling physics to collide with it. + Sets an array of shapes for the tile, enabling collision. </description> </method> <method name="tile_set_texture"> @@ -430,7 +432,7 @@ <argument index="1" name="texture" type="Texture"> </argument> <description> - Set the texture of the tile. + Sets the tile's texture. </description> </method> <method name="tile_set_texture_offset"> @@ -441,7 +443,7 @@ <argument index="1" name="texture_offset" type="Vector2"> </argument> <description> - Set the texture offset of the tile. + Sets the tile's texture offset. </description> </method> <method name="tile_set_tile_mode"> @@ -452,6 +454,7 @@ <argument index="1" name="tilemode" type="int" enum="TileSet.TileMode"> </argument> <description> + Sets the tile's [enum TileMode]. </description> </method> <method name="tile_set_z_index"> @@ -462,6 +465,7 @@ <argument index="1" name="z_index" type="int"> </argument> <description> + Sets the tile's drawing index. </description> </method> </methods> diff --git a/doc/classes/Viewport.xml b/doc/classes/Viewport.xml index af0712d357..05649193a6 100644 --- a/doc/classes/Viewport.xml +++ b/doc/classes/Viewport.xml @@ -22,35 +22,35 @@ <return type="World"> </return> <description> - Return the 3D world of the viewport, or if no such present, the one of the parent viewport. + Returns the 3D world of the viewport, or if none the world of the parent viewport. </description> </method> <method name="find_world_2d" qualifiers="const"> <return type="World2D"> </return> <description> - Return the 2D world of the viewport. + Returns the 2D world of the viewport. </description> </method> <method name="get_camera" qualifiers="const"> <return type="Camera"> </return> <description> - Return the active 3D camera. + Returns the active 3D camera. </description> </method> <method name="get_final_transform" qualifiers="const"> <return type="Transform2D"> </return> <description> - Get the total transform of the viewport. + Returns the total transform of the viewport. </description> </method> <method name="get_mouse_position" qualifiers="const"> <return type="Vector2"> </return> <description> - Get the mouse position, relative to the viewport. + Returns the mouse position relative to the viewport. </description> </method> <method name="get_render_info"> @@ -59,35 +59,35 @@ <argument index="0" name="info" type="int" enum="Viewport.RenderInfo"> </argument> <description> - Get the specific information about the viewport from rendering pipeline. + Returns information about the viewport from the rendering pipeline. </description> </method> <method name="get_size_override" qualifiers="const"> <return type="Vector2"> </return> <description> - Get the size override set with [method set_size_override]. + Returns the size override set with [method set_size_override]. </description> </method> <method name="get_texture" qualifiers="const"> <return type="ViewportTexture"> </return> <description> - Get the viewport's texture, for use with various objects that you want to texture with the viewport. + Returns the viewport's texture. </description> </method> <method name="get_viewport_rid" qualifiers="const"> <return type="RID"> </return> <description> - Get the viewport RID from the [VisualServer]. + Returns the viewport's RID from the [VisualServer]. </description> </method> <method name="get_visible_rect" qualifiers="const"> <return type="Rect2"> </return> <description> - Return the final, visible rect in global screen coordinates. + Returns the visible rectangle in global screen coordinates. </description> </method> <method name="gui_get_drag_data" qualifiers="const"> @@ -101,7 +101,7 @@ <return type="bool"> </return> <description> - Returns whether there are shown modals on-screen. + Returns [code]true[/code] if there are visible modals on-screen. </description> </method> <method name="input"> @@ -116,14 +116,14 @@ <return type="bool"> </return> <description> - Get the enabled status of the size override set with [method set_size_override]. + Returns [code]true[/code] if the size override is enabled. See [method set_size_override]. </description> </method> <method name="is_size_override_stretch_enabled" qualifiers="const"> <return type="bool"> </return> <description> - Get the enabled status of the size stretch override set with [method set_size_override_stretch]. + Returns [code]true[/code] if the size stretch override is enabled. See [method set_size_override_stretch]. </description> </method> <method name="set_attach_to_screen_rect"> @@ -144,7 +144,7 @@ <argument index="2" name="margin" type="Vector2" default="Vector2( 0, 0 )"> </argument> <description> - Set the size override of the viewport. If the enable parameter is true, it would use the override, otherwise it would use the default size. If the size parameter is equal to [code](-1, -1)[/code], it won't update the size. + Sets the size override of the viewport. If the [code]enable[/code] parameter is [code]true[/code] the override is used, otherwise it uses the default size. If the size parameter is [code](-1, -1)[/code], it won't update the size. </description> </method> <method name="set_size_override_stretch"> @@ -153,7 +153,7 @@ <argument index="0" name="enabled" type="bool"> </argument> <description> - Set whether the size override affects stretch as well. + If [code]true[/code] the size override affects stretch as well. </description> </method> <method name="unhandled_input"> @@ -168,7 +168,7 @@ <return type="void"> </return> <description> - Force update of the 2D and 3D worlds. + Forces update of the 2D and 3D worlds. </description> </method> <method name="warp_mouse"> @@ -177,7 +177,7 @@ <argument index="0" name="to_position" type="Vector2"> </argument> <description> - Warp the mouse to a position, relative to the viewport. + Warps the mouse to a position relative to the viewport. </description> </method> </methods> diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp index 60826aa81b..65c41ef579 100644 --- a/editor/editor_help.cpp +++ b/editor/editor_help.cpp @@ -1781,7 +1781,7 @@ void EditorHelp::_notification(int p_what) { case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { - class_desc->add_color_override("selection_color", get_color("text_editor/theme/selection_color", "Editor")); + class_desc->add_color_override("selection_color", EditorSettings::get_singleton()->get("text_editor/theme/selection_color")); _update_doc(); } break; @@ -1863,7 +1863,7 @@ EditorHelp::EditorHelp() { class_desc = memnew(RichTextLabel); add_child(class_desc); class_desc->set_v_size_flags(SIZE_EXPAND_FILL); - class_desc->add_color_override("selection_color", get_color("text_editor/theme/selection_color", "Editor")); + class_desc->add_color_override("selection_color", EditorSettings::get_singleton()->get("text_editor/theme/selection_color")); class_desc->connect("meta_clicked", this, "_class_desc_select"); class_desc->connect("gui_input", this, "_class_desc_input"); @@ -1929,7 +1929,7 @@ void EditorHelpBit::_notification(int p_what) { switch (p_what) { case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { - rich_text->add_color_override("selection_color", get_color("text_editor/theme/selection_color", "Editor")); + rich_text->add_color_override("selection_color", EditorSettings::get_singleton()->get("text_editor/theme/selection_color")); } break; default: break; @@ -1948,7 +1948,7 @@ EditorHelpBit::EditorHelpBit() { add_child(rich_text); //rich_text->set_anchors_and_margins_preset(Control::PRESET_WIDE); rich_text->connect("meta_clicked", this, "_meta_clicked"); - rich_text->add_color_override("selection_color", get_color("text_editor/theme/selection_color", "Editor")); + rich_text->add_color_override("selection_color", EditorSettings::get_singleton()->get("text_editor/theme/selection_color")); rich_text->set_override_selected_font_color(false); set_custom_minimum_size(Size2(0, 70 * EDSCALE)); } diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp index 99a2b2aa67..d3ef737542 100644 --- a/editor/editor_inspector.cpp +++ b/editor/editor_inspector.cpp @@ -1533,9 +1533,10 @@ void EditorInspector::update_tree() { if (capitalize_paths) path_name = path_name.capitalize(); + Color c = sscolor; c.a /= level; - section->setup(path_name, acc_path, object, c, use_folding); + section->setup(path_name, path_name, object, c, use_folding); item_path[acc_path] = section->get_vbox(); } diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 0be6ee8759..975c7f8345 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -74,6 +74,7 @@ #include "editor/plugins/animation_player_editor_plugin.h" #include "editor/plugins/animation_state_machine_editor.h" #include "editor/plugins/animation_tree_editor_plugin.h" +#include "editor/plugins/animation_tree_player_editor_plugin.h" #include "editor/plugins/asset_library_editor_plugin.h" #include "editor/plugins/audio_stream_editor_plugin.h" #include "editor/plugins/baked_lightmap_editor_plugin.h" @@ -5589,16 +5590,13 @@ EditorNode::EditorNode() { add_editor_plugin(memnew(ShaderEditorPlugin(this))); add_editor_plugin(memnew(VisualShaderEditorPlugin(this))); - add_editor_plugin(memnew(AnimationNodeBlendTreeEditorPlugin(this))); - add_editor_plugin(memnew(AnimationNodeBlendSpace1DEditorPlugin(this))); - add_editor_plugin(memnew(AnimationNodeBlendSpace2DEditorPlugin(this))); - add_editor_plugin(memnew(AnimationNodeStateMachineEditorPlugin(this))); add_editor_plugin(memnew(CameraEditorPlugin(this))); add_editor_plugin(memnew(ThemeEditorPlugin(this))); add_editor_plugin(memnew(MultiMeshEditorPlugin(this))); add_editor_plugin(memnew(MeshInstanceEditorPlugin(this))); add_editor_plugin(memnew(AnimationTreeEditorPlugin(this))); + add_editor_plugin(memnew(AnimationTreePlayerEditorPlugin(this))); add_editor_plugin(memnew(MeshLibraryEditorPlugin(this))); add_editor_plugin(memnew(StyleBoxEditorPlugin(this))); add_editor_plugin(memnew(SpriteEditorPlugin(this))); diff --git a/editor/editor_plugin_settings.h b/editor/editor_plugin_settings.h index 310cab699f..194fac6b92 100644 --- a/editor/editor_plugin_settings.h +++ b/editor/editor_plugin_settings.h @@ -31,7 +31,7 @@ #ifndef EDITORPLUGINSETTINGS_H #define EDITORPLUGINSETTINGS_H -#include "editor/plugin_config_dialog.h"; +#include "editor/plugin_config_dialog.h" #include "editor_data.h" #include "property_editor.h" #include "scene/gui/dialogs.h" diff --git a/editor/editor_properties.cpp b/editor/editor_properties.cpp index 0cbd5f0bff..5f1e7273e5 100644 --- a/editor/editor_properties.cpp +++ b/editor/editor_properties.cpp @@ -61,7 +61,7 @@ void EditorPropertyText::_text_changed(const String &p_string) { if (updating) return; - emit_signal("property_changed", get_edited_property(), p_string); + emit_signal("property_changed", get_edited_property(), p_string, true); } void EditorPropertyText::update_property() { @@ -72,6 +72,10 @@ void EditorPropertyText::update_property() { updating = false; } +void EditorPropertyText::set_placeholder(const String &p_string) { + text->set_placeholder(p_string); +} + void EditorPropertyText::_bind_methods() { ClassDB::bind_method(D_METHOD("_text_changed", "txt"), &EditorPropertyText::_text_changed); @@ -92,12 +96,11 @@ EditorPropertyText::EditorPropertyText() { void EditorPropertyMultilineText::_big_text_changed() { text->set_text(big_text->get_text()); - emit_signal("property_changed", get_edited_property(), big_text->get_text()); + emit_signal("property_changed", get_edited_property(), big_text->get_text(), true); } void EditorPropertyMultilineText::_text_changed() { - - emit_signal("property_changed", get_edited_property(), text->get_text()); + emit_signal("property_changed", get_edited_property(), text->get_text(), true); } void EditorPropertyMultilineText::_open_big_text() { @@ -1735,12 +1738,18 @@ EditorPropertyTransform::EditorPropertyTransform() { void EditorPropertyColor::_color_changed(const Color &p_color) { - emit_signal("property_changed", get_edited_property(), p_color); + emit_signal("property_changed", get_edited_property(), p_color, true); +} + +void EditorPropertyColor::_popup_closed() { + + emit_signal("property_changed", get_edited_property(), picker->get_pick_color(), false); } void EditorPropertyColor::_bind_methods() { ClassDB::bind_method(D_METHOD("_color_changed"), &EditorPropertyColor::_color_changed); + ClassDB::bind_method(D_METHOD("_popup_closed"), &EditorPropertyColor::_popup_closed); } void EditorPropertyColor::update_property() { @@ -1758,6 +1767,7 @@ EditorPropertyColor::EditorPropertyColor() { add_child(picker); picker->set_flat(true); picker->connect("color_changed", this, "_color_changed"); + picker->connect("popup_closed", this, "_popup_closed"); } ////////////// NODE PATH ////////////////////// @@ -2777,6 +2787,9 @@ bool EditorInspectorDefaultPlugin::parse_property(Object *p_object, Variant::Typ } else { EditorPropertyText *editor = memnew(EditorPropertyText); + if (p_hint == PROPERTY_HINT_PLACEHOLDER_TEXT) { + editor->set_placeholder(p_hint_text); + } add_property_editor(p_path, editor); } } break; diff --git a/editor/editor_properties.h b/editor/editor_properties.h index d5fac9c1a0..5726ccfa41 100644 --- a/editor/editor_properties.h +++ b/editor/editor_properties.h @@ -61,6 +61,7 @@ protected: public: virtual void update_property(); + void set_placeholder(const String &p_string); EditorPropertyText(); }; @@ -476,6 +477,7 @@ class EditorPropertyColor : public EditorProperty { GDCLASS(EditorPropertyColor, EditorProperty) ColorPickerButton *picker; void _color_changed(const Color &p_color); + void _popup_closed(); protected: static void _bind_methods(); diff --git a/editor/editor_properties_array_dict.cpp b/editor/editor_properties_array_dict.cpp index 8203c85c6a..23dbb026dd 100644 --- a/editor/editor_properties_array_dict.cpp +++ b/editor/editor_properties_array_dict.cpp @@ -125,13 +125,13 @@ EditorPropertyDictionaryObject::EditorPropertyDictionaryObject() { ///////////////////// ARRAY /////////////////////////// -void EditorPropertyArray::_property_changed(const String &p_prop, Variant p_value) { +void EditorPropertyArray::_property_changed(const String &p_prop, Variant p_value, bool changing) { if (p_prop.begins_with("indices")) { int idx = p_prop.get_slice("/", 1).to_int(); Variant array = object->get_array(); array.set(idx, p_value); - emit_signal("property_changed", get_edited_property(), array); + emit_signal("property_changed", get_edited_property(), array, true); if (array.get_type() == Variant::ARRAY) { array = array.call("duplicate"); //dupe, so undo/redo works better @@ -544,7 +544,7 @@ void EditorPropertyArray::_bind_methods() { ClassDB::bind_method("_edit_pressed", &EditorPropertyArray::_edit_pressed); ClassDB::bind_method("_page_changed", &EditorPropertyArray::_page_changed); ClassDB::bind_method("_length_changed", &EditorPropertyArray::_length_changed); - ClassDB::bind_method("_property_changed", &EditorPropertyArray::_property_changed); + ClassDB::bind_method("_property_changed", &EditorPropertyArray::_property_changed, DEFVAL(false)); ClassDB::bind_method("_change_type", &EditorPropertyArray::_change_type); ClassDB::bind_method("_change_type_menu", &EditorPropertyArray::_change_type_menu); } @@ -579,7 +579,7 @@ EditorPropertyArray::EditorPropertyArray() { ///////////////////// DICTIONARY /////////////////////////// -void EditorPropertyDictionary::_property_changed(const String &p_prop, Variant p_value) { +void EditorPropertyDictionary::_property_changed(const String &p_prop, Variant p_value, bool changing) { if (p_prop == "new_item_key") { @@ -593,7 +593,7 @@ void EditorPropertyDictionary::_property_changed(const String &p_prop, Variant p Variant key = dict.get_key_at_index(idx); dict[key] = p_value; - emit_signal("property_changed", get_edited_property(), dict); + emit_signal("property_changed", get_edited_property(), dict, true); dict = dict.duplicate(); //dupe, so undo/redo works better object->set_dict(dict); @@ -1006,7 +1006,7 @@ void EditorPropertyDictionary::_page_changed(double p_page) { void EditorPropertyDictionary::_bind_methods() { ClassDB::bind_method("_edit_pressed", &EditorPropertyDictionary::_edit_pressed); ClassDB::bind_method("_page_changed", &EditorPropertyDictionary::_page_changed); - ClassDB::bind_method("_property_changed", &EditorPropertyDictionary::_property_changed); + ClassDB::bind_method("_property_changed", &EditorPropertyDictionary::_property_changed, DEFVAL(false)); ClassDB::bind_method("_change_type", &EditorPropertyDictionary::_change_type); ClassDB::bind_method("_change_type_menu", &EditorPropertyDictionary::_change_type_menu); ClassDB::bind_method("_add_key_value", &EditorPropertyDictionary::_add_key_value); diff --git a/editor/editor_properties_array_dict.h b/editor/editor_properties_array_dict.h index 75c67d280d..a8ddb02e9d 100644 --- a/editor/editor_properties_array_dict.h +++ b/editor/editor_properties_array_dict.h @@ -67,7 +67,7 @@ class EditorPropertyArray : public EditorProperty { void _page_changed(double p_page); void _length_changed(double p_page); void _edit_pressed(); - void _property_changed(const String &p_prop, Variant p_value); + void _property_changed(const String &p_prop, Variant p_value, bool changing = false); void _change_type(Object *p_button, int p_index); void _change_type_menu(int p_index); @@ -99,7 +99,7 @@ class EditorPropertyDictionary : public EditorProperty { void _page_changed(double p_page); void _edit_pressed(); - void _property_changed(const String &p_prop, Variant p_value); + void _property_changed(const String &p_prop, Variant p_value, bool changing = false); void _change_type(Object *p_button, int p_index); void _change_type_menu(int p_index); diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp index d24816ee02..85186440ab 100644 --- a/editor/editor_settings.cpp +++ b/editor/editor_settings.cpp @@ -357,6 +357,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { hints["text_editor/theme/color_theme"] = PropertyInfo(Variant::STRING, "text_editor/theme/color_theme", PROPERTY_HINT_ENUM, "Adaptive,Default,Custom"); _initial_set("text_editor/theme/line_spacing", 4); + _initial_set("text_editor/theme/selection_color", Color::html("40808080")); _load_default_text_editor_theme(); @@ -473,6 +474,7 @@ void EditorSettings::_load_defaults(Ref<ConfigFile> p_extra_config) { hints["editors/3d/freelook/freelook_modifier_speed_factor"] = PropertyInfo(Variant::REAL, "editors/3d/freelook/freelook_modifier_speed_factor", PROPERTY_HINT_RANGE, "0.0, 10.0, 0.1"); _initial_set("editors/3d/freelook/freelook_speed_zoom_link", false); + _initial_set("editors/2d/grid_color", Color(1.0, 1.0, 1.0, 0.07)); _initial_set("editors/2d/guides_color", Color(0.6, 0.0, 0.8)); _initial_set("editors/2d/bone_width", 5); _initial_set("editors/2d/bone_color1", Color(1.0, 1.0, 1.0, 0.9)); diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 69a013dd00..50d71f1c98 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -381,12 +381,6 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { theme->set_color("error_color", "Editor", error_color); theme->set_color("property_color", "Editor", property_color); - // 2d grid color - const Color grid_minor_color = mono_color * Color(1.0, 1.0, 1.0, 0.07); - const Color grid_major_color = Color(font_color_disabled.r, font_color_disabled.g, font_color_disabled.b, 0.15); - theme->set_color("grid_major_color", "Editor", grid_major_color); - theme->set_color("grid_minor_color", "Editor", grid_minor_color); - const int thumb_size = EDITOR_DEF("filesystem/file_dialog/thumbnail_size", 64); theme->set_constant("scale", "Editor", EDSCALE); theme->set_constant("thumb_size", "Editor", thumb_size); @@ -971,8 +965,8 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // GraphEdit 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("grid_major", "GraphEdit", Color(1.0, 1.0, 1.0, 0.15)); + theme->set_color("grid_minor", "GraphEdit", Color(1.0, 1.0, 1.0, 0.07)); 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")); diff --git a/editor/find_in_files.cpp b/editor/find_in_files.cpp index 2be1f1644e..ef7409fd43 100644 --- a/editor/find_in_files.cpp +++ b/editor/find_in_files.cpp @@ -37,10 +37,10 @@ #include "scene/gui/check_box.h" #include "scene/gui/file_dialog.h" #include "scene/gui/grid_container.h" -#include "scene/gui/item_list.h" #include "scene/gui/label.h" #include "scene/gui/line_edit.h" #include "scene/gui/progress_bar.h" +#include "scene/gui/tree.h" #define ROOT_PREFIX "res://" @@ -58,6 +58,34 @@ static bool is_text_char(CharType c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_'; } +static bool find_next(const String &line, String pattern, int from, bool match_case, bool whole_words, int &out_begin, int &out_end) { + + int end = from; + + while (true) { + int begin = match_case ? line.find(pattern, end) : line.findn(pattern, end); + + if (begin == -1) + return false; + + end = begin + pattern.length(); + out_begin = begin; + out_end = end; + + if (whole_words) { + if (begin > 0 && is_text_char(line[begin - 1])) { + continue; + } + if (end < line.size() && is_text_char(line[end])) { + continue; + } + } + + return true; + } +} + +//-------------------------------------------------------------------------------- FindInFiles::FindInFiles() { _root_prefix = ROOT_PREFIX; _extension_filter.insert("gd"); @@ -246,24 +274,7 @@ void FindInFiles::_scan_file(String fpath) { String line = f->get_line(); - // Find all occurrences in the current line - while (true) { - begin = _match_case ? line.find(_pattern, end) : line.findn(_pattern, end); - - if (begin == -1) - break; - - end = begin + _pattern.length(); - - if (_whole_words) { - if (begin > 0 && is_text_char(line[begin - 1])) { - continue; - } - if (end < line.size() && is_text_char(line[end])) { - continue; - } - } - + while (find_next(line, _pattern, end, _match_case, _whole_words, begin, end)) { emit_signal(SIGNAL_RESULT_FOUND, fpath, line_number, begin, end, line); } } @@ -567,14 +578,18 @@ FindInFilesPanel::FindInFilesPanel() { vbc->add_child(hbc); } - // In the future, this should be replaced by a more specific list container, - // which can highlight text regions and change opacity for enabled/disabled states - _results_display = memnew(ItemList); + _results_display = memnew(Tree); _results_display->add_font_override("font", get_font("source", "EditorFonts")); _results_display->set_v_size_flags(SIZE_EXPAND_FILL); _results_display->connect("item_selected", this, "_on_result_selected"); + _results_display->connect("item_edited", this, "_on_item_edited"); + _results_display->set_hide_root(true); + _results_display->set_select_mode(Tree::SELECT_ROW); + _results_display->create_item(); // Root vbc->add_child(_results_display); + _with_replace = false; + { _replace_container = memnew(HBoxContainer); @@ -600,12 +615,33 @@ FindInFilesPanel::FindInFilesPanel() { void FindInFilesPanel::set_with_replace(bool with_replace) { + _with_replace = with_replace; _replace_container->set_visible(with_replace); + + if (with_replace) { + // Results show checkboxes on their left so they can be opted out + _results_display->set_columns(2); + _results_display->set_column_expand(0, false); + _results_display->set_column_min_width(0, 48 * EDSCALE); + + } else { + // Results are single-cell items + _results_display->set_column_expand(0, true); + _results_display->set_columns(1); + } +} + +void FindInFilesPanel::clear() { + _file_items.clear(); + _result_items.clear(); + _results_display->clear(); + _results_display->create_item(); // Root } void FindInFilesPanel::start_search() { - _results_display->clear(); + clear(); + _status_label->set_text(TTR("Searching...")); _search_text_label->set_text(_finder->get_search_text()); @@ -636,9 +672,90 @@ void FindInFilesPanel::_notification(int p_what) { void FindInFilesPanel::_on_result_found(String fpath, int line_number, int begin, int end, String text) { - int i = _results_display->get_item_count(); - _results_display->add_item(fpath + ": " + String::num(line_number) + ": " + text.replace("\t", " ")); - _results_display->set_item_metadata(i, varray(fpath, line_number, begin, end)); + TreeItem *file_item; + Map<String, TreeItem *>::Element *E = _file_items.find(fpath); + + if (E == NULL) { + file_item = _results_display->create_item(); + file_item->set_text(0, fpath); + file_item->set_metadata(0, fpath); + + // The width of this column is restrained to checkboxes, but that doesn't make sense for the parent items, + // so we override their width so they can expand to full width + file_item->set_expand_right(0, true); + + _file_items[fpath] = file_item; + + } else { + file_item = E->value(); + } + + int text_index = _with_replace ? 1 : 0; + + TreeItem *item = _results_display->create_item(file_item); + + // Do this first because it resets properties of the cell... + item->set_cell_mode(text_index, TreeItem::CELL_MODE_CUSTOM); + + String item_text = String::num_int64(line_number) + ": " + text.replace("\t", " "); + + item->set_text(text_index, item_text); + item->set_custom_draw(text_index, this, "_draw_result_text"); + + Ref<Font> font = _results_display->get_font("font"); + + float raw_text_width = font->get_string_size(text).x; + float item_text_width = font->get_string_size(item_text).x; + + Result r; + r.line_number = line_number; + r.begin = begin; + r.end = end; + r.draw_begin = (item_text_width - raw_text_width) + font->get_string_size(text.left(r.begin)).x; + r.draw_width = font->get_string_size(text.substr(r.begin, r.end - r.begin + 1)).x; + _result_items[item] = r; + + if (_with_replace) { + item->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + item->set_checked(0, true); + item->set_editable(0, true); + } +} + +void FindInFilesPanel::draw_result_text(Object *item_obj, Rect2 rect) { + + TreeItem *item = Object::cast_to<TreeItem>(item_obj); + if (!item) + return; + + Map<TreeItem *, Result>::Element *E = _result_items.find(item); + if (!E) + return; + Result r = E->value(); + + Rect2 match_rect = rect; + match_rect.position.x += r.draw_begin; + match_rect.size.x = r.draw_width; + match_rect.position.y += 1 * EDSCALE; + match_rect.size.y -= 2 * EDSCALE; + + _results_display->draw_rect(match_rect, Color(0, 0, 0, 0.5)); + // Text is drawn by Tree already +} + +void FindInFilesPanel::_on_item_edited() { + + TreeItem *item = _results_display->get_selected(); + + if (item->is_checked(0)) { + item->set_custom_color(1, _results_display->get_color("font_color")); + + } else { + // Grey out + Color color = _results_display->get_color("font_color"); + color.a /= 2.0; + item->set_custom_color(1, color); + } } void FindInFilesPanel::_on_finished() { @@ -653,10 +770,19 @@ void FindInFilesPanel::_on_cancel_button_clicked() { stop_search(); } -void FindInFilesPanel::_on_result_selected(int i) { +void FindInFilesPanel::_on_result_selected() { + + TreeItem *item = _results_display->get_selected(); + Map<TreeItem *, Result>::Element *E = _result_items.find(item); + + if (E == NULL) + return; + Result r = E->value(); + + TreeItem *file_item = item->get_parent(); + String fpath = file_item->get_metadata(0); - Array meta = _results_display->get_item_metadata(i); - emit_signal(SIGNAL_RESULT_SELECTED, meta[0], meta[1], meta[2], meta[3]); + emit_signal(SIGNAL_RESULT_SELECTED, fpath, r.line_number, r.begin, r.end); } void FindInFilesPanel::_on_replace_text_changed(String text) { @@ -668,39 +794,33 @@ void FindInFilesPanel::_on_replace_all_clicked() { String replace_text = get_replace_text(); ERR_FAIL_COND(replace_text.empty()); - String last_fpath; - PoolIntArray locations; PoolStringArray modified_files; - for (int i = 0; i < _results_display->get_item_count(); ++i) { + for (Map<String, TreeItem *>::Element *E = _file_items.front(); E; E = E->next()) { - Array meta = _results_display->get_item_metadata(i); + TreeItem *file_item = E->value(); + String fpath = file_item->get_metadata(0); - String fpath = meta[0]; + Vector<Result> locations; + for (TreeItem *item = file_item->get_children(); item; item = item->get_next()) { - // Results are sorted by file, so we can batch replaces - if (fpath != last_fpath) { - if (locations.size() != 0) { - apply_replaces_in_file(last_fpath, locations, replace_text); - modified_files.append(last_fpath); - locations.resize(0); - } - } + if (!item->is_checked(0)) + continue; - locations.append(meta[1]); // line_number - locations.append(meta[2]); // begin - locations.append(meta[3]); // end - - last_fpath = fpath; - } + Map<TreeItem *, Result>::Element *E = _result_items.find(item); + ERR_FAIL_COND(E == NULL); + locations.push_back(E->value()); + } - if (locations.size() != 0) { - apply_replaces_in_file(last_fpath, locations, replace_text); - modified_files.append(last_fpath); + if (locations.size() != 0) { + // Results are sorted by file, so we can batch replaces + apply_replaces_in_file(fpath, locations, replace_text); + modified_files.append(fpath); + } } // Hide replace bar so we can't trigger the action twice without doing a new search - set_with_replace(false); + _replace_container->hide(); emit_signal(SIGNAL_FILES_MODIFIED, modified_files); } @@ -740,11 +860,7 @@ private: Vector<char> _line_buffer; }; -void FindInFilesPanel::apply_replaces_in_file(String fpath, PoolIntArray locations, String text) { - - ERR_FAIL_COND(locations.size() % 3 != 0); - - //print_line(String("Replacing {0} occurrences in {1}").format(varray(fpath, locations.size() / 3))); +void FindInFilesPanel::apply_replaces_in_file(String fpath, const Vector<Result> &locations, String new_text) { // If the file is already open, I assume the editor will reload it. // If there are unsaved changes, the user will be asked on focus, @@ -759,21 +875,34 @@ void FindInFilesPanel::apply_replaces_in_file(String fpath, PoolIntArray locatio ConservativeGetLine conservative; String line = conservative.get_line(f); + String search_text = _finder->get_search_text(); + + int offset = 0; - PoolIntArray::Read locations_read = locations.read(); - for (int i = 0; i < locations.size(); i += 3) { + for (int i = 0; i < locations.size(); ++i) { - int repl_line_number = locations_read[i]; - int repl_begin = locations_read[i + 1]; - int repl_end = locations_read[i + 2]; + int repl_line_number = locations[i].line_number; while (current_line < repl_line_number) { buffer += line; line = conservative.get_line(f); ++current_line; + offset = 0; + } + + int repl_begin = locations[i].begin + offset; + int repl_end = locations[i].end + offset; + + int _; + if (!find_next(line, search_text, repl_begin, _finder->is_match_case(), _finder->is_whole_words(), _, _)) { + // Make sure the replace is still valid in case the file was tampered with. + print_line(String("Occurrence no longer matches, replace will be ignored in {0}: line {1}, col {2}").format(varray(fpath, repl_line_number, repl_begin))); + continue; } - line = line.left(repl_begin) + text + line.right(repl_end); + line = line.left(repl_begin) + new_text + line.right(repl_end); + // keep an offset in case there are successive replaces in the same line + offset += new_text.length() - (repl_end - repl_begin); } buffer += line; @@ -811,11 +940,13 @@ void FindInFilesPanel::set_progress_visible(bool visible) { void FindInFilesPanel::_bind_methods() { ClassDB::bind_method("_on_result_found", &FindInFilesPanel::_on_result_found); + ClassDB::bind_method("_on_item_edited", &FindInFilesPanel::_on_item_edited); ClassDB::bind_method("_on_finished", &FindInFilesPanel::_on_finished); ClassDB::bind_method("_on_cancel_button_clicked", &FindInFilesPanel::_on_cancel_button_clicked); ClassDB::bind_method("_on_result_selected", &FindInFilesPanel::_on_result_selected); ClassDB::bind_method("_on_replace_text_changed", &FindInFilesPanel::_on_replace_text_changed); ClassDB::bind_method("_on_replace_all_clicked", &FindInFilesPanel::_on_replace_all_clicked); + ClassDB::bind_method("_draw_result_text", &FindInFilesPanel::draw_result_text); ADD_SIGNAL(MethodInfo(SIGNAL_RESULT_SELECTED, PropertyInfo(Variant::STRING, "path"), diff --git a/editor/find_in_files.h b/editor/find_in_files.h index d57184960b..75ea1c3161 100644 --- a/editor/find_in_files.h +++ b/editor/find_in_files.h @@ -131,7 +131,8 @@ private: }; class Button; -class ItemList; +class Tree; +class TreeItem; class ProgressBar; // Display search results @@ -159,22 +160,37 @@ private: void _on_result_found(String fpath, int line_number, int begin, int end, String text); void _on_finished(); void _on_cancel_button_clicked(); - void _on_result_selected(int i); + void _on_result_selected(); + void _on_item_edited(); void _on_replace_text_changed(String text); void _on_replace_all_clicked(); - void apply_replaces_in_file(String fpath, PoolIntArray locations, String text); + struct Result { + int line_number; + int begin; + int end; + float draw_begin; + float draw_width; + }; + void apply_replaces_in_file(String fpath, const Vector<Result> &locations, String new_text); void update_replace_buttons(); String get_replace_text(); + + void draw_result_text(Object *item_obj, Rect2 rect); + void set_progress_visible(bool visible); + void clear(); FindInFiles *_finder; Label *_search_text_label; - ItemList *_results_display; + Tree *_results_display; Label *_status_label; Button *_cancel_button; ProgressBar *_progress_bar; + Map<String, TreeItem *> _file_items; + Map<TreeItem *, Result> _result_items; + bool _with_replace; HBoxContainer *_replace_container; LineEdit *_replace_line_edit; diff --git a/editor/import/resource_importer_texture.cpp b/editor/import/resource_importer_texture.cpp index 17a9394b51..a2d54e0048 100644 --- a/editor/import/resource_importer_texture.cpp +++ b/editor/import/resource_importer_texture.cpp @@ -198,6 +198,7 @@ void ResourceImporterTexture::get_import_options(List<ImportOption> *r_options, r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/fix_alpha_border"), p_preset != PRESET_3D ? true : false)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/premult_alpha"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/HDR_as_SRGB"), false)); + r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "process/invert_color"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "stream"), false)); r_options->push_back(ImportOption(PropertyInfo(Variant::INT, "size_limit", PROPERTY_HINT_RANGE, "0,4096,1"), 0)); r_options->push_back(ImportOption(PropertyInfo(Variant::BOOL, "detect_3d"), p_preset == PRESET_DETECT)); @@ -354,6 +355,7 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String int srgb = p_options["flags/srgb"]; bool fix_alpha_border = p_options["process/fix_alpha_border"]; bool premult_alpha = p_options["process/premult_alpha"]; + bool invert_color = p_options["process/invert_color"]; bool stream = p_options["stream"]; int size_limit = p_options["size_limit"]; bool force_rgbe = int(p_options["compress/hdr_mode"]) == 1; @@ -409,6 +411,19 @@ Error ResourceImporterTexture::import(const String &p_source_file, const String image->premultiply_alpha(); } + if (invert_color) { + int height = image->get_height(); + int width = image->get_width(); + + image->lock(); + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + image->set_pixel(i, j, image->get_pixel(i, j).inverted()); + } + } + image->unlock(); + } + bool detect_3d = p_options["detect_3d"]; bool detect_srgb = srgb == 2; bool detect_normal = normal == 0; diff --git a/editor/import_dock.cpp b/editor/import_dock.cpp index f91802b352..31eb193461 100644 --- a/editor/import_dock.cpp +++ b/editor/import_dock.cpp @@ -420,10 +420,9 @@ ImportDock::ImportDock() { preset->get_popup()->connect("index_pressed", this, "_preset_selected"); hb->add_child(preset); - import_opts = memnew(PropertyEditor); + import_opts = memnew(EditorInspector); add_child(import_opts); import_opts->set_v_size_flags(SIZE_EXPAND_FILL); - import_opts->hide_top_label(); hb = memnew(HBoxContainer); add_child(hb); diff --git a/editor/import_dock.h b/editor/import_dock.h index a7a7eda8d8..41c7298d9a 100644 --- a/editor/import_dock.h +++ b/editor/import_dock.h @@ -31,10 +31,12 @@ #ifndef IMPORTDOCK_H #define IMPORTDOCK_H -#include "editor_file_system.h" -#include "io/resource_import.h" -#include "property_editor.h" +#include "core/io/config_file.h" +#include "core/io/resource_import.h" +#include "editor/editor_file_system.h" +#include "editor/editor_inspector.h" #include "scene/gui/box_container.h" +#include "scene/gui/menu_button.h" #include "scene/gui/option_button.h" #include "scene/gui/popup_menu.h" @@ -45,7 +47,7 @@ class ImportDock : public VBoxContainer { Label *imported; OptionButton *import_as; MenuButton *preset; - PropertyEditor *import_opts; + EditorInspector *import_opts; List<PropertyInfo> properties; Map<StringName, Variant> property_values; diff --git a/editor/inspector_dock.cpp b/editor/inspector_dock.cpp index 615e9df043..0335053162 100644 --- a/editor/inspector_dock.cpp +++ b/editor/inspector_dock.cpp @@ -36,6 +36,16 @@ void InspectorDock::_menu_option(int p_option) { switch (p_option) { + case RESOURCE_MAKE_BUILT_IN: { + _unref_resource(); + } break; + case RESOURCE_COPY: { + _copy_resource(); + } break; + case RESOURCE_EDIT_CLIPBOARD: { + _paste_resource(); + } break; + case RESOURCE_SAVE: { _save_resource(false); } break; @@ -400,10 +410,11 @@ void InspectorDock::update(Object *p_object) { p->add_shortcut(ED_SHORTCUT("property_editor/copy_params", TTR("Copy Params")), OBJECT_COPY_PARAMS); p->add_shortcut(ED_SHORTCUT("property_editor/paste_params", TTR("Paste Params")), OBJECT_PASTE_PARAMS); p->add_separator(); - p->add_shortcut(ED_SHORTCUT("property_editor/paste_resource", TTR("Paste Resource")), RESOURCE_PASTE); + + p->add_shortcut(ED_SHORTCUT("property_editor/paste_resource", TTR("Edit Resource Clipboard")), RESOURCE_EDIT_CLIPBOARD); if (is_resource) { p->add_shortcut(ED_SHORTCUT("property_editor/copy_resource", TTR("Copy Resource")), RESOURCE_COPY); - p->add_shortcut(ED_SHORTCUT("property_editor/unref_resource", TTR("Make Built-In")), RESOURCE_UNREF); + p->add_shortcut(ED_SHORTCUT("property_editor/unref_resource", TTR("Make Built-In")), RESOURCE_MAKE_BUILT_IN); } if (is_resource || is_node) { diff --git a/editor/inspector_dock.h b/editor/inspector_dock.h index f347056158..97ef6899dc 100644 --- a/editor/inspector_dock.h +++ b/editor/inspector_dock.h @@ -51,13 +51,12 @@ class InspectorDock : public VBoxContainer { GDCLASS(InspectorDock, VBoxContainer); enum MenuOptions { - RESOURCE_NEW, RESOURCE_LOAD, RESOURCE_SAVE, RESOURCE_SAVE_AS, - RESOURCE_UNREF, + RESOURCE_MAKE_BUILT_IN, RESOURCE_COPY, - RESOURCE_PASTE, + RESOURCE_EDIT_CLIPBOARD, OBJECT_COPY_PARAMS, OBJECT_PASTE_PARAMS, OBJECT_UNIQUE_RESOURCES, diff --git a/editor/plugins/animation_blend_space_1d_editor.cpp b/editor/plugins/animation_blend_space_1d_editor.cpp index 2e128db883..1106464edf 100644 --- a/editor/plugins/animation_blend_space_1d_editor.cpp +++ b/editor/plugins/animation_blend_space_1d_editor.cpp @@ -3,41 +3,11 @@ #include "os/keyboard.h" #include "scene/animation/animation_blend_tree.h" -void AnimationNodeBlendSpace1DEditorPlugin::edit(Object *p_object) { - anim_tree_editor->edit(Object::cast_to<AnimationNodeBlendSpace1D>(p_object)); +StringName AnimationNodeBlendSpace1DEditor::get_blend_position_path() const { + StringName path = AnimationTreeEditor::get_singleton()->get_base_path()+"blend_position"; + return path; } -bool AnimationNodeBlendSpace1DEditorPlugin::handles(Object *p_object) const { - return p_object->is_class("AnimationNodeBlendSpace1D"); -} - -void AnimationNodeBlendSpace1DEditorPlugin::make_visible(bool p_visible) { - - if (p_visible) { - 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); - } -} - -AnimationNodeBlendSpace1DEditorPlugin::AnimationNodeBlendSpace1DEditorPlugin(EditorNode *p_node) { - editor = p_node; - anim_tree_editor = memnew(AnimationNodeBlendSpace1DEditor); - anim_tree_editor->set_custom_minimum_size(Size2(0, 150 * EDSCALE)); - - button = editor->add_bottom_panel_item(TTR("BlendSpace1D"), anim_tree_editor); - button->hide(); -} - -AnimationNodeBlendSpace1DEditorPlugin::~AnimationNodeBlendSpace1DEditorPlugin() { -} void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventKey> k = p_event; @@ -62,7 +32,7 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven menu->add_submenu_item(TTR("Add Animation"), "animations"); - AnimationTree *gp = blend_space->get_tree(); + AnimationTree *gp = AnimationTreeEditor::get_singleton()->get_tree(); ERR_FAIL_COND(!gp); if (gp->has_node(gp->get_animation_player())) { @@ -85,10 +55,18 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven continue; int idx = menu->get_item_count(); - menu->add_item(vformat("Add %s", name)); + menu->add_item(vformat("Add %s", name),idx); menu->set_item_metadata(idx, E->get()); } + Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard(); + if (clipb.is_valid()) { + menu->add_separator(); + menu->add_item(TTR("Paste"), MENU_PASTE); + } + menu->add_separator(); + menu->add_item(TTR("Load.."), MENU_LOAD_FILE); + menu->set_global_position(blend_space_draw->get_global_transform().xform(mb->get_position())); menu->popup(); @@ -158,7 +136,7 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven blend_pos *= blend_space->get_max_space() - blend_space->get_min_space(); blend_pos += blend_space->get_min_space(); - blend_space->set_blend_pos(blend_pos); + AnimationTreeEditor::get_singleton()->get_tree()->set(get_blend_position_path(),blend_pos); blend_space_draw->update(); } @@ -181,7 +159,8 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEven blend_pos *= blend_space->get_max_space() - blend_space->get_min_space(); blend_pos += blend_space->get_min_space(); - blend_space->set_blend_pos(blend_pos); + AnimationTreeEditor::get_singleton()->get_tree()->set(get_blend_position_path(),blend_pos); + blend_space_draw->update(); } } @@ -277,7 +256,9 @@ void AnimationNodeBlendSpace1DEditor::_blend_space_draw() { color.a *= 0.5; } - float point = blend_space->get_blend_pos(); + float point = AnimationTreeEditor::get_singleton()->get_tree()->get(get_blend_position_path()); + + point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); point *= s.width; @@ -299,12 +280,6 @@ void AnimationNodeBlendSpace1DEditor::_update_space() { updating = true; - if (blend_space->get_parent().is_valid()) { - goto_parent_hb->show(); - } else { - goto_parent_hb->hide(); - } - max_value->set_value(blend_space->get_max_space()); min_value->set_value(blend_space->get_min_space()); @@ -355,15 +330,47 @@ void AnimationNodeBlendSpace1DEditor::_snap_toggled() { blend_space_draw->update(); } +void AnimationNodeBlendSpace1DEditor::_file_opened(const String &p_file) { + + file_loaded = ResourceLoader::load(p_file); + if (file_loaded.is_valid()) { + _add_menu_type(MENU_LOAD_FILE_CONFIRM); + } +} + void AnimationNodeBlendSpace1DEditor::_add_menu_type(int p_index) { - String type = menu->get_item_metadata(p_index); + Ref<AnimationRootNode> node; + if (p_index == MENU_LOAD_FILE) { + + open_file->clear_filters(); + List<String> filters; + ResourceLoader::get_recognized_extensions_for_type("AnimationRootNode", &filters); + for (List<String>::Element *E = filters.front(); E; E = E->next()) { + open_file->add_filter("*." + E->get()); + } + open_file->popup_centered_ratio(); + return; + } else if (p_index == MENU_LOAD_FILE_CONFIRM) { + node = file_loaded; + file_loaded.unref(); + } else if (p_index == MENU_PASTE) { + + node = EditorSettings::get_singleton()->get_resource_clipboard(); + } else { + String type = menu->get_item_metadata(p_index); - Object *obj = ClassDB::instance(type); - ERR_FAIL_COND(!obj); - AnimationNode *an = Object::cast_to<AnimationNode>(obj); - ERR_FAIL_COND(!an); + Object *obj = ClassDB::instance(type); + ERR_FAIL_COND(!obj); + AnimationNode *an = Object::cast_to<AnimationNode>(obj); + ERR_FAIL_COND(!an); - Ref<AnimationNode> node(an); + node = Ref<AnimationNode>(an); + } + + if (!node.is_valid()) { + EditorNode::get_singleton()->show_warning(TTR("This type of node can't be used. Only root nodes are allowed.")); + return; + } updating = true; undo_redo->create_action("Add Node Point"); @@ -438,7 +445,7 @@ void AnimationNodeBlendSpace1DEditor::_update_tool_erase() { if (point_valid) { Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point); - if (EditorNode::get_singleton()->item_has_editor(an.ptr())) { + if (AnimationTreeEditor::get_singleton()->can_edit(an)) { open_editor->show(); } else { open_editor->hide(); @@ -490,17 +497,11 @@ void AnimationNodeBlendSpace1DEditor::_open_editor() { if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) { Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point); ERR_FAIL_COND(an.is_null()); - EditorNode::get_singleton()->edit_item(an.ptr()); + AnimationTreeEditor::get_singleton()->enter_editor(itos(selected_point)); } } -void AnimationNodeBlendSpace1DEditor::_goto_parent() { - EditorNode::get_singleton()->edit_item(blend_space->get_parent().ptr()); -} -void AnimationNodeBlendSpace1DEditor::_removed_from_graph() { - EditorNode::get_singleton()->edit_item(NULL); -} void AnimationNodeBlendSpace1DEditor::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_THEME_CHANGED) { @@ -513,18 +514,16 @@ void AnimationNodeBlendSpace1DEditor::_notification(int p_what) { tool_erase->set_icon(get_icon("Remove", "EditorIcons")); snap->set_icon(get_icon("SnapGrid", "EditorIcons")); open_editor->set_icon(get_icon("Edit", "EditorIcons")); - goto_parent->set_icon(get_icon("MoveUp", "EditorIcons")); + } if (p_what == NOTIFICATION_PROCESS) { String error; - if (!blend_space->get_tree()) { - error = TTR("BlendSpace1D does not belong to an AnimationTree node."); - } else if (!blend_space->get_tree()->is_active()) { + if (!AnimationTreeEditor::get_singleton()->get_tree()->is_active()) { error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails."); - } else if (blend_space->get_tree()->is_state_invalid()) { - error = blend_space->get_tree()->get_invalid_state_reason(); + } else if (AnimationTreeEditor::get_singleton()->get_tree()->is_state_invalid()) { + error = AnimationTreeEditor::get_singleton()->get_tree()->get_invalid_state_reason(); } if (error != error_label->get_text()) { @@ -536,6 +535,10 @@ void AnimationNodeBlendSpace1DEditor::_notification(int p_what) { } } } + + if (p_what==NOTIFICATION_VISIBILITY_CHANGED) { + set_process(is_visible_in_tree()); + } } void AnimationNodeBlendSpace1DEditor::_bind_methods() { @@ -556,28 +559,26 @@ void AnimationNodeBlendSpace1DEditor::_bind_methods() { ClassDB::bind_method("_update_edited_point_pos", &AnimationNodeBlendSpace1DEditor::_update_edited_point_pos); ClassDB::bind_method("_open_editor", &AnimationNodeBlendSpace1DEditor::_open_editor); - ClassDB::bind_method("_goto_parent", &AnimationNodeBlendSpace1DEditor::_goto_parent); - ClassDB::bind_method("_removed_from_graph", &AnimationNodeBlendSpace1DEditor::_removed_from_graph); + ClassDB::bind_method("_file_opened", &AnimationNodeBlendSpace1DEditor::_file_opened); + + + } -void AnimationNodeBlendSpace1DEditor::edit(AnimationNodeBlendSpace1D *p_blend_space) { +bool AnimationNodeBlendSpace1DEditor::can_edit(const Ref<AnimationNode> &p_node) { - if (blend_space.is_valid()) { - blend_space->disconnect("removed_from_graph", this, "_removed_from_graph"); - } + Ref<AnimationNodeBlendSpace1D> b1d=p_node; + return b1d.is_valid(); +} - if (p_blend_space) { - blend_space = Ref<AnimationNodeBlendSpace1D>(p_blend_space); - } else { - blend_space.unref(); - } +void AnimationNodeBlendSpace1DEditor::edit(const Ref<AnimationNode> &p_node) { - if (blend_space.is_null()) { - hide(); - } else { - blend_space->connect("removed_from_graph", this, "_removed_from_graph"); + + blend_space=p_node; + + if (!blend_space.is_null()) { _update_space(); } } @@ -594,14 +595,6 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() { Ref<ButtonGroup> bg; bg.instance(); - goto_parent_hb = memnew(HBoxContainer); - top_hb->add_child(goto_parent_hb); - - goto_parent = memnew(ToolButton); - goto_parent->connect("pressed", this, "_goto_parent", varray(), CONNECT_DEFERRED); - goto_parent_hb->add_child(goto_parent); - goto_parent_hb->add_child(memnew(VSeparator)); - goto_parent_hb->hide(); tool_blend = memnew(ToolButton); tool_blend->set_toggle_mode(true); @@ -726,13 +719,20 @@ AnimationNodeBlendSpace1DEditor::AnimationNodeBlendSpace1DEditor() { menu = memnew(PopupMenu); add_child(menu); - menu->connect("index_pressed", this, "_add_menu_type"); + menu->connect("id_pressed", this, "_add_menu_type"); animations_menu = memnew(PopupMenu); menu->add_child(animations_menu); animations_menu->set_name("animations"); animations_menu->connect("index_pressed", this, "_add_animation_type"); + open_file = memnew(EditorFileDialog); + add_child(open_file); + open_file->set_title(TTR("Open Animation Node")); + open_file->set_mode(EditorFileDialog::MODE_OPEN_FILE); + open_file->connect("file_selected", this, "_file_opened"); + undo_redo = EditorNode::get_singleton()->get_undo_redo(); + selected_point = -1; dragging_selected = false; dragging_selected_attempt = false; diff --git a/editor/plugins/animation_blend_space_1d_editor.h b/editor/plugins/animation_blend_space_1d_editor.h index 52139626e6..f040f6dcab 100644 --- a/editor/plugins/animation_blend_space_1d_editor.h +++ b/editor/plugins/animation_blend_space_1d_editor.h @@ -9,10 +9,11 @@ #include "scene/gui/graph_edit.h" #include "scene/gui/popup.h" #include "scene/gui/tree.h" +#include "editor/plugins/animation_tree_editor_plugin.h" -class AnimationNodeBlendSpace1DEditor : public VBoxContainer { +class AnimationNodeBlendSpace1DEditor : public AnimationTreeNodeEditorPlugin { - GDCLASS(AnimationNodeBlendSpace1DEditor, VBoxContainer) + GDCLASS(AnimationNodeBlendSpace1DEditor, AnimationTreeNodeEditorPlugin) Ref<AnimationNodeBlendSpace1D> blend_space; @@ -81,7 +82,17 @@ class AnimationNodeBlendSpace1DEditor : public VBoxContainer { void _goto_parent(); - void _removed_from_graph(); + EditorFileDialog *open_file; + Ref<AnimationNode> file_loaded; + void _file_opened(const String &p_file); + + enum { + MENU_LOAD_FILE = 1000, + MENU_PASTE = 1001, + MENU_LOAD_FILE_CONFIRM = 1002 + }; + + StringName get_blend_position_path() const; protected: void _notification(int p_what); @@ -89,29 +100,9 @@ protected: public: static AnimationNodeBlendSpace1DEditor *get_singleton() { return singleton; } - void edit(AnimationNodeBlendSpace1D *p_blend_space); + virtual bool can_edit(const Ref<AnimationNode> &p_node); + virtual void edit(const Ref<AnimationNode> &p_node); AnimationNodeBlendSpace1DEditor(); }; -class AnimationNodeBlendSpace1DEditorPlugin : public EditorPlugin { - - GDCLASS(AnimationNodeBlendSpace1DEditorPlugin, EditorPlugin) - - AnimationNodeBlendSpace1DEditor *anim_tree_editor; - EditorNode *editor; - Button *button; - -public: - virtual String get_name() const { return "BlendSpace1D"; } - - 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); - - AnimationNodeBlendSpace1DEditorPlugin(EditorNode *p_node); - ~AnimationNodeBlendSpace1DEditorPlugin(); -}; - #endif // ANIMATION_BLEND_SPACE_1D_EDITOR_H diff --git a/editor/plugins/animation_blend_space_2d_editor.cpp b/editor/plugins/animation_blend_space_2d_editor.cpp index 27df60f87a..e008971e5c 100644 --- a/editor/plugins/animation_blend_space_2d_editor.cpp +++ b/editor/plugins/animation_blend_space_2d_editor.cpp @@ -11,27 +11,26 @@ #include "scene/gui/panel.h" #include "scene/main/viewport.h" -void AnimationNodeBlendSpace2DEditor::edit(AnimationNodeBlendSpace2D *p_blend_space) { +bool AnimationNodeBlendSpace2DEditor::can_edit(const Ref<AnimationNode> &p_node) { - if (blend_space.is_valid()) { - blend_space->disconnect("removed_from_graph", this, "_removed_from_graph"); - } + Ref<AnimationNodeBlendSpace2D> bs2d=p_node; + return bs2d.is_valid(); +} - if (p_blend_space) { - blend_space = Ref<AnimationNodeBlendSpace2D>(p_blend_space); - } else { - blend_space.unref(); - } +void AnimationNodeBlendSpace2DEditor::edit(const Ref<AnimationNode> &p_node) { - if (blend_space.is_null()) { - hide(); - } else { - blend_space->connect("removed_from_graph", this, "_removed_from_graph"); + blend_space = p_node; + if (!blend_space.is_null()) { _update_space(); } } +StringName AnimationNodeBlendSpace2DEditor::get_blend_position_path() const { + StringName path = AnimationTreeEditor::get_singleton()->get_base_path()+"blend_position"; + return path; +} + void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEvent> &p_event) { Ref<InputEventKey> k = p_event; @@ -54,7 +53,7 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven ClassDB::get_inheriters_from_class("AnimationRootNode", &classes); menu->add_submenu_item(TTR("Add Animation"), "animations"); - AnimationTree *gp = blend_space->get_tree(); + AnimationTree *gp = AnimationTreeEditor::get_singleton()->get_tree(); ERR_FAIL_COND(!gp); if (gp && gp->has_node(gp->get_animation_player())) { AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(gp->get_node(gp->get_animation_player())); @@ -74,10 +73,19 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven if (name == "Animation") continue; // nope int idx = menu->get_item_count(); - menu->add_item(vformat("Add %s", name)); + menu->add_item(vformat("Add %s", name),idx); menu->set_item_metadata(idx, E->get()); } + Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard(); + if (clipb.is_valid()) { + menu->add_separator(); + menu->add_item(TTR("Paste"), MENU_PASTE); + } + menu->add_separator(); + menu->add_item(TTR("Load.."), MENU_LOAD_FILE); + + menu->set_global_position(blend_space_draw->get_global_transform().xform(mb->get_position())); menu->popup(); add_point_pos = (mb->get_position() / blend_space_draw->get_size()); @@ -203,7 +211,8 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven blend_pos *= (blend_space->get_max_space() - blend_space->get_min_space()); blend_pos += blend_space->get_min_space(); - blend_space->set_blend_position(blend_pos); + AnimationTreeEditor::get_singleton()->get_tree()->set(get_blend_position_path(),blend_pos); + blend_space_draw->update(); } @@ -237,21 +246,54 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_gui_input(const Ref<InputEven blend_pos *= (blend_space->get_max_space() - blend_space->get_min_space()); blend_pos += blend_space->get_min_space(); - blend_space->set_blend_position(blend_pos); + AnimationTreeEditor::get_singleton()->get_tree()->set(get_blend_position_path(),blend_pos); + blend_space_draw->update(); } } +void AnimationNodeBlendSpace2DEditor::_file_opened(const String &p_file) { + + file_loaded = ResourceLoader::load(p_file); + if (file_loaded.is_valid()) { + _add_menu_type(MENU_LOAD_FILE_CONFIRM); + } +} + void AnimationNodeBlendSpace2DEditor::_add_menu_type(int p_index) { - String type = menu->get_item_metadata(p_index); + Ref<AnimationRootNode> node; + if (p_index == MENU_LOAD_FILE) { + + open_file->clear_filters(); + List<String> filters; + ResourceLoader::get_recognized_extensions_for_type("AnimationRootNode", &filters); + for (List<String>::Element *E = filters.front(); E; E = E->next()) { + open_file->add_filter("*." + E->get()); + } + open_file->popup_centered_ratio(); + return; + } else if (p_index == MENU_LOAD_FILE_CONFIRM) { + node = file_loaded; + file_loaded.unref(); + } else if (p_index == MENU_PASTE) { + + node = EditorSettings::get_singleton()->get_resource_clipboard(); + } else { + String type = menu->get_item_metadata(p_index); + + Object *obj = ClassDB::instance(type); + ERR_FAIL_COND(!obj); + AnimationNode *an = Object::cast_to<AnimationNode>(obj); + ERR_FAIL_COND(!an); - Object *obj = ClassDB::instance(type); - ERR_FAIL_COND(!obj); - AnimationNode *an = Object::cast_to<AnimationNode>(obj); - ERR_FAIL_COND(!an); + node = Ref<AnimationNode>(an); + } - Ref<AnimationNode> node(an); + if (!node.is_valid()) { + EditorNode::get_singleton()->show_warning(TTR("This type of node can't be used. Only root nodes are allowed.")); + return; + } updating = true; undo_redo->create_action("Add Node Point"); @@ -288,7 +330,7 @@ void AnimationNodeBlendSpace2DEditor::_update_tool_erase() { tool_erase->set_disabled(!(selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) && !(selected_triangle >= 0 && selected_triangle < blend_space->get_triangle_count())); if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) { Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point); - if (EditorNode::get_singleton()->item_has_editor(an.ptr())) { + if (AnimationTreeEditor::get_singleton()->can_edit(an)) { open_editor->show(); } else { open_editor->hide(); @@ -490,13 +532,15 @@ void AnimationNodeBlendSpace2DEditor::_blend_space_draw() { color.a *= 0.5; } - Vector2 point = blend_space->get_blend_position(); + Vector2 blend_pos = AnimationTreeEditor::get_singleton()->get_tree()->get(get_blend_position_path()); + Vector2 point = blend_pos; + point = (point - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); point *= s; point.y = s.height - point.y; if (blend_space->get_triangle_count()) { - Vector2 closest = blend_space->get_closest_point(blend_space->get_blend_position()); + Vector2 closest = blend_space->get_closest_point(blend_pos); closest = (closest - blend_space->get_min_space()) / (blend_space->get_max_space() - blend_space->get_min_space()); closest *= s; closest.y = s.height - closest.y; @@ -527,12 +571,6 @@ void AnimationNodeBlendSpace2DEditor::_update_space() { updating = true; - if (blend_space->get_parent().is_valid()) { - goto_parent_hb->show(); - } else { - goto_parent_hb->hide(); - } - if (blend_space->get_auto_triangles()) { tool_triangle->hide(); } else { @@ -685,7 +723,6 @@ void AnimationNodeBlendSpace2DEditor::_notification(int p_what) { tool_erase->set_icon(get_icon("Remove", "EditorIcons")); snap->set_icon(get_icon("SnapGrid", "EditorIcons")); open_editor->set_icon(get_icon("Edit", "EditorIcons")); - goto_parent->set_icon(get_icon("MoveUp", "EditorIcons")); auto_triangles->set_icon(get_icon("AutoTriangle", "EditorIcons")); } @@ -693,12 +730,12 @@ void AnimationNodeBlendSpace2DEditor::_notification(int p_what) { String error; - if (!blend_space->get_tree()) { + if (!AnimationTreeEditor::get_singleton()->get_tree()) { error = TTR("BlendSpace2D does not belong to an AnimationTree node."); - } else if (!blend_space->get_tree()->is_active()) { + } else if (!AnimationTreeEditor::get_singleton()->get_tree()->is_active()) { error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails."); - } else if (blend_space->get_tree()->is_state_invalid()) { - error = blend_space->get_tree()->get_invalid_state_reason(); + } else if (AnimationTreeEditor::get_singleton()->get_tree()->is_state_invalid()) { + error = AnimationTreeEditor::get_singleton()->get_tree()->get_invalid_state_reason(); } else if (blend_space->get_triangle_count() == 0) { error = TTR("No triangles exist, so no blending can take place."); } @@ -712,22 +749,22 @@ void AnimationNodeBlendSpace2DEditor::_notification(int p_what) { } } } + + if (p_what==NOTIFICATION_VISIBILITY_CHANGED) { + set_process(is_visible_in_tree()); + } } + void AnimationNodeBlendSpace2DEditor::_open_editor() { if (selected_point >= 0 && selected_point < blend_space->get_blend_point_count()) { Ref<AnimationNode> an = blend_space->get_blend_point_node(selected_point); - ERR_FAIL_COND(!an.is_valid()); - EditorNode::get_singleton()->edit_item(an.ptr()); + ERR_FAIL_COND(an.is_null()); + AnimationTreeEditor::get_singleton()->enter_editor(itos(selected_point)); } } -void AnimationNodeBlendSpace2DEditor::_goto_parent() { - - EditorNode::get_singleton()->edit_item(blend_space->get_parent().ptr()); -} - void AnimationNodeBlendSpace2DEditor::_removed_from_graph() { EditorNode::get_singleton()->edit_item(NULL); } @@ -761,11 +798,13 @@ void AnimationNodeBlendSpace2DEditor::_bind_methods() { ClassDB::bind_method("_update_edited_point_pos", &AnimationNodeBlendSpace2DEditor::_update_edited_point_pos); ClassDB::bind_method("_open_editor", &AnimationNodeBlendSpace2DEditor::_open_editor); - ClassDB::bind_method("_goto_parent", &AnimationNodeBlendSpace2DEditor::_goto_parent); ClassDB::bind_method("_removed_from_graph", &AnimationNodeBlendSpace2DEditor::_removed_from_graph); ClassDB::bind_method("_auto_triangles_toggled", &AnimationNodeBlendSpace2DEditor::_auto_triangles_toggled); + + ClassDB::bind_method("_file_opened", &AnimationNodeBlendSpace2DEditor::_file_opened); + } AnimationNodeBlendSpace2DEditor *AnimationNodeBlendSpace2DEditor::singleton = NULL; @@ -781,14 +820,6 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() { Ref<ButtonGroup> bg; bg.instance(); - goto_parent_hb = memnew(HBoxContainer); - top_hb->add_child(goto_parent_hb); - goto_parent = memnew(ToolButton); - goto_parent->connect("pressed", this, "_goto_parent", varray(), CONNECT_DEFERRED); - goto_parent_hb->add_child(goto_parent); - goto_parent_hb->add_child(memnew(VSeparator)); - goto_parent_hb->hide(); - tool_blend = memnew(ToolButton); tool_blend->set_toggle_mode(true); tool_blend->set_button_group(bg); @@ -968,13 +999,20 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() { menu = memnew(PopupMenu); add_child(menu); - menu->connect("index_pressed", this, "_add_menu_type"); + menu->connect("id_pressed", this, "_add_menu_type"); animations_menu = memnew(PopupMenu); menu->add_child(animations_menu); animations_menu->set_name("animations"); animations_menu->connect("index_pressed", this, "_add_animation_type"); + open_file = memnew(EditorFileDialog); + add_child(open_file); + open_file->set_title(TTR("Open Animation Node")); + open_file->set_mode(EditorFileDialog::MODE_OPEN_FILE); + open_file->connect("file_selected", this, "_file_opened"); + undo_redo = EditorNode::get_singleton()->get_undo_redo(); + selected_point = -1; selected_triangle = -1; @@ -982,42 +1020,3 @@ AnimationNodeBlendSpace2DEditor::AnimationNodeBlendSpace2DEditor() { dragging_selected_attempt = false; } -void AnimationNodeBlendSpace2DEditorPlugin::edit(Object *p_object) { - - anim_tree_editor->edit(Object::cast_to<AnimationNodeBlendSpace2D>(p_object)); -} - -bool AnimationNodeBlendSpace2DEditorPlugin::handles(Object *p_object) const { - - return p_object->is_class("AnimationNodeBlendSpace2D"); -} - -void AnimationNodeBlendSpace2DEditorPlugin::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); - } -} - -AnimationNodeBlendSpace2DEditorPlugin::AnimationNodeBlendSpace2DEditorPlugin(EditorNode *p_node) { - - editor = p_node; - anim_tree_editor = memnew(AnimationNodeBlendSpace2DEditor); - anim_tree_editor->set_custom_minimum_size(Size2(0, 300)); - - button = editor->add_bottom_panel_item(TTR("BlendSpace2D"), anim_tree_editor); - button->hide(); -} - -AnimationNodeBlendSpace2DEditorPlugin::~AnimationNodeBlendSpace2DEditorPlugin() { -} diff --git a/editor/plugins/animation_blend_space_2d_editor.h b/editor/plugins/animation_blend_space_2d_editor.h index a0e497804e..ae684985f3 100644 --- a/editor/plugins/animation_blend_space_2d_editor.h +++ b/editor/plugins/animation_blend_space_2d_editor.h @@ -9,18 +9,17 @@ #include "scene/gui/graph_edit.h" #include "scene/gui/popup.h" #include "scene/gui/tree.h" +#include "editor/plugins/animation_tree_editor_plugin.h" /** @author Juan Linietsky <reduzio@gmail.com> */ -class AnimationNodeBlendSpace2DEditor : public VBoxContainer { +class AnimationNodeBlendSpace2DEditor : public AnimationTreeNodeEditorPlugin { - GDCLASS(AnimationNodeBlendSpace2DEditor, VBoxContainer); + GDCLASS(AnimationNodeBlendSpace2DEditor, AnimationTreeNodeEditorPlugin); Ref<AnimationNodeBlendSpace2D> blend_space; - HBoxContainer *goto_parent_hb; - ToolButton *goto_parent; PanelContainer *panel; ToolButton *tool_blend; @@ -93,38 +92,32 @@ class AnimationNodeBlendSpace2DEditor : public VBoxContainer { void _edit_point_pos(double); void _open_editor(); - void _goto_parent(); - void _removed_from_graph(); void _auto_triangles_toggled(); + StringName get_blend_position_path() const; + + EditorFileDialog *open_file; + Ref<AnimationNode> file_loaded; + void _file_opened(const String &p_file); + + enum { + MENU_LOAD_FILE = 1000, + MENU_PASTE = 1001, + MENU_LOAD_FILE_CONFIRM = 1002 + }; + protected: void _notification(int p_what); static void _bind_methods(); public: static AnimationNodeBlendSpace2DEditor *get_singleton() { return singleton; } - void edit(AnimationNodeBlendSpace2D *p_blend_space); + virtual bool can_edit(const Ref<AnimationNode> &p_node); + virtual void edit(const Ref<AnimationNode> &p_node); AnimationNodeBlendSpace2DEditor(); }; -class AnimationNodeBlendSpace2DEditorPlugin : public EditorPlugin { - GDCLASS(AnimationNodeBlendSpace2DEditorPlugin, EditorPlugin); - - AnimationNodeBlendSpace2DEditor *anim_tree_editor; - EditorNode *editor; - Button *button; - -public: - virtual String get_name() const { return "BlendSpace2D"; } - 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); - - AnimationNodeBlendSpace2DEditorPlugin(EditorNode *p_node); - ~AnimationNodeBlendSpace2DEditorPlugin(); -}; #endif // ANIMATION_BLEND_SPACE_2D_EDITOR_H diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index c00ad451fa..42e32b9788 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -2,6 +2,7 @@ #include "core/io/resource_loader.h" #include "core/project_settings.h" +#include "editor/editor_inspector.h" #include "os/input.h" #include "os/keyboard.h" #include "scene/animation/animation_player.h" @@ -9,27 +10,6 @@ #include "scene/gui/panel.h" #include "scene/main/viewport.h" -void AnimationNodeBlendTreeEditor::edit(AnimationNodeBlendTree *p_blend_tree) { - - if (blend_tree.is_valid()) { - blend_tree->disconnect("removed_from_graph", this, "_removed_from_graph"); - } - - if (p_blend_tree) { - blend_tree = Ref<AnimationNodeBlendTree>(p_blend_tree); - } else { - blend_tree.unref(); - } - - if (blend_tree.is_null()) { - hide(); - } else { - blend_tree->connect("removed_from_graph", this, "_removed_from_graph"); - - _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++) { @@ -58,10 +38,19 @@ void AnimationNodeBlendTreeEditor::remove_custom_type(const Ref<Script> &p_scrip void AnimationNodeBlendTreeEditor::_update_options_menu() { + print_line("update options"); add_node->get_popup()->clear(); for (int i = 0; i < add_options.size(); i++) { - add_node->get_popup()->add_item(add_options[i].name); + add_node->get_popup()->add_item(add_options[i].name, i); } + + Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard(); + if (clipb.is_valid()) { + add_node->get_popup()->add_separator(); + add_node->get_popup()->add_item(TTR("Paste"), MENU_PASTE); + } + add_node->get_popup()->add_separator(); + add_node->get_popup()->add_item(TTR("Load.."), MENU_LOAD_FILE); } Size2 AnimationNodeBlendTreeEditor::get_minimum_size() const { @@ -69,18 +58,28 @@ Size2 AnimationNodeBlendTreeEditor::get_minimum_size() const { return Size2(10, 200); } +void AnimationNodeBlendTreeEditor::_property_changed(const StringName &p_property, const Variant &p_value) { + + AnimationTree *tree = AnimationTreeEditor::get_singleton()->get_tree(); + updating = true; + undo_redo->create_action("Parameter Changed: " + String(p_property), UndoRedo::MERGE_ENDS); + undo_redo->add_do_property(tree, p_property, p_value); + undo_redo->add_undo_property(tree, p_property, tree->get(p_property)); + undo_redo->add_do_method(this, "_update_graph"); + undo_redo->add_undo_method(this, "_update_graph"); + undo_redo->commit_action(); + updating = false; +} + void AnimationNodeBlendTreeEditor::_update_graph() { if (updating) return; + visible_properties.clear(); + graph->set_scroll_ofs(blend_tree->get_graph_offset() * EDSCALE); - if (blend_tree->get_parent().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++) { @@ -107,7 +106,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() { agnode->connect("changed", this, "_node_changed", varray(agnode->get_instance_id()), CONNECT_DEFERRED); } - node->set_offset(agnode->get_position() * EDSCALE); + node->set_offset(blend_tree->get_node_position(E->get()) * EDSCALE); node->set_title(agnode->get_caption()); node->set_name(E->get()); @@ -133,9 +132,28 @@ void AnimationNodeBlendTreeEditor::_update_graph() { node->set_slot(base + i, true, 0, get_color("font_color", "Label"), false, 0, Color()); } - node->connect("dragged", this, "_node_dragged", varray(agnode)); + List<PropertyInfo> pinfo; + agnode->get_parameter_list(&pinfo); + for (List<PropertyInfo>::Element *F = pinfo.front(); F; F = F->next()) { + + if (!(F->get().usage & PROPERTY_USAGE_EDITOR)) { + continue; + } + String base_path = AnimationTreeEditor::get_singleton()->get_base_path() + String(E->get()) + "/" + F->get().name; + EditorProperty *prop = EditorInspector::instantiate_property_editor(AnimationTreeEditor::get_singleton()->get_tree(), F->get().type, base_path, F->get().hint, F->get().hint_string, F->get().usage); + if (prop) { + prop->set_object_and_property(AnimationTreeEditor::get_singleton()->get_tree(), base_path); + prop->update_property(); + prop->set_name_split_ratio(0); + prop->connect("property_changed", this, "_property_changed"); + node->add_child(prop); + visible_properties.push_back(prop); + } + } + + node->connect("dragged", this, "_node_dragged", varray(E->get())); - if (EditorNode::get_singleton()->item_has_editor(agnode.ptr())) { + if (AnimationTreeEditor::get_singleton()->can_edit(agnode)) { node->add_child(memnew(HSeparator)); Button *open_in_editor = memnew(Button); open_in_editor->set_text(TTR("Open Editor")); @@ -169,7 +187,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() { ProgressBar *pb = memnew(ProgressBar); - AnimationTree *player = anim->get_tree(); + AnimationTree *player = AnimationTreeEditor::get_singleton()->get_tree(); if (player->has_node(player->get_animation_player())) { AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(player->get_node(player->get_animation_player())); if (ap) { @@ -194,6 +212,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() { mb->get_popup()->connect("index_pressed", this, "_anim_selected", varray(options, E->get()), CONNECT_DEFERRED); } + /* should be no longer necesary, as the boolean works Ref<AnimationNodeOneShot> oneshot = agnode; if (oneshot.is_valid()) { @@ -209,7 +228,7 @@ void AnimationNodeBlendTreeEditor::_update_graph() { play_stop->add_child(stop); play_stop->add_spacer(); node->add_child(play_stop); - } + } */ } List<AnimationNodeBlendTree::NodeConnection> connections; @@ -225,16 +244,44 @@ void AnimationNodeBlendTreeEditor::_update_graph() { } } -void AnimationNodeBlendTreeEditor::_add_node(int p_idx) { +void AnimationNodeBlendTreeEditor::_file_opened(const String &p_file) { - ERR_FAIL_INDEX(p_idx, add_options.size()); + file_loaded = ResourceLoader::load(p_file); + if (file_loaded.is_valid()) { + _add_node(MENU_LOAD_FILE_CONFIRM); + } +} + +void AnimationNodeBlendTreeEditor::_add_node(int p_idx) { Ref<AnimationNode> anode; - if (add_options[p_idx].type != String()) { + String base_name; + + if (p_idx == MENU_LOAD_FILE) { + + open_file->clear_filters(); + List<String> filters; + ResourceLoader::get_recognized_extensions_for_type("AnimationNode", &filters); + for (List<String>::Element *E = filters.front(); E; E = E->next()) { + open_file->add_filter("*." + E->get()); + } + open_file->popup_centered_ratio(); + return; + } else if (p_idx == MENU_LOAD_FILE_CONFIRM) { + anode = file_loaded; + file_loaded.unref(); + base_name = anode->get_class(); + } else if (p_idx == MENU_PASTE) { + + anode = EditorSettings::get_singleton()->get_resource_clipboard(); + ERR_FAIL_COND(!anode.is_valid()); + base_name = anode->get_class(); + } else 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); + base_name = add_options[p_idx].name; } else { ERR_FAIL_COND(add_options[p_idx].script.is_null()); String base_type = add_options[p_idx].script->get_instance_base_type(); @@ -242,13 +289,16 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) { ERR_FAIL_COND(!an); anode = Ref<AnimationNode>(an); anode->set_script(add_options[p_idx].script.get_ref_ptr()); + base_name = add_options[p_idx].name; } + Ref<AnimationNodeOutput> out = anode; + if (out.is_valid()) { + EditorNode::get_singleton()->show_warning(TTR("Output node can't be added to the blend tree.")); + return; + } Point2 instance_pos = graph->get_scroll_ofs() + graph->get_size() * 0.5; - anode->set_position(instance_pos / EDSCALE); - - String base_name = add_options[p_idx].name; int base = 1; String name = base_name; while (blend_tree->has_node(name)) { @@ -257,19 +307,19 @@ void AnimationNodeBlendTreeEditor::_add_node(int p_idx) { } undo_redo->create_action("Add Node to BlendTree"); - undo_redo->add_do_method(blend_tree.ptr(), "add_node", name, anode); + undo_redo->add_do_method(blend_tree.ptr(), "add_node", name, anode, instance_pos / EDSCALE); undo_redo->add_undo_method(blend_tree.ptr(), "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) { +void AnimationNodeBlendTreeEditor::_node_dragged(const Vector2 &p_from, const Vector2 &p_to, const StringName &p_which) { 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(blend_tree.ptr(), "set_node_position", p_which, p_to / EDSCALE); + undo_redo->add_undo_method(blend_tree.ptr(), "set_node_position", p_which, p_from / EDSCALE); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -342,20 +392,6 @@ void AnimationNodeBlendTreeEditor::_delete_request(const String &p_which) { 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); @@ -373,13 +409,7 @@ 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_parent().is_valid()) { - EditorNode::get_singleton()->edit_item(blend_tree->get_parent().ptr()); - } + AnimationTreeEditor::get_singleton()->enter_editor(p_which); } void AnimationNodeBlendTreeEditor::_filter_toggled() { @@ -417,14 +447,14 @@ bool AnimationNodeBlendTreeEditor::_update_filters(const Ref<AnimationNode> &ano if (updating || _filter_edit != anode) return false; - NodePath player_path = anode->get_tree()->get_animation_player(); + NodePath player_path = AnimationTreeEditor::get_singleton()->get_tree()->get_animation_player(); - if (!anode->get_tree()->has_node(player_path)) { + if (!AnimationTreeEditor::get_singleton()->get_tree()->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_tree()->get_node(player_path)); + AnimationPlayer *player = Object::cast_to<AnimationPlayer>(AnimationTreeEditor::get_singleton()->get_tree()->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; @@ -593,8 +623,6 @@ 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")); } @@ -603,12 +631,10 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) { String error; - if (!blend_tree->get_tree()) { - error = TTR("BlendTree does not belong to an AnimationTree node."); - } else if (!blend_tree->get_tree()->is_active()) { + if (!AnimationTreeEditor::get_singleton()->get_tree()->is_active()) { error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails."); - } else if (blend_tree->get_tree()->is_state_invalid()) { - error = blend_tree->get_tree()->get_invalid_state_reason(); + } else if (AnimationTreeEditor::get_singleton()->get_tree()->is_state_invalid()) { + error = AnimationTreeEditor::get_singleton()->get_tree()->get_invalid_state_reason(); } if (error != error_label->get_text()) { @@ -624,13 +650,13 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) { 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_tree() && !blend_tree->get_tree()->is_state_invalid()) { + if (AnimationTreeEditor::get_singleton()->get_tree() && !AnimationTreeEditor::get_singleton()->get_tree()->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); } - AnimationTree *graph_player = blend_tree->get_tree(); + AnimationTree *graph_player = AnimationTreeEditor::get_singleton()->get_tree(); 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())); @@ -650,6 +676,14 @@ void AnimationNodeBlendTreeEditor::_notification(int p_what) { } } } + + for (int i = 0; i < visible_properties.size(); i++) { + visible_properties[i]->update_property(); + } + } + + if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { + set_process(is_visible_in_tree()); } } @@ -664,9 +698,9 @@ void AnimationNodeBlendTreeEditor::_scroll_changed(const Vector2 &p_scroll) { void AnimationNodeBlendTreeEditor::_node_changed(ObjectID p_node) { AnimationNode *an = Object::cast_to<AnimationNode>(ObjectDB::get_instance(p_node)); - if (an && an->get_parent() == blend_tree) { - _update_graph(); - } + //if (an && an->get_parent() == blend_tree) { + _update_graph(); + //} } void AnimationNodeBlendTreeEditor::_bind_methods() { @@ -680,17 +714,17 @@ void AnimationNodeBlendTreeEditor::_bind_methods() { 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("_removed_from_graph", &AnimationNodeBlendTreeEditor::_removed_from_graph); + ClassDB::bind_method("_property_changed", &AnimationNodeBlendTreeEditor::_property_changed); + ClassDB::bind_method("_file_opened", &AnimationNodeBlendTreeEditor::_file_opened); + ClassDB::bind_method("_update_options_menu", &AnimationNodeBlendTreeEditor::_update_options_menu); ClassDB::bind_method("_anim_selected", &AnimationNodeBlendTreeEditor::_anim_selected); } @@ -708,7 +742,9 @@ void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<Anima ERR_FAIL_COND(new_name == "" || new_name.find(".") != -1 || new_name.find("/") != -1) - ERR_FAIL_COND(new_name == prev_name); + if (new_name == prev_name) { + return; //nothing to do + } String base_name = new_name; int base = 1; @@ -718,22 +754,61 @@ void AnimationNodeBlendTreeEditor::_node_renamed(const String &p_text, Ref<Anima name = base_name + " " + itos(base); } + String base_path = AnimationTreeEditor::get_singleton()->get_base_path(); + updating = true; undo_redo->create_action("Node Renamed"); undo_redo->add_do_method(blend_tree.ptr(), "rename_node", prev_name, name); undo_redo->add_undo_method(blend_tree.ptr(), "rename_node", name, prev_name); + undo_redo->add_do_method(AnimationTreeEditor::get_singleton()->get_tree(), "rename_parameter", base_path + prev_name, base_path + name); + undo_redo->add_undo_method(AnimationTreeEditor::get_singleton()->get_tree(), "rename_parameter", base_path + name, base_path + 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()); + + //change editors accordingly + for (int i = 0; i < visible_properties.size(); i++) { + String pname = visible_properties[i]->get_edited_property().operator String(); + if (pname.begins_with(base_path + prev_name)) { + String new_name = pname.replace_first(base_path + prev_name, base_path + name); + visible_properties[i]->set_object_and_property(visible_properties[i]->get_edited_object(), new_name); + } + } } void AnimationNodeBlendTreeEditor::_node_renamed_focus_out(Node *le, Ref<AnimationNode> p_node) { _node_renamed(le->call("get_text"), p_node); } +bool AnimationNodeBlendTreeEditor::can_edit(const Ref<AnimationNode> &p_node) { + Ref<AnimationNodeBlendTree> bt = p_node; + return bt.is_valid(); +} + +void AnimationNodeBlendTreeEditor::edit(const Ref<AnimationNode> &p_node) { + + if (blend_tree.is_valid()) { + blend_tree->disconnect("removed_from_graph", this, "_removed_from_graph"); + } + + if (p_node.is_valid()) { + blend_tree = p_node; + } else { + blend_tree.unref(); + } + + if (blend_tree.is_null()) { + hide(); + } else { + blend_tree->connect("removed_from_graph", this, "_removed_from_graph"); + + _update_graph(); + } +} + AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { singleton = this; @@ -757,13 +832,8 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { 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"); - - goto_parent = memnew(Button); - 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->get_popup()->connect("id_pressed", this, "_add_node"); + add_node->connect("about_to_show", this, "_update_options_menu"); add_options.push_back(AddOption("Animation", "AnimationNodeAnimation")); add_options.push_back(AddOption("OneShot", "AnimationNodeOneShot")); @@ -804,45 +874,10 @@ AnimationNodeBlendTreeEditor::AnimationNodeBlendTreeEditor() { filters->set_hide_root(true); filters->connect("item_edited", this, "_filter_edited"); + open_file = memnew(EditorFileDialog); + add_child(open_file); + open_file->set_title(TTR("Open Animation Node")); + open_file->set_mode(EditorFileDialog::MODE_OPEN_FILE); + open_file->connect("file_selected", this, "_file_opened"); 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() { -} diff --git a/editor/plugins/animation_blend_tree_editor_plugin.h b/editor/plugins/animation_blend_tree_editor_plugin.h index deba3b2b0e..35ecc32979 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.h +++ b/editor/plugins/animation_blend_tree_editor_plugin.h @@ -3,6 +3,7 @@ #include "editor/editor_node.h" #include "editor/editor_plugin.h" +#include "editor/plugins/animation_tree_editor_plugin.h" #include "editor/property_editor.h" #include "scene/animation/animation_blend_tree.h" #include "scene/gui/button.h" @@ -13,14 +14,13 @@ @author Juan Linietsky <reduzio@gmail.com> */ -class AnimationNodeBlendTreeEditor : public VBoxContainer { +class AnimationNodeBlendTreeEditor : public AnimationTreeNodeEditorPlugin { - GDCLASS(AnimationNodeBlendTreeEditor, VBoxContainer); + GDCLASS(AnimationNodeBlendTreeEditor, AnimationTreeNodeEditorPlugin); Ref<AnimationNodeBlendTree> blend_tree; GraphEdit *graph; MenuButton *add_node; - Button *goto_parent; PanelContainer *error_panel; Label *error_label; @@ -32,6 +32,7 @@ class AnimationNodeBlendTreeEditor : public VBoxContainer { CheckBox *filter_enabled; Map<StringName, ProgressBar *> animations; + Vector<EditorProperty *> visible_properties; void _update_graph(); @@ -52,7 +53,7 @@ class AnimationNodeBlendTreeEditor : public VBoxContainer { static AnimationNodeBlendTreeEditor *singleton; - void _node_dragged(const Vector2 &p_from, const Vector2 &p_to, Ref<AnimationNode> p_node); + void _node_dragged(const Vector2 &p_from, const Vector2 &p_to, const StringName &p_which); void _node_renamed(const String &p_text, Ref<AnimationNode> p_node); void _node_renamed_focus_out(Node *le, Ref<AnimationNode> p_node); @@ -64,11 +65,8 @@ class AnimationNodeBlendTreeEditor : public VBoxContainer { 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); @@ -78,8 +76,19 @@ class AnimationNodeBlendTreeEditor : public VBoxContainer { void _node_changed(ObjectID p_node); + void _property_changed(const StringName &p_property, const Variant &p_value); void _removed_from_graph(); + EditorFileDialog *open_file; + Ref<AnimationNode> file_loaded; + void _file_opened(const String &p_file); + + enum { + MENU_LOAD_FILE = 1000, + MENU_PASTE = 1001, + MENU_LOAD_FILE_CONFIRM = 1002 + }; + protected: void _notification(int p_what); static void _bind_methods(); @@ -91,27 +100,11 @@ public: 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 { + virtual bool can_edit(const Ref<AnimationNode> &p_node); + virtual void edit(const Ref<AnimationNode> &p_node); - 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(); + AnimationNodeBlendTreeEditor(); }; #endif // ANIMATION_BLEND_TREE_EDITOR_PLUGIN_H diff --git a/editor/plugins/animation_state_machine_editor.cpp b/editor/plugins/animation_state_machine_editor.cpp index ee450333c8..3a65cb9b38 100644 --- a/editor/plugins/animation_state_machine_editor.cpp +++ b/editor/plugins/animation_state_machine_editor.cpp @@ -11,22 +11,17 @@ #include "scene/gui/panel.h" #include "scene/main/viewport.h" -void AnimationNodeStateMachineEditor::edit(AnimationNodeStateMachine *p_state_machine) { +bool AnimationNodeStateMachineEditor::can_edit(const Ref<AnimationNode> &p_node) { - if (state_machine.is_valid()) { - state_machine->disconnect("removed_from_graph", this, "_removed_from_graph"); - } + Ref<AnimationNodeStateMachine> ansm = p_node; + return ansm.is_valid(); +} - if (p_state_machine) { - state_machine = Ref<AnimationNodeStateMachine>(p_state_machine); - } else { - state_machine.unref(); - } +void AnimationNodeStateMachineEditor::edit(const Ref<AnimationNode> &p_node) { - if (state_machine.is_null()) { - hide(); - } else { - state_machine->connect("removed_from_graph", this, "_removed_from_graph"); + state_machine = p_node; + + if (state_machine.is_valid()) { selected_transition_from = StringName(); selected_transition_to = StringName(); @@ -38,6 +33,10 @@ void AnimationNodeStateMachineEditor::edit(AnimationNodeStateMachine *p_state_ma void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEvent> &p_event) { + Ref<AnimationNodeStateMachinePlayback> playback = AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); + if (playback.is_null()) + return; + Ref<InputEventKey> k = p_event; if (tool_select->is_pressed() && k.is_valid() && k->is_pressed() && k->get_scancode() == KEY_DELETE && !k->is_echo()) { if (selected_node != StringName() || selected_transition_to != StringName() || selected_transition_from != StringName()) { @@ -59,7 +58,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv ClassDB::get_inheriters_from_class("AnimationRootNode", &classes); menu->add_submenu_item(TTR("Add Animation"), "animations"); - AnimationTree *gp = state_machine->get_tree(); + AnimationTree *gp = AnimationTreeEditor::get_singleton()->get_tree(); ERR_FAIL_COND(!gp); if (gp && gp->has_node(gp->get_animation_player())) { AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(gp->get_node(gp->get_animation_player())); @@ -79,9 +78,17 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv if (name == "Animation") continue; // nope int idx = menu->get_item_count(); - menu->add_item(vformat("Add %s", name)); + menu->add_item(vformat("Add %s", name), idx); menu->set_item_metadata(idx, E->get()); } + Ref<AnimationNode> clipb = EditorSettings::get_singleton()->get_resource_clipboard(); + + if (clipb.is_valid()) { + menu->add_separator(); + menu->add_item(TTR("Paste"), MENU_PASTE); + } + menu->add_separator(); + menu->add_item(TTR("Load.."), MENU_LOAD_FILE); menu->set_global_position(state_machine_draw->get_global_transform().xform(mb->get_position())); menu->popup(); @@ -98,18 +105,12 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv for (int i = node_rects.size() - 1; i >= 0; i--) { //inverse to draw order if (node_rects[i].play.has_point(mb->get_position())) { //edit name - if (play_mode->get_selected() == 1 || !state_machine->is_playing()) { + if (play_mode->get_selected() == 1 || !playback->is_playing()) { //start - state_machine->start(node_rects[i].node_name); + playback->start(node_rects[i].node_name); } else { //travel - if (!state_machine->travel(node_rects[i].node_name)) { - - state_machine->start(node_rects[i].node_name); - //removing this due to usability.. - //error_time = 5; - //error_text = vformat(TTR("No path found from '%s' to '%s'."), state_machine->get_current_node(), node_rects[i].node_name); - } + playback->travel(node_rects[i].node_name); } state_machine_draw->update(); return; @@ -196,8 +197,8 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv Ref<AnimationNode> an = state_machine->get_node(selected_node); updating = true; undo_redo->create_action("Move Node"); - undo_redo->add_do_method(an.ptr(), "set_position", an->get_position() + drag_ofs / EDSCALE); - undo_redo->add_undo_method(an.ptr(), "set_position", an->get_position()); + undo_redo->add_do_method(state_machine.ptr(), "set_node_position", selected_node, state_machine->get_node_position(selected_node) + drag_ofs / EDSCALE); + undo_redo->add_undo_method(state_machine.ptr(), "set_node_position", selected_node, state_machine->get_node_position(selected_node)); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); undo_redo->commit_action(); @@ -293,7 +294,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv snap_y = StringName(); { //snap - Vector2 cpos = state_machine->get_node(selected_node)->get_position() + drag_ofs / EDSCALE; + Vector2 cpos = state_machine->get_node_position(selected_node) + drag_ofs / EDSCALE; List<StringName> nodes; state_machine->get_node_list(&nodes); @@ -303,7 +304,7 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv for (List<StringName>::Element *E = nodes.front(); E; E = E->next()) { if (E->get() == selected_node) continue; - Vector2 npos = state_machine->get_node(E->get())->get_position(); + Vector2 npos = state_machine->get_node_position(E->get()); float d_x = ABS(npos.x - cpos.x); if (d_x < MIN(5, best_d_x)) { @@ -372,19 +373,58 @@ void AnimationNodeStateMachineEditor::_state_machine_gui_input(const Ref<InputEv } } +void AnimationNodeStateMachineEditor::_file_opened(const String &p_file) { + + file_loaded = ResourceLoader::load(p_file); + if (file_loaded.is_valid()) { + _add_menu_type(MENU_LOAD_FILE_CONFIRM); + } +} + void AnimationNodeStateMachineEditor::_add_menu_type(int p_index) { - String type = menu->get_item_metadata(p_index); + String base_name; + Ref<AnimationRootNode> node; - Object *obj = ClassDB::instance(type); - ERR_FAIL_COND(!obj); - AnimationNode *an = Object::cast_to<AnimationNode>(obj); - ERR_FAIL_COND(!an); + if (p_index == MENU_LOAD_FILE) { - Ref<AnimationNode> node(an); - node->set_position(add_node_pos); + open_file->clear_filters(); + List<String> filters; + ResourceLoader::get_recognized_extensions_for_type("AnimationRootNode", &filters); + for (List<String>::Element *E = filters.front(); E; E = E->next()) { + open_file->add_filter("*." + E->get()); + } + open_file->popup_centered_ratio(); + return; + } else if (p_index == MENU_LOAD_FILE_CONFIRM) { + node = file_loaded; + file_loaded.unref(); + } else if (p_index == MENU_PASTE) { + + node = EditorSettings::get_singleton()->get_resource_clipboard(); + + } else { + String type = menu->get_item_metadata(p_index); + + Object *obj = ClassDB::instance(type); + ERR_FAIL_COND(!obj); + AnimationNode *an = Object::cast_to<AnimationNode>(obj); + ERR_FAIL_COND(!an); + + node = Ref<AnimationNode>(an); + base_name = type.replace_first("AnimationNode", ""); + } + + if (!node.is_valid()) { + EditorNode::get_singleton()->show_warning(TTR("This type of node can't be used. Only root nodes are allowed.")); + return; + } + + if (base_name == String()) { + + base_name = node->get_class().replace_first("AnimationNode", ""); + } - String base_name = type.replace_first("AnimationNode", ""); int base = 1; String name = base_name; while (state_machine->has_node(name)) { @@ -394,7 +434,7 @@ void AnimationNodeStateMachineEditor::_add_menu_type(int p_index) { updating = true; undo_redo->create_action("Add Node"); - undo_redo->add_do_method(state_machine.ptr(), "add_node", name, node); + undo_redo->add_do_method(state_machine.ptr(), "add_node", name, node, add_node_pos); undo_redo->add_undo_method(state_machine.ptr(), "remove_node", name); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); @@ -419,11 +459,9 @@ void AnimationNodeStateMachineEditor::_add_animation_type(int p_index) { name = base_name + " " + itos(base); } - anim->set_position(add_node_pos); - updating = true; undo_redo->create_action("Add Node"); - undo_redo->add_do_method(state_machine.ptr(), "add_node", name, anim); + undo_redo->add_do_method(state_machine.ptr(), "add_node", name, anim, add_node_pos); undo_redo->add_undo_method(state_machine.ptr(), "remove_node", name); undo_redo->add_do_method(this, "_update_graph"); undo_redo->add_undo_method(this, "_update_graph"); @@ -502,6 +540,8 @@ void AnimationNodeStateMachineEditor::_clip_dst_line_to_rect(Vector2 &r_from, Ve void AnimationNodeStateMachineEditor::_state_machine_draw() { + Ref<AnimationNodeStateMachinePlayback> playback = AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); + Ref<StyleBox> style = get_stylebox("frame", "GraphNode"); Ref<StyleBox> style_selected = get_stylebox("selectedframe", "GraphNode"); @@ -515,10 +555,17 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { linecolor.a *= 0.3; Ref<StyleBox> playing_overlay = get_stylebox("position", "GraphNode"); - bool playing = state_machine->is_playing(); - StringName current = state_machine->get_current_node(); - StringName blend_from = state_machine->get_blend_from_node(); - Vector<StringName> travel_path = state_machine->get_travel_path(); + bool playing = false; + StringName current; + StringName blend_from; + Vector<StringName> travel_path; + + if (playback.is_valid()) { + playing = playback->is_playing(); + current = playback->get_current_node(); + blend_from = playback->get_blend_from_node(); + travel_path = playback->get_travel_path(); + } if (state_machine_draw->has_focus()) { state_machine_draw->draw_rect(Rect2(Point2(), state_machine_draw->get_size()), accent, false); @@ -534,13 +581,13 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { //snap lines if (dragging_selected) { - Vector2 from = (state_machine->get_node(selected_node)->get_position() * EDSCALE) + drag_ofs - state_machine->get_graph_offset() * EDSCALE; + Vector2 from = (state_machine->get_node_position(selected_node) * EDSCALE) + drag_ofs - state_machine->get_graph_offset() * EDSCALE; if (snap_x != StringName()) { - Vector2 to = (state_machine->get_node(snap_x)->get_position() * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; + Vector2 to = (state_machine->get_node_position(snap_x) * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; state_machine_draw->draw_line(from, to, linecolor, 2); } if (snap_y != StringName()) { - Vector2 to = (state_machine->get_node(snap_y)->get_position() * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; + Vector2 to = (state_machine->get_node_position(snap_y) * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; state_machine_draw->draw_line(from, to, linecolor, 2); } } @@ -563,7 +610,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { } Vector2 offset; - offset += anode->get_position() * EDSCALE; + offset += state_machine->get_node_position(E->get()) * EDSCALE; if (selected_node == E->get() && dragging_selected) { offset += drag_ofs; } @@ -588,10 +635,10 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { //draw conecting line for potential new transition if (connecting) { - Vector2 from = (state_machine->get_node(connecting_from)->get_position() * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; + Vector2 from = (state_machine->get_node_position(connecting_from) * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; Vector2 to; if (connecting_to_node != StringName()) { - to = (state_machine->get_node(connecting_to_node)->get_position() * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; + to = (state_machine->get_node_position(connecting_to_node) * EDSCALE) - state_machine->get_graph_offset() * EDSCALE; } else { to = connecting_to; } @@ -617,15 +664,17 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { TransitionLine tl; tl.from_node = state_machine->get_transition_from(i); Vector2 ofs_from = (dragging_selected && tl.from_node == selected_node) ? drag_ofs : Vector2(); - tl.from = (state_machine->get_node(tl.from_node)->get_position() * EDSCALE) + ofs_from - state_machine->get_graph_offset() * EDSCALE; + tl.from = (state_machine->get_node_position(tl.from_node) * EDSCALE) + ofs_from - state_machine->get_graph_offset() * EDSCALE; tl.to_node = state_machine->get_transition_to(i); Vector2 ofs_to = (dragging_selected && tl.to_node == selected_node) ? drag_ofs : Vector2(); - tl.to = (state_machine->get_node(tl.to_node)->get_position() * EDSCALE) + ofs_to - state_machine->get_graph_offset() * EDSCALE; + tl.to = (state_machine->get_node_position(tl.to_node) * EDSCALE) + ofs_to - state_machine->get_graph_offset() * EDSCALE; Ref<AnimationNodeStateMachineTransition> tr = state_machine->get_transition(i); tl.disabled = tr->is_disabled(); tl.auto_advance = tr->has_auto_advance(); + tl.advance_condition_name = tr->get_advance_condition_name(); + tl.advance_condition_state = false; tl.mode = tr->get_switch_mode(); tl.width = tr_bidi_offset; @@ -665,7 +714,14 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { } } } - _connection_draw(tl.from, tl.to, tl.mode, !tl.disabled, selected, travel, tl.auto_advance); + + bool auto_advance = tl.auto_advance; + StringName fullpath = AnimationTreeEditor::get_singleton()->get_base_path() + String(tl.advance_condition_name); + if (tl.advance_condition_name != StringName() && bool(AnimationTreeEditor::get_singleton()->get_tree()->get(fullpath))) { + tl.advance_condition_state = true; + auto_advance = true; + } + _connection_draw(tl.from, tl.to, tl.mode, !tl.disabled, selected, travel, auto_advance); transition_lines.push_back(tl); } @@ -675,7 +731,7 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { String name = node_rects[i].node_name; Ref<AnimationNode> anode = state_machine->get_node(name); - bool needs_editor = EditorNode::get_singleton()->item_has_editor(anode.ptr()); + bool needs_editor = AnimationTreeEditor::get_singleton()->can_edit(anode); Ref<StyleBox> sb = name == selected_node ? style_selected : style; int strsize = font->get_string_size(name).width; @@ -757,12 +813,14 @@ void AnimationNodeStateMachineEditor::_state_machine_draw() { void AnimationNodeStateMachineEditor::_state_machine_pos_draw() { - if (!state_machine->is_playing()) + Ref<AnimationNodeStateMachinePlayback> playback = AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); + + if (!playback.is_valid() || !playback->is_playing()) return; int idx = -1; for (int i = 0; node_rects.size(); i++) { - if (node_rects[i].node_name == state_machine->get_current_node()) { + if (node_rects[i].node_name == playback->get_current_node()) { idx = i; break; } @@ -785,9 +843,9 @@ void AnimationNodeStateMachineEditor::_state_machine_pos_draw() { } to.y = from.y; - float len = MAX(0.0001, state_machine->get_current_length()); + float len = MAX(0.0001, playback->get_current_length()); - float pos = CLAMP(state_machine->get_current_play_pos(), 0, len); + float pos = CLAMP(playback->get_current_play_pos(), 0, len); float c = pos / len; Color fg = get_color("font_color", "Label"); Color bg = fg; @@ -807,12 +865,6 @@ void AnimationNodeStateMachineEditor::_update_graph() { updating = true; - if (state_machine->get_parent().is_valid()) { - goto_parent_hbox->show(); - } else { - goto_parent_hbox->hide(); - } - state_machine_draw->update(); updating = false; @@ -824,7 +876,6 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { error_panel->add_style_override("panel", get_stylebox("bg", "Tree")); error_label->add_color_override("font_color", get_color("error_color", "Editor")); panel->add_style_override("panel", get_stylebox("bg", "Tree")); - goto_parent->set_icon(get_icon("MoveUp", "EditorIcons")); tool_select->set_icon(get_icon("ToolSelect", "EditorIcons")); tool_create->set_icon(get_icon("ToolAddNode", "EditorIcons")); @@ -856,19 +907,21 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { String error; + Ref<AnimationNodeStateMachinePlayback> playback = AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); + if (error_time > 0) { error = error_text; error_time -= get_process_delta_time(); - } else if (!state_machine->get_tree()) { - error = TTR("StateMachine does not belong to an AnimationTree node."); - } else if (!state_machine->get_tree()->is_active()) { + } else if (!AnimationTreeEditor::get_singleton()->get_tree()->is_active()) { error = TTR("AnimationTree is inactive.\nActivate to enable playback, check node warnings if activation fails."); - } else if (state_machine->get_tree()->is_state_invalid()) { - error = state_machine->get_tree()->get_invalid_state_reason(); - } else if (state_machine->get_parent().is_valid() && state_machine->get_parent()->is_class("AnimationNodeStateMachine")) { + } else if (AnimationTreeEditor::get_singleton()->get_tree()->is_state_invalid()) { + error = AnimationTreeEditor::get_singleton()->get_tree()->get_invalid_state_reason(); + /*} else if (state_machine->get_parent().is_valid() && state_machine->get_parent()->is_class("AnimationNodeStateMachine")) { if (state_machine->get_start_node() == StringName() || state_machine->get_end_node() == StringName()) { error = TTR("Start and end nodes are needed for a sub-transition."); - } + }*/ + } else if (playback.is_null()) { + error = vformat(TTR("No playback resource set at path: %s."), AnimationTreeEditor::get_singleton()->get_base_path() + "playback"); } if (error != error_label->get_text()) { @@ -904,14 +957,38 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { break; } + if (transition_lines[i].advance_condition_name != state_machine->get_transition(tidx)->get_advance_condition_name()) { + state_machine_draw->update(); + break; + } + if (transition_lines[i].mode != state_machine->get_transition(tidx)->get_switch_mode()) { state_machine_draw->update(); break; } + + bool acstate = transition_lines[i].advance_condition_name != StringName() && bool(AnimationTreeEditor::get_singleton()->get_tree()->get(AnimationTreeEditor::get_singleton()->get_base_path() + String(transition_lines[i].advance_condition_name))); + + if (transition_lines[i].advance_condition_state != acstate) { + state_machine_draw->update(); + break; + } } bool same_travel_path = true; - Vector<StringName> tp = state_machine->get_travel_path(); + Vector<StringName> tp; + bool is_playing = false; + StringName current_node; + StringName blend_from_node; + float play_pos = 0; + + if (playback.is_valid()) { + tp = playback->get_travel_path(); + is_playing = playback->is_playing(); + current_node = playback->get_current_node(); + blend_from_node = playback->get_blend_from_node(); + play_pos = playback->get_current_play_pos(); + } { @@ -928,37 +1005,32 @@ void AnimationNodeStateMachineEditor::_notification(int p_what) { } //update if travel state changed - if (!same_travel_path || last_active != state_machine->is_playing() || last_current_node != state_machine->get_current_node() || last_blend_from_node != state_machine->get_blend_from_node()) { + if (!same_travel_path || last_active != is_playing || last_current_node != current_node || last_blend_from_node != blend_from_node) { state_machine_draw->update(); last_travel_path = tp; - last_current_node = state_machine->get_current_node(); - last_active = state_machine->is_playing(); - last_blend_from_node = state_machine->get_blend_from_node(); + last_current_node = current_node; + last_active = is_playing; + last_blend_from_node = blend_from_node; state_machine_play_pos->update(); } - if (last_play_pos != state_machine->get_current_play_pos()) { + if (last_play_pos != play_pos) { - last_play_pos = state_machine->get_current_play_pos(); + last_play_pos = play_pos; state_machine_play_pos->update(); } } if (p_what == NOTIFICATION_VISIBILITY_CHANGED) { over_node = StringName(); + set_process(is_visible_in_tree()); } } void AnimationNodeStateMachineEditor::_open_editor(const String &p_name) { - Ref<AnimationNode> an = state_machine->get_node(p_name); - ERR_FAIL_COND(!an.is_valid()); - EditorNode::get_singleton()->edit_item(an.ptr()); -} - -void AnimationNodeStateMachineEditor::_goto_parent() { - EditorNode::get_singleton()->edit_item(state_machine->get_parent().ptr()); + AnimationTreeEditor::get_singleton()->enter_editor(p_name); } void AnimationNodeStateMachineEditor::_removed_from_graph() { @@ -1114,7 +1186,6 @@ void AnimationNodeStateMachineEditor::_bind_methods() { ClassDB::bind_method("_name_edited", &AnimationNodeStateMachineEditor::_name_edited); - ClassDB::bind_method("_goto_parent", &AnimationNodeStateMachineEditor::_goto_parent); ClassDB::bind_method("_removed_from_graph", &AnimationNodeStateMachineEditor::_removed_from_graph); ClassDB::bind_method("_open_editor", &AnimationNodeStateMachineEditor::_open_editor); @@ -1124,6 +1195,7 @@ void AnimationNodeStateMachineEditor::_bind_methods() { ClassDB::bind_method("_autoplay_selected", &AnimationNodeStateMachineEditor::_autoplay_selected); ClassDB::bind_method("_end_selected", &AnimationNodeStateMachineEditor::_end_selected); ClassDB::bind_method("_update_mode", &AnimationNodeStateMachineEditor::_update_mode); + ClassDB::bind_method("_file_opened", &AnimationNodeStateMachineEditor::_file_opened); } AnimationNodeStateMachineEditor *AnimationNodeStateMachineEditor::singleton = NULL; @@ -1136,13 +1208,6 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { HBoxContainer *top_hb = memnew(HBoxContainer); add_child(top_hb); - goto_parent_hbox = memnew(HBoxContainer); - goto_parent = memnew(ToolButton); - goto_parent->connect("pressed", this, "_goto_parent", varray(), CONNECT_DEFERRED); - goto_parent_hbox->add_child(goto_parent); - goto_parent_hbox->add_child(memnew(VSeparator)); - top_hb->add_child(goto_parent_hbox); - Ref<ButtonGroup> bg; bg.instance(); @@ -1248,7 +1313,7 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { menu = memnew(PopupMenu); add_child(menu); - menu->connect("index_pressed", this, "_add_menu_type"); + menu->connect("id_pressed", this, "_add_menu_type"); animations_menu = memnew(PopupMenu); menu->add_child(animations_menu); @@ -1261,6 +1326,13 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { name_edit->connect("text_entered", this, "_name_edited"); name_edit->set_as_toplevel(true); + open_file = memnew(EditorFileDialog); + add_child(open_file); + open_file->set_title(TTR("Open Animation Node")); + open_file->set_mode(EditorFileDialog::MODE_OPEN_FILE); + open_file->connect("file_selected", this, "_file_opened"); + undo_redo = EditorNode::get_singleton()->get_undo_redo(); + over_text = false; over_node_what = -1; @@ -1271,43 +1343,3 @@ AnimationNodeStateMachineEditor::AnimationNodeStateMachineEditor() { error_time = 0; } - -void AnimationNodeStateMachineEditorPlugin::edit(Object *p_object) { - - anim_tree_editor->edit(Object::cast_to<AnimationNodeStateMachine>(p_object)); -} - -bool AnimationNodeStateMachineEditorPlugin::handles(Object *p_object) const { - - return p_object->is_class("AnimationNodeStateMachine"); -} - -void AnimationNodeStateMachineEditorPlugin::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); - } -} - -AnimationNodeStateMachineEditorPlugin::AnimationNodeStateMachineEditorPlugin(EditorNode *p_node) { - - editor = p_node; - anim_tree_editor = memnew(AnimationNodeStateMachineEditor); - anim_tree_editor->set_custom_minimum_size(Size2(0, 300)); - - button = editor->add_bottom_panel_item(TTR("StateMachine"), anim_tree_editor); - button->hide(); -} - -AnimationNodeStateMachineEditorPlugin::~AnimationNodeStateMachineEditorPlugin() { -} diff --git a/editor/plugins/animation_state_machine_editor.h b/editor/plugins/animation_state_machine_editor.h index efd3de7415..49d08607cf 100644 --- a/editor/plugins/animation_state_machine_editor.h +++ b/editor/plugins/animation_state_machine_editor.h @@ -3,6 +3,7 @@ #include "editor/editor_node.h" #include "editor/editor_plugin.h" +#include "editor/plugins/animation_tree_editor_plugin.h" #include "editor/property_editor.h" #include "scene/animation/animation_node_state_machine.h" #include "scene/gui/button.h" @@ -10,9 +11,9 @@ #include "scene/gui/popup.h" #include "scene/gui/tree.h" -class AnimationNodeStateMachineEditor : public VBoxContainer { +class AnimationNodeStateMachineEditor : public AnimationTreeNodeEditorPlugin { - GDCLASS(AnimationNodeStateMachineEditor, VBoxContainer); + GDCLASS(AnimationNodeStateMachineEditor, AnimationTreeNodeEditorPlugin); Ref<AnimationNodeStateMachine> state_machine; @@ -29,9 +30,6 @@ class AnimationNodeStateMachineEditor : public VBoxContainer { OptionButton *transition_mode; OptionButton *play_mode; - HBoxContainer *goto_parent_hbox; - ToolButton *goto_parent; - PanelContainer *panel; StringName selected_node; @@ -79,8 +77,6 @@ class AnimationNodeStateMachineEditor : public VBoxContainer { void _add_menu_type(int p_index); void _add_animation_type(int p_index); - void _goto_parent(); - void _removed_from_graph(); struct NodeRect { @@ -99,6 +95,8 @@ class AnimationNodeStateMachineEditor : public VBoxContainer { Vector2 from; Vector2 to; AnimationNodeStateMachineTransition::SwitchMode mode; + StringName advance_condition_name; + bool advance_condition_state; bool disabled; bool auto_advance; float width; @@ -135,33 +133,25 @@ class AnimationNodeStateMachineEditor : public VBoxContainer { float error_time; String error_text; + EditorFileDialog *open_file; + Ref<AnimationNode> file_loaded; + void _file_opened(const String &p_file); + + enum { + MENU_LOAD_FILE = 1000, + MENU_PASTE = 1001, + MENU_LOAD_FILE_CONFIRM = 1002 + }; + protected: void _notification(int p_what); static void _bind_methods(); public: static AnimationNodeStateMachineEditor *get_singleton() { return singleton; } - void edit(AnimationNodeStateMachine *p_state_machine); + virtual bool can_edit(const Ref<AnimationNode> &p_node); + virtual void edit(const Ref<AnimationNode> &p_node); AnimationNodeStateMachineEditor(); }; -class AnimationNodeStateMachineEditorPlugin : public EditorPlugin { - - GDCLASS(AnimationNodeStateMachineEditorPlugin, EditorPlugin); - - AnimationNodeStateMachineEditor *anim_tree_editor; - EditorNode *editor; - Button *button; - -public: - virtual String get_name() const { return "StateMachine"; } - 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); - - AnimationNodeStateMachineEditorPlugin(EditorNode *p_node); - ~AnimationNodeStateMachineEditorPlugin(); -}; - #endif // ANIMATION_STATE_MACHINE_EDITOR_H diff --git a/editor/plugins/animation_tree_editor_plugin.cpp b/editor/plugins/animation_tree_editor_plugin.cpp index 25582ae0b9..19921ef54f 100644 --- a/editor/plugins/animation_tree_editor_plugin.cpp +++ b/editor/plugins/animation_tree_editor_plugin.cpp @@ -1,1418 +1,247 @@ -/*************************************************************************/ -/* animation_tree_editor_plugin.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - #include "animation_tree_editor_plugin.h" +#include "animation_blend_space_1d_editor.h" +#include "animation_blend_space_2d_editor.h" +#include "animation_blend_tree_editor_plugin.h" +#include "animation_state_machine_editor.h" #include "core/io/resource_loader.h" #include "core/project_settings.h" +#include "math/delaunay.h" #include "os/input.h" #include "os/keyboard.h" +#include "scene/animation/animation_blend_tree.h" +#include "scene/animation/animation_player.h" #include "scene/gui/menu_button.h" #include "scene/gui/panel.h" #include "scene/main/viewport.h" +#include "scene/scene_string_names.h" -void AnimationTreeEditor::edit(AnimationTreePlayer *p_anim_tree) { +void AnimationTreeEditor::edit(AnimationTree *p_tree) { - anim_tree = p_anim_tree; + if (tree == p_tree) + return; - if (!anim_tree) { - hide(); - } else { - order.clear(); - p_anim_tree->get_node_list(&order); - /* - for(List<StringName>::Element* E=order.front();E;E=E->next()) { + tree = p_tree; - if (E->get() >= (int)last_id) - last_id=E->get()+1; - }*/ - play_button->set_pressed(p_anim_tree->is_active()); - //read the orders + Vector<String> path; + if (tree->has_meta("_tree_edit_path")) { + path = tree->get_meta("_tree_edit_path"); + edit_path(path); + } else { + current_root = 0; } } -Size2 AnimationTreeEditor::_get_maximum_size() { - - Size2 max; - - for (List<StringName>::Element *E = order.front(); E; E = E->next()) { +void AnimationTreeEditor::_path_button_pressed(int p_path) { - Point2 pos = anim_tree->node_get_position(E->get()); - - if (click_type == CLICK_NODE && click_node == E->get()) { + Ref<AnimationNode> node = tree->get_tree_root(); + if (node.is_null()) + return; - pos += click_motion - click_pos; + edited_path.clear(); + if (p_path >= 0) { + for (int i = 0; i <= p_path; i++) { + Ref<AnimationNode> child = node->get_child_by_name(button_path[i]); + ERR_BREAK(child.is_null()); + node = child; + edited_path.push_back(button_path[i]); } - pos += get_node_size(E->get()); - if (pos.x > max.x) - max.x = pos.x; - if (pos.y > max.y) - max.y = pos.y; } - return max; -} - -const char *AnimationTreeEditor::_node_type_names[] = { "Output", "Animation", "OneShot", "Mix", "Blend2", "Blend3", "Blend4", "TimeScale", "TimeSeek", "Transition" }; - -Size2 AnimationTreeEditor::get_node_size(const StringName &p_node) const { - - AnimationTreePlayer::NodeType type = anim_tree->node_get_type(p_node); - - Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); - Ref<Font> font = get_font("font", "PopupMenu"); - - Size2 size = style->get_minimum_size(); - - int count = 2; // title and name - int inputs = anim_tree->node_get_input_count(p_node); - count += inputs ? inputs : 1; - String name = p_node; - - float name_w = font->get_string_size(name).width; - float type_w = font->get_string_size(String(_node_type_names[type])).width; - float max_w = MAX(name_w, type_w); - - switch (type) { - case AnimationTreePlayer::NODE_TIMESEEK: - case AnimationTreePlayer::NODE_OUTPUT: { - } break; - case AnimationTreePlayer::NODE_ANIMATION: - case AnimationTreePlayer::NODE_ONESHOT: - case AnimationTreePlayer::NODE_MIX: - case AnimationTreePlayer::NODE_BLEND2: - case AnimationTreePlayer::NODE_BLEND3: - case AnimationTreePlayer::NODE_BLEND4: - case AnimationTreePlayer::NODE_TIMESCALE: - case AnimationTreePlayer::NODE_TRANSITION: { - - size.height += font->get_height(); - } break; - case AnimationTreePlayer::NODE_MAX: { + for (int i = 0; i < editors.size(); i++) { + if (editors[i]->can_edit(node)) { + editors[i]->edit(node); + editors[i]->show(); + } else { + editors[i]->edit(Ref<AnimationNode>()); + editors[i]->hide(); } } - - size.x += max_w + 20; - size.y += count * (font->get_height() + get_constant("vseparation", "PopupMenu")); - - return size; } -void AnimationTreeEditor::_edit_dialog_changede(String) { - - edit_dialog->hide(); -} - -void AnimationTreeEditor::_edit_dialog_changeds(String s) { - - _edit_dialog_changed(); -} - -void AnimationTreeEditor::_edit_dialog_changedf(float) { - - _edit_dialog_changed(); -} - -void AnimationTreeEditor::_edit_dialog_changed() { - - if (updating_edit) - return; - - if (renaming_edit) { - - if (anim_tree->node_rename(edited_node, edit_line[0]->get_text()) == OK) { - for (List<StringName>::Element *E = order.front(); E; E = E->next()) { - - if (E->get() == edited_node) - E->get() = edit_line[0]->get_text(); - } - edited_node = edit_line[0]->get_text(); - } - update(); - return; +void AnimationTreeEditor::_update_path() { + while (path_hb->get_child_count()) { + memdelete(path_hb->get_child(0)); } - AnimationTreePlayer::NodeType type = anim_tree->node_get_type(edited_node); - - switch (type) { - - case AnimationTreePlayer::NODE_TIMESCALE: - anim_tree->timescale_node_set_scale(edited_node, edit_line[0]->get_text().to_double()); - break; - case AnimationTreePlayer::NODE_ONESHOT: - anim_tree->oneshot_node_set_fadein_time(edited_node, edit_line[0]->get_text().to_double()); - anim_tree->oneshot_node_set_fadeout_time(edited_node, edit_line[1]->get_text().to_double()); - anim_tree->oneshot_node_set_autorestart_delay(edited_node, edit_line[2]->get_text().to_double()); - anim_tree->oneshot_node_set_autorestart_random_delay(edited_node, edit_line[3]->get_text().to_double()); - anim_tree->oneshot_node_set_autorestart(edited_node, edit_check->is_pressed()); - anim_tree->oneshot_node_set_mix_mode(edited_node, edit_option->get_selected()); - - break; - - case AnimationTreePlayer::NODE_MIX: - - anim_tree->mix_node_set_amount(edited_node, edit_scroll[0]->get_value()); - break; - case AnimationTreePlayer::NODE_BLEND2: - anim_tree->blend2_node_set_amount(edited_node, edit_scroll[0]->get_value()); - - break; - - case AnimationTreePlayer::NODE_BLEND3: - anim_tree->blend3_node_set_amount(edited_node, edit_scroll[0]->get_value()); - - break; - case AnimationTreePlayer::NODE_BLEND4: - - anim_tree->blend4_node_set_amount(edited_node, Point2(edit_scroll[0]->get_value(), edit_scroll[1]->get_value())); - - break; - - case AnimationTreePlayer::NODE_TRANSITION: { - anim_tree->transition_node_set_xfade_time(edited_node, edit_line[0]->get_text().to_double()); - if (anim_tree->transition_node_get_current(edited_node) != edit_option->get_selected()) - anim_tree->transition_node_set_current(edited_node, edit_option->get_selected()); - } break; - default: {} - } -} - -void AnimationTreeEditor::_edit_dialog_animation_changed() { - - Ref<Animation> anim = property_editor->get_variant().operator RefPtr(); - anim_tree->animation_node_set_animation(edited_node, anim); - update(); -} - -void AnimationTreeEditor::_edit_dialog_edit_animation() { - - if (Engine::get_singleton()->is_editor_hint()) { - get_tree()->get_root()->get_child(0)->call("_resource_selected", property_editor->get_variant().operator RefPtr()); - }; -}; - -void AnimationTreeEditor::_edit_oneshot_start() { - - anim_tree->oneshot_node_start(edited_node); -} - -void AnimationTreeEditor::_play_toggled() { - - anim_tree->set_active(play_button->is_pressed()); -} - -void AnimationTreeEditor::_master_anim_menu_item(int p_item) { - - if (p_item == 0) - _edit_filters(); - else { - - String str = master_anim_popup->get_item_text(p_item); - anim_tree->animation_node_set_master_animation(edited_node, str); + Ref<ButtonGroup> group; + group.instance(); + + Button *b = memnew(Button); + b->set_text("root"); + b->set_toggle_mode(true); + b->set_button_group(group); + b->set_pressed(true); + b->set_focus_mode(FOCUS_NONE); + b->connect("pressed", this, "_path_button_pressed", varray(-1)); + path_hb->add_child(b); + for (int i = 0; i < button_path.size(); i++) { + b = memnew(Button); + b->set_text(button_path[i]); + b->set_toggle_mode(true); + b->set_button_group(group); + path_hb->add_child(b); + b->set_pressed(true); + b->set_focus_mode(FOCUS_NONE); + b->connect("pressed", this, "_path_button_pressed", varray(i)); } - update(); } -void AnimationTreeEditor::_popup_edit_dialog() { - - updating_edit = true; - - for (int i = 0; i < 2; i++) - edit_scroll[i]->hide(); - - for (int i = 0; i < 4; i++) { - - edit_line[i]->hide(); - edit_label[i]->hide(); - } - - edit_option->hide(); - edit_button->hide(); - filter_button->hide(); - edit_check->hide(); - - Point2 pos = anim_tree->node_get_position(edited_node) - Point2(h_scroll->get_value(), v_scroll->get_value()); - Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); - Size2 size = get_node_size(edited_node); - Point2 popup_pos(pos.x + style->get_margin(MARGIN_LEFT), pos.y + size.y - style->get_margin(MARGIN_BOTTOM)); - popup_pos += get_global_position(); - - if (renaming_edit) { - - edit_label[0]->set_text(TTR("New name:")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_line[0]->set_begin(Point2(15, 25)); - edit_line[0]->set_text(edited_node); - edit_line[0]->show(); - edit_dialog->set_size(Size2(150, 50)); - - } else { - - AnimationTreePlayer::NodeType type = anim_tree->node_get_type(edited_node); - - switch (type) { - - case AnimationTreePlayer::NODE_ANIMATION: - - if (anim_tree->get_master_player() != NodePath() && anim_tree->has_node(anim_tree->get_master_player()) && Object::cast_to<AnimationPlayer>(anim_tree->get_node(anim_tree->get_master_player()))) { - - AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(anim_tree->get_node(anim_tree->get_master_player())); - master_anim_popup->clear(); - master_anim_popup->add_item(TTR("Edit Filters")); - master_anim_popup->add_separator(); - List<StringName> sn; - ap->get_animation_list(&sn); - sn.sort_custom<StringName::AlphCompare>(); - for (List<StringName>::Element *E = sn.front(); E; E = E->next()) { - master_anim_popup->add_item(E->get()); - } - - master_anim_popup->set_position(popup_pos); - master_anim_popup->popup(); - } else { - property_editor->edit(this, "", Variant::OBJECT, anim_tree->animation_node_get_animation(edited_node), PROPERTY_HINT_RESOURCE_TYPE, "Animation"); - property_editor->set_position(popup_pos); - property_editor->popup(); - updating_edit = false; - } - return; - case AnimationTreePlayer::NODE_TIMESCALE: - edit_label[0]->set_text(TTR("Scale:")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_line[0]->set_begin(Point2(15, 25)); - edit_line[0]->set_text(rtos(anim_tree->timescale_node_get_scale(edited_node))); - edit_line[0]->show(); - edit_dialog->set_size(Size2(150, 50)); - break; - case AnimationTreePlayer::NODE_ONESHOT: - edit_label[0]->set_text(TTR("Fade In (s):")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_line[0]->set_begin(Point2(15, 25)); - edit_line[0]->set_text(rtos(anim_tree->oneshot_node_get_fadein_time(edited_node))); - edit_line[0]->show(); - edit_label[1]->set_text(TTR("Fade Out (s):")); - edit_label[1]->set_position(Point2(5, 55)); - edit_label[1]->show(); - edit_line[1]->set_begin(Point2(15, 75)); - edit_line[1]->set_text(rtos(anim_tree->oneshot_node_get_fadeout_time(edited_node))); - edit_line[1]->show(); - - edit_option->clear(); - edit_option->add_item(TTR("Blend"), 0); - edit_option->add_item(TTR("Mix"), 1); - edit_option->set_begin(Point2(15, 105)); - - edit_option->select(anim_tree->oneshot_node_get_mix_mode(edited_node)); - edit_option->show(); - - edit_check->set_text(TTR("Auto Restart:")); - edit_check->set_begin(Point2(15, 125)); - edit_check->set_pressed(anim_tree->oneshot_node_has_autorestart(edited_node)); - edit_check->show(); - - edit_label[2]->set_text(TTR("Restart (s):")); - edit_label[2]->set_position(Point2(5, 145)); - edit_label[2]->show(); - edit_line[2]->set_begin(Point2(15, 165)); - edit_line[2]->set_text(rtos(anim_tree->oneshot_node_get_autorestart_delay(edited_node))); - edit_line[2]->show(); - edit_label[3]->set_text(TTR("Random Restart (s):")); - edit_label[3]->set_position(Point2(5, 195)); - edit_label[3]->show(); - edit_line[3]->set_begin(Point2(15, 215)); - edit_line[3]->set_text(rtos(anim_tree->oneshot_node_get_autorestart_random_delay(edited_node))); - edit_line[3]->show(); - - filter_button->set_begin(Point2(10, 245)); - filter_button->show(); - - edit_button->set_begin(Point2(10, 268)); - edit_button->set_text(TTR("Start!")); - - edit_button->show(); +void AnimationTreeEditor::edit_path(const Vector<String> &p_path) { - edit_dialog->set_size(Size2(180, 293)); + button_path.clear(); - break; + Ref<AnimationNode> node = tree->get_tree_root(); - case AnimationTreePlayer::NODE_MIX: + if (node.is_valid()) { + current_root = node->get_instance_id(); - edit_label[0]->set_text(TTR("Amount:")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_scroll[0]->set_min(0); - edit_scroll[0]->set_max(1); - edit_scroll[0]->set_step(0.01); - edit_scroll[0]->set_value(anim_tree->mix_node_get_amount(edited_node)); - edit_scroll[0]->set_begin(Point2(15, 25)); - edit_scroll[0]->show(); - edit_dialog->set_size(Size2(150, 50)); + for (int i = 0; i < p_path.size(); i++) { - break; - case AnimationTreePlayer::NODE_BLEND2: - edit_label[0]->set_text(TTR("Blend:")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_scroll[0]->set_min(0); - edit_scroll[0]->set_max(1); - edit_scroll[0]->set_step(0.01); - edit_scroll[0]->set_value(anim_tree->blend2_node_get_amount(edited_node)); - edit_scroll[0]->set_begin(Point2(15, 25)); - edit_scroll[0]->show(); - filter_button->set_begin(Point2(10, 47)); - filter_button->show(); - edit_dialog->set_size(Size2(150, 74)); - - break; - - case AnimationTreePlayer::NODE_BLEND3: - edit_label[0]->set_text(TTR("Blend:")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_scroll[0]->set_min(-1); - edit_scroll[0]->set_max(1); - edit_scroll[0]->set_step(0.01); - edit_scroll[0]->set_value(anim_tree->blend3_node_get_amount(edited_node)); - edit_scroll[0]->set_begin(Point2(15, 25)); - edit_scroll[0]->show(); - edit_dialog->set_size(Size2(150, 50)); - - break; - case AnimationTreePlayer::NODE_BLEND4: - - edit_label[0]->set_text(TTR("Blend 0:")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_scroll[0]->set_min(0); - edit_scroll[0]->set_max(1); - edit_scroll[0]->set_step(0.01); - edit_scroll[0]->set_value(anim_tree->blend4_node_get_amount(edited_node).x); - edit_scroll[0]->set_begin(Point2(15, 25)); - edit_scroll[0]->show(); - edit_label[1]->set_text(TTR("Blend 1:")); - edit_label[1]->set_position(Point2(5, 55)); - edit_label[1]->show(); - edit_scroll[1]->set_min(0); - edit_scroll[1]->set_max(1); - edit_scroll[1]->set_step(0.01); - edit_scroll[1]->set_value(anim_tree->blend4_node_get_amount(edited_node).y); - edit_scroll[1]->set_begin(Point2(15, 75)); - edit_scroll[1]->show(); - edit_dialog->set_size(Size2(150, 100)); - - break; - - case AnimationTreePlayer::NODE_TRANSITION: { - - edit_label[0]->set_text(TTR("X-Fade Time (s):")); - edit_label[0]->set_position(Point2(5, 5)); - edit_label[0]->show(); - edit_line[0]->set_begin(Point2(15, 25)); - edit_line[0]->set_text(rtos(anim_tree->transition_node_get_xfade_time(edited_node))); - edit_line[0]->show(); - - edit_label[1]->set_text(TTR("Current:")); - edit_label[1]->set_position(Point2(5, 55)); - edit_label[1]->show(); - edit_option->set_begin(Point2(15, 75)); - - edit_option->clear(); - - for (int i = 0; i < anim_tree->transition_node_get_input_count(edited_node); i++) { - edit_option->add_item(itos(i), i); - } - - edit_option->select(anim_tree->transition_node_get_current(edited_node)); - edit_option->show(); - edit_dialog->set_size(Size2(150, 100)); - - } break; - default: {} + Ref<AnimationNode> child = node->get_child_by_name(p_path[i]); + ERR_BREAK(child.is_null()); + node = child; + button_path.push_back(p_path[i]); } - } - - edit_dialog->set_position(popup_pos); - edit_dialog->popup(); - - updating_edit = false; -} - -void AnimationTreeEditor::_draw_node(const StringName &p_node) { - - RID ci = get_canvas_item(); - AnimationTreePlayer::NodeType type = anim_tree->node_get_type(p_node); - - Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); - Ref<Font> font = get_font("font", "PopupMenu"); - Color font_color = get_color("font_color", "PopupMenu"); - Color font_color_title = get_color("font_color_hover", "PopupMenu"); - font_color_title.a *= 0.8; - Ref<Texture> slot_icon = get_icon("VisualShaderPort", "EditorIcons"); - - Size2 size = get_node_size(p_node); - Point2 pos = anim_tree->node_get_position(p_node); - if (click_type == CLICK_NODE && click_node == p_node) { - - pos += click_motion - click_pos; - if (pos.x < 5) - pos.x = 5; - if (pos.y < 5) - pos.y = 5; - } - - pos -= Point2(h_scroll->get_value(), v_scroll->get_value()); - style->draw(ci, Rect2(pos, size)); - - float w = size.width - style->get_minimum_size().width; - float h = font->get_height() + get_constant("vseparation", "PopupMenu"); - - Point2 ofs = style->get_offset() + pos; - Point2 ascofs(0, font->get_ascent()); - - Color bx = font_color_title; - bx.a *= 0.1; - draw_rect(Rect2(ofs, Size2(size.width - style->get_minimum_size().width, font->get_height())), bx); - font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, String(_node_type_names[type]), font_color_title); - - ofs.y += h; - font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, p_node, font_color); - ofs.y += h; - - int count = 2; // title and name - int inputs = anim_tree->node_get_input_count(p_node); - count += inputs ? inputs : 1; - - float icon_h_ofs = Math::floor((font->get_height() - slot_icon->get_height()) / 2.0) + 1; - - if (type != AnimationTreePlayer::NODE_OUTPUT) - slot_icon->draw(ci, ofs + Point2(w, icon_h_ofs)); //output - - if (inputs) { - for (int i = 0; i < inputs; i++) { - - slot_icon->draw(ci, ofs + Point2(-slot_icon->get_width(), icon_h_ofs)); - String text; - switch (type) { - - case AnimationTreePlayer::NODE_TIMESCALE: - case AnimationTreePlayer::NODE_TIMESEEK: text = "in"; break; - case AnimationTreePlayer::NODE_OUTPUT: text = "out"; break; - case AnimationTreePlayer::NODE_ANIMATION: break; - case AnimationTreePlayer::NODE_ONESHOT: text = (i == 0 ? "in" : "add"); break; - case AnimationTreePlayer::NODE_BLEND2: - case AnimationTreePlayer::NODE_MIX: text = (i == 0 ? "a" : "b"); break; - case AnimationTreePlayer::NODE_BLEND3: - switch (i) { - case 0: text = "b-"; break; - case 1: text = "a"; break; - case 2: text = "b+"; break; - } - break; - - case AnimationTreePlayer::NODE_BLEND4: - switch (i) { - case 0: text = "a0"; break; - case 1: text = "b0"; break; - case 2: text = "a1"; break; - case 3: text = "b1"; break; - } - break; - - case AnimationTreePlayer::NODE_TRANSITION: - text = itos(i); - if (anim_tree->transition_node_has_input_auto_advance(p_node, i)) - text += "->"; - - break; - default: {} + for (int i = 0; i < editors.size(); i++) { + if (editors[i]->can_edit(node)) { + editors[i]->edit(node); + editors[i]->show(); + } else { + editors[i]->edit(Ref<AnimationNode>()); + editors[i]->hide(); } - font->draw(ci, ofs + ascofs + Point2(3, 0), text, font_color); - - ofs.y += h; } } else { - ofs.y += h; + current_root = 0; } - Ref<StyleBox> pg_bg = get_stylebox("bg", "ProgressBar"); - Ref<StyleBox> pg_fill = get_stylebox("fill", "ProgressBar"); - Rect2 pg_rect(ofs, Size2(w, h)); - - bool editable = true; - switch (type) { - case AnimationTreePlayer::NODE_ANIMATION: { - - Ref<Animation> anim = anim_tree->animation_node_get_animation(p_node); - String text; - if (anim_tree->animation_node_get_master_animation(p_node) != "") - text = anim_tree->animation_node_get_master_animation(p_node); - else if (anim.is_null()) - text = "load..."; - else - text = anim->get_name(); - - font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, text, font_color_title); + edited_path = button_path; - } break; - case AnimationTreePlayer::NODE_ONESHOT: - case AnimationTreePlayer::NODE_MIX: - case AnimationTreePlayer::NODE_BLEND2: - case AnimationTreePlayer::NODE_BLEND3: - case AnimationTreePlayer::NODE_BLEND4: - case AnimationTreePlayer::NODE_TIMESCALE: - case AnimationTreePlayer::NODE_TRANSITION: { - - font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, "edit...", font_color_title); - } break; - default: editable = false; - } - - if (editable) { - - Ref<Texture> arrow = get_icon("GuiDropdown", "EditorIcons"); - Point2 arrow_ofs(w - arrow->get_width(), Math::floor((h - arrow->get_height()) / 2)); - arrow->draw(ci, ofs + arrow_ofs); - } + _update_path(); } -AnimationTreeEditor::ClickType AnimationTreeEditor::_locate_click(const Point2 &p_click, StringName *p_node_id, int *p_slot_index) const { - - Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); - Ref<Font> font = get_font("font", "PopupMenu"); - - float h = (font->get_height() + get_constant("vseparation", "PopupMenu")); - - for (const List<StringName>::Element *E = order.back(); E; E = E->prev()) { - - StringName node = E->get(); - - AnimationTreePlayer::NodeType type = anim_tree->node_get_type(node); - - Point2 pos = anim_tree->node_get_position(node); - Size2 size = get_node_size(node); - - pos -= Point2(h_scroll->get_value(), v_scroll->get_value()); - - if (!Rect2(pos, size).has_point(p_click)) - continue; - - if (p_node_id) - *p_node_id = node; - - pos = p_click - pos; - - float y = pos.y - style->get_offset().height; - - if (y < 2 * h) - return CLICK_NODE; - y -= 2 * h; - - int inputs = anim_tree->node_get_input_count(node); - int count = MAX(inputs, 1); - - if (inputs == 0 || (pos.x > size.width / 2 && type != AnimationTreePlayer::NODE_OUTPUT)) { - - if (y < count * h) { - - if (p_slot_index) - *p_slot_index = 0; - return CLICK_OUTPUT_SLOT; - } - } - - for (int i = 0; i < count; i++) { - - if (y < h) { - if (p_slot_index) - *p_slot_index = i; - return CLICK_INPUT_SLOT; - } - y -= h; - } - - bool has_parameters = type != AnimationTreePlayer::NODE_OUTPUT && type != AnimationTreePlayer::NODE_TIMESEEK; - return has_parameters ? CLICK_PARAMETER : CLICK_NODE; - } - - return CLICK_NONE; -} - -Point2 AnimationTreeEditor::_get_slot_pos(const StringName &p_node_id, bool p_input, int p_slot) { - - Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); - Ref<Font> font = get_font("font", "PopupMenu"); - Ref<Texture> slot_icon = get_icon("VisualShaderPort", "EditorIcons"); - - Size2 size = get_node_size(p_node_id); - Point2 pos = anim_tree->node_get_position(p_node_id); - - if (click_type == CLICK_NODE && click_node == p_node_id) { - - pos += click_motion - click_pos; - if (pos.x < 5) - pos.x = 5; - if (pos.y < 5) - pos.y = 5; - } - - pos -= Point2(h_scroll->get_value(), v_scroll->get_value()); - - float w = size.width - style->get_minimum_size().width; - float h = font->get_height() + get_constant("vseparation", "PopupMenu"); - - pos += style->get_offset(); - - pos.y += h * 2; - - pos.y += h * p_slot; - - pos += Point2(-slot_icon->get_width() / 2.0, h / 2.0).floor(); - - if (!p_input) { - pos.x += w + slot_icon->get_width(); - } - - return pos; +Vector<String> AnimationTreeEditor::get_edited_path() const { + return button_path; } -void AnimationTreeEditor::_gui_input(Ref<InputEvent> p_event) { - - Ref<InputEventMouseButton> mb = p_event; - - if (mb.is_valid()) { - - if (mb->is_pressed()) { +void AnimationTreeEditor::enter_editor(const String &p_path) { - if (mb->get_button_index() == 1) { - click_pos = Point2(mb->get_position().x, mb->get_position().y); - click_motion = click_pos; - click_type = _locate_click(click_pos, &click_node, &click_slot); - if (click_type != CLICK_NONE) { - - order.erase(click_node); - order.push_back(click_node); - update(); - } - - switch (click_type) { - case CLICK_INPUT_SLOT: { - click_pos = _get_slot_pos(click_node, true, click_slot); - } break; - case CLICK_OUTPUT_SLOT: { - click_pos = _get_slot_pos(click_node, false, click_slot); - } break; - case CLICK_PARAMETER: { - - edited_node = click_node; - renaming_edit = false; - _popup_edit_dialog(); - //open editor - //_node_edit_property(click_node); - } break; - default: {} - } - } - if (mb->get_button_index() == 2) { - - if (click_type != CLICK_NONE) { - click_type = CLICK_NONE; - update(); - } else { - // try to disconnect/remove - - Point2 rclick_pos = Point2(mb->get_position().x, mb->get_position().y); - rclick_type = _locate_click(rclick_pos, &rclick_node, &rclick_slot); - if (rclick_type == CLICK_INPUT_SLOT || rclick_type == CLICK_OUTPUT_SLOT) { - - node_popup->clear(); - node_popup->set_size(Size2(1, 1)); - node_popup->add_item(TTR("Disconnect"), NODE_DISCONNECT); - if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION) { - node_popup->add_item(TTR("Add Input"), NODE_ADD_INPUT); - if (rclick_type == CLICK_INPUT_SLOT) { - if (anim_tree->transition_node_has_input_auto_advance(rclick_node, rclick_slot)) - node_popup->add_item(TTR("Clear Auto-Advance"), NODE_CLEAR_AUTOADVANCE); - else - node_popup->add_item(TTR("Set Auto-Advance"), NODE_SET_AUTOADVANCE); - node_popup->add_item(TTR("Delete Input"), NODE_DELETE_INPUT); - } - } - - node_popup->set_position(rclick_pos + get_global_position()); - node_popup->popup(); - } - - if (rclick_type == CLICK_NODE) { - node_popup->clear(); - node_popup->set_size(Size2(1, 1)); - node_popup->add_item(TTR("Rename"), NODE_RENAME); - node_popup->add_item(TTR("Remove"), NODE_ERASE); - if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION) - node_popup->add_item(TTR("Add Input"), NODE_ADD_INPUT); - node_popup->set_position(rclick_pos + get_global_position()); - node_popup->popup(); - } - } - } - } else { - - if (mb->get_button_index() == 1 && click_type != CLICK_NONE) { - - switch (click_type) { - case CLICK_INPUT_SLOT: - case CLICK_OUTPUT_SLOT: { - - Point2 dst_click_pos = Point2(mb->get_position().x, mb->get_position().y); - StringName id; - int slot; - ClickType dst_click_type = _locate_click(dst_click_pos, &id, &slot); - - if (dst_click_type == CLICK_INPUT_SLOT && click_type == CLICK_OUTPUT_SLOT) { - - anim_tree->connect_nodes(click_node, id, slot); - } - if (click_type == CLICK_INPUT_SLOT && dst_click_type == CLICK_OUTPUT_SLOT) { - - anim_tree->connect_nodes(id, click_node, click_slot); - } - - } break; - case CLICK_NODE: { - Point2 new_pos = anim_tree->node_get_position(click_node) + (click_motion - click_pos); - if (new_pos.x < 5) - new_pos.x = 5; - if (new_pos.y < 5) - new_pos.y = 5; - anim_tree->node_set_position(click_node, new_pos); - - } break; - default: {} - } - - click_type = CLICK_NONE; - update(); - } - } - } - - Ref<InputEventMouseMotion> mm = p_event; - - if (mm.is_valid()) { - - if (mm->get_button_mask() & 1 && click_type != CLICK_NONE) { - - click_motion = Point2(mm->get_position().x, mm->get_position().y); - update(); - } - if ((mm->get_button_mask() & 4 || Input::get_singleton()->is_key_pressed(KEY_SPACE))) { - - h_scroll->set_value(h_scroll->get_value() - mm->get_relative().x); - v_scroll->set_value(v_scroll->get_value() - mm->get_relative().y); - update(); - } - } + Vector<String> path = edited_path; + path.push_back(p_path); + edit_path(path); } -void AnimationTreeEditor::_draw_cos_line(const Vector2 &p_from, const Vector2 &p_to, const Color &p_color) { - - static const int steps = 20; - - Rect2 r; - r.position = p_from; - r.expand_to(p_to); - Vector2 sign = Vector2((p_from.x < p_to.x) ? 1 : -1, (p_from.y < p_to.y) ? 1 : -1); - bool flip = sign.x * sign.y < 0; - - Vector2 prev; - for (int i = 0; i <= steps; i++) { - - float d = i / float(steps); - float c = -Math::cos(d * Math_PI) * 0.5 + 0.5; - if (flip) - c = 1.0 - c; - Vector2 p = r.position + Vector2(d * r.size.width, c * r.size.height); - - if (i > 0) { - - draw_line(prev, p, p_color, 2); - } - - prev = p; - } +void AnimationTreeEditor::_about_to_show_root() { } void AnimationTreeEditor::_notification(int p_what) { + if (p_what == NOTIFICATION_PROCESS) { + ObjectID root = 0; + if (tree && tree->get_tree_root().is_valid()) { + root = tree->get_tree_root()->get_instance_id(); + } - switch (p_what) { - - case NOTIFICATION_ENTER_TREE: { - - play_button->set_icon(get_icon("Play", "EditorIcons")); - add_menu->set_icon(get_icon("Add", "EditorIcons")); - } break; - case NOTIFICATION_DRAW: { - - _update_scrollbars(); - //VisualServer::get_singleton()->canvas_item_add_rect(get_canvas_item(),Rect2(Point2(),get_size()),Color(0,0,0,1)); - get_stylebox("bg", "Tree")->draw(get_canvas_item(), Rect2(Point2(), get_size())); - - for (List<StringName>::Element *E = order.front(); E; E = E->next()) { - - _draw_node(E->get()); - } - - if (click_type == CLICK_INPUT_SLOT || click_type == CLICK_OUTPUT_SLOT) { - - _draw_cos_line(click_pos, click_motion, Color(0.5, 1, 0.5, 0.8)); - } - - List<AnimationTreePlayer::Connection> connections; - anim_tree->get_connection_list(&connections); - - for (List<AnimationTreePlayer::Connection>::Element *E = connections.front(); E; E = E->next()) { - - const AnimationTreePlayer::Connection &c = E->get(); - Point2 source = _get_slot_pos(c.src_node, false, 0); - Point2 dest = _get_slot_pos(c.dst_node, true, c.dst_input); - Color col = Color(1, 1, 0.5, 0.8); - /* - if (click_type==CLICK_NODE && click_node==c.src_node) { - - source+=click_motion-click_pos; - } - - if (click_type==CLICK_NODE && click_node==c.dst_node) { - - dest+=click_motion-click_pos; - }*/ - - _draw_cos_line(source, dest, col); - } - - switch (anim_tree->get_last_error()) { - - case AnimationTreePlayer::CONNECT_OK: { - - Ref<Font> f = get_font("font", "Label"); - f->draw(get_canvas_item(), Point2(5, 25 + f->get_ascent()), TTR("Animation tree is valid."), Color(0, 1, 0.6, 0.8)); - } break; - default: { - - Ref<Font> f = get_font("font", "Label"); - f->draw(get_canvas_item(), Point2(5, 25 + f->get_ascent()), TTR("Animation tree is invalid."), Color(1, 0.6, 0.0, 0.8)); - } break; - } - - } break; + if (root != current_root) { + edit_path(Vector<String>()); + } } } -void AnimationTreeEditor::_update_scrollbars() { - - Size2 size = get_size(); - Size2 hmin = h_scroll->get_combined_minimum_size(); - Size2 vmin = v_scroll->get_combined_minimum_size(); - - v_scroll->set_begin(Point2(size.width - vmin.width, 0)); - v_scroll->set_end(Point2(size.width, size.height)); - - h_scroll->set_begin(Point2(0, size.height - hmin.height)); - h_scroll->set_end(Point2(size.width - vmin.width, size.height)); - - Size2 min = _get_maximum_size(); - - if (min.height < size.height - hmin.height) { - - v_scroll->hide(); - offset.y = 0; - } else { - - v_scroll->show(); - v_scroll->set_max(min.height); - v_scroll->set_page(size.height - hmin.height); - offset.y = v_scroll->get_value(); - } - - if (min.width < size.width - vmin.width) { - - h_scroll->hide(); - offset.x = 0; - } else { - - h_scroll->show(); - h_scroll->set_max(min.width); - h_scroll->set_page(size.width - vmin.width); - offset.x = h_scroll->get_value(); - } +void AnimationTreeEditor::_bind_methods() { + ClassDB::bind_method("_path_button_pressed", &AnimationTreeEditor::_path_button_pressed); } -void AnimationTreeEditor::_scroll_moved(float) { +AnimationTreeEditor *AnimationTreeEditor::singleton = NULL; - offset.x = h_scroll->get_value(); - offset.y = v_scroll->get_value(); - update(); +void AnimationTreeEditor::add_plugin(AnimationTreeNodeEditorPlugin *p_editor) { + ERR_FAIL_COND(p_editor->get_parent()); + editor_base->add_child(p_editor); + editors.push_back(p_editor); + p_editor->set_h_size_flags(SIZE_EXPAND_FILL); + p_editor->set_v_size_flags(SIZE_EXPAND_FILL); + p_editor->hide(); } -void AnimationTreeEditor::_node_menu_item(int p_item) { - - switch (p_item) { - - case NODE_DISCONNECT: { - - if (rclick_type == CLICK_INPUT_SLOT) { - - anim_tree->disconnect_nodes(rclick_node, rclick_slot); - update(); - } - - if (rclick_type == CLICK_OUTPUT_SLOT) { - - List<AnimationTreePlayer::Connection> connections; - anim_tree->get_connection_list(&connections); - - for (List<AnimationTreePlayer::Connection>::Element *E = connections.front(); E; E = E->next()) { - - const AnimationTreePlayer::Connection &c = E->get(); - if (c.dst_node == rclick_node) { - - anim_tree->disconnect_nodes(c.dst_node, c.dst_input); - } - } - update(); - } - - } break; - case NODE_RENAME: { - - renaming_edit = true; - edited_node = rclick_node; - _popup_edit_dialog(); - - } break; - case NODE_ADD_INPUT: { - - anim_tree->transition_node_set_input_count(rclick_node, anim_tree->transition_node_get_input_count(rclick_node) + 1); - update(); - } break; - case NODE_DELETE_INPUT: { - - anim_tree->transition_node_delete_input(rclick_node, rclick_slot); - update(); - } break; - case NODE_SET_AUTOADVANCE: { - - anim_tree->transition_node_set_input_auto_advance(rclick_node, rclick_slot, true); - update(); - - } break; - case NODE_CLEAR_AUTOADVANCE: { - - anim_tree->transition_node_set_input_auto_advance(rclick_node, rclick_slot, false); - update(); - - } break; - - case NODE_ERASE: { - - if (rclick_node == "out") - break; - order.erase(rclick_node); - anim_tree->remove_node(rclick_node); - update(); - } break; - } +void AnimationTreeEditor::remove_plugin(AnimationTreeNodeEditorPlugin *p_editor) { + ERR_FAIL_COND(p_editor->get_parent() != editor_base); + editor_base->remove_child(p_editor); + editors.erase(p_editor); } -StringName AnimationTreeEditor::_add_node(int p_item) { - - static const char *bname[] = { - "out", - "anim", - "oneshot", - "mix", - "blend2", - "blend3", - "blend4", - "scale", - "seek", - "transition" - }; - - String name; - int idx = 1; - - while (true) { - - name = bname[p_item]; - if (idx > 1) - name += " " + itos(idx); - if (anim_tree->node_exists(name)) - idx++; - else - break; +String AnimationTreeEditor::get_base_path() { + String path = SceneStringNames::get_singleton()->parameters_base_path; + for (int i = 0; i < edited_path.size(); i++) { + path += edited_path[i] + "/"; } - - anim_tree->add_node((AnimationTreePlayer::NodeType)p_item, name); - anim_tree->node_set_position(name, Point2(last_x, last_y)); - order.push_back(name); - last_x += 10; - last_y += 10; - last_x = last_x % (int)get_size().width; - last_y = last_y % (int)get_size().height; - update(); - - return name; -}; - -void AnimationTreeEditor::_file_dialog_selected(String p_path) { - - switch (file_op) { - - case MENU_IMPORT_ANIMATIONS: { - Vector<String> files = file_dialog->get_selected_files(); - - for (int i = 0; i < files.size(); i++) { - - StringName node = _add_node(AnimationTreePlayer::NODE_ANIMATION); - - RES anim = ResourceLoader::load(files[i]); - anim_tree->animation_node_set_animation(node, anim); - //anim_tree->node_set_name(node, files[i].get_file()); - }; - } break; - - default: - break; - }; -}; - -void AnimationTreeEditor::_add_menu_item(int p_item) { - - if (p_item == MENU_GRAPH_CLEAR) { - - //clear - } else if (p_item == MENU_IMPORT_ANIMATIONS) { - - file_op = MENU_IMPORT_ANIMATIONS; - file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE); - file_dialog->popup_centered_ratio(); - - } else { - - _add_node(p_item); - } -} - -Size2 AnimationTreeEditor::get_minimum_size() const { - - return Size2(10, 200); + return path; } -void AnimationTreeEditor::_find_paths_for_filter(const StringName &p_node, Set<String> &paths) { - - ERR_FAIL_COND(!anim_tree->node_exists(p_node)); - - for (int i = 0; i < anim_tree->node_get_input_count(p_node); i++) { - - StringName port = anim_tree->node_get_input_source(p_node, i); - if (port == StringName()) - continue; - _find_paths_for_filter(port, paths); - } - - if (anim_tree->node_get_type(p_node) == AnimationTreePlayer::NODE_ANIMATION) { - - Ref<Animation> anim = anim_tree->animation_node_get_animation(p_node); - if (anim.is_valid()) { - - for (int i = 0; i < anim->get_track_count(); i++) { - paths.insert(anim->track_get_path(i)); - } +bool AnimationTreeEditor::can_edit(const Ref<AnimationNode> &p_node) const { + for (int i = 0; i < editors.size(); i++) { + if (editors[i]->can_edit(p_node)) { + return true; } } + return false; } -void AnimationTreeEditor::_filter_edited() { - - TreeItem *ed = filter->get_edited(); - if (!ed) - return; +Vector<String> AnimationTreeEditor::get_animation_list() { - if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ONESHOT) { - anim_tree->oneshot_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0)); - } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_BLEND2) { - anim_tree->blend2_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0)); - } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ANIMATION) { - anim_tree->animation_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0)); + if (!singleton->is_visible()) { + return Vector<String>(); } -} - -void AnimationTreeEditor::_edit_filters() { - - filter_dialog->popup_centered_ratio(); - filter->clear(); - - Set<String> npb; - _find_paths_for_filter(edited_node, npb); - - TreeItem *root = filter->create_item(); - filter->set_hide_root(true); - Map<String, TreeItem *> pm; - - Node *base = anim_tree->get_node(anim_tree->get_base_path()); - - for (Set<String>::Element *E = npb.front(); E; E = E->next()) { - - TreeItem *parent = root; - String descr = E->get(); - if (base) { - NodePath np = E->get(); - if (np.get_subname_count() == 1) { - Node *n = base->get_node(np); - Skeleton *s = Object::cast_to<Skeleton>(n); - if (s) { + AnimationTree *tree = singleton->tree; + if (!tree || !tree->has_node(tree->get_animation_player())) + return Vector<String>(); - String skelbase = E->get().substr(0, E->get().find(":")); + AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(tree->get_node(tree->get_animation_player())); - int bidx = s->find_bone(np.get_subname(0)); + if (!ap) + return Vector<String>(); - if (bidx != -1) { - int bparent = s->get_bone_parent(bidx); - // - if (bparent != -1) { - - String bpn = skelbase + ":" + s->get_bone_name(bparent); - if (pm.has(bpn)) { - parent = pm[bpn]; - descr = np.get_subname(0); - } - } else { - - if (pm.has(skelbase)) { - parent = pm[skelbase]; - } - } - } - } - } - } - - TreeItem *it = filter->create_item(parent); - it->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); - it->set_text(0, descr); - it->set_metadata(0, NodePath(E->get())); - it->set_editable(0, true); - if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ONESHOT) { - it->set_checked(0, anim_tree->oneshot_node_is_path_filtered(edited_node, E->get())); - } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_BLEND2) { - it->set_checked(0, anim_tree->blend2_node_is_path_filtered(edited_node, E->get())); - } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ANIMATION) { - it->set_checked(0, anim_tree->animation_node_is_path_filtered(edited_node, E->get())); - } - pm[E->get()] = it; + List<StringName> anims; + ap->get_animation_list(&anims); + Vector<String> ret; + for (List<StringName>::Element *E = anims.front(); E; E = E->next()) { + ret.push_back(E->get()); } -} - -void AnimationTreeEditor::_bind_methods() { - ClassDB::bind_method("_add_menu_item", &AnimationTreeEditor::_add_menu_item); - ClassDB::bind_method("_node_menu_item", &AnimationTreeEditor::_node_menu_item); - ClassDB::bind_method("_gui_input", &AnimationTreeEditor::_gui_input); - //ClassDB::bind_method( "_node_param_changed", &AnimationTreeEditor::_node_param_changed ); - ClassDB::bind_method("_scroll_moved", &AnimationTreeEditor::_scroll_moved); - ClassDB::bind_method("_edit_dialog_changeds", &AnimationTreeEditor::_edit_dialog_changeds); - ClassDB::bind_method("_edit_dialog_changede", &AnimationTreeEditor::_edit_dialog_changede); - ClassDB::bind_method("_edit_dialog_changedf", &AnimationTreeEditor::_edit_dialog_changedf); - ClassDB::bind_method("_edit_dialog_changed", &AnimationTreeEditor::_edit_dialog_changed); - ClassDB::bind_method("_edit_dialog_animation_changed", &AnimationTreeEditor::_edit_dialog_animation_changed); - ClassDB::bind_method("_edit_dialog_edit_animation", &AnimationTreeEditor::_edit_dialog_edit_animation); - ClassDB::bind_method("_play_toggled", &AnimationTreeEditor::_play_toggled); - ClassDB::bind_method("_edit_oneshot_start", &AnimationTreeEditor::_edit_oneshot_start); - ClassDB::bind_method("_file_dialog_selected", &AnimationTreeEditor::_file_dialog_selected); - ClassDB::bind_method("_master_anim_menu_item", &AnimationTreeEditor::_master_anim_menu_item); - ClassDB::bind_method("_edit_filters", &AnimationTreeEditor::_edit_filters); - ClassDB::bind_method("_filter_edited", &AnimationTreeEditor::_filter_edited); + return ret; } AnimationTreeEditor::AnimationTreeEditor() { - set_focus_mode(FOCUS_ALL); - - PopupMenu *p; - List<PropertyInfo> defaults; - - add_menu = memnew(MenuButton); - //add_menu->set_ - add_menu->set_position(Point2(0, 0)); - add_menu->set_size(Point2(25, 15)); - add_child(add_menu); - - p = add_menu->get_popup(); - p->add_item(TTR("Animation Node"), AnimationTreePlayer::NODE_ANIMATION); - p->add_item(TTR("OneShot Node"), AnimationTreePlayer::NODE_ONESHOT); - p->add_item(TTR("Mix Node"), AnimationTreePlayer::NODE_MIX); - p->add_item(TTR("Blend2 Node"), AnimationTreePlayer::NODE_BLEND2); - p->add_item(TTR("Blend3 Node"), AnimationTreePlayer::NODE_BLEND3); - p->add_item(TTR("Blend4 Node"), AnimationTreePlayer::NODE_BLEND4); - p->add_item(TTR("TimeScale Node"), AnimationTreePlayer::NODE_TIMESCALE); - p->add_item(TTR("TimeSeek Node"), AnimationTreePlayer::NODE_TIMESEEK); - p->add_item(TTR("Transition Node"), AnimationTreePlayer::NODE_TRANSITION); - p->add_separator(); - p->add_item(TTR("Import Animations..."), MENU_IMPORT_ANIMATIONS); // wtf - p->add_separator(); - p->add_item(TTR("Clear"), MENU_GRAPH_CLEAR); - - p->connect("id_pressed", this, "_add_menu_item"); - - play_button = memnew(Button); - play_button->set_position(Point2(25, 0)); - play_button->set_size(Point2(25, 15)); - add_child(play_button); - play_button->set_toggle_mode(true); - play_button->connect("pressed", this, "_play_toggled"); - - last_x = 50; - last_y = 50; - - property_editor = memnew(CustomPropertyEditor); - add_child(property_editor); - property_editor->connect("variant_changed", this, "_edit_dialog_animation_changed"); - property_editor->connect("resource_edit_request", this, "_edit_dialog_edit_animation"); - - h_scroll = memnew(HScrollBar); - v_scroll = memnew(VScrollBar); - - add_child(h_scroll); - add_child(v_scroll); - - h_scroll->connect("value_changed", this, "_scroll_moved"); - v_scroll->connect("value_changed", this, "_scroll_moved"); - - node_popup = memnew(PopupMenu); - add_child(node_popup); - node_popup->set_as_toplevel(true); - - master_anim_popup = memnew(PopupMenu); - add_child(master_anim_popup); - master_anim_popup->connect("id_pressed", this, "_master_anim_menu_item"); - - node_popup->connect("id_pressed", this, "_node_menu_item"); - - updating_edit = false; - - edit_dialog = memnew(PopupPanel); - //edit_dialog->get_ok()->hide(); - //edit_dialog->get_cancel()->hide(); - add_child(edit_dialog); - - edit_option = memnew(OptionButton); - edit_option->set_anchor(MARGIN_RIGHT, ANCHOR_END); - edit_option->set_margin(MARGIN_RIGHT, -10); - edit_dialog->add_child(edit_option); - edit_option->connect("item_selected", this, "_edit_dialog_changedf"); - edit_option->hide(); - - for (int i = 0; i < 2; i++) { - edit_scroll[i] = memnew(HSlider); - edit_scroll[i]->set_anchor(MARGIN_RIGHT, ANCHOR_END); - edit_scroll[i]->set_margin(MARGIN_RIGHT, -10); - edit_dialog->add_child(edit_scroll[i]); - edit_scroll[i]->hide(); - edit_scroll[i]->connect("value_changed", this, "_edit_dialog_changedf"); - } - for (int i = 0; i < 4; i++) { - edit_line[i] = memnew(LineEdit); - edit_line[i]->set_anchor(MARGIN_RIGHT, ANCHOR_END); - edit_line[i]->set_margin(MARGIN_RIGHT, -10); - edit_dialog->add_child(edit_line[i]); - edit_line[i]->hide(); - edit_line[i]->connect("text_changed", this, "_edit_dialog_changeds"); - edit_line[i]->connect("text_entered", this, "_edit_dialog_changede"); - edit_label[i] = memnew(Label); - edit_dialog->add_child(edit_label[i]); - edit_label[i]->hide(); - } - - edit_button = memnew(Button); - edit_button->set_anchor(MARGIN_RIGHT, ANCHOR_END); - edit_button->set_margin(MARGIN_RIGHT, -10); - edit_dialog->add_child(edit_button); - edit_button->hide(); - edit_button->connect("pressed", this, "_edit_oneshot_start"); - - edit_check = memnew(CheckButton); - edit_check->set_anchor(MARGIN_RIGHT, ANCHOR_END); - edit_check->set_margin(MARGIN_RIGHT, -10); - edit_dialog->add_child(edit_check); - edit_check->hide(); - edit_check->connect("pressed", this, "_edit_dialog_changed"); - - file_dialog = memnew(EditorFileDialog); - file_dialog->set_enable_multiple_selection(true); - file_dialog->set_current_dir(ProjectSettings::get_singleton()->get_resource_path()); - add_child(file_dialog); - file_dialog->connect("file_selected", this, "_file_dialog_selected"); - - filter_dialog = memnew(AcceptDialog); - filter_dialog->set_title(TTR("Edit Node Filters")); - add_child(filter_dialog); - - filter = memnew(Tree); - filter_dialog->add_child(filter); - //filter_dialog->set_child_rect(filter); - filter->connect("item_edited", this, "_filter_edited"); + AnimationNodeAnimation::get_editable_animation_list = get_animation_list; + path_edit = memnew(ScrollContainer); + add_child(path_edit); + path_edit->set_enable_h_scroll(true); + path_edit->set_enable_v_scroll(false); + path_hb = memnew(HBoxContainer); + path_edit->add_child(path_hb); - filter_button = memnew(Button); - filter_button->set_anchor(MARGIN_RIGHT, ANCHOR_END); - filter_button->set_margin(MARGIN_RIGHT, -10); - edit_dialog->add_child(filter_button); - filter_button->hide(); - filter_button->set_text(TTR("Filters...")); - filter_button->connect("pressed", this, "_edit_filters"); + current_root = 0; + singleton = this; + editor_base = memnew(PanelContainer); + editor_base->set_v_size_flags(SIZE_EXPAND_FILL); + add_child(editor_base); - set_clip_contents(true); + add_plugin(memnew(AnimationNodeBlendTreeEditor)); + add_plugin(memnew(AnimationNodeBlendSpace1DEditor)); + add_plugin(memnew(AnimationNodeBlendSpace2DEditor)); + add_plugin(memnew(AnimationNodeStateMachineEditor)); } void AnimationTreeEditorPlugin::edit(Object *p_object) { - anim_tree_editor->edit(Object::cast_to<AnimationTreePlayer>(p_object)); + anim_tree_editor->edit(Object::cast_to<AnimationTree>(p_object)); } bool AnimationTreeEditorPlugin::handles(Object *p_object) const { - return p_object->is_class("AnimationTreePlayer"); + return p_object->is_class("AnimationTree"); } void AnimationTreeEditorPlugin::make_visible(bool p_visible) { @@ -1422,13 +251,13 @@ void AnimationTreeEditorPlugin::make_visible(bool p_visible) { //editor->animation_panel_make_visible(true); button->show(); editor->make_bottom_panel_item_visible(anim_tree_editor); - anim_tree_editor->set_physics_process(true); + 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_physics_process(false); + anim_tree_editor->set_process(false); } } diff --git a/editor/plugins/animation_tree_editor_plugin.h b/editor/plugins/animation_tree_editor_plugin.h index aeb5b1744f..b12054bb62 100644 --- a/editor/plugins/animation_tree_editor_plugin.h +++ b/editor/plugins/animation_tree_editor_plugin.h @@ -1,167 +1,65 @@ -/*************************************************************************/ -/* animation_tree_editor_plugin.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - #ifndef ANIMATION_TREE_EDITOR_PLUGIN_H #define ANIMATION_TREE_EDITOR_PLUGIN_H #include "editor/editor_node.h" #include "editor/editor_plugin.h" #include "editor/property_editor.h" -#include "scene/animation/animation_tree_player.h" +#include "scene/animation/animation_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 AnimationTreeEditor : public Control { - - GDCLASS(AnimationTreeEditor, Control); - - static const char *_node_type_names[]; - - enum ClickType { - CLICK_NONE, - CLICK_NAME, - CLICK_NODE, - CLICK_INPUT_SLOT, - CLICK_OUTPUT_SLOT, - CLICK_PARAMETER - }; - - enum { - - MENU_GRAPH_CLEAR = 100, - MENU_IMPORT_ANIMATIONS = 101, - NODE_DISCONNECT, - NODE_RENAME, - NODE_ERASE, - NODE_ADD_INPUT, - NODE_DELETE_INPUT, - NODE_SET_AUTOADVANCE, - NODE_CLEAR_AUTOADVANCE - }; - - bool renaming_edit; - StringName edited_node; - bool updating_edit; - Popup *edit_dialog; - HSlider *edit_scroll[2]; - LineEdit *edit_line[4]; - OptionButton *edit_option; - Label *edit_label[4]; - Button *edit_button; - Button *filter_button; - CheckButton *edit_check; - EditorFileDialog *file_dialog; - int file_op; - - void _popup_edit_dialog(); - - void _setup_edit_dialog(const StringName &p_node); - PopupMenu *master_anim_popup; - PopupMenu *node_popup; - PopupMenu *add_popup; - HScrollBar *h_scroll; - VScrollBar *v_scroll; - MenuButton *add_menu; - - CustomPropertyEditor *property_editor; - - AnimationTreePlayer *anim_tree; - List<StringName> order; - Set<StringName> active_nodes; - - int last_x, last_y; - - Point2 offset; - ClickType click_type; - Point2 click_pos; - StringName click_node; - int click_slot; - Point2 click_motion; - ClickType rclick_type; - StringName rclick_node; - int rclick_slot; - - Button *play_button; - - Size2 _get_maximum_size(); - Size2 get_node_size(const StringName &p_node) const; - void _draw_node(const StringName &p_node); - - AcceptDialog *filter_dialog; - Tree *filter; - - void _draw_cos_line(const Vector2 &p_from, const Vector2 &p_to, const Color &p_color); - void _update_scrollbars(); - void _scroll_moved(float); - void _play_toggled(); - /* - void _node_param_changed(); - void _node_add_callback(); - void _node_add(VisualServer::AnimationTreeNodeType p_type); - void _node_edit_property(const StringName& p_node); -*/ - - void _master_anim_menu_item(int p_item); - void _node_menu_item(int p_item); - void _add_menu_item(int p_item); - - void _filter_edited(); - void _find_paths_for_filter(const StringName &p_node, Set<String> &paths); - void _edit_filters(); - - void _edit_oneshot_start(); - void _edit_dialog_animation_changed(); - void _edit_dialog_edit_animation(); - void _edit_dialog_changeds(String); - void _edit_dialog_changede(String); - void _edit_dialog_changedf(float); - void _edit_dialog_changed(); - void _dialog_changed() const; - ClickType _locate_click(const Point2 &p_click, StringName *p_node_id, int *p_slot_index) const; - Point2 _get_slot_pos(const StringName &p_node_id, bool p_input, int p_slot); - - StringName _add_node(int p_item); - void _file_dialog_selected(String p_path); + +class AnimationTreeNodeEditorPlugin : public VBoxContainer { + GDCLASS(AnimationTreeNodeEditorPlugin, VBoxContainer) +public: + virtual bool can_edit(const Ref<AnimationNode> &p_node) = 0; + virtual void edit(const Ref<AnimationNode> &p_node) = 0; +}; + +class AnimationTreeEditor : public VBoxContainer { + + GDCLASS(AnimationTreeEditor, VBoxContainer); + + ScrollContainer *path_edit; + HBoxContainer *path_hb; + + AnimationTree *tree; + PanelContainer *editor_base; + + Vector<String> button_path; + Vector<String> edited_path; + Vector<AnimationTreeNodeEditorPlugin *> editors; + + void _update_path(); + void _about_to_show_root(); + ObjectID current_root; + + void _path_button_pressed(int p_path); + + static Vector<String> get_animation_list(); protected: void _notification(int p_what); - void _gui_input(Ref<InputEvent> p_event); static void _bind_methods(); + static AnimationTreeEditor *singleton; + public: - virtual Size2 get_minimum_size() const; - void edit(AnimationTreePlayer *p_anim_tree); + AnimationTree *get_tree() { return tree; } + void add_plugin(AnimationTreeNodeEditorPlugin *p_editor); + void remove_plugin(AnimationTreeNodeEditorPlugin *p_editor); + + String get_base_path(); + + bool can_edit(const Ref<AnimationNode> &p_node) const; + + void edit_path(const Vector<String> &p_path); + Vector<String> get_edited_path() const; + + void enter_editor(const String &p_path = ""); + static AnimationTreeEditor *get_singleton() { return singleton; } + void edit(AnimationTree *p_tree); AnimationTreeEditor(); }; @@ -174,7 +72,7 @@ class AnimationTreeEditorPlugin : public EditorPlugin { Button *button; public: - virtual String get_name() const { return "AnimTree"; } + virtual String get_name() const { return "AnimationTree"; } bool has_main_screen() const { return false; } virtual void edit(Object *p_object); virtual bool handles(Object *p_object) const; diff --git a/editor/plugins/animation_tree_player_editor_plugin.cpp b/editor/plugins/animation_tree_player_editor_plugin.cpp new file mode 100644 index 0000000000..36d10ab99e --- /dev/null +++ b/editor/plugins/animation_tree_player_editor_plugin.cpp @@ -0,0 +1,1446 @@ +/*************************************************************************/ +/* animation_tree_editor_plugin.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "animation_tree_player_editor_plugin.h" + +#include "core/io/resource_loader.h" +#include "core/project_settings.h" +#include "os/input.h" +#include "os/keyboard.h" +#include "scene/gui/menu_button.h" +#include "scene/gui/panel.h" +#include "scene/main/viewport.h" + +void AnimationTreePlayerEditor::edit(AnimationTreePlayer *p_anim_tree) { + + anim_tree = p_anim_tree; + + if (!anim_tree) { + hide(); + } else { + order.clear(); + p_anim_tree->get_node_list(&order); + /* + for(List<StringName>::Element* E=order.front();E;E=E->next()) { + + if (E->get() >= (int)last_id) + last_id=E->get()+1; + }*/ + play_button->set_pressed(p_anim_tree->is_active()); + //read the orders + } +} + +Size2 AnimationTreePlayerEditor::_get_maximum_size() { + + Size2 max; + + for (List<StringName>::Element *E = order.front(); E; E = E->next()) { + + Point2 pos = anim_tree->node_get_position(E->get()); + + if (click_type == CLICK_NODE && click_node == E->get()) { + + pos += click_motion - click_pos; + } + pos += get_node_size(E->get()); + if (pos.x > max.x) + max.x = pos.x; + if (pos.y > max.y) + max.y = pos.y; + } + + return max; +} + +const char *AnimationTreePlayerEditor::_node_type_names[] = { "Output", "Animation", "OneShot", "Mix", "Blend2", "Blend3", "Blend4", "TimeScale", "TimeSeek", "Transition" }; + +Size2 AnimationTreePlayerEditor::get_node_size(const StringName &p_node) const { + + AnimationTreePlayer::NodeType type = anim_tree->node_get_type(p_node); + + Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); + Ref<Font> font = get_font("font", "PopupMenu"); + + Size2 size = style->get_minimum_size(); + + int count = 2; // title and name + int inputs = anim_tree->node_get_input_count(p_node); + count += inputs ? inputs : 1; + String name = p_node; + + float name_w = font->get_string_size(name).width; + float type_w = font->get_string_size(String(_node_type_names[type])).width; + float max_w = MAX(name_w, type_w); + + switch (type) { + case AnimationTreePlayer::NODE_TIMESEEK: + case AnimationTreePlayer::NODE_OUTPUT: { + } break; + case AnimationTreePlayer::NODE_ANIMATION: + case AnimationTreePlayer::NODE_ONESHOT: + case AnimationTreePlayer::NODE_MIX: + case AnimationTreePlayer::NODE_BLEND2: + case AnimationTreePlayer::NODE_BLEND3: + case AnimationTreePlayer::NODE_BLEND4: + case AnimationTreePlayer::NODE_TIMESCALE: + case AnimationTreePlayer::NODE_TRANSITION: { + + size.height += font->get_height(); + } break; + case AnimationTreePlayer::NODE_MAX: { + } + } + + size.x += max_w + 20; + size.y += count * (font->get_height() + get_constant("vseparation", "PopupMenu")); + + return size; +} + +void AnimationTreePlayerEditor::_edit_dialog_changede(String) { + + edit_dialog->hide(); +} + +void AnimationTreePlayerEditor::_edit_dialog_changeds(String s) { + + _edit_dialog_changed(); +} + +void AnimationTreePlayerEditor::_edit_dialog_changedf(float) { + + _edit_dialog_changed(); +} + +void AnimationTreePlayerEditor::_edit_dialog_changed() { + + if (updating_edit) + return; + + if (renaming_edit) { + + if (anim_tree->node_rename(edited_node, edit_line[0]->get_text()) == OK) { + for (List<StringName>::Element *E = order.front(); E; E = E->next()) { + + if (E->get() == edited_node) + E->get() = edit_line[0]->get_text(); + } + edited_node = edit_line[0]->get_text(); + } + update(); + return; + } + + AnimationTreePlayer::NodeType type = anim_tree->node_get_type(edited_node); + + switch (type) { + + case AnimationTreePlayer::NODE_TIMESCALE: + anim_tree->timescale_node_set_scale(edited_node, edit_line[0]->get_text().to_double()); + break; + case AnimationTreePlayer::NODE_ONESHOT: + anim_tree->oneshot_node_set_fadein_time(edited_node, edit_line[0]->get_text().to_double()); + anim_tree->oneshot_node_set_fadeout_time(edited_node, edit_line[1]->get_text().to_double()); + anim_tree->oneshot_node_set_autorestart_delay(edited_node, edit_line[2]->get_text().to_double()); + anim_tree->oneshot_node_set_autorestart_random_delay(edited_node, edit_line[3]->get_text().to_double()); + anim_tree->oneshot_node_set_autorestart(edited_node, edit_check->is_pressed()); + anim_tree->oneshot_node_set_mix_mode(edited_node, edit_option->get_selected()); + + break; + + case AnimationTreePlayer::NODE_MIX: + + anim_tree->mix_node_set_amount(edited_node, edit_scroll[0]->get_value()); + break; + case AnimationTreePlayer::NODE_BLEND2: + anim_tree->blend2_node_set_amount(edited_node, edit_scroll[0]->get_value()); + + break; + + case AnimationTreePlayer::NODE_BLEND3: + anim_tree->blend3_node_set_amount(edited_node, edit_scroll[0]->get_value()); + + break; + case AnimationTreePlayer::NODE_BLEND4: + + anim_tree->blend4_node_set_amount(edited_node, Point2(edit_scroll[0]->get_value(), edit_scroll[1]->get_value())); + + break; + + case AnimationTreePlayer::NODE_TRANSITION: { + anim_tree->transition_node_set_xfade_time(edited_node, edit_line[0]->get_text().to_double()); + if (anim_tree->transition_node_get_current(edited_node) != edit_option->get_selected()) + anim_tree->transition_node_set_current(edited_node, edit_option->get_selected()); + } break; + default: {} + } +} + +void AnimationTreePlayerEditor::_edit_dialog_animation_changed() { + + Ref<Animation> anim = property_editor->get_variant().operator RefPtr(); + anim_tree->animation_node_set_animation(edited_node, anim); + update(); +} + +void AnimationTreePlayerEditor::_edit_dialog_edit_animation() { + + if (Engine::get_singleton()->is_editor_hint()) { + get_tree()->get_root()->get_child(0)->call("_resource_selected", property_editor->get_variant().operator RefPtr()); + }; +}; + +void AnimationTreePlayerEditor::_edit_oneshot_start() { + + anim_tree->oneshot_node_start(edited_node); +} + +void AnimationTreePlayerEditor::_play_toggled() { + + anim_tree->set_active(play_button->is_pressed()); +} + +void AnimationTreePlayerEditor::_master_anim_menu_item(int p_item) { + + if (p_item == 0) + _edit_filters(); + else { + + String str = master_anim_popup->get_item_text(p_item); + anim_tree->animation_node_set_master_animation(edited_node, str); + } + update(); +} + +void AnimationTreePlayerEditor::_popup_edit_dialog() { + + updating_edit = true; + + for (int i = 0; i < 2; i++) + edit_scroll[i]->hide(); + + for (int i = 0; i < 4; i++) { + + edit_line[i]->hide(); + edit_label[i]->hide(); + } + + edit_option->hide(); + edit_button->hide(); + filter_button->hide(); + edit_check->hide(); + + Point2 pos = anim_tree->node_get_position(edited_node) - Point2(h_scroll->get_value(), v_scroll->get_value()); + Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); + Size2 size = get_node_size(edited_node); + Point2 popup_pos(pos.x + style->get_margin(MARGIN_LEFT), pos.y + size.y - style->get_margin(MARGIN_BOTTOM)); + popup_pos += get_global_position(); + + if (renaming_edit) { + + edit_label[0]->set_text(TTR("New name:")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_line[0]->set_begin(Point2(15, 25)); + edit_line[0]->set_text(edited_node); + edit_line[0]->show(); + edit_dialog->set_size(Size2(150, 50)); + + } else { + + AnimationTreePlayer::NodeType type = anim_tree->node_get_type(edited_node); + + switch (type) { + + case AnimationTreePlayer::NODE_ANIMATION: + + if (anim_tree->get_master_player() != NodePath() && anim_tree->has_node(anim_tree->get_master_player()) && Object::cast_to<AnimationPlayer>(anim_tree->get_node(anim_tree->get_master_player()))) { + + AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(anim_tree->get_node(anim_tree->get_master_player())); + master_anim_popup->clear(); + master_anim_popup->add_item(TTR("Edit Filters")); + master_anim_popup->add_separator(); + List<StringName> sn; + ap->get_animation_list(&sn); + sn.sort_custom<StringName::AlphCompare>(); + for (List<StringName>::Element *E = sn.front(); E; E = E->next()) { + master_anim_popup->add_item(E->get()); + } + + master_anim_popup->set_position(popup_pos); + master_anim_popup->popup(); + } else { + property_editor->edit(this, "", Variant::OBJECT, anim_tree->animation_node_get_animation(edited_node), PROPERTY_HINT_RESOURCE_TYPE, "Animation"); + property_editor->set_position(popup_pos); + property_editor->popup(); + updating_edit = false; + } + return; + case AnimationTreePlayer::NODE_TIMESCALE: + edit_label[0]->set_text(TTR("Scale:")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_line[0]->set_begin(Point2(15, 25)); + edit_line[0]->set_text(rtos(anim_tree->timescale_node_get_scale(edited_node))); + edit_line[0]->show(); + edit_dialog->set_size(Size2(150, 50)); + break; + case AnimationTreePlayer::NODE_ONESHOT: + edit_label[0]->set_text(TTR("Fade In (s):")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_line[0]->set_begin(Point2(15, 25)); + edit_line[0]->set_text(rtos(anim_tree->oneshot_node_get_fadein_time(edited_node))); + edit_line[0]->show(); + edit_label[1]->set_text(TTR("Fade Out (s):")); + edit_label[1]->set_position(Point2(5, 55)); + edit_label[1]->show(); + edit_line[1]->set_begin(Point2(15, 75)); + edit_line[1]->set_text(rtos(anim_tree->oneshot_node_get_fadeout_time(edited_node))); + edit_line[1]->show(); + + edit_option->clear(); + edit_option->add_item(TTR("Blend"), 0); + edit_option->add_item(TTR("Mix"), 1); + edit_option->set_begin(Point2(15, 105)); + + edit_option->select(anim_tree->oneshot_node_get_mix_mode(edited_node)); + edit_option->show(); + + edit_check->set_text(TTR("Auto Restart:")); + edit_check->set_begin(Point2(15, 125)); + edit_check->set_pressed(anim_tree->oneshot_node_has_autorestart(edited_node)); + edit_check->show(); + + edit_label[2]->set_text(TTR("Restart (s):")); + edit_label[2]->set_position(Point2(5, 145)); + edit_label[2]->show(); + edit_line[2]->set_begin(Point2(15, 165)); + edit_line[2]->set_text(rtos(anim_tree->oneshot_node_get_autorestart_delay(edited_node))); + edit_line[2]->show(); + edit_label[3]->set_text(TTR("Random Restart (s):")); + edit_label[3]->set_position(Point2(5, 195)); + edit_label[3]->show(); + edit_line[3]->set_begin(Point2(15, 215)); + edit_line[3]->set_text(rtos(anim_tree->oneshot_node_get_autorestart_random_delay(edited_node))); + edit_line[3]->show(); + + filter_button->set_begin(Point2(10, 245)); + filter_button->show(); + + edit_button->set_begin(Point2(10, 268)); + edit_button->set_text(TTR("Start!")); + + edit_button->show(); + + edit_dialog->set_size(Size2(180, 293)); + + break; + + case AnimationTreePlayer::NODE_MIX: + + edit_label[0]->set_text(TTR("Amount:")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_scroll[0]->set_min(0); + edit_scroll[0]->set_max(1); + edit_scroll[0]->set_step(0.01); + edit_scroll[0]->set_value(anim_tree->mix_node_get_amount(edited_node)); + edit_scroll[0]->set_begin(Point2(15, 25)); + edit_scroll[0]->show(); + edit_dialog->set_size(Size2(150, 50)); + + break; + case AnimationTreePlayer::NODE_BLEND2: + edit_label[0]->set_text(TTR("Blend:")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_scroll[0]->set_min(0); + edit_scroll[0]->set_max(1); + edit_scroll[0]->set_step(0.01); + edit_scroll[0]->set_value(anim_tree->blend2_node_get_amount(edited_node)); + edit_scroll[0]->set_begin(Point2(15, 25)); + edit_scroll[0]->show(); + filter_button->set_begin(Point2(10, 47)); + filter_button->show(); + edit_dialog->set_size(Size2(150, 74)); + + break; + + case AnimationTreePlayer::NODE_BLEND3: + edit_label[0]->set_text(TTR("Blend:")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_scroll[0]->set_min(-1); + edit_scroll[0]->set_max(1); + edit_scroll[0]->set_step(0.01); + edit_scroll[0]->set_value(anim_tree->blend3_node_get_amount(edited_node)); + edit_scroll[0]->set_begin(Point2(15, 25)); + edit_scroll[0]->show(); + edit_dialog->set_size(Size2(150, 50)); + + break; + case AnimationTreePlayer::NODE_BLEND4: + + edit_label[0]->set_text(TTR("Blend 0:")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_scroll[0]->set_min(0); + edit_scroll[0]->set_max(1); + edit_scroll[0]->set_step(0.01); + edit_scroll[0]->set_value(anim_tree->blend4_node_get_amount(edited_node).x); + edit_scroll[0]->set_begin(Point2(15, 25)); + edit_scroll[0]->show(); + edit_label[1]->set_text(TTR("Blend 1:")); + edit_label[1]->set_position(Point2(5, 55)); + edit_label[1]->show(); + edit_scroll[1]->set_min(0); + edit_scroll[1]->set_max(1); + edit_scroll[1]->set_step(0.01); + edit_scroll[1]->set_value(anim_tree->blend4_node_get_amount(edited_node).y); + edit_scroll[1]->set_begin(Point2(15, 75)); + edit_scroll[1]->show(); + edit_dialog->set_size(Size2(150, 100)); + + break; + + case AnimationTreePlayer::NODE_TRANSITION: { + + edit_label[0]->set_text(TTR("X-Fade Time (s):")); + edit_label[0]->set_position(Point2(5, 5)); + edit_label[0]->show(); + edit_line[0]->set_begin(Point2(15, 25)); + edit_line[0]->set_text(rtos(anim_tree->transition_node_get_xfade_time(edited_node))); + edit_line[0]->show(); + + edit_label[1]->set_text(TTR("Current:")); + edit_label[1]->set_position(Point2(5, 55)); + edit_label[1]->show(); + edit_option->set_begin(Point2(15, 75)); + + edit_option->clear(); + + for (int i = 0; i < anim_tree->transition_node_get_input_count(edited_node); i++) { + edit_option->add_item(itos(i), i); + } + + edit_option->select(anim_tree->transition_node_get_current(edited_node)); + edit_option->show(); + edit_dialog->set_size(Size2(150, 100)); + + } break; + default: {} + } + } + + edit_dialog->set_position(popup_pos); + edit_dialog->popup(); + + updating_edit = false; +} + +void AnimationTreePlayerEditor::_draw_node(const StringName &p_node) { + + RID ci = get_canvas_item(); + AnimationTreePlayer::NodeType type = anim_tree->node_get_type(p_node); + + Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); + Ref<Font> font = get_font("font", "PopupMenu"); + Color font_color = get_color("font_color", "PopupMenu"); + Color font_color_title = get_color("font_color_hover", "PopupMenu"); + font_color_title.a *= 0.8; + Ref<Texture> slot_icon = get_icon("VisualShaderPort", "EditorIcons"); + + Size2 size = get_node_size(p_node); + Point2 pos = anim_tree->node_get_position(p_node); + if (click_type == CLICK_NODE && click_node == p_node) { + + pos += click_motion - click_pos; + if (pos.x < 5) + pos.x = 5; + if (pos.y < 5) + pos.y = 5; + } + + pos -= Point2(h_scroll->get_value(), v_scroll->get_value()); + + style->draw(ci, Rect2(pos, size)); + + float w = size.width - style->get_minimum_size().width; + float h = font->get_height() + get_constant("vseparation", "PopupMenu"); + + Point2 ofs = style->get_offset() + pos; + Point2 ascofs(0, font->get_ascent()); + + Color bx = font_color_title; + bx.a *= 0.1; + draw_rect(Rect2(ofs, Size2(size.width - style->get_minimum_size().width, font->get_height())), bx); + font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, String(_node_type_names[type]), font_color_title); + + ofs.y += h; + font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, p_node, font_color); + ofs.y += h; + + int count = 2; // title and name + int inputs = anim_tree->node_get_input_count(p_node); + count += inputs ? inputs : 1; + + float icon_h_ofs = Math::floor((font->get_height() - slot_icon->get_height()) / 2.0) + 1; + + if (type != AnimationTreePlayer::NODE_OUTPUT) + slot_icon->draw(ci, ofs + Point2(w, icon_h_ofs)); //output + + if (inputs) { + for (int i = 0; i < inputs; i++) { + + slot_icon->draw(ci, ofs + Point2(-slot_icon->get_width(), icon_h_ofs)); + String text; + switch (type) { + + case AnimationTreePlayer::NODE_TIMESCALE: + case AnimationTreePlayer::NODE_TIMESEEK: text = "in"; break; + case AnimationTreePlayer::NODE_OUTPUT: text = "out"; break; + case AnimationTreePlayer::NODE_ANIMATION: break; + case AnimationTreePlayer::NODE_ONESHOT: text = (i == 0 ? "in" : "add"); break; + case AnimationTreePlayer::NODE_BLEND2: + case AnimationTreePlayer::NODE_MIX: text = (i == 0 ? "a" : "b"); break; + case AnimationTreePlayer::NODE_BLEND3: + switch (i) { + case 0: text = "b-"; break; + case 1: text = "a"; break; + case 2: text = "b+"; break; + } + break; + + case AnimationTreePlayer::NODE_BLEND4: + switch (i) { + case 0: text = "a0"; break; + case 1: text = "b0"; break; + case 2: text = "a1"; break; + case 3: text = "b1"; break; + } + break; + + case AnimationTreePlayer::NODE_TRANSITION: + text = itos(i); + if (anim_tree->transition_node_has_input_auto_advance(p_node, i)) + text += "->"; + + break; + default: {} + } + font->draw(ci, ofs + ascofs + Point2(3, 0), text, font_color); + + ofs.y += h; + } + } else { + ofs.y += h; + } + + Ref<StyleBox> pg_bg = get_stylebox("bg", "ProgressBar"); + Ref<StyleBox> pg_fill = get_stylebox("fill", "ProgressBar"); + Rect2 pg_rect(ofs, Size2(w, h)); + + bool editable = true; + switch (type) { + case AnimationTreePlayer::NODE_ANIMATION: { + + Ref<Animation> anim = anim_tree->animation_node_get_animation(p_node); + String text; + if (anim_tree->animation_node_get_master_animation(p_node) != "") + text = anim_tree->animation_node_get_master_animation(p_node); + else if (anim.is_null()) + text = "load..."; + else + text = anim->get_name(); + + font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, text, font_color_title); + + } break; + case AnimationTreePlayer::NODE_ONESHOT: + case AnimationTreePlayer::NODE_MIX: + case AnimationTreePlayer::NODE_BLEND2: + case AnimationTreePlayer::NODE_BLEND3: + case AnimationTreePlayer::NODE_BLEND4: + case AnimationTreePlayer::NODE_TIMESCALE: + case AnimationTreePlayer::NODE_TRANSITION: { + + font->draw_halign(ci, ofs + ascofs, HALIGN_CENTER, w, "edit...", font_color_title); + } break; + default: editable = false; + } + + if (editable) { + + Ref<Texture> arrow = get_icon("GuiDropdown", "EditorIcons"); + Point2 arrow_ofs(w - arrow->get_width(), Math::floor((h - arrow->get_height()) / 2)); + arrow->draw(ci, ofs + arrow_ofs); + } +} + +AnimationTreePlayerEditor::ClickType AnimationTreePlayerEditor::_locate_click(const Point2 &p_click, StringName *p_node_id, int *p_slot_index) const { + + Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); + Ref<Font> font = get_font("font", "PopupMenu"); + + float h = (font->get_height() + get_constant("vseparation", "PopupMenu")); + + for (const List<StringName>::Element *E = order.back(); E; E = E->prev()) { + + StringName node = E->get(); + + AnimationTreePlayer::NodeType type = anim_tree->node_get_type(node); + + Point2 pos = anim_tree->node_get_position(node); + Size2 size = get_node_size(node); + + pos -= Point2(h_scroll->get_value(), v_scroll->get_value()); + + if (!Rect2(pos, size).has_point(p_click)) + continue; + + if (p_node_id) + *p_node_id = node; + + pos = p_click - pos; + + float y = pos.y - style->get_offset().height; + + if (y < 2 * h) + return CLICK_NODE; + y -= 2 * h; + + int inputs = anim_tree->node_get_input_count(node); + int count = MAX(inputs, 1); + + if (inputs == 0 || (pos.x > size.width / 2 && type != AnimationTreePlayer::NODE_OUTPUT)) { + + if (y < count * h) { + + if (p_slot_index) + *p_slot_index = 0; + return CLICK_OUTPUT_SLOT; + } + } + + for (int i = 0; i < count; i++) { + + if (y < h) { + if (p_slot_index) + *p_slot_index = i; + return CLICK_INPUT_SLOT; + } + y -= h; + } + + bool has_parameters = type != AnimationTreePlayer::NODE_OUTPUT && type != AnimationTreePlayer::NODE_TIMESEEK; + return has_parameters ? CLICK_PARAMETER : CLICK_NODE; + } + + return CLICK_NONE; +} + +Point2 AnimationTreePlayerEditor::_get_slot_pos(const StringName &p_node_id, bool p_input, int p_slot) { + + Ref<StyleBox> style = get_stylebox("panel", "PopupMenu"); + Ref<Font> font = get_font("font", "PopupMenu"); + Ref<Texture> slot_icon = get_icon("VisualShaderPort", "EditorIcons"); + + Size2 size = get_node_size(p_node_id); + Point2 pos = anim_tree->node_get_position(p_node_id); + + if (click_type == CLICK_NODE && click_node == p_node_id) { + + pos += click_motion - click_pos; + if (pos.x < 5) + pos.x = 5; + if (pos.y < 5) + pos.y = 5; + } + + pos -= Point2(h_scroll->get_value(), v_scroll->get_value()); + + float w = size.width - style->get_minimum_size().width; + float h = font->get_height() + get_constant("vseparation", "PopupMenu"); + + pos += style->get_offset(); + + pos.y += h * 2; + + pos.y += h * p_slot; + + pos += Point2(-slot_icon->get_width() / 2.0, h / 2.0).floor(); + + if (!p_input) { + pos.x += w + slot_icon->get_width(); + } + + return pos; +} + +void AnimationTreePlayerEditor::_gui_input(Ref<InputEvent> p_event) { + + Ref<InputEventMouseButton> mb = p_event; + + if (mb.is_valid()) { + + if (mb->is_pressed()) { + + if (mb->get_button_index() == 1) { + click_pos = Point2(mb->get_position().x, mb->get_position().y); + click_motion = click_pos; + click_type = _locate_click(click_pos, &click_node, &click_slot); + if (click_type != CLICK_NONE) { + + order.erase(click_node); + order.push_back(click_node); + update(); + } + + switch (click_type) { + case CLICK_INPUT_SLOT: { + click_pos = _get_slot_pos(click_node, true, click_slot); + } break; + case CLICK_OUTPUT_SLOT: { + click_pos = _get_slot_pos(click_node, false, click_slot); + } break; + case CLICK_PARAMETER: { + + edited_node = click_node; + renaming_edit = false; + _popup_edit_dialog(); + //open editor + //_node_edit_property(click_node); + } break; + default: {} + } + } + if (mb->get_button_index() == 2) { + + if (click_type != CLICK_NONE) { + click_type = CLICK_NONE; + update(); + } else { + // try to disconnect/remove + + Point2 rclick_pos = Point2(mb->get_position().x, mb->get_position().y); + rclick_type = _locate_click(rclick_pos, &rclick_node, &rclick_slot); + if (rclick_type == CLICK_INPUT_SLOT || rclick_type == CLICK_OUTPUT_SLOT) { + + node_popup->clear(); + node_popup->set_size(Size2(1, 1)); + node_popup->add_item(TTR("Disconnect"), NODE_DISCONNECT); + if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION) { + node_popup->add_item(TTR("Add Input"), NODE_ADD_INPUT); + if (rclick_type == CLICK_INPUT_SLOT) { + if (anim_tree->transition_node_has_input_auto_advance(rclick_node, rclick_slot)) + node_popup->add_item(TTR("Clear Auto-Advance"), NODE_CLEAR_AUTOADVANCE); + else + node_popup->add_item(TTR("Set Auto-Advance"), NODE_SET_AUTOADVANCE); + node_popup->add_item(TTR("Delete Input"), NODE_DELETE_INPUT); + } + } + + node_popup->set_position(rclick_pos + get_global_position()); + node_popup->popup(); + } + + if (rclick_type == CLICK_NODE) { + node_popup->clear(); + node_popup->set_size(Size2(1, 1)); + node_popup->add_item(TTR("Rename"), NODE_RENAME); + node_popup->add_item(TTR("Remove"), NODE_ERASE); + if (anim_tree->node_get_type(rclick_node) == AnimationTreePlayer::NODE_TRANSITION) + node_popup->add_item(TTR("Add Input"), NODE_ADD_INPUT); + node_popup->set_position(rclick_pos + get_global_position()); + node_popup->popup(); + } + } + } + } else { + + if (mb->get_button_index() == 1 && click_type != CLICK_NONE) { + + switch (click_type) { + case CLICK_INPUT_SLOT: + case CLICK_OUTPUT_SLOT: { + + Point2 dst_click_pos = Point2(mb->get_position().x, mb->get_position().y); + StringName id; + int slot; + ClickType dst_click_type = _locate_click(dst_click_pos, &id, &slot); + + if (dst_click_type == CLICK_INPUT_SLOT && click_type == CLICK_OUTPUT_SLOT) { + + anim_tree->connect_nodes(click_node, id, slot); + } + if (click_type == CLICK_INPUT_SLOT && dst_click_type == CLICK_OUTPUT_SLOT) { + + anim_tree->connect_nodes(id, click_node, click_slot); + } + + } break; + case CLICK_NODE: { + Point2 new_pos = anim_tree->node_get_position(click_node) + (click_motion - click_pos); + if (new_pos.x < 5) + new_pos.x = 5; + if (new_pos.y < 5) + new_pos.y = 5; + anim_tree->node_set_position(click_node, new_pos); + + } break; + default: {} + } + + click_type = CLICK_NONE; + update(); + } + } + } + + Ref<InputEventMouseMotion> mm = p_event; + + if (mm.is_valid()) { + + if (mm->get_button_mask() & 1 && click_type != CLICK_NONE) { + + click_motion = Point2(mm->get_position().x, mm->get_position().y); + update(); + } + if ((mm->get_button_mask() & 4 || Input::get_singleton()->is_key_pressed(KEY_SPACE))) { + + h_scroll->set_value(h_scroll->get_value() - mm->get_relative().x); + v_scroll->set_value(v_scroll->get_value() - mm->get_relative().y); + update(); + } + } +} + +void AnimationTreePlayerEditor::_draw_cos_line(const Vector2 &p_from, const Vector2 &p_to, const Color &p_color) { + + static const int steps = 20; + + Rect2 r; + r.position = p_from; + r.expand_to(p_to); + Vector2 sign = Vector2((p_from.x < p_to.x) ? 1 : -1, (p_from.y < p_to.y) ? 1 : -1); + bool flip = sign.x * sign.y < 0; + + Vector2 prev; + for (int i = 0; i <= steps; i++) { + + float d = i / float(steps); + float c = -Math::cos(d * Math_PI) * 0.5 + 0.5; + if (flip) + c = 1.0 - c; + Vector2 p = r.position + Vector2(d * r.size.width, c * r.size.height); + + if (i > 0) { + + draw_line(prev, p, p_color, 2); + } + + prev = p; + } +} + +void AnimationTreePlayerEditor::_notification(int p_what) { + + switch (p_what) { + + case NOTIFICATION_ENTER_TREE: { + + play_button->set_icon(get_icon("Play", "EditorIcons")); + add_menu->set_icon(get_icon("Add", "EditorIcons")); + } break; + case NOTIFICATION_DRAW: { + + _update_scrollbars(); + //VisualServer::get_singleton()->canvas_item_add_rect(get_canvas_item(),Rect2(Point2(),get_size()),Color(0,0,0,1)); + get_stylebox("bg", "Tree")->draw(get_canvas_item(), Rect2(Point2(), get_size())); + + for (List<StringName>::Element *E = order.front(); E; E = E->next()) { + + _draw_node(E->get()); + } + + if (click_type == CLICK_INPUT_SLOT || click_type == CLICK_OUTPUT_SLOT) { + + _draw_cos_line(click_pos, click_motion, Color(0.5, 1, 0.5, 0.8)); + } + + List<AnimationTreePlayer::Connection> connections; + anim_tree->get_connection_list(&connections); + + for (List<AnimationTreePlayer::Connection>::Element *E = connections.front(); E; E = E->next()) { + + const AnimationTreePlayer::Connection &c = E->get(); + Point2 source = _get_slot_pos(c.src_node, false, 0); + Point2 dest = _get_slot_pos(c.dst_node, true, c.dst_input); + Color col = Color(1, 1, 0.5, 0.8); + /* + if (click_type==CLICK_NODE && click_node==c.src_node) { + + source+=click_motion-click_pos; + } + + if (click_type==CLICK_NODE && click_node==c.dst_node) { + + dest+=click_motion-click_pos; + }*/ + + _draw_cos_line(source, dest, col); + } + + switch (anim_tree->get_last_error()) { + + case AnimationTreePlayer::CONNECT_OK: { + + Ref<Font> f = get_font("font", "Label"); + f->draw(get_canvas_item(), Point2(5, 25 + f->get_ascent()), TTR("Animation tree is valid."), Color(0, 1, 0.6, 0.8)); + } break; + default: { + + Ref<Font> f = get_font("font", "Label"); + f->draw(get_canvas_item(), Point2(5, 25 + f->get_ascent()), TTR("Animation tree is invalid."), Color(1, 0.6, 0.0, 0.8)); + } break; + } + + } break; + } +} + +void AnimationTreePlayerEditor::_update_scrollbars() { + + Size2 size = get_size(); + Size2 hmin = h_scroll->get_combined_minimum_size(); + Size2 vmin = v_scroll->get_combined_minimum_size(); + + v_scroll->set_begin(Point2(size.width - vmin.width, 0)); + v_scroll->set_end(Point2(size.width, size.height)); + + h_scroll->set_begin(Point2(0, size.height - hmin.height)); + h_scroll->set_end(Point2(size.width - vmin.width, size.height)); + + Size2 min = _get_maximum_size(); + + if (min.height < size.height - hmin.height) { + + v_scroll->hide(); + offset.y = 0; + } else { + + v_scroll->show(); + v_scroll->set_max(min.height); + v_scroll->set_page(size.height - hmin.height); + offset.y = v_scroll->get_value(); + } + + if (min.width < size.width - vmin.width) { + + h_scroll->hide(); + offset.x = 0; + } else { + + h_scroll->show(); + h_scroll->set_max(min.width); + h_scroll->set_page(size.width - vmin.width); + offset.x = h_scroll->get_value(); + } +} + +void AnimationTreePlayerEditor::_scroll_moved(float) { + + offset.x = h_scroll->get_value(); + offset.y = v_scroll->get_value(); + update(); +} + +void AnimationTreePlayerEditor::_node_menu_item(int p_item) { + + switch (p_item) { + + case NODE_DISCONNECT: { + + if (rclick_type == CLICK_INPUT_SLOT) { + + anim_tree->disconnect_nodes(rclick_node, rclick_slot); + update(); + } + + if (rclick_type == CLICK_OUTPUT_SLOT) { + + List<AnimationTreePlayer::Connection> connections; + anim_tree->get_connection_list(&connections); + + for (List<AnimationTreePlayer::Connection>::Element *E = connections.front(); E; E = E->next()) { + + const AnimationTreePlayer::Connection &c = E->get(); + if (c.dst_node == rclick_node) { + + anim_tree->disconnect_nodes(c.dst_node, c.dst_input); + } + } + update(); + } + + } break; + case NODE_RENAME: { + + renaming_edit = true; + edited_node = rclick_node; + _popup_edit_dialog(); + + } break; + case NODE_ADD_INPUT: { + + anim_tree->transition_node_set_input_count(rclick_node, anim_tree->transition_node_get_input_count(rclick_node) + 1); + update(); + } break; + case NODE_DELETE_INPUT: { + + anim_tree->transition_node_delete_input(rclick_node, rclick_slot); + update(); + } break; + case NODE_SET_AUTOADVANCE: { + + anim_tree->transition_node_set_input_auto_advance(rclick_node, rclick_slot, true); + update(); + + } break; + case NODE_CLEAR_AUTOADVANCE: { + + anim_tree->transition_node_set_input_auto_advance(rclick_node, rclick_slot, false); + update(); + + } break; + + case NODE_ERASE: { + + if (rclick_node == "out") + break; + order.erase(rclick_node); + anim_tree->remove_node(rclick_node); + update(); + } break; + } +} + +StringName AnimationTreePlayerEditor::_add_node(int p_item) { + + static const char *bname[] = { + "out", + "anim", + "oneshot", + "mix", + "blend2", + "blend3", + "blend4", + "scale", + "seek", + "transition" + }; + + String name; + int idx = 1; + + while (true) { + + name = bname[p_item]; + if (idx > 1) + name += " " + itos(idx); + if (anim_tree->node_exists(name)) + idx++; + else + break; + } + + anim_tree->add_node((AnimationTreePlayer::NodeType)p_item, name); + anim_tree->node_set_position(name, Point2(last_x, last_y)); + order.push_back(name); + last_x += 10; + last_y += 10; + last_x = last_x % (int)get_size().width; + last_y = last_y % (int)get_size().height; + update(); + + return name; +}; + +void AnimationTreePlayerEditor::_file_dialog_selected(String p_path) { + + switch (file_op) { + + case MENU_IMPORT_ANIMATIONS: { + Vector<String> files = file_dialog->get_selected_files(); + + for (int i = 0; i < files.size(); i++) { + + StringName node = _add_node(AnimationTreePlayer::NODE_ANIMATION); + + RES anim = ResourceLoader::load(files[i]); + anim_tree->animation_node_set_animation(node, anim); + //anim_tree->node_set_name(node, files[i].get_file()); + }; + } break; + + default: + break; + }; +}; + +void AnimationTreePlayerEditor::_add_menu_item(int p_item) { + + if (p_item == MENU_GRAPH_CLEAR) { + + //clear + } else if (p_item == MENU_IMPORT_ANIMATIONS) { + + file_op = MENU_IMPORT_ANIMATIONS; + file_dialog->set_mode(EditorFileDialog::MODE_OPEN_FILE); + file_dialog->popup_centered_ratio(); + + } else { + + _add_node(p_item); + } +} + +Size2 AnimationTreePlayerEditor::get_minimum_size() const { + + return Size2(10, 200); +} + +void AnimationTreePlayerEditor::_find_paths_for_filter(const StringName &p_node, Set<String> &paths) { + + ERR_FAIL_COND(!anim_tree->node_exists(p_node)); + + for (int i = 0; i < anim_tree->node_get_input_count(p_node); i++) { + + StringName port = anim_tree->node_get_input_source(p_node, i); + if (port == StringName()) + continue; + _find_paths_for_filter(port, paths); + } + + if (anim_tree->node_get_type(p_node) == AnimationTreePlayer::NODE_ANIMATION) { + + Ref<Animation> anim = anim_tree->animation_node_get_animation(p_node); + if (anim.is_valid()) { + + for (int i = 0; i < anim->get_track_count(); i++) { + paths.insert(anim->track_get_path(i)); + } + } + } +} + +void AnimationTreePlayerEditor::_filter_edited() { + + TreeItem *ed = filter->get_edited(); + if (!ed) + return; + + if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ONESHOT) { + anim_tree->oneshot_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0)); + } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_BLEND2) { + anim_tree->blend2_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0)); + } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ANIMATION) { + anim_tree->animation_node_set_filter_path(edited_node, ed->get_metadata(0), ed->is_checked(0)); + } +} + +void AnimationTreePlayerEditor::_edit_filters() { + + filter_dialog->popup_centered_ratio(); + filter->clear(); + + Set<String> npb; + _find_paths_for_filter(edited_node, npb); + + TreeItem *root = filter->create_item(); + filter->set_hide_root(true); + Map<String, TreeItem *> pm; + + Node *base = anim_tree->get_node(anim_tree->get_base_path()); + + for (Set<String>::Element *E = npb.front(); E; E = E->next()) { + + TreeItem *parent = root; + String descr = E->get(); + if (base) { + NodePath np = E->get(); + + if (np.get_subname_count() == 1) { + Node *n = base->get_node(np); + Skeleton *s = Object::cast_to<Skeleton>(n); + if (s) { + + String skelbase = E->get().substr(0, E->get().find(":")); + + int bidx = s->find_bone(np.get_subname(0)); + + if (bidx != -1) { + int bparent = s->get_bone_parent(bidx); + // + if (bparent != -1) { + + String bpn = skelbase + ":" + s->get_bone_name(bparent); + if (pm.has(bpn)) { + parent = pm[bpn]; + descr = np.get_subname(0); + } + } else { + + if (pm.has(skelbase)) { + parent = pm[skelbase]; + } + } + } + } + } + } + + TreeItem *it = filter->create_item(parent); + it->set_cell_mode(0, TreeItem::CELL_MODE_CHECK); + it->set_text(0, descr); + it->set_metadata(0, NodePath(E->get())); + it->set_editable(0, true); + if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ONESHOT) { + it->set_checked(0, anim_tree->oneshot_node_is_path_filtered(edited_node, E->get())); + } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_BLEND2) { + it->set_checked(0, anim_tree->blend2_node_is_path_filtered(edited_node, E->get())); + } else if (anim_tree->node_get_type(edited_node) == AnimationTreePlayer::NODE_ANIMATION) { + it->set_checked(0, anim_tree->animation_node_is_path_filtered(edited_node, E->get())); + } + pm[E->get()] = it; + } +} + +void AnimationTreePlayerEditor::_bind_methods() { + + ClassDB::bind_method("_add_menu_item", &AnimationTreePlayerEditor::_add_menu_item); + ClassDB::bind_method("_node_menu_item", &AnimationTreePlayerEditor::_node_menu_item); + ClassDB::bind_method("_gui_input", &AnimationTreePlayerEditor::_gui_input); + //ClassDB::bind_method( "_node_param_changed", &AnimationTreeEditor::_node_param_changed ); + ClassDB::bind_method("_scroll_moved", &AnimationTreePlayerEditor::_scroll_moved); + ClassDB::bind_method("_edit_dialog_changeds", &AnimationTreePlayerEditor::_edit_dialog_changeds); + ClassDB::bind_method("_edit_dialog_changede", &AnimationTreePlayerEditor::_edit_dialog_changede); + ClassDB::bind_method("_edit_dialog_changedf", &AnimationTreePlayerEditor::_edit_dialog_changedf); + ClassDB::bind_method("_edit_dialog_changed", &AnimationTreePlayerEditor::_edit_dialog_changed); + ClassDB::bind_method("_edit_dialog_animation_changed", &AnimationTreePlayerEditor::_edit_dialog_animation_changed); + ClassDB::bind_method("_edit_dialog_edit_animation", &AnimationTreePlayerEditor::_edit_dialog_edit_animation); + ClassDB::bind_method("_play_toggled", &AnimationTreePlayerEditor::_play_toggled); + ClassDB::bind_method("_edit_oneshot_start", &AnimationTreePlayerEditor::_edit_oneshot_start); + ClassDB::bind_method("_file_dialog_selected", &AnimationTreePlayerEditor::_file_dialog_selected); + ClassDB::bind_method("_master_anim_menu_item", &AnimationTreePlayerEditor::_master_anim_menu_item); + ClassDB::bind_method("_edit_filters", &AnimationTreePlayerEditor::_edit_filters); + ClassDB::bind_method("_filter_edited", &AnimationTreePlayerEditor::_filter_edited); +} + +AnimationTreePlayerEditor::AnimationTreePlayerEditor() { + + set_focus_mode(FOCUS_ALL); + + PopupMenu *p; + List<PropertyInfo> defaults; + + add_menu = memnew(MenuButton); + //add_menu->set_ + add_menu->set_position(Point2(0, 0)); + add_menu->set_size(Point2(25, 15)); + add_child(add_menu); + + p = add_menu->get_popup(); + p->add_item(TTR("Animation Node"), AnimationTreePlayer::NODE_ANIMATION); + p->add_item(TTR("OneShot Node"), AnimationTreePlayer::NODE_ONESHOT); + p->add_item(TTR("Mix Node"), AnimationTreePlayer::NODE_MIX); + p->add_item(TTR("Blend2 Node"), AnimationTreePlayer::NODE_BLEND2); + p->add_item(TTR("Blend3 Node"), AnimationTreePlayer::NODE_BLEND3); + p->add_item(TTR("Blend4 Node"), AnimationTreePlayer::NODE_BLEND4); + p->add_item(TTR("TimeScale Node"), AnimationTreePlayer::NODE_TIMESCALE); + p->add_item(TTR("TimeSeek Node"), AnimationTreePlayer::NODE_TIMESEEK); + p->add_item(TTR("Transition Node"), AnimationTreePlayer::NODE_TRANSITION); + p->add_separator(); + p->add_item(TTR("Import Animations..."), MENU_IMPORT_ANIMATIONS); // wtf + p->add_separator(); + p->add_item(TTR("Clear"), MENU_GRAPH_CLEAR); + + p->connect("id_pressed", this, "_add_menu_item"); + + play_button = memnew(Button); + play_button->set_position(Point2(25, 0)); + play_button->set_size(Point2(25, 15)); + add_child(play_button); + play_button->set_toggle_mode(true); + play_button->connect("pressed", this, "_play_toggled"); + + last_x = 50; + last_y = 50; + + property_editor = memnew(CustomPropertyEditor); + add_child(property_editor); + property_editor->connect("variant_changed", this, "_edit_dialog_animation_changed"); + property_editor->connect("resource_edit_request", this, "_edit_dialog_edit_animation"); + + h_scroll = memnew(HScrollBar); + v_scroll = memnew(VScrollBar); + + add_child(h_scroll); + add_child(v_scroll); + + h_scroll->connect("value_changed", this, "_scroll_moved"); + v_scroll->connect("value_changed", this, "_scroll_moved"); + + node_popup = memnew(PopupMenu); + add_child(node_popup); + node_popup->set_as_toplevel(true); + + master_anim_popup = memnew(PopupMenu); + add_child(master_anim_popup); + master_anim_popup->connect("id_pressed", this, "_master_anim_menu_item"); + + node_popup->connect("id_pressed", this, "_node_menu_item"); + + updating_edit = false; + + edit_dialog = memnew(PopupPanel); + //edit_dialog->get_ok()->hide(); + //edit_dialog->get_cancel()->hide(); + add_child(edit_dialog); + + edit_option = memnew(OptionButton); + edit_option->set_anchor(MARGIN_RIGHT, ANCHOR_END); + edit_option->set_margin(MARGIN_RIGHT, -10); + edit_dialog->add_child(edit_option); + edit_option->connect("item_selected", this, "_edit_dialog_changedf"); + edit_option->hide(); + + for (int i = 0; i < 2; i++) { + edit_scroll[i] = memnew(HSlider); + edit_scroll[i]->set_anchor(MARGIN_RIGHT, ANCHOR_END); + edit_scroll[i]->set_margin(MARGIN_RIGHT, -10); + edit_dialog->add_child(edit_scroll[i]); + edit_scroll[i]->hide(); + edit_scroll[i]->connect("value_changed", this, "_edit_dialog_changedf"); + } + for (int i = 0; i < 4; i++) { + edit_line[i] = memnew(LineEdit); + edit_line[i]->set_anchor(MARGIN_RIGHT, ANCHOR_END); + edit_line[i]->set_margin(MARGIN_RIGHT, -10); + edit_dialog->add_child(edit_line[i]); + edit_line[i]->hide(); + edit_line[i]->connect("text_changed", this, "_edit_dialog_changeds"); + edit_line[i]->connect("text_entered", this, "_edit_dialog_changede"); + edit_label[i] = memnew(Label); + edit_dialog->add_child(edit_label[i]); + edit_label[i]->hide(); + } + + edit_button = memnew(Button); + edit_button->set_anchor(MARGIN_RIGHT, ANCHOR_END); + edit_button->set_margin(MARGIN_RIGHT, -10); + edit_dialog->add_child(edit_button); + edit_button->hide(); + edit_button->connect("pressed", this, "_edit_oneshot_start"); + + edit_check = memnew(CheckButton); + edit_check->set_anchor(MARGIN_RIGHT, ANCHOR_END); + edit_check->set_margin(MARGIN_RIGHT, -10); + edit_dialog->add_child(edit_check); + edit_check->hide(); + edit_check->connect("pressed", this, "_edit_dialog_changed"); + + file_dialog = memnew(EditorFileDialog); + file_dialog->set_enable_multiple_selection(true); + file_dialog->set_current_dir(ProjectSettings::get_singleton()->get_resource_path()); + add_child(file_dialog); + file_dialog->connect("file_selected", this, "_file_dialog_selected"); + + filter_dialog = memnew(AcceptDialog); + filter_dialog->set_title(TTR("Edit Node Filters")); + add_child(filter_dialog); + + filter = memnew(Tree); + filter_dialog->add_child(filter); + //filter_dialog->set_child_rect(filter); + filter->connect("item_edited", this, "_filter_edited"); + + filter_button = memnew(Button); + filter_button->set_anchor(MARGIN_RIGHT, ANCHOR_END); + filter_button->set_margin(MARGIN_RIGHT, -10); + edit_dialog->add_child(filter_button); + filter_button->hide(); + filter_button->set_text(TTR("Filters...")); + filter_button->connect("pressed", this, "_edit_filters"); + + set_clip_contents(true); +} + +void AnimationTreePlayerEditorPlugin::edit(Object *p_object) { + + anim_tree_editor->edit(Object::cast_to<AnimationTreePlayer>(p_object)); +} + +bool AnimationTreePlayerEditorPlugin::handles(Object *p_object) const { + + return p_object->is_class("AnimationTreePlayer"); +} + +void AnimationTreePlayerEditorPlugin::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_physics_process(true); + } else { + + if (anim_tree_editor->is_visible_in_tree()) + editor->hide_bottom_panel(); + button->hide(); + anim_tree_editor->set_physics_process(false); + } +} + +AnimationTreePlayerEditorPlugin::AnimationTreePlayerEditorPlugin(EditorNode *p_node) { + + editor = p_node; + anim_tree_editor = memnew(AnimationTreePlayerEditor); + anim_tree_editor->set_custom_minimum_size(Size2(0, 300)); + + button = editor->add_bottom_panel_item(TTR("AnimationTree"), anim_tree_editor); + button->hide(); +} + +AnimationTreePlayerEditorPlugin::~AnimationTreePlayerEditorPlugin() { +} diff --git a/editor/plugins/animation_tree_player_editor_plugin.h b/editor/plugins/animation_tree_player_editor_plugin.h new file mode 100644 index 0000000000..d1c5f395e4 --- /dev/null +++ b/editor/plugins/animation_tree_player_editor_plugin.h @@ -0,0 +1,187 @@ +/*************************************************************************/ +/* animation_tree_editor_plugin.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2018 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2018 Godot Engine contributors (cf. AUTHORS.md) */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef ANIMATION_TREE_PLAYER_EDITOR_PLUGIN_H +#define ANIMATION_TREE_PLAYER_EDITOR_PLUGIN_H + +#include "editor/editor_node.h" +#include "editor/editor_plugin.h" +#include "editor/property_editor.h" +#include "scene/animation/animation_tree_player.h" +#include "scene/gui/button.h" +#include "scene/gui/popup.h" +#include "scene/gui/tree.h" +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ + +class AnimationTreePlayerEditor : public Control { + + GDCLASS(AnimationTreePlayerEditor, Control); + + static const char *_node_type_names[]; + + enum ClickType { + CLICK_NONE, + CLICK_NAME, + CLICK_NODE, + CLICK_INPUT_SLOT, + CLICK_OUTPUT_SLOT, + CLICK_PARAMETER + }; + + enum { + + MENU_GRAPH_CLEAR = 100, + MENU_IMPORT_ANIMATIONS = 101, + NODE_DISCONNECT, + NODE_RENAME, + NODE_ERASE, + NODE_ADD_INPUT, + NODE_DELETE_INPUT, + NODE_SET_AUTOADVANCE, + NODE_CLEAR_AUTOADVANCE + }; + + bool renaming_edit; + StringName edited_node; + bool updating_edit; + Popup *edit_dialog; + HSlider *edit_scroll[2]; + LineEdit *edit_line[4]; + OptionButton *edit_option; + Label *edit_label[4]; + Button *edit_button; + Button *filter_button; + CheckButton *edit_check; + EditorFileDialog *file_dialog; + int file_op; + + void _popup_edit_dialog(); + + void _setup_edit_dialog(const StringName &p_node); + PopupMenu *master_anim_popup; + PopupMenu *node_popup; + PopupMenu *add_popup; + HScrollBar *h_scroll; + VScrollBar *v_scroll; + MenuButton *add_menu; + + CustomPropertyEditor *property_editor; + + AnimationTreePlayer *anim_tree; + List<StringName> order; + Set<StringName> active_nodes; + + int last_x, last_y; + + Point2 offset; + ClickType click_type; + Point2 click_pos; + StringName click_node; + int click_slot; + Point2 click_motion; + ClickType rclick_type; + StringName rclick_node; + int rclick_slot; + + Button *play_button; + + Size2 _get_maximum_size(); + Size2 get_node_size(const StringName &p_node) const; + void _draw_node(const StringName &p_node); + + AcceptDialog *filter_dialog; + Tree *filter; + + void _draw_cos_line(const Vector2 &p_from, const Vector2 &p_to, const Color &p_color); + void _update_scrollbars(); + void _scroll_moved(float); + void _play_toggled(); + /* + void _node_param_changed(); + void _node_add_callback(); + void _node_add(VisualServer::AnimationTreeNodeType p_type); + void _node_edit_property(const StringName& p_node); +*/ + + void _master_anim_menu_item(int p_item); + void _node_menu_item(int p_item); + void _add_menu_item(int p_item); + + void _filter_edited(); + void _find_paths_for_filter(const StringName &p_node, Set<String> &paths); + void _edit_filters(); + + void _edit_oneshot_start(); + void _edit_dialog_animation_changed(); + void _edit_dialog_edit_animation(); + void _edit_dialog_changeds(String); + void _edit_dialog_changede(String); + void _edit_dialog_changedf(float); + void _edit_dialog_changed(); + void _dialog_changed() const; + ClickType _locate_click(const Point2 &p_click, StringName *p_node_id, int *p_slot_index) const; + Point2 _get_slot_pos(const StringName &p_node_id, bool p_input, int p_slot); + + StringName _add_node(int p_item); + void _file_dialog_selected(String p_path); + +protected: + void _notification(int p_what); + void _gui_input(Ref<InputEvent> p_event); + static void _bind_methods(); + +public: + virtual Size2 get_minimum_size() const; + void edit(AnimationTreePlayer *p_anim_tree); + AnimationTreePlayerEditor(); +}; + +class AnimationTreePlayerEditorPlugin : public EditorPlugin { + + GDCLASS(AnimationTreePlayerEditorPlugin, EditorPlugin); + + AnimationTreePlayerEditor *anim_tree_editor; + EditorNode *editor; + Button *button; + +public: + virtual String get_name() const { return "AnimTree"; } + 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); + + AnimationTreePlayerEditorPlugin(EditorNode *p_node); + ~AnimationTreePlayerEditorPlugin(); +}; + +#endif // ANIMATION_TREE_EDITOR_PLUGIN_H diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 145a2ef9bb..72248b62a3 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -2276,14 +2276,14 @@ void CanvasItemEditor::_draw_grid() { real_grid_offset = grid_offset; } - const Color grid_minor_color = get_color("grid_minor_color", "Editor"); + const Color grid_color = EditorSettings::get_singleton()->get("editors/2d/grid_color"); if (grid_step.x != 0) { for (int i = 0; i < s.width; i++) { int cell = Math::fast_ftoi(Math::floor((xform.xform(Vector2(i, 0)).x - real_grid_offset.x) / (grid_step.x * Math::pow(2.0, grid_step_multiplier)))); if (i == 0) last_cell = cell; if (last_cell != cell) - viewport->draw_line(Point2(i, 0), Point2(i, s.height), grid_minor_color); + viewport->draw_line(Point2(i, 0), Point2(i, s.height), grid_color); last_cell = cell; } } @@ -2294,7 +2294,7 @@ void CanvasItemEditor::_draw_grid() { if (i == 0) last_cell = cell; if (last_cell != cell) - viewport->draw_line(Point2(0, i), Point2(s.width, i), grid_minor_color); + viewport->draw_line(Point2(0, i), Point2(s.width, i), grid_color); last_cell = cell; } } diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp index 49c54ad67d..f5bdf77973 100644 --- a/editor/plugins/curve_editor_plugin.cpp +++ b/editor/plugins/curve_editor_plugin.cpp @@ -616,8 +616,8 @@ void CurveEditor::_draw() { Vector2 min_edge = get_world_pos(Vector2(0, view_size.y)); Vector2 max_edge = get_world_pos(Vector2(view_size.x, 0)); - const Color grid_color0 = get_color("grid_major_color", "Editor"); - const Color grid_color1 = get_color("grid_minor_color", "Editor"); + const Color grid_color0 = Color(1.0, 1.0, 1.0, 0.15); + const Color grid_color1 = Color(1.0, 1.0, 1.0, 0.07); draw_line(Vector2(min_edge.x, curve.get_min_value()), Vector2(max_edge.x, curve.get_min_value()), grid_color0); draw_line(Vector2(max_edge.x, curve.get_max_value()), Vector2(min_edge.x, curve.get_max_value()), grid_color0); draw_line(Vector2(0, min_edge.y), Vector2(0, max_edge.y), grid_color0); diff --git a/editor/plugins/spatial_editor_plugin.cpp b/editor/plugins/spatial_editor_plugin.cpp index cd10dca045..7ad117deb6 100644 --- a/editor/plugins/spatial_editor_plugin.cpp +++ b/editor/plugins/spatial_editor_plugin.cpp @@ -4352,10 +4352,13 @@ void SpatialEditor::_menu_item_pressed(int p_option) { bool is_checked = view_menu->get_popup()->is_item_checked(view_menu->get_popup()->get_item_index(p_option)); - is_checked = !is_checked; - VisualServer::get_singleton()->instance_set_visible(origin_instance, is_checked); + origin_enabled = !is_checked; + VisualServer::get_singleton()->instance_set_visible(origin_instance, origin_enabled); + // Update the grid since its appearance depends on whether the origin is enabled + _finish_grid(); + _init_grid(); - view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(p_option), is_checked); + view_menu->get_popup()->set_item_checked(view_menu->get_popup()->get_item_index(p_option), origin_enabled); } break; case MENU_VIEW_GRID: { @@ -4778,7 +4781,11 @@ void SpatialEditor::_init_grid() { Vector3 p2_dest = p2 * (-axis_n1 + axis_n2); Color line_color = secondary_grid_color; - if (j % primary_grid_steps == 0) { + if (origin_enabled && j == 0) { + // Don't draw the center lines of the grid if the origin is enabled + // The origin would overlap the grid lines in this case, causing flickering + continue; + } else if (j % primary_grid_steps == 0) { line_color = primary_grid_color; } diff --git a/editor/plugins/spatial_editor_plugin.h b/editor/plugins/spatial_editor_plugin.h index 4057145c2f..5850c0dbf1 100644 --- a/editor/plugins/spatial_editor_plugin.h +++ b/editor/plugins/spatial_editor_plugin.h @@ -508,6 +508,7 @@ private: RID origin; RID origin_instance; + bool origin_enabled; RID grid[3]; RID grid_instance[3]; bool grid_visible[3]; //currently visible diff --git a/editor/plugins/texture_region_editor_plugin.cpp b/editor/plugins/texture_region_editor_plugin.cpp index d13e01dc1e..4a9cbfe535 100644 --- a/editor/plugins/texture_region_editor_plugin.cpp +++ b/editor/plugins/texture_region_editor_plugin.cpp @@ -70,7 +70,7 @@ void TextureRegionEditor::_region_draw() { VS::get_singleton()->canvas_item_add_set_transform(edit_draw->get_canvas_item(), Transform2D()); if (snap_mode == SNAP_GRID) { - Color grid_color = get_color("grid_major_color", "Editor"); + Color grid_color = Color(1.0, 1.0, 1.0, 0.15); Size2 s = edit_draw->get_size(); int last_cell = 0; diff --git a/editor/project_export.cpp b/editor/project_export.cpp index 3cebbb5d21..019d5d382c 100644 --- a/editor/project_export.cpp +++ b/editor/project_export.cpp @@ -29,6 +29,7 @@ /*************************************************************************/ #include "project_export.h" + #include "compressed_translation.h" #include "editor_data.h" #include "editor_node.h" @@ -389,7 +390,6 @@ void ProjectExportDialog::_patch_deleted() { void ProjectExportDialog::_update_parameters(const String &p_edited_property) { _edit_preset(presets->get_current()); - parameters->update_tree(); } void ProjectExportDialog::_runnable_pressed() { @@ -842,12 +842,10 @@ ProjectExportDialog::ProjectExportDialog() { settings_vb->add_child(sections); sections->set_v_size_flags(SIZE_EXPAND_FILL); - parameters = memnew(PropertyEditor); + parameters = memnew(EditorInspector); sections->add_child(parameters); parameters->set_name(TTR("Options")); - parameters->hide_top_label(); parameters->set_v_size_flags(SIZE_EXPAND_FILL); - parameters->connect("property_edited", this, "_update_parameters"); VBoxContainer *resources_vb = memnew(VBoxContainer); diff --git a/editor/project_export.h b/editor/project_export.h index b62254974d..1f8723febd 100644 --- a/editor/project_export.h +++ b/editor/project_export.h @@ -31,26 +31,27 @@ #ifndef PROJECT_EXPORT_SETTINGS_H #define PROJECT_EXPORT_SETTINGS_H +#include "core/os/dir_access.h" +#include "core/os/thread.h" +#include "editor/editor_export.h" #include "editor/editor_file_dialog.h" -#include "os/dir_access.h" -#include "os/thread.h" +#include "editor/editor_file_system.h" +#include "editor/editor_inspector.h" #include "scene/gui/button.h" +#include "scene/gui/check_button.h" #include "scene/gui/control.h" #include "scene/gui/dialogs.h" #include "scene/gui/file_dialog.h" #include "scene/gui/label.h" #include "scene/gui/link_button.h" +#include "scene/gui/menu_button.h" #include "scene/gui/option_button.h" #include "scene/gui/rich_text_label.h" +#include "scene/gui/slider.h" #include "scene/gui/tab_container.h" #include "scene/gui/tree.h" #include "scene/main/timer.h" -#include "editor/editor_file_system.h" -#include "editor_export.h" -#include "property_editor.h" -#include "scene/gui/slider.h" - class EditorNode; class ProjectExportDialog : public ConfirmationDialog { @@ -64,12 +65,9 @@ private: ItemList *presets; LineEdit *name; - PropertyEditor *parameters; + EditorInspector *parameters; CheckButton *runnable; - //EditorFileDialog *pck_export; - //EditorFileDialog *file_export; - Button *button_export; bool updating; diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp index 0857635492..032654c3f7 100644 --- a/modules/bullet/bullet_physics_server.cpp +++ b/modules/bullet/bullet_physics_server.cpp @@ -869,6 +869,14 @@ bool BulletPhysicsServer::body_test_motion(RID p_body, const Transform &p_from, return body->get_space()->test_body_motion(body, p_from, p_motion, p_infinite_inertia, r_result); } +int BulletPhysicsServer::body_test_ray_separation(RID p_body, const Transform &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, SeparationResult *r_results, int p_result_max, float p_margin) { + RigidBodyBullet *body = rigid_body_owner.get(p_body); + ERR_FAIL_COND_V(!body, 0); + ERR_FAIL_COND_V(!body->get_space(), 0); + + return body->get_space()->test_ray_separation(body, p_transform, p_infinite_inertia, r_recover_motion, r_results, p_result_max, p_margin); +} + RID BulletPhysicsServer::soft_body_create(bool p_init_sleeping) { SoftBodyBullet *body = bulletnew(SoftBodyBullet); body->set_collision_layer(1); diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h index 0e858ff311..2ff83bfd7d 100644 --- a/modules/bullet/bullet_physics_server.h +++ b/modules/bullet/bullet_physics_server.h @@ -259,6 +259,7 @@ public: virtual PhysicsDirectBodyState *body_get_direct_state(RID p_body); virtual bool body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result = NULL); + virtual int body_test_ray_separation(RID p_body, const Transform &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, SeparationResult *r_results, int p_result_max, float p_margin = 0.001); /* SOFT BODY API */ diff --git a/modules/bullet/godot_result_callbacks.cpp b/modules/bullet/godot_result_callbacks.cpp index 197550d686..85178bf83c 100644 --- a/modules/bullet/godot_result_callbacks.cpp +++ b/modules/bullet/godot_result_callbacks.cpp @@ -51,8 +51,8 @@ bool GodotClosestRayResultCallback::needsCollision(btBroadphaseProxy *proxy0) co if (needs) { btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject); CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer()); - if (m_pickRay && gObj->is_ray_pickable()) { - return true; + if (m_pickRay && !gObj->is_ray_pickable()) { + return false; } else if (m_exclude->has(gObj->get_self())) { return false; } @@ -260,10 +260,19 @@ void GodotDeepPenetrationContactResultCallback::addContactPoint(const btVector3 if (m_penetration_distance > depth) { // Has penetration? - bool isSwapped = m_manifoldPtr->getBody0() != m_body0Wrap->getCollisionObject(); + const bool isSwapped = m_manifoldPtr->getBody0() != m_body0Wrap->getCollisionObject(); m_penetration_distance = depth; m_other_compound_shape_index = isSwapped ? m_index0 : m_index1; - m_pointNormalWorld = isSwapped ? normalOnBInWorld * -1 : normalOnBInWorld; m_pointWorld = isSwapped ? (pointInWorldOnB + (normalOnBInWorld * depth)) : pointInWorldOnB; + + const btCollisionObjectWrapper *bw0 = m_body0Wrap; + if (isSwapped) + bw0 = m_body1Wrap; + + if (bw0->getCollisionShape()->getShapeType() == CUSTOM_CONVEX_SHAPE_TYPE) { + m_pointNormalWorld = bw0->m_worldTransform.getBasis().transpose() * btVector3(0, 0, 1); + } else { + m_pointNormalWorld = isSwapped ? normalOnBInWorld * -1 : normalOnBInWorld; + } } } diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp index 97228a972f..c0fea910c2 100644 --- a/modules/bullet/space_bullet.cpp +++ b/modules/bullet/space_bullet.cpp @@ -894,6 +894,12 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f // Skip no convex shape continue; } + + if (p_body->get_bt_shape(shIndex)->getShapeType() == CUSTOM_CONVEX_SHAPE_TYPE) { + // Skip rayshape in order to implement custom separation process + continue; + } + btConvexShape *convex_shape_test(static_cast<btConvexShape *>(p_body->get_bt_shape(shIndex))); btTransform shape_world_from = body_transform * p_body->get_kinematic_utilities()->shapes[shIndex].transform; @@ -924,11 +930,11 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f btVector3 __rec(0, 0, 0); RecoverResult r_recover_result; - has_penetration = recover_from_penetration(p_body, body_transform, 0, p_infinite_inertia, __rec, &r_recover_result); + has_penetration = recover_from_penetration(p_body, body_transform, 1, p_infinite_inertia, __rec, &r_recover_result); // Parse results if (r_result) { - B_TO_G(motion + initial_recover_motion, r_result->motion); + B_TO_G(motion + initial_recover_motion + __rec, r_result->motion); if (has_penetration) { @@ -964,6 +970,39 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f return has_penetration; } +int SpaceBullet::test_ray_separation(RigidBodyBullet *p_body, const Transform &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, PhysicsServer::SeparationResult *r_results, int p_result_max, float p_margin) { + + btTransform body_transform; + G_TO_B(p_transform, body_transform); + UNSCALE_BT_BASIS(body_transform); + + btVector3 recover_motion(0, 0, 0); + + int rays_found; + + for (int t(RECOVERING_MOVEMENT_CYCLES); 0 < t; --t) { + int last_ray_index = recover_from_penetration_ray(p_body, body_transform, RECOVERING_MOVEMENT_SCALE, p_infinite_inertia, p_result_max, recover_motion, r_results); + + rays_found = MAX(last_ray_index, rays_found); + if (!rays_found) { + break; + } else { + body_transform.getOrigin() += recover_motion; + } + } + + //optimize results (remove non colliding) + for (int i = 0; i < rays_found; i++) { + if (r_results[i].collision_depth >= 0) { + rays_found--; + SWAP(r_results[i], r_results[rays_found]); + } + } + + B_TO_G(recover_motion, r_recover_motion); + return rays_found; +} + struct RecoverPenetrationBroadPhaseCallback : public btBroadphaseAabbCallback { private: const btCollisionObject *self_collision_object; @@ -1020,6 +1059,11 @@ bool SpaceBullet::recover_from_penetration(RigidBodyBullet *p_body, const btTran continue; } + if (kin_shape.shape->getShapeType() == CUSTOM_CONVEX_SHAPE_TYPE) { + // Skip rayshape in order to implement custom separation process + continue; + } + body_shape_position = p_body_position * kin_shape.transform; body_shape_position_recovered = body_shape_position; body_shape_position_recovered.getOrigin() += r_delta_recover_movement; @@ -1122,7 +1166,6 @@ bool SpaceBullet::RFP_convex_world_test(const btConvexShape *p_shapeA, const btC if (contactPointResult.hasHit()) { r_delta_recover_movement += contactPointResult.m_pointNormalWorld * (contactPointResult.m_penetration_distance * -1 * p_recover_movement_scale); - if (r_recover_result) { if (contactPointResult.m_penetration_distance < r_recover_result->penetration_distance) { r_recover_result->hasPenetration = true; @@ -1138,3 +1181,79 @@ bool SpaceBullet::RFP_convex_world_test(const btConvexShape *p_shapeA, const btC } return false; } + +int SpaceBullet::recover_from_penetration_ray(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, int p_result_max, btVector3 &r_delta_recover_movement, PhysicsServer::SeparationResult *r_results) { + + RecoverPenetrationBroadPhaseCallback recover_broad_result(p_body->get_bt_collision_object(), p_body->get_collision_layer(), p_body->get_collision_mask()); + + btTransform body_shape_position; + btTransform body_shape_position_recovered; + + // Broad phase support + btVector3 minAabb, maxAabb; + + int ray_index = 0; + + // For each shape + for (int kinIndex = p_body->get_kinematic_utilities()->shapes.size() - 1; 0 <= kinIndex; --kinIndex) { + + recover_broad_result.reset(); + + if (ray_index >= p_result_max) { + break; + } + + const RigidBodyBullet::KinematicShape &kin_shape(p_body->get_kinematic_utilities()->shapes[kinIndex]); + if (!kin_shape.is_active()) { + continue; + } + + if (kin_shape.shape->getShapeType() != CUSTOM_CONVEX_SHAPE_TYPE) { + continue; + } + + body_shape_position = p_body_position * kin_shape.transform; + body_shape_position_recovered = body_shape_position; + body_shape_position_recovered.getOrigin() += r_delta_recover_movement; + + kin_shape.shape->getAabb(body_shape_position_recovered, minAabb, maxAabb); + dynamicsWorld->getBroadphase()->aabbTest(minAabb, maxAabb, recover_broad_result); + + for (int i = recover_broad_result.result_collision_objects.size() - 1; 0 <= i; --i) { + btCollisionObject *otherObject = recover_broad_result.result_collision_objects[i]; + if (p_infinite_inertia && !otherObject->isStaticOrKinematicObject()) { + otherObject->activate(); // Force activation of hitten rigid, soft body + continue; + } else if (!p_body->get_bt_collision_object()->checkCollideWith(otherObject) || !otherObject->checkCollideWith(p_body->get_bt_collision_object())) + continue; + + if (otherObject->getCollisionShape()->isCompound()) { + + // Each convex shape + btCompoundShape *cs = static_cast<btCompoundShape *>(otherObject->getCollisionShape()); + for (int x = cs->getNumChildShapes() - 1; 0 <= x; --x) { + + RecoverResult r_recover_result; + if (RFP_convex_world_test(kin_shape.shape, cs->getChildShape(x), p_body->get_bt_collision_object(), otherObject, kinIndex, x, body_shape_position, otherObject->getWorldTransform() * cs->getChildTransform(x), p_recover_movement_scale, r_delta_recover_movement, &r_recover_result)) { + + const btRigidBody *btRigid = static_cast<const btRigidBody *>(otherObject); + CollisionObjectBullet *collisionObject = static_cast<CollisionObjectBullet *>(otherObject->getUserPointer()); + + r_results[ray_index].collision_depth = r_recover_result.penetration_distance; + B_TO_G(r_recover_result.pointWorld, r_results[ray_index].collision_point); + B_TO_G(r_recover_result.normal, r_results[ray_index].collision_normal); + B_TO_G(btRigid->getVelocityInLocalPoint(r_recover_result.pointWorld - btRigid->getWorldTransform().getOrigin()), r_results[ray_index].collider_velocity); + r_results[ray_index].collision_local_shape = kinIndex; + r_results[ray_index].collider_id = collisionObject->get_instance_id(); + r_results[ray_index].collider = collisionObject->get_self(); + r_results[ray_index].collider_shape = r_recover_result.other_compound_shape_index; + } + } + } + } + + ++ray_index; + } + + return ray_index; +} diff --git a/modules/bullet/space_bullet.h b/modules/bullet/space_bullet.h index 6b86fc2f03..06f1c3f250 100644 --- a/modules/bullet/space_bullet.h +++ b/modules/bullet/space_bullet.h @@ -175,6 +175,7 @@ public: void update_gravity(); bool test_body_motion(RigidBodyBullet *p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer::MotionResult *r_result); + int test_ray_separation(RigidBodyBullet *p_body, const Transform &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, PhysicsServer::SeparationResult *r_results, int p_result_max, float p_margin); private: void create_empty_world(bool p_create_soft_world); @@ -208,5 +209,7 @@ private: /// This is an API that recover a kinematic object from penetration /// Using this we leave Bullet to select the best algorithm, For example GJK in case we have Convex Convex, or a Bullet accelerated algorithm bool RFP_convex_world_test(const btConvexShape *p_shapeA, const btCollisionShape *p_shapeB, btCollisionObject *p_objectA, btCollisionObject *p_objectB, int p_shapeId_A, int p_shapeId_B, const btTransform &p_transformA, const btTransform &p_transformB, btScalar p_recover_movement_scale, btVector3 &r_delta_recover_movement, RecoverResult *r_recover_result = NULL); + + int recover_from_penetration_ray(RigidBodyBullet *p_body, const btTransform &p_body_position, btScalar p_recover_movement_scale, bool p_infinite_inertia, int p_result_max, btVector3 &r_delta_recover_movement, PhysicsServer::SeparationResult *r_results); }; #endif diff --git a/modules/gdnative/include/nativescript/godot_nativescript.h b/modules/gdnative/include/nativescript/godot_nativescript.h index f28ba352ab..1c5422d723 100644 --- a/modules/gdnative/include/nativescript/godot_nativescript.h +++ b/modules/gdnative/include/nativescript/godot_nativescript.h @@ -68,6 +68,7 @@ typedef enum { GODOT_PROPERTY_HINT_GLOBAL_DIR, ///< a directort path must be passed GODOT_PROPERTY_HINT_RESOURCE_TYPE, ///< a resource object type GODOT_PROPERTY_HINT_MULTILINE_TEXT, ///< used for string properties that can contain multiple lines + GODOT_PROPERTY_HINT_PLACEHOLDER_TEXT, ///< used to set a placeholder text for string properties GODOT_PROPERTY_HINT_COLOR_NO_ALPHA, ///< used for ignoring alpha component when editing a color GODOT_PROPERTY_HINT_IMAGE_COMPRESS_LOSSY, GODOT_PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS, diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index abd56d2757..934c93059a 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -46,12 +46,12 @@ void GDScriptLanguage::get_comment_delimiters(List<String> *p_delimiters) const { p_delimiters->push_back("#"); - p_delimiters->push_back("\"\"\" \"\"\""); } void GDScriptLanguage::get_string_delimiters(List<String> *p_delimiters) const { p_delimiters->push_back("\" \""); p_delimiters->push_back("' '"); + p_delimiters->push_back("\"\"\" \"\"\""); } Ref<Script> GDScriptLanguage::get_template(const String &p_class_name, const String &p_base_class_name) const { #ifdef TOOLS_ENABLED diff --git a/modules/mono/config.py b/modules/mono/config.py index c4f8dcfde8..70fd1a35f1 100644 --- a/modules/mono/config.py +++ b/modules/mono/config.py @@ -265,11 +265,13 @@ def pkgconfig_try_find_mono_root(mono_lib_names, sharedlib_ext): def pkgconfig_try_find_mono_version(): + from compat import decode_utf8 + lines = subprocess.check_output(['pkg-config', 'monosgen-2', '--modversion']).splitlines() greater_version = None for line in lines: try: - version = LooseVersion(line) + version = LooseVersion(decode_utf8(line)) if greater_version is None or version > greater_version: greater_version = version except ValueError: diff --git a/platform/android/detect.py b/platform/android/detect.py index ada36e2814..0c6c9fdca3 100644 --- a/platform/android/detect.py +++ b/platform/android/detect.py @@ -128,7 +128,7 @@ def configure(env): env.extra_suffix = ".armv7" + env.extra_suffix elif env["android_arch"] == "arm64v8": if get_platform(env["ndk_platform"]) < 21: - print("WARNING: android_arch=arm64v8 is not supported by ndk_platform lower than andorid-21; setting ndk_platform=android-21") + print("WARNING: android_arch=arm64v8 is not supported by ndk_platform lower than android-21; setting ndk_platform=android-21") env["ndk_platform"] = "android-21" env['ARCH'] = 'arch-arm64' target_subpath = "aarch64-linux-android-4.9" diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index 9ad0219746..fb19c5d33f 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -1086,7 +1086,6 @@ public: virtual void get_export_options(List<ExportOption> *r_options) { - /*r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "graphics/api", PROPERTY_HINT_ENUM, "OpenGL ES 2.0,OpenGL ES 3.0"), 1));*/ r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "graphics/32_bits_framebuffer"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "one_click_deploy/clear_previous_install"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_package/debug", PROPERTY_HINT_GLOBAL_FILE, "apk"), "")); @@ -1094,8 +1093,8 @@ public: r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name"), "1.0")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name"), "org.godotengine.$genname")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.$genname"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "package/signed"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "screen/immersive_mode"), true)); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "screen/orientation", PROPERTY_HINT_ENUM, "Landscape,Portrait"), 0)); @@ -1133,8 +1132,6 @@ public: r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "user_permissions/" + itos(i)), false)); } - - //r_options->push_back( PropertyInfo( Variant::INT, "resources/pack_mode", PROPERTY_HINT_ENUM,"Copy,Single Exec.,Pack (.pck),Bundles (Optical)")); } virtual String get_name() const { diff --git a/platform/iphone/export/export.cpp b/platform/iphone/export/export.cpp index e59c81a148..9e4e648eb6 100644 --- a/platform/iphone/export/export.cpp +++ b/platform/iphone/export/export.cpp @@ -175,17 +175,17 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/app_store_team_id"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_debug"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_debug"), "iPhone Developer")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_debug", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Developer"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_debug", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 1)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/provisioning_profile_uuid_release"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_release"), "iPhone Distribution")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/code_sign_identity_release", PROPERTY_HINT_PLACEHOLDER_TEXT, "iPhone Distribution"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/export_method_release", PROPERTY_HINT_ENUM, "App Store,Development,Ad-Hoc,Enterprise"), 0)); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/info"), "Made with Godot Engine")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/identifier"), "org.godotengine.iosgame")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), "????")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "come.example.game"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), "")); diff --git a/platform/osx/export/export.cpp b/platform/osx/export/export.cpp index b630e4f223..03b8dd6420 100644 --- a/platform/osx/export/export.cpp +++ b/platform/osx/export/export.cpp @@ -109,11 +109,11 @@ void EditorExportPlatformOSX::get_export_options(List<ExportOption> *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_package/debug", PROPERTY_HINT_GLOBAL_FILE, "zip"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "custom_package/release", PROPERTY_HINT_GLOBAL_FILE, "zip"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/info"), "Made with Godot Engine")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_FILE, "png"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/identifier"), "org.godotengine.macgame")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), "godotmacgame")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/identifier", PROPERTY_HINT_PLACEHOLDER_TEXT, "com.example.game"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/signature"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version"), "1.0")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version"), "1.0")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), "")); diff --git a/platform/uwp/export/export.cpp b/platform/uwp/export/export.cpp index ebc2c2d7a2..c253bf587b 100644 --- a/platform/uwp/export/export.cpp +++ b/platform/uwp/export/export.cpp @@ -1050,15 +1050,15 @@ public: r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "command_line/extra_args"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/display_name"), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/short_name"), "Godot")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name"), "Godot.Engine")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/description"), "Godot Engine")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/publisher"), "CN=GodotEngine")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/publisher_display_name"), "Godot Engine")); - - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "identity/product_guid"), "00000000-0000-0000-0000-000000000000")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "identity/publisher_guid"), "00000000-0000-0000-0000-000000000000")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/display_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/short_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/unique_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game.Name"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/description"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/publisher", PROPERTY_HINT_PLACEHOLDER_TEXT, "CN=CompanyName"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "package/publisher_display_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Company Name"), "")); + + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "identity/product_guid", PROPERTY_HINT_PLACEHOLDER_TEXT, "00000000-0000-0000-0000-000000000000"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "identity/publisher_guid", PROPERTY_HINT_PLACEHOLDER_TEXT, "00000000-0000-0000-0000-000000000000"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "signing/certificate", PROPERTY_HINT_GLOBAL_FILE, "*.pfx"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "signing/password"), "")); diff --git a/platform/windows/detect.py b/platform/windows/detect.py index 34fc3e09b5..7667de160d 100644 --- a/platform/windows/detect.py +++ b/platform/windows/detect.py @@ -172,6 +172,7 @@ def configure_msvc(env, manual_msvc_config): env.Append(CCFLAGS=['/O1']) env.Append(LINKFLAGS=['/SUBSYSTEM:WINDOWS']) env.Append(LINKFLAGS=['/ENTRY:mainCRTStartup']) + env.Append(LINKFLAGS=['/OPT:REF']) elif (env["target"] == "release_debug"): if (env["optimize"] == "speed"): #optimize for speed (default) @@ -180,6 +181,7 @@ def configure_msvc(env, manual_msvc_config): env.Append(CCFLAGS=['/O1']) env.AppendUnique(CPPDEFINES = ['DEBUG_ENABLED']) env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE']) + env.Append(LINKFLAGS=['/OPT:REF']) elif (env["target"] == "debug_release"): env.Append(CCFLAGS=['/Z7', '/Od']) @@ -194,6 +196,10 @@ def configure_msvc(env, manual_msvc_config): env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE']) env.Append(LINKFLAGS=['/DEBUG']) + if (env["debug_symbols"] == "full" or env["debug_symbols"] == "yes"): + env.AppendUnique(CCFLAGS=['/Z7']) + env.AppendUnique(LINKFLAGS=['/DEBUG']) + ## Compile/link flags env.AppendUnique(CCFLAGS=['/MT', '/Gd', '/GR', '/nologo']) diff --git a/platform/windows/export/export.cpp b/platform/windows/export/export.cpp index 97544c18f5..38fd6366c7 100644 --- a/platform/windows/export/export.cpp +++ b/platform/windows/export/export.cpp @@ -137,14 +137,14 @@ Error EditorExportPlatformWindows::export_project(const Ref<EditorExportPreset> void EditorExportPlatformWindows::get_export_options(List<ExportOption> *r_options) { EditorExportPlatformPC::get_export_options(r_options); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_GLOBAL_FILE, "*.ico"), String())); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version"), String())); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version"), String())); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/company_name"), String())); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_name"), String())); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_description"), String())); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), String())); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/trademarks"), String())); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/icon", PROPERTY_HINT_GLOBAL_FILE, "*.ico"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_version"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_version"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/company_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Company Name"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/product_name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Game Name"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/file_description"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/copyright"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/trademarks"), "")); } void register_windows_exporter() { diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp index d6cfd039d9..d5bb85c035 100644 --- a/platform/windows/os_windows.cpp +++ b/platform/windows/os_windows.cpp @@ -2790,9 +2790,13 @@ bool OS_Windows::is_disable_crash_handler() const { Error OS_Windows::move_to_trash(const String &p_path) { SHFILEOPSTRUCTW sf; + WCHAR *from = new WCHAR[p_path.length() + 2]; + wcscpy(from, p_path.c_str()); + from[p_path.length() + 1] = 0; + sf.hwnd = hWnd; sf.wFunc = FO_DELETE; - sf.pFrom = p_path.c_str(); + sf.pFrom = from; sf.pTo = NULL; sf.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION; sf.fAnyOperationsAborted = FALSE; @@ -2800,6 +2804,7 @@ Error OS_Windows::move_to_trash(const String &p_path) { sf.lpszProgressTitle = NULL; int ret = SHFileOperationW(&sf); + delete[] from; if (ret) { ERR_PRINTS("SHFileOperation error: " + itos(ret)); diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index 8758ffef9f..02213e07d0 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -1207,7 +1207,7 @@ bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_ //so, if you pass 45 as limit, avoid numerical precision erros when angle is 45. #define FLOOR_ANGLE_THRESHOLD 0.01 -Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction, bool p_infinite_inertia, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle) { +Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction, bool p_infinite_inertia, bool p_stop_on_slope, int p_max_slides, float p_floor_max_angle) { Vector2 floor_motion = floor_velocity; if (on_floor && on_floor_body.is_valid()) { @@ -1228,6 +1228,8 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const colliders.clear(); floor_velocity = Vector2(); + Vector2 lv_n = p_linear_velocity.normalized(); + while (p_max_slides) { Collision collision; @@ -1254,8 +1256,10 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const if (collided) { + colliders.push_back(collision); motion = collision.remainder; + bool is_on_slope = false; if (p_floor_direction == Vector2()) { //all is a wall on_wall = true; @@ -1266,15 +1270,17 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const on_floor_body = collision.collider_rid; floor_velocity = collision.collider_vel; - Vector2 rel_v = lv - floor_velocity; - Vector2 hv = rel_v - p_floor_direction * p_floor_direction.dot(rel_v); - - if (collision.travel.length() < 1 && hv.length() < p_slope_stop_min_velocity) { - Transform2D gt = get_global_transform(); - gt.elements[2] -= collision.travel; - set_global_transform(gt); - return Vector2(); + if (p_stop_on_slope) { + if (Vector2() == lv_n + p_floor_direction) { + Transform2D gt = get_global_transform(); + gt.elements[2] -= collision.travel; + set_global_transform(gt); + return Vector2(); + } } + + is_on_slope = true; + } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { //ceiling on_ceiling = true; } else { @@ -1282,12 +1288,18 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const } } - Vector2 n = collision.normal; - motion = motion.slide(n); - lv = lv.slide(n); - - colliders.push_back(collision); + if (p_stop_on_slope && is_on_slope) { + motion = motion.slide(p_floor_direction); + lv = lv.slide(p_floor_direction); + } else { + Vector2 n = collision.normal; + motion = motion.slide(n); + lv = lv.slide(n); + } } + + if (p_stop_on_slope) + break; } if (!found_collision) { @@ -1301,11 +1313,11 @@ Vector2 KinematicBody2D::move_and_slide(const Vector2 &p_linear_velocity, const return lv; } -Vector2 KinematicBody2D::move_and_slide_with_snap(const Vector2 &p_linear_velocity, const Vector2 &p_snap, const Vector2 &p_floor_direction, bool p_infinite_inertia, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle) { +Vector2 KinematicBody2D::move_and_slide_with_snap(const Vector2 &p_linear_velocity, const Vector2 &p_snap, const Vector2 &p_floor_direction, bool p_infinite_inertia, bool p_stop_on_slope, int p_max_slides, float p_floor_max_angle) { bool was_on_floor = on_floor; - Vector2 ret = move_and_slide(p_linear_velocity, p_floor_direction, p_infinite_inertia, p_slope_stop_min_velocity, p_max_slides, p_floor_max_angle); + Vector2 ret = move_and_slide(p_linear_velocity, p_floor_direction, p_infinite_inertia, p_stop_on_slope, p_max_slides, p_floor_max_angle); if (!was_on_floor || p_snap == Vector2()) { return ret; } @@ -1439,8 +1451,8 @@ void KinematicBody2D::_notification(int p_what) { void KinematicBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia", "exclude_raycast_shapes", "test_only"), &KinematicBody2D::_move, DEFVAL(true), DEFVAL(true), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "infinite_inertia", "slope_stop_min_velocity", "max_bounces", "floor_max_angle"), &KinematicBody2D::move_and_slide, DEFVAL(Vector2(0, 0)), DEFVAL(true), DEFVAL(5), DEFVAL(4), DEFVAL(Math::deg2rad((float)45))); - ClassDB::bind_method(D_METHOD("move_and_slide_with_snap", "linear_velocity", "snap", "floor_normal", "infinite_inertia", "slope_stop_min_velocity", "max_bounces", "floor_max_angle"), &KinematicBody2D::move_and_slide_with_snap, DEFVAL(Vector2(0, 0)), DEFVAL(true), DEFVAL(5), DEFVAL(4), DEFVAL(Math::deg2rad((float)45))); + ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "infinite_inertia", "stop_on_slope", "max_bounces", "floor_max_angle"), &KinematicBody2D::move_and_slide, DEFVAL(Vector2(0, 0)), DEFVAL(true), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((float)45))); + ClassDB::bind_method(D_METHOD("move_and_slide_with_snap", "linear_velocity", "snap", "floor_normal", "infinite_inertia", "stop_on_slope", "max_bounces", "floor_max_angle"), &KinematicBody2D::move_and_slide_with_snap, DEFVAL(Vector2(0, 0)), DEFVAL(true), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((float)45))); ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "infinite_inertia"), &KinematicBody2D::test_move); diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index 0a2ce0918b..852963a721 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -338,8 +338,8 @@ public: void set_safe_margin(float p_margin); float get_safe_margin() const; - Vector2 move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction = Vector2(0, 0), bool p_infinite_inertia = true, float p_slope_stop_min_velocity = 5, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45)); - Vector2 move_and_slide_with_snap(const Vector2 &p_linear_velocity, const Vector2 &p_snap, const Vector2 &p_floor_direction = Vector2(0, 0), bool p_infinite_inertia = true, float p_slope_stop_min_velocity = 5, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45)); + Vector2 move_and_slide(const Vector2 &p_linear_velocity, const Vector2 &p_floor_direction = Vector2(0, 0), bool p_infinite_inertia = true, bool p_stop_on_slope = false, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45)); + Vector2 move_and_slide_with_snap(const Vector2 &p_linear_velocity, const Vector2 &p_snap, const Vector2 &p_floor_direction = Vector2(0, 0), bool p_infinite_inertia = true, bool p_stop_on_slope = false, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45)); bool is_on_floor() const; bool is_on_wall() const; bool is_on_ceiling() const; diff --git a/scene/3d/physics_body.cpp b/scene/3d/physics_body.cpp index e53ccb4cf4..2df6ef7c8a 100644 --- a/scene/3d/physics_body.cpp +++ b/scene/3d/physics_body.cpp @@ -1078,10 +1078,10 @@ void RigidBody::_reload_physics_characteristics() { ////////////////////////////////////////////////////// ////////////////////////// -Ref<KinematicCollision> KinematicBody::_move(const Vector3 &p_motion, bool p_infinite_inertia) { +Ref<KinematicCollision> KinematicBody::_move(const Vector3 &p_motion, bool p_infinite_inertia, bool p_test_only) { Collision col; - if (move_and_collide(p_motion, p_infinite_inertia, col)) { + if (move_and_collide(p_motion, p_infinite_inertia, col, p_test_only)) { if (motion_cache.is_null()) { motion_cache.instance(); motion_cache->owner = this; @@ -1095,7 +1095,7 @@ Ref<KinematicCollision> KinematicBody::_move(const Vector3 &p_motion, bool p_inf return Ref<KinematicCollision>(); } -bool KinematicBody::move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collision) { +bool KinematicBody::move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_test_only) { Transform gt = get_global_transform(); PhysicsServer::MotionResult result; @@ -1108,6 +1108,7 @@ bool KinematicBody::move_and_collide(const Vector3 &p_motion, bool p_infinite_in r_collision.collision = result.collision_point; r_collision.normal = result.collision_normal; r_collision.collider = result.collider_id; + r_collision.collider_rid = result.collider; r_collision.travel = result.motion; r_collision.remainder = result.remainder; r_collision.local_shape = result.collision_local_shape; @@ -1119,8 +1120,10 @@ bool KinematicBody::move_and_collide(const Vector3 &p_motion, bool p_infinite_in } } - gt.origin += result.motion; - set_global_transform(gt); + if (!p_test_only) { + gt.origin += result.motion; + set_global_transform(gt); + } return colliding; } @@ -1128,7 +1131,7 @@ bool KinematicBody::move_and_collide(const Vector3 &p_motion, bool p_infinite_in //so, if you pass 45 as limit, avoid numerical precision erros when angle is 45. #define FLOOR_ANGLE_THRESHOLD 0.01 -Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction, float p_slope_stop_min_velocity, int p_max_slides, float p_floor_max_angle, bool p_infinite_inertia) { +Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction, bool p_stop_on_slope, int p_max_slides, float p_floor_max_angle, bool p_infinite_inertia) { Vector3 lv = p_linear_velocity; @@ -1146,69 +1149,127 @@ Vector3 KinematicBody::move_and_slide(const Vector3 &p_linear_velocity, const Ve colliders.clear(); floor_velocity = Vector3(); + Vector3 lv_n = p_linear_velocity.normalized(); + while (p_max_slides) { Collision collision; - bool collided = move_and_collide(motion, p_infinite_inertia, collision); - - if (collided) { + bool found_collision = false; - motion = collision.remainder; + int test_type = 0; - if (p_floor_direction == Vector3()) { - //all is a wall - on_wall = true; + do { + bool collided; + if (test_type == 0) { //collide + collided = move_and_collide(motion, p_infinite_inertia, collision); + if (!collided) { + motion = Vector3(); //clear because no collision happened and motion completed + } } else { - if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { //floor + collided = separate_raycast_shapes(p_infinite_inertia, collision); + if (collided) { + collision.remainder = motion; //keep + collision.travel = Vector3(); + } + } - on_floor = true; - floor_velocity = collision.collider_vel; + if (collided) { + found_collision = true; + } - Vector3 rel_v = lv - floor_velocity; - Vector3 hv = rel_v - p_floor_direction * p_floor_direction.dot(rel_v); + if (collided) { - if (collision.travel.length() < 0.05 && hv.length() < p_slope_stop_min_velocity) { - Transform gt = get_global_transform(); - gt.origin -= collision.travel; - set_global_transform(gt); - return floor_velocity - p_floor_direction * p_floor_direction.dot(floor_velocity); - } - } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { //ceiling - on_ceiling = true; - } else { + colliders.push_back(collision); + motion = collision.remainder; + + bool is_on_slope = false; + if (p_floor_direction == Vector3()) { + //all is a wall on_wall = true; + } else { + if (collision.normal.dot(p_floor_direction) >= Math::cos(p_floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { //floor + + on_floor = true; + on_floor_body = collision.collider_rid; + floor_velocity = collision.collider_vel; + + if (p_stop_on_slope) { + if (Vector3() == lv_n + p_floor_direction) { + Transform gt = get_global_transform(); + gt.origin -= collision.travel; + set_global_transform(gt); + return Vector3(); + } + } + + is_on_slope = true; + + } else if (collision.normal.dot(-p_floor_direction) >= Math::cos(p_floor_max_angle + FLOOR_ANGLE_THRESHOLD)) { //ceiling + on_ceiling = true; + } else { + on_wall = true; + } } - } - Vector3 n = collision.normal; - motion = motion.slide(n); - lv = lv.slide(n); + if (p_stop_on_slope && is_on_slope) { + motion = motion.slide(p_floor_direction); + lv = lv.slide(p_floor_direction); + } else { + Vector3 n = collision.normal; + motion = motion.slide(n); + lv = lv.slide(n); + } - for (int i = 0; i < 3; i++) { - if (locked_axis & (1 << i)) { - lv[i] = 0; + for (int i = 0; i < 3; i++) { + if (locked_axis & (1 << i)) { + lv[i] = 0; + } } } - colliders.push_back(collision); + ++test_type; + } while (!p_stop_on_slope && test_type < 2); - } else { + if (!found_collision || motion == Vector3()) break; - } - p_max_slides--; - if (motion == Vector3()) - break; + --p_max_slides; } return lv; } +Vector3 KinematicBody::move_and_slide_with_snap(const Vector3 &p_linear_velocity, const Vector3 &p_snap, const Vector3 &p_floor_direction, bool p_infinite_inertia, bool p_stop_on_slope, int p_max_slides, float p_floor_max_angle) { + + bool was_on_floor = on_floor; + + Vector3 ret = move_and_slide(p_linear_velocity, p_floor_direction, p_stop_on_slope, p_max_slides, p_floor_max_angle, p_infinite_inertia); + if (!was_on_floor || p_snap == Vector3()) { + return ret; + } + + Collision col; + Transform gt = get_global_transform(); + + if (move_and_collide(p_snap, p_infinite_inertia, col, true)) { + gt.origin += col.travel; + if (p_floor_direction != Vector3() && Math::acos(p_floor_direction.normalized().dot(col.normal)) < p_floor_max_angle) { + on_floor = true; + on_floor_body = col.collider_rid; + floor_velocity = col.collider_vel; + } + set_global_transform(gt); + } + + return ret; +} + bool KinematicBody::is_on_floor() const { return on_floor; } + bool KinematicBody::is_on_wall() const { return on_wall; @@ -1230,6 +1291,43 @@ bool KinematicBody::test_move(const Transform &p_from, const Vector3 &p_motion, return PhysicsServer::get_singleton()->body_test_motion(get_rid(), p_from, p_motion, p_infinite_inertia); } +bool KinematicBody::separate_raycast_shapes(bool p_infinite_inertia, Collision &r_collision) { + + PhysicsServer::SeparationResult sep_res[8]; //max 8 rays + + Transform gt = get_global_transform(); + + Vector3 recover; + int hits = PhysicsServer::get_singleton()->body_test_ray_separation(get_rid(), gt, p_infinite_inertia, recover, sep_res, 8, margin); + int deepest = -1; + float deepest_depth; + for (int i = 0; i < hits; i++) { + if (deepest == -1 || sep_res[i].collision_depth > deepest_depth) { + deepest = i; + deepest_depth = sep_res[i].collision_depth; + } + } + + gt.origin += recover; + set_global_transform(gt); + + if (deepest != -1) { + r_collision.collider = sep_res[deepest].collider_id; + r_collision.collider_metadata = sep_res[deepest].collider_metadata; + r_collision.collider_shape = sep_res[deepest].collider_shape; + r_collision.collider_vel = sep_res[deepest].collider_velocity; + r_collision.collision = sep_res[deepest].collision_point; + r_collision.normal = sep_res[deepest].collision_normal; + r_collision.local_shape = sep_res[deepest].collision_local_shape; + r_collision.travel = recover; + r_collision.remainder = Vector3(); + + return true; + } else { + return false; + } +} + void KinematicBody::set_axis_lock(PhysicsServer::BodyAxis p_axis, bool p_lock) { PhysicsServer::get_singleton()->body_set_axis_lock(get_rid(), p_axis, p_lock); } @@ -1276,8 +1374,9 @@ Ref<KinematicCollision> KinematicBody::_get_slide_collision(int p_bounce) { void KinematicBody::_bind_methods() { - ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia"), &KinematicBody::_move, DEFVAL(true)); - ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "slope_stop_min_velocity", "max_slides", "floor_max_angle", "infinite_inertia"), &KinematicBody::move_and_slide, DEFVAL(Vector3(0, 0, 0)), DEFVAL(0.05), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia", "test_only"), &KinematicBody::_move, DEFVAL(true), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "floor_normal", "stop_on_slope", "max_slides", "floor_max_angle", "infinite_inertia"), &KinematicBody::move_and_slide, DEFVAL(Vector3(0, 0, 0)), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("move_and_slide_with_snap", "linear_velocity", "snap", "floor_normal", "infinite_inertia", "stop_on_slope", "max_bounces", "floor_max_angle"), &KinematicBody::move_and_slide_with_snap, DEFVAL(Vector3(0, 0, 0)), DEFVAL(true), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((float)45))); ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "infinite_inertia"), &KinematicBody::test_move); @@ -1295,13 +1394,9 @@ void KinematicBody::_bind_methods() { ClassDB::bind_method(D_METHOD("get_slide_count"), &KinematicBody::get_slide_count); ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &KinematicBody::_get_slide_collision); - ADD_GROUP("Axis Lock", "axis_lock_"); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_linear_x"), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_LINEAR_X); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_linear_y"), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_LINEAR_Y); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_linear_z"), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_LINEAR_Z); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_angular_x"), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_ANGULAR_X); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_angular_y"), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_ANGULAR_Y); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_angular_z"), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_ANGULAR_Z); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "move_lock_x", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_LINEAR_X); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "move_lock_y", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_LINEAR_Y); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "move_lock_z", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_axis_lock", "get_axis_lock", PhysicsServer::BODY_AXIS_LINEAR_Z); ADD_PROPERTY(PropertyInfo(Variant::REAL, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin"); } diff --git a/scene/3d/physics_body.h b/scene/3d/physics_body.h index 80bf422c98..c4db41f577 100644 --- a/scene/3d/physics_body.h +++ b/scene/3d/physics_body.h @@ -285,6 +285,7 @@ public: Vector3 normal; Vector3 collider_vel; ObjectID collider; + RID collider_rid; int collider_shape; Variant collider_metadata; Vector3 remainder; @@ -298,6 +299,7 @@ private: float margin; Vector3 floor_velocity; + RID on_floor_body; bool on_floor; bool on_ceiling; bool on_wall; @@ -307,23 +309,26 @@ private: _FORCE_INLINE_ bool _ignores_mode(PhysicsServer::BodyMode) const; - Ref<KinematicCollision> _move(const Vector3 &p_motion, bool p_infinite_inertia = true); + Ref<KinematicCollision> _move(const Vector3 &p_motion, bool p_infinite_inertia = true, bool p_test_only = false); Ref<KinematicCollision> _get_slide_collision(int p_bounce); protected: static void _bind_methods(); public: - bool move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collisionz); + bool move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collisionz, bool p_test_only = false); bool test_move(const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia); + bool separate_raycast_shapes(bool p_infinite_inertia, Collision &r_collision); + void set_axis_lock(PhysicsServer::BodyAxis p_axis, bool p_lock); bool get_axis_lock(PhysicsServer::BodyAxis p_axis) const; void set_safe_margin(float p_margin); float get_safe_margin() const; - Vector3 move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction = Vector3(0, 0, 0), float p_slope_stop_min_velocity = 0.05, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45), bool p_infinite_inertia = true); + Vector3 move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_floor_direction = Vector3(0, 0, 0), bool p_stop_on_slope = false, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45), bool p_infinite_inertia = true); + Vector3 move_and_slide_with_snap(const Vector3 &p_linear_velocity, const Vector3 &p_snap, const Vector3 &p_floor_direction = Vector3(0, 0, 0), bool p_infinite_inertia = true, bool p_stop_on_slope = false, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45)); bool is_on_floor() const; bool is_on_wall() const; bool is_on_ceiling() const; diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp index 1bc9fa4b12..2bde8a16c9 100644 --- a/scene/animation/animation_blend_space_1d.cpp +++ b/scene/animation/animation_blend_space_1d.cpp @@ -1,12 +1,15 @@ #include "animation_blend_space_1d.h" -void AnimationNodeBlendSpace1D::set_tree(AnimationTree *p_player) { - AnimationRootNode::set_tree(p_player); +void AnimationNodeBlendSpace1D::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::REAL,blend_position)); +} +Variant AnimationNodeBlendSpace1D::get_parameter_default_value(const StringName &p_parameter) const { + return 0; +} - for (int i = 0; i < blend_points_used; i++) { - blend_points[i].node->set_tree(p_player); - } +Ref<AnimationNode> AnimationNodeBlendSpace1D::get_child_by_name(const StringName &p_name) { + return get_blend_point_node(p_name.operator String().to_int()); } void AnimationNodeBlendSpace1D::_validate_property(PropertyInfo &property) const { @@ -20,6 +23,10 @@ void AnimationNodeBlendSpace1D::_validate_property(PropertyInfo &property) const AnimationRootNode::_validate_property(property); } +void AnimationNodeBlendSpace1D::_tree_changed() { + emit_signal("tree_changed"); +} + void AnimationNodeBlendSpace1D::_bind_methods() { ClassDB::bind_method(D_METHOD("add_blend_point", "node", "pos", "at_index"), &AnimationNodeBlendSpace1D::add_blend_point, DEFVAL(-1)); ClassDB::bind_method(D_METHOD("set_blend_point_position", "point", "pos"), &AnimationNodeBlendSpace1D::set_blend_point_position); @@ -38,30 +45,37 @@ void AnimationNodeBlendSpace1D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_snap", "snap"), &AnimationNodeBlendSpace1D::set_snap); ClassDB::bind_method(D_METHOD("get_snap"), &AnimationNodeBlendSpace1D::get_snap); - ClassDB::bind_method(D_METHOD("set_blend_pos", "pos"), &AnimationNodeBlendSpace1D::set_blend_pos); - ClassDB::bind_method(D_METHOD("get_blend_pos"), &AnimationNodeBlendSpace1D::get_blend_pos); - ClassDB::bind_method(D_METHOD("set_value_label", "text"), &AnimationNodeBlendSpace1D::set_value_label); ClassDB::bind_method(D_METHOD("get_value_label"), &AnimationNodeBlendSpace1D::get_value_label); ClassDB::bind_method(D_METHOD("_add_blend_point", "index", "node"), &AnimationNodeBlendSpace1D::_add_blend_point); + ClassDB::bind_method(D_METHOD("_tree_changed"), &AnimationNodeBlendSpace1D::_tree_changed); + for (int i = 0; i < MAX_BLEND_POINTS; i++) { - ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "_add_blend_point", "get_blend_point_node", i); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_add_blend_point", "get_blend_point_node", i); ADD_PROPERTYI(PropertyInfo(Variant::REAL, "blend_point_" + itos(i) + "/pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_blend_point_position", "get_blend_point_position", i); } ADD_PROPERTY(PropertyInfo(Variant::REAL, "min_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_min_space", "get_min_space"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_space", "get_max_space"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_snap", "get_snap"); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "blend_pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_blend_pos", "get_blend_pos"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "value_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_value_label", "get_value_label"); } +void AnimationNodeBlendSpace1D::get_child_nodes(List<ChildNode> *r_child_nodes) { + for(int i=0;i<blend_points_used;i++) { + ChildNode cn; + cn.name=itos(i); + cn.node=blend_points[i].node; + r_child_nodes->push_back(cn); + } +} + void AnimationNodeBlendSpace1D::add_blend_point(const Ref<AnimationRootNode> &p_node, float p_position, int p_at_index) { ERR_FAIL_COND(blend_points_used >= MAX_BLEND_POINTS); ERR_FAIL_COND(p_node.is_null()); - ERR_FAIL_COND(p_node->get_parent().is_valid()); + ERR_FAIL_COND(p_at_index < -1 || p_at_index > blend_points_used); if (p_at_index == -1 || p_at_index == blend_points_used) { @@ -75,10 +89,12 @@ void AnimationNodeBlendSpace1D::add_blend_point(const Ref<AnimationRootNode> &p_ blend_points[p_at_index].node = p_node; blend_points[p_at_index].position = p_position; - blend_points[p_at_index].node->set_parent(this); - blend_points[p_at_index].node->set_tree(get_tree()); + blend_points[p_at_index].node->connect("tree_changed",this,"_tree_changed",varray(),CONNECT_REFERENCE_COUNTED); blend_points_used++; + emit_signal("tree_changed"); + + } void AnimationNodeBlendSpace1D::set_blend_point_position(int p_point, float p_position) { @@ -92,13 +108,14 @@ void AnimationNodeBlendSpace1D::set_blend_point_node(int p_point, const Ref<Anim ERR_FAIL_COND(p_node.is_null()); if (blend_points[p_point].node.is_valid()) { - blend_points[p_point].node->set_parent(NULL); - blend_points[p_point].node->set_tree(NULL); + blend_points[p_point].node->disconnect("tree_changed",this,"_tree_changed"); } blend_points[p_point].node = p_node; - blend_points[p_point].node->set_parent(this); - blend_points[p_point].node->set_tree(get_tree()); + blend_points[p_point].node->connect("tree_changed",this,"_tree_changed",varray(),CONNECT_REFERENCE_COUNTED); + + emit_signal("tree_changed"); + } float AnimationNodeBlendSpace1D::get_blend_point_position(int p_point) const { @@ -114,14 +131,16 @@ Ref<AnimationRootNode> AnimationNodeBlendSpace1D::get_blend_point_node(int p_poi void AnimationNodeBlendSpace1D::remove_blend_point(int p_point) { ERR_FAIL_INDEX(p_point, blend_points_used); - blend_points[p_point].node->set_parent(NULL); - blend_points[p_point].node->set_tree(NULL); + blend_points[p_point].node->disconnect("tree_changed",this,"_tree_changed"); for (int i = p_point; i < blend_points_used - 1; i++) { blend_points[i] = blend_points[i + 1]; } + blend_points_used--; + emit_signal("tree_changed"); + } int AnimationNodeBlendSpace1D::get_blend_point_count() const { @@ -161,13 +180,6 @@ float AnimationNodeBlendSpace1D::get_snap() const { return snap; } -void AnimationNodeBlendSpace1D::set_blend_pos(float p_pos) { - blend_pos = p_pos; -} - -float AnimationNodeBlendSpace1D::get_blend_pos() const { - return blend_pos; -} void AnimationNodeBlendSpace1D::set_value_label(const String &p_label) { value_label = p_label; @@ -191,11 +203,14 @@ float AnimationNodeBlendSpace1D::process(float p_time, bool p_seek) { return 0.0; } + if (blend_points_used == 1) { // only one point available, just play that animation - return blend_node(blend_points[0].node, p_time, p_seek, 1.0, FILTER_IGNORE, false); + return blend_node(blend_points[0].name,blend_points[0].node, p_time, p_seek, 1.0, FILTER_IGNORE, false); } + float blend_pos = get_parameter(blend_position); + float weights[MAX_BLEND_POINTS] = {}; int point_lower = -1; @@ -262,7 +277,7 @@ float AnimationNodeBlendSpace1D::process(float p_time, bool p_seek) { float max_time_remaining = 0.0; for (int i = 0; i < blend_points_used; i++) { - float remaining = blend_node(blend_points[i].node, p_time, p_seek, weights[i], FILTER_IGNORE, false); + float remaining = blend_node(blend_points[i].name,blend_points[i].node, p_time, p_seek, weights[i], FILTER_IGNORE, false); max_time_remaining = MAX(max_time_remaining, remaining); } @@ -276,18 +291,19 @@ String AnimationNodeBlendSpace1D::get_caption() const { AnimationNodeBlendSpace1D::AnimationNodeBlendSpace1D() { + for(int i=0;i<MAX_BLEND_POINTS;i++) { + blend_points[i].name=itos(i); + } blend_points_used = 0; max_space = 1; min_space = -1; snap = 0.1; value_label = "value"; + + blend_position="blend_position"; } AnimationNodeBlendSpace1D::~AnimationNodeBlendSpace1D() { - for (int i = 0; i < blend_points_used; i++) { - blend_points[i].node->set_parent(this); - blend_points[i].node->set_tree(get_tree()); - } } diff --git a/scene/animation/animation_blend_space_1d.h b/scene/animation/animation_blend_space_1d.h index d1ed4c6a1f..b47accf973 100644 --- a/scene/animation/animation_blend_space_1d.h +++ b/scene/animation/animation_blend_space_1d.h @@ -11,6 +11,7 @@ class AnimationNodeBlendSpace1D : public AnimationRootNode { }; struct BlendPoint { + StringName name; Ref<AnimationRootNode> node; float position; }; @@ -18,8 +19,6 @@ class AnimationNodeBlendSpace1D : public AnimationRootNode { BlendPoint blend_points[MAX_BLEND_POINTS]; int blend_points_used; - float blend_pos; - float max_space; float min_space; @@ -29,12 +28,21 @@ class AnimationNodeBlendSpace1D : public AnimationRootNode { void _add_blend_point(int p_index, const Ref<AnimationRootNode> &p_node); + void _tree_changed(); + + StringName blend_position; + protected: virtual void _validate_property(PropertyInfo &property) const; static void _bind_methods(); public: - virtual void set_tree(AnimationTree *p_player); + + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; + + virtual void get_child_nodes(List<ChildNode> *r_child_nodes); + void add_blend_point(const Ref<AnimationRootNode> &p_node, float p_position, int p_at_index = -1); void set_blend_point_position(int p_point, float p_position); @@ -54,15 +62,14 @@ public: void set_snap(float p_snap); float get_snap() const; - void set_blend_pos(float p_pos); - float get_blend_pos() const; - void set_value_label(const String &p_label); String get_value_label() const; float process(float p_time, bool p_seek); String get_caption() const; + Ref<AnimationNode> get_child_by_name(const StringName &p_name); + AnimationNodeBlendSpace1D(); ~AnimationNodeBlendSpace1D(); }; diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp index 3c93a0c8ec..bce1477d4e 100644 --- a/scene/animation/animation_blend_space_2d.cpp +++ b/scene/animation/animation_blend_space_2d.cpp @@ -1,18 +1,27 @@ #include "animation_blend_space_2d.h" #include "math/delaunay.h" -void AnimationNodeBlendSpace2D::set_tree(AnimationTree *p_player) { - AnimationRootNode::set_tree(p_player); - for (int i = 0; i < blend_points_used; i++) { - blend_points[i].node->set_tree(p_player); +void AnimationNodeBlendSpace2D::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::VECTOR2,blend_position)); +} +Variant AnimationNodeBlendSpace2D::get_parameter_default_value(const StringName &p_parameter) const { + return Vector2(); +} + + +void AnimationNodeBlendSpace2D::get_child_nodes(List<ChildNode> *r_child_nodes) { + for(int i=0;i<blend_points_used;i++) { + ChildNode cn; + cn.name=itos(i); + cn.node=blend_points[i].node; + r_child_nodes->push_back(cn); } } void AnimationNodeBlendSpace2D::add_blend_point(const Ref<AnimationRootNode> &p_node, const Vector2 &p_position, int p_at_index) { ERR_FAIL_COND(blend_points_used >= MAX_BLEND_POINTS); ERR_FAIL_COND(p_node.is_null()); - ERR_FAIL_COND(p_node->get_parent().is_valid()); ERR_FAIL_COND(p_at_index < -1 || p_at_index > blend_points_used); if (p_at_index == -1 || p_at_index == blend_points_used) { @@ -32,13 +41,15 @@ void AnimationNodeBlendSpace2D::add_blend_point(const Ref<AnimationRootNode> &p_ blend_points[p_at_index].node = p_node; blend_points[p_at_index].position = p_position; - blend_points[p_at_index].node->set_parent(this); - blend_points[p_at_index].node->set_tree(get_tree()); + blend_points[p_at_index].node->connect("tree_changed",this,"_tree_changed",varray(),CONNECT_REFERENCE_COUNTED); blend_points_used++; + if (auto_triangles) { trianges_dirty = true; } + emit_signal("tree_changed"); + } void AnimationNodeBlendSpace2D::set_blend_point_position(int p_point, const Vector2 &p_position) { @@ -53,12 +64,13 @@ void AnimationNodeBlendSpace2D::set_blend_point_node(int p_point, const Ref<Anim ERR_FAIL_COND(p_node.is_null()); if (blend_points[p_point].node.is_valid()) { - blend_points[p_point].node->set_parent(NULL); - blend_points[p_point].node->set_tree(NULL); + blend_points[p_point].node->disconnect("tree_changed",this,"_tree_changed"); } blend_points[p_point].node = p_node; - blend_points[p_point].node->set_parent(this); - blend_points[p_point].node->set_tree(get_tree()); + blend_points[p_point].node->connect("tree_changed",this,"_tree_changed",varray(),CONNECT_REFERENCE_COUNTED); + + emit_signal("tree_changed"); + } Vector2 AnimationNodeBlendSpace2D::get_blend_point_position(int p_point) const { ERR_FAIL_INDEX_V(p_point, blend_points_used, Vector2()); @@ -71,8 +83,7 @@ Ref<AnimationRootNode> AnimationNodeBlendSpace2D::get_blend_point_node(int p_poi void AnimationNodeBlendSpace2D::remove_blend_point(int p_point) { ERR_FAIL_INDEX(p_point, blend_points_used); - blend_points[p_point].node->set_parent(NULL); - blend_points[p_point].node->set_tree(NULL); + blend_points[p_point].node->disconnect("tree_changed",this,"_tree_changed"); for (int i = 0; i < triangles.size(); i++) { bool erase = false; @@ -95,6 +106,8 @@ void AnimationNodeBlendSpace2D::remove_blend_point(int p_point) { blend_points[i] = blend_points[i + 1]; } blend_points_used--; + emit_signal("tree_changed"); + } int AnimationNodeBlendSpace2D::get_blend_point_count() const { @@ -217,13 +230,6 @@ Vector2 AnimationNodeBlendSpace2D::get_snap() const { return snap; } -void AnimationNodeBlendSpace2D::set_blend_position(const Vector2 &p_pos) { - blend_pos = p_pos; -} -Vector2 AnimationNodeBlendSpace2D::get_blend_position() const { - return blend_pos; -} - void AnimationNodeBlendSpace2D::set_x_label(const String &p_label) { x_label = p_label; } @@ -381,6 +387,8 @@ float AnimationNodeBlendSpace2D::process(float p_time, bool p_seek) { _update_triangles(); + Vector2 blend_pos = get_parameter(blend_position); + if (triangles.size() == 0) return 0; @@ -443,7 +451,7 @@ float AnimationNodeBlendSpace2D::process(float p_time, bool p_seek) { for (int j = 0; j < 3; j++) { if (i == triangle_points[j]) { //blend with the given weight - float t = blend_node(blend_points[i].node, p_time, p_seek, blend_weights[j], FILTER_IGNORE, false); + float t = blend_node(blend_points[i].name,blend_points[i].node, p_time, p_seek, blend_weights[j], FILTER_IGNORE, false); if (first || t < mind) { mind = t; first = false; @@ -455,7 +463,7 @@ float AnimationNodeBlendSpace2D::process(float p_time, bool p_seek) { if (!found) { //ignore - blend_node(blend_points[i].node, p_time, p_seek, 0, FILTER_IGNORE, false); + blend_node(blend_points[i].name,blend_points[i].node, p_time, p_seek, 0, FILTER_IGNORE, false); } } return mind; @@ -483,10 +491,19 @@ void AnimationNodeBlendSpace2D::set_auto_triangles(bool p_enable) { } } + bool AnimationNodeBlendSpace2D::get_auto_triangles() const { return auto_triangles; } +Ref<AnimationNode> AnimationNodeBlendSpace2D::get_child_by_name(const StringName &p_name) { + return get_blend_point_node(p_name.operator String().to_int()); +} + +void AnimationNodeBlendSpace2D::_tree_changed() { + emit_signal("tree_changed"); +} + void AnimationNodeBlendSpace2D::_bind_methods() { ClassDB::bind_method(D_METHOD("add_blend_point", "node", "pos", "at_index"), &AnimationNodeBlendSpace2D::add_blend_point, DEFVAL(-1)); @@ -511,9 +528,6 @@ void AnimationNodeBlendSpace2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_snap", "snap"), &AnimationNodeBlendSpace2D::set_snap); ClassDB::bind_method(D_METHOD("get_snap"), &AnimationNodeBlendSpace2D::get_snap); - ClassDB::bind_method(D_METHOD("set_blend_position", "pos"), &AnimationNodeBlendSpace2D::set_blend_position); - ClassDB::bind_method(D_METHOD("get_blend_position"), &AnimationNodeBlendSpace2D::get_blend_position); - ClassDB::bind_method(D_METHOD("set_x_label", "text"), &AnimationNodeBlendSpace2D::set_x_label); ClassDB::bind_method(D_METHOD("get_x_label"), &AnimationNodeBlendSpace2D::get_x_label); @@ -528,10 +542,12 @@ void AnimationNodeBlendSpace2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_auto_triangles", "enable"), &AnimationNodeBlendSpace2D::set_auto_triangles); ClassDB::bind_method(D_METHOD("get_auto_triangles"), &AnimationNodeBlendSpace2D::get_auto_triangles); + ClassDB::bind_method(D_METHOD("_tree_changed"), &AnimationNodeBlendSpace2D::_tree_changed); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_triangles", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_auto_triangles", "get_auto_triangles"); for (int i = 0; i < MAX_BLEND_POINTS; i++) { - ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "_add_blend_point", "get_blend_point_node", i); + ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "blend_point_" + itos(i) + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_add_blend_point", "get_blend_point_node", i); ADD_PROPERTYI(PropertyInfo(Variant::VECTOR2, "blend_point_" + itos(i) + "/pos", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_blend_point_position", "get_blend_point_position", i); } @@ -540,13 +556,15 @@ void AnimationNodeBlendSpace2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "min_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_min_space", "get_min_space"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "max_space", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_space", "get_max_space"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "snap", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_snap", "get_snap"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "blend_position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_blend_position", "get_blend_position"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "x_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_x_label", "get_x_label"); ADD_PROPERTY(PropertyInfo(Variant::STRING, "y_label", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_y_label", "get_y_label"); } AnimationNodeBlendSpace2D::AnimationNodeBlendSpace2D() { + for(int i=0;i<MAX_BLEND_POINTS;i++) { + blend_points[i].name=itos(i); + } auto_triangles = true; blend_points_used = 0; max_space = Vector2(1, 1); @@ -555,12 +573,10 @@ AnimationNodeBlendSpace2D::AnimationNodeBlendSpace2D() { x_label = "x"; y_label = "y"; trianges_dirty = false; + blend_position = "blend_position"; } AnimationNodeBlendSpace2D::~AnimationNodeBlendSpace2D() { - for (int i = 0; i < blend_points_used; i++) { - blend_points[i].node->set_parent(this); - blend_points[i].node->set_tree(get_tree()); - } + } diff --git a/scene/animation/animation_blend_space_2d.h b/scene/animation/animation_blend_space_2d.h index 74d20b6013..342909531b 100644 --- a/scene/animation/animation_blend_space_2d.h +++ b/scene/animation/animation_blend_space_2d.h @@ -11,6 +11,7 @@ class AnimationNodeBlendSpace2D : public AnimationRootNode { }; struct BlendPoint { + StringName name; Ref<AnimationRootNode> node; Vector2 position; }; @@ -24,7 +25,7 @@ class AnimationNodeBlendSpace2D : public AnimationRootNode { Vector<BlendTriangle> triangles; - Vector2 blend_pos; + StringName blend_position; Vector2 max_space; Vector2 min_space; Vector2 snap; @@ -42,12 +43,19 @@ class AnimationNodeBlendSpace2D : public AnimationRootNode { void _update_triangles(); + void _tree_changed(); + + protected: virtual void _validate_property(PropertyInfo &property) const; static void _bind_methods(); public: - virtual void set_tree(AnimationTree *p_player); + + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; + + virtual void get_child_nodes(List<ChildNode> *r_child_nodes); void add_blend_point(const Ref<AnimationRootNode> &p_node, const Vector2 &p_position, int p_at_index = -1); void set_blend_point_position(int p_point, const Vector2 &p_position); @@ -72,9 +80,6 @@ public: void set_snap(const Vector2 &p_snap); Vector2 get_snap() const; - void set_blend_position(const Vector2 &p_pos); - Vector2 get_blend_position() const; - void set_x_label(const String &p_label); String get_x_label() const; @@ -89,6 +94,8 @@ public: void set_auto_triangles(bool p_enable); bool get_auto_triangles() const; + virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name); + AnimationNodeBlendSpace2D(); ~AnimationNodeBlendSpace2D(); }; diff --git a/scene/animation/animation_blend_tree.cpp b/scene/animation/animation_blend_tree.cpp index 65904410d3..31ee31745a 100644 --- a/scene/animation/animation_blend_tree.cpp +++ b/scene/animation/animation_blend_tree.cpp @@ -3,6 +3,7 @@ void AnimationNodeAnimation::set_animation(const StringName &p_name) { animation = p_name; + _change_notify("animation"); } StringName AnimationNodeAnimation::get_animation() const { @@ -13,43 +14,36 @@ float AnimationNodeAnimation::get_playback_time() const { return time; } +Vector<String> (*AnimationNodeAnimation::get_editable_animation_list)() = NULL; + void AnimationNodeAnimation::_validate_property(PropertyInfo &property) const { - if (property.name == "animation") { - AnimationTree *gp = get_tree(); - if (gp && gp->has_node(gp->get_animation_player())) { - AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(gp->get_node(gp->get_animation_player())); - if (ap) { - List<StringName> names; - ap->get_animation_list(&names); - String anims; - for (List<StringName>::Element *E = names.front(); E; E = E->next()) { - if (E != names.front()) { - anims += ","; - } - anims += String(E->get()); - } - if (anims != String()) { - property.hint = PROPERTY_HINT_ENUM; - property.hint_string = anims; - } + if (property.name == "animation" && get_editable_animation_list) { + Vector<String> names = get_editable_animation_list(); + String anims; + for (int i = 0; i < names.size(); i++) { + + if (i > 0) { + anims += ","; } + anims += String(names[i]); + } + if (anims != String()) { + property.hint = PROPERTY_HINT_ENUM; + property.hint_string = anims; } } - - AnimationRootNode::_validate_property(property); } float AnimationNodeAnimation::process(float p_time, bool p_seek) { - AnimationPlayer *ap = get_player(); + AnimationPlayer *ap = state->player; ERR_FAIL_COND_V(!ap, 0); - Ref<Animation> anim = ap->get_animation(animation); - if (!anim.is_valid()) { + if (!ap->has_animation(animation)) { - Ref<AnimationNodeBlendTree> tree = get_parent(); - if (tree.is_valid()) { + AnimationNodeBlendTree *tree = Object::cast_to<AnimationNodeBlendTree>(parent); + if (tree) { String name = tree->get_node_name(Ref<AnimationNodeAnimation>(this)); make_invalid(vformat(RTR("On BlendTree node '%s', animation not found: '%s'"), name, animation)); @@ -60,6 +54,8 @@ float AnimationNodeAnimation::process(float p_time, bool p_seek) { return 0; } + Ref<Animation> anim = ap->get_animation(animation); + if (p_seek) { time = p_time; step = 0; @@ -108,6 +104,20 @@ AnimationNodeAnimation::AnimationNodeAnimation() { //////////////////////////////////////////////////////// +void AnimationNodeOneShot::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::BOOL, active)); + r_list->push_back(PropertyInfo(Variant::BOOL, prev_active, PROPERTY_HINT_NONE, "", 0)); + r_list->push_back(PropertyInfo(Variant::REAL, time, PROPERTY_HINT_NONE, "", 0)); + r_list->push_back(PropertyInfo(Variant::REAL, remaining, PROPERTY_HINT_NONE, "", 0)); +} +Variant AnimationNodeOneShot::get_parameter_default_value(const StringName &p_parameter) const { + if (p_parameter == active || p_parameter == prev_active) { + return false; + } else { + return 0.0; + } +} + void AnimationNodeOneShot::set_fadein_time(float p_time) { fade_in = p_time; @@ -162,18 +172,6 @@ AnimationNodeOneShot::MixMode AnimationNodeOneShot::get_mix_mode() const { return mix; } -void AnimationNodeOneShot::start() { - active = true; - do_start = true; -} -void AnimationNodeOneShot::stop() { - active = false; -} -bool AnimationNodeOneShot::is_active() const { - - return active; -} - String AnimationNodeOneShot::get_caption() const { return "OneShot"; } @@ -184,8 +182,16 @@ bool AnimationNodeOneShot::has_filter() const { float AnimationNodeOneShot::process(float p_time, bool p_seek) { + bool active = get_parameter(this->active); + bool prev_active = get_parameter(this->prev_active); + float time = get_parameter(this->time); + float remaining = get_parameter(this->remaining); + if (!active) { //make it as if this node doesn't exist, pass input 0 by. + if (prev_active) { + set_parameter(this->prev_active, false); + } return blend_input(0, p_time, p_seek, 1.0, FILTER_IGNORE, !sync); } @@ -193,9 +199,12 @@ float AnimationNodeOneShot::process(float p_time, bool p_seek) { if (p_seek) time = p_time; + bool do_start = !prev_active; + if (do_start) { time = 0; os_seek = true; + set_parameter(this->prev_active, true); } float blend; @@ -233,10 +242,15 @@ float AnimationNodeOneShot::process(float p_time, bool p_seek) { if (!p_seek) { time += p_time; remaining = os_rem; - if (remaining <= 0) - active = false; + if (remaining <= 0) { + set_parameter(this->active, false); + set_parameter(this->prev_active, false); + } } + set_parameter(this->time, time); + set_parameter(this->remaining, remaining); + return MAX(main_rem, remaining); } void AnimationNodeOneShot::set_use_sync(bool p_sync) { @@ -269,10 +283,6 @@ void AnimationNodeOneShot::_bind_methods() { ClassDB::bind_method(D_METHOD("set_mix_mode", "mode"), &AnimationNodeOneShot::set_mix_mode); ClassDB::bind_method(D_METHOD("get_mix_mode"), &AnimationNodeOneShot::get_mix_mode); - ClassDB::bind_method(D_METHOD("start"), &AnimationNodeOneShot::start); - ClassDB::bind_method(D_METHOD("stop"), &AnimationNodeOneShot::stop); - ClassDB::bind_method(D_METHOD("is_active"), &AnimationNodeOneShot::is_active); - ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeOneShot::set_use_sync); ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeOneShot::is_using_sync); @@ -297,26 +307,22 @@ AnimationNodeOneShot::AnimationNodeOneShot() { add_input("in"); add_input("shot"); - time = 0; fade_in = 0.1; fade_out = 0.1; autorestart = false; autorestart_delay = 1; - autorestart_remaining = 0; + mix = MIX_MODE_BLEND; - active = false; - do_start = false; sync = false; } //////////////////////////////////////////////// -void AnimationNodeAdd2::set_amount(float p_amount) { - amount = p_amount; +void AnimationNodeAdd2::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::REAL, add_amount, PROPERTY_HINT_RANGE, "0,1,0.01")); } - -float AnimationNodeAdd2::get_amount() const { - return amount; +Variant AnimationNodeAdd2::get_parameter_default_value(const StringName &p_parameter) const { + return 0; } String AnimationNodeAdd2::get_caption() const { @@ -339,6 +345,7 @@ bool AnimationNodeAdd2::has_filter() const { float AnimationNodeAdd2::process(float p_time, bool p_seek) { + float amount = get_parameter(add_amount); float rem0 = blend_input(0, p_time, p_seek, 1.0, FILTER_IGNORE, !sync); blend_input(1, p_time, p_seek, amount, FILTER_PASS, !sync); @@ -347,32 +354,27 @@ float AnimationNodeAdd2::process(float p_time, bool p_seek) { void AnimationNodeAdd2::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_amount", "amount"), &AnimationNodeAdd2::set_amount); - ClassDB::bind_method(D_METHOD("get_amount"), &AnimationNodeAdd2::get_amount); - ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeAdd2::set_use_sync); ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeAdd2::is_using_sync); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "amount", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_amount", "get_amount"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync"); } AnimationNodeAdd2::AnimationNodeAdd2() { + add_amount = "add_amount"; add_input("in"); add_input("add"); - amount = 0; sync = false; } //////////////////////////////////////////////// -void AnimationNodeAdd3::set_amount(float p_amount) { - amount = p_amount; +void AnimationNodeAdd3::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::REAL, add_amount, PROPERTY_HINT_RANGE, "-1,1,0.01")); } - -float AnimationNodeAdd3::get_amount() const { - return amount; +Variant AnimationNodeAdd3::get_parameter_default_value(const StringName &p_parameter) const { + return 0; } String AnimationNodeAdd3::get_caption() const { @@ -395,6 +397,7 @@ bool AnimationNodeAdd3::has_filter() const { float AnimationNodeAdd3::process(float p_time, bool p_seek) { + float amount = get_parameter(add_amount); blend_input(0, p_time, p_seek, MAX(0, -amount), FILTER_PASS, !sync); float rem0 = blend_input(1, p_time, p_seek, 1.0, FILTER_IGNORE, !sync); blend_input(2, p_time, p_seek, MAX(0, amount), FILTER_PASS, !sync); @@ -404,39 +407,37 @@ float AnimationNodeAdd3::process(float p_time, bool p_seek) { void AnimationNodeAdd3::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_amount", "amount"), &AnimationNodeAdd3::set_amount); - ClassDB::bind_method(D_METHOD("get_amount"), &AnimationNodeAdd3::get_amount); - ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeAdd3::set_use_sync); ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeAdd3::is_using_sync); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "amount", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_amount", "get_amount"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync"); } AnimationNodeAdd3::AnimationNodeAdd3() { + add_amount = "add_amount"; add_input("-add"); add_input("in"); add_input("+add"); - amount = 0; sync = false; } ///////////////////////////////////////////// -void AnimationNodeBlend2::set_amount(float p_amount) { - amount = p_amount; +void AnimationNodeBlend2::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::REAL, blend_amount, PROPERTY_HINT_RANGE, "0,1,0.01")); } - -float AnimationNodeBlend2::get_amount() const { - return amount; +Variant AnimationNodeBlend2::get_parameter_default_value(const StringName &p_parameter) const { + return 0; //for blend amount } + String AnimationNodeBlend2::get_caption() const { return "Blend2"; } float AnimationNodeBlend2::process(float p_time, bool p_seek) { + float amount = get_parameter(blend_amount); + float rem0 = blend_input(0, p_time, p_seek, 1.0 - amount, FILTER_BLEND, !sync); float rem1 = blend_input(1, p_time, p_seek, amount, FILTER_PASS, !sync); @@ -459,31 +460,25 @@ bool AnimationNodeBlend2::has_filter() const { } void AnimationNodeBlend2::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_amount", "amount"), &AnimationNodeBlend2::set_amount); - ClassDB::bind_method(D_METHOD("get_amount"), &AnimationNodeBlend2::get_amount); - ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeBlend2::set_use_sync); ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeBlend2::is_using_sync); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "amount", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_amount", "get_amount"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync"); } AnimationNodeBlend2::AnimationNodeBlend2() { + blend_amount = "blend_amount"; add_input("in"); add_input("blend"); sync = false; - - amount = 0; } ////////////////////////////////////// -void AnimationNodeBlend3::set_amount(float p_amount) { - amount = p_amount; +void AnimationNodeBlend3::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::REAL, blend_amount, PROPERTY_HINT_RANGE, "-1,1,0.01")); } - -float AnimationNodeBlend3::get_amount() const { - return amount; +Variant AnimationNodeBlend3::get_parameter_default_value(const StringName &p_parameter) const { + return 0; //for blend amount } String AnimationNodeBlend3::get_caption() const { @@ -502,6 +497,7 @@ bool AnimationNodeBlend3::is_using_sync() const { float AnimationNodeBlend3::process(float p_time, bool p_seek) { + float amount = get_parameter(blend_amount); float rem0 = blend_input(0, p_time, p_seek, MAX(0, -amount), FILTER_IGNORE, !sync); float rem1 = blend_input(1, p_time, p_seek, 1.0 - ABS(amount), FILTER_IGNORE, !sync); float rem2 = blend_input(2, p_time, p_seek, MAX(0, amount), FILTER_IGNORE, !sync); @@ -511,31 +507,26 @@ float AnimationNodeBlend3::process(float p_time, bool p_seek) { void AnimationNodeBlend3::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_amount", "amount"), &AnimationNodeBlend3::set_amount); - ClassDB::bind_method(D_METHOD("get_amount"), &AnimationNodeBlend3::get_amount); - ClassDB::bind_method(D_METHOD("set_use_sync", "enable"), &AnimationNodeBlend3::set_use_sync); ClassDB::bind_method(D_METHOD("is_using_sync"), &AnimationNodeBlend3::is_using_sync); - ADD_PROPERTY(PropertyInfo(Variant::REAL, "amount", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_amount", "get_amount"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync"), "set_use_sync", "is_using_sync"); } AnimationNodeBlend3::AnimationNodeBlend3() { + blend_amount = "blend_amount"; add_input("-blend"); add_input("in"); add_input("+blend"); sync = false; - amount = 0; } ///////////////////////////////// -void AnimationNodeTimeScale::set_scale(float p_scale) { - scale = p_scale; +void AnimationNodeTimeScale::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::REAL, scale, PROPERTY_HINT_RANGE, "0,32,0.01,or_greater")); } - -float AnimationNodeTimeScale::get_scale() const { - return scale; +Variant AnimationNodeTimeScale::get_parameter_default_value(const StringName &p_parameter) const { + return 1.0; //initial timescale } String AnimationNodeTimeScale::get_caption() const { @@ -544,6 +535,7 @@ String AnimationNodeTimeScale::get_caption() const { float AnimationNodeTimeScale::process(float p_time, bool p_seek) { + float scale = get_parameter(this->scale); if (p_seek) { return blend_input(0, p_time, true, 1.0, FILTER_IGNORE, false); } else { @@ -552,25 +544,19 @@ float AnimationNodeTimeScale::process(float p_time, bool p_seek) { } void AnimationNodeTimeScale::_bind_methods() { - - ClassDB::bind_method(D_METHOD("set_scale", "scale"), &AnimationNodeTimeScale::set_scale); - ClassDB::bind_method(D_METHOD("get_scale"), &AnimationNodeTimeScale::get_scale); - - ADD_PROPERTY(PropertyInfo(Variant::REAL, "scale", PROPERTY_HINT_RANGE, "0,32,0.01,or_greater"), "set_scale", "get_scale"); } AnimationNodeTimeScale::AnimationNodeTimeScale() { + scale = "scale"; add_input("in"); - scale = 1.0; } //////////////////////////////////// -void AnimationNodeTimeSeek::set_seek_pos(float p_seek_pos) { - seek_pos = p_seek_pos; +void AnimationNodeTimeSeek::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::REAL, seek_pos, PROPERTY_HINT_RANGE, "-1,3600,0.01,or_greater")); } - -float AnimationNodeTimeSeek::get_seek_pos() const { - return seek_pos; +Variant AnimationNodeTimeSeek::get_parameter_default_value(const StringName &p_parameter) const { + return 1.0; //initial timescale } String AnimationNodeTimeSeek::get_caption() const { @@ -579,11 +565,12 @@ String AnimationNodeTimeSeek::get_caption() const { float AnimationNodeTimeSeek::process(float p_time, bool p_seek) { + float seek_pos = get_parameter(this->seek_pos); if (p_seek) { return blend_input(0, p_time, true, 1.0, FILTER_IGNORE, false); } else if (seek_pos >= 0) { float ret = blend_input(0, seek_pos, true, 1.0, FILTER_IGNORE, false); - seek_pos = -1; + set_parameter(this->seek_pos, -1.0); //reset _change_notify("seek_pos"); return ret; } else { @@ -592,19 +579,41 @@ float AnimationNodeTimeSeek::process(float p_time, bool p_seek) { } void AnimationNodeTimeSeek::_bind_methods() { - - ClassDB::bind_method(D_METHOD("set_seek_pos", "seek_pos"), &AnimationNodeTimeSeek::set_seek_pos); - ClassDB::bind_method(D_METHOD("get_seek_pos"), &AnimationNodeTimeSeek::get_seek_pos); - - ADD_PROPERTY(PropertyInfo(Variant::REAL, "seek_pos", PROPERTY_HINT_RANGE, "-1,3600,0.01,or_greater"), "set_seek_pos", "get_seek_pos"); } + AnimationNodeTimeSeek::AnimationNodeTimeSeek() { add_input("in"); - seek_pos = -1; + seek_pos = "seek_position"; } ///////////////////////////////////////////////// +void AnimationNodeTransition::get_parameter_list(List<PropertyInfo> *r_list) const { + + String anims; + for (int i = 0; i < enabled_inputs; i++) { + if (i > 0) { + anims += ","; + } + anims += inputs[i].name; + } + + r_list->push_back(PropertyInfo(Variant::INT, current, PROPERTY_HINT_ENUM, anims)); + r_list->push_back(PropertyInfo(Variant::INT, prev_current, PROPERTY_HINT_NONE, "", 0)); + r_list->push_back(PropertyInfo(Variant::INT, prev, PROPERTY_HINT_NONE, "", 0)); + r_list->push_back(PropertyInfo(Variant::REAL, time, PROPERTY_HINT_NONE, "", 0)); + r_list->push_back(PropertyInfo(Variant::REAL, prev_xfading, PROPERTY_HINT_NONE, "", 0)); +} +Variant AnimationNodeTransition::get_parameter_default_value(const StringName &p_parameter) const { + if (p_parameter == time || p_parameter == prev_xfading) { + return 0.0; + } else if (p_parameter == prev || p_parameter == prev_current) { + return -1; + } else { + return 0; + } +} + String AnimationNodeTransition::get_caption() const { return "Transition"; } @@ -650,18 +659,12 @@ String AnimationNodeTransition::get_input_caption(int p_input) const { return inputs[p_input].name; } -void AnimationNodeTransition::set_current(int p_current) { - - if (current == p_current) - return; - ERR_FAIL_INDEX(p_current, enabled_inputs); - +#if 0 Ref<AnimationNodeBlendTree> tree = get_parent(); if (tree.is_valid() && current >= 0) { prev = current; - prev_xfading = xfade; - prev_time = time; + prev_xfading = xfade; time = 0; current = p_current; switched = true; @@ -669,11 +672,8 @@ void AnimationNodeTransition::set_current(int p_current) { } else { current = p_current; } -} +#endif -int AnimationNodeTransition::get_current() const { - return current; -} void AnimationNodeTransition::set_cross_fade_time(float p_fade) { xfade = p_fade; } @@ -684,9 +684,34 @@ float AnimationNodeTransition::get_cross_fade_time() const { float AnimationNodeTransition::process(float p_time, bool p_seek) { + int current = get_parameter(this->current); + int prev = get_parameter(this->prev); + int prev_current = get_parameter(this->prev_current); + + float time = get_parameter(this->time); + float prev_xfading = get_parameter(this->prev_xfading); + + bool switched = current != prev_current; + + if (switched) { + set_parameter(this->prev_current, current); + set_parameter(this->prev, prev_current); + + prev = prev_current; + prev_xfading = xfade; + time = 0; + switched = true; + } + + if (current < 0 || current >= enabled_inputs || prev >= enabled_inputs) { + return 0; + } + + float rem = 0; + if (prev < 0) { // process current animation, check for transition - float rem = blend_input(current, p_time, p_seek, 1.0, FILTER_IGNORE, false); + rem = blend_input(current, p_time, p_seek, 1.0, FILTER_IGNORE, false); if (p_seek) time = p_time; @@ -695,16 +720,13 @@ float AnimationNodeTransition::process(float p_time, bool p_seek) { if (inputs[current].auto_advance && rem <= xfade) { - set_current((current + 1) % enabled_inputs); + set_parameter(this->current, (current + 1) % enabled_inputs); } - return rem; } else { // cross-fading from prev to current float blend = xfade ? (prev_xfading / xfade) : 1; - float rem; - if (!p_seek && switched) { //just switched, seek to start of current rem = blend_input(current, 0, true, 1.0 - blend, FILTER_IGNORE, false); @@ -723,28 +745,19 @@ float AnimationNodeTransition::process(float p_time, bool p_seek) { time += p_time; prev_xfading -= p_time; if (prev_xfading < 0) { - prev = -1; + set_parameter(this->prev, -1); } } - - return rem; } + + set_parameter(this->time, time); + set_parameter(this->prev_xfading, prev_xfading); + + return rem; } void AnimationNodeTransition::_validate_property(PropertyInfo &property) const { - if (property.name == "current" && enabled_inputs > 0) { - property.hint = PROPERTY_HINT_ENUM; - String anims; - for (int i = 0; i < enabled_inputs; i++) { - if (i > 0) { - anims += ","; - } - anims += inputs[i].name; - } - property.hint_string = anims; - } - if (property.name.begins_with("input_")) { String n = property.name.get_slicec('/', 0).get_slicec('_', 1); if (n != "count") { @@ -769,14 +782,10 @@ void AnimationNodeTransition::_bind_methods() { ClassDB::bind_method(D_METHOD("set_input_caption", "input", "caption"), &AnimationNodeTransition::set_input_caption); ClassDB::bind_method(D_METHOD("get_input_caption", "input"), &AnimationNodeTransition::get_input_caption); - ClassDB::bind_method(D_METHOD("set_current", "index"), &AnimationNodeTransition::set_current); - ClassDB::bind_method(D_METHOD("get_current"), &AnimationNodeTransition::get_current); - ClassDB::bind_method(D_METHOD("set_cross_fade_time", "time"), &AnimationNodeTransition::set_cross_fade_time); ClassDB::bind_method(D_METHOD("get_cross_fade_time"), &AnimationNodeTransition::get_cross_fade_time); ADD_PROPERTY(PropertyInfo(Variant::INT, "input_count", PROPERTY_HINT_RANGE, "0,64,1", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_enabled_inputs", "get_enabled_inputs"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "current", PROPERTY_HINT_RANGE, "0,64,1"), "set_current", "get_current"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "xfade_time", PROPERTY_HINT_RANGE, "0,120,0.01"), "set_cross_fade_time", "get_cross_fade_time"); for (int i = 0; i < MAX_INPUTS; i++) { @@ -786,13 +795,15 @@ void AnimationNodeTransition::_bind_methods() { } AnimationNodeTransition::AnimationNodeTransition() { + + prev_xfading = "prev_xfading"; + prev = "prev"; + time = "time"; + current = "current"; + prev_current = "prev_current"; + ; + enabled_inputs = 0; - xfade = 0; - current = -1; - prev = -1; - prev_time = 0; - prev_xfading = 0; - switched = false; for (int i = 0; i < MAX_INPUTS; i++) { inputs[i].auto_advance = false; inputs[i].name = itos(i + 1); @@ -814,69 +825,102 @@ AnimationNodeOutput::AnimationNodeOutput() { } /////////////////////////////////////////////////////// -void AnimationNodeBlendTree::add_node(const StringName &p_name, Ref<AnimationNode> p_node) { +void AnimationNodeBlendTree::add_node(const StringName &p_name, Ref<AnimationNode> p_node, const Vector2 &p_position) { ERR_FAIL_COND(nodes.has(p_name)); ERR_FAIL_COND(p_node.is_null()); - ERR_FAIL_COND(p_node->get_parent().is_valid()); - ERR_FAIL_COND(p_node->get_tree() != NULL); ERR_FAIL_COND(p_name == SceneStringNames::get_singleton()->output); ERR_FAIL_COND(String(p_name).find("/") != -1); - nodes[p_name] = p_node; - p_node->set_parent(this); - p_node->set_tree(get_tree()); + Node n; + n.node = p_node; + n.position = p_position; + n.connections.resize(n.node->get_input_count()); + nodes[p_name] = n; emit_changed(); + emit_signal("tree_changed"); + + p_node->connect("tree_changed", this, "_tree_changed", varray(), CONNECT_REFERENCE_COUNTED); + p_node->connect("changed", this, "_node_changed", varray(p_name), CONNECT_REFERENCE_COUNTED); } Ref<AnimationNode> AnimationNodeBlendTree::get_node(const StringName &p_name) const { ERR_FAIL_COND_V(!nodes.has(p_name), Ref<AnimationNode>()); - return nodes[p_name]; + return nodes[p_name].node; } StringName AnimationNodeBlendTree::get_node_name(const Ref<AnimationNode> &p_node) const { - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { - if (E->get() == p_node) { + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { + if (E->get().node == p_node) { return E->key(); } } ERR_FAIL_V(StringName()); } + +void AnimationNodeBlendTree::set_node_position(const StringName &p_node, const Vector2 &p_position) { + ERR_FAIL_COND(!nodes.has(p_node)); + nodes[p_node].position = p_position; +} + +Vector2 AnimationNodeBlendTree::get_node_position(const StringName &p_node) const { + ERR_FAIL_COND_V(!nodes.has(p_node), Vector2()); + return nodes[p_node].position; +} + +void AnimationNodeBlendTree::get_child_nodes(List<ChildNode> *r_child_nodes) { + Vector<StringName> ns; + + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { + ns.push_back(E->key()); + } + + ns.sort_custom<StringName::AlphCompare>(); + + for (int i = 0; i < ns.size(); i++) { + ChildNode cn; + cn.name = ns[i]; + cn.node = nodes[cn.name].node; + r_child_nodes->push_back(cn); + } +} + bool AnimationNodeBlendTree::has_node(const StringName &p_name) const { return nodes.has(p_name); } +Vector<StringName> AnimationNodeBlendTree::get_node_connection_array(const StringName &p_name) const { + + ERR_FAIL_COND_V(!nodes.has(p_name), Vector<StringName>()); + return nodes[p_name].connections; +} void AnimationNodeBlendTree::remove_node(const StringName &p_name) { ERR_FAIL_COND(!nodes.has(p_name)); ERR_FAIL_COND(p_name == SceneStringNames::get_singleton()->output); //can't delete output { - //erase node connections - Ref<AnimationNode> node = nodes[p_name]; - for (int i = 0; i < node->get_input_count(); i++) { - node->set_input_connection(i, StringName()); - } - node->set_parent(NULL); - node->set_tree(NULL); + Ref<AnimationNode> node = nodes[p_name].node; + node->disconnect("tree_changed", this, "_tree_changed"); + node->disconnect("changed", this, "_node_changed"); } nodes.erase(p_name); //erase connections to name - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { - Ref<AnimationNode> node = E->get(); - for (int i = 0; i < node->get_input_count(); i++) { - if (node->get_input_connection(i) == p_name) { - node->set_input_connection(i, StringName()); + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { + for (int i = 0; i < E->get().connections.size(); i++) { + if (E->get().connections[i] == p_name) { + E->get().connections.write[i] = StringName(); } } } emit_changed(); + emit_signal("tree_changed"); } void AnimationNodeBlendTree::rename_node(const StringName &p_name, const StringName &p_new_name) { @@ -886,18 +930,24 @@ void AnimationNodeBlendTree::rename_node(const StringName &p_name, const StringN ERR_FAIL_COND(p_name == SceneStringNames::get_singleton()->output); ERR_FAIL_COND(p_new_name == SceneStringNames::get_singleton()->output); + nodes[p_name].node->disconnect("changed", this, "_node_changed"); + nodes[p_new_name] = nodes[p_name]; nodes.erase(p_name); //rename connections - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { - Ref<AnimationNode> node = E->get(); - for (int i = 0; i < node->get_input_count(); i++) { - if (node->get_input_connection(i) == p_name) { - node->set_input_connection(i, p_new_name); + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { + + for (int i = 0; i < E->get().connections.size(); i++) { + if (E->get().connections[i] == p_name) { + E->get().connections.write[i] = p_new_name; } } } + //connection must be done with new name + nodes[p_new_name].node->connect("changed", this, "_node_changed", varray(p_new_name), CONNECT_REFERENCE_COUNTED); + + emit_signal("tree_changed"); } void AnimationNodeBlendTree::connect_node(const StringName &p_input_node, int p_input_index, const StringName &p_output_node) { @@ -907,18 +957,18 @@ void AnimationNodeBlendTree::connect_node(const StringName &p_input_node, int p_ ERR_FAIL_COND(p_output_node == SceneStringNames::get_singleton()->output); ERR_FAIL_COND(p_input_node == p_output_node); - Ref<AnimationNode> input = nodes[p_input_node]; - ERR_FAIL_INDEX(p_input_index, input->get_input_count()); + Ref<AnimationNode> input = nodes[p_input_node].node; + ERR_FAIL_INDEX(p_input_index, nodes[p_input_node].connections.size()); - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { - Ref<AnimationNode> node = E->get(); - for (int i = 0; i < node->get_input_count(); i++) { - StringName output = node->get_input_connection(i); + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { + for (int i = 0; i < E->get().connections.size(); i++) { + StringName output = E->get().connections[i]; ERR_FAIL_COND(output == p_output_node); } } - input->set_input_connection(p_input_index, p_output_node); + nodes[p_input_node].connections.write[p_input_index] = p_output_node; + emit_changed(); } @@ -926,20 +976,21 @@ void AnimationNodeBlendTree::disconnect_node(const StringName &p_node, int p_inp ERR_FAIL_COND(!nodes.has(p_node)); - Ref<AnimationNode> input = nodes[p_node]; - ERR_FAIL_INDEX(p_input_index, input->get_input_count()); + Ref<AnimationNode> input = nodes[p_node].node; + ERR_FAIL_INDEX(p_input_index, nodes[p_node].connections.size()); - input->set_input_connection(p_input_index, StringName()); + nodes[p_node].connections.write[p_input_index] = StringName(); } float AnimationNodeBlendTree::get_connection_activity(const StringName &p_input_node, int p_input_index) const { ERR_FAIL_COND_V(!nodes.has(p_input_node), 0); - Ref<AnimationNode> input = nodes[p_input_node]; - ERR_FAIL_INDEX_V(p_input_index, input->get_input_count(), 0); + Ref<AnimationNode> input = nodes[p_input_node].node; + ERR_FAIL_INDEX_V(p_input_index, nodes[p_input_node].connections.size(), 0); - return input->get_input_activity(p_input_index); + //return input->get_input_activity(p_input_index); + return 0; } AnimationNodeBlendTree::ConnectionError AnimationNodeBlendTree::can_connect_node(const StringName &p_input_node, int p_input_index, const StringName &p_output_node) const { @@ -956,20 +1007,19 @@ AnimationNodeBlendTree::ConnectionError AnimationNodeBlendTree::can_connect_node return CONNECTION_ERROR_SAME_NODE; } - Ref<AnimationNode> input = nodes[p_input_node]; + Ref<AnimationNode> input = nodes[p_input_node].node; - if (p_input_index < 0 || p_input_index >= input->get_input_count()) { + if (p_input_index < 0 || p_input_index >= nodes[p_input_node].connections.size()) { return CONNECTION_ERROR_NO_INPUT_INDEX; } - if (input->get_input_connection(p_input_index) != StringName()) { + if (nodes[p_input_node].connections[p_input_index] != StringName()) { return CONNECTION_ERROR_CONNECTION_EXISTS; } - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { - Ref<AnimationNode> node = E->get(); - for (int i = 0; i < node->get_input_count(); i++) { - StringName output = node->get_input_connection(i); + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { + for (int i = 0; i < E->get().connections.size(); i++) { + StringName output = E->get().connections[i]; if (output == p_output_node) { return CONNECTION_ERROR_CONNECTION_EXISTS; } @@ -980,10 +1030,9 @@ AnimationNodeBlendTree::ConnectionError AnimationNodeBlendTree::can_connect_node void AnimationNodeBlendTree::get_node_connections(List<NodeConnection> *r_connections) const { - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { - Ref<AnimationNode> node = E->get(); - for (int i = 0; i < node->get_input_count(); i++) { - StringName output = node->get_input_connection(i); + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { + for (int i = 0; i < E->get().connections.size(); i++) { + StringName output = E->get().connections[i]; if (output != StringName()) { NodeConnection nc; nc.input_node = E->key(); @@ -1001,13 +1050,13 @@ String AnimationNodeBlendTree::get_caption() const { float AnimationNodeBlendTree::process(float p_time, bool p_seek) { - Ref<AnimationNodeOutput> output = nodes[SceneStringNames::get_singleton()->output]; - return blend_node(output, p_time, p_seek, 1.0); + Ref<AnimationNodeOutput> output = nodes[SceneStringNames::get_singleton()->output].node; + return _blend_node("output", nodes[SceneStringNames::get_singleton()->output].connections, this, output, p_time, p_seek, 1.0); } void AnimationNodeBlendTree::get_node_list(List<StringName> *r_list) { - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { r_list->push_back(E->key()); } } @@ -1022,14 +1071,8 @@ Vector2 AnimationNodeBlendTree::get_graph_offset() const { return graph_offset; } -void AnimationNodeBlendTree::set_tree(AnimationTree *p_player) { - - AnimationNode::set_tree(p_player); - - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { - Ref<AnimationNode> node = E->get(); - node->set_tree(p_player); - } +Ref<AnimationNode> AnimationNodeBlendTree::get_child_by_name(const StringName &p_name) { + return get_node(p_name); } bool AnimationNodeBlendTree::_set(const StringName &p_name, const Variant &p_value) { @@ -1051,7 +1094,7 @@ bool AnimationNodeBlendTree::_set(const StringName &p_name, const Variant &p_val if (what == "position") { if (nodes.has(node_name)) { - nodes[node_name]->set_position(p_value); + nodes[node_name].position = p_value; } return true; } @@ -1078,7 +1121,7 @@ bool AnimationNodeBlendTree::_get(const StringName &p_name, Variant &r_ret) cons if (what == "node") { if (nodes.has(node_name)) { - r_ret = nodes[node_name]; + r_ret = nodes[node_name].node; return true; } } @@ -1086,7 +1129,7 @@ bool AnimationNodeBlendTree::_get(const StringName &p_name, Variant &r_ret) cons if (what == "position") { if (nodes.has(node_name)) { - r_ret = nodes[node_name]->get_position(); + r_ret = nodes[node_name].position; return true; } } @@ -1113,7 +1156,7 @@ bool AnimationNodeBlendTree::_get(const StringName &p_name, Variant &r_ret) cons void AnimationNodeBlendTree::_get_property_list(List<PropertyInfo> *p_list) const { List<StringName> names; - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { + for (Map<StringName, Node>::Element *E = nodes.front(); E; E = E->next()) { names.push_back(E->key()); } names.sort_custom<StringName::AlphCompare>(); @@ -1121,7 +1164,7 @@ void AnimationNodeBlendTree::_get_property_list(List<PropertyInfo> *p_list) cons for (List<StringName>::Element *E = names.front(); E; E = E->next()) { String name = E->get(); if (name != "output") { - p_list->push_back(PropertyInfo(Variant::OBJECT, "nodes/" + name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "nodes/" + name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NOEDITOR)); } p_list->push_back(PropertyInfo(Variant::VECTOR2, "nodes/" + name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } @@ -1129,9 +1172,19 @@ void AnimationNodeBlendTree::_get_property_list(List<PropertyInfo> *p_list) cons p_list->push_back(PropertyInfo(Variant::ARRAY, "node_connections", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } +void AnimationNodeBlendTree::_tree_changed() { + emit_signal("tree_changed"); +} + +void AnimationNodeBlendTree::_node_changed(const StringName &p_node) { + + ERR_FAIL_COND(!nodes.has(p_node)); + nodes[p_node].connections.resize(nodes[p_node].node->get_input_count()); +} + void AnimationNodeBlendTree::_bind_methods() { - ClassDB::bind_method(D_METHOD("add_node", "name", "node"), &AnimationNodeBlendTree::add_node); + ClassDB::bind_method(D_METHOD("add_node", "name", "node", "position"), &AnimationNodeBlendTree::add_node, DEFVAL(Vector2())); ClassDB::bind_method(D_METHOD("get_node", "name"), &AnimationNodeBlendTree::get_node); ClassDB::bind_method(D_METHOD("remove_node", "name"), &AnimationNodeBlendTree::remove_node); ClassDB::bind_method(D_METHOD("rename_node", "name", "new_name"), &AnimationNodeBlendTree::rename_node); @@ -1139,9 +1192,15 @@ void AnimationNodeBlendTree::_bind_methods() { ClassDB::bind_method(D_METHOD("connect_node", "input_node", "input_index", "output_node"), &AnimationNodeBlendTree::connect_node); ClassDB::bind_method(D_METHOD("disconnect_node", "input_node", "input_index"), &AnimationNodeBlendTree::disconnect_node); + ClassDB::bind_method(D_METHOD("set_node_position", "name", "position"), &AnimationNodeBlendTree::set_node_position); + ClassDB::bind_method(D_METHOD("get_node_position", "name"), &AnimationNodeBlendTree::get_node_position); + ClassDB::bind_method(D_METHOD("set_graph_offset", "offset"), &AnimationNodeBlendTree::set_graph_offset); ClassDB::bind_method(D_METHOD("get_graph_offset"), &AnimationNodeBlendTree::get_graph_offset); + ClassDB::bind_method(D_METHOD("_tree_changed"), &AnimationNodeBlendTree::_tree_changed); + ClassDB::bind_method(D_METHOD("_node_changed", "node"), &AnimationNodeBlendTree::_node_changed); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_graph_offset", "get_graph_offset"); BIND_CONSTANT(CONNECTION_OK); @@ -1156,15 +1215,12 @@ AnimationNodeBlendTree::AnimationNodeBlendTree() { Ref<AnimationNodeOutput> output; output.instance(); - output->set_position(Vector2(300, 150)); - output->set_parent(this); - nodes["output"] = output; + Node n; + n.node = output; + n.position = Vector2(300, 150); + n.connections.resize(1); + nodes["output"] = n; } AnimationNodeBlendTree::~AnimationNodeBlendTree() { - - for (Map<StringName, Ref<AnimationNode> >::Element *E = nodes.front(); E; E = E->next()) { - E->get()->set_parent(NULL); - E->get()->set_tree(NULL); - } } diff --git a/scene/animation/animation_blend_tree.h b/scene/animation/animation_blend_tree.h index e86cc2e823..37bd45c74a 100644 --- a/scene/animation/animation_blend_tree.h +++ b/scene/animation/animation_blend_tree.h @@ -20,6 +20,8 @@ protected: static void _bind_methods(); public: + static Vector<String> (*get_editable_animation_list)(); + virtual String get_caption() const; virtual float process(float p_time, bool p_seek); @@ -41,8 +43,6 @@ public: }; private: - bool active; - bool do_start; float fade_in; float fade_out; @@ -51,15 +51,25 @@ private: float autorestart_random_delay; MixMode mix; - float time; - float remaining; - float autorestart_remaining; bool sync; + /* bool active; + bool do_start; + float time; + float remaining;*/ + + StringName active; + StringName prev_active; + StringName time; + StringName remaining; + protected: static void _bind_methods(); public: + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; + virtual String get_caption() const; void set_fadein_time(float p_time); @@ -79,10 +89,6 @@ public: 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; @@ -97,17 +103,17 @@ VARIANT_ENUM_CAST(AnimationNodeOneShot::MixMode) class AnimationNodeAdd2 : public AnimationNode { GDCLASS(AnimationNodeAdd2, AnimationNode); - float amount; + StringName add_amount; bool sync; protected: static void _bind_methods(); public: - virtual String get_caption() const; + void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; - void set_amount(float p_amount); - float get_amount() const; + virtual String get_caption() const; void set_use_sync(bool p_sync); bool is_using_sync() const; @@ -121,17 +127,17 @@ public: class AnimationNodeAdd3 : public AnimationNode { GDCLASS(AnimationNodeAdd3, AnimationNode); - float amount; + StringName add_amount; bool sync; protected: static void _bind_methods(); public: - virtual String get_caption() const; + void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; - void set_amount(float p_amount); - float get_amount() const; + virtual String get_caption() const; void set_use_sync(bool p_sync); bool is_using_sync() const; @@ -145,19 +151,19 @@ public: class AnimationNodeBlend2 : public AnimationNode { GDCLASS(AnimationNodeBlend2, AnimationNode); - float amount; + StringName blend_amount; bool sync; protected: static void _bind_methods(); public: + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; + 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; @@ -168,17 +174,17 @@ public: class AnimationNodeBlend3 : public AnimationNode { GDCLASS(AnimationNodeBlend3, AnimationNode); - float amount; + StringName blend_amount; bool sync; protected: static void _bind_methods(); public: - virtual String get_caption() const; + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; - void set_amount(float p_amount); - float get_amount() const; + virtual String get_caption() const; void set_use_sync(bool p_sync); bool is_using_sync() const; @@ -190,16 +196,16 @@ public: class AnimationNodeTimeScale : public AnimationNode { GDCLASS(AnimationNodeTimeScale, AnimationNode); - float scale; + StringName scale; protected: static void _bind_methods(); public: - virtual String get_caption() const; + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; - void set_scale(float p_scale); - float get_scale() const; + virtual String get_caption() const; float process(float p_time, bool p_seek); @@ -209,16 +215,16 @@ public: class AnimationNodeTimeSeek : public AnimationNode { GDCLASS(AnimationNodeTimeSeek, AnimationNode); - float seek_pos; + StringName seek_pos; protected: static void _bind_methods(); public: - virtual String get_caption() const; + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; - void set_seek_pos(float p_sec); - float get_seek_pos() const; + virtual String get_caption() const; float process(float p_time, bool p_seek); @@ -241,13 +247,18 @@ class AnimationNodeTransition : public AnimationNode { InputData inputs[MAX_INPUTS]; int enabled_inputs; - float prev_time; + /* float prev_xfading; int prev; - bool switched; - float time; int current; + int prev_current; */ + + StringName prev_xfading; + StringName prev; + StringName time; + StringName current; + StringName prev_current; float xfade; @@ -258,6 +269,9 @@ protected: void _validate_property(PropertyInfo &property) const; public: + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; + virtual String get_caption() const; void set_enabled_inputs(int p_inputs); @@ -269,9 +283,6 @@ public: 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; @@ -293,10 +304,19 @@ public: class AnimationNodeBlendTree : public AnimationRootNode { GDCLASS(AnimationNodeBlendTree, AnimationRootNode) - Map<StringName, Ref<AnimationNode> > nodes; + struct Node { + Ref<AnimationNode> node; + Vector2 position; + Vector<StringName> connections; + }; + + Map<StringName, Node> nodes; Vector2 graph_offset; + void _tree_changed(); + void _node_changed(const StringName &p_node); + protected: static void _bind_methods(); bool _set(const StringName &p_name, const Variant &p_value); @@ -314,12 +334,18 @@ public: //no need to check for cycles due to tree topology }; - void add_node(const StringName &p_name, Ref<AnimationNode> p_node); + void add_node(const StringName &p_name, Ref<AnimationNode> p_node, const Vector2 &p_position = Vector2()); 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; + Vector<StringName> get_node_connection_array(const StringName &p_name) const; + + void set_node_position(const StringName &p_node, const Vector2 &p_position); + Vector2 get_node_position(const StringName &p_node) const; + + virtual void get_child_nodes(List<ChildNode> *r_child_nodes); 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); @@ -342,7 +368,8 @@ public: void set_graph_offset(const Vector2 &p_graph_offset); Vector2 get_graph_offset() const; - virtual void set_tree(AnimationTree *p_player); + virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name); + AnimationNodeBlendTree(); ~AnimationNodeBlendTree(); }; diff --git a/scene/animation/animation_node_state_machine.cpp b/scene/animation/animation_node_state_machine.cpp index f478112a36..cbbed2efcc 100644 --- a/scene/animation/animation_node_state_machine.cpp +++ b/scene/animation/animation_node_state_machine.cpp @@ -20,6 +20,26 @@ bool AnimationNodeStateMachineTransition::has_auto_advance() const { return auto_advance; } +void AnimationNodeStateMachineTransition::set_advance_condition(const StringName &p_condition) { + String cs = p_condition; + ERR_FAIL_COND(cs.find("/") != -1 || cs.find(":") != -1); + advance_condition = p_condition; + if (cs != String()) { + advance_condition_name = "conditions/" + cs; + } else { + advance_condition_name = StringName(); + } + emit_signal("advance_condition_changed"); +} + +StringName AnimationNodeStateMachineTransition::get_advance_condition() const { + return advance_condition; +} + +StringName AnimationNodeStateMachineTransition::get_advance_condition_name() const { + return advance_condition_name; +} + void AnimationNodeStateMachineTransition::set_xfade_time(float p_xfade) { ERR_FAIL_COND(p_xfade < 0); @@ -56,6 +76,9 @@ void AnimationNodeStateMachineTransition::_bind_methods() { ClassDB::bind_method(D_METHOD("set_auto_advance", "auto_advance"), &AnimationNodeStateMachineTransition::set_auto_advance); ClassDB::bind_method(D_METHOD("has_auto_advance"), &AnimationNodeStateMachineTransition::has_auto_advance); + ClassDB::bind_method(D_METHOD("set_advance_condition", "name"), &AnimationNodeStateMachineTransition::set_advance_condition); + ClassDB::bind_method(D_METHOD("get_advance_condition"), &AnimationNodeStateMachineTransition::get_advance_condition); + ClassDB::bind_method(D_METHOD("set_xfade_time", "secs"), &AnimationNodeStateMachineTransition::set_xfade_time); ClassDB::bind_method(D_METHOD("get_xfade_time"), &AnimationNodeStateMachineTransition::get_xfade_time); @@ -67,6 +90,7 @@ void AnimationNodeStateMachineTransition::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "switch_mode", PROPERTY_HINT_ENUM, "Immediate,Sync,AtEnd"), "set_switch_mode", "get_switch_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "auto_advance"), "set_auto_advance", "has_auto_advance"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "advance_condition"), "set_advance_condition", "get_advance_condition"); ADD_PROPERTY(PropertyInfo(Variant::REAL, "xfade_time", PROPERTY_HINT_RANGE, "0,240,0.01"), "set_xfade_time", "get_xfade_time"); ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,32,1"), "set_priority", "get_priority"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); @@ -74,6 +98,8 @@ void AnimationNodeStateMachineTransition::_bind_methods() { BIND_ENUM_CONSTANT(SWITCH_MODE_IMMEDIATE); BIND_ENUM_CONSTANT(SWITCH_MODE_SYNC); BIND_ENUM_CONSTANT(SWITCH_MODE_AT_END); + + ADD_SIGNAL(MethodInfo("advance_condition_changed")); } AnimationNodeStateMachineTransition::AnimationNodeStateMachineTransition() { @@ -85,277 +111,241 @@ AnimationNodeStateMachineTransition::AnimationNodeStateMachineTransition() { priority = 1; } -/////////////////////////////////////////////////////// -void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<AnimationNode> p_node) { - - ERR_FAIL_COND(states.has(p_name)); - ERR_FAIL_COND(p_node.is_null()); - ERR_FAIL_COND(p_node->get_parent().is_valid()); - ERR_FAIL_COND(p_node->get_tree() != NULL); - ERR_FAIL_COND(String(p_name).find("/") != -1); - states[p_name] = p_node; +//////////////////////////////////////////////////////// - p_node->set_parent(this); - p_node->set_tree(get_tree()); +void AnimationNodeStateMachinePlayback::travel(const StringName &p_state) { - emit_changed(); + start_request_travel = true; + start_request = p_state; + stop_request = false; } -Ref<AnimationNode> AnimationNodeStateMachine::get_node(const StringName &p_name) const { - - ERR_FAIL_COND_V(!states.has(p_name), Ref<AnimationNode>()); - - return states[p_name]; +void AnimationNodeStateMachinePlayback::start(const StringName &p_state) { + start_request_travel = false; + start_request = p_state; + stop_request = false; + print_line("wants start"); } +void AnimationNodeStateMachinePlayback::stop() { -StringName AnimationNodeStateMachine::get_node_name(const Ref<AnimationNode> &p_node) const { - for (Map<StringName, Ref<AnimationRootNode> >::Element *E = states.front(); E; E = E->next()) { - if (E->get() == p_node) { - return E->key(); - } - } - - ERR_FAIL_V(StringName()); + stop_request = true; } - -bool AnimationNodeStateMachine::has_node(const StringName &p_name) const { - return states.has(p_name); +bool AnimationNodeStateMachinePlayback::is_playing() const { + return playing; +} +StringName AnimationNodeStateMachinePlayback::get_current_node() const { + return current; +} +StringName AnimationNodeStateMachinePlayback::get_blend_from_node() const { + return fading_from; +} +Vector<StringName> AnimationNodeStateMachinePlayback::get_travel_path() const { + return path; +} +float AnimationNodeStateMachinePlayback::get_current_play_pos() const { + return pos_current; +} +float AnimationNodeStateMachinePlayback::get_current_length() const { + return len_current; } -void AnimationNodeStateMachine::remove_node(const StringName &p_name) { - - ERR_FAIL_COND(!states.has(p_name)); - - { - //erase node connections - Ref<AnimationNode> node = states[p_name]; - for (int i = 0; i < node->get_input_count(); i++) { - node->set_input_connection(i, StringName()); - } - node->set_parent(NULL); - node->set_tree(NULL); - } - states.erase(p_name); - path.erase(p_name); +bool AnimationNodeStateMachinePlayback::_travel(AnimationNodeStateMachine *sm, const StringName &p_travel) { - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == p_name || transitions[i].to == p_name) { - transitions.remove(i); - i--; - } - } + ERR_FAIL_COND_V(!playing, false); + ERR_FAIL_COND_V(!sm->states.has(p_travel), false); + ERR_FAIL_COND_V(!sm->states.has(current), false); - if (start_node == p_name) { - start_node = StringName(); - } + path.clear(); //a new one will be needed - if (end_node == p_name) { - end_node = StringName(); - } + if (current == p_travel) + return true; //nothing to do - if (playing && current == p_name) { - stop(); - } - emit_changed(); -} + loops_current = 0; // reset loops, so fade does not happen immediately -void AnimationNodeStateMachine::rename_node(const StringName &p_name, const StringName &p_new_name) { + Vector2 current_pos = sm->states[current].position; + Vector2 target_pos = sm->states[p_travel].position; - ERR_FAIL_COND(!states.has(p_name)); - ERR_FAIL_COND(states.has(p_new_name)); + Map<StringName, AStarCost> cost_map; - states[p_new_name] = states[p_name]; - states.erase(p_name); + List<int> open_list; - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == p_name) { - transitions.write[i].from = p_new_name; - } + //build open list + for (int i = 0; i < sm->transitions.size(); i++) { + if (sm->transitions[i].from == current) { + open_list.push_back(i); + float cost = sm->states[sm->transitions[i].to].position.distance_to(current_pos); + cost *= sm->transitions[i].transition->get_priority(); + AStarCost ap; + ap.prev = current; + ap.distance = cost; + cost_map[sm->transitions[i].to] = ap; - if (transitions[i].to == p_name) { - transitions.write[i].to = p_new_name; + if (sm->transitions[i].to == p_travel) { //prematurely found it! :D + path.push_back(p_travel); + return true; + } } } - if (start_node == p_name) { - start_node = p_new_name; - } + //begin astar + bool found_route = false; + while (!found_route) { - if (end_node == p_name) { - end_node = p_new_name; - } + if (open_list.size() == 0) { + return false; //no path found + } - if (playing && current == p_name) { - current = p_new_name; - } + //find the last cost transition + List<int>::Element *least_cost_transition = NULL; + float least_cost = 1e20; - path.clear(); //clear path -} + for (List<int>::Element *E = open_list.front(); E; E = E->next()) { -void AnimationNodeStateMachine::get_node_list(List<StringName> *r_nodes) const { + float cost = cost_map[sm->transitions[E->get()].to].distance; + cost += sm->states[sm->transitions[E->get()].to].position.distance_to(target_pos); - List<StringName> nodes; - for (Map<StringName, Ref<AnimationRootNode> >::Element *E = states.front(); E; E = E->next()) { - nodes.push_back(E->key()); - } - nodes.sort_custom<StringName::AlphCompare>(); + if (cost < least_cost) { + least_cost_transition = E; + } + } - for (List<StringName>::Element *E = nodes.front(); E; E = E->next()) { - r_nodes->push_back(E->get()); - } -} + StringName transition_prev = sm->transitions[least_cost_transition->get()].from; + StringName transition = sm->transitions[least_cost_transition->get()].to; -bool AnimationNodeStateMachine::has_transition(const StringName &p_from, const StringName &p_to) const { + for (int i = 0; i < sm->transitions.size(); i++) { + if (sm->transitions[i].from != transition || sm->transitions[i].to == transition_prev) { + continue; //not interested on those + } - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == p_from && transitions[i].to == p_to) - return true; - } - return false; -} + float distance = sm->states[sm->transitions[i].from].position.distance_to(sm->states[sm->transitions[i].to].position); + distance *= sm->transitions[i].transition->get_priority(); + distance += cost_map[sm->transitions[i].from].distance; -int AnimationNodeStateMachine::find_transition(const StringName &p_from, const StringName &p_to) const { + if (cost_map.has(sm->transitions[i].to)) { + //oh this was visited already, can we win the cost? + if (distance < cost_map[sm->transitions[i].to].distance) { + cost_map[sm->transitions[i].to].distance = distance; + cost_map[sm->transitions[i].to].prev = sm->transitions[i].from; + } + } else { + //add to open list + AStarCost ac; + ac.prev = sm->transitions[i].from; + ac.distance = distance; + cost_map[sm->transitions[i].to] = ac; - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == p_from && transitions[i].to == p_to) - return i; - } - return -1; -} + open_list.push_back(i); -void AnimationNodeStateMachine::add_transition(const StringName &p_from, const StringName &p_to, const Ref<AnimationNodeStateMachineTransition> &p_transition) { + if (sm->transitions[i].to == p_travel) { + found_route = true; + break; + } + } + } - ERR_FAIL_COND(p_from == p_to); - ERR_FAIL_COND(!states.has(p_from)); - ERR_FAIL_COND(!states.has(p_to)); - ERR_FAIL_COND(p_transition.is_null()); + if (found_route) { + break; + } - for (int i = 0; i < transitions.size(); i++) { - ERR_FAIL_COND(transitions[i].from == p_from && transitions[i].to == p_to); + open_list.erase(least_cost_transition); } - Transition tr; - tr.from = p_from; - tr.to = p_to; - tr.transition = p_transition; - - transitions.push_back(tr); -} - -Ref<AnimationNodeStateMachineTransition> AnimationNodeStateMachine::get_transition(int p_transition) const { - ERR_FAIL_INDEX_V(p_transition, transitions.size(), Ref<AnimationNodeStateMachineTransition>()); - return transitions[p_transition].transition; -} -StringName AnimationNodeStateMachine::get_transition_from(int p_transition) const { + //make path + StringName at = p_travel; + while (at != current) { + path.push_back(at); + at = cost_map[at].prev; + } - ERR_FAIL_INDEX_V(p_transition, transitions.size(), StringName()); - return transitions[p_transition].from; -} -StringName AnimationNodeStateMachine::get_transition_to(int p_transition) const { + path.invert(); - ERR_FAIL_INDEX_V(p_transition, transitions.size(), StringName()); - return transitions[p_transition].to; + return true; } -int AnimationNodeStateMachine::get_transition_count() const { - - return transitions.size(); -} -void AnimationNodeStateMachine::remove_transition(const StringName &p_from, const StringName &p_to) { +float AnimationNodeStateMachinePlayback::process(AnimationNodeStateMachine *sm, float p_time, bool p_seek) { - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == p_from && transitions[i].to == p_to) { - transitions.remove(i); - return; + //if not playing and it can restart, then restart + if (!playing && start_request == StringName()) { + if (!stop_request && sm->start_node) { + start(sm->start_node); + } else { + return 0; } } - if (playing) { - path.clear(); - } -} - -void AnimationNodeStateMachine::remove_transition_by_index(int p_transition) { - - transitions.remove(p_transition); - if (playing) { - path.clear(); + if (playing && stop_request) { + stop_request = false; + playing = false; + return 0; } -} - -void AnimationNodeStateMachine::set_start_node(const StringName &p_node) { - - ERR_FAIL_COND(p_node != StringName() && !states.has(p_node)); - start_node = p_node; -} - -String AnimationNodeStateMachine::get_start_node() const { - - return start_node; -} - -void AnimationNodeStateMachine::set_end_node(const StringName &p_node) { - - ERR_FAIL_COND(p_node != StringName() && !states.has(p_node)); - end_node = p_node; -} - -String AnimationNodeStateMachine::get_end_node() const { - - return end_node; -} -void AnimationNodeStateMachine::set_graph_offset(const Vector2 &p_offset) { - graph_offset = p_offset; -} + bool play_start = false; -Vector2 AnimationNodeStateMachine::get_graph_offset() const { - return graph_offset; -} + if (start_request != StringName()) { -float AnimationNodeStateMachine::process(float p_time, bool p_seek) { + if (start_request_travel) { + if (!playing) { + start_request = StringName(); + ERR_EXPLAIN("Can't travel to '" + String(start_request) + "' if state machine is not active."); + ERR_FAIL_V(0); + } - //if not playing and it can restart, then restart - if (!playing) { - if (start_node) { - start(start_node); + if (!_travel(sm, start_request)) { + //cant travel, then teleport + path.clear(); + current = start_request; + } } else { - return 0; + path.clear(); + current = start_request; + playing = true; + play_start = true; } + + start_request = StringName(); //clear start request } bool do_start = (p_seek && p_time == 0) || play_start || current == StringName(); if (do_start) { - if (start_node != StringName() && p_seek && p_time == 0) { - current = start_node; + if (sm->start_node != StringName() && p_seek && p_time == 0) { + current = sm->start_node; } - len_current = blend_node(states[current], 0, true, 1.0, FILTER_IGNORE, false); + len_current = sm->blend_node(current, sm->states[current].node, 0, true, 1.0, AnimationNode::FILTER_IGNORE, false); pos_current = 0; loops_current = 0; play_start = false; } + if (!sm->states.has(current)) { + playing = false; //current does not exist + current = StringName(); + return 0; + } float fade_blend = 1.0; if (fading_from != StringName()) { - if (!p_seek) { - fading_pos += p_time; - } - fade_blend = MIN(1.0, fading_pos / fading_time); - if (fade_blend >= 1.0) { + if (!sm->states.has(fading_from)) { fading_from = StringName(); + } else { + if (!p_seek) { + fading_pos += p_time; + } + fade_blend = MIN(1.0, fading_pos / fading_time); + if (fade_blend >= 1.0) { + fading_from = StringName(); + } } } - float rem = blend_node(states[current], p_time, p_seek, fade_blend, FILTER_IGNORE, false); + float rem = sm->blend_node(current, sm->states[current].node, p_time, p_seek, fade_blend, AnimationNode::FILTER_IGNORE, false); if (fading_from != StringName()) { - blend_node(states[fading_from], p_time, p_seek, 1.0 - fade_blend, FILTER_IGNORE, false); + sm->blend_node(current, sm->states[fading_from].node, p_time, p_seek, 1.0 - fade_blend, AnimationNode::FILTER_IGNORE, false); } //guess playback position @@ -380,29 +370,39 @@ float AnimationNodeStateMachine::process(float p_time, bool p_seek) { if (path.size()) { - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == current && transitions[i].to == path[0]) { - next_xfade = transitions[i].transition->get_xfade_time(); - switch_mode = transitions[i].transition->get_switch_mode(); + for (int i = 0; i < sm->transitions.size(); i++) { + if (sm->transitions[i].from == current && sm->transitions[i].to == path[0]) { + next_xfade = sm->transitions[i].transition->get_xfade_time(); + switch_mode = sm->transitions[i].transition->get_switch_mode(); next = path[0]; } } } else { float priority_best = 1e20; int auto_advance_to = -1; - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == current && transitions[i].transition->has_auto_advance()) { + for (int i = 0; i < sm->transitions.size(); i++) { + + bool auto_advance = false; + if (sm->transitions[i].transition->has_auto_advance()) { + auto_advance = true; + } + StringName advance_condition_name = sm->transitions[i].transition->get_advance_condition_name(); + if (advance_condition_name != StringName() && bool(sm->get_parameter(advance_condition_name))) { + auto_advance = true; + } + + if (sm->transitions[i].from == current && sm->transitions[i].transition->has_auto_advance()) { - if (transitions[i].transition->get_priority() < priority_best) { + if (sm->transitions[i].transition->get_priority() < priority_best) { auto_advance_to = i; } } } if (auto_advance_to != -1) { - next = transitions[auto_advance_to].to; - next_xfade = transitions[auto_advance_to].transition->get_xfade_time(); - switch_mode = transitions[auto_advance_to].transition->get_switch_mode(); + next = sm->transitions[auto_advance_to].to; + next_xfade = sm->transitions[auto_advance_to].transition->get_xfade_time(); + switch_mode = sm->transitions[auto_advance_to].transition->get_switch_mode(); } } @@ -437,12 +437,12 @@ float AnimationNodeStateMachine::process(float p_time, bool p_seek) { } current = next; if (switch_mode == AnimationNodeStateMachineTransition::SWITCH_MODE_SYNC) { - len_current = blend_node(states[current], 0, true, 0, FILTER_IGNORE, false); + len_current = sm->blend_node(current, sm->states[current].node, 0, true, 0, AnimationNode::FILTER_IGNORE, false); pos_current = MIN(pos_current, len_current); - blend_node(states[current], pos_current, true, 0, FILTER_IGNORE, false); + sm->blend_node(current, sm->states[current].node, pos_current, true, 0, AnimationNode::FILTER_IGNORE, false); } else { - len_current = blend_node(states[current], 0, true, 0, FILTER_IGNORE, false); + len_current = sm->blend_node(current, sm->states[current].node, 0, true, 0, AnimationNode::FILTER_IGNORE, false); pos_current = 0; } @@ -453,169 +453,321 @@ float AnimationNodeStateMachine::process(float p_time, bool p_seek) { //compute time left for transitions by using the end node - if (end_node != StringName() && end_node != current) { + if (sm->end_node != StringName() && sm->end_node != current) { - rem = blend_node(states[end_node], 0, true, 0, FILTER_IGNORE, false); + rem = sm->blend_node(current, sm->states[sm->end_node].node, 0, true, 0, AnimationNode::FILTER_IGNORE, false); } return rem; } -bool AnimationNodeStateMachine::travel(const StringName &p_state) { - ERR_FAIL_COND_V(!playing, false); - ERR_FAIL_COND_V(!states.has(p_state), false); - ERR_FAIL_COND_V(!states.has(current), false); +void AnimationNodeStateMachinePlayback::_bind_methods() { - path.clear(); //a new one will be needed + ClassDB::bind_method(D_METHOD("travel", "to_node"), &AnimationNodeStateMachinePlayback::travel); + ClassDB::bind_method(D_METHOD("start", "node"), &AnimationNodeStateMachinePlayback::start); + ClassDB::bind_method(D_METHOD("stop"), &AnimationNodeStateMachinePlayback::stop); + ClassDB::bind_method(D_METHOD("is_playing"), &AnimationNodeStateMachinePlayback::is_playing); + ClassDB::bind_method(D_METHOD("get_current_node"), &AnimationNodeStateMachinePlayback::get_current_node); + ClassDB::bind_method(D_METHOD("get_travel_path"), &AnimationNodeStateMachinePlayback::get_travel_path); +} - if (current == p_state) - return true; //nothing to do +AnimationNodeStateMachinePlayback::AnimationNodeStateMachinePlayback() { + set_local_to_scene(true); //only one per instanced scene - loops_current = 0; // reset loops, so fade does not happen immediately + playing = false; + len_current = 0; + fading_time = 0; + stop_request = false; +} - Vector2 current_pos = states[current]->get_position(); - Vector2 target_pos = states[p_state]->get_position(); +/////////////////////////////////////////////////////// - Map<StringName, AStarCost> cost_map; +void AnimationNodeStateMachine::get_parameter_list(List<PropertyInfo> *r_list) const { + r_list->push_back(PropertyInfo(Variant::OBJECT, playback, PROPERTY_HINT_RESOURCE_TYPE, "AnimationNodeStateMachinePlayback", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); + List<StringName> advance_conditions; + for (int i = 0; i < transitions.size(); i++) { + StringName ac = transitions[i].transition->get_advance_condition_name(); + if (ac != StringName() && advance_conditions.find(ac) == NULL) { + advance_conditions.push_back(ac); + } + } - List<int> open_list; + advance_conditions.sort_custom<StringName::AlphCompare>(); + for (List<StringName>::Element *E = advance_conditions.front(); E; E = E->next()) { + r_list->push_back(PropertyInfo(Variant::BOOL, E->get())); + } +} - //build open list - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from == current) { - open_list.push_back(i); - float cost = states[transitions[i].to]->get_position().distance_to(current_pos); - cost *= transitions[i].transition->get_priority(); - AStarCost ap; - ap.prev = current; - ap.distance = cost; - cost_map[transitions[i].to] = ap; +Variant AnimationNodeStateMachine::get_parameter_default_value(const StringName &p_parameter) const { - if (transitions[i].to == p_state) { //prematurely found it! :D - path.push_back(p_state); - return true; - } - } + if (p_parameter == playback) { + Ref<AnimationNodeStateMachinePlayback> p; + p.instance(); + return p; + } else { + return false; //advance condition } +} - //begin astar - bool found_route = false; - while (!found_route) { +void AnimationNodeStateMachine::add_node(const StringName &p_name, Ref<AnimationNode> p_node, const Vector2 &p_position) { - if (open_list.size() == 0) { - return false; //no path found + ERR_FAIL_COND(states.has(p_name)); + ERR_FAIL_COND(p_node.is_null()); + ERR_FAIL_COND(String(p_name).find("/") != -1); + + State state; + state.node = p_node; + state.position = p_position; + + states[p_name] = state; + + emit_changed(); + emit_signal("tree_changed"); + + p_node->connect("tree_changed", this, "_tree_changed", varray(), CONNECT_REFERENCE_COUNTED); +} + +Ref<AnimationNode> AnimationNodeStateMachine::get_node(const StringName &p_name) const { + + ERR_FAIL_COND_V(!states.has(p_name), Ref<AnimationNode>()); + + return states[p_name].node; +} + +StringName AnimationNodeStateMachine::get_node_name(const Ref<AnimationNode> &p_node) const { + for (Map<StringName, State>::Element *E = states.front(); E; E = E->next()) { + if (E->get().node == p_node) { + return E->key(); } + } - //find the last cost transition - List<int>::Element *least_cost_transition = NULL; - float least_cost = 1e20; + ERR_FAIL_V(StringName()); +} - for (List<int>::Element *E = open_list.front(); E; E = E->next()) { +void AnimationNodeStateMachine::get_child_nodes(List<ChildNode> *r_child_nodes) { + Vector<StringName> nodes; - float cost = cost_map[transitions[E->get()].to].distance; - cost += states[transitions[E->get()].to]->get_position().distance_to(target_pos); + for (Map<StringName, State>::Element *E = states.front(); E; E = E->next()) { + nodes.push_back(E->key()); + } - if (cost < least_cost) { - least_cost_transition = E; - } + nodes.sort_custom<StringName::AlphCompare>(); + + for (int i = 0; i < nodes.size(); i++) { + ChildNode cn; + cn.name = nodes[i]; + cn.node = states[cn.name].node; + r_child_nodes->push_back(cn); + } +} + +bool AnimationNodeStateMachine::has_node(const StringName &p_name) const { + return states.has(p_name); +} +void AnimationNodeStateMachine::remove_node(const StringName &p_name) { + + ERR_FAIL_COND(!states.has(p_name)); + + { + Ref<AnimationNode> node = states[p_name].node; + node->disconnect("tree_changed", this, "_tree_changed"); + } + + states.erase(p_name); + //path.erase(p_name); + + for (int i = 0; i < transitions.size(); i++) { + if (transitions[i].from == p_name || transitions[i].to == p_name) { + transitions.write[i].transition->disconnect("advance_condition_changed", this, "_tree_changed"); + transitions.remove(i); + i--; } + } - StringName transition_prev = transitions[least_cost_transition->get()].from; - StringName transition = transitions[least_cost_transition->get()].to; + if (start_node == p_name) { + start_node = StringName(); + } - for (int i = 0; i < transitions.size(); i++) { - if (transitions[i].from != transition || transitions[i].to == transition_prev) { - continue; //not interested on those - } + if (end_node == p_name) { + end_node = StringName(); + } - float distance = states[transitions[i].from]->get_position().distance_to(states[transitions[i].to]->get_position()); - distance *= transitions[i].transition->get_priority(); - distance += cost_map[transitions[i].from].distance; + /*if (playing && current == p_name) { + stop(); + }*/ - if (cost_map.has(transitions[i].to)) { - //oh this was visited already, can we win the cost? - if (distance < cost_map[transitions[i].to].distance) { - cost_map[transitions[i].to].distance = distance; - cost_map[transitions[i].to].prev = transitions[i].from; - } - } else { - //add to open list - AStarCost ac; - ac.prev = transitions[i].from; - ac.distance = distance; - cost_map[transitions[i].to] = ac; + emit_changed(); + emit_signal("tree_changed"); +} - open_list.push_back(i); +void AnimationNodeStateMachine::rename_node(const StringName &p_name, const StringName &p_new_name) { - if (transitions[i].to == p_state) { - found_route = true; - break; - } - } + ERR_FAIL_COND(!states.has(p_name)); + ERR_FAIL_COND(states.has(p_new_name)); + + states[p_new_name] = states[p_name]; + states.erase(p_name); + + for (int i = 0; i < transitions.size(); i++) { + if (transitions[i].from == p_name) { + transitions.write[i].from = p_new_name; } - if (found_route) { - break; + if (transitions[i].to == p_name) { + transitions.write[i].to = p_new_name; } + } - open_list.erase(least_cost_transition); + if (start_node == p_name) { + start_node = p_new_name; } - //make path - StringName at = p_state; - while (at != current) { - path.push_back(at); - at = cost_map[at].prev; + if (end_node == p_name) { + end_node = p_new_name; } - path.invert(); + /*if (playing && current == p_name) { + current = p_new_name; + }*/ - return true; + //path.clear(); //clear path + emit_signal("tree_changed"); } -void AnimationNodeStateMachine::start(const StringName &p_state) { +void AnimationNodeStateMachine::get_node_list(List<StringName> *r_nodes) const { - ERR_FAIL_COND(!states.has(p_state)); - path.clear(); - current = p_state; - playing = true; - play_start = true; + List<StringName> nodes; + for (Map<StringName, State>::Element *E = states.front(); E; E = E->next()) { + nodes.push_back(E->key()); + } + nodes.sort_custom<StringName::AlphCompare>(); + + for (List<StringName>::Element *E = nodes.front(); E; E = E->next()) { + r_nodes->push_back(E->get()); + } } -void AnimationNodeStateMachine::stop() { - playing = false; - play_start = false; - current = StringName(); + +bool AnimationNodeStateMachine::has_transition(const StringName &p_from, const StringName &p_to) const { + + for (int i = 0; i < transitions.size(); i++) { + if (transitions[i].from == p_from && transitions[i].to == p_to) + return true; + } + return false; } -bool AnimationNodeStateMachine::is_playing() const { - return playing; +int AnimationNodeStateMachine::find_transition(const StringName &p_from, const StringName &p_to) const { + + for (int i = 0; i < transitions.size(); i++) { + if (transitions[i].from == p_from && transitions[i].to == p_to) + return i; + } + return -1; } -StringName AnimationNodeStateMachine::get_current_node() const { - if (!playing) { - return StringName(); + +void AnimationNodeStateMachine::add_transition(const StringName &p_from, const StringName &p_to, const Ref<AnimationNodeStateMachineTransition> &p_transition) { + + ERR_FAIL_COND(p_from == p_to); + ERR_FAIL_COND(!states.has(p_from)); + ERR_FAIL_COND(!states.has(p_to)); + ERR_FAIL_COND(p_transition.is_null()); + + for (int i = 0; i < transitions.size(); i++) { + ERR_FAIL_COND(transitions[i].from == p_from && transitions[i].to == p_to); } - return current; + Transition tr; + tr.from = p_from; + tr.to = p_to; + tr.transition = p_transition; + + tr.transition->connect("advance_condition_changed", this, "_tree_changed", varray(), CONNECT_REFERENCE_COUNTED); + + transitions.push_back(tr); +} + +Ref<AnimationNodeStateMachineTransition> AnimationNodeStateMachine::get_transition(int p_transition) const { + ERR_FAIL_INDEX_V(p_transition, transitions.size(), Ref<AnimationNodeStateMachineTransition>()); + return transitions[p_transition].transition; } +StringName AnimationNodeStateMachine::get_transition_from(int p_transition) const { + + ERR_FAIL_INDEX_V(p_transition, transitions.size(), StringName()); + return transitions[p_transition].from; +} +StringName AnimationNodeStateMachine::get_transition_to(int p_transition) const { + + ERR_FAIL_INDEX_V(p_transition, transitions.size(), StringName()); + return transitions[p_transition].to; +} + +int AnimationNodeStateMachine::get_transition_count() const { + + return transitions.size(); +} +void AnimationNodeStateMachine::remove_transition(const StringName &p_from, const StringName &p_to) { -StringName AnimationNodeStateMachine::get_blend_from_node() const { - if (!playing) { - return StringName(); + for (int i = 0; i < transitions.size(); i++) { + if (transitions[i].from == p_from && transitions[i].to == p_to) { + transitions.write[i].transition->disconnect("advance_condition_changed", this, "_tree_changed"); + transitions.remove(i); + return; + } } - return fading_from; + /*if (playing) { + path.clear(); + }*/ } -float AnimationNodeStateMachine::get_current_play_pos() const { - return pos_current; +void AnimationNodeStateMachine::remove_transition_by_index(int p_transition) { + + ERR_FAIL_INDEX(p_transition, transitions.size()); + transitions.write[p_transition].transition->disconnect("advance_condition_changed", this, "_tree_changed"); + transitions.remove(p_transition); + /*if (playing) { + path.clear(); + }*/ } -float AnimationNodeStateMachine::get_current_length() const { - return len_current; + +void AnimationNodeStateMachine::set_start_node(const StringName &p_node) { + + ERR_FAIL_COND(p_node != StringName() && !states.has(p_node)); + start_node = p_node; } -Vector<StringName> AnimationNodeStateMachine::get_travel_path() const { - return path; +String AnimationNodeStateMachine::get_start_node() const { + + return start_node; +} + +void AnimationNodeStateMachine::set_end_node(const StringName &p_node) { + + ERR_FAIL_COND(p_node != StringName() && !states.has(p_node)); + end_node = p_node; +} + +String AnimationNodeStateMachine::get_end_node() const { + + return end_node; +} + +void AnimationNodeStateMachine::set_graph_offset(const Vector2 &p_offset) { + graph_offset = p_offset; +} + +Vector2 AnimationNodeStateMachine::get_graph_offset() const { + return graph_offset; +} + +float AnimationNodeStateMachine::process(float p_time, bool p_seek) { + + Ref<AnimationNodeStateMachinePlayback> playback = get_parameter(this->playback); + ERR_FAIL_COND_V(playback.is_null(), 0.0); + + return playback->process(this, p_time, p_seek); } + String AnimationNodeStateMachine::get_caption() const { return "StateMachine"; } @@ -623,14 +775,8 @@ String AnimationNodeStateMachine::get_caption() const { void AnimationNodeStateMachine::_notification(int p_what) { } -void AnimationNodeStateMachine::set_tree(AnimationTree *p_player) { - - AnimationNode::set_tree(p_player); - - for (Map<StringName, Ref<AnimationRootNode> >::Element *E = states.front(); E; E = E->next()) { - Ref<AnimationRootNode> node = E->get(); - node->set_tree(p_player); - } +Ref<AnimationNode> AnimationNodeStateMachine::get_child_by_name(const StringName &p_name) { + return get_node(p_name); } bool AnimationNodeStateMachine::_set(const StringName &p_name, const Variant &p_value) { @@ -651,7 +797,7 @@ bool AnimationNodeStateMachine::_set(const StringName &p_name, const Variant &p_ if (what == "position") { if (states.has(node_name)) { - states[node_name]->set_position(p_value); + states[node_name].position = p_value; } return true; } @@ -687,7 +833,7 @@ bool AnimationNodeStateMachine::_get(const StringName &p_name, Variant &r_ret) c if (what == "node") { if (states.has(node_name)) { - r_ret = states[node_name]; + r_ret = states[node_name].node; return true; } } @@ -695,7 +841,7 @@ bool AnimationNodeStateMachine::_get(const StringName &p_name, Variant &r_ret) c if (what == "position") { if (states.has(node_name)) { - r_ret = states[node_name]->get_position(); + r_ret = states[node_name].position; return true; } } @@ -727,14 +873,14 @@ bool AnimationNodeStateMachine::_get(const StringName &p_name, Variant &r_ret) c void AnimationNodeStateMachine::_get_property_list(List<PropertyInfo> *p_list) const { List<StringName> names; - for (Map<StringName, Ref<AnimationRootNode> >::Element *E = states.front(); E; E = E->next()) { + for (Map<StringName, State>::Element *E = states.front(); E; E = E->next()) { names.push_back(E->key()); } names.sort_custom<StringName::AlphCompare>(); for (List<StringName>::Element *E = names.front(); E; E = E->next()) { String name = E->get(); - p_list->push_back(PropertyInfo(Variant::OBJECT, "states/" + name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "states/" + name + "/node", PROPERTY_HINT_RESOURCE_TYPE, "AnimationNode", PROPERTY_USAGE_NOEDITOR)); p_list->push_back(PropertyInfo(Variant::VECTOR2, "states/" + name + "/position", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } @@ -744,15 +890,33 @@ void AnimationNodeStateMachine::_get_property_list(List<PropertyInfo> *p_list) c p_list->push_back(PropertyInfo(Variant::VECTOR2, "graph_offset", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } +void AnimationNodeStateMachine::set_node_position(const StringName &p_name, const Vector2 &p_position) { + ERR_FAIL_COND(!states.has(p_name)); + states[p_name].position = p_position; +} + +Vector2 AnimationNodeStateMachine::get_node_position(const StringName &p_name) const { + + ERR_FAIL_COND_V(!states.has(p_name), Vector2()); + return states[p_name].position; +} + +void AnimationNodeStateMachine::_tree_changed() { + emit_signal("tree_changed"); +} + void AnimationNodeStateMachine::_bind_methods() { - ClassDB::bind_method(D_METHOD("add_node", "name", "node"), &AnimationNodeStateMachine::add_node); + ClassDB::bind_method(D_METHOD("add_node", "name", "node", "position"), &AnimationNodeStateMachine::add_node, DEFVAL(Vector2())); ClassDB::bind_method(D_METHOD("get_node", "name"), &AnimationNodeStateMachine::get_node); ClassDB::bind_method(D_METHOD("remove_node", "name"), &AnimationNodeStateMachine::remove_node); ClassDB::bind_method(D_METHOD("rename_node", "name", "new_name"), &AnimationNodeStateMachine::rename_node); ClassDB::bind_method(D_METHOD("has_node", "name"), &AnimationNodeStateMachine::has_node); ClassDB::bind_method(D_METHOD("get_node_name", "node"), &AnimationNodeStateMachine::get_node_name); + ClassDB::bind_method(D_METHOD("set_node_position", "name", "position"), &AnimationNodeStateMachine::set_node_position); + ClassDB::bind_method(D_METHOD("get_node_position", "name"), &AnimationNodeStateMachine::get_node_position); + ClassDB::bind_method(D_METHOD("has_transition", "from", "to"), &AnimationNodeStateMachine::add_transition); ClassDB::bind_method(D_METHOD("add_transition", "from", "to", "transition"), &AnimationNodeStateMachine::add_transition); ClassDB::bind_method(D_METHOD("get_transition", "idx"), &AnimationNodeStateMachine::get_transition); @@ -771,20 +935,10 @@ void AnimationNodeStateMachine::_bind_methods() { ClassDB::bind_method(D_METHOD("set_graph_offset", "name"), &AnimationNodeStateMachine::set_graph_offset); ClassDB::bind_method(D_METHOD("get_graph_offset"), &AnimationNodeStateMachine::get_graph_offset); - ClassDB::bind_method(D_METHOD("travel", "to_node"), &AnimationNodeStateMachine::travel); - ClassDB::bind_method(D_METHOD("start", "node"), &AnimationNodeStateMachine::start); - ClassDB::bind_method(D_METHOD("stop"), &AnimationNodeStateMachine::stop); - ClassDB::bind_method(D_METHOD("is_playing"), &AnimationNodeStateMachine::is_playing); - ClassDB::bind_method(D_METHOD("get_current_node"), &AnimationNodeStateMachine::get_current_node); - ClassDB::bind_method(D_METHOD("get_travel_path"), &AnimationNodeStateMachine::get_travel_path); + ClassDB::bind_method(D_METHOD("_tree_changed"), &AnimationNodeStateMachine::_tree_changed); } AnimationNodeStateMachine::AnimationNodeStateMachine() { - play_start = false; - - playing = false; - len_current = 0; - - fading_time = 0; + playback = "playback"; } diff --git a/scene/animation/animation_node_state_machine.h b/scene/animation/animation_node_state_machine.h index e7357e09ea..5d633d6334 100644 --- a/scene/animation/animation_node_state_machine.h +++ b/scene/animation/animation_node_state_machine.h @@ -15,6 +15,8 @@ public: private: SwitchMode switch_mode; bool auto_advance; + StringName advance_condition; + StringName advance_condition_name; float xfade; bool disabled; int priority; @@ -29,6 +31,11 @@ public: void set_auto_advance(bool p_enable); bool has_auto_advance() const; + void set_advance_condition(const StringName &p_condition); + StringName get_advance_condition() const; + + StringName get_advance_condition_name() const; + void set_xfade_time(float p_xfade); float get_xfade_time() const; @@ -43,39 +50,24 @@ public: VARIANT_ENUM_CAST(AnimationNodeStateMachineTransition::SwitchMode) -class AnimationNodeStateMachine : public AnimationRootNode { - - GDCLASS(AnimationNodeStateMachine, AnimationRootNode); +class AnimationNodeStateMachine; -private: - Map<StringName, Ref<AnimationRootNode> > states; +class AnimationNodeStateMachinePlayback : public Resource { + GDCLASS(AnimationNodeStateMachinePlayback, Resource); - struct Transition { - - StringName from; - StringName to; - Ref<AnimationNodeStateMachineTransition> transition; - }; + friend class AnimationNodeStateMachine; struct AStarCost { float distance; StringName prev; }; - Vector<Transition> transitions; - float len_total; float len_current; float pos_current; int loops_current; - bool play_start; - StringName start_node; - StringName end_node; - - Vector2 graph_offset; - StringName current; StringName fading_from; @@ -85,6 +77,63 @@ private: Vector<StringName> path; bool playing; + StringName start_request; + bool start_request_travel; + bool stop_request; + + bool _travel(AnimationNodeStateMachine *p_state_machine, const StringName &p_travel); + + float process(AnimationNodeStateMachine *p_state_machine, float p_time, bool p_seek); + +protected: + static void _bind_methods(); + +public: + void travel(const StringName &p_state); + void start(const StringName &p_state); + void stop(); + bool is_playing() const; + StringName get_current_node() const; + StringName get_blend_from_node() const; + Vector<StringName> get_travel_path() const; + float get_current_play_pos() const; + float get_current_length() const; + + AnimationNodeStateMachinePlayback(); +}; + +class AnimationNodeStateMachine : public AnimationRootNode { + + GDCLASS(AnimationNodeStateMachine, AnimationRootNode); + +private: + friend class AnimationNodeStateMachinePlayback; + + struct State { + Ref<AnimationRootNode> node; + Vector2 position; + }; + + Map<StringName, State> states; + + struct Transition { + + StringName from; + StringName to; + Ref<AnimationNodeStateMachineTransition> transition; + }; + + Vector<Transition> transitions; + + StringName playback; + + StringName start_node; + StringName end_node; + + Vector2 graph_offset; + + void _tree_changed(); + protected: void _notification(int p_what); static void _bind_methods(); @@ -94,7 +143,10 @@ protected: void _get_property_list(List<PropertyInfo> *p_list) const; public: - void add_node(const StringName &p_name, Ref<AnimationNode> p_node); + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; + + void add_node(const StringName &p_name, Ref<AnimationNode> p_node, const Vector2 &p_position = Vector2()); 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); @@ -102,6 +154,11 @@ public: StringName get_node_name(const Ref<AnimationNode> &p_node) const; void get_node_list(List<StringName> *r_nodes) const; + void set_node_position(const StringName &p_name, const Vector2 &p_position); + Vector2 get_node_position(const StringName &p_name) const; + + virtual void get_child_nodes(List<ChildNode> *r_child_nodes); + bool has_transition(const StringName &p_from, const StringName &p_to) const; int find_transition(const StringName &p_from, const StringName &p_to) const; void add_transition(const StringName &p_from, const StringName &p_to, const Ref<AnimationNodeStateMachineTransition> &p_transition); @@ -124,17 +181,7 @@ public: virtual float process(float p_time, bool p_seek); virtual String get_caption() const; - bool travel(const StringName &p_state); - void start(const StringName &p_state); - void stop(); - bool is_playing() const; - StringName get_current_node() const; - StringName get_blend_from_node() const; - Vector<StringName> get_travel_path() const; - float get_current_play_pos() const; - float get_current_length() const; - - virtual void set_tree(AnimationTree *p_player); + virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name); AnimationNodeStateMachine(); }; diff --git a/scene/animation/animation_tree.cpp b/scene/animation/animation_tree.cpp index 8bbc05eed3..68b0602c67 100644 --- a/scene/animation/animation_tree.cpp +++ b/scene/animation/animation_tree.cpp @@ -5,6 +5,38 @@ #include "scene/scene_string_names.h" #include "servers/audio/audio_stream.h" + + +void AnimationNode::get_parameter_list(List<PropertyInfo> *r_list) const { + +} + +Variant AnimationNode::get_parameter_default_value(const StringName &p_parameter) const { + return Variant(); +} + +void AnimationNode::set_parameter(const StringName& p_name, const Variant& p_value) { + ERR_FAIL_COND(!state); + ERR_FAIL_COND(!state->tree->property_parent_map.has(base_path)); + ERR_FAIL_COND(!state->tree->property_parent_map[base_path].has(p_name)); + StringName path = state->tree->property_parent_map[base_path][p_name]; + + state->tree->property_map[path]=p_value; +} + +Variant AnimationNode::get_parameter(const StringName& p_name) const { + ERR_FAIL_COND_V(!state,Variant()); + ERR_FAIL_COND_V(!state->tree->property_parent_map.has(base_path),Variant()); + ERR_FAIL_COND_V(!state->tree->property_parent_map[base_path].has(p_name),Variant()); + + StringName path = state->tree->property_parent_map[base_path][p_name]; + return state->tree->property_map[path]; +} + +void AnimationNode::get_child_nodes(List<ChildNode> *r_child_nodes) { + +} + void AnimationNode::blend_animation(const StringName &p_animation, float p_time, float p_delta, bool p_seeked, float p_blend) { ERR_FAIL_COND(!state); @@ -14,8 +46,8 @@ void AnimationNode::blend_animation(const StringName &p_animation, float p_time, if (animation.is_null()) { - Ref<AnimationNodeBlendTree> btree = get_parent(); - if (btree.is_valid()) { + AnimationNodeBlendTree* btree = Object::cast_to<AnimationNodeBlendTree>(parent); + if (btree) { String name = btree->get_node_name(Ref<AnimationNodeAnimation>(this)); make_invalid(vformat(RTR("In node '%s', invalid animation: '%s'."), name, p_animation)); } else { @@ -37,10 +69,20 @@ void AnimationNode::blend_animation(const StringName &p_animation, float p_time, state->animation_states.push_back(anim_state); } -float AnimationNode::_pre_process(State *p_state, float p_time, bool p_seek) { +float AnimationNode::_pre_process(const StringName& p_base_path, AnimationNode *p_parent, State *p_state, float p_time, bool p_seek, const Vector<StringName>& p_connections) { + + base_path = p_base_path; + parent = p_parent; + connections=p_connections; state = p_state; + float t = process(p_time, p_seek); + state = NULL; + parent = NULL; + base_path = StringName(); + connections.clear(); + return t; } @@ -56,39 +98,31 @@ void AnimationNode::make_invalid(const String &p_reason) { float AnimationNode::blend_input(int p_input, float p_time, bool p_seek, float p_blend, FilterAction p_filter, bool p_optimize) { ERR_FAIL_INDEX_V(p_input, inputs.size(), 0); ERR_FAIL_COND_V(!state, 0); - ERR_FAIL_COND_V(!get_tree(), 0); //should not happen, but used to catch bugs - - Ref<AnimationNodeBlendTree> tree = get_parent(); - - if (!tree.is_valid() && get_tree()->get_tree_root().ptr() != this) { - make_invalid(RTR("Can't blend input because node is not in a tree")); - return 0; - } - ERR_FAIL_COND_V(!tree.is_valid(), 0); //should not happen + AnimationNodeBlendTree* blend_tree = Object::cast_to<AnimationNodeBlendTree>(parent); + ERR_FAIL_COND_V(!blend_tree,0); - StringName anim_name = inputs[p_input].connected_to; + StringName node_name = connections[p_input]; - Ref<AnimationNode> node = tree->get_node(anim_name); - - if (node.is_null()) { - - String name = tree->get_node_name(Ref<AnimationNodeAnimation>(this)); + if (!blend_tree->has_node(node_name)) { + String name = blend_tree->get_node_name(Ref<AnimationNode>(this)); make_invalid(vformat(RTR("Nothing connected to input '%s' of node '%s'."), get_input_name(p_input), name)); return 0; } - inputs.write[p_input].last_pass = state->last_pass; + Ref<AnimationNode> node = blend_tree->get_node(node_name); - return _blend_node(node, p_time, p_seek, p_blend, p_filter, p_optimize, &inputs.write[p_input].activity); + //inputs.write[p_input].last_pass = state->last_pass; + float activity; + return _blend_node(node_name,blend_tree->get_node_connection_array(node_name),NULL,node, p_time, p_seek, p_blend, p_filter, p_optimize, &activity); } -float AnimationNode::blend_node(Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter, bool p_optimize) { +float AnimationNode::blend_node(const StringName& p_sub_path,Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter, bool p_optimize) { - return _blend_node(p_node, p_time, p_seek, p_blend, p_filter, p_optimize); + return _blend_node(p_sub_path,Vector<StringName>(),this,p_node, p_time, p_seek, p_blend, p_filter, p_optimize); } -float AnimationNode::_blend_node(Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter, bool p_optimize, float *r_max) { +float AnimationNode::_blend_node(const StringName &p_subpath, const Vector<StringName>& p_connections, AnimationNode *p_new_parent, Ref<AnimationNode> p_node, float p_time, bool p_seek, float p_blend, FilterAction p_filter, bool p_optimize, float *r_max) { ERR_FAIL_COND_V(!p_node.is_valid(), 0); ERR_FAIL_COND_V(!state, 0); @@ -189,7 +223,19 @@ float AnimationNode::_blend_node(Ref<AnimationNode> p_node, float p_time, bool p if (!p_seek && p_optimize && !any_valid) //pointless to go on, all are zero return 0; - return p_node->_pre_process(state, p_time, p_seek); + String new_path; + AnimationNode *new_parent; + + //this is the slowest part of processing, but as strings process in powers of 2, and the paths always exist, it will not result in that many allocations + if (p_new_parent) { + new_parent = p_new_parent; + new_path = String(base_path)+String(p_subpath)+"/"; + } else { + ERR_FAIL_COND_V(!parent,0); + new_parent = parent; + new_path = String(parent->base_path) + String(p_subpath)+"/"; + } + return p_node->_pre_process(new_path,new_parent,state, p_time, p_seek, p_connections); } int AnimationNode::get_input_count() const { @@ -201,28 +247,6 @@ String AnimationNode::get_input_name(int p_input) { return inputs[p_input].name; } -float AnimationNode::get_input_activity(int p_input) const { - - ERR_FAIL_INDEX_V(p_input, inputs.size(), 0); - if (!get_tree()) - return 0; - - if (get_tree()->get_last_process_pass() != inputs[p_input].last_pass) { - return 0; - } - return inputs[p_input].activity; -} -StringName AnimationNode::get_input_connection(int p_input) { - - ERR_FAIL_INDEX_V(p_input, inputs.size(), StringName()); - return inputs[p_input].connected_to; -} - -void AnimationNode::set_input_connection(int p_input, const StringName &p_connection) { - - ERR_FAIL_INDEX(p_input, inputs.size()); - inputs.write[p_input].connected_to = p_connection; -} String AnimationNode::get_caption() const { @@ -239,8 +263,6 @@ void AnimationNode::add_input(const String &p_name) { Input input; ERR_FAIL_COND(p_name.find(".") != -1 || p_name.find("/") != -1); input.name = p_name; - input.activity = 0; - input.last_pass = 0; inputs.push_back(input); emit_changed(); } @@ -258,35 +280,6 @@ void AnimationNode::remove_input(int p_index) { emit_changed(); } -void AnimationNode::_set_parent(Object *p_parent) { - set_parent(Object::cast_to<AnimationNode>(p_parent)); -} - -void AnimationNode::set_parent(AnimationNode *p_parent) { - parent = p_parent; //do not use ref because parent contains children - if (get_script_instance()) { - get_script_instance()->call("_parent_set", p_parent); - } -} - -Ref<AnimationNode> AnimationNode::get_parent() const { - if (parent) { - return Ref<AnimationNode>(parent); - } - - return Ref<AnimationNode>(); -} - -AnimationTree *AnimationNode::get_tree() const { - - return player; -} - -AnimationPlayer *AnimationNode::get_player() const { - ERR_FAIL_COND_V(!state, NULL); - return state->player; -} - float AnimationNode::process(float p_time, bool p_seek) { if (get_script_instance()) { @@ -320,21 +313,7 @@ bool AnimationNode::has_filter() const { return false; } -void AnimationNode::set_position(const Vector2 &p_position) { - position = p_position; -} - -Vector2 AnimationNode::get_position() const { - return position; -} - -void AnimationNode::set_tree(AnimationTree *p_player) { - if (player != NULL && p_player == NULL) { - emit_signal("removed_from_graph"); - } - player = p_player; -} Array AnimationNode::_get_filters() const { @@ -361,12 +340,14 @@ void AnimationNode::_validate_property(PropertyInfo &property) const { } } +Ref<AnimationNode> AnimationNode::get_child_by_name(const StringName &p_name) { + return Ref<AnimationNode>(); +} + void AnimationNode::_bind_methods() { ClassDB::bind_method(D_METHOD("get_input_count"), &AnimationNode::get_input_count); ClassDB::bind_method(D_METHOD("get_input_name", "input"), &AnimationNode::get_input_name); - ClassDB::bind_method(D_METHOD("get_input_connection", "input"), &AnimationNode::get_input_connection); - ClassDB::bind_method(D_METHOD("get_input_activity", "input"), &AnimationNode::get_input_activity); ClassDB::bind_method(D_METHOD("add_input", "name"), &AnimationNode::add_input); ClassDB::bind_method(D_METHOD("remove_input", "index"), &AnimationNode::remove_input); @@ -377,19 +358,16 @@ void AnimationNode::_bind_methods() { ClassDB::bind_method(D_METHOD("set_filter_enabled", "enable"), &AnimationNode::set_filter_enabled); ClassDB::bind_method(D_METHOD("is_filter_enabled"), &AnimationNode::is_filter_enabled); - ClassDB::bind_method(D_METHOD("set_position", "position"), &AnimationNode::set_position); - ClassDB::bind_method(D_METHOD("get_position"), &AnimationNode::get_position); ClassDB::bind_method(D_METHOD("_set_filters", "filters"), &AnimationNode::_set_filters); ClassDB::bind_method(D_METHOD("_get_filters"), &AnimationNode::_get_filters); ClassDB::bind_method(D_METHOD("blend_animation", "animation", "time", "delta", "seeked", "blend"), &AnimationNode::blend_animation); - ClassDB::bind_method(D_METHOD("blend_node", "node", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true)); + ClassDB::bind_method(D_METHOD("blend_node", "name","node", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_node, DEFVAL(FILTER_IGNORE), DEFVAL(true)); ClassDB::bind_method(D_METHOD("blend_input", "input_index", "time", "seek", "blend", "filter", "optimize"), &AnimationNode::blend_input, DEFVAL(FILTER_IGNORE), DEFVAL(true)); - ClassDB::bind_method(D_METHOD("set_parent", "parent"), &AnimationNode::_set_parent); - ClassDB::bind_method(D_METHOD("get_parent"), &AnimationNode::get_parent); - ClassDB::bind_method(D_METHOD("get_tree"), &AnimationNode::get_tree); + ClassDB::bind_method(D_METHOD("set_parameter","name","value"), &AnimationNode::set_parameter); + ClassDB::bind_method(D_METHOD("get_parameter","name"), &AnimationNode::get_parameter); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "filter_enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_filter_enabled", "is_filter_enabled"); ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "filters", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_filters", "_get_filters"); @@ -397,9 +375,11 @@ void AnimationNode::_bind_methods() { BIND_VMETHOD(MethodInfo("process", PropertyInfo(Variant::REAL, "time"), PropertyInfo(Variant::BOOL, "seek"))); BIND_VMETHOD(MethodInfo(Variant::STRING, "get_caption")); BIND_VMETHOD(MethodInfo(Variant::STRING, "has_filter")); - BIND_VMETHOD(MethodInfo("_parent_set", PropertyInfo(Variant::OBJECT, "parent"))); ADD_SIGNAL(MethodInfo("removed_from_graph")); + + ADD_SIGNAL(MethodInfo("tree_changed")); + BIND_ENUM_CONSTANT(FILTER_IGNORE); BIND_ENUM_CONSTANT(FILTER_PASS); BIND_ENUM_CONSTANT(FILTER_STOP); @@ -410,8 +390,6 @@ AnimationNode::AnimationNode() { state = NULL; parent = NULL; - player = NULL; - set_local_to_scene(true); filter_enabled = false; } @@ -420,18 +398,17 @@ AnimationNode::AnimationNode() { void AnimationTree::set_tree_root(const Ref<AnimationNode> &p_root) { if (root.is_valid()) { - root->set_tree(NULL); - } - if (p_root.is_valid()) { - ERR_EXPLAIN("root node already set to another player"); - ERR_FAIL_COND(p_root->player); + root->disconnect("tree_changed",this,"_tree_changed"); } + root = p_root; if (root.is_valid()) { - root->set_tree(this); + root->connect("tree_changed",this,"_tree_changed"); } + properties_dirty=true; + update_configuration_warning(); } @@ -699,7 +676,10 @@ void AnimationTree::_clear_caches() { void AnimationTree::_process_graph(float p_delta) { + _update_properties(); //if properties need updating, update them + //check all tracks, see if they need modification + root_motion_transform = Transform(); if (!root.is_valid()) { @@ -741,6 +721,7 @@ void AnimationTree::_process_graph(float p_delta) { state.valid = true; state.player = player; state.last_pass = process_pass; + state.tree = this; // root source blends @@ -757,11 +738,11 @@ void AnimationTree::_process_graph(float p_delta) { if (started) { //if started, seek - root->_pre_process(&state, 0, true); + root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path,NULL,&state, 0, true,Vector<StringName>()); started = false; } - root->_pre_process(&state, p_delta, false); + root->_pre_process(SceneStringNames::get_singleton()->parameters_base_path,NULL,&state, p_delta, false,Vector<StringName>()); } if (!state.valid) { @@ -1297,6 +1278,117 @@ Transform AnimationTree::get_root_motion_transform() const { return root_motion_transform; } +void AnimationTree::_tree_changed() { + if (properties_dirty) { + return; + } + + call_deferred("_update_properties"); + properties_dirty=true; +} + +void AnimationTree::_update_properties_for_node(const String& p_base_path,Ref<AnimationNode> node) { + + if (!property_parent_map.has(p_base_path)) { + property_parent_map[p_base_path]=HashMap<StringName, StringName>(); + } + + List<PropertyInfo> plist; + node->get_parameter_list(&plist); + for(List<PropertyInfo>::Element *E=plist.front();E;E=E->next()) { + PropertyInfo pinfo = E->get(); + + StringName key = pinfo.name; + + if (!property_map.has(p_base_path +key)) { + property_map[p_base_path + key] = node->get_parameter_default_value(key); + } + + property_parent_map[p_base_path][key]=p_base_path+key; + + pinfo.name = p_base_path + key; + properties.push_back(pinfo); + } + + List<AnimationNode::ChildNode> children; + node->get_child_nodes(&children); + + for (List<AnimationNode::ChildNode>::Element *E=children.front();E;E=E->next()) { + _update_properties_for_node(p_base_path+E->get().name+"/",E->get().node); + } +} + +void AnimationTree::_update_properties() { + if (!properties_dirty) { + return; + } + + properties.clear(); + property_parent_map.clear(); + + if (root.is_valid()) { + _update_properties_for_node(SceneStringNames::get_singleton()->parameters_base_path,root); + } + + properties_dirty = false; + + _change_notify(); +} + +bool AnimationTree::_set(const StringName &p_name, const Variant &p_value) { + if (properties_dirty) { + _update_properties(); + } + + if (property_map.has(p_name)) { + property_map[p_name]=p_value; +#ifdef TOOLS_ENABLED + _change_notify(p_name.operator String().utf8().get_data()); +#endif + return true; + } + + return false; +} + +bool AnimationTree::_get(const StringName &p_name, Variant &r_ret) const { + if (properties_dirty) { + const_cast<AnimationTree*>(this)->_update_properties(); + } + + if (property_map.has(p_name)) { + r_ret=property_map[p_name]; + return true; + } + + return false; + +} +void AnimationTree::_get_property_list(List<PropertyInfo> *p_list) const { + if (properties_dirty) { + const_cast<AnimationTree*>(this)->_update_properties(); + } + + for (const List<PropertyInfo>::Element *E=properties.front();E;E=E->next()) { + p_list->push_back(E->get()); + } +} + +void AnimationTree::rename_parameter(const String& p_base,const String& p_new_base) { + + //rename values first + for (const List<PropertyInfo>::Element *E=properties.front();E;E=E->next()) { + if (E->get().name.begins_with(p_base)) { + String new_name = E->get().name.replace_first(p_base,p_new_base); + property_map[new_name]=property_map[E->get().name]; + } + } + + //update tree second + properties_dirty=true; + _update_properties(); +} + void AnimationTree::_bind_methods() { ClassDB::bind_method(D_METHOD("set_active", "active"), &AnimationTree::set_active); ClassDB::bind_method(D_METHOD("is_active"), &AnimationTree::is_active); @@ -1315,11 +1407,17 @@ void AnimationTree::_bind_methods() { ClassDB::bind_method(D_METHOD("get_root_motion_transform"), &AnimationTree::get_root_motion_transform); + ClassDB::bind_method(D_METHOD("_tree_changed"), &AnimationTree::_tree_changed); + ClassDB::bind_method(D_METHOD("_update_properties"), &AnimationTree::_update_properties); + + ClassDB::bind_method(D_METHOD("rename_parameter","old_name","new_name"), &AnimationTree::rename_parameter); + + ClassDB::bind_method(D_METHOD("advance", "delta"), &AnimationTree::advance); ClassDB::bind_method(D_METHOD("_node_removed"), &AnimationTree::_node_removed); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tree_root", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "set_tree_root", "get_tree_root"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "tree_root", PROPERTY_HINT_RESOURCE_TYPE, "AnimationRootNode"), "set_tree_root", "get_tree_root"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "anim_player", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "AnimationPlayer"), "set_animation_player", "get_animation_player"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active"); ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Physics,Idle,Manual"), "set_process_mode", "get_process_mode"); @@ -1338,10 +1436,9 @@ AnimationTree::AnimationTree() { cache_valid = false; setup_pass = 1; started = true; + properties_dirty = true; } AnimationTree::~AnimationTree() { - if (root.is_valid()) { - root->player = NULL; - } + } diff --git a/scene/animation/animation_tree.h b/scene/animation/animation_tree.h index 87092a4a0e..3c615b2f92 100644 --- a/scene/animation/animation_tree.h +++ b/scene/animation/animation_tree.h @@ -23,9 +23,6 @@ public: struct Input { String name; - StringName connected_to; - float activity; - uint64_t last_pass; }; Vector<Input> inputs; @@ -51,30 +48,33 @@ public: List<AnimationState> animation_states; bool valid; AnimationPlayer *player; + AnimationTree *tree; String invalid_reasons; uint64_t last_pass; }; Vector<float> blends; State *state; - float _pre_process(State *p_state, float p_time, bool p_seek); + String path; + float _pre_process(const StringName &p_base_path, AnimationNode *p_parent, State *p_state, float p_time, bool p_seek, const Vector<StringName> &p_connections); void _pre_update_animations(HashMap<NodePath, int> *track_map); - Vector2 position; + //all this is temporary + StringName base_path; + Vector<StringName> connections; AnimationNode *parent; - AnimationTree *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); + friend class AnimationNodeBlendTree; + float _blend_node(const StringName &p_subpath, const Vector<StringName> &p_connections, AnimationNode *p_new_parent, 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); 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_node(const StringName &p_sub_path, 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); @@ -85,20 +85,24 @@ protected: void _set_parent(Object *p_parent); public: - void set_parent(AnimationNode *p_parent); - Ref<AnimationNode> get_parent() const; - virtual void set_tree(AnimationTree *p_player); - AnimationTree *get_tree() const; - AnimationPlayer *get_player() const; + virtual void get_parameter_list(List<PropertyInfo> *r_list) const; + virtual Variant get_parameter_default_value(const StringName &p_parameter) const; + + void set_parameter(const StringName &p_name, const Variant &p_value); + Variant get_parameter(const StringName &p_name) const; + + struct ChildNode { + StringName name; + Ref<AnimationNode> node; + }; + + virtual void get_child_nodes(List<ChildNode> *r_child_nodes); virtual float process(float p_time, bool p_seek); virtual String get_caption() const; 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); @@ -112,8 +116,7 @@ public: virtual bool has_filter() const; - void set_position(const Vector2 &p_position); - Vector2 get_position() const; + virtual Ref<AnimationNode> get_child_by_name(const StringName &p_name); AnimationNode(); }; @@ -245,7 +248,21 @@ private: NodePath root_motion_track; Transform root_motion_transform; + friend class AnimationNode; + bool properties_dirty; + void _tree_changed(); + void _update_properties(); + List<PropertyInfo> properties; + HashMap<StringName, HashMap<StringName, StringName> > property_parent_map; + HashMap<StringName, Variant> property_map; + + void _update_properties_for_node(const String &p_base_path, Ref<AnimationNode> node); + protected: + 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; + void _notification(int p_what); static void _bind_methods(); @@ -274,6 +291,8 @@ public: void advance(float p_time); + void rename_parameter(const String &p_base, const String &p_new_base); + uint64_t get_last_process_pass() const; AnimationTree(); ~AnimationTree(); diff --git a/scene/animation/skeleton_ik.cpp b/scene/animation/skeleton_ik.cpp index 4991cedfab..9b1cb1369a 100644 --- a/scene/animation/skeleton_ik.cpp +++ b/scene/animation/skeleton_ik.cpp @@ -34,6 +34,8 @@ #include "skeleton_ik.h" +#ifndef _3D_DISABLED + FabrikInverseKinematic::ChainItem *FabrikInverseKinematic::ChainItem::find_child(const BoneId p_bone_id) { for (int i = childs.size() - 1; 0 <= i; --i) { if (p_bone_id == childs[i].bone) { @@ -549,3 +551,5 @@ void SkeletonIK::_solve_chain() { return; FabrikInverseKinematic::solve(task, interpolation, use_magnet, magnet_position); } + +#endif // _3D_DISABLED diff --git a/scene/animation/skeleton_ik.h b/scene/animation/skeleton_ik.h index 366c599c01..08fb00e798 100644 --- a/scene/animation/skeleton_ik.h +++ b/scene/animation/skeleton_ik.h @@ -31,6 +31,8 @@ #ifndef SKELETON_IK_H #define SKELETON_IK_H +#ifndef _3D_DISABLED + /** * @author AndreaCatania */ @@ -209,4 +211,6 @@ private: void _solve_chain(); }; +#endif // _3D_DISABLED + #endif // SKELETON_IK_H diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index d95ec9e495..a7163adbe6 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -356,14 +356,14 @@ bool GraphEdit::_filter_input(const Point2 &p_point) { for (int j = 0; j < gn->get_connection_output_count(); j++) { Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); - if (create_hot_zone(pos).has_point(p_point)) + if (is_in_hot_zone(pos, p_point)) return true; } for (int j = 0; j < gn->get_connection_input_count(); j++) { Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); - if (create_hot_zone(pos).has_point(p_point)) { + if (is_in_hot_zone(pos, p_point)) { return true; } } @@ -388,7 +388,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { for (int j = 0; j < gn->get_connection_output_count(); j++) { Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); - if (create_hot_zone(pos).has_point(mpos)) { + if (is_in_hot_zone(pos, mpos)) { if (valid_left_disconnect_types.has(gn->get_connection_output_type(j))) { //check disconnect @@ -435,7 +435,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { for (int j = 0; j < gn->get_connection_input_count(); j++) { Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); - if (create_hot_zone(pos).has_point(mpos)) { + if (is_in_hot_zone(pos, mpos)) { if (right_disconnects || valid_right_disconnect_types.has(gn->get_connection_input_type(j))) { //check disconnect @@ -502,7 +502,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { Vector2 pos = gn->get_connection_output_position(j) + gn->get_position(); int type = gn->get_connection_output_type(j); - if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && create_hot_zone(pos).has_point(mpos)) { + if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos, mpos)) { connecting_target = true; connecting_to = pos; @@ -517,7 +517,7 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { Vector2 pos = gn->get_connection_input_position(j) + gn->get_position(); int type = gn->get_connection_input_type(j); - if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && create_hot_zone(pos).has_point(mpos)) { + if ((type == connecting_type || valid_connection_types.has(ConnType(type, connecting_type))) && is_in_hot_zone(pos, mpos)) { connecting_target = true; connecting_to = pos; connecting_target_to = gn->get_name(); @@ -557,8 +557,55 @@ void GraphEdit::_top_layer_input(const Ref<InputEvent> &p_ev) { } } -Rect2 GraphEdit::create_hot_zone(const Vector2 &pos) { - return Rect2(pos.x - port_grab_distance_horizontal, pos.y - port_grab_distance_vertical, port_grab_distance_horizontal * 2, port_grab_distance_vertical * 2); +bool GraphEdit::_check_clickable_control(Control *p_control, const Vector2 &pos) { + + if (p_control->is_set_as_toplevel() || !p_control->is_visible()) + return false; + + if (!p_control->has_point(pos) || p_control->get_mouse_filter() == MOUSE_FILTER_IGNORE) { + //test children + for (int i = 0; i < p_control->get_child_count(); i++) { + Control *subchild = Object::cast_to<Control>(p_control->get_child(i)); + if (!subchild) + continue; + if (_check_clickable_control(subchild, pos - subchild->get_position())) { + return true; + } + } + + return false; + } else { + return true; + } +} + +bool GraphEdit::is_in_hot_zone(const Vector2 &pos, const Vector2 &p_mouse_pos) { + if (!Rect2(pos.x - port_grab_distance_horizontal, pos.y - port_grab_distance_vertical, port_grab_distance_horizontal * 2, port_grab_distance_vertical * 2).has_point(p_mouse_pos)) + return false; + + for (int i = 0; i < get_child_count(); i++) { + Control *child = Object::cast_to<Control>(get_child(i)); + if (!child) + continue; + Rect2 rect = child->get_rect(); + if (rect.has_point(p_mouse_pos)) { + + //check sub-controls + Vector2 subpos = p_mouse_pos - rect.position; + + for (int j = 0; j < child->get_child_count(); j++) { + Control *subchild = Object::cast_to<Control>(child->get_child(j)); + if (!subchild) + continue; + + if (_check_clickable_control(subchild, subpos - subchild->get_position())) { + return false; + } + } + } + } + + return true; } template <class Vector2> diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h index 64ba18681e..31a449eb59 100644 --- a/scene/gui/graph_edit.h +++ b/scene/gui/graph_edit.h @@ -131,7 +131,7 @@ private: GraphEditFilter *top_layer; void _top_layer_input(const Ref<InputEvent> &p_ev); - Rect2 create_hot_zone(const Vector2 &pos); + bool is_in_hot_zone(const Vector2 &pos, const Vector2 &p_mouse_pos); void _top_layer_draw(); void _connections_layer_draw(); @@ -172,6 +172,8 @@ private: void _snap_toggled(); void _snap_value_changed(double); + bool _check_clickable_control(Control *p_control, const Vector2 &pos); + protected: static void _bind_methods(); virtual void add_child_notify(Node *p_child); diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp index 5c79741682..d61bd97c2a 100644 --- a/scene/gui/item_list.cpp +++ b/scene/gui/item_list.cpp @@ -1423,6 +1423,9 @@ void ItemList::_bind_methods() { ClassDB::bind_method(D_METHOD("set_item_custom_bg_color", "idx", "custom_bg_color"), &ItemList::set_item_custom_bg_color); ClassDB::bind_method(D_METHOD("get_item_custom_bg_color", "idx"), &ItemList::get_item_custom_bg_color); + ClassDB::bind_method(D_METHOD("set_item_custom_fg_color", "idx", "custom_fg_color"), &ItemList::set_item_custom_fg_color); + ClassDB::bind_method(D_METHOD("get_item_custom_fg_color", "idx"), &ItemList::get_item_custom_fg_color); + ClassDB::bind_method(D_METHOD("set_item_tooltip_enabled", "idx", "enable"), &ItemList::set_item_tooltip_enabled); ClassDB::bind_method(D_METHOD("is_item_tooltip_enabled", "idx"), &ItemList::is_item_tooltip_enabled); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 40134a2480..673d44dab2 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -51,7 +51,6 @@ #include "scene/2d/parallax_background.h" #include "scene/2d/parallax_layer.h" #include "scene/2d/particles_2d.h" - #include "scene/2d/path_2d.h" #include "scene/2d/physics_body_2d.h" #include "scene/2d/polygon_2d.h" @@ -367,14 +366,12 @@ void register_scene_types() { ClassDB::register_class<Spatial>(); ClassDB::register_virtual_class<SpatialGizmo>(); ClassDB::register_class<Skeleton>(); - ClassDB::register_class<SkeletonIK>(); ClassDB::register_class<AnimationPlayer>(); ClassDB::register_class<Tween>(); OS::get_singleton()->yield(); //may take time to init #ifndef _3D_DISABLED - ClassDB::register_class<BoneAttachment>(); ClassDB::register_virtual_class<VisualInstance>(); ClassDB::register_virtual_class<GeometryInstance>(); ClassDB::register_class<Camera>(); @@ -416,6 +413,8 @@ void register_scene_types() { ClassDB::register_class<AnimationNodeBlendSpace1D>(); ClassDB::register_class<AnimationNodeBlendSpace2D>(); ClassDB::register_class<AnimationNodeStateMachine>(); + ClassDB::register_class<AnimationNodeStateMachinePlayback>(); + ClassDB::register_class<AnimationNodeStateMachineTransition>(); ClassDB::register_class<AnimationNodeOutput>(); ClassDB::register_class<AnimationNodeOneShot>(); @@ -441,6 +440,9 @@ void register_scene_types() { ClassDB::register_class<PhysicalBone>(); ClassDB::register_class<SoftBody>(); + ClassDB::register_class<SkeletonIK>(); + ClassDB::register_class<BoneAttachment>(); + ClassDB::register_class<VehicleBody>(); ClassDB::register_class<VehicleWheel>(); ClassDB::register_class<Area>(); diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index 875b72159a..143a1438ea 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -34,6 +34,8 @@ void Material::set_next_pass(const Ref<Material> &p_pass) { + ERR_FAIL_COND(p_pass == this); + if (next_pass == p_pass) return; diff --git a/scene/resources/physics_material.h b/scene/resources/physics_material.h index dfe48d94cf..c69e44a7da 100644 --- a/scene/resources/physics_material.h +++ b/scene/resources/physics_material.h @@ -37,7 +37,7 @@ class PhysicsMaterial : public Resource { GDCLASS(PhysicsMaterial, Resource); OBJ_SAVE_TYPE(PhysicsMaterial); - RES_BASE_EXTENSION("PhyMat"); + RES_BASE_EXTENSION("phymat"); real_t friction; bool rough; diff --git a/scene/scene_string_names.cpp b/scene/scene_string_names.cpp index 661606c7ef..e468b3dab4 100644 --- a/scene/scene_string_names.cpp +++ b/scene/scene_string_names.cpp @@ -201,4 +201,6 @@ SceneStringNames::SceneStringNames() { } _mesh_changed = StaticCString::create("_mesh_changed"); + + parameters_base_path = "parameters/"; } diff --git a/scene/scene_string_names.h b/scene/scene_string_names.h index 817158f9f3..dbbcf79b9f 100644 --- a/scene/scene_string_names.h +++ b/scene/scene_string_names.h @@ -203,6 +203,8 @@ public: StringName output; + StringName parameters_base_path; + enum { MAX_MATERIALS = 32 }; diff --git a/servers/physics/physics_server_sw.h b/servers/physics/physics_server_sw.h index 1c5754124d..2b3d2ae91f 100644 --- a/servers/physics/physics_server_sw.h +++ b/servers/physics/physics_server_sw.h @@ -231,6 +231,7 @@ public: virtual bool body_is_ray_pickable(RID p_body) const; virtual bool body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result = NULL); + virtual int body_test_ray_separation(RID p_body, const Transform &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, SeparationResult *r_results, int p_result_max, float p_margin = 0.001) { return 0; } // this function only works on physics process, errors and returns null otherwise virtual PhysicsDirectBodyState *body_get_direct_state(RID p_body); diff --git a/servers/physics_server.h b/servers/physics_server.h index 948aec1a2d..982fcd68e3 100644 --- a/servers/physics_server.h +++ b/servers/physics_server.h @@ -484,6 +484,21 @@ public: virtual bool body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result = NULL) = 0; + struct SeparationResult { + + float collision_depth; + Vector3 collision_point; + Vector3 collision_normal; + Vector3 collider_velocity; + int collision_local_shape; + ObjectID collider_id; + RID collider; + int collider_shape; + Variant collider_metadata; + }; + + virtual int body_test_ray_separation(RID p_body, const Transform &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, SeparationResult *r_results, int p_result_max, float p_margin = 0.001) = 0; + /* SOFT BODY */ virtual RID soft_body_create(bool p_init_sleeping = false) = 0; diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp index ca50d0d049..8705033326 100644 --- a/servers/visual/shader_language.cpp +++ b/servers/visual/shader_language.cpp @@ -3875,8 +3875,8 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct return ERR_PARSE_ERROR; } - if (!uniform && (type < TYPE_FLOAT || type > TYPE_VEC4)) { - _set_error("Invalid type for varying, only float,vec2,vec3,vec4 allowed."); + if (!uniform && (type < TYPE_FLOAT || type > TYPE_MAT4)) { + _set_error("Invalid type for varying, only float,vec2,vec3,vec4,mat2,mat3,mat4 allowed."); return ERR_PARSE_ERROR; } diff --git a/servers/visual/shader_types.cpp b/servers/visual/shader_types.cpp index 0de8676f32..caa454b98e 100644 --- a/servers/visual/shader_types.cpp +++ b/servers/visual/shader_types.cpp @@ -127,6 +127,7 @@ ShaderTypes::ShaderTypes() { shader_modes[VS::SHADER_SPATIAL].functions["light"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT); shader_modes[VS::SHADER_SPATIAL].functions["light"].built_ins["VIEWPORT_SIZE"] = constt(ShaderLanguage::TYPE_VEC2); + shader_modes[VS::SHADER_SPATIAL].functions["light"].built_ins["FRAGCOORD"] = constt(ShaderLanguage::TYPE_VEC4); shader_modes[VS::SHADER_SPATIAL].functions["light"].built_ins["NORMAL"] = constt(ShaderLanguage::TYPE_VEC3); shader_modes[VS::SHADER_SPATIAL].functions["light"].built_ins["VIEW"] = constt(ShaderLanguage::TYPE_VEC3); shader_modes[VS::SHADER_SPATIAL].functions["light"].built_ins["LIGHT"] = constt(ShaderLanguage::TYPE_VEC3); |