summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.appveyor.yml4
-rw-r--r--.travis.yml2
-rw-r--r--core/class_db.h4
-rw-r--r--core/color.cpp3
-rw-r--r--core/global_constants.cpp1
-rw-r--r--core/image.cpp4
-rw-r--r--core/input_map.cpp2
-rw-r--r--core/math/quick_hull.cpp11
-rw-r--r--core/object.cpp33
-rw-r--r--core/object.h8
-rw-r--r--core/os/os.h2
-rw-r--r--core/reference.h7
-rw-r--r--core/resource.cpp6
-rw-r--r--core/variant_call.cpp18
-rw-r--r--doc/classes/@GlobalScope.xml8
-rw-r--r--doc/classes/AnimationNode.xml72
-rw-r--r--doc/classes/AnimationNodeAdd2.xml2
-rw-r--r--doc/classes/AnimationNodeAdd3.xml2
-rw-r--r--doc/classes/AnimationNodeBlend2.xml2
-rw-r--r--doc/classes/AnimationNodeBlend3.xml2
-rw-r--r--doc/classes/AnimationNodeBlendSpace1D.xml2
-rw-r--r--doc/classes/AnimationNodeBlendSpace2D.xml2
-rw-r--r--doc/classes/AnimationNodeBlendTree.xml20
-rw-r--r--doc/classes/AnimationNodeOneShot.xml18
-rw-r--r--doc/classes/AnimationNodeStateMachine.xml54
-rw-r--r--doc/classes/AnimationNodeStateMachinePlayback.xml55
-rw-r--r--doc/classes/AnimationNodeStateMachineTransition.xml8
-rw-r--r--doc/classes/AnimationNodeTimeScale.xml4
-rw-r--r--doc/classes/AnimationNodeTimeSeek.xml4
-rw-r--r--doc/classes/AnimationNodeTransition.xml2
-rw-r--r--doc/classes/AnimationTree.xml10
-rw-r--r--doc/classes/ArrayMesh.xml4
-rw-r--r--doc/classes/AudioEffectRecord.xml39
-rw-r--r--doc/classes/AudioServer.xml22
-rw-r--r--doc/classes/AudioStreamMicrophone.xml15
-rw-r--r--doc/classes/AudioStreamSample.xml8
-rw-r--r--doc/classes/BakedLightmap.xml15
-rw-r--r--doc/classes/BitMap.xml20
-rw-r--r--doc/classes/Camera.xml18
-rw-r--r--doc/classes/Color.xml290
-rw-r--r--doc/classes/ColorPicker.xml10
-rw-r--r--doc/classes/ColorPickerButton.xml6
-rw-r--r--doc/classes/ColorRect.xml8
-rw-r--r--doc/classes/Control.xml69
-rw-r--r--doc/classes/Dictionary.xml2
-rw-r--r--doc/classes/DirectionalLight.xml18
-rw-r--r--doc/classes/EditorFileSystem.xml4
-rw-r--r--doc/classes/EditorPlugin.xml9
-rw-r--r--doc/classes/EditorSpatialGizmo.xml14
-rw-r--r--doc/classes/Expression.xml49
-rw-r--r--doc/classes/InputEventKey.xml1
-rw-r--r--doc/classes/InputMap.xml9
-rw-r--r--doc/classes/ItemList.xml18
-rw-r--r--doc/classes/KinematicBody.xml43
-rw-r--r--doc/classes/KinematicBody2D.xml9
-rw-r--r--doc/classes/Light.xml15
-rw-r--r--doc/classes/LineEdit.xml8
-rw-r--r--doc/classes/Node.xml6
-rw-r--r--doc/classes/Object.xml2
-rw-r--r--doc/classes/OmniLight.xml8
-rw-r--r--doc/classes/PhysicalBone.xml6
-rw-r--r--doc/classes/PhysicsDirectBodyState.xml2
-rw-r--r--doc/classes/PhysicsMaterial.xml10
-rw-r--r--doc/classes/Plane.xml6
-rw-r--r--doc/classes/PopupMenu.xml14
-rw-r--r--doc/classes/ProgressBar.xml1
-rw-r--r--doc/classes/ProjectSettings.xml78
-rw-r--r--doc/classes/Quat.xml2
-rw-r--r--doc/classes/Range.xml16
-rw-r--r--doc/classes/ReferenceRect.xml4
-rw-r--r--doc/classes/ResourceLoader.xml18
-rw-r--r--doc/classes/Skeleton.xml10
-rw-r--r--doc/classes/Skeleton2D.xml2
-rw-r--r--doc/classes/SkeletonIK.xml61
-rw-r--r--doc/classes/SpringArm.xml53
-rw-r--r--doc/classes/Tabs.xml14
-rw-r--r--doc/classes/Texture.xml2
-rw-r--r--doc/classes/Texture3D.xml15
-rw-r--r--doc/classes/TextureArray.xml15
-rw-r--r--doc/classes/TextureLayered.xml103
-rw-r--r--doc/classes/TileSet.xml56
-rw-r--r--doc/classes/Transform.xml8
-rw-r--r--doc/classes/Transform2D.xml6
-rw-r--r--doc/classes/Vector2.xml30
-rw-r--r--doc/classes/Vector3.xml34
-rw-r--r--doc/classes/Viewport.xml34
-rw-r--r--doc/classes/VisualInstance.xml18
-rw-r--r--doc/classes/VisualServer.xml103
-rw-r--r--drivers/gles2/rasterizer_gles2.cpp6
-rw-r--r--drivers/unix/os_unix.cpp2
-rw-r--r--drivers/unix/os_unix.h2
-rw-r--r--editor/editor_help.cpp26
-rw-r--r--editor/editor_help.h2
-rw-r--r--editor/editor_inspector.cpp5
-rw-r--r--editor/editor_node.cpp6
-rw-r--r--editor/editor_plugin_settings.h2
-rw-r--r--editor/editor_properties.cpp23
-rw-r--r--editor/editor_properties.h2
-rw-r--r--editor/editor_properties_array_dict.cpp12
-rw-r--r--editor/editor_properties_array_dict.h4
-rw-r--r--editor/editor_run.cpp4
-rw-r--r--editor/editor_settings.cpp2
-rw-r--r--editor/editor_themes.cpp10
-rw-r--r--editor/find_in_files.cpp257
-rw-r--r--editor/find_in_files.h24
-rw-r--r--editor/import/resource_importer_texture.cpp15
-rw-r--r--editor/import/resource_importer_wav.cpp24
-rw-r--r--editor/import_dock.cpp3
-rw-r--r--editor/import_dock.h10
-rw-r--r--editor/inspector_dock.cpp15
-rw-r--r--editor/inspector_dock.h5
-rw-r--r--editor/plugins/animation_blend_space_1d_editor.cpp173
-rw-r--r--editor/plugins/animation_blend_space_1d_editor.h41
-rw-r--r--editor/plugins/animation_blend_space_2d_editor.cpp183
-rw-r--r--editor/plugins/animation_blend_space_2d_editor.h43
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.cpp293
-rw-r--r--editor/plugins/animation_blend_tree_editor_plugin.h45
-rw-r--r--editor/plugins/animation_state_machine_editor.cpp302
-rw-r--r--editor/plugins/animation_state_machine_editor.h44
-rw-r--r--editor/plugins/animation_tree_editor_plugin.cpp1477
-rw-r--r--editor/plugins/animation_tree_editor_plugin.h198
-rw-r--r--editor/plugins/animation_tree_player_editor_plugin.cpp1446
-rw-r--r--editor/plugins/animation_tree_player_editor_plugin.h187
-rw-r--r--editor/plugins/asset_library_editor_plugin.cpp11
-rw-r--r--editor/plugins/canvas_item_editor_plugin.cpp6
-rw-r--r--editor/plugins/curve_editor_plugin.cpp4
-rw-r--r--editor/plugins/spatial_editor_plugin.cpp18
-rw-r--r--editor/plugins/spatial_editor_plugin.h1
-rw-r--r--editor/plugins/texture_region_editor_plugin.cpp2
-rw-r--r--editor/project_export.cpp6
-rw-r--r--editor/project_export.h20
-rw-r--r--editor/script_editor_debugger.cpp3
-rw-r--r--editor/spatial_editor_gizmos.cpp76
-rw-r--r--editor/spatial_editor_gizmos.h15
-rw-r--r--modules/bullet/bullet_physics_server.cpp24
-rw-r--r--modules/bullet/bullet_physics_server.h6
-rw-r--r--modules/bullet/godot_result_callbacks.cpp68
-rw-r--r--modules/bullet/godot_result_callbacks.h44
-rw-r--r--modules/bullet/shape_bullet.cpp45
-rw-r--r--modules/bullet/shape_bullet.h24
-rw-r--r--modules/bullet/space_bullet.cpp153
-rw-r--r--modules/bullet/space_bullet.h17
-rw-r--r--modules/gdnative/doc_classes/NativeScript.xml4
-rw-r--r--modules/gdnative/include/nativescript/godot_nativescript.h1
-rw-r--r--modules/gdscript/gdscript_editor.cpp2
-rw-r--r--modules/mono/config.py4
-rw-r--r--modules/mono/glue/cs_files/Color.cs5
-rw-r--r--modules/mono/glue/cs_files/Plane.cs9
-rw-r--r--modules/mono/utils/thread_local.cpp4
-rw-r--r--modules/mono/utils/thread_local.h3
-rw-r--r--platform/android/detect.py2
-rw-r--r--platform/android/export/export.cpp25
-rw-r--r--platform/iphone/export/export.cpp10
-rw-r--r--platform/javascript/export/export.cpp33
-rw-r--r--platform/javascript/http_request.js6
-rw-r--r--platform/javascript/javascript_eval.cpp4
-rw-r--r--platform/javascript/os_javascript.cpp2
-rw-r--r--platform/osx/export/export.cpp6
-rw-r--r--platform/uwp/export/export.cpp18
-rw-r--r--platform/windows/detect.py12
-rw-r--r--platform/windows/export/export.cpp16
-rw-r--r--platform/windows/os_windows.cpp45
-rw-r--r--platform/windows/os_windows.h2
-rw-r--r--scene/2d/canvas_item.cpp21
-rw-r--r--scene/2d/canvas_item.h2
-rw-r--r--scene/2d/collision_object_2d.cpp4
-rw-r--r--scene/2d/collision_shape_2d.cpp2
-rw-r--r--scene/2d/physics_body_2d.cpp85
-rw-r--r--scene/2d/physics_body_2d.h5
-rw-r--r--scene/2d/ray_cast_2d.cpp34
-rw-r--r--scene/2d/ray_cast_2d.h9
-rw-r--r--scene/2d/skeleton_2d.cpp2
-rw-r--r--scene/3d/camera.cpp249
-rw-r--r--scene/3d/camera.h60
-rw-r--r--scene/3d/physics_body.cpp193
-rw-r--r--scene/3d/physics_body.h11
-rw-r--r--scene/3d/ray_cast.cpp34
-rw-r--r--scene/3d/ray_cast.h10
-rw-r--r--scene/3d/spring_arm.cpp172
-rw-r--r--scene/3d/spring_arm.h71
-rw-r--r--scene/animation/animation_blend_space_1d.cpp77
-rw-r--r--scene/animation/animation_blend_space_1d.h17
-rw-r--r--scene/animation/animation_blend_space_2d.cpp69
-rw-r--r--scene/animation/animation_blend_space_2d.h15
-rw-r--r--scene/animation/animation_blend_tree.cpp517
-rw-r--r--scene/animation/animation_blend_tree.h111
-rw-r--r--scene/animation/animation_node_state_machine.cpp838
-rw-r--r--scene/animation/animation_node_state_machine.h109
-rw-r--r--scene/animation/animation_tree.cpp322
-rw-r--r--scene/animation/animation_tree.h57
-rw-r--r--scene/animation/skeleton_ik.cpp4
-rw-r--r--scene/animation/skeleton_ik.h4
-rw-r--r--scene/gui/gradient_edit.cpp1
-rw-r--r--scene/gui/graph_edit.cpp63
-rw-r--r--scene/gui/graph_edit.h4
-rw-r--r--scene/gui/item_list.cpp3
-rw-r--r--scene/main/canvas_layer.cpp19
-rw-r--r--scene/main/canvas_layer.h1
-rw-r--r--scene/main/node.cpp2
-rw-r--r--scene/main/viewport.cpp6
-rw-r--r--scene/register_scene_types.cpp12
-rw-r--r--scene/resources/material.cpp2
-rw-r--r--scene/resources/mesh.cpp2
-rw-r--r--scene/resources/physics_material.h2
-rw-r--r--scene/resources/shape.cpp23
-rw-r--r--scene/resources/shape.h6
-rw-r--r--scene/resources/texture.cpp2
-rw-r--r--scene/scene_string_names.cpp2
-rw-r--r--scene/scene_string_names.h2
-rw-r--r--servers/audio/effects/audio_effect_record.cpp2
-rw-r--r--servers/audio_server.cpp4
-rw-r--r--servers/physics/physics_server_sw.cpp24
-rw-r--r--servers/physics/physics_server_sw.h7
-rw-r--r--servers/physics/space_sw.cpp185
-rw-r--r--servers/physics/space_sw.h15
-rw-r--r--servers/physics_2d/space_2d_sw.cpp38
-rw-r--r--servers/physics_2d/space_2d_sw.h12
-rw-r--r--servers/physics_2d_server.cpp46
-rw-r--r--servers/physics_2d_server.h25
-rw-r--r--servers/physics_server.cpp42
-rw-r--r--servers/physics_server.h44
-rw-r--r--servers/visual/shader_language.cpp4
-rw-r--r--servers/visual/shader_types.cpp1
-rw-r--r--servers/visual_server.cpp36
224 files changed, 7420 insertions, 3802 deletions
diff --git a/.appveyor.yml b/.appveyor.yml
index 7691f65d6a..567da9cd5d 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -4,7 +4,7 @@ environment:
HOME: "%HOMEDRIVE%%HOMEPATH%"
PYTHON: C:\Python27
SCONS_CACHE_ROOT: "%HOME%\\scons_cache"
- SCONS_CACHE_LIMIT: 512
+ SCONS_CACHE_LIMIT: 1024
matrix:
- VS: C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat
GD_PLATFORM: windows
@@ -29,4 +29,4 @@ before_build:
- SET "SCONS_CACHE=%SCONS_CACHE_ROOT%\master"
build_script:
-- scons platform=%GD_PLATFORM% target=%TARGET% tools=%TOOLS% verbose=yes progress=no gdnative_wrapper=yes
+- scons platform=%GD_PLATFORM% target=%TARGET% tools=%TOOLS% debug_symbols=no verbose=yes progress=no gdnative_wrapper=yes
diff --git a/.travis.yml b/.travis.yml
index e89774a2a2..404bdc9d90 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,7 +8,7 @@ env:
global:
- SCONS_CACHE=$HOME/.scons_cache
- SCONS_CACHE_LIMIT=1024
- - OPTIONS="verbose=yes progress=no gdnative_wrapper=yes"
+ - OPTIONS="debug_symbols=no verbose=yes progress=no gdnative_wrapper=yes"
- secure: "QLFRizqry/Y5pnEZvDlQz5S3YydQ+600u4rHEzFgUTd0heYeQaETXAQeMzp0ymuG1BkdRAl5YJoLVJgAzjwI9hrvugvoUlh2//SfpqZCHN/Q1fYbtGgNTn01R3VFEpcfYQL93I2EjrxVm0WTM4PwCvMO+hU0aWTRDvCt1Lty0kMR+RMDQOO/woqunoXh5wvFNxTJJkAmuLe0v962DJYOIwJAnqMLR0aFYjmeQJ20bc/2X5oLt+WuJDuf/lGj6WSlD6z/o/kL3YxHoUyw4A/HAZ2IX0IfNHKuay60ESWzl/NlobnePiPwHAE2pdDVu//q16fanb9VeYnBYRFse49TpFRb86Lo+Qz8nKDJqpQEIY0YKNCFqekrubqTM++Lj6QvGpykQZNxUhybmELcEsRG4PS0UMvCpebdnJD46nNB+DtO2Lgb4xXDLQwpq19z1wizq/XDQ5hz61TIIx8+i8TsgdSQKCTeWovd4HcD4CVjAD5XTLGgyRmI/zC2d+lTnKo6W9diLq/bX/Goq2QPeaTPABqv817IaJka2JyugQ7Qal/+gNTjYRRsimRCL9B2tVh+Uh8rWhTFhQL4QbP5P65HF+p8qojUzqtAhPMbZ8mxUtNukUI3liVgPgiMss96sG0nTVglFgkkAkEjIMFnqMSKnTfG812K4jIhp2jCO2Q3NeI="
cache:
diff --git a/core/class_db.h b/core/class_db.h
index f1d1879236..66a67f6c9f 100644
--- a/core/class_db.h
+++ b/core/class_db.h
@@ -39,6 +39,10 @@
@author Juan Linietsky <reduzio@gmail.com>
*/
+/** To bind more then 6 parameters include this:
+ * #include "method_bind_ext.gen.inc"
+ */
+
#define DEFVAL(m_defval) (m_defval)
//#define SIMPLE_METHODDEF
diff --git a/core/color.cpp b/core/color.cpp
index fcfcf20355..0d8cad1a5e 100644
--- a/core/color.cpp
+++ b/core/color.cpp
@@ -506,8 +506,11 @@ Color Color::from_hsv(float p_h, float p_s, float p_v, float p_a) {
return Color(m + r, m + g, m + b, p_a);
}
+// FIXME: Remove once Godot 3.1 has been released
float Color::gray() const {
+ ERR_EXPLAIN("Color.gray() is deprecated and will be removed in a future version. Use Color.get_v() for a better grayscale approximation.");
+ WARN_DEPRECATED
return (r + g + b) / 3.0;
}
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/image.cpp b/core/image.cpp
index c94f2c3534..2fd7ea5c14 100644
--- a/core/image.cpp
+++ b/core/image.cpp
@@ -2230,10 +2230,10 @@ void Image::set_pixel(int p_x, int p_y, const Color &p_color) {
switch (format) {
case FORMAT_L8: {
- ptr[ofs] = uint8_t(CLAMP(p_color.gray() * 255.0, 0, 255));
+ ptr[ofs] = uint8_t(CLAMP(p_color.get_v() * 255.0, 0, 255));
} break;
case FORMAT_LA8: {
- ptr[ofs * 2 + 0] = uint8_t(CLAMP(p_color.gray() * 255.0, 0, 255));
+ ptr[ofs * 2 + 0] = uint8_t(CLAMP(p_color.get_v() * 255.0, 0, 255));
ptr[ofs * 2 + 1] = uint8_t(CLAMP(p_color.a * 255.0, 0, 255));
} break;
case FORMAT_R8: {
diff --git a/core/input_map.cpp b/core/input_map.cpp
index d33f40cbcf..ffc8a39da5 100644
--- a/core/input_map.cpp
+++ b/core/input_map.cpp
@@ -44,7 +44,7 @@ void InputMap::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_action", "action", "deadzone"), &InputMap::add_action, DEFVAL(0.5f));
ClassDB::bind_method(D_METHOD("erase_action", "action"), &InputMap::erase_action);
- ClassDB::bind_method(D_METHOD("action_set_deadzone", "deadzone"), &InputMap::action_set_deadzone);
+ ClassDB::bind_method(D_METHOD("action_set_deadzone", "action", "deadzone"), &InputMap::action_set_deadzone);
ClassDB::bind_method(D_METHOD("action_add_event", "action", "event"), &InputMap::action_add_event);
ClassDB::bind_method(D_METHOD("action_has_event", "action", "event"), &InputMap::action_has_event);
ClassDB::bind_method(D_METHOD("action_erase_event", "action", "event"), &InputMap::action_erase_event);
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..76226d113a 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;
@@ -1521,6 +1530,10 @@ bool Object::is_connected(const StringName &p_signal, Object *p_to_object, const
void Object::disconnect(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method) {
+ _disconnect(p_signal, p_to_object, p_to_method);
+}
+void Object::_disconnect(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method, bool p_force) {
+
ERR_FAIL_NULL(p_to_object);
Signal *s = signal_map.getptr(p_signal);
if (!s) {
@@ -1539,7 +1552,16 @@ 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];
+
+ if (!p_force) {
+ 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 +1783,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) {
@@ -1948,13 +1971,13 @@ Object::~Object() {
Connection &c = E->get();
ERR_CONTINUE(c.source != this); //bug?
- this->disconnect(c.signal, c.target, c.method);
+ this->_disconnect(c.signal, c.target, c.method, true);
}
while (connections.size()) {
Connection c = connections.front()->get();
- c.source->disconnect(c.signal, c.target, c.method);
+ c.source->_disconnect(c.signal, c.target, c.method, true);
}
ObjectDB::remove_instance(this);
diff --git a/core/object.h b/core/object.h
index 8dc3426d1d..d741371306 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;
@@ -547,6 +551,8 @@ protected:
friend class ClassDB;
virtual void _validate_property(PropertyInfo &property) const;
+ void _disconnect(const StringName &p_signal, Object *p_to_object, const StringName &p_to_method, bool p_force = false);
+
public: //should be protected, but bug in clang++
static void initialize_class();
_FORCE_INLINE_ static void register_custom_data_to_otdb(){};
diff --git a/core/os/os.h b/core/os/os.h
index dd783408e8..12c0222ad4 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -254,7 +254,7 @@ public:
virtual String get_executable_path() const;
virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false) = 0;
- virtual Error kill(const ProcessID &p_pid) = 0;
+ virtual Error kill(const ProcessID &p_pid, const int p_max_wait_msec = -1) = 0;
virtual int get_process_id() const;
virtual Error shell_open(String p_uri);
diff --git a/core/reference.h b/core/reference.h
index 0d6b1ced6e..25e02180fa 100644
--- a/core/reference.h
+++ b/core/reference.h
@@ -87,6 +87,13 @@ class Ref {
//virtual Reference * get_reference() const { return reference; }
public:
+ _FORCE_INLINE_ bool operator==(const T *p_ptr) const {
+ return reference == p_ptr;
+ }
+ _FORCE_INLINE_ bool operator!=(const T *p_ptr) const {
+ return reference != p_ptr;
+ }
+
_FORCE_INLINE_ bool operator<(const Ref<T> &p_r) const {
return reference < p_r.reference;
diff --git a/core/resource.cpp b/core/resource.cpp
index 87ff4d3c2a..3078eb135a 100644
--- a/core/resource.cpp
+++ b/core/resource.cpp
@@ -151,7 +151,7 @@ Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, Map<Ref<Res
List<PropertyInfo> plist;
get_property_list(&plist);
- Resource *r = (Resource *)ClassDB::instance(get_class());
+ Resource *r = Object::cast_to<Resource>(ClassDB::instance(get_class()));
ERR_FAIL_COND_V(!r, Ref<Resource>());
r->local_scene = p_for_scene;
@@ -182,7 +182,9 @@ Ref<Resource> Resource::duplicate_for_local_scene(Node *p_for_scene, Map<Ref<Res
r->set(E->get().name, p);
}
- return Ref<Resource>(r);
+ RES res = Ref<Resource>(r);
+
+ return res;
}
void Resource::configure_for_local_scene(Node *p_for_scene, Map<Ref<Resource>, Ref<Resource> > &remap_cache) {
diff --git a/core/variant_call.cpp b/core/variant_call.cpp
index ea51419233..1c50df75f5 100644
--- a/core/variant_call.cpp
+++ b/core/variant_call.cpp
@@ -1920,23 +1920,11 @@ void register_variant_methods() {
transform_x.set(1, 0, 0, 0, 1, 0, 0, 0, -1, 0, 0, 0);
_VariantCall::add_variant_constant(Variant::TRANSFORM, "FLIP_Z", transform_z);
- _VariantCall::add_variant_constant(Variant::PLANE, "X", Plane(Vector3(1, 0, 0), 0));
- _VariantCall::add_variant_constant(Variant::PLANE, "Y", Plane(Vector3(0, 1, 0), 0));
- _VariantCall::add_variant_constant(Variant::PLANE, "Z", Plane(Vector3(0, 0, 1), 0));
+ _VariantCall::add_variant_constant(Variant::PLANE, "PLANE_YZ", Plane(Vector3(1, 0, 0), 0));
+ _VariantCall::add_variant_constant(Variant::PLANE, "PLANE_XZ", Plane(Vector3(0, 1, 0), 0));
+ _VariantCall::add_variant_constant(Variant::PLANE, "PLANE_XY", Plane(Vector3(0, 0, 1), 0));
_VariantCall::add_variant_constant(Variant::QUAT, "IDENTITY", Quat(0, 0, 0, 1));
-
- CharType black_circle[2] = { 0x25CF, 0 };
- _VariantCall::add_variant_constant(Variant::STRING, "BLACK_CIRCLE", String(black_circle));
- CharType white_circle[2] = { 0x25CB, 0 };
- _VariantCall::add_variant_constant(Variant::STRING, "WHITE_CIRCLE", String(white_circle));
- CharType black_diamond[2] = { 0x25C6, 0 };
- _VariantCall::add_variant_constant(Variant::STRING, "BLACK_DIAMOND", String(black_diamond));
- CharType white_diamond[2] = { 0x25C7, 0 };
- _VariantCall::add_variant_constant(Variant::STRING, "WHITE_DIAMOND", String(white_diamond));
-
- _VariantCall::add_variant_constant(Variant::NODE_PATH, "CURRENT", String("."));
- _VariantCall::add_variant_constant(Variant::NODE_PATH, "PARENT", String(".."));
}
void unregister_variant_methods() {
diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml
index 47553659c9..d2c6a853ad 100644
--- a/doc/classes/@GlobalScope.xml
+++ b/doc/classes/@GlobalScope.xml
@@ -1262,12 +1262,14 @@
</constant>
<constant name="PROPERTY_HINT_MULTILINE_TEXT" value="18" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_COLOR_NO_ALPHA" value="19" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_PLACEHOLDER_TEXT" value="19" enum="PropertyHint">
</constant>
- <constant name="PROPERTY_HINT_IMAGE_COMPRESS_LOSSY" value="20" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_COLOR_NO_ALPHA" value="20" enum="PropertyHint">
+ </constant>
+ <constant name="PROPERTY_HINT_IMAGE_COMPRESS_LOSSY" value="21" enum="PropertyHint">
Hints that the image is compressed using lossy compression.
</constant>
- <constant name="PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS" value="21" enum="PropertyHint">
+ <constant name="PROPERTY_HINT_IMAGE_COMPRESS_LOSSLESS" value="22" enum="PropertyHint">
Hints that the image is compressed using lossless compression.
</constant>
<constant name="PROPERTY_USAGE_STORAGE" value="1" enum="PropertyUsageFlags">
diff --git a/doc/classes/AnimationNode.xml b/doc/classes/AnimationNode.xml
index 2f991699d6..d9bad150df 100644
--- a/doc/classes/AnimationNode.xml
+++ b/doc/classes/AnimationNode.xml
@@ -9,14 +9,6 @@
<demos>
</demos>
<methods>
- <method name="_parent_set" qualifiers="virtual">
- <return type="void">
- </return>
- <argument index="0" name="parent" type="Object">
- </argument>
- <description>
- </description>
- </method>
<method name="add_input">
<return type="void">
</return>
@@ -62,40 +54,26 @@
<method name="blend_node">
<return type="float">
</return>
- <argument index="0" name="node" type="AnimationNode">
+ <argument index="0" name="name" type="String">
</argument>
- <argument index="1" name="time" type="float">
+ <argument index="1" name="node" type="AnimationNode">
</argument>
- <argument index="2" name="seek" type="bool">
+ <argument index="2" name="time" type="float">
</argument>
- <argument index="3" name="blend" type="float">
+ <argument index="3" name="seek" type="bool">
</argument>
- <argument index="4" name="filter" type="int" enum="AnimationNode.FilterAction" default="0">
+ <argument index="4" name="blend" type="float">
</argument>
- <argument index="5" name="optimize" type="bool" default="true">
+ <argument index="5" name="filter" type="int" enum="AnimationNode.FilterAction" default="0">
</argument>
- <description>
- </description>
- </method>
- <method name="get_caption" qualifiers="virtual">
- <return type="String">
- </return>
- <description>
- </description>
- </method>
- <method name="get_input_activity" qualifiers="const">
- <return type="float">
- </return>
- <argument index="0" name="input" type="int">
+ <argument index="6" name="optimize" type="bool" default="true">
</argument>
<description>
</description>
</method>
- <method name="get_input_connection">
+ <method name="get_caption" qualifiers="virtual">
<return type="String">
</return>
- <argument index="0" name="input" type="int">
- </argument>
<description>
</description>
</method>
@@ -113,21 +91,11 @@
<description>
</description>
</method>
- <method name="get_parent" qualifiers="const">
- <return type="AnimationNode">
- </return>
- <description>
- </description>
- </method>
- <method name="get_position" qualifiers="const">
- <return type="Vector2">
- </return>
- <description>
- </description>
- </method>
- <method name="get_tree" qualifiers="const">
- <return type="AnimationTree">
+ <method name="get_parameter" qualifiers="const">
+ <return type="Variant">
</return>
+ <argument index="0" name="name" type="String">
+ </argument>
<description>
</description>
</method>
@@ -173,18 +141,12 @@
<description>
</description>
</method>
- <method name="set_parent">
+ <method name="set_parameter">
<return type="void">
</return>
- <argument index="0" name="parent" type="Object">
+ <argument index="0" name="name" type="String">
</argument>
- <description>
- </description>
- </method>
- <method name="set_position">
- <return type="void">
- </return>
- <argument index="0" name="position" type="Vector2">
+ <argument index="1" name="value" type="Variant">
</argument>
<description>
</description>
@@ -199,6 +161,10 @@
<description>
</description>
</signal>
+ <signal name="tree_changed">
+ <description>
+ </description>
+ </signal>
</signals>
<constants>
<constant name="FILTER_IGNORE" value="0" enum="FilterAction">
diff --git a/doc/classes/AnimationNodeAdd2.xml b/doc/classes/AnimationNodeAdd2.xml
index 267eec6406..9297faa1b7 100644
--- a/doc/classes/AnimationNodeAdd2.xml
+++ b/doc/classes/AnimationNodeAdd2.xml
@@ -11,8 +11,6 @@
<methods>
</methods>
<members>
- <member name="amount" type="float" setter="set_amount" getter="get_amount">
- </member>
<member name="sync" type="bool" setter="set_use_sync" getter="is_using_sync">
</member>
</members>
diff --git a/doc/classes/AnimationNodeAdd3.xml b/doc/classes/AnimationNodeAdd3.xml
index 6596b76f85..deb8d74a38 100644
--- a/doc/classes/AnimationNodeAdd3.xml
+++ b/doc/classes/AnimationNodeAdd3.xml
@@ -11,8 +11,6 @@
<methods>
</methods>
<members>
- <member name="amount" type="float" setter="set_amount" getter="get_amount">
- </member>
<member name="sync" type="bool" setter="set_use_sync" getter="is_using_sync">
</member>
</members>
diff --git a/doc/classes/AnimationNodeBlend2.xml b/doc/classes/AnimationNodeBlend2.xml
index 8ef114f69b..42bb12d9d0 100644
--- a/doc/classes/AnimationNodeBlend2.xml
+++ b/doc/classes/AnimationNodeBlend2.xml
@@ -11,8 +11,6 @@
<methods>
</methods>
<members>
- <member name="amount" type="float" setter="set_amount" getter="get_amount">
- </member>
<member name="sync" type="bool" setter="set_use_sync" getter="is_using_sync">
</member>
</members>
diff --git a/doc/classes/AnimationNodeBlend3.xml b/doc/classes/AnimationNodeBlend3.xml
index 094810d008..cd3d9f9bc8 100644
--- a/doc/classes/AnimationNodeBlend3.xml
+++ b/doc/classes/AnimationNodeBlend3.xml
@@ -11,8 +11,6 @@
<methods>
</methods>
<members>
- <member name="amount" type="float" setter="set_amount" getter="get_amount">
- </member>
<member name="sync" type="bool" setter="set_use_sync" getter="is_using_sync">
</member>
</members>
diff --git a/doc/classes/AnimationNodeBlendSpace1D.xml b/doc/classes/AnimationNodeBlendSpace1D.xml
index 96c94a8972..2113948b2e 100644
--- a/doc/classes/AnimationNodeBlendSpace1D.xml
+++ b/doc/classes/AnimationNodeBlendSpace1D.xml
@@ -73,8 +73,6 @@
</method>
</methods>
<members>
- <member name="blend_pos" type="float" setter="set_blend_pos" getter="get_blend_pos">
- </member>
<member name="max_space" type="float" setter="set_max_space" getter="get_max_space">
</member>
<member name="min_space" type="float" setter="set_min_space" getter="get_min_space">
diff --git a/doc/classes/AnimationNodeBlendSpace2D.xml b/doc/classes/AnimationNodeBlendSpace2D.xml
index 31dc7eebac..39d780b6ef 100644
--- a/doc/classes/AnimationNodeBlendSpace2D.xml
+++ b/doc/classes/AnimationNodeBlendSpace2D.xml
@@ -113,8 +113,6 @@
<members>
<member name="auto_triangles" type="bool" setter="set_auto_triangles" getter="get_auto_triangles">
</member>
- <member name="blend_position" type="Vector2" setter="set_blend_position" getter="get_blend_position">
- </member>
<member name="max_space" type="Vector2" setter="set_max_space" getter="get_max_space">
</member>
<member name="min_space" type="Vector2" setter="set_min_space" getter="get_min_space">
diff --git a/doc/classes/AnimationNodeBlendTree.xml b/doc/classes/AnimationNodeBlendTree.xml
index 88257883a3..cd28232908 100644
--- a/doc/classes/AnimationNodeBlendTree.xml
+++ b/doc/classes/AnimationNodeBlendTree.xml
@@ -16,6 +16,8 @@
</argument>
<argument index="1" name="node" type="AnimationNode">
</argument>
+ <argument index="2" name="position" type="Vector2" default="Vector2( 0, 0 )">
+ </argument>
<description>
</description>
</method>
@@ -49,6 +51,14 @@
<description>
</description>
</method>
+ <method name="get_node_position" qualifiers="const">
+ <return type="Vector2">
+ </return>
+ <argument index="0" name="name" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="has_node" qualifiers="const">
<return type="bool">
</return>
@@ -75,6 +85,16 @@
<description>
</description>
</method>
+ <method name="set_node_position">
+ <return type="void">
+ </return>
+ <argument index="0" name="name" type="String">
+ </argument>
+ <argument index="1" name="position" type="Vector2">
+ </argument>
+ <description>
+ </description>
+ </method>
</methods>
<members>
<member name="graph_offset" type="Vector2" setter="set_graph_offset" getter="get_graph_offset">
diff --git a/doc/classes/AnimationNodeOneShot.xml b/doc/classes/AnimationNodeOneShot.xml
index b59b6e2b83..12cb9775a2 100644
--- a/doc/classes/AnimationNodeOneShot.xml
+++ b/doc/classes/AnimationNodeOneShot.xml
@@ -15,12 +15,6 @@
<description>
</description>
</method>
- <method name="is_active" qualifiers="const">
- <return type="bool">
- </return>
- <description>
- </description>
- </method>
<method name="set_mix_mode">
<return type="void">
</return>
@@ -29,18 +23,6 @@
<description>
</description>
</method>
- <method name="start">
- <return type="void">
- </return>
- <description>
- </description>
- </method>
- <method name="stop">
- <return type="void">
- </return>
- <description>
- </description>
- </method>
</methods>
<members>
<member name="autorestart" type="bool" setter="set_autorestart" getter="has_autorestart">
diff --git a/doc/classes/AnimationNodeStateMachine.xml b/doc/classes/AnimationNodeStateMachine.xml
index ad40f20db1..ed4098d938 100644
--- a/doc/classes/AnimationNodeStateMachine.xml
+++ b/doc/classes/AnimationNodeStateMachine.xml
@@ -16,6 +16,8 @@
</argument>
<argument index="1" name="node" type="AnimationNode">
</argument>
+ <argument index="2" name="position" type="Vector2" default="Vector2( 0, 0 )">
+ </argument>
<description>
</description>
</method>
@@ -31,12 +33,6 @@
<description>
</description>
</method>
- <method name="get_current_node" qualifiers="const">
- <return type="String">
- </return>
- <description>
- </description>
- </method>
<method name="get_end_node" qualifiers="const">
<return type="String">
</return>
@@ -65,6 +61,14 @@
<description>
</description>
</method>
+ <method name="get_node_position" qualifiers="const">
+ <return type="Vector2">
+ </return>
+ <argument index="0" name="name" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="get_start_node" qualifiers="const">
<return type="String">
</return>
@@ -101,12 +105,6 @@
<description>
</description>
</method>
- <method name="get_travel_path" qualifiers="const">
- <return type="PoolStringArray">
- </return>
- <description>
- </description>
- </method>
<method name="has_node" qualifiers="const">
<return type="bool">
</return>
@@ -115,21 +113,13 @@
<description>
</description>
</method>
- <method name="has_transition">
- <return type="void">
+ <method name="has_transition" qualifiers="const">
+ <return type="bool">
</return>
<argument index="0" name="from" type="String">
</argument>
<argument index="1" name="to" type="String">
</argument>
- <argument index="2" name="arg2" type="AnimationNodeStateMachineTransition">
- </argument>
- <description>
- </description>
- </method>
- <method name="is_playing" qualifiers="const">
- <return type="bool">
- </return>
<description>
</description>
</method>
@@ -185,32 +175,20 @@
<description>
</description>
</method>
- <method name="set_start_node">
+ <method name="set_node_position">
<return type="void">
</return>
<argument index="0" name="name" type="String">
</argument>
- <description>
- </description>
- </method>
- <method name="start">
- <return type="void">
- </return>
- <argument index="0" name="node" type="String">
+ <argument index="1" name="position" type="Vector2">
</argument>
<description>
</description>
</method>
- <method name="stop">
+ <method name="set_start_node">
<return type="void">
</return>
- <description>
- </description>
- </method>
- <method name="travel">
- <return type="bool">
- </return>
- <argument index="0" name="to_node" type="String">
+ <argument index="0" name="name" type="String">
</argument>
<description>
</description>
diff --git a/doc/classes/AnimationNodeStateMachinePlayback.xml b/doc/classes/AnimationNodeStateMachinePlayback.xml
new file mode 100644
index 0000000000..6bf9504efb
--- /dev/null
+++ b/doc/classes/AnimationNodeStateMachinePlayback.xml
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="AnimationNodeStateMachinePlayback" inherits="Resource" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="get_current_node" qualifiers="const">
+ <return type="String">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_travel_path" qualifiers="const">
+ <return type="PoolStringArray">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="is_playing" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="start">
+ <return type="void">
+ </return>
+ <argument index="0" name="node" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="stop">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="travel">
+ <return type="void">
+ </return>
+ <argument index="0" name="to_node" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/AnimationNodeStateMachineTransition.xml b/doc/classes/AnimationNodeStateMachineTransition.xml
index 280a1413b8..e07a9fc980 100644
--- a/doc/classes/AnimationNodeStateMachineTransition.xml
+++ b/doc/classes/AnimationNodeStateMachineTransition.xml
@@ -11,6 +11,8 @@
<methods>
</methods>
<members>
+ <member name="advance_condition" type="String" setter="set_advance_condition" getter="get_advance_condition">
+ </member>
<member name="auto_advance" type="bool" setter="set_auto_advance" getter="has_auto_advance">
</member>
<member name="disabled" type="bool" setter="set_disabled" getter="is_disabled">
@@ -22,6 +24,12 @@
<member name="xfade_time" type="float" setter="set_xfade_time" getter="get_xfade_time">
</member>
</members>
+ <signals>
+ <signal name="advance_condition_changed">
+ <description>
+ </description>
+ </signal>
+ </signals>
<constants>
<constant name="SWITCH_MODE_IMMEDIATE" value="0" enum="SwitchMode">
</constant>
diff --git a/doc/classes/AnimationNodeTimeScale.xml b/doc/classes/AnimationNodeTimeScale.xml
index b3e90d190b..226c855b83 100644
--- a/doc/classes/AnimationNodeTimeScale.xml
+++ b/doc/classes/AnimationNodeTimeScale.xml
@@ -10,10 +10,6 @@
</demos>
<methods>
</methods>
- <members>
- <member name="scale" type="float" setter="set_scale" getter="get_scale">
- </member>
- </members>
<constants>
</constants>
</class>
diff --git a/doc/classes/AnimationNodeTimeSeek.xml b/doc/classes/AnimationNodeTimeSeek.xml
index a479208128..707b09a4a5 100644
--- a/doc/classes/AnimationNodeTimeSeek.xml
+++ b/doc/classes/AnimationNodeTimeSeek.xml
@@ -10,10 +10,6 @@
</demos>
<methods>
</methods>
- <members>
- <member name="seek_pos" type="float" setter="set_seek_pos" getter="get_seek_pos">
- </member>
- </members>
<constants>
</constants>
</class>
diff --git a/doc/classes/AnimationNodeTransition.xml b/doc/classes/AnimationNodeTransition.xml
index 3731fc05ed..317bc5ed69 100644
--- a/doc/classes/AnimationNodeTransition.xml
+++ b/doc/classes/AnimationNodeTransition.xml
@@ -11,8 +11,6 @@
<methods>
</methods>
<members>
- <member name="current" type="int" setter="set_current" getter="get_current">
- </member>
<member name="input_0/auto_advance" type="bool" setter="set_input_as_auto_advance" getter="is_input_set_as_auto_advance">
</member>
<member name="input_0/name" type="String" setter="set_input_caption" getter="get_input_caption">
diff --git a/doc/classes/AnimationTree.xml b/doc/classes/AnimationTree.xml
index 9b3679ae93..9a6a75079c 100644
--- a/doc/classes/AnimationTree.xml
+++ b/doc/classes/AnimationTree.xml
@@ -23,6 +23,16 @@
<description>
</description>
</method>
+ <method name="rename_parameter">
+ <return type="void">
+ </return>
+ <argument index="0" name="old_name" type="String">
+ </argument>
+ <argument index="1" name="new_name" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
</methods>
<members>
<member name="active" type="bool" setter="set_active" getter="is_active">
diff --git a/doc/classes/ArrayMesh.xml b/doc/classes/ArrayMesh.xml
index 5ccf0b55aa..1e2478dd14 100644
--- a/doc/classes/ArrayMesh.xml
+++ b/doc/classes/ArrayMesh.xml
@@ -76,9 +76,9 @@
<method name="lightmap_unwrap">
<return type="int" enum="Error">
</return>
- <argument index="0" name="arg0" type="Transform">
+ <argument index="0" name="transform" type="Transform">
</argument>
- <argument index="1" name="arg1" type="float">
+ <argument index="1" name="texel_size" type="float">
</argument>
<description>
Will perform a UV unwrap on the [code]ArrayMesh[/code] to prepare the mesh for lightmapping.
diff --git a/doc/classes/AudioEffectRecord.xml b/doc/classes/AudioEffectRecord.xml
new file mode 100644
index 0000000000..b7771fc9c5
--- /dev/null
+++ b/doc/classes/AudioEffectRecord.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="AudioEffectRecord" inherits="AudioEffect" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="get_recording" qualifiers="const">
+ <return type="AudioStreamSample">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="is_recording_active" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="set_recording_active">
+ <return type="void">
+ </return>
+ <argument index="0" name="record" type="bool">
+ </argument>
+ <description>
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="format" type="int" setter="set_format" getter="get_format" enum="AudioStreamSample.Format">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/AudioServer.xml b/doc/classes/AudioServer.xml
index 51df1e99dd..37756bcfd8 100644
--- a/doc/classes/AudioServer.xml
+++ b/doc/classes/AudioServer.xml
@@ -34,6 +34,26 @@
Adds an [AudioEffect] effect to the bus [code]bus_idx[/code] at [code]at_position[/code].
</description>
</method>
+ <method name="capture_get_device">
+ <return type="String">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="capture_get_device_list">
+ <return type="Array">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="capture_set_device">
+ <return type="void">
+ </return>
+ <argument index="0" name="name" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="generate_bus_layout" qualifiers="const">
<return type="AudioBusLayout">
</return>
@@ -328,7 +348,7 @@
<method name="set_device">
<return type="void">
</return>
- <argument index="0" name="arg0" type="String">
+ <argument index="0" name="device" type="String">
</argument>
<description>
</description>
diff --git a/doc/classes/AudioStreamMicrophone.xml b/doc/classes/AudioStreamMicrophone.xml
new file mode 100644
index 0000000000..079555d17e
--- /dev/null
+++ b/doc/classes/AudioStreamMicrophone.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="AudioStreamMicrophone" inherits="AudioStream" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/AudioStreamSample.xml b/doc/classes/AudioStreamSample.xml
index b6abda1a6f..9e56cc6016 100644
--- a/doc/classes/AudioStreamSample.xml
+++ b/doc/classes/AudioStreamSample.xml
@@ -11,6 +11,14 @@
<demos>
</demos>
<methods>
+ <method name="save_to_wav">
+ <return type="void">
+ </return>
+ <argument index="0" name="path" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
</methods>
<members>
<member name="data" type="PoolByteArray" setter="set_data" getter="get_data">
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/BitMap.xml b/doc/classes/BitMap.xml
index 98f554ebaa..7fe6a2acef 100644
--- a/doc/classes/BitMap.xml
+++ b/doc/classes/BitMap.xml
@@ -54,6 +54,26 @@
Returns the amount of bitmap elements that are set to true.
</description>
</method>
+ <method name="grow_mask">
+ <return type="void">
+ </return>
+ <argument index="0" name="pixels" type="int">
+ </argument>
+ <argument index="1" name="rect" type="Rect2">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="opaque_to_polygons" qualifiers="const">
+ <return type="Array">
+ </return>
+ <argument index="0" name="rect" type="Rect2">
+ </argument>
+ <argument index="1" name="epsilon" type="float" default="2.0">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="set_bit">
<return type="void">
</return>
diff --git a/doc/classes/Camera.xml b/doc/classes/Camera.xml
index 7f7f152ae9..c7eb365891 100644
--- a/doc/classes/Camera.xml
+++ b/doc/classes/Camera.xml
@@ -27,6 +27,14 @@
Gets the camera transform. Subclassed cameras (such as CharacterCamera) may provide different transforms than the [Node] transform.
</description>
</method>
+ <method name="get_cull_mask_bit" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="layer" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="is_position_behind" qualifiers="const">
<return type="bool">
</return>
@@ -79,6 +87,16 @@
Returns a 3D position in worldspace, that is the result of projecting a point on the [Viewport] rectangle by the camera projection. This is useful for casting rays in the form of (origin, normal) for object intersection or picking.
</description>
</method>
+ <method name="set_cull_mask_bit">
+ <return type="void">
+ </return>
+ <argument index="0" name="layer" type="int">
+ </argument>
+ <argument index="1" name="enable" type="bool">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="set_orthogonal">
<return type="void">
</return>
diff --git a/doc/classes/Color.xml b/doc/classes/Color.xml
index c675bbe994..b66239181a 100644
--- a/doc/classes/Color.xml
+++ b/doc/classes/Color.xml
@@ -305,5 +305,295 @@
</member>
</members>
<constants>
+ <constant name="gray" value="Color( 0.75, 0.75, 0.75, 1 )">
+ </constant>
+ <constant name="aliceblue" value="Color( 0.94, 0.97, 1, 1 )">
+ </constant>
+ <constant name="antiquewhite" value="Color( 0.98, 0.92, 0.84, 1 )">
+ </constant>
+ <constant name="aqua" value="Color( 0, 1, 1, 1 )">
+ </constant>
+ <constant name="aquamarine" value="Color( 0.5, 1, 0.83, 1 )">
+ </constant>
+ <constant name="azure" value="Color( 0.94, 1, 1, 1 )">
+ </constant>
+ <constant name="beige" value="Color( 0.96, 0.96, 0.86, 1 )">
+ </constant>
+ <constant name="bisque" value="Color( 1, 0.89, 0.77, 1 )">
+ </constant>
+ <constant name="black" value="Color( 0, 0, 0, 1 )">
+ </constant>
+ <constant name="blanchedalmond" value="Color( 1, 0.92, 0.8, 1 )">
+ </constant>
+ <constant name="blue" value="Color( 0, 0, 1, 1 )">
+ </constant>
+ <constant name="blueviolet" value="Color( 0.54, 0.17, 0.89, 1 )">
+ </constant>
+ <constant name="brown" value="Color( 0.65, 0.16, 0.16, 1 )">
+ </constant>
+ <constant name="burlywood" value="Color( 0.87, 0.72, 0.53, 1 )">
+ </constant>
+ <constant name="cadetblue" value="Color( 0.37, 0.62, 0.63, 1 )">
+ </constant>
+ <constant name="chartreuse" value="Color( 0.5, 1, 0, 1 )">
+ </constant>
+ <constant name="chocolate" value="Color( 0.82, 0.41, 0.12, 1 )">
+ </constant>
+ <constant name="coral" value="Color( 1, 0.5, 0.31, 1 )">
+ </constant>
+ <constant name="cornflower" value="Color( 0.39, 0.58, 0.93, 1 )">
+ </constant>
+ <constant name="cornsilk" value="Color( 1, 0.97, 0.86, 1 )">
+ </constant>
+ <constant name="crimson" value="Color( 0.86, 0.08, 0.24, 1 )">
+ </constant>
+ <constant name="cyan" value="Color( 0, 1, 1, 1 )">
+ </constant>
+ <constant name="darkblue" value="Color( 0, 0, 0.55, 1 )">
+ </constant>
+ <constant name="darkcyan" value="Color( 0, 0.55, 0.55, 1 )">
+ </constant>
+ <constant name="darkgoldenrod" value="Color( 0.72, 0.53, 0.04, 1 )">
+ </constant>
+ <constant name="darkgray" value="Color( 0.66, 0.66, 0.66, 1 )">
+ </constant>
+ <constant name="darkgreen" value="Color( 0, 0.39, 0, 1 )">
+ </constant>
+ <constant name="darkkhaki" value="Color( 0.74, 0.72, 0.42, 1 )">
+ </constant>
+ <constant name="darkmagenta" value="Color( 0.55, 0, 0.55, 1 )">
+ </constant>
+ <constant name="darkolivegreen" value="Color( 0.33, 0.42, 0.18, 1 )">
+ </constant>
+ <constant name="darkorange" value="Color( 1, 0.55, 0, 1 )">
+ </constant>
+ <constant name="darkorchid" value="Color( 0.6, 0.2, 0.8, 1 )">
+ </constant>
+ <constant name="darkred" value="Color( 0.55, 0, 0, 1 )">
+ </constant>
+ <constant name="darksalmon" value="Color( 0.91, 0.59, 0.48, 1 )">
+ </constant>
+ <constant name="darkseagreen" value="Color( 0.56, 0.74, 0.56, 1 )">
+ </constant>
+ <constant name="darkslateblue" value="Color( 0.28, 0.24, 0.55, 1 )">
+ </constant>
+ <constant name="darkslategray" value="Color( 0.18, 0.31, 0.31, 1 )">
+ </constant>
+ <constant name="darkturquoise" value="Color( 0, 0.81, 0.82, 1 )">
+ </constant>
+ <constant name="darkviolet" value="Color( 0.58, 0, 0.83, 1 )">
+ </constant>
+ <constant name="deeppink" value="Color( 1, 0.08, 0.58, 1 )">
+ </constant>
+ <constant name="deepskyblue" value="Color( 0, 0.75, 1, 1 )">
+ </constant>
+ <constant name="dimgray" value="Color( 0.41, 0.41, 0.41, 1 )">
+ </constant>
+ <constant name="dodgerblue" value="Color( 0.12, 0.56, 1, 1 )">
+ </constant>
+ <constant name="firebrick" value="Color( 0.7, 0.13, 0.13, 1 )">
+ </constant>
+ <constant name="floralwhite" value="Color( 1, 0.98, 0.94, 1 )">
+ </constant>
+ <constant name="forestgreen" value="Color( 0.13, 0.55, 0.13, 1 )">
+ </constant>
+ <constant name="fuchsia" value="Color( 1, 0, 1, 1 )">
+ </constant>
+ <constant name="gainsboro" value="Color( 0.86, 0.86, 0.86, 1 )">
+ </constant>
+ <constant name="ghostwhite" value="Color( 0.97, 0.97, 1, 1 )">
+ </constant>
+ <constant name="gold" value="Color( 1, 0.84, 0, 1 )">
+ </constant>
+ <constant name="goldenrod" value="Color( 0.85, 0.65, 0.13, 1 )">
+ </constant>
+ <constant name="green" value="Color( 0, 1, 0, 1 )">
+ </constant>
+ <constant name="greenyellow" value="Color( 0.68, 1, 0.18, 1 )">
+ </constant>
+ <constant name="honeydew" value="Color( 0.94, 1, 0.94, 1 )">
+ </constant>
+ <constant name="hotpink" value="Color( 1, 0.41, 0.71, 1 )">
+ </constant>
+ <constant name="indianred" value="Color( 0.8, 0.36, 0.36, 1 )">
+ </constant>
+ <constant name="indigo" value="Color( 0.29, 0, 0.51, 1 )">
+ </constant>
+ <constant name="ivory" value="Color( 1, 1, 0.94, 1 )">
+ </constant>
+ <constant name="khaki" value="Color( 0.94, 0.9, 0.55, 1 )">
+ </constant>
+ <constant name="lavender" value="Color( 0.9, 0.9, 0.98, 1 )">
+ </constant>
+ <constant name="lavenderblush" value="Color( 1, 0.94, 0.96, 1 )">
+ </constant>
+ <constant name="lawngreen" value="Color( 0.49, 0.99, 0, 1 )">
+ </constant>
+ <constant name="lemonchiffon" value="Color( 1, 0.98, 0.8, 1 )">
+ </constant>
+ <constant name="lightblue" value="Color( 0.68, 0.85, 0.9, 1 )">
+ </constant>
+ <constant name="lightcoral" value="Color( 0.94, 0.5, 0.5, 1 )">
+ </constant>
+ <constant name="lightcyan" value="Color( 0.88, 1, 1, 1 )">
+ </constant>
+ <constant name="lightgoldenrod" value="Color( 0.98, 0.98, 0.82, 1 )">
+ </constant>
+ <constant name="lightgray" value="Color( 0.83, 0.83, 0.83, 1 )">
+ </constant>
+ <constant name="lightgreen" value="Color( 0.56, 0.93, 0.56, 1 )">
+ </constant>
+ <constant name="lightpink" value="Color( 1, 0.71, 0.76, 1 )">
+ </constant>
+ <constant name="lightsalmon" value="Color( 1, 0.63, 0.48, 1 )">
+ </constant>
+ <constant name="lightseagreen" value="Color( 0.13, 0.7, 0.67, 1 )">
+ </constant>
+ <constant name="lightskyblue" value="Color( 0.53, 0.81, 0.98, 1 )">
+ </constant>
+ <constant name="lightslategray" value="Color( 0.47, 0.53, 0.6, 1 )">
+ </constant>
+ <constant name="lightsteelblue" value="Color( 0.69, 0.77, 0.87, 1 )">
+ </constant>
+ <constant name="lightyellow" value="Color( 1, 1, 0.88, 1 )">
+ </constant>
+ <constant name="lime" value="Color( 0, 1, 0, 1 )">
+ </constant>
+ <constant name="limegreen" value="Color( 0.2, 0.8, 0.2, 1 )">
+ </constant>
+ <constant name="linen" value="Color( 0.98, 0.94, 0.9, 1 )">
+ </constant>
+ <constant name="magenta" value="Color( 1, 0, 1, 1 )">
+ </constant>
+ <constant name="maroon" value="Color( 0.69, 0.19, 0.38, 1 )">
+ </constant>
+ <constant name="mediumaquamarine" value="Color( 0.4, 0.8, 0.67, 1 )">
+ </constant>
+ <constant name="mediumblue" value="Color( 0, 0, 0.8, 1 )">
+ </constant>
+ <constant name="mediumorchid" value="Color( 0.73, 0.33, 0.83, 1 )">
+ </constant>
+ <constant name="mediumpurple" value="Color( 0.58, 0.44, 0.86, 1 )">
+ </constant>
+ <constant name="mediumseagreen" value="Color( 0.24, 0.7, 0.44, 1 )">
+ </constant>
+ <constant name="mediumslateblue" value="Color( 0.48, 0.41, 0.93, 1 )">
+ </constant>
+ <constant name="mediumspringgreen" value="Color( 0, 0.98, 0.6, 1 )">
+ </constant>
+ <constant name="mediumturquoise" value="Color( 0.28, 0.82, 0.8, 1 )">
+ </constant>
+ <constant name="mediumvioletred" value="Color( 0.78, 0.08, 0.52, 1 )">
+ </constant>
+ <constant name="midnightblue" value="Color( 0.1, 0.1, 0.44, 1 )">
+ </constant>
+ <constant name="mintcream" value="Color( 0.96, 1, 0.98, 1 )">
+ </constant>
+ <constant name="mistyrose" value="Color( 1, 0.89, 0.88, 1 )">
+ </constant>
+ <constant name="moccasin" value="Color( 1, 0.89, 0.71, 1 )">
+ </constant>
+ <constant name="navajowhite" value="Color( 1, 0.87, 0.68, 1 )">
+ </constant>
+ <constant name="navyblue" value="Color( 0, 0, 0.5, 1 )">
+ </constant>
+ <constant name="oldlace" value="Color( 0.99, 0.96, 0.9, 1 )">
+ </constant>
+ <constant name="olive" value="Color( 0.5, 0.5, 0, 1 )">
+ </constant>
+ <constant name="olivedrab" value="Color( 0.42, 0.56, 0.14, 1 )">
+ </constant>
+ <constant name="orange" value="Color( 1, 0.65, 0, 1 )">
+ </constant>
+ <constant name="orangered" value="Color( 1, 0.27, 0, 1 )">
+ </constant>
+ <constant name="orchid" value="Color( 0.85, 0.44, 0.84, 1 )">
+ </constant>
+ <constant name="palegoldenrod" value="Color( 0.93, 0.91, 0.67, 1 )">
+ </constant>
+ <constant name="palegreen" value="Color( 0.6, 0.98, 0.6, 1 )">
+ </constant>
+ <constant name="paleturquoise" value="Color( 0.69, 0.93, 0.93, 1 )">
+ </constant>
+ <constant name="palevioletred" value="Color( 0.86, 0.44, 0.58, 1 )">
+ </constant>
+ <constant name="papayawhip" value="Color( 1, 0.94, 0.84, 1 )">
+ </constant>
+ <constant name="peachpuff" value="Color( 1, 0.85, 0.73, 1 )">
+ </constant>
+ <constant name="peru" value="Color( 0.8, 0.52, 0.25, 1 )">
+ </constant>
+ <constant name="pink" value="Color( 1, 0.75, 0.8, 1 )">
+ </constant>
+ <constant name="plum" value="Color( 0.87, 0.63, 0.87, 1 )">
+ </constant>
+ <constant name="powderblue" value="Color( 0.69, 0.88, 0.9, 1 )">
+ </constant>
+ <constant name="purple" value="Color( 0.63, 0.13, 0.94, 1 )">
+ </constant>
+ <constant name="rebeccapurple" value="Color( 0.4, 0.2, 0.6, 1 )">
+ </constant>
+ <constant name="red" value="Color( 1, 0, 0, 1 )">
+ </constant>
+ <constant name="rosybrown" value="Color( 0.74, 0.56, 0.56, 1 )">
+ </constant>
+ <constant name="royalblue" value="Color( 0.25, 0.41, 0.88, 1 )">
+ </constant>
+ <constant name="saddlebrown" value="Color( 0.55, 0.27, 0.07, 1 )">
+ </constant>
+ <constant name="salmon" value="Color( 0.98, 0.5, 0.45, 1 )">
+ </constant>
+ <constant name="sandybrown" value="Color( 0.96, 0.64, 0.38, 1 )">
+ </constant>
+ <constant name="seagreen" value="Color( 0.18, 0.55, 0.34, 1 )">
+ </constant>
+ <constant name="seashell" value="Color( 1, 0.96, 0.93, 1 )">
+ </constant>
+ <constant name="sienna" value="Color( 0.63, 0.32, 0.18, 1 )">
+ </constant>
+ <constant name="silver" value="Color( 0.75, 0.75, 0.75, 1 )">
+ </constant>
+ <constant name="skyblue" value="Color( 0.53, 0.81, 0.92, 1 )">
+ </constant>
+ <constant name="slateblue" value="Color( 0.42, 0.35, 0.8, 1 )">
+ </constant>
+ <constant name="slategray" value="Color( 0.44, 0.5, 0.56, 1 )">
+ </constant>
+ <constant name="snow" value="Color( 1, 0.98, 0.98, 1 )">
+ </constant>
+ <constant name="springgreen" value="Color( 0, 1, 0.5, 1 )">
+ </constant>
+ <constant name="steelblue" value="Color( 0.27, 0.51, 0.71, 1 )">
+ </constant>
+ <constant name="tan" value="Color( 0.82, 0.71, 0.55, 1 )">
+ </constant>
+ <constant name="teal" value="Color( 0, 0.5, 0.5, 1 )">
+ </constant>
+ <constant name="thistle" value="Color( 0.85, 0.75, 0.85, 1 )">
+ </constant>
+ <constant name="tomato" value="Color( 1, 0.39, 0.28, 1 )">
+ </constant>
+ <constant name="turquoise" value="Color( 0.25, 0.88, 0.82, 1 )">
+ </constant>
+ <constant name="violet" value="Color( 0.93, 0.51, 0.93, 1 )">
+ </constant>
+ <constant name="webgray" value="Color( 0.5, 0.5, 0.5, 1 )">
+ </constant>
+ <constant name="webgreen" value="Color( 0, 0.5, 0, 1 )">
+ </constant>
+ <constant name="webmaroon" value="Color( 0.5, 0, 0, 1 )">
+ </constant>
+ <constant name="webpurple" value="Color( 0.5, 0, 0.5, 1 )">
+ </constant>
+ <constant name="wheat" value="Color( 0.96, 0.87, 0.7, 1 )">
+ </constant>
+ <constant name="white" value="Color( 1, 1, 1, 1 )">
+ </constant>
+ <constant name="whitesmoke" value="Color( 0.96, 0.96, 0.96, 1 )">
+ </constant>
+ <constant name="yellow" value="Color( 1, 1, 0, 1 )">
+ </constant>
+ <constant name="yellowgreen" value="Color( 0.6, 0.8, 0.2, 1 )">
+ </constant>
</constants>
</class>
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..4301102e4a 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.
@@ -19,11 +19,17 @@
<demos>
</demos>
<methods>
+ <method name="_clips_input" qualifiers="virtual">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="_get_minimum_size" qualifiers="virtual">
<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 +135,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 +154,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 +179,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 +214,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 +227,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 +239,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 +263,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 +280,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 +327,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 +383,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 +466,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 +525,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 +543,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 +573,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 +594,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 +603,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 +762,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 +786,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 +912,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/Dictionary.xml b/doc/classes/Dictionary.xml
index 9fa1e3ea6c..800a76ccf1 100644
--- a/doc/classes/Dictionary.xml
+++ b/doc/classes/Dictionary.xml
@@ -33,6 +33,8 @@
</description>
</method>
<method name="erase">
+ <return type="bool">
+ </return>
<argument index="0" name="key" type="var">
</argument>
<description>
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/EditorFileSystem.xml b/doc/classes/EditorFileSystem.xml
index 5a8b506f9e..ade6d2034d 100644
--- a/doc/classes/EditorFileSystem.xml
+++ b/doc/classes/EditorFileSystem.xml
@@ -93,6 +93,10 @@
Remitted if a resource is reimported.
</description>
</signal>
+ <signal name="script_classes_updated">
+ <description>
+ </description>
+ </signal>
<signal name="sources_changed">
<argument index="0" name="exist" type="bool">
</argument>
diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml
index f5fbf8e313..62fc56e990 100644
--- a/doc/classes/EditorPlugin.xml
+++ b/doc/classes/EditorPlugin.xml
@@ -156,15 +156,6 @@
Clear all the state and reset the object being edited to zero. This ensures your plugin does not keep editing a currently existing node, or a node from the wrong scene.
</description>
</method>
- <method name="create_spatial_gizmo" qualifiers="virtual">
- <return type="EditorSpatialGizmo">
- </return>
- <argument index="0" name="for_spatial" type="Spatial">
- </argument>
- <description>
- This is used for plugins that create gizmos used by the spatial editor. Just check that the node passed in the "for_spatial" argument matches your plugin.
- </description>
- </method>
<method name="edit" qualifiers="virtual">
<return type="void">
</return>
diff --git a/doc/classes/EditorSpatialGizmo.xml b/doc/classes/EditorSpatialGizmo.xml
index 3636442b85..2081ae7a4d 100644
--- a/doc/classes/EditorSpatialGizmo.xml
+++ b/doc/classes/EditorSpatialGizmo.xml
@@ -33,9 +33,11 @@
</return>
<argument index="0" name="handles" type="PoolVector3Array">
</argument>
- <argument index="1" name="billboard" type="bool" default="false">
+ <argument index="1" name="material" type="Material">
</argument>
- <argument index="2" name="secondary" type="bool" default="false">
+ <argument index="2" name="billboard" type="bool" default="false">
+ </argument>
+ <argument index="3" name="secondary" type="bool" default="false">
</argument>
<description>
Add a list of handles (points) which can be used to deform the object being edited.
@@ -138,6 +140,14 @@
The [Camera] is also provided so screen coordinates can be converted to raycasts.
</description>
</method>
+ <method name="set_hidden">
+ <return type="void">
+ </return>
+ <argument index="0" name="hidden" type="bool">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="set_spatial_node">
<return type="void">
</return>
diff --git a/doc/classes/Expression.xml b/doc/classes/Expression.xml
new file mode 100644
index 0000000000..d5b831a61a
--- /dev/null
+++ b/doc/classes/Expression.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="Expression" inherits="Reference" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="execute">
+ <return type="Variant">
+ </return>
+ <argument index="0" name="inputs" type="Array" default="[ ]">
+ </argument>
+ <argument index="1" name="base_instance" type="Object" default="null">
+ </argument>
+ <argument index="2" name="show_error" type="bool" default="true">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_error_text" qualifiers="const">
+ <return type="String">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="has_execute_failed" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="parse">
+ <return type="int" enum="Error">
+ </return>
+ <argument index="0" name="expression" type="String">
+ </argument>
+ <argument index="1" name="input_names" type="PoolStringArray" default="PoolStringArray( )">
+ </argument>
+ <description>
+ </description>
+ </method>
+ </methods>
+ <constants>
+ </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..7fd1d7e8ac 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,15 +51,15 @@
<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">
<return type="void">
</return>
- <argument index="0" name="deadzone" type="String">
+ <argument index="0" name="action" type="String">
</argument>
- <argument index="1" name="arg1" type="float">
+ <argument index="1" name="deadzone" type="float">
</argument>
<description>
</description>
@@ -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/KinematicBody.xml b/doc/classes/KinematicBody.xml
index e6d537b895..0c66319ae7 100644
--- a/doc/classes/KinematicBody.xml
+++ b/doc/classes/KinematicBody.xml
@@ -65,8 +65,11 @@
</argument>
<argument index="1" name="infinite_inertia" type="bool" default="true">
</argument>
+ <argument index="2" name="test_only" type="bool" default="false">
+ </argument>
<description>
Moves the body along the vector [code]rel_vec[/code]. The body will stop if it collides. Returns a [KinematicCollision], which contains information about the collision.
+ If [code]test_only[/code] is [code]true[/true], the body does not move but the would-be collision information is given.
</description>
</method>
<method name="move_and_slide">
@@ -76,7 +79,7 @@
</argument>
<argument index="1" name="floor_normal" type="Vector3" default="Vector3( 0, 0, 0 )">
</argument>
- <argument index="2" name="slope_stop_min_velocity" type="float" default="0.05">
+ <argument index="2" name="stop_on_slope" type="bool" default="false">
</argument>
<argument index="3" name="max_slides" type="int" default="4">
</argument>
@@ -88,12 +91,32 @@
Moves the body along a vector. If the body collides with another, it will slide along the other body rather than stop immediately. If the other body is a [code]KinematicBody[/code] or [RigidBody], it will also be affected by the motion of the other body. You can use this to make moving or rotating platforms, or to make nodes push other nodes.
[code]linear_velocity[/code] is a value in pixels per second. Unlike in for example [method move_and_collide], you should [i]not[/i] multiply it with [code]delta[/code] — this is done by the method.
[code]floor_normal[/code] is the up direction, used to determine what is a wall and what is a floor or a ceiling. If set to the default value of [code]Vector3(0, 0, 0)[/code], everything is considered a wall. This is useful for topdown games.
- If the body is standing on a slope and the horizontal speed (relative to the floor's speed) goes below [code]slope_stop_min_velocity[/code], the body will stop completely. This prevents the body from sliding down slopes when you include gravity in [code]linear_velocity[/code]. When set to lower values, the body will not be able to stand still on steep slopes.
+ [i]TODO: Update for new stop_on_slode argument.[/i] If the body is standing on a slope and the horizontal speed (relative to the floor's speed) goes below [code]slope_stop_min_velocity[/code], the body will stop completely. This prevents the body from sliding down slopes when you include gravity in [code]linear_velocity[/code]. When set to lower values, the body will not be able to stand still on steep slopes.
If the body collides, it will change direction a maximum of [code]max_slides[/code] times before it stops.
[code]floor_max_angle[/code] is the maximum angle (in radians) where a slope is still considered a floor (or a ceiling), rather than a wall. The default value equals 45 degrees.
Returns the movement that remained when the body stopped. To get more detailed information about collisions that occurred, use [method get_slide_collision].
</description>
</method>
+ <method name="move_and_slide_with_snap">
+ <return type="Vector3">
+ </return>
+ <argument index="0" name="linear_velocity" type="Vector3">
+ </argument>
+ <argument index="1" name="snap" type="Vector3">
+ </argument>
+ <argument index="2" name="floor_normal" type="Vector3" default="Vector3( 0, 0, 0 )">
+ </argument>
+ <argument index="3" name="infinite_inertia" type="bool" default="true">
+ </argument>
+ <argument index="4" name="stop_on_slope" type="bool" default="false">
+ </argument>
+ <argument index="5" name="max_bounces" type="int" default="4">
+ </argument>
+ <argument index="6" name="floor_max_angle" type="float" default="0.785398">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="test_move">
<return type="bool">
</return>
@@ -109,20 +132,14 @@
</method>
</methods>
<members>
- <member name="axis_lock_angular_x" type="bool" setter="set_axis_lock" getter="get_axis_lock">
- </member>
- <member name="axis_lock_angular_y" type="bool" setter="set_axis_lock" getter="get_axis_lock">
- </member>
- <member name="axis_lock_angular_z" type="bool" setter="set_axis_lock" getter="get_axis_lock">
- </member>
- <member name="axis_lock_linear_x" type="bool" setter="set_axis_lock" getter="get_axis_lock">
+ <member name="collision/safe_margin" type="float" setter="set_safe_margin" getter="get_safe_margin">
+ If the body is at least this close to another body, this body will consider them to be colliding.
</member>
- <member name="axis_lock_linear_y" type="bool" setter="set_axis_lock" getter="get_axis_lock">
+ <member name="move_lock_x" type="bool" setter="set_axis_lock" getter="get_axis_lock">
</member>
- <member name="axis_lock_linear_z" type="bool" setter="set_axis_lock" getter="get_axis_lock">
+ <member name="move_lock_y" type="bool" setter="set_axis_lock" getter="get_axis_lock">
</member>
- <member name="collision/safe_margin" type="float" setter="set_safe_margin" getter="get_safe_margin">
- If the body is at least this close to another body, this body will consider them to be colliding.
+ <member name="move_lock_z" type="bool" setter="set_axis_lock" getter="get_axis_lock">
</member>
</members>
<constants>
diff --git a/doc/classes/KinematicBody2D.xml b/doc/classes/KinematicBody2D.xml
index fdc974630f..e48660a889 100644
--- a/doc/classes/KinematicBody2D.xml
+++ b/doc/classes/KinematicBody2D.xml
@@ -81,7 +81,7 @@
</argument>
<argument index="2" name="infinite_inertia" type="bool" default="true">
</argument>
- <argument index="3" name="slope_stop_min_velocity" type="float" default="5">
+ <argument index="3" name="stop_on_slope" type="bool" default="false">
</argument>
<argument index="4" name="max_bounces" type="int" default="4">
</argument>
@@ -91,7 +91,7 @@
Moves the body along a vector. If the body collides with another, it will slide along the other body rather than stop immediately. If the other body is a [code]KinematicBody2D[/code] or [RigidBody2D], it will also be affected by the motion of the other body. You can use this to make moving or rotating platforms, or to make nodes push other nodes.
[code]linear_velocity[/code] is a value in pixels per second. Unlike in for example [method move_and_collide], you should [i]not[/i] multiply it with [code]delta[/code] — this is done by the method.
[code]floor_normal[/code] is the up direction, used to determine what is a wall and what is a floor or a ceiling. If set to the default value of [code]Vector2(0, 0)[/code], everything is considered a wall. This is useful for topdown games.
- If the body is standing on a slope and the horizontal speed (relative to the floor's speed) goes below [code]slope_stop_min_velocity[/code], the body will stop completely. This prevents the body from sliding down slopes when you include gravity in [code]linear_velocity[/code]. When set to lower values, the body will not be able to stand still on steep slopes.
+ [i]TODO: Update for stop_on_slope argument.[/i] If the body is standing on a slope and the horizontal speed (relative to the floor's speed) goes below [code]slope_stop_min_velocity[/code], the body will stop completely. This prevents the body from sliding down slopes when you include gravity in [code]linear_velocity[/code]. When set to lower values, the body will not be able to stand still on steep slopes.
If the body collides, it will change direction a maximum of [code]max_bounces[/code] times before it stops.
[code]floor_max_angle[/code] is the maximum angle (in radians) where a slope is still considered a floor (or a ceiling), rather than a wall. The default value equals 45 degrees.
Returns the movement that remained when the body stopped. To get more detailed information about collisions that occurred, use [method get_slide_collision].
@@ -108,13 +108,15 @@
</argument>
<argument index="3" name="infinite_inertia" type="bool" default="true">
</argument>
- <argument index="4" name="slope_stop_min_velocity" type="float" default="5">
+ <argument index="4" name="stop_on_slope" type="bool" default="false">
</argument>
<argument index="5" name="max_bounces" type="int" default="4">
</argument>
<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 [method move_and_slide].
+ 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/LineEdit.xml b/doc/classes/LineEdit.xml
index d7a0385bb3..c244b8b7a7 100644
--- a/doc/classes/LineEdit.xml
+++ b/doc/classes/LineEdit.xml
@@ -88,6 +88,8 @@
<member name="caret_position" type="int" setter="set_cursor_position" getter="get_cursor_position">
The cursor's position inside the [code]LineEdit[/code]. When set, the text may scroll to accommodate it.
</member>
+ <member name="clear_button_enabled" type="bool" setter="set_clear_button_enabled" getter="is_clear_button_enabled">
+ </member>
<member name="context_menu_enabled" type="bool" setter="set_context_menu_enabled" getter="is_context_menu_enabled">
If [code]true[/code] the context menu will appear when right clicked.
</member>
@@ -172,6 +174,12 @@
</constant>
</constants>
<theme_items>
+ <theme_item name="clear" type="Texture">
+ </theme_item>
+ <theme_item name="clear_button_color" type="Color">
+ </theme_item>
+ <theme_item name="clear_button_color_pressed" type="Color">
+ </theme_item>
<theme_item name="cursor_color" type="Color">
</theme_item>
<theme_item name="focus" type="StyleBox">
diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml
index bfef3c8588..d02e3dfdfa 100644
--- a/doc/classes/Node.xml
+++ b/doc/classes/Node.xml
@@ -38,6 +38,12 @@
Corresponds to the NOTIFICATION_EXIT_TREE notification in [method Object._notification] and signal [signal tree_exiting]. To get notified when the node has already left the active tree, connect to the [signal tree_exited]
</description>
</method>
+ <method name="_get_configuration_warning" qualifiers="virtual">
+ <return type="String">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="_input" qualifiers="virtual">
<return type="void">
</return>
diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml
index a830468042..f4ad196ad2 100644
--- a/doc/classes/Object.xml
+++ b/doc/classes/Object.xml
@@ -420,5 +420,7 @@
<constant name="CONNECT_ONESHOT" value="4" enum="ConnectFlags">
One shot connections disconnect themselves after emission.
</constant>
+ <constant name="CONNECT_REFERENCE_COUNTED" value="8" enum="ConnectFlags">
+ </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/PhysicalBone.xml b/doc/classes/PhysicalBone.xml
index 99f551b865..5eb4550e93 100644
--- a/doc/classes/PhysicalBone.xml
+++ b/doc/classes/PhysicalBone.xml
@@ -9,6 +9,12 @@
<demos>
</demos>
<methods>
+ <method name="get_bone_id" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="get_simulate_physics">
<return type="bool">
</return>
diff --git a/doc/classes/PhysicsDirectBodyState.xml b/doc/classes/PhysicsDirectBodyState.xml
index 5117c9ef6c..91fc4df4ff 100644
--- a/doc/classes/PhysicsDirectBodyState.xml
+++ b/doc/classes/PhysicsDirectBodyState.xml
@@ -121,7 +121,7 @@
<argument index="0" name="contact_idx" type="int">
</argument>
<description>
- Impulse created by the contact. Only implemented for Bullet physics.
+ Impulse created by the contact. Only implemented for Bullet physics.
</description>
</method>
<method name="get_contact_local_normal" qualifiers="const">
diff --git a/doc/classes/PhysicsMaterial.xml b/doc/classes/PhysicsMaterial.xml
index 5512c4605a..bfebb472a5 100644
--- a/doc/classes/PhysicsMaterial.xml
+++ b/doc/classes/PhysicsMaterial.xml
@@ -10,6 +10,16 @@
</demos>
<methods>
</methods>
+ <members>
+ <member name="absorbent" type="bool" setter="set_absorbent" getter="is_absorbent">
+ </member>
+ <member name="bounce" type="float" setter="set_bounce" getter="get_bounce">
+ </member>
+ <member name="friction" type="float" setter="set_friction" getter="get_friction">
+ </member>
+ <member name="rough" type="bool" setter="set_rough" getter="is_rough">
+ </member>
+ </members>
<constants>
</constants>
</class>
diff --git a/doc/classes/Plane.xml b/doc/classes/Plane.xml
index 6f616401cb..62e4fc5d9d 100644
--- a/doc/classes/Plane.xml
+++ b/doc/classes/Plane.xml
@@ -157,5 +157,11 @@
</member>
</members>
<constants>
+ <constant name="X" value="Plane( 1, 0, 0, 0 )">
+ </constant>
+ <constant name="Y" value="Plane( 0, 1, 0, 0 )">
+ </constant>
+ <constant name="Z" value="Plane( 0, 0, 1, 0 )">
+ </constant>
</constants>
</class>
diff --git a/doc/classes/PopupMenu.xml b/doc/classes/PopupMenu.xml
index 0c8769714b..fe7dbe52b0 100644
--- a/doc/classes/PopupMenu.xml
+++ b/doc/classes/PopupMenu.xml
@@ -260,6 +260,12 @@
<description>
</description>
</method>
+ <method name="is_hide_on_window_lose_focus" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="is_item_checkable" qualifiers="const">
<return type="bool">
</return>
@@ -322,6 +328,14 @@
Removes the item at index "idx" from the menu. Note that the indexes of items after the removed item are going to be shifted by one.
</description>
</method>
+ <method name="set_hide_on_window_lose_focus">
+ <return type="void">
+ </return>
+ <argument index="0" name="enable" type="bool">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="set_item_accelerator">
<return type="void">
</return>
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/ProjectSettings.xml b/doc/classes/ProjectSettings.xml
index a4af5509a6..358b7292a5 100644
--- a/doc/classes/ProjectSettings.xml
+++ b/doc/classes/ProjectSettings.xml
@@ -224,6 +224,58 @@
</member>
<member name="compression/formats/zstd/window_log_size" type="int" setter="" getter="">
</member>
+ <member name="debug/gdscript/warnings/constant_used_as_function" type="bool" setter="" getter="">
+ </member>
+ <member name="debug/gdscript/warnings/enable" type="bool" setter="" getter="">
+ </member>
+ <member name="debug/gdscript/warnings/function_conflicts_constant" type="bool" setter="" getter="">
+ </member>
+ <member name="debug/gdscript/warnings/function_conflicts_variable" type="bool" setter="" getter="">
+ </member>
+ <member name="debug/gdscript/warnings/function_may_yield" type="bool" setter="" getter="">
+ </member>
+ <member name="debug/gdscript/warnings/function_used_as_property" type="bool" setter="" getter="">
+ </member>
+ <member name="debug/gdscript/warnings/incompatible_ternary" type="bool" setter="" getter="">
+ </member>
+ <member name="debug/gdscript/warnings/integer_division" type="bool" setter="" getter="">
+ </member>
+ <member name="debug/gdscript/warnings/narrowing_conversion" type="bool" setter="" getter="">
+ </member>
+ <member name="debug/gdscript/warnings/property_used_as_function" type="bool" setter="" getter="">
+ </member>
+ <member name="debug/gdscript/warnings/return_value_discarded" type="bool" setter="" getter="">
+ </member>
+ <member name="debug/gdscript/warnings/standalone_expression" type="bool" setter="" getter="">
+ </member>
+ <member name="debug/gdscript/warnings/treat_warnings_as_errors" type="bool" setter="" getter="">
+ </member>
+ <member name="debug/gdscript/warnings/unassigned_variable" type="bool" setter="" getter="">
+ </member>
+ <member name="debug/gdscript/warnings/unassigned_variable_op_assign" type="bool" setter="" getter="">
+ </member>
+ <member name="debug/gdscript/warnings/unreachable_code" type="bool" setter="" getter="">
+ </member>
+ <member name="debug/gdscript/warnings/unsafe_call_argument" type="bool" setter="" getter="">
+ </member>
+ <member name="debug/gdscript/warnings/unsafe_cast" type="bool" setter="" getter="">
+ </member>
+ <member name="debug/gdscript/warnings/unsafe_method_access" type="bool" setter="" getter="">
+ </member>
+ <member name="debug/gdscript/warnings/unsafe_property_access" type="bool" setter="" getter="">
+ </member>
+ <member name="debug/gdscript/warnings/unused_argument" type="bool" setter="" getter="">
+ </member>
+ <member name="debug/gdscript/warnings/unused_class_variable" type="bool" setter="" getter="">
+ </member>
+ <member name="debug/gdscript/warnings/unused_signal" type="bool" setter="" getter="">
+ </member>
+ <member name="debug/gdscript/warnings/unused_variable" type="bool" setter="" getter="">
+ </member>
+ <member name="debug/gdscript/warnings/variable_conflicts_function" type="bool" setter="" getter="">
+ </member>
+ <member name="debug/gdscript/warnings/void_assignment" type="bool" setter="" getter="">
+ </member>
<member name="debug/settings/crash_handler/message" type="String" setter="" getter="">
</member>
<member name="debug/settings/fps/force_fps" type="int" setter="" getter="">
@@ -315,31 +367,31 @@
<member name="gui/timers/text_edit_idle_detect_sec" type="int" setter="" getter="">
Timer for detecting idle in the editor.
</member>
- <member name="input/ui_accept" type="Array" setter="" getter="">
+ <member name="input/ui_accept" type="Dictionary" setter="" getter="">
</member>
- <member name="input/ui_cancel" type="Array" setter="" getter="">
+ <member name="input/ui_cancel" type="Dictionary" setter="" getter="">
</member>
- <member name="input/ui_down" type="Array" setter="" getter="">
+ <member name="input/ui_down" type="Dictionary" setter="" getter="">
</member>
- <member name="input/ui_end" type="Array" setter="" getter="">
+ <member name="input/ui_end" type="Dictionary" setter="" getter="">
</member>
- <member name="input/ui_focus_next" type="Array" setter="" getter="">
+ <member name="input/ui_focus_next" type="Dictionary" setter="" getter="">
</member>
- <member name="input/ui_focus_prev" type="Array" setter="" getter="">
+ <member name="input/ui_focus_prev" type="Dictionary" setter="" getter="">
</member>
- <member name="input/ui_home" type="Array" setter="" getter="">
+ <member name="input/ui_home" type="Dictionary" setter="" getter="">
</member>
- <member name="input/ui_left" type="Array" setter="" getter="">
+ <member name="input/ui_left" type="Dictionary" setter="" getter="">
</member>
- <member name="input/ui_page_down" type="Array" setter="" getter="">
+ <member name="input/ui_page_down" type="Dictionary" setter="" getter="">
</member>
- <member name="input/ui_page_up" type="Array" setter="" getter="">
+ <member name="input/ui_page_up" type="Dictionary" setter="" getter="">
</member>
- <member name="input/ui_right" type="Array" setter="" getter="">
+ <member name="input/ui_right" type="Dictionary" setter="" getter="">
</member>
- <member name="input/ui_select" type="Array" setter="" getter="">
+ <member name="input/ui_select" type="Dictionary" setter="" getter="">
</member>
- <member name="input/ui_up" type="Array" setter="" getter="">
+ <member name="input/ui_up" type="Dictionary" setter="" getter="">
</member>
<member name="input_devices/pointing/emulate_mouse_from_touch" type="bool" setter="" getter="">
</member>
diff --git a/doc/classes/Quat.xml b/doc/classes/Quat.xml
index c755e6b02a..67631bbc92 100644
--- a/doc/classes/Quat.xml
+++ b/doc/classes/Quat.xml
@@ -188,5 +188,7 @@
</member>
</members>
<constants>
+ <constant name="IDENTITY" value="Quat( 0, 0, 0, 1 )">
+ </constant>
</constants>
</class>
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/ReferenceRect.xml b/doc/classes/ReferenceRect.xml
index 4c6f014965..e1cb104e40 100644
--- a/doc/classes/ReferenceRect.xml
+++ b/doc/classes/ReferenceRect.xml
@@ -12,10 +12,10 @@
</demos>
<methods>
</methods>
- <constants>
- </constants>
<members>
<member name="border_color" type="Color" setter="set_border_color" getter="get_border_color">
</member>
</members>
+ <constants>
+ </constants>
</class>
diff --git a/doc/classes/ResourceLoader.xml b/doc/classes/ResourceLoader.xml
index ac00c13b2f..ae900e34ef 100644
--- a/doc/classes/ResourceLoader.xml
+++ b/doc/classes/ResourceLoader.xml
@@ -11,6 +11,16 @@
<demos>
</demos>
<methods>
+ <method name="exists">
+ <return type="bool">
+ </return>
+ <argument index="0" name="path" type="String">
+ </argument>
+ <argument index="1" name="type_hint" type="String" default="&quot;&quot;">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="get_dependencies">
<return type="PoolStringArray">
</return>
@@ -36,6 +46,14 @@
<description>
</description>
</method>
+ <method name="has_cached">
+ <return type="bool">
+ </return>
+ <argument index="0" name="path" type="String">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="load">
<return type="Resource">
</return>
diff --git a/doc/classes/Skeleton.xml b/doc/classes/Skeleton.xml
index 4d826002fe..233df28255 100644
--- a/doc/classes/Skeleton.xml
+++ b/doc/classes/Skeleton.xml
@@ -196,6 +196,16 @@
<description>
</description>
</method>
+ <method name="set_bone_ignore_animation">
+ <return type="void">
+ </return>
+ <argument index="0" name="bone" type="int">
+ </argument>
+ <argument index="1" name="ignore" type="bool">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="set_bone_parent">
<return type="void">
</return>
diff --git a/doc/classes/Skeleton2D.xml b/doc/classes/Skeleton2D.xml
index 654c816ba8..712b9ca2a5 100644
--- a/doc/classes/Skeleton2D.xml
+++ b/doc/classes/Skeleton2D.xml
@@ -12,7 +12,7 @@
<method name="get_bone">
<return type="Bone2D">
</return>
- <argument index="0" name="arg0" type="int">
+ <argument index="0" name="idx" type="int">
</argument>
<description>
</description>
diff --git a/doc/classes/SkeletonIK.xml b/doc/classes/SkeletonIK.xml
new file mode 100644
index 0000000000..50246f1b18
--- /dev/null
+++ b/doc/classes/SkeletonIK.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="SkeletonIK" inherits="Node" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="get_parent_skeleton" qualifiers="const">
+ <return type="Skeleton">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="is_running">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="start">
+ <return type="void">
+ </return>
+ <argument index="0" name="one_time" type="bool" default="false">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="stop">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="interpolation" type="float" setter="set_interpolation" getter="get_interpolation">
+ </member>
+ <member name="magnet" type="Vector3" setter="set_magnet_position" getter="get_magnet_position">
+ </member>
+ <member name="max_iterations" type="int" setter="set_max_iterations" getter="get_max_iterations">
+ </member>
+ <member name="min_distance" type="float" setter="set_min_distance" getter="get_min_distance">
+ </member>
+ <member name="root_bone" type="String" setter="set_root_bone" getter="get_root_bone">
+ </member>
+ <member name="target" type="Transform" setter="set_target_transform" getter="get_target_transform">
+ </member>
+ <member name="target_node" type="NodePath" setter="set_target_node" getter="get_target_node">
+ </member>
+ <member name="tip_bone" type="String" setter="set_tip_bone" getter="get_tip_bone">
+ </member>
+ <member name="use_magnet" type="bool" setter="set_use_magnet" getter="is_using_magnet">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/SpringArm.xml b/doc/classes/SpringArm.xml
new file mode 100644
index 0000000000..198ff4a81d
--- /dev/null
+++ b/doc/classes/SpringArm.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="SpringArm" inherits="Spatial" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="add_excluded_object">
+ <return type="void">
+ </return>
+ <argument index="0" name="RID" type="RID">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="clear_excluded_objects">
+ <return type="void">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_hit_length">
+ <return type="float">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="remove_excluded_object">
+ <return type="bool">
+ </return>
+ <argument index="0" name="RID" type="RID">
+ </argument>
+ <description>
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="collision_mask" type="int" setter="set_collision_mask" getter="get_collision_mask">
+ </member>
+ <member name="margin" type="float" setter="set_margin" getter="get_margin">
+ </member>
+ <member name="shape" type="Shape" setter="set_shape" getter="get_shape">
+ </member>
+ <member name="spring_length" type="float" setter="set_length" getter="get_length">
+ </member>
+ </members>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/Tabs.xml b/doc/classes/Tabs.xml
index 0d5f1f0ba9..fc1d0476ed 100644
--- a/doc/classes/Tabs.xml
+++ b/doc/classes/Tabs.xml
@@ -35,6 +35,12 @@
<description>
</description>
</method>
+ <method name="get_select_with_rmb" qualifiers="const">
+ <return type="bool">
+ </return>
+ <description>
+ </description>
+ </method>
<method name="get_tab_count" qualifiers="const">
<return type="int">
</return>
@@ -105,6 +111,14 @@
<description>
</description>
</method>
+ <method name="set_select_with_rmb">
+ <return type="void">
+ </return>
+ <argument index="0" name="enabled" type="bool">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="set_tab_disabled">
<return type="void">
</return>
diff --git a/doc/classes/Texture.xml b/doc/classes/Texture.xml
index eb7cfac87b..a9be42d1a1 100644
--- a/doc/classes/Texture.xml
+++ b/doc/classes/Texture.xml
@@ -128,7 +128,7 @@
<constant name="FLAG_MIRRORED_REPEAT" value="32" enum="Flags">
Repeats texture with alternate sections mirrored.
</constant>
- <constant name="FLAG_VIDEO_SURFACE" value="4096" enum="Flags">
+ <constant name="FLAG_VIDEO_SURFACE" value="2048" enum="Flags">
Texture is a video surface.
</constant>
</constants>
diff --git a/doc/classes/Texture3D.xml b/doc/classes/Texture3D.xml
new file mode 100644
index 0000000000..691d1f229e
--- /dev/null
+++ b/doc/classes/Texture3D.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="Texture3D" inherits="TextureLayered" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/TextureArray.xml b/doc/classes/TextureArray.xml
new file mode 100644
index 0000000000..a08d8421f1
--- /dev/null
+++ b/doc/classes/TextureArray.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="TextureArray" inherits="TextureLayered" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ </methods>
+ <constants>
+ </constants>
+</class>
diff --git a/doc/classes/TextureLayered.xml b/doc/classes/TextureLayered.xml
new file mode 100644
index 0000000000..026144cf5a
--- /dev/null
+++ b/doc/classes/TextureLayered.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<class name="TextureLayered" inherits="Resource" category="Core" version="3.1">
+ <brief_description>
+ </brief_description>
+ <description>
+ </description>
+ <tutorials>
+ </tutorials>
+ <demos>
+ </demos>
+ <methods>
+ <method name="create">
+ <return type="void">
+ </return>
+ <argument index="0" name="width" type="int">
+ </argument>
+ <argument index="1" name="height" type="int">
+ </argument>
+ <argument index="2" name="depth" type="int">
+ </argument>
+ <argument index="3" name="format" type="int" enum="Image.Format">
+ </argument>
+ <argument index="4" name="flags" type="int" default="4">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_depth" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_format" qualifiers="const">
+ <return type="int" enum="Image.Format">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_height" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="get_layer_data" qualifiers="const">
+ <return type="Image">
+ </return>
+ <argument index="0" name="layer" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="get_width" qualifiers="const">
+ <return type="int">
+ </return>
+ <description>
+ </description>
+ </method>
+ <method name="set_data_partial">
+ <return type="void">
+ </return>
+ <argument index="0" name="image" type="Image">
+ </argument>
+ <argument index="1" name="x_offset" type="int">
+ </argument>
+ <argument index="2" name="y_offset" type="int">
+ </argument>
+ <argument index="3" name="layer" type="int">
+ </argument>
+ <argument index="4" name="mipmap" type="int" default="0">
+ </argument>
+ <description>
+ </description>
+ </method>
+ <method name="set_layer_data">
+ <return type="void">
+ </return>
+ <argument index="0" name="image" type="Image">
+ </argument>
+ <argument index="1" name="layer" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
+ </methods>
+ <members>
+ <member name="data" type="Dictionary" setter="_set_data" getter="_get_data">
+ </member>
+ <member name="flags" type="int" setter="set_flags" getter="get_flags">
+ </member>
+ </members>
+ <constants>
+ <constant name="FLAG_MIPMAPS" value="1" enum="Flags">
+ </constant>
+ <constant name="FLAG_REPEAT" value="2" enum="Flags">
+ </constant>
+ <constant name="FLAG_FILTER" value="4" enum="Flags">
+ </constant>
+ <constant name="FLAGS_DEFAULT" value="4" enum="Flags">
+ </constant>
+ </constants>
+</class>
diff --git a/doc/classes/TileSet.xml b/doc/classes/TileSet.xml
index 7121bc8b9c..56bb33c5e1 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>
@@ -492,7 +496,7 @@
</constant>
<constant name="AUTO_TILE" value="1" enum="TileMode">
</constant>
- <constant name="ANIMATED_TILE" value="2" enum="TileMode">
+ <constant name="ATLAS_TILE" value="2" enum="TileMode">
</constant>
</constants>
</class>
diff --git a/doc/classes/Transform.xml b/doc/classes/Transform.xml
index 0dd8038b36..24c009d922 100644
--- a/doc/classes/Transform.xml
+++ b/doc/classes/Transform.xml
@@ -168,5 +168,13 @@
</member>
</members>
<constants>
+ <constant name="IDENTITY" value="Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )">
+ </constant>
+ <constant name="FLIP_X" value="Transform( -1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )">
+ </constant>
+ <constant name="FLIP_Y" value="Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )">
+ </constant>
+ <constant name="FLIP_Z" value="Transform( 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 )">
+ </constant>
</constants>
</class>
diff --git a/doc/classes/Transform2D.xml b/doc/classes/Transform2D.xml
index d5427ad7f8..b38d9a1a86 100644
--- a/doc/classes/Transform2D.xml
+++ b/doc/classes/Transform2D.xml
@@ -173,5 +173,11 @@
</member>
</members>
<constants>
+ <constant name="IDENTITY" value="Transform2D( 1, 0, 0, 1, 0, 0 )">
+ </constant>
+ <constant name="FLIP_X" value="Transform2D( -1, 0, 0, 1, 0, 0 )">
+ </constant>
+ <constant name="FLIP_Y" value="Transform2D( 1, 0, 0, -1, 0, 0 )">
+ </constant>
</constants>
</class>
diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml
index 9b18962a6f..4bc18b926e 100644
--- a/doc/classes/Vector2.xml
+++ b/doc/classes/Vector2.xml
@@ -130,15 +130,6 @@
Returns the distance to vector [code]b[/code].
</description>
</method>
- <method name="project">
- <return type="Vector2">
- </return>
- <argument index="0" name="b" type="Vector2">
- </argument>
- <description>
- Returns the vector projected onto the vector [code]b[/code].
- </description>
- </method>
<method name="dot">
<return type="float">
</return>
@@ -194,6 +185,15 @@
Returns the vector scaled to unit length. Equivalent to [code]v / v.length()[/code].
</description>
</method>
+ <method name="project">
+ <return type="Vector2">
+ </return>
+ <argument index="0" name="b" type="Vector2">
+ </argument>
+ <description>
+ Returns the vector projected onto the vector [code]b[/code].
+ </description>
+ </method>
<method name="reflect">
<return type="Vector2">
</return>
@@ -266,5 +266,17 @@
</member>
</members>
<constants>
+ <constant name="ZERO" value="Vector2( 0, 0 )">
+ </constant>
+ <constant name="INF" value="Vector2( inf, inf )">
+ </constant>
+ <constant name="LEFT" value="Vector2( -1, 0 )">
+ </constant>
+ <constant name="RIGHT" value="Vector2( 1, 0 )">
+ </constant>
+ <constant name="UP" value="Vector2( 0, -1 )">
+ </constant>
+ <constant name="DOWN" value="Vector2( 0, 1 )">
+ </constant>
</constants>
</class>
diff --git a/doc/classes/Vector3.xml b/doc/classes/Vector3.xml
index 22384c5012..b4dcc6c6aa 100644
--- a/doc/classes/Vector3.xml
+++ b/doc/classes/Vector3.xml
@@ -99,15 +99,6 @@
Returns the distance to [code]b[/code].
</description>
</method>
- <method name="project_onto">
- <return type="Vector3">
- </return>
- <argument index="0" name="b" type="Vector3">
- </argument>
- <description>
- Returns the vector projected onto the vector [code]b[/code].
- </description>
- </method>
<method name="dot">
<return type="float">
</return>
@@ -193,6 +184,15 @@
Returns the outer product with [code]b[/code].
</description>
</method>
+ <method name="project">
+ <return type="Vector3">
+ </return>
+ <argument index="0" name="b" type="Vector3">
+ </argument>
+ <description>
+ Returns the vector projected onto the vector [code]b[/code].
+ </description>
+ </method>
<method name="reflect">
<return type="Vector3">
</return>
@@ -279,5 +279,21 @@
<constant name="AXIS_Z" value="2">
Enumerated value for the Z axis.
</constant>
+ <constant name="ZERO" value="Vector3( 0, 0, 0 )">
+ </constant>
+ <constant name="INF" value="Vector3( inf, inf, inf )">
+ </constant>
+ <constant name="LEFT" value="Vector3( -1, 0, 0 )">
+ </constant>
+ <constant name="RIGHT" value="Vector3( 1, 0, 0 )">
+ </constant>
+ <constant name="UP" value="Vector3( 0, 1, 0 )">
+ </constant>
+ <constant name="DOWN" value="Vector3( 0, -1, 0 )">
+ </constant>
+ <constant name="FORWARD" value="Vector3( 0, 0, -1 )">
+ </constant>
+ <constant name="BACK" value="Vector3( 0, 0, 1 )">
+ </constant>
</constants>
</class>
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/doc/classes/VisualInstance.xml b/doc/classes/VisualInstance.xml
index 502209fac5..30dedb06f4 100644
--- a/doc/classes/VisualInstance.xml
+++ b/doc/classes/VisualInstance.xml
@@ -16,6 +16,14 @@
Returns the [AABB] (also known as the bounding box) for this VisualInstance.
</description>
</method>
+ <method name="get_layer_mask_bit" qualifiers="const">
+ <return type="bool">
+ </return>
+ <argument index="0" name="layer" type="int">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="get_transformed_aabb" qualifiers="const">
<return type="AABB">
</return>
@@ -34,6 +42,16 @@
It is recommended to only use set_base if you know what you're doing.
</description>
</method>
+ <method name="set_layer_mask_bit">
+ <return type="void">
+ </return>
+ <argument index="0" name="layer" type="int">
+ </argument>
+ <argument index="1" name="enabled" type="bool">
+ </argument>
+ <description>
+ </description>
+ </method>
</methods>
<members>
<member name="layers" type="int" setter="set_layer_mask" getter="get_layer_mask">
diff --git a/doc/classes/VisualServer.xml b/doc/classes/VisualServer.xml
index c4ebf7c96f..395fb9e829 100644
--- a/doc/classes/VisualServer.xml
+++ b/doc/classes/VisualServer.xml
@@ -1336,7 +1336,7 @@
<method name="gi_probe_get_bias" qualifiers="const">
<return type="float">
</return>
- <argument index="0" name="arg0" type="RID">
+ <argument index="0" name="probe" type="RID">
</argument>
<description>
</description>
@@ -1360,7 +1360,7 @@
<method name="gi_probe_get_dynamic_data" qualifiers="const">
<return type="PoolIntArray">
</return>
- <argument index="0" name="arg0" type="RID">
+ <argument index="0" name="probe" type="RID">
</argument>
<description>
</description>
@@ -1368,7 +1368,7 @@
<method name="gi_probe_get_dynamic_range" qualifiers="const">
<return type="int">
</return>
- <argument index="0" name="arg0" type="RID">
+ <argument index="0" name="probe" type="RID">
</argument>
<description>
</description>
@@ -1376,7 +1376,7 @@
<method name="gi_probe_get_energy" qualifiers="const">
<return type="float">
</return>
- <argument index="0" name="arg0" type="RID">
+ <argument index="0" name="probe" type="RID">
</argument>
<description>
</description>
@@ -1384,7 +1384,7 @@
<method name="gi_probe_get_normal_bias" qualifiers="const">
<return type="float">
</return>
- <argument index="0" name="arg0" type="RID">
+ <argument index="0" name="probe" type="RID">
</argument>
<description>
</description>
@@ -1392,7 +1392,7 @@
<method name="gi_probe_get_propagation" qualifiers="const">
<return type="float">
</return>
- <argument index="0" name="arg0" type="RID">
+ <argument index="0" name="probe" type="RID">
</argument>
<description>
</description>
@@ -1400,7 +1400,7 @@
<method name="gi_probe_get_to_cell_xform" qualifiers="const">
<return type="Transform">
</return>
- <argument index="0" name="arg0" type="RID">
+ <argument index="0" name="probe" type="RID">
</argument>
<description>
</description>
@@ -1408,7 +1408,7 @@
<method name="gi_probe_is_compressed" qualifiers="const">
<return type="bool">
</return>
- <argument index="0" name="arg0" type="RID">
+ <argument index="0" name="probe" type="RID">
</argument>
<description>
</description>
@@ -1416,7 +1416,7 @@
<method name="gi_probe_is_interior" qualifiers="const">
<return type="bool">
</return>
- <argument index="0" name="arg0" type="RID">
+ <argument index="0" name="probe" type="RID">
</argument>
<description>
</description>
@@ -1424,9 +1424,9 @@
<method name="gi_probe_set_bias">
<return type="void">
</return>
- <argument index="0" name="bias" type="RID">
+ <argument index="0" name="probe" type="RID">
</argument>
- <argument index="1" name="arg1" type="float">
+ <argument index="1" name="bias" type="float">
</argument>
<description>
</description>
@@ -1454,9 +1454,9 @@
<method name="gi_probe_set_compress">
<return type="void">
</return>
- <argument index="0" name="enable" type="RID">
+ <argument index="0" name="probe" type="RID">
</argument>
- <argument index="1" name="arg1" type="bool">
+ <argument index="1" name="enable" type="bool">
</argument>
<description>
</description>
@@ -1464,9 +1464,9 @@
<method name="gi_probe_set_dynamic_data">
<return type="void">
</return>
- <argument index="0" name="data" type="RID">
+ <argument index="0" name="probe" type="RID">
</argument>
- <argument index="1" name="arg1" type="PoolIntArray">
+ <argument index="1" name="data" type="PoolIntArray">
</argument>
<description>
</description>
@@ -1474,9 +1474,9 @@
<method name="gi_probe_set_dynamic_range">
<return type="void">
</return>
- <argument index="0" name="range" type="RID">
+ <argument index="0" name="probe" type="RID">
</argument>
- <argument index="1" name="arg1" type="int">
+ <argument index="1" name="range" type="int">
</argument>
<description>
</description>
@@ -1484,9 +1484,9 @@
<method name="gi_probe_set_energy">
<return type="void">
</return>
- <argument index="0" name="energy" type="RID">
+ <argument index="0" name="probe" type="RID">
</argument>
- <argument index="1" name="arg1" type="float">
+ <argument index="1" name="energy" type="float">
</argument>
<description>
</description>
@@ -1494,9 +1494,9 @@
<method name="gi_probe_set_interior">
<return type="void">
</return>
- <argument index="0" name="enable" type="RID">
+ <argument index="0" name="probe" type="RID">
</argument>
- <argument index="1" name="arg1" type="bool">
+ <argument index="1" name="enable" type="bool">
</argument>
<description>
</description>
@@ -1504,9 +1504,9 @@
<method name="gi_probe_set_normal_bias">
<return type="void">
</return>
- <argument index="0" name="bias" type="RID">
+ <argument index="0" name="probe" type="RID">
</argument>
- <argument index="1" name="arg1" type="float">
+ <argument index="1" name="bias" type="float">
</argument>
<description>
</description>
@@ -1514,9 +1514,9 @@
<method name="gi_probe_set_propagation">
<return type="void">
</return>
- <argument index="0" name="propagation" type="RID">
+ <argument index="0" name="probe" type="RID">
</argument>
- <argument index="1" name="arg1" type="float">
+ <argument index="1" name="propagation" type="float">
</argument>
<description>
</description>
@@ -1524,9 +1524,9 @@
<method name="gi_probe_set_to_cell_xform">
<return type="void">
</return>
- <argument index="0" name="xform" type="RID">
+ <argument index="0" name="probe" type="RID">
</argument>
- <argument index="1" name="arg1" type="Transform">
+ <argument index="1" name="xform" type="Transform">
</argument>
<description>
</description>
@@ -3300,12 +3300,15 @@
</argument>
<argument index="2" name="height" type="int">
</argument>
- <argument index="3" name="format" type="int" enum="Image.Format">
+ <argument index="3" name="depth_3d" type="int">
</argument>
- <argument index="4" name="flags" type="int" default="7">
+ <argument index="4" name="format" type="int" enum="Image.Format">
+ </argument>
+ <argument index="5" name="type" type="int" enum="VisualServer.TextureType">
+ </argument>
+ <argument index="6" name="flags" type="int" default="7">
</argument>
<description>
- Allocates space for a texture's image or video.
</description>
</method>
<method name="texture_create">
@@ -3338,12 +3341,20 @@
</return>
<argument index="0" name="texture" type="RID">
</argument>
- <argument index="1" name="cube_side" type="int" enum="VisualServer.CubeMapSide" default="0">
+ <argument index="1" name="cube_side" type="int" default="0">
</argument>
<description>
Returns a copy of a texture's image unless it's a CubeMap, in which case it returns the [RID] of the image at one of the cubes sides.
</description>
</method>
+ <method name="texture_get_depth" qualifiers="const">
+ <return type="int">
+ </return>
+ <argument index="0" name="texture" type="RID">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="texture_get_flags" qualifiers="const">
<return type="int">
</return>
@@ -3389,6 +3400,14 @@
Returns the opengl id of the texture's image.
</description>
</method>
+ <method name="texture_get_type" qualifiers="const">
+ <return type="int" enum="VisualServer.TextureType">
+ </return>
+ <argument index="0" name="texture" type="RID">
+ </argument>
+ <description>
+ </description>
+ </method>
<method name="texture_get_width" qualifiers="const">
<return type="int">
</return>
@@ -3405,7 +3424,7 @@
</argument>
<argument index="1" name="image" type="Image">
</argument>
- <argument index="2" name="cube_side" type="int" enum="VisualServer.CubeMapSide" default="0">
+ <argument index="2" name="layer" type="int" default="0">
</argument>
<description>
Sets the texture's image data. If it's a CubeMap, it sets the image data at a cube side.
@@ -3432,7 +3451,7 @@
</argument>
<argument index="8" name="dst_mip" type="int">
</argument>
- <argument index="9" name="cube_side" type="int" enum="VisualServer.CubeMapSide" default="0">
+ <argument index="9" name="layer" type="int" default="0">
</argument>
<description>
</description>
@@ -3477,8 +3496,9 @@
</argument>
<argument index="2" name="height" type="int">
</argument>
+ <argument index="3" name="depth" type="int">
+ </argument>
<description>
- Overwrites the texture's width and height.
</description>
</method>
<method name="textures_keep_original">
@@ -3873,6 +3893,14 @@
<constant name="CUBEMAP_BACK" value="5" enum="CubeMapSide">
Marks the back side of a cubemap.
</constant>
+ <constant name="TEXTURE_TYPE_2D" value="0" enum="TextureType">
+ </constant>
+ <constant name="TEXTURE_TYPE_CUBEMAP" value="1" enum="TextureType">
+ </constant>
+ <constant name="TEXTURE_TYPE_2D_ARRAY" value="2" enum="TextureType">
+ </constant>
+ <constant name="TEXTURE_TYPE_3D" value="3" enum="TextureType">
+ </constant>
<constant name="TEXTURE_FLAG_MIPMAPS" value="1" enum="TextureFlags">
Generate mipmaps, which are smaller versions of the same texture to use when zoomed out, keeping the aspect ratio.
</constant>
@@ -3892,10 +3920,7 @@
<constant name="TEXTURE_FLAG_MIRRORED_REPEAT" value="32" enum="TextureFlags">
Repeat texture with alternate sections mirrored.
</constant>
- <constant name="TEXTURE_FLAG_CUBEMAP" value="2048" enum="TextureFlags">
- Texture is a cubemap.
- </constant>
- <constant name="TEXTURE_FLAG_USED_FOR_STREAMING" value="4096" enum="TextureFlags">
+ <constant name="TEXTURE_FLAG_USED_FOR_STREAMING" value="2048" enum="TextureFlags">
Texture is a video surface.
</constant>
<constant name="TEXTURE_FLAGS_DEFAULT" value="7" enum="TextureFlags">
@@ -4208,7 +4233,7 @@
</constant>
<constant name="INSTANCE_FLAG_USE_BAKED_LIGHT" value="0" enum="InstanceFlags">
</constant>
- <constant name="INSTANCE_FLAG_REDRAW_FRAME_IF_VISIBLE" value="1" enum="InstanceFlags">
+ <constant name="INSTANCE_FLAG_DRAW_NEXT_FRAME_IF_VISIBLE" value="1" enum="InstanceFlags">
</constant>
<constant name="INSTANCE_FLAG_MAX" value="2" enum="InstanceFlags">
</constant>
diff --git a/drivers/gles2/rasterizer_gles2.cpp b/drivers/gles2/rasterizer_gles2.cpp
index 73d93f7b46..a1a0b9e2c6 100644
--- a/drivers/gles2/rasterizer_gles2.cpp
+++ b/drivers/gles2/rasterizer_gles2.cpp
@@ -392,12 +392,6 @@ void RasterizerGLES2::end_frame(bool p_swap_buffers) {
OS::get_singleton()->swap_buffers();
else
glFinish();
-
- if (p_swap_buffers) {
- glColorMask(true, true, true, true);
- glClearColor(0, 0, 0, 1);
- glClear(GL_COLOR_BUFFER_BIT);
- }
}
void RasterizerGLES2::finalize() {
diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp
index 05dfd69f58..8aab4cb521 100644
--- a/drivers/unix/os_unix.cpp
+++ b/drivers/unix/os_unix.cpp
@@ -329,7 +329,7 @@ Error OS_Unix::execute(const String &p_path, const List<String> &p_arguments, bo
return OK;
}
-Error OS_Unix::kill(const ProcessID &p_pid) {
+Error OS_Unix::kill(const ProcessID &p_pid, const int p_max_wait_msec) {
int ret = ::kill(p_pid, SIGKILL);
if (!ret) {
diff --git a/drivers/unix/os_unix.h b/drivers/unix/os_unix.h
index 95b74d23ff..c5240231fa 100644
--- a/drivers/unix/os_unix.h
+++ b/drivers/unix/os_unix.h
@@ -91,7 +91,7 @@ public:
virtual uint64_t get_ticks_usec() const;
virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false);
- virtual Error kill(const ProcessID &p_pid);
+ virtual Error kill(const ProcessID &p_pid, const int p_max_wait_msec = -1);
virtual int get_process_id() const;
virtual bool has_environment(const String &p_var) const;
diff --git a/editor/editor_help.cpp b/editor/editor_help.cpp
index 60826aa81b..50b3810e52 100644
--- a/editor/editor_help.cpp
+++ b/editor/editor_help.cpp
@@ -624,6 +624,22 @@ void EditorHelp::_add_type(const String &p_type, const String &p_enum) {
class_desc->pop();
}
+String EditorHelp::_fix_constant(const String &p_constant) const {
+
+ if (p_constant.strip_edges() == "4294967295") {
+ return "0xFFFFFFFF";
+ }
+
+ if (p_constant.strip_edges() == "2147483647") {
+ return "0x7FFFFFFF";
+ }
+ if (p_constant.strip_edges() == "1048575") {
+ return "0xfffff";
+ }
+
+ return p_constant;
+}
+
void EditorHelp::_add_method(const DocData::MethodDoc &p_method, bool p_overview) {
method_line[p_method.name] = class_desc->get_line_count() - 2; //gets overridden if description
@@ -673,7 +689,7 @@ void EditorHelp::_add_method(const DocData::MethodDoc &p_method, bool p_overview
class_desc->push_color(symbol_color);
class_desc->add_text("=");
class_desc->pop();
- _add_text(p_method.arguments[j].default_value);
+ _add_text(_fix_constant(p_method.arguments[j].default_value));
}
class_desc->pop();
@@ -1781,7 +1797,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 +1879,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 +1945,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 +1964,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_help.h b/editor/editor_help.h
index dbea97e98b..ad81a39945 100644
--- a/editor/editor_help.h
+++ b/editor/editor_help.h
@@ -244,6 +244,8 @@ class EditorHelp : public VBoxContainer {
void _unhandled_key_input(const Ref<InputEvent> &p_ev);
+ String _fix_constant(const String &p_constant) const;
+
protected:
void _notification(int p_what);
static void _bind_methods();
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 99a2b2aa67..852e1930d2 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(acc_path, path_name, object, c, use_folding);
item_path[acc_path] = section->get_vbox();
}
@@ -2221,7 +2222,7 @@ EditorInspector::EditorInspector() {
show_categories = false;
hide_script = true;
use_doc_hints = false;
- capitalize_paths = false;
+ capitalize_paths = true;
use_filter = false;
autoclear = false;
changing = 0;
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 7ff48880fc..98206f9443 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"
@@ -5593,16 +5594,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_run.cpp b/editor/editor_run.cpp
index 749cf6aa2b..072bd948e1 100644
--- a/editor/editor_run.cpp
+++ b/editor/editor_run.cpp
@@ -181,8 +181,8 @@ Error EditorRun::run(const String &p_scene, const String p_custom_args, const Li
void EditorRun::stop() {
if (status != STATUS_STOP && pid != 0) {
-
- OS::get_singleton()->kill(pid);
+ const int max_wait_msec = GLOBAL_DEF("editor/stop_max_wait_msec", 10000);
+ OS::get_singleton()->kill(pid, max_wait_msec);
}
status = STATUS_STOP;
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/resource_importer_wav.cpp b/editor/import/resource_importer_wav.cpp
index 41f5a892eb..9e99dcc5c8 100644
--- a/editor/import/resource_importer_wav.cpp
+++ b/editor/import/resource_importer_wav.cpp
@@ -157,15 +157,18 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
//Consider revision for engine version 3.0
compression_code = file->get_16();
if (compression_code != 1 && compression_code != 3) {
- ERR_PRINT("Format not supported for WAVE file (not PCM). Save WAVE files as uncompressed PCM instead.");
- break;
+ file->close();
+ memdelete(file);
+ ERR_EXPLAIN("Format not supported for WAVE file (not PCM). Save WAVE files as uncompressed PCM instead.");
+ ERR_FAIL_V(ERR_INVALID_DATA);
}
format_channels = file->get_16();
if (format_channels != 1 && format_channels != 2) {
-
- ERR_PRINT("Format not supported for WAVE file (not stereo or mono)");
- break;
+ file->close();
+ memdelete(file);
+ ERR_EXPLAIN("Format not supported for WAVE file (not stereo or mono).");
+ ERR_FAIL_V(ERR_INVALID_DATA);
}
format_freq = file->get_32(); //sampling rate
@@ -174,10 +177,11 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
file->get_16(); // block align (unused)
format_bits = file->get_16(); // bits per sample
- if (format_bits % 8) {
-
- ERR_PRINT("Strange number of bits in sample (not 8,16,24,32)");
- break;
+ if (format_bits % 8 || format_bits == 0) {
+ file->close();
+ memdelete(file);
+ ERR_EXPLAIN("Invalid amount of bits in the sample (should be one of 8, 16, 24 or 32).");
+ ERR_FAIL_V(ERR_INVALID_DATA);
}
/* Don't need anything else, continue */
@@ -185,7 +189,7 @@ Error ResourceImporterWAV::import(const String &p_source_file, const String &p_s
}
if (chunkID[0] == 'd' && chunkID[1] == 'a' && chunkID[2] == 't' && chunkID[3] == 'a' && !data_found) {
- /* IS FORMAT CHUNK */
+ /* IS DATA CHUNK */
data_found = true;
if (!format_found) {
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..5373015654 100644
--- a/editor/plugins/animation_blend_space_1d_editor.cpp
+++ b/editor/plugins/animation_blend_space_1d_editor.cpp
@@ -3,40 +3,9 @@
#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));
-}
-
-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() {
+StringName AnimationNodeBlendSpace1DEditor::get_blend_position_path() const {
+ StringName path = AnimationTreeEditor::get_singleton()->get_base_path() + "blend_position";
+ return path;
}
void AnimationNodeBlendSpace1DEditor::_blend_space_gui_input(const Ref<InputEvent> &p_event) {
@@ -62,7 +31,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 +54,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 +135,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 +158,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 +255,8 @@ 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 +278,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 +328,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);
+ 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");
@@ -438,7 +443,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,18 +495,10 @@ 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) {
error_panel->add_style_override("panel", get_stylebox("bg", "Tree"));
@@ -513,18 +510,15 @@ 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 +530,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 +554,21 @@ 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,15 +585,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);
tool_blend->set_button_group(bg);
@@ -726,13 +708,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..278357b9c7 100644
--- a/editor/plugins/animation_blend_space_1d_editor.h
+++ b/editor/plugins/animation_blend_space_1d_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_blend_space_1d.h"
#include "scene/gui/button.h"
@@ -10,9 +11,9 @@
#include "scene/gui/popup.h"
#include "scene/gui/tree.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..e5476aaf08 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,18 @@ 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 +210,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 +245,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 +329,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 +531,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 +570,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 +722,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 +729,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 +748,21 @@ 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 +796,12 @@ 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 +817,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,56 +996,23 @@ 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;
dragging_selected = false;
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..0bf1e25d7a 100644
--- a/editor/plugins/animation_blend_space_2d_editor.h
+++ b/editor/plugins/animation_blend_space_2d_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_blend_space_2d.h"
#include "scene/gui/button.h"
@@ -13,15 +14,12 @@
@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;
ToolButton *tool_select;
@@ -93,38 +91,31 @@ 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/asset_library_editor_plugin.cpp b/editor/plugins/asset_library_editor_plugin.cpp
index 4ed2b051aa..66770d98e5 100644
--- a/editor/plugins/asset_library_editor_plugin.cpp
+++ b/editor/plugins/asset_library_editor_plugin.cpp
@@ -421,7 +421,16 @@ void EditorAssetLibraryItemDownload::_notification(int p_what) {
int cstatus = download->get_http_client_status();
if (cstatus == HTTPClient::STATUS_BODY) {
- status->set_text(vformat(TTR("Downloading (%s / %s)..."), String::humanize_size(download->get_downloaded_bytes()), String::humanize_size(download->get_body_size())));
+ if (download->get_body_size() > 0) {
+ status->set_text(
+ vformat(
+ TTR("Downloading (%s / %s)..."),
+ String::humanize_size(download->get_downloaded_bytes()),
+ String::humanize_size(download->get_body_size())));
+ } else {
+ // Total file size is unknown, so it cannot be displayed
+ status->set_text(TTR("Downloading..."));
+ }
}
if (cstatus != prev_status) {
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 9782b9d1f4..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;
}
@@ -5187,9 +5194,10 @@ void SpatialEditor::_register_all_gizmos() {
register_gizmo_plugin(Ref<MeshInstanceSpatialGizmoPlugin>(memnew(MeshInstanceSpatialGizmoPlugin)));
register_gizmo_plugin(Ref<SoftBodySpatialGizmoPlugin>(memnew(SoftBodySpatialGizmoPlugin)));
register_gizmo_plugin(Ref<Sprite3DSpatialGizmoPlugin>(memnew(Sprite3DSpatialGizmoPlugin)));
- register_gizmo_plugin(Ref<Position3DSpatialGizmoPlugin>(memnew(Position3DSpatialGizmoPlugin)));
register_gizmo_plugin(Ref<SkeletonSpatialGizmoPlugin>(memnew(SkeletonSpatialGizmoPlugin)));
+ register_gizmo_plugin(Ref<Position3DSpatialGizmoPlugin>(memnew(Position3DSpatialGizmoPlugin)));
register_gizmo_plugin(Ref<RayCastSpatialGizmoPlugin>(memnew(RayCastSpatialGizmoPlugin)));
+ register_gizmo_plugin(Ref<SpringArmSpatialGizmoPlugin>(memnew(SpringArmSpatialGizmoPlugin)));
register_gizmo_plugin(Ref<VehicleWheelSpatialGizmoPlugin>(memnew(VehicleWheelSpatialGizmoPlugin)));
register_gizmo_plugin(Ref<VisibilityNotifierGizmoPlugin>(memnew(VisibilityNotifierGizmoPlugin)));
register_gizmo_plugin(Ref<ParticlesGizmoPlugin>(memnew(ParticlesGizmoPlugin)));
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/editor/script_editor_debugger.cpp b/editor/script_editor_debugger.cpp
index 97147535c0..746e1cd28f 100644
--- a/editor/script_editor_debugger.cpp
+++ b/editor/script_editor_debugger.cpp
@@ -1240,6 +1240,9 @@ void ScriptEditorDebugger::stop() {
if (connection.is_valid()) {
EditorNode::get_log()->add_message("** Debug Process Stopped **");
connection.unref();
+
+ reason->set_text("");
+ reason->set_tooltip("");
}
pending_in_queue = 0;
diff --git a/editor/spatial_editor_gizmos.cpp b/editor/spatial_editor_gizmos.cpp
index 64fdc778d0..64638cdb1e 100644
--- a/editor/spatial_editor_gizmos.cpp
+++ b/editor/spatial_editor_gizmos.cpp
@@ -677,7 +677,7 @@ void EditorSpatialGizmo::_bind_methods() {
ClassDB::bind_method(D_METHOD("add_collision_segments", "segments"), &EditorSpatialGizmo::add_collision_segments);
ClassDB::bind_method(D_METHOD("add_collision_triangles", "triangles"), &EditorSpatialGizmo::add_collision_triangles);
ClassDB::bind_method(D_METHOD("add_unscaled_billboard", "material", "default_scale"), &EditorSpatialGizmo::add_unscaled_billboard, DEFVAL(1));
- ClassDB::bind_method(D_METHOD("add_handles", "handles", "billboard", "secondary"), &EditorSpatialGizmo::add_handles, DEFVAL(false), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("add_handles", "handles", "material", "billboard", "secondary"), &EditorSpatialGizmo::add_handles, DEFVAL(false), DEFVAL(false));
ClassDB::bind_method(D_METHOD("set_spatial_node", "node"), &EditorSpatialGizmo::_set_spatial_node);
ClassDB::bind_method(D_METHOD("clear"), &EditorSpatialGizmo::clear);
ClassDB::bind_method(D_METHOD("set_hidden", "hidden"), &EditorSpatialGizmo::set_hidden);
@@ -1337,6 +1337,48 @@ void CameraSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
p_gizmo->add_collision_segments(lines);
p_gizmo->add_unscaled_billboard(icon, 0.05);
p_gizmo->add_handles(handles, get_material("handles"));
+
+ ClippedCamera *clipcam = Object::cast_to<ClippedCamera>(camera);
+ if (clipcam) {
+ Spatial *parent = Object::cast_to<Spatial>(camera->get_parent());
+ if (!parent) {
+ return;
+ }
+ Vector3 cam_normal = -camera->get_global_transform().basis.get_axis(Vector3::AXIS_Z).normalized();
+ Vector3 cam_x = camera->get_global_transform().basis.get_axis(Vector3::AXIS_X).normalized();
+ Vector3 cam_y = camera->get_global_transform().basis.get_axis(Vector3::AXIS_Y).normalized();
+ Vector3 cam_pos = camera->get_global_transform().origin;
+ Vector3 parent_pos = parent->get_global_transform().origin;
+
+ Plane parent_plane(parent_pos, cam_normal);
+ Vector3 ray_from = parent_plane.project(cam_pos);
+
+ lines.clear();
+ lines.push_back(ray_from + cam_x * 0.5 + cam_y * 0.5);
+ lines.push_back(ray_from + cam_x * 0.5 + cam_y * -0.5);
+
+ lines.push_back(ray_from + cam_x * 0.5 + cam_y * -0.5);
+ lines.push_back(ray_from + cam_x * -0.5 + cam_y * -0.5);
+
+ lines.push_back(ray_from + cam_x * -0.5 + cam_y * -0.5);
+ lines.push_back(ray_from + cam_x * -0.5 + cam_y * 0.5);
+
+ lines.push_back(ray_from + cam_x * -0.5 + cam_y * 0.5);
+ lines.push_back(ray_from + cam_x * 0.5 + cam_y * 0.5);
+
+ if (parent_plane.distance_to(cam_pos) < 0) {
+ lines.push_back(ray_from);
+ lines.push_back(cam_pos);
+ }
+
+ Transform local = camera->get_global_transform().affine_inverse();
+ for (int i = 0; i < lines.size(); i++) {
+ lines.write[i] = local.xform(lines[i]);
+ }
+
+ p_gizmo->add_lines(lines, material);
+ p_gizmo->add_collision_segments(lines);
+ }
}
//////
@@ -1920,6 +1962,38 @@ void RayCastSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
/////
+void SpringArmSpatialGizmoPlugin::redraw(EditorSpatialGizmo *p_gizmo) {
+
+ SpringArm *spring_arm = Object::cast_to<SpringArm>(p_gizmo->get_spatial_node());
+
+ p_gizmo->clear();
+
+ Vector<Vector3> lines;
+
+ lines.push_back(Vector3());
+ lines.push_back(Vector3(0, 0, 1.0) * spring_arm->get_length());
+
+ Ref<SpatialMaterial> material = get_material("shape_material", p_gizmo);
+
+ p_gizmo->add_lines(lines, material);
+ p_gizmo->add_collision_segments(lines);
+}
+
+SpringArmSpatialGizmoPlugin::SpringArmSpatialGizmoPlugin() {
+ Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1));
+ create_material("shape_material", gizmo_color);
+}
+
+bool SpringArmSpatialGizmoPlugin::has_gizmo(Spatial *p_spatial) {
+ return Object::cast_to<SpringArm>(p_spatial) != NULL;
+}
+
+String SpringArmSpatialGizmoPlugin::get_name() const {
+ return "SpringArm";
+}
+
+/////
+
VehicleWheelSpatialGizmoPlugin::VehicleWheelSpatialGizmoPlugin() {
Color gizmo_color = EDITOR_DEF("editors/3d_gizmos/gizmo_colors/shape", Color(0.5, 0.7, 1));
diff --git a/editor/spatial_editor_gizmos.h b/editor/spatial_editor_gizmos.h
index 877590b91d..6f29e9d999 100644
--- a/editor/spatial_editor_gizmos.h
+++ b/editor/spatial_editor_gizmos.h
@@ -49,6 +49,7 @@
#include "scene/3d/ray_cast.h"
#include "scene/3d/reflection_probe.h"
#include "scene/3d/room_instance.h"
+#include "scene/3d/spring_arm.h"
#include "scene/3d/sprite_3d.h"
#include "scene/3d/vehicle_body.h"
#include "scene/3d/visibility_notifier.h"
@@ -196,6 +197,18 @@ public:
RayCastSpatialGizmoPlugin();
};
+class SpringArmSpatialGizmoPlugin : public EditorSpatialGizmoPlugin {
+
+ GDCLASS(SpringArmSpatialGizmoPlugin, EditorSpatialGizmoPlugin);
+
+public:
+ bool has_gizmo(Spatial *p_spatial);
+ String get_name() const;
+ void redraw(EditorSpatialGizmo *p_gizmo);
+
+ SpringArmSpatialGizmoPlugin();
+};
+
class VehicleWheelSpatialGizmoPlugin : public EditorSpatialGizmoPlugin {
GDCLASS(VehicleWheelSpatialGizmoPlugin, EditorSpatialGizmoPlugin);
@@ -330,14 +343,12 @@ public:
};
class CollisionPolygonSpatialGizmoPlugin : public EditorSpatialGizmoPlugin {
-
GDCLASS(CollisionPolygonSpatialGizmoPlugin, EditorSpatialGizmoPlugin);
public:
bool has_gizmo(Spatial *p_spatial);
String get_name() const;
void redraw(EditorSpatialGizmo *p_gizmo);
-
CollisionPolygonSpatialGizmoPlugin();
};
diff --git a/modules/bullet/bullet_physics_server.cpp b/modules/bullet/bullet_physics_server.cpp
index 0857635492..70f70e7e5f 100644
--- a/modules/bullet/bullet_physics_server.cpp
+++ b/modules/bullet/bullet_physics_server.cpp
@@ -163,6 +163,18 @@ Variant BulletPhysicsServer::shape_get_data(RID p_shape) const {
return shape->get_data();
}
+void BulletPhysicsServer::shape_set_margin(RID p_shape, real_t p_margin) {
+ ShapeBullet *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND(!shape);
+ shape->set_margin(p_margin);
+}
+
+real_t BulletPhysicsServer::shape_get_margin(RID p_shape) const {
+ ShapeBullet *shape = shape_owner.get(p_shape);
+ ERR_FAIL_COND_V(!shape, 0.0);
+ return shape->get_margin();
+}
+
real_t BulletPhysicsServer::shape_get_custom_solver_bias(RID p_shape) const {
//WARN_PRINT("Bias not supported by Bullet physics engine");
return 0.;
@@ -861,12 +873,20 @@ PhysicsDirectBodyState *BulletPhysicsServer::body_get_direct_state(RID p_body) {
return BulletPhysicsDirectBodyState::get_singleton(body);
}
-bool BulletPhysicsServer::body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result) {
+bool BulletPhysicsServer::body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result, bool p_exclude_raycast_shapes) {
RigidBodyBullet *body = rigid_body_owner.get(p_body);
ERR_FAIL_COND_V(!body, false);
ERR_FAIL_COND_V(!body->get_space(), false);
- return body->get_space()->test_body_motion(body, p_from, p_motion, p_infinite_inertia, r_result);
+ return body->get_space()->test_body_motion(body, p_from, p_motion, p_infinite_inertia, r_result, p_exclude_raycast_shapes);
+}
+
+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) {
diff --git a/modules/bullet/bullet_physics_server.h b/modules/bullet/bullet_physics_server.h
index 0e858ff311..e9c568d605 100644
--- a/modules/bullet/bullet_physics_server.h
+++ b/modules/bullet/bullet_physics_server.h
@@ -99,6 +99,9 @@ public:
virtual ShapeType shape_get_type(RID p_shape) const;
virtual Variant shape_get_data(RID p_shape) const;
+ virtual void shape_set_margin(RID p_shape, real_t p_margin);
+ virtual real_t shape_get_margin(RID p_shape) const;
+
/// Not supported
virtual void shape_set_custom_solver_bias(RID p_shape, real_t p_bias);
/// Not supported
@@ -258,7 +261,8 @@ public:
// this function only works on physics process, errors and returns null otherwise
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 bool body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result = NULL, bool p_exclude_raycast_shapes = true);
+ 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..534034d707 100644
--- a/modules/bullet/godot_result_callbacks.cpp
+++ b/modules/bullet/godot_result_callbacks.cpp
@@ -30,6 +30,7 @@
#include "godot_result_callbacks.h"
+#include "area_bullet.h"
#include "bullet_types_converter.h"
#include "collision_object_bullet.h"
#include "rigid_body_bullet.h"
@@ -51,11 +52,23 @@ 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;
- } else if (m_exclude->has(gObj->get_self())) {
+
+ if (CollisionObjectBullet::TYPE_AREA == gObj->getType()) {
+ if (!collide_with_areas)
+ return false;
+ } else {
+ if (!collide_with_bodies)
+ return false;
+ }
+
+ if (m_pickRay && !gObj->is_ray_pickable()) {
+ return false;
+ }
+
+ if (m_exclude->has(gObj->get_self())) {
return false;
}
+
return true;
} else {
return false;
@@ -124,6 +137,15 @@ bool GodotClosestConvexResultCallback::needsCollision(btBroadphaseProxy *proxy0)
if (needs) {
btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer());
+
+ if (CollisionObjectBullet::TYPE_AREA == gObj->getType()) {
+ if (!collide_with_areas)
+ return false;
+ } else {
+ if (!collide_with_bodies)
+ return false;
+ }
+
if (m_exclude->has(gObj->get_self())) {
return false;
}
@@ -144,6 +166,15 @@ bool GodotAllContactResultCallback::needsCollision(btBroadphaseProxy *proxy0) co
if (needs) {
btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer());
+
+ if (CollisionObjectBullet::TYPE_AREA == gObj->getType()) {
+ if (!collide_with_areas)
+ return false;
+ } else {
+ if (!collide_with_bodies)
+ return false;
+ }
+
if (m_exclude->has(gObj->get_self())) {
return false;
}
@@ -189,6 +220,15 @@ bool GodotContactPairContactResultCallback::needsCollision(btBroadphaseProxy *pr
if (needs) {
btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer());
+
+ if (CollisionObjectBullet::TYPE_AREA == gObj->getType()) {
+ if (!collide_with_areas)
+ return false;
+ } else {
+ if (!collide_with_bodies)
+ return false;
+ }
+
if (m_exclude->has(gObj->get_self())) {
return false;
}
@@ -218,6 +258,15 @@ bool GodotRestInfoContactResultCallback::needsCollision(btBroadphaseProxy *proxy
if (needs) {
btCollisionObject *btObj = static_cast<btCollisionObject *>(proxy0->m_clientObject);
CollisionObjectBullet *gObj = static_cast<CollisionObjectBullet *>(btObj->getUserPointer());
+
+ if (CollisionObjectBullet::TYPE_AREA == gObj->getType()) {
+ if (!collide_with_areas)
+ return false;
+ } else {
+ if (!collide_with_bodies)
+ return false;
+ }
+
if (m_exclude->has(gObj->get_self())) {
return false;
}
@@ -260,10 +309,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/godot_result_callbacks.h b/modules/bullet/godot_result_callbacks.h
index 363051f24c..3948f43c00 100644
--- a/modules/bullet/godot_result_callbacks.h
+++ b/modules/bullet/godot_result_callbacks.h
@@ -56,12 +56,17 @@ struct GodotClosestRayResultCallback : public btCollisionWorld::ClosestRayResult
bool m_pickRay;
int m_shapeId;
+ bool collide_with_bodies;
+ bool collide_with_areas;
+
public:
- GodotClosestRayResultCallback(const btVector3 &rayFromWorld, const btVector3 &rayToWorld, const Set<RID> *p_exclude) :
+ GodotClosestRayResultCallback(const btVector3 &rayFromWorld, const btVector3 &rayToWorld, const Set<RID> *p_exclude, bool p_collide_with_bodies, bool p_collide_with_areas) :
btCollisionWorld::ClosestRayResultCallback(rayFromWorld, rayToWorld),
m_exclude(p_exclude),
m_pickRay(false),
- m_shapeId(0) {}
+ m_shapeId(0),
+ collide_with_bodies(p_collide_with_bodies),
+ collide_with_areas(p_collide_with_areas) {}
virtual bool needsCollision(btBroadphaseProxy *proxy0) const;
@@ -108,9 +113,14 @@ public:
const Set<RID> *m_exclude;
int m_shapeId;
- GodotClosestConvexResultCallback(const btVector3 &convexFromWorld, const btVector3 &convexToWorld, const Set<RID> *p_exclude) :
+ bool collide_with_bodies;
+ bool collide_with_areas;
+
+ GodotClosestConvexResultCallback(const btVector3 &convexFromWorld, const btVector3 &convexToWorld, const Set<RID> *p_exclude, bool p_collide_with_bodies, bool p_collide_with_areas) :
btCollisionWorld::ClosestConvexResultCallback(convexFromWorld, convexToWorld),
- m_exclude(p_exclude) {}
+ m_exclude(p_exclude),
+ collide_with_bodies(p_collide_with_bodies),
+ collide_with_areas(p_collide_with_areas) {}
virtual bool needsCollision(btBroadphaseProxy *proxy0) const;
@@ -125,12 +135,17 @@ public:
int m_count;
const Set<RID> *m_exclude;
- GodotAllContactResultCallback(btCollisionObject *p_self_object, PhysicsDirectSpaceState::ShapeResult *p_results, int p_resultMax, const Set<RID> *p_exclude) :
+ bool collide_with_bodies;
+ bool collide_with_areas;
+
+ GodotAllContactResultCallback(btCollisionObject *p_self_object, PhysicsDirectSpaceState::ShapeResult *p_results, int p_resultMax, const Set<RID> *p_exclude, bool p_collide_with_bodies, bool p_collide_with_areas) :
m_self_object(p_self_object),
m_results(p_results),
m_exclude(p_exclude),
m_resultMax(p_resultMax),
- m_count(0) {}
+ m_count(0),
+ collide_with_bodies(p_collide_with_bodies),
+ collide_with_areas(p_collide_with_areas) {}
virtual bool needsCollision(btBroadphaseProxy *proxy0) const;
@@ -146,12 +161,17 @@ public:
int m_count;
const Set<RID> *m_exclude;
- GodotContactPairContactResultCallback(btCollisionObject *p_self_object, Vector3 *p_results, int p_resultMax, const Set<RID> *p_exclude) :
+ bool collide_with_bodies;
+ bool collide_with_areas;
+
+ GodotContactPairContactResultCallback(btCollisionObject *p_self_object, Vector3 *p_results, int p_resultMax, const Set<RID> *p_exclude, bool p_collide_with_bodies, bool p_collide_with_areas) :
m_self_object(p_self_object),
m_results(p_results),
m_exclude(p_exclude),
m_resultMax(p_resultMax),
- m_count(0) {}
+ m_count(0),
+ collide_with_bodies(p_collide_with_bodies),
+ collide_with_areas(p_collide_with_areas) {}
virtual bool needsCollision(btBroadphaseProxy *proxy0) const;
@@ -167,13 +187,17 @@ public:
const btCollisionObject *m_rest_info_collision_object;
btVector3 m_rest_info_bt_point;
const Set<RID> *m_exclude;
+ bool collide_with_bodies;
+ bool collide_with_areas;
- GodotRestInfoContactResultCallback(btCollisionObject *p_self_object, PhysicsDirectSpaceState::ShapeRestInfo *p_result, const Set<RID> *p_exclude) :
+ GodotRestInfoContactResultCallback(btCollisionObject *p_self_object, PhysicsDirectSpaceState::ShapeRestInfo *p_result, const Set<RID> *p_exclude, bool p_collide_with_bodies, bool p_collide_with_areas) :
m_self_object(p_self_object),
m_result(p_result),
m_exclude(p_exclude),
m_collided(false),
- m_min_distance(0) {}
+ m_min_distance(0),
+ collide_with_bodies(p_collide_with_bodies),
+ collide_with_areas(p_collide_with_areas) {}
virtual bool needsCollision(btBroadphaseProxy *proxy0) const;
diff --git a/modules/bullet/shape_bullet.cpp b/modules/bullet/shape_bullet.cpp
index e4c1a5f9b5..fab8d0cf3d 100644
--- a/modules/bullet/shape_bullet.cpp
+++ b/modules/bullet/shape_bullet.cpp
@@ -44,19 +44,20 @@
@author AndreaCatania
*/
-ShapeBullet::ShapeBullet() {}
+ShapeBullet::ShapeBullet() :
+ margin(0.04) {}
ShapeBullet::~ShapeBullet() {}
-btCollisionShape *ShapeBullet::create_bt_shape(const Vector3 &p_implicit_scale, real_t p_margin) {
+btCollisionShape *ShapeBullet::create_bt_shape(const Vector3 &p_implicit_scale, real_t p_extra_edge) {
btVector3 s;
G_TO_B(p_implicit_scale, s);
- return create_bt_shape(s, p_margin);
+ return create_bt_shape(s, p_extra_edge);
}
btCollisionShape *ShapeBullet::prepare(btCollisionShape *p_btShape) const {
p_btShape->setUserPointer(const_cast<ShapeBullet *>(this));
- p_btShape->setMargin(0.);
+ p_btShape->setMargin(margin);
return p_btShape;
}
@@ -93,6 +94,15 @@ const Map<ShapeOwnerBullet *, int> &ShapeBullet::get_owners() const {
return owners;
}
+void ShapeBullet::set_margin(real_t p_margin) {
+ margin = p_margin;
+ notifyShapeChanged();
+}
+
+real_t ShapeBullet::get_margin() const {
+ return margin;
+}
+
btEmptyShape *ShapeBullet::create_shape_empty() {
return bulletnew(btEmptyShape);
}
@@ -166,7 +176,7 @@ void PlaneShapeBullet::setup(const Plane &p_plane) {
notifyShapeChanged();
}
-btCollisionShape *PlaneShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
+btCollisionShape *PlaneShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
btVector3 btPlaneNormal;
G_TO_B(plane.normal, btPlaneNormal);
return prepare(PlaneShapeBullet::create_shape_plane(btPlaneNormal, plane.d));
@@ -194,8 +204,8 @@ void SphereShapeBullet::setup(real_t p_radius) {
notifyShapeChanged();
}
-btCollisionShape *SphereShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
- return prepare(ShapeBullet::create_shape_sphere(radius * p_implicit_scale[0] + p_margin));
+btCollisionShape *SphereShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+ return prepare(ShapeBullet::create_shape_sphere(radius * p_implicit_scale[0] + p_extra_edge));
}
/* Box */
@@ -221,8 +231,8 @@ void BoxShapeBullet::setup(const Vector3 &p_half_extents) {
notifyShapeChanged();
}
-btCollisionShape *BoxShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
- return prepare(ShapeBullet::create_shape_box((half_extents * p_implicit_scale) + btVector3(p_margin, p_margin, p_margin)));
+btCollisionShape *BoxShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+ return prepare(ShapeBullet::create_shape_box((half_extents * p_implicit_scale) + btVector3(p_extra_edge, p_extra_edge, p_extra_edge)));
}
/* Capsule */
@@ -254,8 +264,8 @@ void CapsuleShapeBullet::setup(real_t p_height, real_t p_radius) {
notifyShapeChanged();
}
-btCollisionShape *CapsuleShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
- return prepare(ShapeBullet::create_shape_capsule(radius * p_implicit_scale[0] + p_margin, height * p_implicit_scale[1] + p_margin));
+btCollisionShape *CapsuleShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+ return prepare(ShapeBullet::create_shape_capsule(radius * p_implicit_scale[0] + p_extra_edge, height * p_implicit_scale[1] + p_extra_edge));
}
/* Cylinder */
@@ -329,11 +339,10 @@ void ConvexPolygonShapeBullet::setup(const Vector<Vector3> &p_vertices) {
notifyShapeChanged();
}
-btCollisionShape *ConvexPolygonShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
+btCollisionShape *ConvexPolygonShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
btCollisionShape *cs(ShapeBullet::create_shape_convex(vertices));
cs->setLocalScaling(p_implicit_scale);
prepare(cs);
- cs->setMargin(p_margin);
return cs;
}
@@ -402,14 +411,13 @@ void ConcavePolygonShapeBullet::setup(PoolVector<Vector3> p_faces) {
notifyShapeChanged();
}
-btCollisionShape *ConcavePolygonShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
+btCollisionShape *ConcavePolygonShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
btCollisionShape *cs = ShapeBullet::create_shape_concave(meshShape);
if (!cs)
// This is necessary since if 0 faces the creation of concave return NULL
cs = ShapeBullet::create_shape_empty();
cs->setLocalScaling(p_implicit_scale);
prepare(cs);
- cs->setMargin(p_margin);
return cs;
}
@@ -495,11 +503,10 @@ void HeightMapShapeBullet::setup(PoolVector<real_t> &p_heights, int p_width, int
notifyShapeChanged();
}
-btCollisionShape *HeightMapShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
+btCollisionShape *HeightMapShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
btCollisionShape *cs(ShapeBullet::create_shape_height_field(heights, width, depth, min_height, max_height));
cs->setLocalScaling(p_implicit_scale);
prepare(cs);
- cs->setMargin(p_margin);
return cs;
}
@@ -533,6 +540,6 @@ void RayShapeBullet::setup(real_t p_length, bool p_slips_on_slope) {
notifyShapeChanged();
}
-btCollisionShape *RayShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin) {
- return prepare(ShapeBullet::create_shape_ray(length * p_implicit_scale[1] + p_margin, slips_on_slope));
+btCollisionShape *RayShapeBullet::create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge) {
+ return prepare(ShapeBullet::create_shape_ray(length * p_implicit_scale[1] + p_extra_edge, slips_on_slope));
}
diff --git a/modules/bullet/shape_bullet.h b/modules/bullet/shape_bullet.h
index 16a5ac1fc6..638e044e6a 100644
--- a/modules/bullet/shape_bullet.h
+++ b/modules/bullet/shape_bullet.h
@@ -52,6 +52,7 @@ class btBvhTriangleMeshShape;
class ShapeBullet : public RIDBullet {
Map<ShapeOwnerBullet *, int> owners;
+ real_t margin;
protected:
/// return self
@@ -62,14 +63,17 @@ public:
ShapeBullet();
virtual ~ShapeBullet();
- btCollisionShape *create_bt_shape(const Vector3 &p_implicit_scale, real_t p_margin = 0);
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0) = 0;
+ btCollisionShape *create_bt_shape(const Vector3 &p_implicit_scale, real_t p_extra_edge = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0) = 0;
void add_owner(ShapeOwnerBullet *p_owner);
void remove_owner(ShapeOwnerBullet *p_owner, bool p_permanentlyFromThisBody = false);
bool is_owner(ShapeOwnerBullet *p_owner) const;
const Map<ShapeOwnerBullet *, int> &get_owners() const;
+ void set_margin(real_t p_margin);
+ real_t get_margin() const;
+
/// Setup the shape
virtual void set_data(const Variant &p_data) = 0;
virtual Variant get_data() const = 0;
@@ -100,7 +104,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(const Plane &p_plane);
@@ -117,7 +121,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(real_t p_radius);
@@ -134,7 +138,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(const Vector3 &p_half_extents);
@@ -153,7 +157,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(real_t p_height, real_t p_radius);
@@ -189,7 +193,7 @@ public:
void get_vertices(Vector<Vector3> &out_vertices);
virtual Variant get_data() const;
virtual PhysicsServer::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(const Vector<Vector3> &p_vertices);
@@ -207,7 +211,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(PoolVector<Vector3> p_faces);
@@ -227,7 +231,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(PoolVector<real_t> &p_heights, int p_width, int p_depth, real_t p_min_height, real_t p_max_height);
@@ -244,7 +248,7 @@ public:
virtual void set_data(const Variant &p_data);
virtual Variant get_data() const;
virtual PhysicsServer::ShapeType get_type() const;
- virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_margin = 0);
+ virtual btCollisionShape *create_bt_shape(const btVector3 &p_implicit_scale, real_t p_extra_edge = 0);
private:
void setup(real_t p_length, bool p_slips_on_slope);
diff --git a/modules/bullet/space_bullet.cpp b/modules/bullet/space_bullet.cpp
index 97228a972f..b5329bc347 100644
--- a/modules/bullet/space_bullet.cpp
+++ b/modules/bullet/space_bullet.cpp
@@ -61,7 +61,7 @@ BulletPhysicsDirectSpaceState::BulletPhysicsDirectSpaceState(SpaceBullet *p_spac
PhysicsDirectSpaceState(),
space(p_space) {}
-int BulletPhysicsDirectSpaceState::intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask) {
+int BulletPhysicsDirectSpaceState::intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
if (p_result_max <= 0)
return 0;
@@ -69,13 +69,13 @@ int BulletPhysicsDirectSpaceState::intersect_point(const Vector3 &p_point, Shape
btVector3 bt_point;
G_TO_B(p_point, bt_point);
- btSphereShape sphere_point(0.f);
+ btSphereShape sphere_point(0.001f);
btCollisionObject collision_object_point;
collision_object_point.setCollisionShape(&sphere_point);
collision_object_point.setWorldTransform(btTransform(btQuaternion::getIdentity(), bt_point));
// Setup query
- GodotAllContactResultCallback btResult(&collision_object_point, r_results, p_result_max, &p_exclude);
+ GodotAllContactResultCallback btResult(&collision_object_point, r_results, p_result_max, &p_exclude, p_collide_with_bodies, p_collide_with_areas);
btResult.m_collisionFilterGroup = 0;
btResult.m_collisionFilterMask = p_collision_mask;
space->dynamicsWorld->contactTest(&collision_object_point, btResult);
@@ -84,7 +84,7 @@ int BulletPhysicsDirectSpaceState::intersect_point(const Vector3 &p_point, Shape
return btResult.m_count;
}
-bool BulletPhysicsDirectSpaceState::intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_pick_ray) {
+bool BulletPhysicsDirectSpaceState::intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas, bool p_pick_ray) {
btVector3 btVec_from;
btVector3 btVec_to;
@@ -93,7 +93,7 @@ bool BulletPhysicsDirectSpaceState::intersect_ray(const Vector3 &p_from, const V
G_TO_B(p_to, btVec_to);
// setup query
- GodotClosestRayResultCallback btResult(btVec_from, btVec_to, &p_exclude);
+ GodotClosestRayResultCallback btResult(btVec_from, btVec_to, &p_exclude, p_collide_with_bodies, p_collide_with_areas);
btResult.m_collisionFilterGroup = 0;
btResult.m_collisionFilterMask = p_collision_mask;
btResult.m_pickRay = p_pick_ray;
@@ -117,7 +117,7 @@ bool BulletPhysicsDirectSpaceState::intersect_ray(const Vector3 &p_from, const V
}
}
-int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Transform &p_xform, float p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask) {
+int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Transform &p_xform, float p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
if (p_result_max <= 0)
return 0;
@@ -139,7 +139,7 @@ int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Tra
collision_object.setCollisionShape(btConvex);
collision_object.setWorldTransform(bt_xform);
- GodotAllContactResultCallback btQuery(&collision_object, r_results, p_result_max, &p_exclude);
+ GodotAllContactResultCallback btQuery(&collision_object, r_results, p_result_max, &p_exclude, p_collide_with_bodies, p_collide_with_areas);
btQuery.m_collisionFilterGroup = 0;
btQuery.m_collisionFilterMask = p_collision_mask;
btQuery.m_closestDistanceThreshold = 0;
@@ -150,7 +150,7 @@ int BulletPhysicsDirectSpaceState::intersect_shape(const RID &p_shape, const Tra
return btQuery.m_count;
}
-bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, float p_margin, float &p_closest_safe, float &p_closest_unsafe, const Set<RID> &p_exclude, uint32_t p_collision_mask, ShapeRestInfo *r_info) {
+bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, float p_margin, float &p_closest_safe, float &p_closest_unsafe, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas, ShapeRestInfo *r_info) {
ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get(p_shape);
btCollisionShape *btShape = shape->create_bt_shape(p_xform.basis.get_scale(), p_margin);
@@ -171,7 +171,7 @@ bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transf
btTransform bt_xform_to(bt_xform_from);
bt_xform_to.getOrigin() += bt_motion;
- GodotClosestConvexResultCallback btResult(bt_xform_from.getOrigin(), bt_xform_to.getOrigin(), &p_exclude);
+ GodotClosestConvexResultCallback btResult(bt_xform_from.getOrigin(), bt_xform_to.getOrigin(), &p_exclude, p_collide_with_bodies, p_collide_with_areas);
btResult.m_collisionFilterGroup = 0;
btResult.m_collisionFilterMask = p_collision_mask;
@@ -197,7 +197,7 @@ bool BulletPhysicsDirectSpaceState::cast_motion(const RID &p_shape, const Transf
}
/// Returns the list of contacts pairs in this order: Local contact, other body contact
-bool BulletPhysicsDirectSpaceState::collide_shape(RID p_shape, const Transform &p_shape_xform, float p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude, uint32_t p_collision_mask) {
+bool BulletPhysicsDirectSpaceState::collide_shape(RID p_shape, const Transform &p_shape_xform, float p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
if (p_result_max <= 0)
return 0;
@@ -219,7 +219,7 @@ bool BulletPhysicsDirectSpaceState::collide_shape(RID p_shape, const Transform &
collision_object.setCollisionShape(btConvex);
collision_object.setWorldTransform(bt_xform);
- GodotContactPairContactResultCallback btQuery(&collision_object, r_results, p_result_max, &p_exclude);
+ GodotContactPairContactResultCallback btQuery(&collision_object, r_results, p_result_max, &p_exclude, p_collide_with_bodies, p_collide_with_areas);
btQuery.m_collisionFilterGroup = 0;
btQuery.m_collisionFilterMask = p_collision_mask;
btQuery.m_closestDistanceThreshold = 0;
@@ -231,7 +231,7 @@ bool BulletPhysicsDirectSpaceState::collide_shape(RID p_shape, const Transform &
return btQuery.m_count;
}
-bool BulletPhysicsDirectSpaceState::rest_info(RID p_shape, const Transform &p_shape_xform, float p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude, uint32_t p_collision_mask) {
+bool BulletPhysicsDirectSpaceState::rest_info(RID p_shape, const Transform &p_shape_xform, float p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
ShapeBullet *shape = space->get_physics_server()->get_shape_owner()->get(p_shape);
@@ -251,7 +251,7 @@ bool BulletPhysicsDirectSpaceState::rest_info(RID p_shape, const Transform &p_sh
collision_object.setCollisionShape(btConvex);
collision_object.setWorldTransform(bt_xform);
- GodotRestInfoContactResultCallback btQuery(&collision_object, r_info, &p_exclude);
+ GodotRestInfoContactResultCallback btQuery(&collision_object, r_info, &p_exclude, p_collide_with_bodies, p_collide_with_areas);
btQuery.m_collisionFilterGroup = 0;
btQuery.m_collisionFilterMask = p_collision_mask;
btQuery.m_closestDistanceThreshold = 0;
@@ -819,7 +819,7 @@ static Ref<SpatialMaterial> red_mat;
static Ref<SpatialMaterial> blue_mat;
#endif
-bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer::MotionResult *r_result) {
+bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer::MotionResult *r_result, bool p_exclude_raycast_shapes) {
#if debug_test_motion
/// Yes I know this is not good, but I've used it as fast debugging hack.
@@ -894,6 +894,12 @@ bool SpaceBullet::test_body_motion(RigidBodyBullet *p_body, const Transform &p_f
// Skip no convex shape
continue;
}
+
+ if (p_exclude_raycast_shapes && 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..517ec67ffa 100644
--- a/modules/bullet/space_bullet.h
+++ b/modules/bullet/space_bullet.h
@@ -73,13 +73,13 @@ private:
public:
BulletPhysicsDirectSpaceState(SpaceBullet *p_space);
- virtual int intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF);
- virtual bool intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_pick_ray = false);
- virtual int intersect_shape(const RID &p_shape, const Transform &p_xform, float p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF);
- virtual bool cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, float p_margin, float &p_closest_safe, float &p_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, ShapeRestInfo *r_info = NULL);
+ virtual int intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
+ virtual bool intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_ray = false);
+ virtual int intersect_shape(const RID &p_shape, const Transform &p_xform, float p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
+ virtual bool cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, float p_margin, float &p_closest_safe, float &p_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, ShapeRestInfo *r_info = NULL);
/// Returns the list of contacts pairs in this order: Local contact, other body contact
- virtual bool collide_shape(RID p_shape, const Transform &p_shape_xform, float p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF);
- virtual bool rest_info(RID p_shape, const Transform &p_shape_xform, float p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF);
+ virtual bool collide_shape(RID p_shape, const Transform &p_shape_xform, float p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
+ virtual bool rest_info(RID p_shape, const Transform &p_shape_xform, float p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
virtual Vector3 get_closest_point_to_object_volume(RID p_object, const Vector3 p_point) const;
};
@@ -174,7 +174,8 @@ 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);
+ bool test_body_motion(RigidBodyBullet *p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, PhysicsServer::MotionResult *r_result, bool p_exclude_raycast_shapes);
+ 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/doc_classes/NativeScript.xml b/modules/gdnative/doc_classes/NativeScript.xml
index 1d3053244b..37d5b79e7a 100644
--- a/modules/gdnative/doc_classes/NativeScript.xml
+++ b/modules/gdnative/doc_classes/NativeScript.xml
@@ -57,6 +57,10 @@
</member>
<member name="library" type="GDNativeLibrary" setter="set_library" getter="get_library">
</member>
+ <member name="script_class_icon_path" type="String" setter="set_script_class_icon_path" getter="get_script_class_icon_path">
+ </member>
+ <member name="script_class_name" type="String" setter="set_script_class_name" getter="get_script_class_name">
+ </member>
</members>
<constants>
</constants>
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/modules/mono/glue/cs_files/Color.cs b/modules/mono/glue/cs_files/Color.cs
index 1195071bd3..49e04b333a 100644
--- a/modules/mono/glue/cs_files/Color.cs
+++ b/modules/mono/glue/cs_files/Color.cs
@@ -258,11 +258,6 @@ namespace Godot
return res;
}
- public float Gray()
- {
- return (r + g + b) / 3.0f;
- }
-
public Color Inverted()
{
return new Color(
diff --git a/modules/mono/glue/cs_files/Plane.cs b/modules/mono/glue/cs_files/Plane.cs
index 1020f06bf5..9611dce11e 100644
--- a/modules/mono/glue/cs_files/Plane.cs
+++ b/modules/mono/glue/cs_files/Plane.cs
@@ -145,6 +145,15 @@ namespace Godot
return point - _normal * DistanceTo(point);
}
+ // Constants
+ private static readonly Plane _planeYZ = new Plane(1, 0, 0, 0);
+ private static readonly Plane _planeXZ = new Plane(0, 1, 0, 0);
+ private static readonly Plane _planeXY = new Plane(0, 0, 1, 0);
+
+ public static Plane PlaneYZ { get { return _planeYZ; } }
+ public static Plane PlaneXZ { get { return _planeXZ; } }
+ public static Plane PlaneXY { get { return _planeXY; } }
+
// Constructors
public Plane(real_t a, real_t b, real_t c, real_t d)
{
diff --git a/modules/mono/utils/thread_local.cpp b/modules/mono/utils/thread_local.cpp
index ae9f130518..a0e28fca5f 100644
--- a/modules/mono/utils/thread_local.cpp
+++ b/modules/mono/utils/thread_local.cpp
@@ -69,7 +69,7 @@ struct ThreadLocalStorage::Impl {
#define _CALLBACK_FUNC_
#endif
- Impl(void (_CALLBACK_FUNC_ *p_destr_callback_func)(void *)) {
+ Impl(void(_CALLBACK_FUNC_ *p_destr_callback_func)(void *)) {
#ifdef WINDOWS_ENABLED
dwFlsIndex = FlsAlloc(p_destr_callback_func);
ERR_FAIL_COND(dwFlsIndex == FLS_OUT_OF_INDEXES);
@@ -95,7 +95,7 @@ void ThreadLocalStorage::set_value(void *p_value) const {
pimpl->set_value(p_value);
}
-void ThreadLocalStorage::alloc(void (_CALLBACK_FUNC_ *p_destr_callback)(void *)) {
+void ThreadLocalStorage::alloc(void(_CALLBACK_FUNC_ *p_destr_callback)(void *)) {
pimpl = memnew(ThreadLocalStorage::Impl(p_destr_callback));
}
diff --git a/modules/mono/utils/thread_local.h b/modules/mono/utils/thread_local.h
index 783e40dc01..84dae1d86b 100644
--- a/modules/mono/utils/thread_local.h
+++ b/modules/mono/utils/thread_local.h
@@ -76,7 +76,7 @@ struct ThreadLocalStorage {
void *get_value() const;
void set_value(void *p_value) const;
- void alloc(void (_CALLBACK_FUNC_ *p_dest_callback)(void *));
+ void alloc(void(_CALLBACK_FUNC_ *p_dest_callback)(void *));
void free();
private:
@@ -95,7 +95,6 @@ class ThreadLocal {
memdelete(static_cast<T *>(tls_data));
}
-
T *_tls_get_value() const {
void *tls_data = storage.get_value();
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..4537f6f326 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 {
@@ -1336,12 +1333,28 @@ public:
virtual bool can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
+ String err;
r_missing_templates = find_export_template("android_debug.apk") == String() || find_export_template("android_release.apk") == String();
+ if (p_preset->get("custom_package/debug") != "") {
+ if (FileAccess::exists(p_preset->get("custom_package/debug"))) {
+ r_missing_templates = false;
+ } else {
+ err += "Custom debug package not found.\n";
+ }
+ }
+
+ if (p_preset->get("custom_package/release") != "") {
+ if (FileAccess::exists(p_preset->get("custom_package/release"))) {
+ r_missing_templates = false;
+ } else {
+ err += "Custom release package not found.\n";
+ }
+ }
+
bool valid = !r_missing_templates;
String adb = EditorSettings::get_singleton()->get("export/android/adb");
- String err;
if (!FileAccess::exists(adb)) {
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/javascript/export/export.cpp b/platform/javascript/export/export.cpp
index 7cff6ba172..342e0e7802 100644
--- a/platform/javascript/export/export.cpp
+++ b/platform/javascript/export/export.cpp
@@ -140,14 +140,35 @@ Ref<Texture> EditorExportPlatformJavaScript::get_logo() const {
bool EditorExportPlatformJavaScript::can_export(const Ref<EditorExportPreset> &p_preset, String &r_error, bool &r_missing_templates) const {
- r_missing_templates = false;
+ bool valid = false;
+ String err;
+
+ if (find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_RELEASE) != "")
+ valid = true;
+ else if (find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_DEBUG) != "")
+ valid = true;
+
+ if (p_preset->get("custom_template/debug") != "") {
+ if (FileAccess::exists(p_preset->get("custom_template/debug"))) {
+ valid = true;
+ } else {
+ err += "Custom debug template not found.\n";
+ }
+ }
+
+ if (p_preset->get("custom_template/release") != "") {
+ if (FileAccess::exists(p_preset->get("custom_template/release"))) {
+ valid = true;
+ } else {
+ err += "Custom release template not found.\n";
+ }
+ }
- if (find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_RELEASE) == String())
- r_missing_templates = true;
- else if (find_export_template(EXPORT_TEMPLATE_WEBASSEMBLY_DEBUG) == String())
- r_missing_templates = true;
+ if (!err.empty())
+ r_error = err;
- return !r_missing_templates;
+ r_missing_templates = !valid;
+ return valid;
}
String EditorExportPlatformJavaScript::get_binary_extension(const Ref<EditorExportPreset> &p_preset) const {
diff --git a/platform/javascript/http_request.js b/platform/javascript/http_request.js
index c420052e54..ee1c06c623 100644
--- a/platform/javascript/http_request.js
+++ b/platform/javascript/http_request.js
@@ -82,7 +82,7 @@ var GodotHTTPRequest = {
godot_xhr_send_string: function(xhrId, strPtr) {
if (!strPtr) {
- Module.printErr("Failed to send string per XHR: null pointer");
+ console.warn("Failed to send string per XHR: null pointer");
return;
}
GodotHTTPRequest.requests[xhrId].send(UTF8ToString(strPtr));
@@ -90,11 +90,11 @@ var GodotHTTPRequest = {
godot_xhr_send_data: function(xhrId, ptr, len) {
if (!ptr) {
- Module.printErr("Failed to send data per XHR: null pointer");
+ console.warn("Failed to send data per XHR: null pointer");
return;
}
if (len < 0) {
- Module.printErr("Failed to send data per XHR: buffer length less than 0");
+ console.warn("Failed to send data per XHR: buffer length less than 0");
return;
}
GodotHTTPRequest.requests[xhrId].send(HEAPU8.subarray(ptr, ptr + len));
diff --git a/platform/javascript/javascript_eval.cpp b/platform/javascript/javascript_eval.cpp
index 2ef88345f6..07b4c192e6 100644
--- a/platform/javascript/javascript_eval.cpp
+++ b/platform/javascript/javascript_eval.cpp
@@ -69,7 +69,7 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
eval_ret = eval(UTF8ToString(CODE));
}
} catch (e) {
- Module.printErr(e);
+ console.warn(e);
eval_ret = null;
}
@@ -97,7 +97,7 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) {
if (array_ptr!==0) {
_free(array_ptr)
}
- Module.printErr(e);
+ console.warn(e);
// fall through
}
break;
diff --git a/platform/javascript/os_javascript.cpp b/platform/javascript/os_javascript.cpp
index b9d586e233..a2c6bdd629 100644
--- a/platform/javascript/os_javascript.cpp
+++ b/platform/javascript/os_javascript.cpp
@@ -788,7 +788,7 @@ bool OS_JavaScript::main_loop_iterate() {
/* clang-format off */
EM_ASM(
FS.syncfs(function(err) {
- if (err) { Module.printErr('Failed to save IDB file system: ' + err.message); }
+ if (err) { console.warn('Failed to save IDB file system: ' + err.message); }
});
);
/* clang-format on */
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..dcd3597e88 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,12 +181,7 @@ def configure_msvc(env, manual_msvc_config):
env.Append(CCFLAGS=['/O1'])
env.AppendUnique(CPPDEFINES = ['DEBUG_ENABLED'])
env.Append(LINKFLAGS=['/SUBSYSTEM:CONSOLE'])
-
- elif (env["target"] == "debug_release"):
- env.Append(CCFLAGS=['/Z7', '/Od'])
- env.Append(LINKFLAGS=['/DEBUG'])
- env.Append(LINKFLAGS=['/SUBSYSTEM:WINDOWS'])
- env.Append(LINKFLAGS=['/ENTRY:mainCRTStartup'])
+ env.Append(LINKFLAGS=['/OPT:REF'])
elif (env["target"] == "debug"):
env.AppendUnique(CCFLAGS=['/Z7', '/Od', '/EHsc'])
@@ -194,6 +190,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..ca3bbedef2 100644
--- a/platform/windows/os_windows.cpp
+++ b/platform/windows/os_windows.cpp
@@ -190,6 +190,28 @@ BOOL WINAPI HandlerRoutine(_In_ DWORD dwCtrlType) {
}
}
+BOOL CALLBACK _CloseWindowsEnum(HWND hWnd, LPARAM lParam) {
+ DWORD dwID;
+
+ GetWindowThreadProcessId(hWnd, &dwID);
+
+ if (dwID == (DWORD)lParam) {
+ PostMessage(hWnd, WM_CLOSE, 0, 0);
+ }
+
+ return TRUE;
+}
+
+bool _close_gracefully(const PROCESS_INFORMATION &pi, const DWORD dwStopWaitMsec) {
+ if (!EnumWindows(_CloseWindowsEnum, pi.dwProcessId))
+ return false;
+
+ if (WaitForSingleObject(pi.hProcess, dwStopWaitMsec) != WAIT_OBJECT_0)
+ return false;
+
+ return true;
+}
+
void OS_Windows::initialize_debugging() {
SetConsoleCtrlHandler(HandlerRoutine, TRUE);
@@ -2322,20 +2344,26 @@ Error OS_Windows::execute(const String &p_path, const List<String> &p_arguments,
return OK;
};
-Error OS_Windows::kill(const ProcessID &p_pid) {
-
+Error OS_Windows::kill(const ProcessID &p_pid, const int p_max_wait_msec) {
ERR_FAIL_COND_V(!process_map->has(p_pid), FAILED);
const PROCESS_INFORMATION pi = (*process_map)[p_pid].pi;
process_map->erase(p_pid);
- const int ret = TerminateProcess(pi.hProcess, 0);
+ Error result;
+
+ if (p_max_wait_msec != -1 && _close_gracefully(pi, p_max_wait_msec)) {
+ result = OK;
+ } else {
+ const int ret = TerminateProcess(pi.hProcess, 0);
+ result = ret != 0 ? OK : FAILED;
+ }
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
- return ret != 0 ? OK : FAILED;
-};
+ return result;
+}
int OS_Windows::get_process_id() const {
return _getpid();
@@ -2790,9 +2818,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 +2832,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/platform/windows/os_windows.h b/platform/windows/os_windows.h
index 69c7d851b8..8e39e4c990 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -258,7 +258,7 @@ public:
virtual uint64_t get_ticks_usec() const;
virtual Error execute(const String &p_path, const List<String> &p_arguments, bool p_blocking, ProcessID *r_child_id = NULL, String *r_pipe = NULL, int *r_exitcode = NULL, bool read_stderr = false);
- virtual Error kill(const ProcessID &p_pid);
+ virtual Error kill(const ProcessID &p_pid, const int p_stop_max_wait_msec = -1);
virtual int get_process_id() const;
virtual bool has_environment(const String &p_var) const;
diff --git a/scene/2d/canvas_item.cpp b/scene/2d/canvas_item.cpp
index a035d9021f..7f7e3542ed 100644
--- a/scene/2d/canvas_item.cpp
+++ b/scene/2d/canvas_item.cpp
@@ -349,23 +349,12 @@ void CanvasItem::_update_callback() {
Transform2D CanvasItem::get_global_transform_with_canvas() const {
- const CanvasItem *ci = this;
- Transform2D xform;
- const CanvasItem *last_valid = NULL;
-
- while (ci) {
-
- last_valid = ci;
- xform = ci->get_transform() * xform;
- ci = ci->get_parent_item();
- }
-
- if (last_valid->canvas_layer)
- return last_valid->canvas_layer->get_transform() * xform;
+ if (canvas_layer)
+ return canvas_layer->get_transform() * get_global_transform();
else if (is_inside_tree())
- return get_viewport()->get_canvas_transform() * xform;
-
- return xform;
+ return get_viewport()->get_canvas_transform() * get_global_transform();
+ else
+ return get_global_transform();
}
Transform2D CanvasItem::get_global_transform() const {
diff --git a/scene/2d/canvas_item.h b/scene/2d/canvas_item.h
index 1e6a251c9c..85f8564ac2 100644
--- a/scene/2d/canvas_item.h
+++ b/scene/2d/canvas_item.h
@@ -139,6 +139,8 @@ class CanvasItem : public Node {
GDCLASS(CanvasItem, Node);
+ friend class CanvasLayer;
+
public:
enum BlendMode {
diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp
index 1e2184bd41..52d04ac10a 100644
--- a/scene/2d/collision_object_2d.cpp
+++ b/scene/2d/collision_object_2d.cpp
@@ -38,7 +38,7 @@ void CollisionObject2D::_notification(int p_what) {
case NOTIFICATION_ENTER_TREE: {
- Transform2D global_transform = get_global_transform();
+ Transform2D global_transform = get_global_transform_with_canvas();
if (area)
Physics2DServer::get_singleton()->area_set_transform(rid, global_transform);
@@ -64,7 +64,7 @@ void CollisionObject2D::_notification(int p_what) {
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
- Transform2D global_transform = get_global_transform();
+ Transform2D global_transform = get_global_transform_with_canvas();
if (only_update_transform_changes && global_transform == last_transform) {
return;
diff --git a/scene/2d/collision_shape_2d.cpp b/scene/2d/collision_shape_2d.cpp
index 83ef4df8f4..ff5f7062c4 100644
--- a/scene/2d/collision_shape_2d.cpp
+++ b/scene/2d/collision_shape_2d.cpp
@@ -119,7 +119,7 @@ void CollisionShape2D::_notification(int p_what) {
Color draw_col = get_tree()->get_debug_collisions_color();
if (disabled) {
- float g = draw_col.gray();
+ float g = draw_col.get_v();
draw_col.r = g;
draw_col.g = g;
draw_col.b = g;
diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp
index 8758ffef9f..66686f10a8 100644
--- a/scene/2d/physics_body_2d.cpp
+++ b/scene/2d/physics_body_2d.cpp
@@ -35,19 +35,6 @@
#include "engine.h"
#include "math_funcs.h"
#include "scene/scene_string_names.h"
-void PhysicsBody2D::_notification(int p_what) {
-
- /*
- switch(p_what) {
-
- case NOTIFICATION_TRANSFORM_CHANGED: {
-
- Physics2DServer::get_singleton()->body_set_state(get_rid(),Physics2DServer::BODY_STATE_TRANSFORM,get_global_transform());
-
- } break;
- }
- */
-}
void PhysicsBody2D::_set_layers(uint32_t p_mask) {
@@ -436,7 +423,7 @@ bool RigidBody2D::_test_motion(const Vector2 &p_motion, bool p_infinite_inertia,
Physics2DServer::MotionResult *r = NULL;
if (p_result.is_valid())
r = p_result->get_result_ptr();
- return Physics2DServer::get_singleton()->body_test_motion(get_rid(), get_global_transform(), p_motion, p_infinite_inertia, p_margin, r);
+ return Physics2DServer::get_singleton()->body_test_motion(get_rid(), get_global_transform_with_canvas(), p_motion, p_infinite_inertia, p_margin, r);
}
void RigidBody2D::_direct_state_changed(Object *p_state) {
@@ -449,7 +436,7 @@ void RigidBody2D::_direct_state_changed(Object *p_state) {
set_block_transform_notify(true); // don't want notify (would feedback loop)
if (mode != MODE_KINEMATIC)
- set_global_transform(state->get_transform());
+ set_global_transform(get_canvas_transform().affine_inverse() * state->get_transform());
linear_velocity = state->get_linear_velocity();
angular_velocity = state->get_angular_velocity();
if (sleeping != state->is_sleeping()) {
@@ -1144,7 +1131,7 @@ bool KinematicBody2D::separate_raycast_shapes(bool p_infinite_inertia, Collision
Physics2DServer::SeparationResult sep_res[8]; //max 8 rays
- Transform2D gt = get_global_transform();
+ Transform2D gt = get_global_transform_with_canvas();
Vector2 recover;
int hits = Physics2DServer::get_singleton()->body_test_ray_separation(get_rid(), gt, p_infinite_inertia, recover, sep_res, 8, margin);
@@ -1158,7 +1145,7 @@ bool KinematicBody2D::separate_raycast_shapes(bool p_infinite_inertia, Collision
}
gt.elements[2] += recover;
- set_global_transform(gt);
+ set_global_transform(get_canvas_transform().affine_inverse() * gt);
if (deepest != -1) {
r_collision.collider = sep_res[deepest].collider_id;
@@ -1179,7 +1166,7 @@ bool KinematicBody2D::separate_raycast_shapes(bool p_infinite_inertia, Collision
bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_exclude_raycast_shapes, bool p_test_only) {
- Transform2D gt = get_global_transform();
+ Transform2D gt = get_global_transform_with_canvas();
Physics2DServer::MotionResult result;
bool colliding = Physics2DServer::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, margin, &result, p_exclude_raycast_shapes);
@@ -1198,7 +1185,7 @@ bool KinematicBody2D::move_and_collide(const Vector2 &p_motion, bool p_infinite_
if (!p_test_only) {
gt.elements[2] += result.motion;
- set_global_transform(gt);
+ set_global_transform(get_canvas_transform().affine_inverse() * gt);
}
return colliding;
@@ -1207,7 +1194,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 +1215,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 +1243,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 +1257,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_with_canvas();
+ gt.elements[2] -= collision.travel;
+ set_global_transform(get_canvas_transform().affine_inverse() * 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 +1275,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,17 +1300,17 @@ 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;
}
Collision col;
- Transform2D gt = get_global_transform();
+ Transform2D gt = get_global_transform_with_canvas();
if (move_and_collide(p_snap, p_infinite_inertia, col, false, true)) {
gt.elements[2] += col.travel;
@@ -1320,7 +1319,7 @@ Vector2 KinematicBody2D::move_and_slide_with_snap(const Vector2 &p_linear_veloci
on_floor_body = col.collider_rid;
floor_velocity = col.collider_vel;
}
- set_global_transform(gt);
+ set_global_transform(get_canvas_transform().affine_inverse() * gt);
}
return ret;
@@ -1417,30 +1416,30 @@ void KinematicBody2D::_direct_state_changed(Object *p_state) {
last_valid_transform = state->get_transform();
set_notify_local_transform(false);
- set_global_transform(last_valid_transform);
+ set_global_transform(get_canvas_transform().affine_inverse() * last_valid_transform);
set_notify_local_transform(true);
}
void KinematicBody2D::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
- last_valid_transform = get_global_transform();
+ last_valid_transform = get_global_transform_with_canvas();
}
if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) {
//used by sync to physics, send the new transform to the physics
- Transform2D new_transform = get_global_transform();
+ Transform2D new_transform = get_global_transform_with_canvas();
Physics2DServer::get_singleton()->body_set_state(get_rid(), Physics2DServer::BODY_STATE_TRANSFORM, new_transform);
//but then revert changes
set_notify_local_transform(false);
- set_global_transform(last_valid_transform);
+ set_global_transform(get_canvas_transform().affine_inverse() * last_valid_transform);
set_notify_local_transform(true);
}
}
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..0900438e3c 100644
--- a/scene/2d/physics_body_2d.h
+++ b/scene/2d/physics_body_2d.h
@@ -49,7 +49,6 @@ class PhysicsBody2D : public CollisionObject2D {
uint32_t _get_layers() const;
protected:
- void _notification(int p_what);
PhysicsBody2D(Physics2DServer::BodyMode p_mode);
static void _bind_methods();
@@ -338,8 +337,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/2d/ray_cast_2d.cpp b/scene/2d/ray_cast_2d.cpp
index 255d2d38d5..9582c08110 100644
--- a/scene/2d/ray_cast_2d.cpp
+++ b/scene/2d/ray_cast_2d.cpp
@@ -209,7 +209,7 @@ void RayCast2D::_update_raycast_state() {
Physics2DDirectSpaceState::RayResult rr;
- if (dss->intersect_ray(gt.get_origin(), gt.xform(to), rr, exclude, collision_mask)) {
+ if (dss->intersect_ray(gt.get_origin(), gt.xform(to), rr, exclude, collision_mask, collide_with_bodies, collide_with_areas)) {
collided = true;
against = rr.collider_id;
@@ -258,6 +258,26 @@ void RayCast2D::clear_exceptions() {
exclude.clear();
}
+void RayCast2D::set_collide_with_areas(bool p_clip) {
+
+ collide_with_areas = p_clip;
+}
+
+bool RayCast2D::is_collide_with_areas_enabled() const {
+
+ return collide_with_areas;
+}
+
+void RayCast2D::set_collide_with_bodies(bool p_clip) {
+
+ collide_with_bodies = p_clip;
+}
+
+bool RayCast2D::is_collide_with_bodies_enabled() const {
+
+ return collide_with_bodies;
+}
+
void RayCast2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &RayCast2D::set_enabled);
@@ -291,10 +311,20 @@ void RayCast2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_exclude_parent_body", "mask"), &RayCast2D::set_exclude_parent_body);
ClassDB::bind_method(D_METHOD("get_exclude_parent_body"), &RayCast2D::get_exclude_parent_body);
+ ClassDB::bind_method(D_METHOD("set_collide_with_areas", "enable"), &RayCast2D::set_collide_with_areas);
+ ClassDB::bind_method(D_METHOD("is_collide_with_areas_enabled"), &RayCast2D::is_collide_with_areas_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_collide_with_bodies", "enable"), &RayCast2D::set_collide_with_bodies);
+ ClassDB::bind_method(D_METHOD("is_collide_with_bodies_enabled"), &RayCast2D::is_collide_with_bodies_enabled);
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "exclude_parent"), "set_exclude_parent_body", "get_exclude_parent_body");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "cast_to"), "set_cast_to", "get_cast_to");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask");
+
+ ADD_GROUP("Collide With", "collide_with");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_areas", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_areas", "is_collide_with_areas_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_bodies", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_bodies", "is_collide_with_bodies_enabled");
}
RayCast2D::RayCast2D() {
@@ -306,4 +336,6 @@ RayCast2D::RayCast2D() {
collision_mask = 1;
cast_to = Vector2(0, 50);
exclude_parent_body = true;
+ collide_with_bodies = true;
+ collide_with_areas = false;
}
diff --git a/scene/2d/ray_cast_2d.h b/scene/2d/ray_cast_2d.h
index 0850cdc7cc..a438be87b6 100644
--- a/scene/2d/ray_cast_2d.h
+++ b/scene/2d/ray_cast_2d.h
@@ -49,12 +49,21 @@ class RayCast2D : public Node2D {
Vector2 cast_to;
+ bool collide_with_areas;
+ bool collide_with_bodies;
+
protected:
void _notification(int p_what);
void _update_raycast_state();
static void _bind_methods();
public:
+ void set_collide_with_areas(bool p_clip);
+ bool is_collide_with_areas_enabled() const;
+
+ void set_collide_with_bodies(bool p_clip);
+ bool is_collide_with_bodies_enabled() const;
+
void set_enabled(bool p_enabled);
bool is_enabled() const;
diff --git a/scene/2d/skeleton_2d.cpp b/scene/2d/skeleton_2d.cpp
index e6e9bde20a..2c362f1b31 100644
--- a/scene/2d/skeleton_2d.cpp
+++ b/scene/2d/skeleton_2d.cpp
@@ -288,7 +288,7 @@ void Skeleton2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("_update_transform"), &Skeleton2D::_update_transform);
ClassDB::bind_method(D_METHOD("get_bone_count"), &Skeleton2D::get_bone_count);
- ClassDB::bind_method(D_METHOD("get_bone"), &Skeleton2D::get_bone);
+ ClassDB::bind_method(D_METHOD("get_bone", "idx"), &Skeleton2D::get_bone);
ClassDB::bind_method(D_METHOD("get_skeleton"), &Skeleton2D::get_skeleton);
}
diff --git a/scene/3d/camera.cpp b/scene/3d/camera.cpp
index 2176b45faf..bcd015875b 100644
--- a/scene/3d/camera.cpp
+++ b/scene/3d/camera.cpp
@@ -31,9 +31,10 @@
#include "camera.h"
#include "camera_matrix.h"
+#include "collision_object.h"
+#include "engine.h"
#include "scene/resources/material.h"
#include "scene/resources/surface_tool.h"
-
void Camera::_update_audio_listener_state() {
}
@@ -313,6 +314,32 @@ bool Camera::is_position_behind(const Vector3 &p_pos) const {
return eyedir.dot(p_pos) < (eyedir.dot(t.origin) + near);
}
+Vector<Vector3> Camera::get_near_plane_points() const {
+ if (!is_inside_tree()) {
+ ERR_EXPLAIN("Camera is not inside scene.");
+ ERR_FAIL_COND_V(!is_inside_tree(), Vector<Vector3>());
+ }
+
+ Size2 viewport_size = get_viewport()->get_visible_rect().size;
+
+ CameraMatrix cm;
+
+ if (mode == PROJECTION_ORTHOGONAL)
+ cm.set_orthogonal(size, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH);
+ else
+ cm.set_perspective(fov, viewport_size.aspect(), near, far, keep_aspect == KEEP_WIDTH);
+
+ Vector3 endpoints[8];
+ cm.get_endpoints(Transform(), endpoints);
+
+ Vector<Vector3> points;
+ points.push_back(Vector3());
+ for (int i = 0; i < 4; i++) {
+ points.push_back(endpoints[i + 4]);
+ }
+ return points;
+}
+
Point2 Camera::unproject_position(const Vector3 &p_pos) const {
if (!is_inside_tree()) {
@@ -484,7 +511,7 @@ void Camera::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "fov", PROPERTY_HINT_RANGE, "1,179,0.1"), "set_fov", "get_fov");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "size", PROPERTY_HINT_RANGE, "0.1,16384,0.01"), "set_size", "get_size");
- ADD_PROPERTY(PropertyInfo(Variant::REAL, "near", PROPERTY_HINT_EXP_RANGE, "0.1,8192,0.1,or_greater"), "set_znear", "get_znear");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "near", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01,or_greater"), "set_znear", "get_znear");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "far", PROPERTY_HINT_EXP_RANGE, "0.1,8192,0.1,or_greater"), "set_zfar", "get_zfar");
BIND_ENUM_CONSTANT(PROJECTION_PERSPECTIVE);
@@ -638,3 +665,221 @@ Camera::~Camera() {
VisualServer::get_singleton()->free(camera);
}
+
+////////////////////////////////////////
+
+void ClippedCamera::set_margin(float p_margin) {
+ margin = p_margin;
+}
+float ClippedCamera::get_margin() const {
+ return margin;
+}
+void ClippedCamera::set_process_mode(ProcessMode p_mode) {
+
+ if (process_mode == p_mode) {
+ return;
+ }
+ set_process_internal(p_mode == CLIP_PROCESS_IDLE);
+ set_physics_process_internal(p_mode == CLIP_PROCESS_PHYSICS);
+}
+ClippedCamera::ProcessMode ClippedCamera::get_process_mode() const {
+ return process_mode;
+}
+
+Transform ClippedCamera::get_camera_transform() const {
+
+ Transform t = Camera::get_camera_transform();
+ t.origin += -t.basis.get_axis(Vector3::AXIS_Z).normalized() * clip_offset;
+ return t;
+}
+
+void ClippedCamera::_notification(int p_what) {
+ if (p_what == NOTIFICATION_INTERNAL_PROCESS || p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
+
+ Spatial *parent = Object::cast_to<Spatial>(get_parent());
+ if (!parent) {
+ return;
+ }
+
+ PhysicsDirectSpaceState *dspace = get_world()->get_direct_space_state();
+ ERR_FAIL_COND(!dspace); // most likely physics set to threads
+
+ Vector3 cam_fw = -get_global_transform().basis.get_axis(Vector3::AXIS_Z).normalized();
+ Vector3 cam_pos = get_global_transform().origin;
+ Vector3 parent_pos = parent->get_global_transform().origin;
+
+ Plane parent_plane(parent_pos, cam_fw);
+
+ if (parent_plane.is_point_over(cam_pos)) {
+ //cam is beyond parent plane
+ return;
+ }
+
+ Vector3 ray_from = parent_plane.project(cam_pos);
+
+ clip_offset = 0; //reset by defau;t
+
+ { //check if points changed
+ Vector<Vector3> local_points = get_near_plane_points();
+
+ bool all_equal = true;
+
+ for (int i = 0; i < 5; i++) {
+ if (points[i] != local_points[i]) {
+ all_equal = false;
+ break;
+ }
+ }
+
+ if (!all_equal) {
+ PhysicsServer::get_singleton()->shape_set_data(pyramid_shape, local_points);
+ points = local_points;
+ }
+ }
+
+ Transform xf = get_global_transform();
+ xf.origin = ray_from;
+ xf.orthonormalize();
+
+ float csafe, cunsafe;
+ if (dspace->cast_motion(pyramid_shape, xf, cam_pos - ray_from, margin, csafe, cunsafe, exclude, collision_mask, clip_to_bodies, clip_to_areas)) {
+ clip_offset = cam_pos.distance_to(ray_from + (cam_pos - ray_from).normalized() * csafe);
+ }
+
+ _update_camera();
+ }
+
+ if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) {
+ update_gizmo();
+ }
+}
+
+void ClippedCamera::set_collision_mask(uint32_t p_mask) {
+
+ collision_mask = p_mask;
+}
+
+uint32_t ClippedCamera::get_collision_mask() const {
+
+ return collision_mask;
+}
+
+void ClippedCamera::set_collision_mask_bit(int p_bit, bool p_value) {
+
+ uint32_t mask = get_collision_mask();
+ if (p_value)
+ mask |= 1 << p_bit;
+ else
+ mask &= ~(1 << p_bit);
+ set_collision_mask(mask);
+}
+
+bool ClippedCamera::get_collision_mask_bit(int p_bit) const {
+
+ return get_collision_mask() & (1 << p_bit);
+}
+
+void ClippedCamera::add_exception_rid(const RID &p_rid) {
+
+ exclude.insert(p_rid);
+}
+
+void ClippedCamera::add_exception(const Object *p_object) {
+
+ ERR_FAIL_NULL(p_object);
+ const CollisionObject *co = Object::cast_to<CollisionObject>(p_object);
+ if (!co)
+ return;
+ add_exception_rid(co->get_rid());
+}
+
+void ClippedCamera::remove_exception_rid(const RID &p_rid) {
+
+ exclude.erase(p_rid);
+}
+
+void ClippedCamera::remove_exception(const Object *p_object) {
+
+ ERR_FAIL_NULL(p_object);
+ const CollisionObject *co = Object::cast_to<CollisionObject>(p_object);
+ if (!co)
+ return;
+ remove_exception_rid(co->get_rid());
+}
+
+void ClippedCamera::clear_exceptions() {
+
+ exclude.clear();
+}
+
+void ClippedCamera::set_clip_to_areas(bool p_clip) {
+
+ clip_to_areas = p_clip;
+}
+
+bool ClippedCamera::is_clip_to_areas_enabled() const {
+
+ return clip_to_areas;
+}
+
+void ClippedCamera::set_clip_to_bodies(bool p_clip) {
+
+ clip_to_bodies = p_clip;
+}
+
+bool ClippedCamera::is_clip_to_bodies_enabled() const {
+
+ return clip_to_bodies;
+}
+
+void ClippedCamera::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_margin", "margin"), &ClippedCamera::set_margin);
+ ClassDB::bind_method(D_METHOD("get_margin"), &ClippedCamera::get_margin);
+
+ ClassDB::bind_method(D_METHOD("set_process_mode", "process_mode"), &ClippedCamera::set_process_mode);
+ ClassDB::bind_method(D_METHOD("get_process_mode"), &ClippedCamera::get_process_mode);
+
+ ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &ClippedCamera::set_collision_mask);
+ ClassDB::bind_method(D_METHOD("get_collision_mask"), &ClippedCamera::get_collision_mask);
+
+ ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &ClippedCamera::set_collision_mask_bit);
+ ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &ClippedCamera::get_collision_mask_bit);
+
+ ClassDB::bind_method(D_METHOD("add_exception_rid", "rid"), &ClippedCamera::add_exception_rid);
+ ClassDB::bind_method(D_METHOD("add_exception", "node"), &ClippedCamera::add_exception);
+
+ ClassDB::bind_method(D_METHOD("remove_exception_rid", "rid"), &ClippedCamera::remove_exception_rid);
+ ClassDB::bind_method(D_METHOD("remove_exception", "node"), &ClippedCamera::remove_exception);
+
+ ClassDB::bind_method(D_METHOD("set_clip_to_areas", "enable"), &ClippedCamera::set_clip_to_areas);
+ ClassDB::bind_method(D_METHOD("is_clip_to_areas_enabled"), &ClippedCamera::is_clip_to_areas_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_clip_to_bodies", "enable"), &ClippedCamera::set_clip_to_bodies);
+ ClassDB::bind_method(D_METHOD("is_clip_to_bodies_enabled"), &ClippedCamera::is_clip_to_bodies_enabled);
+
+ ClassDB::bind_method(D_METHOD("clear_exceptions"), &ClippedCamera::clear_exceptions);
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "margin", PROPERTY_HINT_RANGE, "0,32,0.01"), "set_margin", "get_margin");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_process_mode", "get_process_mode");
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
+
+ ADD_GROUP("Clip To", "clip_to");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_to_areas", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_clip_to_areas", "is_clip_to_areas_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_to_bodies", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_clip_to_bodies", "is_clip_to_bodies_enabled");
+}
+ClippedCamera::ClippedCamera() {
+ margin = 0;
+ clip_offset = 0;
+ process_mode = CLIP_PROCESS_PHYSICS;
+ set_physics_process_internal(true);
+ collision_mask = 1;
+ set_notify_local_transform(Engine::get_singleton()->is_editor_hint());
+ points.resize(5);
+ pyramid_shape = PhysicsServer::get_singleton()->shape_create(PhysicsServer::SHAPE_CONVEX_POLYGON);
+ clip_to_areas = false;
+ clip_to_bodies = true;
+}
+ClippedCamera::~ClippedCamera() {
+ PhysicsServer::get_singleton()->free(pyramid_shape);
+}
diff --git a/scene/3d/camera.h b/scene/3d/camera.h
index 97705d8ae0..a35c9d6e7f 100644
--- a/scene/3d/camera.h
+++ b/scene/3d/camera.h
@@ -139,6 +139,8 @@ public:
bool is_position_behind(const Vector3 &p_pos) const;
virtual Vector3 project_position(const Point2 &p_point) const;
+ Vector<Vector3> get_near_plane_points() const;
+
void set_cull_mask(uint32_t p_layers);
uint32_t get_cull_mask() const;
@@ -172,4 +174,62 @@ VARIANT_ENUM_CAST(Camera::Projection);
VARIANT_ENUM_CAST(Camera::KeepAspect);
VARIANT_ENUM_CAST(Camera::DopplerTracking);
+class ClippedCamera : public Camera {
+
+ GDCLASS(ClippedCamera, Camera);
+
+public:
+ enum ProcessMode {
+ CLIP_PROCESS_PHYSICS,
+ CLIP_PROCESS_IDLE,
+ };
+
+private:
+ ProcessMode process_mode;
+ RID pyramid_shape;
+ float margin;
+ float clip_offset;
+ uint32_t collision_mask;
+ bool clip_to_areas;
+ bool clip_to_bodies;
+
+ Set<RID> exclude;
+
+ Vector<Vector3> points;
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+ virtual Transform get_camera_transform() const;
+
+public:
+ void set_clip_to_areas(bool p_clip);
+ bool is_clip_to_areas_enabled() const;
+
+ void set_clip_to_bodies(bool p_clip);
+ bool is_clip_to_bodies_enabled() const;
+
+ void set_margin(float p_margin);
+ float get_margin() const;
+
+ void set_process_mode(ProcessMode p_mode);
+ ProcessMode get_process_mode() const;
+
+ void set_collision_mask(uint32_t p_mask);
+ uint32_t get_collision_mask() const;
+
+ void set_collision_mask_bit(int p_bit, bool p_value);
+ bool get_collision_mask_bit(int p_bit) const;
+
+ void add_exception_rid(const RID &p_rid);
+ void add_exception(const Object *p_object);
+ void remove_exception_rid(const RID &p_rid);
+ void remove_exception(const Object *p_object);
+ void clear_exceptions();
+
+ ClippedCamera();
+ ~ClippedCamera();
+};
+
+VARIANT_ENUM_CAST(ClippedCamera::ProcessMode);
#endif
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/3d/ray_cast.cpp b/scene/3d/ray_cast.cpp
index 7f83e2c3ea..b846a5b6c0 100644
--- a/scene/3d/ray_cast.cpp
+++ b/scene/3d/ray_cast.cpp
@@ -208,7 +208,7 @@ void RayCast::_update_raycast_state() {
PhysicsDirectSpaceState::RayResult rr;
- if (dss->intersect_ray(gt.get_origin(), gt.xform(to), rr, exclude, collision_mask)) {
+ if (dss->intersect_ray(gt.get_origin(), gt.xform(to), rr, exclude, collision_mask, collide_with_bodies, collide_with_areas)) {
collided = true;
against = rr.collider_id;
@@ -259,6 +259,26 @@ void RayCast::clear_exceptions() {
exclude.clear();
}
+void RayCast::set_collide_with_areas(bool p_clip) {
+
+ collide_with_areas = p_clip;
+}
+
+bool RayCast::is_collide_with_areas_enabled() const {
+
+ return collide_with_areas;
+}
+
+void RayCast::set_collide_with_bodies(bool p_clip) {
+
+ collide_with_bodies = p_clip;
+}
+
+bool RayCast::is_collide_with_bodies_enabled() const {
+
+ return collide_with_bodies;
+}
+
void RayCast::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &RayCast::set_enabled);
@@ -292,10 +312,20 @@ void RayCast::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_exclude_parent_body", "mask"), &RayCast::set_exclude_parent_body);
ClassDB::bind_method(D_METHOD("get_exclude_parent_body"), &RayCast::get_exclude_parent_body);
+ ClassDB::bind_method(D_METHOD("set_collide_with_areas", "enable"), &RayCast::set_collide_with_areas);
+ ClassDB::bind_method(D_METHOD("is_collide_with_areas_enabled"), &RayCast::is_collide_with_areas_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_collide_with_bodies", "enable"), &RayCast::set_collide_with_bodies);
+ ClassDB::bind_method(D_METHOD("is_collide_with_bodies_enabled"), &RayCast::is_collide_with_bodies_enabled);
+
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "exclude_parent"), "set_exclude_parent_body", "get_exclude_parent_body");
ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "cast_to"), "set_cast_to", "get_cast_to");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
+
+ ADD_GROUP("Collide With", "collide_with");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_areas", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_areas", "is_collide_with_areas_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_bodies", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_bodies", "is_collide_with_bodies_enabled");
}
void RayCast::_create_debug_shape() {
@@ -370,4 +400,6 @@ RayCast::RayCast() {
cast_to = Vector3(0, -1, 0);
debug_shape = NULL;
exclude_parent_body = true;
+ collide_with_areas = false;
+ collide_with_bodies = true;
}
diff --git a/scene/3d/ray_cast.h b/scene/3d/ray_cast.h
index 20cea80700..e95382e1fe 100644
--- a/scene/3d/ray_cast.h
+++ b/scene/3d/ray_cast.h
@@ -45,7 +45,6 @@ class RayCast : public Spatial {
Vector3 collision_normal;
Vector3 cast_to;
-
Set<RID> exclude;
uint32_t collision_mask;
@@ -58,12 +57,21 @@ class RayCast : public Spatial {
void _update_debug_shape();
void _clear_debug_shape();
+ bool collide_with_areas;
+ bool collide_with_bodies;
+
protected:
void _notification(int p_what);
void _update_raycast_state();
static void _bind_methods();
public:
+ void set_collide_with_areas(bool p_clip);
+ bool is_collide_with_areas_enabled() const;
+
+ void set_collide_with_bodies(bool p_clip);
+ bool is_collide_with_bodies_enabled() const;
+
void set_enabled(bool p_enabled);
bool is_enabled() const;
diff --git a/scene/3d/spring_arm.cpp b/scene/3d/spring_arm.cpp
new file mode 100644
index 0000000000..492c6b806e
--- /dev/null
+++ b/scene/3d/spring_arm.cpp
@@ -0,0 +1,172 @@
+/*************************************************************************/
+/* spring_arm.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 "spring_arm.h"
+#include "engine.h"
+#include "scene/3d/collision_object.h"
+#include "scene/resources/sphere_shape.h"
+#include "servers/physics_server.h"
+
+SpringArm::SpringArm() :
+ spring_length(1),
+ mask(1),
+ current_spring_length(0),
+ margin(0.01) {}
+
+void SpringArm::_notification(int p_what) {
+ switch (p_what) {
+ case NOTIFICATION_ENTER_TREE:
+ if (!Engine::get_singleton()->is_editor_hint()) {
+ set_process_internal(true);
+ }
+ break;
+ case NOTIFICATION_EXIT_TREE:
+ if (!Engine::get_singleton()->is_editor_hint()) {
+ set_process_internal(false);
+ }
+ break;
+ case NOTIFICATION_INTERNAL_PROCESS:
+ process_spring();
+ break;
+ }
+}
+
+void SpringArm::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("get_hit_length"), &SpringArm::get_hit_length);
+
+ ClassDB::bind_method(D_METHOD("set_length", "length"), &SpringArm::set_length);
+ ClassDB::bind_method(D_METHOD("get_length"), &SpringArm::get_length);
+
+ ClassDB::bind_method(D_METHOD("set_shape", "shape"), &SpringArm::set_shape);
+ ClassDB::bind_method(D_METHOD("get_shape"), &SpringArm::get_shape);
+
+ ClassDB::bind_method(D_METHOD("add_excluded_object", "RID"), &SpringArm::add_excluded_object);
+ ClassDB::bind_method(D_METHOD("remove_excluded_object", "RID"), &SpringArm::remove_excluded_object);
+ ClassDB::bind_method(D_METHOD("clear_excluded_objects"), &SpringArm::clear_excluded_objects);
+
+ ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &SpringArm::set_mask);
+ ClassDB::bind_method(D_METHOD("get_collision_mask"), &SpringArm::get_mask);
+
+ ClassDB::bind_method(D_METHOD("set_margin", "margin"), &SpringArm::set_margin);
+ ClassDB::bind_method(D_METHOD("get_margin"), &SpringArm::get_margin);
+
+ ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
+ ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape"), "set_shape", "get_shape");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "spring_length"), "set_length", "get_length");
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "margin"), "set_margin", "get_margin");
+}
+
+float SpringArm::get_length() const {
+ return spring_length;
+}
+
+void SpringArm::set_length(float p_length) {
+ if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_collisions_hint()))
+ update_gizmo();
+
+ spring_length = p_length;
+}
+
+void SpringArm::set_shape(Ref<Shape> p_shape) {
+ shape = p_shape;
+}
+
+Ref<Shape> SpringArm::get_shape() const {
+ return shape;
+}
+
+void SpringArm::set_mask(uint32_t p_mask) {
+ mask = p_mask;
+}
+
+uint32_t SpringArm::get_mask() {
+ return mask;
+}
+
+float SpringArm::get_margin() {
+ return margin;
+}
+
+void SpringArm::set_margin(float p_margin) {
+ margin = p_margin;
+}
+
+void SpringArm::add_excluded_object(RID p_rid) {
+ excluded_objects.insert(p_rid);
+}
+
+bool SpringArm::remove_excluded_object(RID p_rid) {
+ return excluded_objects.erase(p_rid);
+}
+
+void SpringArm::clear_excluded_objects() {
+ excluded_objects.clear();
+}
+
+float SpringArm::get_hit_length() {
+ return current_spring_length;
+}
+
+void SpringArm::process_spring() {
+ // From
+ real_t motion_delta(1);
+ real_t motion_delta_unsafe(1);
+
+ Vector3 motion;
+ const Vector3 cast_direction(get_global_transform().basis.xform(Vector3(0, 0, 1)));
+
+ if (shape.is_null()) {
+ motion = Vector3(cast_direction * (spring_length));
+ PhysicsDirectSpaceState::RayResult r;
+ bool intersected = get_world()->get_direct_space_state()->intersect_ray(get_global_transform().origin, get_global_transform().origin + motion, r, excluded_objects, mask);
+ if (intersected) {
+ float dist = get_global_transform().origin.distance_to(r.position);
+ dist -= margin;
+ motion_delta = dist / (spring_length);
+ }
+ } else {
+ motion = Vector3(cast_direction * spring_length);
+ get_world()->get_direct_space_state()->cast_motion(shape->get_rid(), get_global_transform(), motion, 0, motion_delta, motion_delta_unsafe, excluded_objects, mask);
+ }
+
+ current_spring_length = spring_length * motion_delta;
+ Transform childs_transform;
+ childs_transform.origin = get_global_transform().origin + cast_direction * (spring_length * motion_delta);
+
+ for (int i = get_child_count() - 1; 0 <= i; --i) {
+
+ Spatial *child = Object::cast_to<Spatial>(get_child(i));
+ if (child) {
+ childs_transform.basis = child->get_global_transform().basis;
+ child->set_global_transform(childs_transform);
+ }
+ }
+}
diff --git a/scene/3d/spring_arm.h b/scene/3d/spring_arm.h
new file mode 100644
index 0000000000..24d912d371
--- /dev/null
+++ b/scene/3d/spring_arm.h
@@ -0,0 +1,71 @@
+/*************************************************************************/
+/* spring_arm.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 SPRING_ARM_H
+#define SPRING_ARM_H
+
+#include "scene/3d/spatial.h"
+
+class SpringArm : public Spatial {
+ GDCLASS(SpringArm, Spatial);
+
+ Ref<Shape> shape;
+ Set<RID> excluded_objects;
+ float spring_length;
+ bool keep_child_basis;
+ float current_spring_length;
+ uint32_t mask;
+ float margin;
+
+protected:
+ void _notification(int p_what);
+ static void _bind_methods();
+
+public:
+ void set_length(float p_length);
+ float get_length() const;
+ void set_shape(Ref<Shape> p_shape);
+ Ref<Shape> get_shape() const;
+ void set_mask(uint32_t p_mask);
+ uint32_t get_mask();
+ void add_excluded_object(RID p_rid);
+ bool remove_excluded_object(RID p_rid);
+ void clear_excluded_objects();
+ float get_hit_length();
+ void set_margin(float p_margin);
+ float get_margin();
+
+ SpringArm();
+
+private:
+ void process_spring();
+};
+
+#endif
diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp
index 1bc9fa4b12..289cf7a3a7 100644
--- a/scene/animation/animation_blend_space_1d.cpp
+++ b/scene/animation/animation_blend_space_1d.cpp
@@ -1,12 +1,14 @@
#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 +22,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 +44,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 +88,10 @@ 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 +105,13 @@ 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 +127,14 @@ 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,14 +174,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;
}
@@ -193,9 +198,11 @@ float AnimationNodeBlendSpace1D::process(float p_time, bool p_seek) {
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 +269,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 +283,18 @@ 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..f4e20f0d70 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,19 @@ 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 +60,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..3dc7f2a86f 100644
--- a/scene/animation/animation_blend_space_2d.cpp
+++ b/scene/animation/animation_blend_space_2d.cpp
@@ -1,18 +1,25 @@
#include "animation_blend_space_2d.h"
#include "math/delaunay.h"
-void AnimationNodeBlendSpace2D::set_tree(AnimationTree *p_player) {
- AnimationRootNode::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++) {
- blend_points[i].node->set_tree(p_player);
+ 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 +39,13 @@ 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 +60,12 @@ 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 +78,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 +101,7 @@ 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 +224,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 +381,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 +445,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 +457,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;
@@ -487,6 +489,14 @@ 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 +521,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 +535,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 +549,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 +566,8 @@ 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..3e1c66c924 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,17 @@ 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 +78,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 +92,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..66a9c5babd 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,27 @@ 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;
+
+ active = "active";
+ prev_active = "prev_active";
+ time = "time";
+ remaining = "remaining";
}
////////////////////////////////////////////////
-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 +350,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 +359,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 +402,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 +412,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 +465,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 +502,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 +512,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 +540,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 +549,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 +570,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 +584,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 +664,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 +677,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 +689,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 +725,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 +750,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 +787,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 +800,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 +830,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 +935,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 +962,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 +981,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 +1012,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 +1035,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 +1055,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 +1076,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 +1099,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 +1126,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 +1134,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 +1161,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 +1169,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 +1177,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 +1197,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 +1220,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..c28e918a16 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,16 +890,34 @@ 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("has_transition", "from", "to"), &AnimationNodeStateMachine::add_transition);
+ 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::has_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);
ClassDB::bind_method(D_METHOD("get_transition_from", "idx"), &AnimationNodeStateMachine::get_transition_from);
@@ -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..1513010a8a 100644
--- a/scene/animation/animation_tree.cpp
+++ b/scene/animation/animation_tree.cpp
@@ -5,6 +5,34 @@
#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 +42,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 +65,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 +94,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 +219,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,29 +243,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 {
if (get_script_instance()) {
@@ -239,8 +258,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 +275,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,22 +308,6 @@ 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 {
Array paths;
@@ -361,12 +333,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 +351,15 @@ 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 +367,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 +382,6 @@ AnimationNode::AnimationNode() {
state = NULL;
parent = NULL;
- player = NULL;
- set_local_to_scene(true);
filter_enabled = false;
}
@@ -420,18 +390,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 +668,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 +713,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 +730,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 +1270,116 @@ 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 +1398,16 @@ 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 +1426,8 @@ 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/gradient_edit.cpp b/scene/gui/gradient_edit.cpp
index 749efe8364..e82c0c4ad1 100644
--- a/scene/gui/gradient_edit.cpp
+++ b/scene/gui/gradient_edit.cpp
@@ -147,7 +147,6 @@ void GradientEdit::_gui_input(const Ref<InputEvent> &p_event) {
grabbed = _get_point_from_pos(x);
//grab or select
if (grabbed != -1) {
- grabbed = false;
return;
}
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/main/canvas_layer.cpp b/scene/main/canvas_layer.cpp
index a2e890e7a7..c044443b51 100644
--- a/scene/main/canvas_layer.cpp
+++ b/scene/main/canvas_layer.cpp
@@ -29,6 +29,7 @@
/*************************************************************************/
#include "canvas_layer.h"
+#include "scene/2d/canvas_item.h"
#include "viewport.h"
void CanvasLayer::set_layer(int p_xform) {
@@ -62,6 +63,24 @@ void CanvasLayer::_update_xform() {
transform.set_origin(ofs);
if (viewport.is_valid())
VisualServer::get_singleton()->viewport_set_canvas_transform(viewport, canvas, transform);
+
+ if (!is_inside_tree())
+ return;
+
+ _notify_xform(this);
+}
+
+void CanvasLayer::_notify_xform(Node *p_node) {
+
+ for (int i = 0; i < p_node->get_child_count(); i++) {
+
+ CanvasItem *ci = Object::cast_to<CanvasItem>(p_node->get_child(i));
+ if (ci) {
+ ci->_notify_transform(ci);
+ } else {
+ _notify_xform(p_node->get_child(i));
+ }
+ }
}
void CanvasLayer::_update_locrotscale() {
diff --git a/scene/main/canvas_layer.h b/scene/main/canvas_layer.h
index aae23fbb12..fd347c4739 100644
--- a/scene/main/canvas_layer.h
+++ b/scene/main/canvas_layer.h
@@ -56,6 +56,7 @@ class CanvasLayer : public Node {
int sort_index;
void _update_xform();
+ void _notify_xform(Node *p_node);
void _update_locrotscale();
protected:
diff --git a/scene/main/node.cpp b/scene/main/node.cpp
index d6a80bfb1a..f6905e7c2e 100644
--- a/scene/main/node.cpp
+++ b/scene/main/node.cpp
@@ -240,7 +240,7 @@ void Node::_propagate_enter_tree() {
void Node::_propagate_exit_tree() {
-//block while removing children
+ //block while removing children
#ifdef DEBUG_ENABLED
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index e43c2da02d..f92b6e7583 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -444,7 +444,7 @@ void Viewport::_notification(int p_what) {
Vector2 point = get_canvas_transform().affine_inverse().xform(pos);
Physics2DDirectSpaceState::ShapeResult res[64];
- int rc = ss2d->intersect_point(point, res, 64, Set<RID>(), 0xFFFFFFFF, true);
+ int rc = ss2d->intersect_point(point, res, 64, Set<RID>(), 0xFFFFFFFF, true, true, true);
for (int i = 0; i < rc; i++) {
if (res[i].collider_id && res[i].collider) {
@@ -527,7 +527,7 @@ void Viewport::_notification(int p_what) {
PhysicsDirectSpaceState *space = PhysicsServer::get_singleton()->space_get_direct_state(find_world()->get_space());
if (space) {
- bool col = space->intersect_ray(from, from + dir * 10000, result, Set<RID>(), 0xFFFFFFFF, true);
+ bool col = space->intersect_ray(from, from + dir * 10000, result, Set<RID>(), 0xFFFFFFFF, true, true, true);
ObjectID new_collider = 0;
if (col) {
@@ -563,7 +563,7 @@ void Viewport::_notification(int p_what) {
PhysicsDirectSpaceState *space = PhysicsServer::get_singleton()->space_get_direct_state(find_world()->get_space());
if (space) {
- bool col = space->intersect_ray(from, from + dir * 10000, result, Set<RID>(), 0xFFFFFFFF, true);
+ bool col = space->intersect_ray(from, from + dir * 10000, result, Set<RID>(), 0xFFFFFFFF, true, true, true);
ObjectID new_collider = 0;
if (col) {
CollisionObject *co = Object::cast_to<CollisionObject>(result.collider);
diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp
index 382bddfb4e..da4e2f991d 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"
@@ -201,6 +200,7 @@
#include "scene/3d/room_instance.h"
#include "scene/3d/skeleton.h"
#include "scene/3d/soft_body.h"
+#include "scene/3d/spring_arm.h"
#include "scene/3d/sprite_3d.h"
#include "scene/3d/vehicle_body.h"
#include "scene/3d/visibility_notifier.h"
@@ -366,17 +366,16 @@ 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>();
+ ClassDB::register_class<ClippedCamera>();
ClassDB::register_class<Listener>();
ClassDB::register_class<ARVRCamera>();
ClassDB::register_class<ARVRController>();
@@ -415,6 +414,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>();
@@ -435,9 +436,14 @@ void register_scene_types() {
ClassDB::register_class<RigidBody>();
ClassDB::register_class<KinematicCollision>();
ClassDB::register_class<KinematicBody>();
+ ClassDB::register_class<SpringArm>();
+
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/mesh.cpp b/scene/resources/mesh.cpp
index 4e6004709e..dcd87a2a61 100644
--- a/scene/resources/mesh.cpp
+++ b/scene/resources/mesh.cpp
@@ -1334,7 +1334,7 @@ void ArrayMesh::_bind_methods() {
ClassDB::set_method_flags(get_class_static(), _scs_create("center_geometry"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
ClassDB::bind_method(D_METHOD("regen_normalmaps"), &ArrayMesh::regen_normalmaps);
ClassDB::set_method_flags(get_class_static(), _scs_create("regen_normalmaps"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
- ClassDB::bind_method(D_METHOD("lightmap_unwrap"), &ArrayMesh::lightmap_unwrap);
+ ClassDB::bind_method(D_METHOD("lightmap_unwrap", "transform", "texel_size"), &ArrayMesh::lightmap_unwrap);
ClassDB::set_method_flags(get_class_static(), _scs_create("lightmap_unwrap"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR);
ClassDB::bind_method(D_METHOD("get_faces"), &ArrayMesh::get_faces);
ClassDB::bind_method(D_METHOD("generate_triangle_mesh"), &ArrayMesh::generate_triangle_mesh);
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/resources/shape.cpp b/scene/resources/shape.cpp
index 418d8ce819..a48ce0564b 100644
--- a/scene/resources/shape.cpp
+++ b/scene/resources/shape.cpp
@@ -50,6 +50,15 @@ void Shape::add_vertices_to_array(PoolVector<Vector3> &array, const Transform &p
}
}
+real_t Shape::get_margin() const {
+ return margin;
+}
+
+void Shape::set_margin(real_t p_margin) {
+ margin = p_margin;
+ PhysicsServer::get_singleton()->shape_set_margin(shape, margin);
+}
+
Ref<ArrayMesh> Shape::get_debug_mesh() {
if (debug_mesh_cache.is_valid())
@@ -87,12 +96,22 @@ Ref<ArrayMesh> Shape::get_debug_mesh() {
return debug_mesh_cache;
}
-Shape::Shape() {
+void Shape::_bind_methods() {
+
+ ClassDB::bind_method(D_METHOD("set_margin", "margin"), &Shape::set_margin);
+ ClassDB::bind_method(D_METHOD("get_margin"), &Shape::get_margin);
+
+ ADD_PROPERTY(PropertyInfo(Variant::REAL, "margin", PROPERTY_HINT_RANGE, "0.04,10,0.01"), "set_margin", "get_margin");
+}
+
+Shape::Shape() :
+ margin(0.04) {
ERR_PRINT("Constructor must not be called!");
}
-Shape::Shape(RID p_shape) {
+Shape::Shape(RID p_shape) :
+ margin(0.04) {
shape = p_shape;
}
diff --git a/scene/resources/shape.h b/scene/resources/shape.h
index ad87a69679..0c44b86e92 100644
--- a/scene/resources/shape.h
+++ b/scene/resources/shape.h
@@ -40,10 +40,13 @@ class Shape : public Resource {
OBJ_SAVE_TYPE(Shape);
RES_BASE_EXTENSION("shape");
RID shape;
+ real_t margin;
Ref<ArrayMesh> debug_mesh_cache;
protected:
+ static void _bind_methods();
+
_FORCE_INLINE_ RID get_shape() const { return shape; }
Shape(RID p_shape);
@@ -55,6 +58,9 @@ public:
void add_vertices_to_array(PoolVector<Vector3> &array, const Transform &p_xform);
+ real_t get_margin() const;
+ void set_margin(real_t p_margin);
+
Shape();
~Shape();
};
diff --git a/scene/resources/texture.cpp b/scene/resources/texture.cpp
index 96edb17eea..d8efbeba17 100644
--- a/scene/resources/texture.cpp
+++ b/scene/resources/texture.cpp
@@ -1990,7 +1990,7 @@ void TextureLayered::_bind_methods() {
ClassDB::bind_method(D_METHOD("create", "width", "height", "depth", "format", "flags"), &TextureLayered::create, DEFVAL(FLAGS_DEFAULT));
ClassDB::bind_method(D_METHOD("set_layer_data", "image", "layer"), &TextureLayered::set_layer_data);
- ClassDB::bind_method(D_METHOD("get_layer_data", "layer"), &TextureLayered::set_layer_data);
+ ClassDB::bind_method(D_METHOD("get_layer_data", "layer"), &TextureLayered::get_layer_data);
ClassDB::bind_method(D_METHOD("set_data_partial", "image", "x_offset", "y_offset", "layer", "mipmap"), &TextureLayered::set_data_partial, DEFVAL(0));
ClassDB::bind_method(D_METHOD("_set_data", "data"), &TextureLayered::_set_data);
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/audio/effects/audio_effect_record.cpp b/servers/audio/effects/audio_effect_record.cpp
index 74a6838d1a..78ba658ed8 100644
--- a/servers/audio/effects/audio_effect_record.cpp
+++ b/servers/audio/effects/audio_effect_record.cpp
@@ -233,7 +233,7 @@ Ref<AudioStreamSample> AudioEffectRecord::get_recording() const {
w[i * 2 + 1] = rr[i];
}
} else {
- ERR_EXPLAIN("format not implemented");
+ ERR_PRINT("Format not implemented.");
}
Ref<AudioStreamSample> sample;
diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp
index f3934e5c01..b737f4681d 100644
--- a/servers/audio_server.cpp
+++ b/servers/audio_server.cpp
@@ -1281,11 +1281,11 @@ void AudioServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_mix_rate"), &AudioServer::get_mix_rate);
ClassDB::bind_method(D_METHOD("get_device_list"), &AudioServer::get_device_list);
ClassDB::bind_method(D_METHOD("get_device"), &AudioServer::get_device);
- ClassDB::bind_method(D_METHOD("set_device"), &AudioServer::set_device);
+ ClassDB::bind_method(D_METHOD("set_device", "device"), &AudioServer::set_device);
ClassDB::bind_method(D_METHOD("capture_get_device_list"), &AudioServer::capture_get_device_list);
ClassDB::bind_method(D_METHOD("capture_get_device"), &AudioServer::capture_get_device);
- ClassDB::bind_method(D_METHOD("capture_set_device"), &AudioServer::capture_set_device);
+ ClassDB::bind_method(D_METHOD("capture_set_device", "name"), &AudioServer::capture_set_device);
ClassDB::bind_method(D_METHOD("set_bus_layout", "bus_layout"), &AudioServer::set_bus_layout);
ClassDB::bind_method(D_METHOD("generate_bus_layout"), &AudioServer::generate_bus_layout);
diff --git a/servers/physics/physics_server_sw.cpp b/servers/physics/physics_server_sw.cpp
index 3a32c46a9b..472283833e 100644
--- a/servers/physics/physics_server_sw.cpp
+++ b/servers/physics/physics_server_sw.cpp
@@ -124,6 +124,13 @@ Variant PhysicsServerSW::shape_get_data(RID p_shape) const {
return shape->get_data();
};
+void PhysicsServerSW::shape_set_margin(RID p_shape, real_t p_margin) {
+}
+
+real_t PhysicsServerSW::shape_get_margin(RID p_shape) const {
+ return 0.0;
+}
+
real_t PhysicsServerSW::shape_get_custom_solver_bias(RID p_shape) const {
const ShapeSW *shape = shape_owner.get(p_shape);
@@ -292,6 +299,7 @@ void PhysicsServerSW::area_set_shape(RID p_area, int p_shape_idx, RID p_shape) {
area->set_shape(p_shape_idx, shape);
}
+
void PhysicsServerSW::area_set_shape_transform(RID p_area, int p_shape_idx, const Transform &p_transform) {
AreaSW *area = area_owner.get(p_area);
@@ -941,7 +949,19 @@ bool PhysicsServerSW::body_is_ray_pickable(RID p_body) const {
return body->is_ray_pickable();
}
-bool PhysicsServerSW::body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result) {
+bool PhysicsServerSW::body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result, bool p_exclude_raycast_shapes) {
+
+ BodySW *body = body_owner.get(p_body);
+ ERR_FAIL_COND_V(!body, false);
+ ERR_FAIL_COND_V(!body->get_space(), false);
+ ERR_FAIL_COND_V(body->get_space()->is_locked(), false);
+
+ _update_shapes();
+
+ return body->get_space()->test_body_motion(body, p_from, p_motion, p_infinite_inertia, body->get_kinematic_margin(), r_result, p_exclude_raycast_shapes);
+}
+
+int PhysicsServerSW::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) {
BodySW *body = body_owner.get(p_body);
ERR_FAIL_COND_V(!body, false);
@@ -950,7 +970,7 @@ bool PhysicsServerSW::body_test_motion(RID p_body, const Transform &p_from, cons
_update_shapes();
- return body->get_space()->test_body_motion(body, p_from, p_motion, p_infinite_inertia, body->get_kinematic_margin(), r_result);
+ return body->get_space()->test_body_ray_separation(body, p_transform, p_infinite_inertia, r_recover_motion, r_results, p_result_max, p_margin);
}
PhysicsDirectBodyState *PhysicsServerSW::body_get_direct_state(RID p_body) {
diff --git a/servers/physics/physics_server_sw.h b/servers/physics/physics_server_sw.h
index 1c5754124d..4131c5e248 100644
--- a/servers/physics/physics_server_sw.h
+++ b/servers/physics/physics_server_sw.h
@@ -85,6 +85,10 @@ public:
virtual ShapeType shape_get_type(RID p_shape) const;
virtual Variant shape_get_data(RID p_shape) const;
+
+ virtual void shape_set_margin(RID p_shape, real_t p_margin);
+ virtual real_t shape_get_margin(RID p_shape) const;
+
virtual real_t shape_get_custom_solver_bias(RID p_shape) const;
/* SPACE API */
@@ -230,7 +234,8 @@ public:
virtual void body_set_ray_pickable(RID p_body, bool p_enable);
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 bool body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result = NULL, bool p_exclude_raycast_shapes = true);
+ 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);
// 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/space_sw.cpp b/servers/physics/space_sw.cpp
index b604e5cdf6..cae2e6fb00 100644
--- a/servers/physics/space_sw.cpp
+++ b/servers/physics/space_sw.cpp
@@ -34,12 +34,22 @@
#include "physics_server_sw.h"
#include "project_settings.h"
-_FORCE_INLINE_ static bool _can_collide_with(CollisionObjectSW *p_object, uint32_t p_collision_mask) {
+_FORCE_INLINE_ static bool _can_collide_with(CollisionObjectSW *p_object, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
- return p_object->get_collision_layer() & p_collision_mask;
+ if (!(p_object->get_collision_layer() & p_collision_mask)) {
+ return false;
+ }
+
+ if (p_object->get_type() == CollisionObjectSW::TYPE_AREA && !p_collide_with_areas)
+ return false;
+
+ if (p_object->get_type() == CollisionObjectSW::TYPE_BODY && !p_collide_with_bodies)
+ return false;
+
+ return true;
}
-int PhysicsDirectSpaceStateSW::intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask) {
+int PhysicsDirectSpaceStateSW::intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
ERR_FAIL_COND_V(space->locked, false);
int amount = space->broadphase->cull_point(p_point, space->intersection_query_results, SpaceSW::INTERSECTION_QUERY_MAX, space->intersection_query_subindex_results);
@@ -52,7 +62,7 @@ int PhysicsDirectSpaceStateSW::intersect_point(const Vector3 &p_point, ShapeResu
if (cc >= p_result_max)
break;
- if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask))
+ if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask, p_collide_with_bodies, p_collide_with_areas))
continue;
//area can't be picked by ray (default)
@@ -83,7 +93,7 @@ int PhysicsDirectSpaceStateSW::intersect_point(const Vector3 &p_point, ShapeResu
return cc;
}
-bool PhysicsDirectSpaceStateSW::intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_pick_ray) {
+bool PhysicsDirectSpaceStateSW::intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas, bool p_pick_ray) {
ERR_FAIL_COND_V(space->locked, false);
@@ -105,7 +115,7 @@ bool PhysicsDirectSpaceStateSW::intersect_ray(const Vector3 &p_from, const Vecto
for (int i = 0; i < amount; i++) {
- if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask))
+ if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask, p_collide_with_bodies, p_collide_with_areas))
continue;
if (p_pick_ray && !(static_cast<CollisionObjectSW *>(space->intersection_query_results[i])->is_ray_pickable()))
@@ -161,7 +171,7 @@ bool PhysicsDirectSpaceStateSW::intersect_ray(const Vector3 &p_from, const Vecto
return true;
}
-int PhysicsDirectSpaceStateSW::intersect_shape(const RID &p_shape, const Transform &p_xform, real_t p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask) {
+int PhysicsDirectSpaceStateSW::intersect_shape(const RID &p_shape, const Transform &p_xform, real_t p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
if (p_result_max <= 0)
return 0;
@@ -182,7 +192,7 @@ int PhysicsDirectSpaceStateSW::intersect_shape(const RID &p_shape, const Transfo
if (cc >= p_result_max)
break;
- if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask))
+ if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask, p_collide_with_bodies, p_collide_with_areas))
continue;
//area can't be picked by ray (default)
@@ -212,7 +222,7 @@ int PhysicsDirectSpaceStateSW::intersect_shape(const RID &p_shape, const Transfo
return cc;
}
-bool PhysicsDirectSpaceStateSW::cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, real_t p_margin, real_t &p_closest_safe, real_t &p_closest_unsafe, const Set<RID> &p_exclude, uint32_t p_collision_mask, ShapeRestInfo *r_info) {
+bool PhysicsDirectSpaceStateSW::cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, real_t p_margin, real_t &p_closest_safe, real_t &p_closest_unsafe, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas, ShapeRestInfo *r_info) {
ShapeSW *shape = static_cast<PhysicsServerSW *>(PhysicsServer::get_singleton())->shape_owner.get(p_shape);
ERR_FAIL_COND_V(!shape, false);
@@ -242,7 +252,7 @@ bool PhysicsDirectSpaceStateSW::cast_motion(const RID &p_shape, const Transform
for (int i = 0; i < amount; i++) {
- if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask))
+ if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask, p_collide_with_bodies, p_collide_with_areas))
continue;
if (p_exclude.has(space->intersection_query_results[i]->get_self()))
@@ -326,7 +336,7 @@ bool PhysicsDirectSpaceStateSW::cast_motion(const RID &p_shape, const Transform
return true;
}
-bool PhysicsDirectSpaceStateSW::collide_shape(RID p_shape, const Transform &p_shape_xform, real_t p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude, uint32_t p_collision_mask) {
+bool PhysicsDirectSpaceStateSW::collide_shape(RID p_shape, const Transform &p_shape_xform, real_t p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
if (p_result_max <= 0)
return 0;
@@ -356,7 +366,7 @@ bool PhysicsDirectSpaceStateSW::collide_shape(RID p_shape, const Transform &p_sh
for (int i = 0; i < amount; i++) {
- if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask))
+ if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask, p_collide_with_bodies, p_collide_with_areas))
continue;
const CollisionObjectSW *col_obj = space->intersection_query_results[i];
@@ -405,7 +415,7 @@ static void _rest_cbk_result(const Vector3 &p_point_A, const Vector3 &p_point_B,
rd->best_object = rd->object;
rd->best_shape = rd->shape;
}
-bool PhysicsDirectSpaceStateSW::rest_info(RID p_shape, const Transform &p_shape_xform, real_t p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude, uint32_t p_collision_mask) {
+bool PhysicsDirectSpaceStateSW::rest_info(RID p_shape, const Transform &p_shape_xform, real_t p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
ShapeSW *shape = static_cast<PhysicsServerSW *>(PhysicsServer::get_singleton())->shape_owner.get(p_shape);
ERR_FAIL_COND_V(!shape, 0);
@@ -422,7 +432,7 @@ bool PhysicsDirectSpaceStateSW::rest_info(RID p_shape, const Transform &p_shape_
for (int i = 0; i < amount; i++) {
- if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask))
+ if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask, p_collide_with_bodies, p_collide_with_areas))
continue;
const CollisionObjectSW *col_obj = space->intersection_query_results[i];
@@ -541,7 +551,144 @@ int SpaceSW::_cull_aabb_for_body(BodySW *p_body, const AABB &p_aabb) {
return amount;
}
-bool SpaceSW::test_body_motion(BodySW *p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, real_t p_margin, PhysicsServer::MotionResult *r_result) {
+int SpaceSW::test_body_ray_separation(BodySW *p_body, const Transform &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, PhysicsServer::SeparationResult *r_results, int p_result_max, real_t p_margin) {
+
+ AABB body_aabb;
+
+ for (int i = 0; i < p_body->get_shape_count(); i++) {
+
+ if (i == 0)
+ body_aabb = p_body->get_shape_aabb(i);
+ else
+ body_aabb = body_aabb.merge(p_body->get_shape_aabb(i));
+ }
+
+ // Undo the currently transform the physics server is aware of and apply the provided one
+ body_aabb = p_transform.xform(p_body->get_inv_transform().xform(body_aabb));
+ body_aabb = body_aabb.grow(p_margin);
+
+ Transform body_transform = p_transform;
+
+ for (int i = 0; i < p_result_max; i++) {
+ //reset results
+ r_results[i].collision_depth = 0;
+ }
+
+ int rays_found = 0;
+
+ {
+ // raycast AND separate
+
+ const int max_results = 32;
+ int recover_attempts = 4;
+ Vector3 sr[max_results * 2];
+ PhysicsServerSW::CollCbkData cbk;
+ cbk.max = max_results;
+ PhysicsServerSW::CollCbkData *cbkptr = &cbk;
+ CollisionSolverSW::CallbackResult cbkres = PhysicsServerSW::_shape_col_cbk;
+
+ do {
+
+ Vector3 recover_motion;
+
+ bool collided = false;
+
+ int amount = _cull_aabb_for_body(p_body, body_aabb);
+ int ray_index = 0;
+
+ for (int j = 0; j < p_body->get_shape_count(); j++) {
+ if (p_body->is_shape_set_as_disabled(j))
+ continue;
+
+ ShapeSW *body_shape = p_body->get_shape(j);
+
+ if (body_shape->get_type() != PhysicsServer::SHAPE_RAY)
+ continue;
+
+ Transform body_shape_xform = body_transform * p_body->get_shape_transform(j);
+
+ for (int i = 0; i < amount; i++) {
+
+ const CollisionObjectSW *col_obj = intersection_query_results[i];
+ int shape_idx = intersection_query_subindex_results[i];
+
+ cbk.amount = 0;
+ cbk.ptr = sr;
+
+ if (CollisionObjectSW::TYPE_BODY == col_obj->get_type()) {
+ const BodySW *b = static_cast<const BodySW *>(col_obj);
+ if (p_infinite_inertia && PhysicsServer::BODY_MODE_STATIC != b->get_mode() && PhysicsServer::BODY_MODE_KINEMATIC != b->get_mode()) {
+ continue;
+ }
+ }
+
+ ShapeSW *against_shape = col_obj->get_shape(shape_idx);
+ if (CollisionSolverSW::solve_static(body_shape, body_shape_xform, against_shape, col_obj->get_transform() * col_obj->get_shape_transform(shape_idx), cbkres, cbkptr, NULL, p_margin)) {
+ if (cbk.amount > 0) {
+ collided = true;
+ }
+
+ if (ray_index < p_result_max) {
+ PhysicsServer::SeparationResult &result = r_results[ray_index];
+
+ for (int k = 0; k < cbk.amount; k++) {
+ Vector3 a = sr[k * 2 + 0];
+ Vector3 b = sr[k * 2 + 1];
+
+ recover_motion += (b - a) * 0.4;
+
+ float depth = a.distance_to(b);
+ if (depth > result.collision_depth) {
+
+ result.collision_depth = depth;
+ result.collision_point = b;
+ result.collision_normal = (b - a).normalized();
+ result.collision_local_shape = shape_idx;
+ result.collider = col_obj->get_self();
+ result.collider_id = col_obj->get_instance_id();
+ //result.collider_metadata = col_obj->get_shape_metadata(shape_idx);
+ if (col_obj->get_type() == CollisionObjectSW::TYPE_BODY) {
+ BodySW *body = (BodySW *)col_obj;
+
+ Vector3 rel_vec = b - body->get_transform().get_origin();
+ //result.collider_velocity = Vector3(-body->get_angular_velocity() * rel_vec.y, body->get_angular_velocity() * rel_vec.x) + body->get_linear_velocity();
+ result.collider_velocity = body->get_linear_velocity() + (body->get_angular_velocity()).cross(body->get_transform().origin - rel_vec); // * mPos);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ ray_index++;
+ }
+
+ rays_found = MAX(ray_index, rays_found);
+
+ if (!collided || recover_motion == Vector3()) {
+ break;
+ }
+
+ body_transform.origin += recover_motion;
+ body_aabb.position += recover_motion;
+
+ recover_attempts--;
+ } while (recover_attempts);
+ }
+
+ //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]);
+ }
+ }
+
+ r_recover_motion = body_transform.origin - p_transform.origin;
+ return rays_found;
+}
+
+bool SpaceSW::test_body_motion(BodySW *p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, real_t p_margin, PhysicsServer::MotionResult *r_result, bool p_exclude_raycast_shapes) {
//give me back regular physics engine logic
//this is madness
@@ -597,6 +744,10 @@ bool SpaceSW::test_body_motion(BodySW *p_body, const Transform &p_from, const Ve
Transform body_shape_xform = body_transform * p_body->get_shape_transform(j);
ShapeSW *body_shape = p_body->get_shape(j);
+ if (p_exclude_raycast_shapes && body_shape->get_type() == PhysicsServer::SHAPE_RAY) {
+ continue;
+ }
+
for (int i = 0; i < amount; i++) {
const CollisionObjectSW *col_obj = intersection_query_results[i];
@@ -655,6 +806,10 @@ bool SpaceSW::test_body_motion(BodySW *p_body, const Transform &p_from, const Ve
Transform body_shape_xform = body_transform * p_body->get_shape_transform(j);
ShapeSW *body_shape = p_body->get_shape(j);
+ if (p_exclude_raycast_shapes && body_shape->get_type() == PhysicsServer::SHAPE_RAY) {
+ continue;
+ }
+
Transform body_shape_xform_inv = body_shape_xform.affine_inverse();
MotionShapeSW mshape;
mshape.shape = body_shape;
diff --git a/servers/physics/space_sw.h b/servers/physics/space_sw.h
index 4d864e9a51..e7231df532 100644
--- a/servers/physics/space_sw.h
+++ b/servers/physics/space_sw.h
@@ -48,12 +48,12 @@ class PhysicsDirectSpaceStateSW : public PhysicsDirectSpaceState {
public:
SpaceSW *space;
- virtual int intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF);
- virtual bool intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_pick_ray = false);
- virtual int intersect_shape(const RID &p_shape, const Transform &p_xform, real_t p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF);
- virtual bool cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, real_t p_margin, real_t &p_closest_safe, real_t &p_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, ShapeRestInfo *r_info = NULL);
- virtual bool collide_shape(RID p_shape, const Transform &p_shape_xform, real_t p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF);
- virtual bool rest_info(RID p_shape, const Transform &p_shape_xform, real_t p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF);
+ virtual int intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
+ virtual bool intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_ray = false);
+ virtual int intersect_shape(const RID &p_shape, const Transform &p_xform, real_t p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
+ virtual bool cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, real_t p_margin, real_t &p_closest_safe, real_t &p_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, ShapeRestInfo *r_info = NULL);
+ virtual bool collide_shape(RID p_shape, const Transform &p_shape_xform, real_t p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
+ virtual bool rest_info(RID p_shape, const Transform &p_shape_xform, real_t p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
virtual Vector3 get_closest_point_to_object_volume(RID p_object, const Vector3 p_point) const;
PhysicsDirectSpaceStateSW();
@@ -197,7 +197,8 @@ public:
void set_elapsed_time(ElapsedTime p_time, uint64_t p_msec) { elapsed_time[p_time] = p_msec; }
uint64_t get_elapsed_time(ElapsedTime p_time) const { return elapsed_time[p_time]; }
- bool test_body_motion(BodySW *p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, real_t p_margin, PhysicsServer::MotionResult *r_result);
+ int test_body_ray_separation(BodySW *p_body, const Transform &p_transform, bool p_infinite_inertia, Vector3 &r_recover_motion, PhysicsServer::SeparationResult *r_results, int p_result_max, real_t p_margin);
+ bool test_body_motion(BodySW *p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, real_t p_margin, PhysicsServer::MotionResult *r_result, bool p_exclude_raycast_shapes);
SpaceSW();
~SpaceSW();
diff --git a/servers/physics_2d/space_2d_sw.cpp b/servers/physics_2d/space_2d_sw.cpp
index 6e45951f42..ecebd09436 100644
--- a/servers/physics_2d/space_2d_sw.cpp
+++ b/servers/physics_2d/space_2d_sw.cpp
@@ -34,12 +34,22 @@
#include "pair.h"
#include "physics_2d_server_sw.h"
-_FORCE_INLINE_ static bool _can_collide_with(CollisionObject2DSW *p_object, uint32_t p_collision_mask) {
+_FORCE_INLINE_ static bool _can_collide_with(CollisionObject2DSW *p_object, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
- return p_object->get_collision_layer() & p_collision_mask;
+ if (!(p_object->get_collision_layer() & p_collision_mask)) {
+ return false;
+ }
+
+ if (p_object->get_type() == CollisionObject2DSW::TYPE_AREA && !p_collide_with_areas)
+ return false;
+
+ if (p_object->get_type() == CollisionObject2DSW::TYPE_BODY && !p_collide_with_bodies)
+ return false;
+
+ return true;
}
-int Physics2DDirectSpaceStateSW::intersect_point(const Vector2 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_pick_point) {
+int Physics2DDirectSpaceStateSW::intersect_point(const Vector2 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas, bool p_pick_point) {
if (p_result_max <= 0)
return 0;
@@ -54,7 +64,7 @@ int Physics2DDirectSpaceStateSW::intersect_point(const Vector2 &p_point, ShapeRe
for (int i = 0; i < amount; i++) {
- if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask))
+ if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask, p_collide_with_bodies, p_collide_with_areas))
continue;
if (p_exclude.has(space->intersection_query_results[i]->get_self()))
@@ -90,7 +100,7 @@ int Physics2DDirectSpaceStateSW::intersect_point(const Vector2 &p_point, ShapeRe
return cc;
}
-bool Physics2DDirectSpaceStateSW::intersect_ray(const Vector2 &p_from, const Vector2 &p_to, RayResult &r_result, const Set<RID> &p_exclude, uint32_t p_collision_mask) {
+bool Physics2DDirectSpaceStateSW::intersect_ray(const Vector2 &p_from, const Vector2 &p_to, RayResult &r_result, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
ERR_FAIL_COND_V(space->locked, false);
@@ -112,7 +122,7 @@ bool Physics2DDirectSpaceStateSW::intersect_ray(const Vector2 &p_from, const Vec
for (int i = 0; i < amount; i++) {
- if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask))
+ if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask, p_collide_with_bodies, p_collide_with_areas))
continue;
if (p_exclude.has(space->intersection_query_results[i]->get_self()))
@@ -170,7 +180,7 @@ bool Physics2DDirectSpaceStateSW::intersect_ray(const Vector2 &p_from, const Vec
return true;
}
-int Physics2DDirectSpaceStateSW::intersect_shape(const RID &p_shape, const Transform2D &p_xform, const Vector2 &p_motion, real_t p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask) {
+int Physics2DDirectSpaceStateSW::intersect_shape(const RID &p_shape, const Transform2D &p_xform, const Vector2 &p_motion, real_t p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
if (p_result_max <= 0)
return 0;
@@ -190,7 +200,7 @@ int Physics2DDirectSpaceStateSW::intersect_shape(const RID &p_shape, const Trans
if (cc >= p_result_max)
break;
- if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask))
+ if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask, p_collide_with_bodies, p_collide_with_areas))
continue;
if (p_exclude.has(space->intersection_query_results[i]->get_self()))
@@ -215,7 +225,7 @@ int Physics2DDirectSpaceStateSW::intersect_shape(const RID &p_shape, const Trans
return cc;
}
-bool Physics2DDirectSpaceStateSW::cast_motion(const RID &p_shape, const Transform2D &p_xform, const Vector2 &p_motion, real_t p_margin, real_t &p_closest_safe, real_t &p_closest_unsafe, const Set<RID> &p_exclude, uint32_t p_collision_mask) {
+bool Physics2DDirectSpaceStateSW::cast_motion(const RID &p_shape, const Transform2D &p_xform, const Vector2 &p_motion, real_t p_margin, real_t &p_closest_safe, real_t &p_closest_unsafe, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
Shape2DSW *shape = Physics2DServerSW::singletonsw->shape_owner.get(p_shape);
ERR_FAIL_COND_V(!shape, false);
@@ -236,7 +246,7 @@ bool Physics2DDirectSpaceStateSW::cast_motion(const RID &p_shape, const Transfor
for (int i = 0; i < amount; i++) {
- if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask))
+ if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask, p_collide_with_bodies, p_collide_with_areas))
continue;
if (p_exclude.has(space->intersection_query_results[i]->get_self()))
@@ -299,7 +309,7 @@ bool Physics2DDirectSpaceStateSW::cast_motion(const RID &p_shape, const Transfor
return true;
}
-bool Physics2DDirectSpaceStateSW::collide_shape(RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, real_t p_margin, Vector2 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude, uint32_t p_collision_mask) {
+bool Physics2DDirectSpaceStateSW::collide_shape(RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, real_t p_margin, Vector2 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
if (p_result_max <= 0)
return 0;
@@ -330,7 +340,7 @@ bool Physics2DDirectSpaceStateSW::collide_shape(RID p_shape, const Transform2D &
for (int i = 0; i < amount; i++) {
- if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask))
+ if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask, p_collide_with_bodies, p_collide_with_areas))
continue;
const CollisionObject2DSW *col_obj = space->intersection_query_results[i];
@@ -388,7 +398,7 @@ static void _rest_cbk_result(const Vector2 &p_point_A, const Vector2 &p_point_B,
rd->best_shape = rd->shape;
}
-bool Physics2DDirectSpaceStateSW::rest_info(RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, real_t p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude, uint32_t p_collision_mask) {
+bool Physics2DDirectSpaceStateSW::rest_info(RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, real_t p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
Shape2DSW *shape = Physics2DServerSW::singletonsw->shape_owner.get(p_shape);
ERR_FAIL_COND_V(!shape, 0);
@@ -406,7 +416,7 @@ bool Physics2DDirectSpaceStateSW::rest_info(RID p_shape, const Transform2D &p_sh
for (int i = 0; i < amount; i++) {
- if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask))
+ if (!_can_collide_with(space->intersection_query_results[i], p_collision_mask, p_collide_with_bodies, p_collide_with_areas))
continue;
const CollisionObject2DSW *col_obj = space->intersection_query_results[i];
diff --git a/servers/physics_2d/space_2d_sw.h b/servers/physics_2d/space_2d_sw.h
index 1247317b03..6e2e025185 100644
--- a/servers/physics_2d/space_2d_sw.h
+++ b/servers/physics_2d/space_2d_sw.h
@@ -48,12 +48,12 @@ class Physics2DDirectSpaceStateSW : public Physics2DDirectSpaceState {
public:
Space2DSW *space;
- virtual int intersect_point(const Vector2 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_pick_point = false);
- virtual bool intersect_ray(const Vector2 &p_from, const Vector2 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF);
- virtual int intersect_shape(const RID &p_shape, const Transform2D &p_xform, const Vector2 &p_motion, real_t p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF);
- virtual bool cast_motion(const RID &p_shape, const Transform2D &p_xform, const Vector2 &p_motion, real_t p_margin, real_t &p_closest_safe, real_t &p_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF);
- virtual bool collide_shape(RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, real_t p_margin, Vector2 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF);
- virtual bool rest_info(RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, real_t p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF);
+ virtual int intersect_point(const Vector2 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_point = false);
+ virtual bool intersect_ray(const Vector2 &p_from, const Vector2 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
+ virtual int intersect_shape(const RID &p_shape, const Transform2D &p_xform, const Vector2 &p_motion, real_t p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
+ virtual bool cast_motion(const RID &p_shape, const Transform2D &p_xform, const Vector2 &p_motion, real_t p_margin, real_t &p_closest_safe, real_t &p_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
+ virtual bool collide_shape(RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, real_t p_margin, Vector2 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
+ virtual bool rest_info(RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, real_t p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
Physics2DDirectSpaceStateSW();
};
diff --git a/servers/physics_2d_server.cpp b/servers/physics_2d_server.cpp
index 37c4bc83ad..a51b938541 100644
--- a/servers/physics_2d_server.cpp
+++ b/servers/physics_2d_server.cpp
@@ -203,6 +203,22 @@ Vector<RID> Physics2DShapeQueryParameters::get_exclude() const {
return ret;
}
+void Physics2DShapeQueryParameters::set_collide_with_bodies(bool p_enable) {
+ collide_with_bodies = p_enable;
+}
+
+bool Physics2DShapeQueryParameters::is_collide_with_bodies_enabled() const {
+ return collide_with_bodies;
+}
+
+void Physics2DShapeQueryParameters::set_collide_with_areas(bool p_enable) {
+ collide_with_areas = p_enable;
+}
+
+bool Physics2DShapeQueryParameters::is_collide_with_areas_enabled() const {
+ return collide_with_areas;
+}
+
void Physics2DShapeQueryParameters::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_shape", "shape"), &Physics2DShapeQueryParameters::set_shape);
@@ -224,6 +240,12 @@ void Physics2DShapeQueryParameters::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_exclude", "exclude"), &Physics2DShapeQueryParameters::set_exclude);
ClassDB::bind_method(D_METHOD("get_exclude"), &Physics2DShapeQueryParameters::get_exclude);
+ ClassDB::bind_method(D_METHOD("set_collide_with_bodies", "enable"), &Physics2DShapeQueryParameters::set_collide_with_bodies);
+ ClassDB::bind_method(D_METHOD("is_collide_with_bodies_enabled"), &Physics2DShapeQueryParameters::is_collide_with_bodies_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_collide_with_areas", "enable"), &Physics2DShapeQueryParameters::set_collide_with_areas);
+ ClassDB::bind_method(D_METHOD("is_collide_with_areas_enabled"), &Physics2DShapeQueryParameters::is_collide_with_areas_enabled);
+
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_layer", "get_collision_layer");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "exclude", PROPERTY_HINT_NONE, itos(Variant::_RID) + ":"), "set_exclude", "get_exclude");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "margin", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_margin", "get_margin");
@@ -231,22 +253,26 @@ void Physics2DShapeQueryParameters::_bind_methods() {
//ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D"), "set_shape", ""); // FIXME: Lacks a getter
ADD_PROPERTY(PropertyInfo(Variant::_RID, "shape_rid"), "set_shape_rid", "get_shape_rid");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "transform"), "set_transform", "get_transform");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_bodies"), "set_collide_with_bodies", "is_collide_with_bodies_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_areas"), "set_collide_with_areas", "is_collide_with_areas_enabled");
}
Physics2DShapeQueryParameters::Physics2DShapeQueryParameters() {
margin = 0;
collision_mask = 0x7FFFFFFF;
+ collide_with_bodies = true;
+ collide_with_areas = false;
}
-Dictionary Physics2DDirectSpaceState::_intersect_ray(const Vector2 &p_from, const Vector2 &p_to, const Vector<RID> &p_exclude, uint32_t p_layers) {
+Dictionary Physics2DDirectSpaceState::_intersect_ray(const Vector2 &p_from, const Vector2 &p_to, const Vector<RID> &p_exclude, uint32_t p_layers, bool p_collide_with_bodies, bool p_collide_with_areas) {
RayResult inters;
Set<RID> exclude;
for (int i = 0; i < p_exclude.size(); i++)
exclude.insert(p_exclude[i]);
- bool res = intersect_ray(p_from, p_to, inters, exclude, p_layers);
+ bool res = intersect_ray(p_from, p_to, inters, exclude, p_layers, p_collide_with_bodies, p_collide_with_areas);
if (!res)
return Dictionary();
@@ -267,7 +293,7 @@ Array Physics2DDirectSpaceState::_intersect_shape(const Ref<Physics2DShapeQueryP
Vector<ShapeResult> sr;
sr.resize(p_max_results);
- int rc = intersect_shape(p_shape_query->shape, p_shape_query->transform, p_shape_query->motion, p_shape_query->margin, sr.ptrw(), sr.size(), p_shape_query->exclude, p_shape_query->collision_mask);
+ int rc = intersect_shape(p_shape_query->shape, p_shape_query->transform, p_shape_query->motion, p_shape_query->margin, sr.ptrw(), sr.size(), p_shape_query->exclude, p_shape_query->collision_mask, p_shape_query->collide_with_bodies, p_shape_query->collide_with_areas);
Array ret;
ret.resize(rc);
for (int i = 0; i < rc; i++) {
@@ -287,7 +313,7 @@ Array Physics2DDirectSpaceState::_intersect_shape(const Ref<Physics2DShapeQueryP
Array Physics2DDirectSpaceState::_cast_motion(const Ref<Physics2DShapeQueryParameters> &p_shape_query) {
float closest_safe, closest_unsafe;
- bool res = cast_motion(p_shape_query->shape, p_shape_query->transform, p_shape_query->motion, p_shape_query->margin, closest_safe, closest_unsafe, p_shape_query->exclude, p_shape_query->collision_mask);
+ bool res = cast_motion(p_shape_query->shape, p_shape_query->transform, p_shape_query->motion, p_shape_query->margin, closest_safe, closest_unsafe, p_shape_query->exclude, p_shape_query->collision_mask, p_shape_query->collide_with_bodies, p_shape_query->collide_with_areas);
if (!res)
return Array();
Array ret;
@@ -297,7 +323,7 @@ Array Physics2DDirectSpaceState::_cast_motion(const Ref<Physics2DShapeQueryParam
return ret;
}
-Array Physics2DDirectSpaceState::_intersect_point(const Vector2 &p_point, int p_max_results, const Vector<RID> &p_exclude, uint32_t p_layers) {
+Array Physics2DDirectSpaceState::_intersect_point(const Vector2 &p_point, int p_max_results, const Vector<RID> &p_exclude, uint32_t p_layers, bool p_collide_with_bodies, bool p_collide_with_areas) {
Set<RID> exclude;
for (int i = 0; i < p_exclude.size(); i++)
@@ -306,7 +332,7 @@ Array Physics2DDirectSpaceState::_intersect_point(const Vector2 &p_point, int p_
Vector<ShapeResult> ret;
ret.resize(p_max_results);
- int rc = intersect_point(p_point, ret.ptrw(), ret.size(), exclude, p_layers);
+ int rc = intersect_point(p_point, ret.ptrw(), ret.size(), exclude, p_layers, p_collide_with_bodies, p_collide_with_areas);
if (rc == 0)
return Array();
@@ -330,7 +356,7 @@ Array Physics2DDirectSpaceState::_collide_shape(const Ref<Physics2DShapeQueryPar
Vector<Vector2> ret;
ret.resize(p_max_results * 2);
int rc = 0;
- bool res = collide_shape(p_shape_query->shape, p_shape_query->transform, p_shape_query->motion, p_shape_query->margin, ret.ptrw(), p_max_results, rc, p_shape_query->exclude, p_shape_query->collision_mask);
+ bool res = collide_shape(p_shape_query->shape, p_shape_query->transform, p_shape_query->motion, p_shape_query->margin, ret.ptrw(), p_max_results, rc, p_shape_query->exclude, p_shape_query->collision_mask, p_shape_query->collide_with_bodies, p_shape_query->collide_with_areas);
if (!res)
return Array();
Array r;
@@ -343,7 +369,7 @@ Dictionary Physics2DDirectSpaceState::_get_rest_info(const Ref<Physics2DShapeQue
ShapeRestInfo sri;
- bool res = rest_info(p_shape_query->shape, p_shape_query->transform, p_shape_query->motion, p_shape_query->margin, &sri, p_shape_query->exclude, p_shape_query->collision_mask);
+ bool res = rest_info(p_shape_query->shape, p_shape_query->transform, p_shape_query->motion, p_shape_query->margin, &sri, p_shape_query->exclude, p_shape_query->collision_mask, p_shape_query->collide_with_bodies, p_shape_query->collide_with_areas);
Dictionary r;
if (!res)
return r;
@@ -364,8 +390,8 @@ Physics2DDirectSpaceState::Physics2DDirectSpaceState() {
void Physics2DDirectSpaceState::_bind_methods() {
- ClassDB::bind_method(D_METHOD("intersect_point", "point", "max_results", "exclude", "collision_layer"), &Physics2DDirectSpaceState::_intersect_point, DEFVAL(32), DEFVAL(Array()), DEFVAL(0x7FFFFFFF));
- ClassDB::bind_method(D_METHOD("intersect_ray", "from", "to", "exclude", "collision_layer"), &Physics2DDirectSpaceState::_intersect_ray, DEFVAL(Array()), DEFVAL(0x7FFFFFFF));
+ ClassDB::bind_method(D_METHOD("intersect_point", "point", "max_results", "exclude", "collision_layer", "collide_with_bodies", "collide_with_areas"), &Physics2DDirectSpaceState::_intersect_point, DEFVAL(32), DEFVAL(Array()), DEFVAL(0x7FFFFFFF), DEFVAL(true), DEFVAL(false));
+ ClassDB::bind_method(D_METHOD("intersect_ray", "from", "to", "exclude", "collision_layer", "collide_with_bodies", "collide_with_areas"), &Physics2DDirectSpaceState::_intersect_ray, DEFVAL(Array()), DEFVAL(0x7FFFFFFF), DEFVAL(true), DEFVAL(false));
ClassDB::bind_method(D_METHOD("intersect_shape", "shape", "max_results"), &Physics2DDirectSpaceState::_intersect_shape, DEFVAL(32));
ClassDB::bind_method(D_METHOD("cast_motion", "shape"), &Physics2DDirectSpaceState::_cast_motion);
ClassDB::bind_method(D_METHOD("collide_shape", "shape", "max_results"), &Physics2DDirectSpaceState::_collide_shape, DEFVAL(32));
diff --git a/servers/physics_2d_server.h b/servers/physics_2d_server.h
index f42d9868f0..82b4eb75d8 100644
--- a/servers/physics_2d_server.h
+++ b/servers/physics_2d_server.h
@@ -107,6 +107,9 @@ class Physics2DShapeQueryParameters : public Reference {
Set<RID> exclude;
uint32_t collision_mask;
+ bool collide_with_bodies;
+ bool collide_with_areas;
+
protected:
static void _bind_methods();
@@ -127,6 +130,12 @@ public:
void set_collision_mask(int p_collision_mask);
int get_collision_mask() const;
+ void set_collide_with_bodies(bool p_enable);
+ bool is_collide_with_bodies_enabled() const;
+
+ void set_collide_with_areas(bool p_enable);
+ bool is_collide_with_areas_enabled() const;
+
void set_exclude(const Vector<RID> &p_exclude);
Vector<RID> get_exclude() const;
@@ -137,9 +146,9 @@ class Physics2DDirectSpaceState : public Object {
GDCLASS(Physics2DDirectSpaceState, Object);
- Dictionary _intersect_ray(const Vector2 &p_from, const Vector2 &p_to, const Vector<RID> &p_exclude = Vector<RID>(), uint32_t p_layers = 0);
+ Dictionary _intersect_ray(const Vector2 &p_from, const Vector2 &p_to, const Vector<RID> &p_exclude = Vector<RID>(), uint32_t p_layers = 0, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
- Array _intersect_point(const Vector2 &p_point, int p_max_results = 32, const Vector<RID> &p_exclude = Vector<RID>(), uint32_t p_layers = 0);
+ Array _intersect_point(const Vector2 &p_point, int p_max_results = 32, const Vector<RID> &p_exclude = Vector<RID>(), uint32_t p_layers = 0, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
Array _intersect_shape(const Ref<Physics2DShapeQueryParameters> &p_shape_query, int p_max_results = 32);
Array _cast_motion(const Ref<Physics2DShapeQueryParameters> &p_shape_query);
Array _collide_shape(const Ref<Physics2DShapeQueryParameters> &p_shape_query, int p_max_results = 32);
@@ -160,7 +169,7 @@ public:
Variant metadata;
};
- virtual bool intersect_ray(const Vector2 &p_from, const Vector2 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF) = 0;
+ virtual bool intersect_ray(const Vector2 &p_from, const Vector2 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0;
struct ShapeResult {
@@ -171,13 +180,13 @@ public:
Variant metadata;
};
- virtual int intersect_point(const Vector2 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF, bool p_pick_point = false) = 0;
+ virtual int intersect_point(const Vector2 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_point = false) = 0;
- virtual int intersect_shape(const RID &p_shape, const Transform2D &p_xform, const Vector2 &p_motion, float p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF) = 0;
+ virtual int intersect_shape(const RID &p_shape, const Transform2D &p_xform, const Vector2 &p_motion, float p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0;
- virtual bool cast_motion(const RID &p_shape, const Transform2D &p_xform, const Vector2 &p_motion, float p_margin, float &p_closest_safe, float &p_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF) = 0;
+ virtual bool cast_motion(const RID &p_shape, const Transform2D &p_xform, const Vector2 &p_motion, float p_margin, float &p_closest_safe, float &p_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0;
- virtual bool collide_shape(RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, float p_margin, Vector2 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF) = 0;
+ virtual bool collide_shape(RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, float p_margin, Vector2 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0;
struct ShapeRestInfo {
@@ -190,7 +199,7 @@ public:
Variant metadata;
};
- virtual bool rest_info(RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, float p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF) = 0;
+ virtual bool rest_info(RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, float p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_layer = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0;
Physics2DDirectSpaceState();
};
diff --git a/servers/physics_server.cpp b/servers/physics_server.cpp
index cda3856ecc..deb3cd9bbe 100644
--- a/servers/physics_server.cpp
+++ b/servers/physics_server.cpp
@@ -29,7 +29,9 @@
/*************************************************************************/
#include "physics_server.h"
+
#include "core/project_settings.h"
+#include "method_bind_ext.gen.inc"
#include "print_string.h"
PhysicsServer *PhysicsServer::singleton = NULL;
@@ -198,6 +200,22 @@ Vector<RID> PhysicsShapeQueryParameters::get_exclude() const {
return ret;
}
+void PhysicsShapeQueryParameters::set_collide_with_bodies(bool p_enable) {
+ collide_with_bodies = p_enable;
+}
+
+bool PhysicsShapeQueryParameters::is_collide_with_bodies_enabled() const {
+ return collide_with_bodies;
+}
+
+void PhysicsShapeQueryParameters::set_collide_with_areas(bool p_enable) {
+ collide_with_areas = p_enable;
+}
+
+bool PhysicsShapeQueryParameters::is_collide_with_areas_enabled() const {
+ return collide_with_areas;
+}
+
void PhysicsShapeQueryParameters::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_shape", "shape"), &PhysicsShapeQueryParameters::set_shape);
@@ -216,18 +234,28 @@ void PhysicsShapeQueryParameters::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_exclude", "exclude"), &PhysicsShapeQueryParameters::set_exclude);
ClassDB::bind_method(D_METHOD("get_exclude"), &PhysicsShapeQueryParameters::get_exclude);
+ ClassDB::bind_method(D_METHOD("set_collide_with_bodies", "enable"), &PhysicsShapeQueryParameters::set_collide_with_bodies);
+ ClassDB::bind_method(D_METHOD("is_collide_with_bodies_enabled"), &PhysicsShapeQueryParameters::is_collide_with_bodies_enabled);
+
+ ClassDB::bind_method(D_METHOD("set_collide_with_areas", "enable"), &PhysicsShapeQueryParameters::set_collide_with_areas);
+ ClassDB::bind_method(D_METHOD("is_collide_with_areas_enabled"), &PhysicsShapeQueryParameters::is_collide_with_areas_enabled);
+
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
ADD_PROPERTY(PropertyInfo(Variant::ARRAY, "exclude", PROPERTY_HINT_NONE, itos(Variant::_RID) + ":"), "set_exclude", "get_exclude");
ADD_PROPERTY(PropertyInfo(Variant::REAL, "margin", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_margin", "get_margin");
//ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape2D"), "set_shape", ""); // FIXME: Lacks a getter
ADD_PROPERTY(PropertyInfo(Variant::_RID, "shape_rid"), "set_shape_rid", "get_shape_rid");
ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "transform"), "set_transform", "get_transform");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_bodies"), "set_collide_with_bodies", "is_collide_with_bodies_enabled");
+ ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_areas"), "set_collide_with_areas", "is_collide_with_areas_enabled");
}
PhysicsShapeQueryParameters::PhysicsShapeQueryParameters() {
margin = 0;
collision_mask = 0x7FFFFFFF;
+ collide_with_bodies = true;
+ collide_with_areas = false;
}
/////////////////////////////////////
@@ -262,14 +290,14 @@ Variant PhysicsDirectSpaceState::_intersect_shape(const RID& p_shape, const Tran
}
*/
-Dictionary PhysicsDirectSpaceState::_intersect_ray(const Vector3 &p_from, const Vector3 &p_to, const Vector<RID> &p_exclude, uint32_t p_collision_mask) {
+Dictionary PhysicsDirectSpaceState::_intersect_ray(const Vector3 &p_from, const Vector3 &p_to, const Vector<RID> &p_exclude, uint32_t p_collision_mask, bool p_collide_with_bodies, bool p_collide_with_areas) {
RayResult inters;
Set<RID> exclude;
for (int i = 0; i < p_exclude.size(); i++)
exclude.insert(p_exclude[i]);
- bool res = intersect_ray(p_from, p_to, inters, exclude, p_collision_mask);
+ bool res = intersect_ray(p_from, p_to, inters, exclude, p_collision_mask, p_collide_with_bodies, p_collide_with_areas);
if (!res)
return Dictionary();
@@ -289,7 +317,7 @@ Array PhysicsDirectSpaceState::_intersect_shape(const Ref<PhysicsShapeQueryParam
Vector<ShapeResult> sr;
sr.resize(p_max_results);
- int rc = intersect_shape(p_shape_query->shape, p_shape_query->transform, p_shape_query->margin, sr.ptrw(), sr.size(), p_shape_query->exclude, p_shape_query->collision_mask);
+ int rc = intersect_shape(p_shape_query->shape, p_shape_query->transform, p_shape_query->margin, sr.ptrw(), sr.size(), p_shape_query->exclude, p_shape_query->collision_mask, p_shape_query->collide_with_bodies, p_shape_query->collide_with_areas);
Array ret;
ret.resize(rc);
for (int i = 0; i < rc; i++) {
@@ -308,7 +336,7 @@ Array PhysicsDirectSpaceState::_intersect_shape(const Ref<PhysicsShapeQueryParam
Array PhysicsDirectSpaceState::_cast_motion(const Ref<PhysicsShapeQueryParameters> &p_shape_query, const Vector3 &p_motion) {
float closest_safe, closest_unsafe;
- bool res = cast_motion(p_shape_query->shape, p_shape_query->transform, p_motion, p_shape_query->margin, closest_safe, closest_unsafe, p_shape_query->exclude, p_shape_query->collision_mask);
+ bool res = cast_motion(p_shape_query->shape, p_shape_query->transform, p_motion, p_shape_query->margin, closest_safe, closest_unsafe, p_shape_query->exclude, p_shape_query->collision_mask, p_shape_query->collide_with_bodies, p_shape_query->collide_with_areas);
if (!res)
return Array();
Array ret;
@@ -322,7 +350,7 @@ Array PhysicsDirectSpaceState::_collide_shape(const Ref<PhysicsShapeQueryParamet
Vector<Vector3> ret;
ret.resize(p_max_results * 2);
int rc = 0;
- bool res = collide_shape(p_shape_query->shape, p_shape_query->transform, p_shape_query->margin, ret.ptrw(), p_max_results, rc, p_shape_query->exclude, p_shape_query->collision_mask);
+ bool res = collide_shape(p_shape_query->shape, p_shape_query->transform, p_shape_query->margin, ret.ptrw(), p_max_results, rc, p_shape_query->exclude, p_shape_query->collision_mask, p_shape_query->collide_with_bodies, p_shape_query->collide_with_areas);
if (!res)
return Array();
Array r;
@@ -335,7 +363,7 @@ Dictionary PhysicsDirectSpaceState::_get_rest_info(const Ref<PhysicsShapeQueryPa
ShapeRestInfo sri;
- bool res = rest_info(p_shape_query->shape, p_shape_query->transform, p_shape_query->margin, &sri, p_shape_query->exclude, p_shape_query->collision_mask);
+ bool res = rest_info(p_shape_query->shape, p_shape_query->transform, p_shape_query->margin, &sri, p_shape_query->exclude, p_shape_query->collision_mask, p_shape_query->collide_with_bodies, p_shape_query->collide_with_areas);
Dictionary r;
if (!res)
return r;
@@ -358,7 +386,7 @@ void PhysicsDirectSpaceState::_bind_methods() {
//ClassDB::bind_method(D_METHOD("intersect_ray","from","to","exclude","umask"),&PhysicsDirectSpaceState::_intersect_ray,DEFVAL(Array()),DEFVAL(0));
//ClassDB::bind_method(D_METHOD("intersect_shape","shape","xform","result_max","exclude","umask"),&PhysicsDirectSpaceState::_intersect_shape,DEFVAL(Array()),DEFVAL(0));
- ClassDB::bind_method(D_METHOD("intersect_ray", "from", "to", "exclude", "collision_layer"), &PhysicsDirectSpaceState::_intersect_ray, DEFVAL(Array()), DEFVAL(0x7FFFFFFF));
+ ClassDB::bind_method(D_METHOD("intersect_ray", "from", "to", "exclude", "collision_mask", "collide_with_bodies", "collide_with_areas"), &PhysicsDirectSpaceState::_intersect_ray, DEFVAL(Array()), DEFVAL(0x7FFFFFFF), DEFVAL(true), DEFVAL(false));
ClassDB::bind_method(D_METHOD("intersect_shape", "shape", "max_results"), &PhysicsDirectSpaceState::_intersect_shape, DEFVAL(32));
ClassDB::bind_method(D_METHOD("cast_motion", "shape", "motion"), &PhysicsDirectSpaceState::_cast_motion);
ClassDB::bind_method(D_METHOD("collide_shape", "shape", "max_results"), &PhysicsDirectSpaceState::_collide_shape, DEFVAL(32));
diff --git a/servers/physics_server.h b/servers/physics_server.h
index 948aec1a2d..f2aa33a6cc 100644
--- a/servers/physics_server.h
+++ b/servers/physics_server.h
@@ -108,6 +108,9 @@ class PhysicsShapeQueryParameters : public Reference {
Set<RID> exclude;
uint32_t collision_mask;
+ bool collide_with_bodies;
+ bool collide_with_areas;
+
protected:
static void _bind_methods();
@@ -128,6 +131,12 @@ public:
void set_exclude(const Vector<RID> &p_exclude);
Vector<RID> get_exclude() const;
+ void set_collide_with_bodies(bool p_enable);
+ bool is_collide_with_bodies_enabled() const;
+
+ void set_collide_with_areas(bool p_enable);
+ bool is_collide_with_areas_enabled() const;
+
PhysicsShapeQueryParameters();
};
@@ -136,7 +145,7 @@ class PhysicsDirectSpaceState : public Object {
GDCLASS(PhysicsDirectSpaceState, Object);
private:
- Dictionary _intersect_ray(const Vector3 &p_from, const Vector3 &p_to, const Vector<RID> &p_exclude = Vector<RID>(), uint32_t p_collision_mask = 0);
+ Dictionary _intersect_ray(const Vector3 &p_from, const Vector3 &p_to, const Vector<RID> &p_exclude = Vector<RID>(), uint32_t p_collision_mask = 0, bool p_collide_with_bodies = true, bool p_collide_with_areas = false);
Array _intersect_shape(const Ref<PhysicsShapeQueryParameters> &p_shape_query, int p_max_results = 32);
Array _cast_motion(const Ref<PhysicsShapeQueryParameters> &p_shape_query, const Vector3 &p_motion);
Array _collide_shape(const Ref<PhysicsShapeQueryParameters> &p_shape_query, int p_max_results = 32);
@@ -154,7 +163,7 @@ public:
int shape;
};
- virtual int intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF) = 0;
+ virtual int intersect_point(const Vector3 &p_point, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0;
struct RayResult {
@@ -166,9 +175,9 @@ public:
int shape;
};
- virtual bool intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_pick_ray = false) = 0;
+ virtual bool intersect_ray(const Vector3 &p_from, const Vector3 &p_to, RayResult &r_result, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, bool p_pick_ray = false) = 0;
- virtual int intersect_shape(const RID &p_shape, const Transform &p_xform, float p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF) = 0;
+ virtual int intersect_shape(const RID &p_shape, const Transform &p_xform, float p_margin, ShapeResult *r_results, int p_result_max, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0;
struct ShapeRestInfo {
@@ -180,11 +189,11 @@ public:
Vector3 linear_velocity; //velocity at contact point
};
- virtual bool cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, float p_margin, float &p_closest_safe, float &p_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, ShapeRestInfo *r_info = NULL) = 0;
+ virtual bool cast_motion(const RID &p_shape, const Transform &p_xform, const Vector3 &p_motion, float p_margin, float &p_closest_safe, float &p_closest_unsafe, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false, ShapeRestInfo *r_info = NULL) = 0;
- virtual bool collide_shape(RID p_shape, const Transform &p_shape_xform, float p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF) = 0;
+ virtual bool collide_shape(RID p_shape, const Transform &p_shape_xform, float p_margin, Vector3 *r_results, int p_result_max, int &r_result_count, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0;
- virtual bool rest_info(RID p_shape, const Transform &p_shape_xform, float p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF) = 0;
+ virtual bool rest_info(RID p_shape, const Transform &p_shape_xform, float p_margin, ShapeRestInfo *r_info, const Set<RID> &p_exclude = Set<RID>(), uint32_t p_collision_mask = 0xFFFFFFFF, bool p_collide_with_bodies = true, bool p_collide_with_areas = false) = 0;
virtual Vector3 get_closest_point_to_object_volume(RID p_object, const Vector3 p_point) const = 0;
@@ -243,6 +252,10 @@ public:
virtual ShapeType shape_get_type(RID p_shape) const = 0;
virtual Variant shape_get_data(RID p_shape) const = 0;
+
+ virtual void shape_set_margin(RID p_shape, real_t p_margin) = 0;
+ virtual real_t shape_get_margin(RID p_shape) const = 0;
+
virtual real_t shape_get_custom_solver_bias(RID p_shape) const = 0;
/* SPACE API */
@@ -482,7 +495,22 @@ public:
Variant collider_metadata;
};
- 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;
+ virtual bool body_test_motion(RID p_body, const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia, MotionResult *r_result = NULL, bool p_exclude_raycast_shapes = true) = 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 */
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);
diff --git a/servers/visual_server.cpp b/servers/visual_server.cpp
index bc9e9042ec..5537e4480c 100644
--- a/servers/visual_server.cpp
+++ b/servers/visual_server.cpp
@@ -1806,24 +1806,24 @@ void VisualServer::_bind_methods() {
ClassDB::bind_method(D_METHOD("gi_probe_get_bounds", "probe"), &VisualServer::gi_probe_get_bounds);
ClassDB::bind_method(D_METHOD("gi_probe_set_cell_size", "probe", "range"), &VisualServer::gi_probe_set_cell_size);
ClassDB::bind_method(D_METHOD("gi_probe_get_cell_size", "probe"), &VisualServer::gi_probe_get_cell_size);
- ClassDB::bind_method(D_METHOD("gi_probe_set_to_cell_xform", "xform"), &VisualServer::gi_probe_set_to_cell_xform);
- ClassDB::bind_method(D_METHOD("gi_probe_get_to_cell_xform"), &VisualServer::gi_probe_get_to_cell_xform);
- ClassDB::bind_method(D_METHOD("gi_probe_set_dynamic_data", "data"), &VisualServer::gi_probe_set_dynamic_data);
- ClassDB::bind_method(D_METHOD("gi_probe_get_dynamic_data"), &VisualServer::gi_probe_get_dynamic_data);
- ClassDB::bind_method(D_METHOD("gi_probe_set_dynamic_range", "range"), &VisualServer::gi_probe_set_dynamic_range);
- ClassDB::bind_method(D_METHOD("gi_probe_get_dynamic_range"), &VisualServer::gi_probe_get_dynamic_range);
- ClassDB::bind_method(D_METHOD("gi_probe_set_energy", "energy"), &VisualServer::gi_probe_set_energy);
- ClassDB::bind_method(D_METHOD("gi_probe_get_energy"), &VisualServer::gi_probe_get_energy);
- ClassDB::bind_method(D_METHOD("gi_probe_set_bias", "bias"), &VisualServer::gi_probe_set_bias);
- ClassDB::bind_method(D_METHOD("gi_probe_get_bias"), &VisualServer::gi_probe_get_bias);
- ClassDB::bind_method(D_METHOD("gi_probe_set_normal_bias", "bias"), &VisualServer::gi_probe_set_normal_bias);
- ClassDB::bind_method(D_METHOD("gi_probe_get_normal_bias"), &VisualServer::gi_probe_get_normal_bias);
- ClassDB::bind_method(D_METHOD("gi_probe_set_propagation", "propagation"), &VisualServer::gi_probe_set_propagation);
- ClassDB::bind_method(D_METHOD("gi_probe_get_propagation"), &VisualServer::gi_probe_get_propagation);
- ClassDB::bind_method(D_METHOD("gi_probe_set_interior", "enable"), &VisualServer::gi_probe_set_interior);
- ClassDB::bind_method(D_METHOD("gi_probe_is_interior"), &VisualServer::gi_probe_is_interior);
- ClassDB::bind_method(D_METHOD("gi_probe_set_compress", "enable"), &VisualServer::gi_probe_set_compress);
- ClassDB::bind_method(D_METHOD("gi_probe_is_compressed"), &VisualServer::gi_probe_is_compressed);
+ ClassDB::bind_method(D_METHOD("gi_probe_set_to_cell_xform", "probe", "xform"), &VisualServer::gi_probe_set_to_cell_xform);
+ ClassDB::bind_method(D_METHOD("gi_probe_get_to_cell_xform", "probe"), &VisualServer::gi_probe_get_to_cell_xform);
+ ClassDB::bind_method(D_METHOD("gi_probe_set_dynamic_data", "probe", "data"), &VisualServer::gi_probe_set_dynamic_data);
+ ClassDB::bind_method(D_METHOD("gi_probe_get_dynamic_data", "probe"), &VisualServer::gi_probe_get_dynamic_data);
+ ClassDB::bind_method(D_METHOD("gi_probe_set_dynamic_range", "probe", "range"), &VisualServer::gi_probe_set_dynamic_range);
+ ClassDB::bind_method(D_METHOD("gi_probe_get_dynamic_range", "probe"), &VisualServer::gi_probe_get_dynamic_range);
+ ClassDB::bind_method(D_METHOD("gi_probe_set_energy", "probe", "energy"), &VisualServer::gi_probe_set_energy);
+ ClassDB::bind_method(D_METHOD("gi_probe_get_energy", "probe"), &VisualServer::gi_probe_get_energy);
+ ClassDB::bind_method(D_METHOD("gi_probe_set_bias", "probe", "bias"), &VisualServer::gi_probe_set_bias);
+ ClassDB::bind_method(D_METHOD("gi_probe_get_bias", "probe"), &VisualServer::gi_probe_get_bias);
+ ClassDB::bind_method(D_METHOD("gi_probe_set_normal_bias", "probe", "bias"), &VisualServer::gi_probe_set_normal_bias);
+ ClassDB::bind_method(D_METHOD("gi_probe_get_normal_bias", "probe"), &VisualServer::gi_probe_get_normal_bias);
+ ClassDB::bind_method(D_METHOD("gi_probe_set_propagation", "probe", "propagation"), &VisualServer::gi_probe_set_propagation);
+ ClassDB::bind_method(D_METHOD("gi_probe_get_propagation", "probe"), &VisualServer::gi_probe_get_propagation);
+ ClassDB::bind_method(D_METHOD("gi_probe_set_interior", "probe", "enable"), &VisualServer::gi_probe_set_interior);
+ ClassDB::bind_method(D_METHOD("gi_probe_is_interior", "probe"), &VisualServer::gi_probe_is_interior);
+ ClassDB::bind_method(D_METHOD("gi_probe_set_compress", "probe", "enable"), &VisualServer::gi_probe_set_compress);
+ ClassDB::bind_method(D_METHOD("gi_probe_is_compressed", "probe"), &VisualServer::gi_probe_is_compressed);
ClassDB::bind_method(D_METHOD("lightmap_capture_create"), &VisualServer::lightmap_capture_create);
ClassDB::bind_method(D_METHOD("lightmap_capture_set_bounds", "capture", "bounds"), &VisualServer::lightmap_capture_set_bounds);