diff options
83 files changed, 1991 insertions, 714 deletions
diff --git a/core/math/vector2.cpp b/core/math/vector2.cpp index 54abc1b7f2..b53dc05a00 100644 --- a/core/math/vector2.cpp +++ b/core/math/vector2.cpp @@ -34,6 +34,10 @@ real_t Vector2::angle() const { return Math::atan2(y, x); } +Vector2 Vector2::from_angle(const real_t p_angle) { + return Vector2(Math::cos(p_angle), Math::sin(p_angle)); +} + real_t Vector2::length() const { return Math::sqrt(x * x + y * y); } diff --git a/core/math/vector2.h b/core/math/vector2.h index 330b4741b1..332c0475fa 100644 --- a/core/math/vector2.h +++ b/core/math/vector2.h @@ -147,6 +147,7 @@ struct Vector2 { bool operator>=(const Vector2 &p_vec2) const { return x == p_vec2.x ? (y >= p_vec2.y) : (x > p_vec2.x); } real_t angle() const; + static Vector2 from_angle(const real_t p_angle); _FORCE_INLINE_ Vector2 abs() const { return Vector2(Math::abs(x), Math::abs(y)); diff --git a/core/templates/vector.h b/core/templates/vector.h index 08cbef6ba4..033345d04c 100644 --- a/core/templates/vector.h +++ b/core/templates/vector.h @@ -229,7 +229,7 @@ public: _FORCE_INLINE_ bool operator==(const ConstIterator &b) const { return elem_ptr == b.elem_ptr; } _FORCE_INLINE_ bool operator!=(const ConstIterator &b) const { return elem_ptr != b.elem_ptr; } - ConstIterator(T *p_ptr) { elem_ptr = p_ptr; } + ConstIterator(const T *p_ptr) { elem_ptr = p_ptr; } ConstIterator() {} ConstIterator(const ConstIterator &p_it) { elem_ptr = p_it.elem_ptr; } diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index c9c7eca40d..39207df9e7 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1502,6 +1502,8 @@ static void _register_variant_builtin_methods() { bind_method(Vector2, clamp, sarray("min", "max"), varray()); bind_method(Vector2, snapped, sarray("step"), varray()); + bind_static_method(Vector2, from_angle, sarray("angle"), varray()); + /* Vector2i */ bind_method(Vector2i, aspect, sarray(), varray()); diff --git a/core/variant/variant_utility.cpp b/core/variant/variant_utility.cpp index f5cb2d40d6..232054d0ca 100644 --- a/core/variant/variant_utility.cpp +++ b/core/variant/variant_utility.cpp @@ -267,14 +267,6 @@ struct VariantUtilityFunctions { return Math::db2linear(db); } - static inline Vector2 polar2cartesian(double r, double th) { - return Vector2(r * Math::cos(th), r * Math::sin(th)); - } - - static inline Vector2 cartesian2polar(double x, double y) { - return Vector2(Math::sqrt(x * x + y * y), Math::atan2(y, x)); - } - static inline int64_t wrapi(int64_t value, int64_t min, int64_t max) { return Math::wrapi(value, min, max); } @@ -1214,9 +1206,6 @@ void Variant::_register_variant_utility_functions() { FUNCBINDR(linear2db, sarray("lin"), Variant::UTILITY_FUNC_TYPE_MATH); FUNCBINDR(db2linear, sarray("db"), Variant::UTILITY_FUNC_TYPE_MATH); - FUNCBINDR(polar2cartesian, sarray("r", "th"), Variant::UTILITY_FUNC_TYPE_MATH); - FUNCBINDR(cartesian2polar, sarray("x", "y"), Variant::UTILITY_FUNC_TYPE_MATH); - FUNCBINDR(wrapi, sarray("value", "min", "max"), Variant::UTILITY_FUNC_TYPE_MATH); FUNCBINDR(wrapf, sarray("value", "min", "max"), Variant::UTILITY_FUNC_TYPE_MATH); diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 585b94a522..616d88c7f3 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -99,14 +99,6 @@ [b]Warning:[/b] Deserialized object can contain code which gets executed. Do not use this option if the serialized object comes from untrusted sources to avoid potential security threats (remote code execution). </description> </method> - <method name="cartesian2polar"> - <return type="Vector2" /> - <argument index="0" name="x" type="float" /> - <argument index="1" name="y" type="float" /> - <description> - Converts a 2D point expressed in the cartesian coordinate system (X and Y axis) to the polar coordinate system (a distance from the origin and an angle). - </description> - </method> <method name="ceil"> <return type="float" /> <argument index="0" name="x" type="float" /> @@ -521,14 +513,6 @@ [b]Warning:[/b] Due to the way it is implemented, this function returns [code]0[/code] rather than [code]1[/code] for non-positive values of [code]value[/code] (in reality, 1 is the smallest integer power of 2). </description> </method> - <method name="polar2cartesian"> - <return type="Vector2" /> - <argument index="0" name="r" type="float" /> - <argument index="1" name="th" type="float" /> - <description> - Converts a 2D point expressed in the polar coordinate system (a distance from the origin [code]r[/code] and an angle [code]th[/code]) to the cartesian coordinate system (X and Y axis). - </description> - </method> <method name="posmod"> <return type="int" /> <argument index="0" name="x" type="int" /> @@ -2474,6 +2458,7 @@ <constant name="METHOD_FLAG_STATIC" value="256" enum="MethodFlags"> </constant> <constant name="METHOD_FLAG_OBJECT_CORE" value="512" enum="MethodFlags"> + Used internally. Allows to not dump core virtuals such as [code]_notification[/code] to the JSON API. </constant> <constant name="METHOD_FLAGS_DEFAULT" value="1" enum="MethodFlags"> Default method flags. diff --git a/doc/classes/CharacterBody2D.xml b/doc/classes/CharacterBody2D.xml index e5f60541b9..71e6eeab5a 100644 --- a/doc/classes/CharacterBody2D.xml +++ b/doc/classes/CharacterBody2D.xml @@ -152,8 +152,11 @@ <member name="motion_mode" type="int" setter="set_motion_mode" getter="get_motion_mode" enum="CharacterBody2D.MotionMode" default="0"> Sets the motion mode which defines the behaviour of [method move_and_slide]. See [enum MotionMode] constants for available modes. </member> - <member name="moving_platform_ignore_layers" type="int" setter="set_moving_platform_ignore_layers" getter="get_moving_platform_ignore_layers" default="0"> - Collision layers that will be excluded for detecting bodies that will act as moving platforms to be followed by the [CharacterBody2D]. By default, all touching bodies are detected and propagate their velocity. You can add excluded layers to ignore bodies that are contained in these layers. + <member name="moving_platform_floor_layers" type="int" setter="set_moving_platform_floor_layers" getter="get_moving_platform_floor_layers" default="4294967295"> + Collision layers that will be included for detecting floor bodies that will act as moving platforms to be followed by the [CharacterBody2D]. By default, all floor bodies are detected and propagate their velocity. + </member> + <member name="moving_platform_wall_layers" type="int" setter="set_moving_platform_wall_layers" getter="get_moving_platform_wall_layers" default="0"> + Collision layers that will be included for detecting wall bodies that will act as moving platforms to be followed by the [CharacterBody2D]. By default, all wall bodies are ignored. </member> <member name="slide_on_ceiling" type="bool" setter="set_slide_on_ceiling_enabled" getter="is_slide_on_ceiling_enabled" default="true"> If [code]true[/code], during a jump against the ceiling, the body will slide, if [code]false[/code] it will be stopped and will fall vertically. diff --git a/doc/classes/EditorPlugin.xml b/doc/classes/EditorPlugin.xml index e564e8045c..b8436be76a 100644 --- a/doc/classes/EditorPlugin.xml +++ b/doc/classes/EditorPlugin.xml @@ -56,11 +56,11 @@ Called by the engine when the 3D editor's viewport is updated. Use the [code]overlay[/code] [Control] for drawing. You can update the viewport manually by calling [method update_overlays]. [codeblocks] [gdscript] - func _forward_spatial_3d_over_viewport(overlay): + func _forward_3d_draw_over_viewport(overlay): # Draw a circle at cursor position. overlay.draw_circle(overlay.get_local_mouse_position(), 64) - func _forward_spatial_gui_input(camera, event): + func _forward_3d_gui_input(camera, event): if event is InputEventMouseMotion: # Redraw viewport when cursor is moved. update_overlays() @@ -68,13 +68,13 @@ return false [/gdscript] [csharp] - public override void ForwardSpatialDrawOverViewport(Godot.Control overlay) + public override void _Forward3dDrawOverViewport(Godot.Control overlay) { // Draw a circle at cursor position. overlay.DrawCircle(overlay.GetLocalMousePosition(), 64, Colors.White); } - public override bool ForwardSpatialGuiInput(Godot.Camera3D camera, InputEvent @event) + public override bool _Forward3dGuiInput(Godot.Camera3D camera, InputEvent @event) { if (@event is InputEventMouseMotion) { @@ -104,12 +104,12 @@ [codeblocks] [gdscript] # Prevents the InputEvent to reach other Editor classes. - func _forward_spatial_gui_input(camera, event): + func _forward_3d_gui_input(camera, event): return true [/gdscript] [csharp] // Prevents the InputEvent to reach other Editor classes. - public override bool ForwardSpatialGuiInput(Camera3D camera, InputEvent @event) + public override bool _Forward3dGuiInput(Camera3D camera, InputEvent @event) { return true; } @@ -119,12 +119,12 @@ [codeblocks] [gdscript] # Consumes InputEventMouseMotion and forwards other InputEvent types. - func _forward_spatial_gui_input(camera, event): + func _forward_3d_gui_input(camera, event): return event is InputEventMouseMotion [/gdscript] [csharp] // Consumes InputEventMouseMotion and forwards other InputEvent types. - public override bool ForwardSpatialGuiInput(Camera3D camera, InputEvent @event) + public override bool _Forward3dGuiInput(Camera3D camera, InputEvent @event) { return @event is InputEventMouseMotion; } diff --git a/doc/classes/PhysicsServer2D.xml b/doc/classes/PhysicsServer2D.xml index d0ae665139..9867b98ae6 100644 --- a/doc/classes/PhysicsServer2D.xml +++ b/doc/classes/PhysicsServer2D.xml @@ -489,6 +489,9 @@ <argument index="2" name="userdata" type="Variant" default="null" /> <description> Sets the function used to calculate physics for an object, if that object allows it (see [method body_set_omit_force_integration]). + The force integration function takes 2 arguments: + [code]state:[/code] [PhysicsDirectBodyState2D] used to retrieve and modify the body's state. + [code]userdata:[/code] Optional user data, if it was passed when calling [code]body_set_force_integration_callback[/code]. </description> </method> <method name="body_set_max_contacts_reported"> diff --git a/doc/classes/PhysicsServer3D.xml b/doc/classes/PhysicsServer3D.xml index c47a311161..46cbe48b28 100644 --- a/doc/classes/PhysicsServer3D.xml +++ b/doc/classes/PhysicsServer3D.xml @@ -478,6 +478,9 @@ <argument index="2" name="userdata" type="Variant" default="null" /> <description> Sets the function used to calculate physics for an object, if that object allows it (see [method body_set_omit_force_integration]). + The force integration function takes 2 arguments: + [code]state:[/code] [PhysicsDirectBodyState3D] used to retrieve and modify the body's state. + [code]userdata:[/code] Optional user data, if it was passed when calling [code]body_set_force_integration_callback[/code]. </description> </method> <method name="body_set_max_contacts_reported"> diff --git a/doc/classes/SoftBody3D.xml b/doc/classes/SoftBody3D.xml index ddfc14ceac..d5f0e3c95c 100644 --- a/doc/classes/SoftBody3D.xml +++ b/doc/classes/SoftBody3D.xml @@ -42,6 +42,20 @@ <description> </description> </method> + <method name="get_point_transform"> + <return type="Vector3" /> + <argument index="0" name="point_index" type="int" /> + <description> + Returns local translation of a vertex in the surface array. + </description> + </method> + <method name="is_point_pinned" qualifiers="const"> + <return type="bool" /> + <argument index="0" name="point_index" type="int" /> + <description> + Returns [code]true[/code] if vertex is set to pinned. + </description> + </method> <method name="remove_collision_exception_with"> <return type="void" /> <argument index="0" name="body" type="Node" /> @@ -65,6 +79,15 @@ Based on [code]value[/code], enables or disables the specified layer in the [member collision_mask], given a [code]layer_number[/code] between 1 and 32. </description> </method> + <method name="set_point_pinned"> + <return type="void" /> + <argument index="0" name="point_index" type="int" /> + <argument index="1" name="pinned" type="bool" /> + <argument index="2" name="attachment_path" type="NodePath" default="NodePath("")" /> + <description> + Sets the pinned state of a surface vertex. When set to [code]true[/code], the optional [code]attachment_path[/code] can define a [Node3D] the pinned vertex will be attached to. + </description> + </method> </methods> <members> <member name="collision_layer" type="int" setter="set_collision_layer" getter="get_collision_layer" default="1"> diff --git a/doc/classes/TextureProgressBar.xml b/doc/classes/TextureProgressBar.xml index 9da89ebe43..ee47557b39 100644 --- a/doc/classes/TextureProgressBar.xml +++ b/doc/classes/TextureProgressBar.xml @@ -60,6 +60,9 @@ [Texture2D] that clips based on the node's [code]value[/code] and [member fill_mode]. As [code]value[/code] increased, the texture fills up. It shows entirely when [code]value[/code] reaches [code]max_value[/code]. It doesn't show at all if [code]value[/code] is equal to [code]min_value[/code]. The [code]value[/code] property comes from [Range]. See [member Range.value], [member Range.min_value], [member Range.max_value]. </member> + <member name="texture_progress_offset" type="Vector2" setter="set_texture_progress_offset" getter="get_texture_progress_offset" default="Vector2(0, 0)"> + The offset of [member texture_progress]. Useful for [member texture_over] and [member texture_under] with fancy borders, to avoid transparent margins in your progress texture. + </member> <member name="texture_under" type="Texture2D" setter="set_under_texture" getter="get_under_texture"> [Texture2D] that draws under the progress bar. The bar's background. </member> diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml index cb5662419e..ab4d0e181a 100644 --- a/doc/classes/Vector2.xml +++ b/doc/classes/Vector2.xml @@ -155,6 +155,18 @@ Returns the vector with all components rounded down (towards negative infinity). </description> </method> + <method name="from_angle" qualifiers="static"> + <return type="Vector2" /> + <argument index="0" name="angle" type="float" /> + <description> + Creates a unit [Vector2] rotated to the given [code]angle[/code] in radians. This is equivalent to doing [code]Vector2(cos(angle), sin(angle))[/code] or [code]Vector2.RIGHT.rotated(angle)[/code]. + [codeblock] + print(Vector2.from_angle(0)) # Prints (1, 0). + print(Vector2(1, 0).angle()) # Prints 0, which is the angle used above. + print(Vector2.from_angle(PI / 2)) # Prints (0, 1). + [/codeblock] + </description> + </method> <method name="is_equal_approx" qualifiers="const"> <return type="bool" /> <argument index="0" name="to" type="Vector2" /> diff --git a/editor/debugger/debug_adapter/debug_adapter_parser.cpp b/editor/debugger/debug_adapter/debug_adapter_parser.cpp index 945291b163..fbd3aaa409 100644 --- a/editor/debugger/debug_adapter/debug_adapter_parser.cpp +++ b/editor/debugger/debug_adapter/debug_adapter_parser.cpp @@ -33,12 +33,15 @@ #include "editor/debugger/editor_debugger_node.h" #include "editor/debugger/script_editor_debugger.h" #include "editor/editor_node.h" +#include "editor/editor_run_native.h" void DebugAdapterParser::_bind_methods() { // Requests ClassDB::bind_method(D_METHOD("req_initialize", "params"), &DebugAdapterParser::req_initialize); - ClassDB::bind_method(D_METHOD("req_disconnect", "params"), &DebugAdapterParser::prepare_success_response); + ClassDB::bind_method(D_METHOD("req_disconnect", "params"), &DebugAdapterParser::req_disconnect); ClassDB::bind_method(D_METHOD("req_launch", "params"), &DebugAdapterParser::req_launch); + ClassDB::bind_method(D_METHOD("req_attach", "params"), &DebugAdapterParser::req_attach); + ClassDB::bind_method(D_METHOD("req_restart", "params"), &DebugAdapterParser::req_restart); ClassDB::bind_method(D_METHOD("req_terminate", "params"), &DebugAdapterParser::req_terminate); ClassDB::bind_method(D_METHOD("req_configurationDone", "params"), &DebugAdapterParser::prepare_success_response); ClassDB::bind_method(D_METHOD("req_pause", "params"), &DebugAdapterParser::req_pause); @@ -46,10 +49,13 @@ void DebugAdapterParser::_bind_methods() { ClassDB::bind_method(D_METHOD("req_threads", "params"), &DebugAdapterParser::req_threads); ClassDB::bind_method(D_METHOD("req_stackTrace", "params"), &DebugAdapterParser::req_stackTrace); ClassDB::bind_method(D_METHOD("req_setBreakpoints", "params"), &DebugAdapterParser::req_setBreakpoints); + ClassDB::bind_method(D_METHOD("req_breakpointLocations", "params"), &DebugAdapterParser::req_breakpointLocations); ClassDB::bind_method(D_METHOD("req_scopes", "params"), &DebugAdapterParser::req_scopes); ClassDB::bind_method(D_METHOD("req_variables", "params"), &DebugAdapterParser::req_variables); ClassDB::bind_method(D_METHOD("req_next", "params"), &DebugAdapterParser::req_next); ClassDB::bind_method(D_METHOD("req_stepIn", "params"), &DebugAdapterParser::req_stepIn); + ClassDB::bind_method(D_METHOD("req_evaluate", "params"), &DebugAdapterParser::req_evaluate); + ClassDB::bind_method(D_METHOD("req_godot/put_msg", "params"), &DebugAdapterParser::req_godot_put_msg); } Dictionary DebugAdapterParser::prepare_base_event() const { @@ -80,13 +86,31 @@ Dictionary DebugAdapterParser::prepare_error_response(const Dictionary &p_params DAP::Message message; String error, error_desc; switch (err_type) { + case DAP::ErrorType::WRONG_PATH: + error = "wrong_path"; + error_desc = "The editor and client are working on different paths; the client is on \"{clientPath}\", but the editor is on \"{editorPath}\""; + break; + case DAP::ErrorType::NOT_RUNNING: + error = "not_running"; + error_desc = "Can't attach to a running session since there isn't one."; + break; + case DAP::ErrorType::TIMEOUT: + error = "timeout"; + error_desc = "Timeout reached while processing a request."; + break; + case DAP::ErrorType::UNKNOWN_PLATFORM: + error = "unknown_platform"; + error_desc = "The specified platform is unknown."; + break; + case DAP::ErrorType::MISSING_DEVICE: + error = "missing_device"; + error_desc = "There's no connected device with specified id."; + break; case DAP::ErrorType::UNKNOWN: + default: error = "unknown"; error_desc = "An unknown error has ocurred when processing the request."; break; - case DAP::ErrorType::WRONG_PATH: - error = "wrong_path"; - error_desc = "The editor and client are working on different paths; the client is on \"{clientPath}\", but the editor is on \"{editorPath}\""; } message.id = err_type; @@ -114,10 +138,35 @@ Dictionary DebugAdapterParser::req_initialize(const Dictionary &p_params) const DebugAdapterProtocol::get_singleton()->notify_initialized(); + if (DebugAdapterProtocol::get_singleton()->_sync_breakpoints) { + // Send all current breakpoints + List<String> breakpoints; + ScriptEditor::get_singleton()->get_breakpoints(&breakpoints); + for (List<String>::Element *E = breakpoints.front(); E; E = E->next()) { + String breakpoint = E->get(); + + String path = breakpoint.left(breakpoint.find(":", 6)); // Skip initial part of path, aka "res://" + int line = breakpoint.substr(path.size()).to_int(); + + DebugAdapterProtocol::get_singleton()->on_debug_breakpoint_toggled(path, line, true); + } + } else { + // Remove all current breakpoints + EditorDebuggerNode::get_singleton()->get_default_debugger()->_clear_breakpoints(); + } + return response; } -Dictionary DebugAdapterParser::req_launch(const Dictionary &p_params) { +Dictionary DebugAdapterParser::req_disconnect(const Dictionary &p_params) const { + if (!DebugAdapterProtocol::get_singleton()->get_current_peer()->attached) { + EditorNode::get_singleton()->run_stop(); + } + + return prepare_success_response(p_params); +} + +Dictionary DebugAdapterParser::req_launch(const Dictionary &p_params) const { Dictionary args = p_params["arguments"]; if (args.has("project") && !is_valid_path(args["project"])) { Dictionary variables; @@ -126,17 +175,85 @@ Dictionary DebugAdapterParser::req_launch(const Dictionary &p_params) { return prepare_error_response(p_params, DAP::ErrorType::WRONG_PATH, variables); } + if (args.has("godot/custom_data")) { + DebugAdapterProtocol::get_singleton()->get_current_peer()->supportsCustomData = args["godot/custom_data"]; + } + ScriptEditorDebugger *dbg = EditorDebuggerNode::get_singleton()->get_default_debugger(); if ((bool)args["noDebug"] != dbg->is_skip_breakpoints()) { dbg->debug_skip_breakpoints(); } - EditorNode::get_singleton()->run_play(); + String platform_string = args.get("platform", "host"); + if (platform_string == "host") { + EditorNode::get_singleton()->run_play(); + } else { + int device = args.get("device", -1); + int idx = -1; + if (platform_string == "android") { + for (int i = 0; i < EditorExport::get_singleton()->get_export_platform_count(); i++) { + if (EditorExport::get_singleton()->get_export_platform(i)->get_name() == "Android") { + idx = i; + break; + } + } + } else if (platform_string == "web") { + for (int i = 0; i < EditorExport::get_singleton()->get_export_platform_count(); i++) { + if (EditorExport::get_singleton()->get_export_platform(i)->get_name() == "HTML5") { + idx = i; + break; + } + } + } + + if (idx == -1) { + return prepare_error_response(p_params, DAP::ErrorType::UNKNOWN_PLATFORM); + } + + EditorNode *editor = EditorNode::get_singleton(); + Error err = platform_string == "android" ? editor->run_play_native(device, idx) : editor->run_play_native(-1, idx); + if (err) { + if (err == ERR_INVALID_PARAMETER && platform_string == "android") { + return prepare_error_response(p_params, DAP::ErrorType::MISSING_DEVICE); + } else { + return prepare_error_response(p_params, DAP::ErrorType::UNKNOWN); + } + } + } + + DebugAdapterProtocol::get_singleton()->get_current_peer()->attached = false; DebugAdapterProtocol::get_singleton()->notify_process(); return prepare_success_response(p_params); } +Dictionary DebugAdapterParser::req_attach(const Dictionary &p_params) const { + ScriptEditorDebugger *dbg = EditorDebuggerNode::get_singleton()->get_default_debugger(); + if (!dbg->is_session_active()) { + return prepare_error_response(p_params, DAP::ErrorType::NOT_RUNNING); + } + + DebugAdapterProtocol::get_singleton()->get_current_peer()->attached = true; + DebugAdapterProtocol::get_singleton()->notify_process(); + return prepare_success_response(p_params); +} + +Dictionary DebugAdapterParser::req_restart(const Dictionary &p_params) const { + // Extract embedded "arguments" so it can be given to req_launch/req_attach + Dictionary params = p_params, args; + args = params["arguments"]; + args = args["arguments"]; + params["arguments"] = args; + + Dictionary response = DebugAdapterProtocol::get_singleton()->get_current_peer()->attached ? req_attach(params) : req_launch(params); + if (!response["success"]) { + response["command"] = p_params["command"]; + return response; + } + + return prepare_success_response(p_params); +} + Dictionary DebugAdapterParser::req_terminate(const Dictionary &p_params) const { EditorNode::get_singleton()->run_stop(); @@ -205,7 +322,7 @@ Dictionary DebugAdapterParser::req_stackTrace(const Dictionary &p_params) const return response; } -Dictionary DebugAdapterParser::req_setBreakpoints(const Dictionary &p_params) { +Dictionary DebugAdapterParser::req_setBreakpoints(const Dictionary &p_params) const { Dictionary response = prepare_success_response(p_params), body; response["body"] = body; @@ -230,14 +347,30 @@ Dictionary DebugAdapterParser::req_setBreakpoints(const Dictionary &p_params) { lines.push_back(breakpoint.line + !lines_at_one); } - EditorDebuggerNode::get_singleton()->set_breakpoints(ProjectSettings::get_singleton()->localize_path(source.path), lines); Array updated_breakpoints = DebugAdapterProtocol::get_singleton()->update_breakpoints(source.path, lines); body["breakpoints"] = updated_breakpoints; return response; } -Dictionary DebugAdapterParser::req_scopes(const Dictionary &p_params) { +Dictionary DebugAdapterParser::req_breakpointLocations(const Dictionary &p_params) const { + Dictionary response = prepare_success_response(p_params), body; + response["body"] = body; + Dictionary args = p_params["arguments"]; + + Array locations; + DAP::BreakpointLocation location; + location.line = args["line"]; + if (args.has("endLine")) { + location.endLine = args["endLine"]; + } + locations.push_back(location.to_json()); + + body["breakpoints"] = locations; + return response; +} + +Dictionary DebugAdapterParser::req_scopes(const Dictionary &p_params) const { Dictionary response = prepare_success_response(p_params), body; response["body"] = body; @@ -291,7 +424,14 @@ Dictionary DebugAdapterParser::req_variables(const Dictionary &p_params) const { int variable_id = args["variablesReference"]; Map<int, Array>::Element *E = DebugAdapterProtocol::get_singleton()->variable_list.find(variable_id); + if (E) { + if (!DebugAdapterProtocol::get_singleton()->get_current_peer()->supportsVariableType) { + for (int i = 0; i < E->value().size(); i++) { + Dictionary variable = E->value()[i]; + variable.erase("type"); + } + } body["variables"] = E ? E->value() : Array(); return response; } else { @@ -313,6 +453,29 @@ Dictionary DebugAdapterParser::req_stepIn(const Dictionary &p_params) const { return prepare_success_response(p_params); } +Dictionary DebugAdapterParser::req_evaluate(const Dictionary &p_params) const { + Dictionary response = prepare_success_response(p_params), body; + response["body"] = body; + + Dictionary args = p_params["arguments"]; + + String value = EditorDebuggerNode::get_singleton()->get_var_value(args["expression"]); + body["result"] = value; + + return response; +} + +Dictionary DebugAdapterParser::req_godot_put_msg(const Dictionary &p_params) const { + Dictionary args = p_params["arguments"]; + + String msg = args["message"]; + Array data = args["data"]; + + EditorDebuggerNode::get_singleton()->get_default_debugger()->_put_msg(msg, data); + + return prepare_success_response(p_params); +} + Dictionary DebugAdapterParser::ev_initialized() const { Dictionary event = prepare_base_event(); event["event"] = "initialized"; @@ -423,3 +586,25 @@ Dictionary DebugAdapterParser::ev_output(const String &p_message) const { return event; } + +Dictionary DebugAdapterParser::ev_breakpoint(const DAP::Breakpoint &p_breakpoint, const bool &p_enabled) const { + Dictionary event = prepare_base_event(), body; + event["event"] = "breakpoint"; + event["body"] = body; + + body["reason"] = p_enabled ? "new" : "removed"; + body["breakpoint"] = p_breakpoint.to_json(); + + return event; +} + +Dictionary DebugAdapterParser::ev_custom_data(const String &p_msg, const Array &p_data) const { + Dictionary event = prepare_base_event(), body; + event["event"] = "godot/custom_data"; + event["body"] = body; + + body["message"] = p_msg; + body["data"] = p_data; + + return event; +} diff --git a/editor/debugger/debug_adapter/debug_adapter_parser.h b/editor/debugger/debug_adapter/debug_adapter_parser.h index b86b37d067..4c93464e39 100644 --- a/editor/debugger/debug_adapter/debug_adapter_parser.h +++ b/editor/debugger/debug_adapter/debug_adapter_parser.h @@ -44,7 +44,7 @@ class DebugAdapterParser : public Object { private: friend DebugAdapterProtocol; - _FORCE_INLINE_ bool is_valid_path(const String &p_path) { + _FORCE_INLINE_ bool is_valid_path(const String &p_path) const { return p_path.begins_with(ProjectSettings::get_singleton()->get_resource_path()); } @@ -60,17 +60,23 @@ protected: public: // Requests Dictionary req_initialize(const Dictionary &p_params) const; - Dictionary req_launch(const Dictionary &p_params); + Dictionary req_launch(const Dictionary &p_params) const; + Dictionary req_disconnect(const Dictionary &p_params) const; + Dictionary req_attach(const Dictionary &p_params) const; + Dictionary req_restart(const Dictionary &p_params) const; Dictionary req_terminate(const Dictionary &p_params) const; Dictionary req_pause(const Dictionary &p_params) const; Dictionary req_continue(const Dictionary &p_params) const; Dictionary req_threads(const Dictionary &p_params) const; Dictionary req_stackTrace(const Dictionary &p_params) const; - Dictionary req_setBreakpoints(const Dictionary &p_params); - Dictionary req_scopes(const Dictionary &p_params); + Dictionary req_setBreakpoints(const Dictionary &p_params) const; + Dictionary req_breakpointLocations(const Dictionary &p_params) const; + Dictionary req_scopes(const Dictionary &p_params) const; Dictionary req_variables(const Dictionary &p_params) const; Dictionary req_next(const Dictionary &p_params) const; Dictionary req_stepIn(const Dictionary &p_params) const; + Dictionary req_evaluate(const Dictionary &p_params) const; + Dictionary req_godot_put_msg(const Dictionary &p_params) const; // Events Dictionary ev_initialized() const; @@ -83,6 +89,8 @@ public: Dictionary ev_stopped_step() const; Dictionary ev_continued() const; Dictionary ev_output(const String &p_message) const; + Dictionary ev_custom_data(const String &p_msg, const Array &p_data) const; + Dictionary ev_breakpoint(const DAP::Breakpoint &p_breakpoint, const bool &p_enabled) const; }; #endif diff --git a/editor/debugger/debug_adapter/debug_adapter_protocol.cpp b/editor/debugger/debug_adapter/debug_adapter_protocol.cpp index 0482271432..2e0e6cb7c8 100644 --- a/editor/debugger/debug_adapter/debug_adapter_protocol.cpp +++ b/editor/debugger/debug_adapter/debug_adapter_protocol.cpp @@ -94,11 +94,17 @@ Error DAPeer::handle_data() { String msg; msg.parse_utf8((const char *)req_buf, req_pos); + // Apply a timestamp if it there's none yet + if (!timestamp) { + timestamp = OS::get_singleton()->get_ticks_msec(); + } + // Response if (DebugAdapterProtocol::get_singleton()->process_message(msg)) { // Reset to read again req_pos = 0; has_header = false; + timestamp = 0; } } return OK; @@ -180,12 +186,460 @@ void DebugAdapterProtocol::reset_stack_info() { variable_list.clear(); } +int DebugAdapterProtocol::parse_variant(const Variant &p_var) { + switch (p_var.get_type()) { + case Variant::VECTOR2: + case Variant::VECTOR2I: { + int id = variable_id++; + Vector2 vec = p_var; + DAP::Variable x, y; + x.name = "x"; + y.name = "y"; + x.type = y.type = Variant::get_type_name(p_var.get_type() == Variant::VECTOR2 ? Variant::FLOAT : Variant::INT); + x.value = rtos(vec.x); + y.value = rtos(vec.y); + + Array arr; + arr.push_back(x.to_json()); + arr.push_back(y.to_json()); + variable_list.insert(id, arr); + return id; + } + case Variant::RECT2: + case Variant::RECT2I: { + int id = variable_id++; + Rect2 rect = p_var; + DAP::Variable x, y, w, h; + x.name = "x"; + y.name = "y"; + w.name = "w"; + h.name = "h"; + x.type = y.type = w.type = h.type = Variant::get_type_name(p_var.get_type() == Variant::RECT2 ? Variant::FLOAT : Variant::INT); + x.value = rtos(rect.position.x); + y.value = rtos(rect.position.y); + w.value = rtos(rect.size.x); + h.value = rtos(rect.size.y); + + Array arr; + arr.push_back(x.to_json()); + arr.push_back(y.to_json()); + arr.push_back(w.to_json()); + arr.push_back(h.to_json()); + variable_list.insert(id, arr); + return id; + } + case Variant::VECTOR3: + case Variant::VECTOR3I: { + int id = variable_id++; + Vector3 vec = p_var; + DAP::Variable x, y, z; + x.name = "x"; + y.name = "y"; + z.name = "z"; + x.type = y.type = z.type = Variant::get_type_name(p_var.get_type() == Variant::VECTOR3 ? Variant::FLOAT : Variant::INT); + x.value = rtos(vec.x); + y.value = rtos(vec.y); + z.value = rtos(vec.z); + + Array arr; + arr.push_back(x.to_json()); + arr.push_back(y.to_json()); + arr.push_back(z.to_json()); + variable_list.insert(id, arr); + return id; + } + case Variant::TRANSFORM2D: { + int id = variable_id++; + Transform2D transform = p_var; + DAP::Variable x, y, origin; + x.name = "x"; + y.name = "y"; + origin.name = "origin"; + x.type = y.type = origin.type = Variant::get_type_name(Variant::VECTOR2); + x.value = transform.elements[0]; + y.value = transform.elements[1]; + origin.value = transform.elements[2]; + x.variablesReference = parse_variant(transform.elements[0]); + y.variablesReference = parse_variant(transform.elements[1]); + origin.variablesReference = parse_variant(transform.elements[2]); + + Array arr; + arr.push_back(x.to_json()); + arr.push_back(y.to_json()); + arr.push_back(origin.to_json()); + variable_list.insert(id, arr); + return id; + } + case Variant::PLANE: { + int id = variable_id++; + Plane plane = p_var; + DAP::Variable d, normal; + d.name = "d"; + normal.name = "normal"; + d.type = Variant::get_type_name(Variant::FLOAT); + normal.type = Variant::get_type_name(Variant::VECTOR3); + d.value = rtos(plane.d); + normal.value = plane.normal; + normal.variablesReference = parse_variant(plane.normal); + + Array arr; + arr.push_back(d.to_json()); + arr.push_back(normal.to_json()); + variable_list.insert(id, arr); + return id; + } + case Variant::QUATERNION: { + int id = variable_id++; + Quaternion quat = p_var; + DAP::Variable x, y, z, w; + x.name = "x"; + y.name = "y"; + z.name = "z"; + w.name = "w"; + x.type = y.type = z.type = w.type = Variant::get_type_name(Variant::FLOAT); + x.value = rtos(quat.x); + y.value = rtos(quat.y); + z.value = rtos(quat.z); + w.value = rtos(quat.w); + + Array arr; + arr.push_back(x.to_json()); + arr.push_back(y.to_json()); + arr.push_back(z.to_json()); + arr.push_back(w.to_json()); + variable_list.insert(id, arr); + return id; + } + case Variant::AABB: { + int id = variable_id++; + AABB aabb = p_var; + DAP::Variable position, size; + position.name = "position"; + size.name = "size"; + position.type = size.type = Variant::get_type_name(Variant::VECTOR3); + position.value = aabb.position; + size.value = aabb.size; + position.variablesReference = parse_variant(aabb.position); + size.variablesReference = parse_variant(aabb.size); + + Array arr; + arr.push_back(position.to_json()); + arr.push_back(size.to_json()); + variable_list.insert(id, arr); + return id; + } + case Variant::BASIS: { + int id = variable_id++; + Basis basis = p_var; + DAP::Variable x, y, z; + x.name = "x"; + y.name = "y"; + z.name = "z"; + x.type = y.type = z.type = Variant::get_type_name(Variant::VECTOR2); + x.value = basis.elements[0]; + y.value = basis.elements[1]; + z.value = basis.elements[2]; + x.variablesReference = parse_variant(basis.elements[0]); + y.variablesReference = parse_variant(basis.elements[1]); + z.variablesReference = parse_variant(basis.elements[2]); + + Array arr; + arr.push_back(x.to_json()); + arr.push_back(y.to_json()); + arr.push_back(z.to_json()); + variable_list.insert(id, arr); + return id; + } + case Variant::TRANSFORM3D: { + int id = variable_id++; + Transform3D transform = p_var; + DAP::Variable basis, origin; + basis.name = "basis"; + origin.name = "origin"; + basis.type = Variant::get_type_name(Variant::BASIS); + origin.type = Variant::get_type_name(Variant::VECTOR3); + basis.value = transform.basis; + origin.value = transform.origin; + basis.variablesReference = parse_variant(transform.basis); + origin.variablesReference = parse_variant(transform.origin); + + Array arr; + arr.push_back(basis.to_json()); + arr.push_back(origin.to_json()); + variable_list.insert(id, arr); + return id; + } + case Variant::COLOR: { + int id = variable_id++; + Color color = p_var; + DAP::Variable r, g, b, a; + r.name = "r"; + g.name = "g"; + b.name = "b"; + a.name = "a"; + r.type = g.type = b.type = a.type = Variant::get_type_name(Variant::FLOAT); + r.value = rtos(color.r); + g.value = rtos(color.g); + b.value = rtos(color.b); + a.value = rtos(color.a); + + Array arr; + arr.push_back(r.to_json()); + arr.push_back(g.to_json()); + arr.push_back(b.to_json()); + arr.push_back(a.to_json()); + variable_list.insert(id, arr); + return id; + } + case Variant::ARRAY: { + int id = variable_id++; + Array array = p_var; + DAP::Variable size; + size.name = "size"; + size.type = Variant::get_type_name(Variant::INT); + size.value = itos(array.size()); + + Array arr; + arr.push_back(size.to_json()); + + for (int i = 0; i < array.size(); i++) { + DAP::Variable var; + var.name = itos(i); + var.type = Variant::get_type_name(array[i].get_type()); + var.value = array[i]; + var.variablesReference = parse_variant(array[i]); + arr.push_back(var.to_json()); + } + variable_list.insert(id, arr); + return id; + } + case Variant::DICTIONARY: { + int id = variable_id++; + Dictionary dictionary = p_var; + Array arr; + + for (int i = 0; i < dictionary.size(); i++) { + DAP::Variable var; + var.name = dictionary.get_key_at_index(i); + Variant value = dictionary.get_value_at_index(i); + var.type = Variant::get_type_name(value.get_type()); + var.value = value; + var.variablesReference = parse_variant(value); + arr.push_back(var.to_json()); + } + variable_list.insert(id, arr); + return id; + } + case Variant::PACKED_BYTE_ARRAY: { + int id = variable_id++; + PackedByteArray array = p_var; + DAP::Variable size; + size.name = "size"; + size.type = Variant::get_type_name(Variant::INT); + size.value = itos(array.size()); + + Array arr; + arr.push_back(size.to_json()); + + for (int i = 0; i < array.size(); i++) { + DAP::Variable var; + var.name = itos(i); + var.type = "byte"; + var.value = itos(array[i]); + arr.push_back(var.to_json()); + } + variable_list.insert(id, arr); + return id; + } + case Variant::PACKED_INT32_ARRAY: { + int id = variable_id++; + PackedInt32Array array = p_var; + DAP::Variable size; + size.name = "size"; + size.type = Variant::get_type_name(Variant::INT); + size.value = itos(array.size()); + + Array arr; + arr.push_back(size.to_json()); + + for (int i = 0; i < array.size(); i++) { + DAP::Variable var; + var.name = itos(i); + var.type = "int"; + var.value = itos(array[i]); + arr.push_back(var.to_json()); + } + variable_list.insert(id, arr); + return id; + } + case Variant::PACKED_INT64_ARRAY: { + int id = variable_id++; + PackedInt64Array array = p_var; + DAP::Variable size; + size.name = "size"; + size.type = Variant::get_type_name(Variant::INT); + size.value = itos(array.size()); + + Array arr; + arr.push_back(size.to_json()); + + for (int i = 0; i < array.size(); i++) { + DAP::Variable var; + var.name = itos(i); + var.type = "long"; + var.value = itos(array[i]); + arr.push_back(var.to_json()); + } + variable_list.insert(id, arr); + return id; + } + case Variant::PACKED_FLOAT32_ARRAY: { + int id = variable_id++; + PackedFloat32Array array = p_var; + DAP::Variable size; + size.name = "size"; + size.type = Variant::get_type_name(Variant::INT); + size.value = itos(array.size()); + + Array arr; + arr.push_back(size.to_json()); + + for (int i = 0; i < array.size(); i++) { + DAP::Variable var; + var.name = itos(i); + var.type = "float"; + var.value = rtos(array[i]); + arr.push_back(var.to_json()); + } + variable_list.insert(id, arr); + return id; + } + case Variant::PACKED_FLOAT64_ARRAY: { + int id = variable_id++; + PackedFloat64Array array = p_var; + DAP::Variable size; + size.name = "size"; + size.type = Variant::get_type_name(Variant::INT); + size.value = itos(array.size()); + + Array arr; + arr.push_back(size.to_json()); + + for (int i = 0; i < array.size(); i++) { + DAP::Variable var; + var.name = itos(i); + var.type = "double"; + var.value = rtos(array[i]); + arr.push_back(var.to_json()); + } + variable_list.insert(id, arr); + return id; + } + case Variant::PACKED_STRING_ARRAY: { + int id = variable_id++; + PackedStringArray array = p_var; + DAP::Variable size; + size.name = "size"; + size.type = Variant::get_type_name(Variant::INT); + size.value = itos(array.size()); + + Array arr; + arr.push_back(size.to_json()); + + for (int i = 0; i < array.size(); i++) { + DAP::Variable var; + var.name = itos(i); + var.type = Variant::get_type_name(Variant::STRING); + var.value = array[i]; + arr.push_back(var.to_json()); + } + variable_list.insert(id, arr); + return id; + } + case Variant::PACKED_VECTOR2_ARRAY: { + int id = variable_id++; + PackedVector2Array array = p_var; + DAP::Variable size; + size.name = "size"; + size.type = Variant::get_type_name(Variant::INT); + size.value = itos(array.size()); + + Array arr; + arr.push_back(size.to_json()); + + for (int i = 0; i < array.size(); i++) { + DAP::Variable var; + var.name = itos(i); + var.type = Variant::get_type_name(Variant::VECTOR2); + var.value = array[i]; + var.variablesReference = parse_variant(array[i]); + arr.push_back(var.to_json()); + } + variable_list.insert(id, arr); + return id; + } + case Variant::PACKED_VECTOR3_ARRAY: { + int id = variable_id++; + PackedVector2Array array = p_var; + DAP::Variable size; + size.name = "size"; + size.type = Variant::get_type_name(Variant::INT); + size.value = itos(array.size()); + + Array arr; + arr.push_back(size.to_json()); + + for (int i = 0; i < array.size(); i++) { + DAP::Variable var; + var.name = itos(i); + var.type = Variant::get_type_name(Variant::VECTOR3); + var.value = array[i]; + var.variablesReference = parse_variant(array[i]); + arr.push_back(var.to_json()); + } + variable_list.insert(id, arr); + return id; + } + case Variant::PACKED_COLOR_ARRAY: { + int id = variable_id++; + PackedColorArray array = p_var; + DAP::Variable size; + size.name = "size"; + size.type = Variant::get_type_name(Variant::INT); + size.value = itos(array.size()); + + Array arr; + arr.push_back(size.to_json()); + + for (int i = 0; i < array.size(); i++) { + DAP::Variable var; + var.name = itos(i); + var.type = Variant::get_type_name(Variant::COLOR); + var.value = array[i]; + var.variablesReference = parse_variant(array[i]); + arr.push_back(var.to_json()); + } + variable_list.insert(id, arr); + return id; + } + default: + // Simple atomic stuff, or too complex to be manipulated + return 0; + } +} + bool DebugAdapterProtocol::process_message(const String &p_text) { JSON json; ERR_FAIL_COND_V_MSG(json.parse(p_text) != OK, true, "Mal-formed message!"); Dictionary params = json.get_data(); bool completed = true; + if (OS::get_singleton()->get_ticks_msec() - _current_peer->timestamp > _request_timeout) { + Dictionary response = parser->prepare_error_response(params, DAP::ErrorType::TIMEOUT); + _current_peer->res_queue.push_front(response); + return true; + } + // Append "req_" to any command received; prevents name clash with existing functions, and possibly exploiting String command = "req_" + (String)params["command"]; if (parser->has_method(command)) { @@ -211,7 +665,7 @@ void DebugAdapterProtocol::notify_initialized() { } void DebugAdapterProtocol::notify_process() { - String launch_mode = _current_request.is_empty() ? "launch" : _current_request; + String launch_mode = _current_peer->attached ? "attach" : "launch"; Dictionary event = parser->ev_process(launch_mode); for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) { @@ -222,7 +676,7 @@ void DebugAdapterProtocol::notify_process() { void DebugAdapterProtocol::notify_terminated() { Dictionary event = parser->ev_terminated(); for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) { - if (_current_request == "launch" && _current_peer == E->get()) { + if ((_current_request == "launch" || _current_request == "restart") && _current_peer == E->get()) { continue; } E->get()->res_queue.push_back(event); @@ -232,7 +686,7 @@ void DebugAdapterProtocol::notify_terminated() { void DebugAdapterProtocol::notify_exited(const int &p_exitcode) { Dictionary event = parser->ev_exited(p_exitcode); for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) { - if (_current_request == "launch" && _current_peer == E->get()) { + if ((_current_request == "launch" || _current_request == "restart") && _current_peer == E->get()) { continue; } E->get()->res_queue.push_back(event); @@ -286,25 +740,46 @@ void DebugAdapterProtocol::notify_output(const String &p_message) { } } +void DebugAdapterProtocol::notify_custom_data(const String &p_msg, const Array &p_data) { + Dictionary event = parser->ev_custom_data(p_msg, p_data); + for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) { + Ref<DAPeer> peer = E->get(); + if (peer->supportsCustomData) { + peer->res_queue.push_back(event); + } + } +} + +void DebugAdapterProtocol::notify_breakpoint(const DAP::Breakpoint &p_breakpoint, const bool &p_enabled) { + Dictionary event = parser->ev_breakpoint(p_breakpoint, p_enabled); + for (List<Ref<DAPeer>>::Element *E = clients.front(); E; E = E->next()) { + if (_current_request == "setBreakpoints" && E->get() == _current_peer) { + continue; + } + E->get()->res_queue.push_back(event); + } +} + Array DebugAdapterProtocol::update_breakpoints(const String &p_path, const Array &p_lines) { Array updated_breakpoints; + // Add breakpoints for (int i = 0; i < p_lines.size(); i++) { + EditorDebuggerNode::get_singleton()->get_default_debugger()->_set_breakpoint(p_path, p_lines[i], true); DAP::Breakpoint breakpoint; - breakpoint.verified = true; - breakpoint.source.path = p_path; - breakpoint.source.compute_checksums(); breakpoint.line = p_lines[i]; + breakpoint.source.path = p_path; - List<DAP::Breakpoint>::Element *E = breakpoint_list.find(breakpoint); - if (E) { - breakpoint.id = E->get().id; - } else { - breakpoint.id = breakpoint_id++; - breakpoint_list.push_back(breakpoint); - } + ERR_FAIL_COND_V(!breakpoint_list.find(breakpoint), Array()); + updated_breakpoints.push_back(breakpoint_list.find(breakpoint)->get().to_json()); + } - updated_breakpoints.push_back(breakpoint.to_json()); + // Remove breakpoints + for (List<DAP::Breakpoint>::Element *E = breakpoint_list.front(); E; E = E->next()) { + DAP::Breakpoint b = E->get(); + if (b.source.path == p_path && !p_lines.has(b.line)) { + EditorDebuggerNode::get_singleton()->get_default_debugger()->_set_breakpoint(p_path, b.line, false); + } } return updated_breakpoints; @@ -347,6 +822,29 @@ void DebugAdapterProtocol::on_debug_breaked(const bool &p_reallydid, const bool _processing_stackdump = p_has_stackdump; } +void DebugAdapterProtocol::on_debug_breakpoint_toggled(const String &p_path, const int &p_line, const bool &p_enabled) { + DAP::Breakpoint breakpoint; + breakpoint.verified = true; + breakpoint.source.path = ProjectSettings::get_singleton()->globalize_path(p_path); + breakpoint.source.compute_checksums(); + breakpoint.line = p_line; + + if (p_enabled) { + // Add the breakpoint + breakpoint.id = breakpoint_id++; + breakpoint_list.push_back(breakpoint); + } else { + // Remove the breakpoint + List<DAP::Breakpoint>::Element *E = breakpoint_list.find(breakpoint); + if (E) { + breakpoint.id = E->get().id; + breakpoint_list.erase(E); + } + } + + notify_breakpoint(breakpoint, p_enabled); +} + void DebugAdapterProtocol::on_debug_stack_dump(const Array &p_stack_dump) { if (_processing_breakpoint && !p_stack_dump.is_empty()) { // Find existing breakpoint @@ -424,11 +922,21 @@ void DebugAdapterProtocol::on_debug_stack_frame_var(const Array &p_data) { variable.name = stack_var.name; variable.value = stack_var.value; variable.type = Variant::get_type_name(stack_var.value.get_type()); + variable.variablesReference = parse_variant(stack_var.value); variable_list.find(variable_id)->value().push_back(variable.to_json()); _remaining_vars--; } +void DebugAdapterProtocol::on_debug_data(const String &p_msg, const Array &p_data) { + // Ignore data that is already handled by DAP + if (p_msg == "debug_enter" || p_msg == "debug_exit" || p_msg == "stack_dump" || p_msg == "stack_frame_vars" || p_msg == "stack_frame_var" || p_msg == "output" || p_msg == "request_quit") { + return; + } + + notify_custom_data(p_msg, p_data); +} + void DebugAdapterProtocol::poll() { if (server->is_connection_available()) { on_client_connected(); @@ -459,6 +967,8 @@ void DebugAdapterProtocol::poll() { } Error DebugAdapterProtocol::start(int p_port, const IPAddress &p_bind_ip) { + _request_timeout = (uint64_t)_EDITOR_GET("network/debug_adapter/request_timeout"); + _sync_breakpoints = (bool)_EDITOR_GET("network/debug_adapter/sync_breakpoints"); _initialized = true; return server->listen(p_port, p_bind_ip); } @@ -484,12 +994,15 @@ DebugAdapterProtocol::DebugAdapterProtocol() { node->get_pause_button()->connect("pressed", callable_mp(this, &DebugAdapterProtocol::on_debug_paused)); EditorDebuggerNode *debugger_node = EditorDebuggerNode::get_singleton(); + debugger_node->connect("breakpoint_toggled", callable_mp(this, &DebugAdapterProtocol::on_debug_breakpoint_toggled)); + debugger_node->get_default_debugger()->connect("stopped", callable_mp(this, &DebugAdapterProtocol::on_debug_stopped)); debugger_node->get_default_debugger()->connect("output", callable_mp(this, &DebugAdapterProtocol::on_debug_output)); debugger_node->get_default_debugger()->connect("breaked", callable_mp(this, &DebugAdapterProtocol::on_debug_breaked)); debugger_node->get_default_debugger()->connect("stack_dump", callable_mp(this, &DebugAdapterProtocol::on_debug_stack_dump)); debugger_node->get_default_debugger()->connect("stack_frame_vars", callable_mp(this, &DebugAdapterProtocol::on_debug_stack_frame_vars)); debugger_node->get_default_debugger()->connect("stack_frame_var", callable_mp(this, &DebugAdapterProtocol::on_debug_stack_frame_var)); + debugger_node->get_default_debugger()->connect("debug_data", callable_mp(this, &DebugAdapterProtocol::on_debug_data)); } DebugAdapterProtocol::~DebugAdapterProtocol() { diff --git a/editor/debugger/debug_adapter/debug_adapter_protocol.h b/editor/debugger/debug_adapter/debug_adapter_protocol.h index 6b542033ed..d4291992bf 100644 --- a/editor/debugger/debug_adapter/debug_adapter_protocol.h +++ b/editor/debugger/debug_adapter/debug_adapter_protocol.h @@ -52,12 +52,17 @@ struct DAPeer : RefCounted { int content_length = 0; List<Dictionary> res_queue; int seq = 0; + uint64_t timestamp = 0; // Client specific info bool linesStartAt1 = false; bool columnsStartAt1 = false; bool supportsVariableType = false; bool supportsInvalidatedEvent = false; + bool supportsCustomData = false; + + // Internal client info + bool attached = false; Error handle_data(); Error send_data(); @@ -82,20 +87,26 @@ private: void on_debug_stopped(); void on_debug_output(const String &p_message); void on_debug_breaked(const bool &p_reallydid, const bool &p_can_debug, const String &p_reason, const bool &p_has_stackdump); + void on_debug_breakpoint_toggled(const String &p_path, const int &p_line, const bool &p_enabled); void on_debug_stack_dump(const Array &p_stack_dump); void on_debug_stack_frame_vars(const int &p_size); void on_debug_stack_frame_var(const Array &p_data); + void on_debug_data(const String &p_msg, const Array &p_data); void reset_current_info(); void reset_ids(); void reset_stack_info(); + int parse_variant(const Variant &p_var); + bool _initialized = false; bool _processing_breakpoint = false; bool _stepping = false; bool _processing_stackdump = false; int _remaining_vars = 0; int _current_frame = 0; + uint64_t _request_timeout = 1000; + bool _sync_breakpoints = false; String _current_request; Ref<DAPeer> _current_peer; @@ -108,6 +119,8 @@ private: Map<int, Array> variable_list; public: + friend class DebugAdapterServer; + _FORCE_INLINE_ static DebugAdapterProtocol *get_singleton() { return singleton; } _FORCE_INLINE_ bool is_active() const { return _initialized && clients.size() > 0; } @@ -126,8 +139,10 @@ public: void notify_stopped_step(); void notify_continued(); void notify_output(const String &p_message); + void notify_custom_data(const String &p_msg, const Array &p_data); + void notify_breakpoint(const DAP::Breakpoint &p_breakpoint, const bool &p_enabled); - Array update_breakpoints(const String &p_path, const Array &p_breakpoints); + Array update_breakpoints(const String &p_path, const Array &p_lines); void poll(); Error start(int p_port, const IPAddress &p_bind_ip); diff --git a/editor/debugger/debug_adapter/debug_adapter_server.cpp b/editor/debugger/debug_adapter/debug_adapter_server.cpp index f9092a1791..4775e2c8b0 100644 --- a/editor/debugger/debug_adapter/debug_adapter_server.cpp +++ b/editor/debugger/debug_adapter/debug_adapter_server.cpp @@ -36,7 +36,8 @@ DebugAdapterServer::DebugAdapterServer() { _EDITOR_DEF("network/debug_adapter/remote_port", remote_port); - _EDITOR_DEF("network/debug_adapter/use_thread", use_thread); + _EDITOR_DEF("network/debug_adapter/request_timeout", protocol._request_timeout); + _EDITOR_DEF("network/debug_adapter/sync_breakpoints", protocol._sync_breakpoints); } void DebugAdapterServer::_notification(int p_what) { @@ -50,16 +51,17 @@ void DebugAdapterServer::_notification(int p_what) { case NOTIFICATION_INTERNAL_PROCESS: { // The main loop can be run again during request processing, which modifies internal state of the protocol. // Thus, "polling" is needed to prevent it from parsing other requests while the current one isn't finished. - if (started && !use_thread && !polling) { + if (started && !polling) { polling = true; protocol.poll(); polling = false; } } break; case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { + protocol._request_timeout = EditorSettings::get_singleton()->get("network/debug_adapter/request_timeout"); + protocol._sync_breakpoints = EditorSettings::get_singleton()->get("network/debug_adapter/sync_breakpoints"); int remote_port = (int)_EDITOR_GET("network/debug_adapter/remote_port"); - bool use_thread = (bool)_EDITOR_GET("network/debug_adapter/use_thread"); - if (remote_port != this->remote_port || use_thread != this->use_thread) { + if (remote_port != this->remote_port) { this->stop(); this->start(); } @@ -67,35 +69,16 @@ void DebugAdapterServer::_notification(int p_what) { } } -void DebugAdapterServer::thread_func(void *p_userdata) { - DebugAdapterServer *self = static_cast<DebugAdapterServer *>(p_userdata); - while (self->thread_running) { - // Poll 20 times per second - self->protocol.poll(); - OS::get_singleton()->delay_usec(50000); - } -} - void DebugAdapterServer::start() { remote_port = (int)_EDITOR_GET("network/debug_adapter/remote_port"); - use_thread = (bool)_EDITOR_GET("network/debug_adapter/use_thread"); if (protocol.start(remote_port, IPAddress("127.0.0.1")) == OK) { EditorNode::get_log()->add_message("--- Debug adapter server started ---", EditorLog::MSG_TYPE_EDITOR); - if (use_thread) { - thread_running = true; - thread.start(DebugAdapterServer::thread_func, this); - } - set_process_internal(!use_thread); + set_process_internal(true); started = true; } } void DebugAdapterServer::stop() { - if (use_thread) { - ERR_FAIL_COND(!thread.is_started()); - thread_running = false; - thread.wait_to_finish(); - } protocol.stop(); started = false; EditorNode::get_log()->add_message("--- Debug adapter server stopped ---", EditorLog::MSG_TYPE_EDITOR); diff --git a/editor/debugger/debug_adapter/debug_adapter_server.h b/editor/debugger/debug_adapter/debug_adapter_server.h index f8a38965cc..c449403cc2 100644 --- a/editor/debugger/debug_adapter/debug_adapter_server.h +++ b/editor/debugger/debug_adapter/debug_adapter_server.h @@ -39,11 +39,9 @@ class DebugAdapterServer : public EditorPlugin { DebugAdapterProtocol protocol; - Thread thread; int remote_port = 6006; bool thread_running = false; bool started = false; - bool use_thread = false; bool polling = false; static void thread_func(void *p_userdata); diff --git a/editor/debugger/debug_adapter/debug_adapter_types.h b/editor/debugger/debug_adapter/debug_adapter_types.h index aa9ab1adcd..5156c91d14 100644 --- a/editor/debugger/debug_adapter/debug_adapter_types.h +++ b/editor/debugger/debug_adapter/debug_adapter_types.h @@ -38,7 +38,11 @@ namespace DAP { enum ErrorType { UNKNOWN, - WRONG_PATH + WRONG_PATH, + NOT_RUNNING, + TIMEOUT, + UNKNOWN_PLATFORM, + MISSING_DEVICE }; struct Checksum { @@ -118,10 +122,14 @@ struct Breakpoint { struct BreakpointLocation { int line; + int endLine = -1; _FORCE_INLINE_ Dictionary to_json() const { Dictionary dict; dict["line"] = line; + if (endLine >= 0) { + dict["endLine"] = endLine; + } return dict; } diff --git a/editor/debugger/editor_debugger_node.cpp b/editor/debugger/editor_debugger_node.cpp index a9cb1a0131..07c02eb022 100644 --- a/editor/debugger/editor_debugger_node.cpp +++ b/editor/debugger/editor_debugger_node.cpp @@ -164,6 +164,7 @@ void EditorDebuggerNode::_bind_methods() { ADD_SIGNAL(MethodInfo("set_execution", PropertyInfo("script"), PropertyInfo(Variant::INT, "line"))); ADD_SIGNAL(MethodInfo("clear_execution", PropertyInfo("script"))); ADD_SIGNAL(MethodInfo("breaked", PropertyInfo(Variant::BOOL, "reallydid"), PropertyInfo(Variant::BOOL, "can_debug"))); + ADD_SIGNAL(MethodInfo("breakpoint_toggled", PropertyInfo(Variant::STRING, "path"), PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::BOOL, "enabled"))); } EditorDebuggerRemoteObject *EditorDebuggerNode::get_inspected_remote_object() { @@ -487,6 +488,8 @@ void EditorDebuggerNode::set_breakpoint(const String &p_path, int p_line, bool p _for_all(tabs, [&](ScriptEditorDebugger *dbg) { dbg->set_breakpoint(p_path, p_line, p_enabled); }); + + emit_signal("breakpoint_toggled", p_path, p_line, p_enabled); } void EditorDebuggerNode::set_breakpoints(const String &p_path, Array p_lines) { diff --git a/editor/debugger/script_editor_debugger.cpp b/editor/debugger/script_editor_debugger.cpp index 2a5013893f..2f5bde64a9 100644 --- a/editor/debugger/script_editor_debugger.cpp +++ b/editor/debugger/script_editor_debugger.cpp @@ -296,6 +296,7 @@ Size2 ScriptEditorDebugger::get_minimum_size() const { } void ScriptEditorDebugger::_parse_message(const String &p_msg, const Array &p_data) { + emit_signal(SNAME("debug_data"), p_msg, p_data); if (p_msg == "debug_enter") { _put_msg("get_stack_dump", Array()); @@ -872,6 +873,16 @@ void ScriptEditorDebugger::_clear_execution() { inspector->clear_stack_variables(); } +void ScriptEditorDebugger::_set_breakpoint(const String &p_file, const int &p_line, const bool &p_enabled) { + Ref<Script> script = ResourceLoader::load(p_file); + emit_signal("set_breakpoint", script, p_line - 1, p_enabled); + script.unref(); +} + +void ScriptEditorDebugger::_clear_breakpoints() { + emit_signal("clear_breakpoints"); +} + void ScriptEditorDebugger::start(Ref<RemoteDebuggerPeer> p_peer) { error_count = 0; warning_count = 0; @@ -1503,6 +1514,9 @@ void ScriptEditorDebugger::_bind_methods() { ADD_SIGNAL(MethodInfo("stack_dump", PropertyInfo(Variant::ARRAY, "stack_dump"))); ADD_SIGNAL(MethodInfo("stack_frame_vars", PropertyInfo(Variant::INT, "num_vars"))); ADD_SIGNAL(MethodInfo("stack_frame_var", PropertyInfo(Variant::ARRAY, "data"))); + ADD_SIGNAL(MethodInfo("debug_data", PropertyInfo(Variant::STRING, "msg"), PropertyInfo(Variant::ARRAY, "data"))); + ADD_SIGNAL(MethodInfo("set_breakpoint", PropertyInfo("script"), PropertyInfo(Variant::INT, "line"), PropertyInfo(Variant::BOOL, "enabled"))); + ADD_SIGNAL(MethodInfo("clear_breakpoints")); } void ScriptEditorDebugger::add_debugger_plugin(const Ref<Script> &p_script) { diff --git a/editor/debugger/script_editor_debugger.h b/editor/debugger/script_editor_debugger.h index 499dda86da..1c1c0fd3e5 100644 --- a/editor/debugger/script_editor_debugger.h +++ b/editor/debugger/script_editor_debugger.h @@ -205,6 +205,9 @@ private: void _clear_execution(); void _stop_and_notify(); + void _set_breakpoint(const String &p_path, const int &p_line, const bool &p_enabled); + void _clear_breakpoints(); + protected: void _notification(int p_what); static void _bind_methods(); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 5f029e53c9..f86a36df2f 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -4764,6 +4764,10 @@ bool EditorNode::ensure_main_scene(bool p_from_native) { return true; } +Error EditorNode::run_play_native(int p_idx, int p_platform) { + return run_native->run_native(p_idx, p_platform); +} + void EditorNode::run_play() { _menu_option_confirm(RUN_STOP, true); _run(false); diff --git a/editor/editor_node.h b/editor/editor_node.h index 51d01d07ef..03c18a8972 100644 --- a/editor/editor_node.h +++ b/editor/editor_node.h @@ -898,6 +898,7 @@ public: bool ensure_main_scene(bool p_from_native); + Error run_play_native(int p_idx, int p_platform); void run_play(); void run_play_current(); void run_play_custom(const String &p_custom); diff --git a/editor/editor_run_native.cpp b/editor/editor_run_native.cpp index e115cd77e1..5828549bdc 100644 --- a/editor/editor_run_native.cpp +++ b/editor/editor_run_native.cpp @@ -52,8 +52,8 @@ void EditorRunNative::_notification(int p_what) { small_icon.instantiate(); small_icon->create_from_image(im); MenuButton *mb = memnew(MenuButton); - mb->get_popup()->connect("id_pressed", callable_mp(this, &EditorRunNative::_run_native), varray(i)); - mb->connect("pressed", callable_mp(this, &EditorRunNative::_run_native), varray(-1, i)); + mb->get_popup()->connect("id_pressed", callable_mp(this, &EditorRunNative::run_native), varray(i)); + mb->connect("pressed", callable_mp(this, &EditorRunNative::run_native), varray(-1, i)); mb->set_icon(small_icon); add_child(mb); menus[i] = mb; @@ -93,22 +93,22 @@ void EditorRunNative::_notification(int p_what) { } } -void EditorRunNative::_run_native(int p_idx, int p_platform) { +Error EditorRunNative::run_native(int p_idx, int p_platform) { if (!EditorNode::get_singleton()->ensure_main_scene(true)) { resume_idx = p_idx; resume_platform = p_platform; - return; + return OK; } Ref<EditorExportPlatform> eep = EditorExport::get_singleton()->get_export_platform(p_platform); - ERR_FAIL_COND(eep.is_null()); + ERR_FAIL_COND_V(eep.is_null(), ERR_UNAVAILABLE); if (p_idx == -1) { if (eep->get_options_count() == 1) { menus[p_platform]->get_popup()->hide(); p_idx = 0; } else { - return; + return ERR_INVALID_PARAMETER; } } @@ -124,7 +124,7 @@ void EditorRunNative::_run_native(int p_idx, int p_platform) { if (preset.is_null()) { EditorNode::get_singleton()->show_warning(TTR("No runnable export preset found for this platform.\nPlease add a runnable preset in the Export menu or define an existing preset as runnable.")); - return; + return ERR_UNAVAILABLE; } emit_signal(SNAME("native_run"), preset); @@ -149,11 +149,11 @@ void EditorRunNative::_run_native(int p_idx, int p_platform) { flags |= EditorExportPlatform::DEBUG_FLAG_VIEW_NAVIGATION; } - eep->run(preset, p_idx, flags); + return eep->run(preset, p_idx, flags); } void EditorRunNative::resume_run_native() { - _run_native(resume_idx, resume_platform); + run_native(resume_idx, resume_platform); } void EditorRunNative::_bind_methods() { diff --git a/editor/editor_run_native.h b/editor/editor_run_native.h index 3516f668c6..97f6fc005a 100644 --- a/editor/editor_run_native.h +++ b/editor/editor_run_native.h @@ -43,13 +43,12 @@ class EditorRunNative : public HBoxContainer { int resume_idx; int resume_platform; - void _run_native(int p_idx, int p_platform); - protected: static void _bind_methods(); void _notification(int p_what); public: + Error run_native(int p_idx, int p_platform); bool is_deploy_debug_remote_enabled() const; void resume_run_native(); diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index 8c348731d6..32ec49e11e 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -1201,11 +1201,11 @@ Ref<Theme> create_editor_theme(const Ref<Theme> p_theme) { // TooltipPanel Ref<StyleBoxFlat> style_tooltip = style_popup->duplicate(); style_tooltip->set_shadow_size(0); - style_tooltip->set_default_margin(SIDE_LEFT, default_margin_size * EDSCALE); + style_tooltip->set_default_margin(SIDE_LEFT, default_margin_size * EDSCALE * 0.5); style_tooltip->set_default_margin(SIDE_TOP, default_margin_size * EDSCALE * 0.5); - style_tooltip->set_default_margin(SIDE_RIGHT, default_margin_size * EDSCALE); + style_tooltip->set_default_margin(SIDE_RIGHT, default_margin_size * EDSCALE * 0.5); style_tooltip->set_default_margin(SIDE_BOTTOM, default_margin_size * EDSCALE * 0.5); - style_tooltip->set_bg_color(mono_color.inverted() * Color(1, 1, 1, 0.9)); + style_tooltip->set_bg_color(dark_color_3 * Color(0.8, 0.8, 0.8, 0.9)); style_tooltip->set_border_width_all(0); theme->set_color("font_color", "TooltipLabel", font_hover_color); theme->set_color("font_color_shadow", "TooltipLabel", Color(0, 0, 0, 0)); diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index a24249b95b..7ef5993ec5 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -37,6 +37,7 @@ #include "core/os/keyboard.h" #include "core/os/os.h" #include "editor/debugger/editor_debugger_node.h" +#include "editor/debugger/script_editor_debugger.h" #include "editor/editor_node.h" #include "editor/editor_run_script.h" #include "editor/editor_scale.h" @@ -479,6 +480,29 @@ void ScriptEditor::_clear_execution(REF p_script) { } } +void ScriptEditor::_set_breakpoint(REF p_script, int p_line, bool p_enabled) { + Ref<Script> script = Object::cast_to<Script>(*p_script); + if (script.is_valid() && (script->has_source_code() || script->get_path().is_resource_file())) { + if (edit(p_script, p_line, 0, false)) { + editor->push_item(p_script.ptr()); + + ScriptEditorBase *se = _get_current_editor(); + if (se) { + se->set_breakpoint(p_line, p_enabled); + } + } + } +} + +void ScriptEditor::_clear_breakpoints() { + for (int i = 0; i < tab_container->get_child_count(); i++) { + ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_child(i)); + if (se) { + se->clear_breakpoints(); + } + } +} + ScriptEditorBase *ScriptEditor::_get_current_editor() const { int selected = tab_container->get_current_tab(); if (selected < 0 || selected >= tab_container->get_child_count()) { @@ -3484,6 +3508,8 @@ ScriptEditor::ScriptEditor(EditorNode *p_editor) { debugger->connect("set_execution", callable_mp(this, &ScriptEditor::_set_execution)); debugger->connect("clear_execution", callable_mp(this, &ScriptEditor::_clear_execution)); debugger->connect("breaked", callable_mp(this, &ScriptEditor::_breaked)); + debugger->get_default_debugger()->connect("set_breakpoint", callable_mp(this, &ScriptEditor::_set_breakpoint)); + debugger->get_default_debugger()->connect("clear_breakpoints", callable_mp(this, &ScriptEditor::_clear_breakpoints)); menu_hb->add_spacer(); diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h index a57aeea5c0..e2420b4623 100644 --- a/editor/plugins/script_editor_plugin.h +++ b/editor/plugins/script_editor_plugin.h @@ -155,6 +155,8 @@ public: virtual void tag_saved_version() = 0; virtual void reload(bool p_soft) {} virtual Array get_breakpoints() = 0; + virtual void set_breakpoint(int p_line, bool p_enabled) = 0; + virtual void clear_breakpoints() = 0; virtual void add_callback(const String &p_function, PackedStringArray p_args) = 0; virtual void update_settings() = 0; virtual void set_debugger_active(bool p_active) = 0; @@ -374,6 +376,8 @@ class ScriptEditor : public PanelContainer { void _breaked(bool p_breaked, bool p_can_debug); void _update_window_menu(); void _script_created(Ref<Script> p_script); + void _set_breakpoint(REF p_scrpt, int p_line, bool p_enabled); + void _clear_breakpoints(); ScriptEditorBase *_get_current_editor() const; Array _get_open_script_editors() const; diff --git a/editor/plugins/script_text_editor.cpp b/editor/plugins/script_text_editor.cpp index b7d5403919..c44760807f 100644 --- a/editor/plugins/script_text_editor.cpp +++ b/editor/plugins/script_text_editor.cpp @@ -1370,6 +1370,14 @@ Array ScriptTextEditor::get_breakpoints() { return code_editor->get_text_editor()->get_breakpointed_lines(); } +void ScriptTextEditor::set_breakpoint(int p_line, bool p_enabled) { + code_editor->get_text_editor()->set_line_as_breakpoint(p_line, p_enabled); +} + +void ScriptTextEditor::clear_breakpoints() { + code_editor->get_text_editor()->clear_breakpointed_lines(); +} + void ScriptTextEditor::set_tooltip_request_func(String p_method, Object *p_obj) { code_editor->get_text_editor()->set_tooltip_request_func(p_obj, p_method, this); } diff --git a/editor/plugins/script_text_editor.h b/editor/plugins/script_text_editor.h index 1ca6f56ea1..4208d67f17 100644 --- a/editor/plugins/script_text_editor.h +++ b/editor/plugins/script_text_editor.h @@ -225,6 +225,8 @@ public: virtual void reload(bool p_soft) override; virtual Array get_breakpoints() override; + virtual void set_breakpoint(int p_line, bool p_enabled) override; + virtual void clear_breakpoints() override; virtual void add_callback(const String &p_function, PackedStringArray p_args) override; virtual void update_settings() override; diff --git a/editor/plugins/text_editor.h b/editor/plugins/text_editor.h index 839e1c5f7a..6bf0042393 100644 --- a/editor/plugins/text_editor.h +++ b/editor/plugins/text_editor.h @@ -121,6 +121,8 @@ public: virtual void set_edit_state(const Variant &p_state) override; virtual Vector<String> get_functions() override; virtual Array get_breakpoints() override; + virtual void set_breakpoint(int p_line, bool p_enabled) override{}; + virtual void clear_breakpoints() override{}; virtual void goto_line(int p_line, bool p_with_error = false) override; void goto_line_selection(int p_line, int p_begin, int p_end); virtual void set_executing_line(int p_line) override; diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index 6f76ca05dc..a0cf41d5a3 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -132,6 +132,76 @@ static GDScriptParser::DataType make_builtin_meta_type(Variant::Type p_type) { return type; } +bool GDScriptAnalyzer::has_member_name_conflict_in_script_class(const StringName &p_member_name, const GDScriptParser::ClassNode *p_class) { + if (p_class->members_indices.has(p_member_name)) { + int index = p_class->members_indices[p_member_name]; + const GDScriptParser::ClassNode::Member *member = &p_class->members[index]; + + if (member->type == GDScriptParser::ClassNode::Member::VARIABLE || + member->type == GDScriptParser::ClassNode::Member::CONSTANT || + member->type == GDScriptParser::ClassNode::Member::ENUM || + member->type == GDScriptParser::ClassNode::Member::ENUM_VALUE || + member->type == GDScriptParser::ClassNode::Member::CLASS || + member->type == GDScriptParser::ClassNode::Member::SIGNAL) { + return true; + } + } + + return false; +} + +bool GDScriptAnalyzer::has_member_name_conflict_in_native_type(const StringName &p_member_name, const StringName &p_native_type_string) { + if (ClassDB::has_signal(p_native_type_string, p_member_name)) { + return true; + } + if (ClassDB::has_property(p_native_type_string, p_member_name)) { + return true; + } + if (ClassDB::has_integer_constant(p_native_type_string, p_member_name)) { + return true; + } + + return false; +} + +Error GDScriptAnalyzer::check_native_member_name_conflict(const StringName &p_member_name, const GDScriptParser::Node *p_member_node, const StringName &p_native_type_string) { + if (has_member_name_conflict_in_native_type(p_member_name, p_native_type_string)) { + push_error(vformat(R"(Member "%s" redefined (original in native class '%s'))", p_member_name, p_native_type_string), p_member_node); + return ERR_PARSE_ERROR; + } + + if (class_exists(p_member_name)) { + push_error(vformat(R"(The class "%s" shadows a native class.)", p_member_name), p_member_node); + return ERR_PARSE_ERROR; + } + + return OK; +} + +Error GDScriptAnalyzer::check_class_member_name_conflict(const GDScriptParser::ClassNode *p_class_node, const StringName &p_member_name, const GDScriptParser::Node *p_member_node) { + const GDScriptParser::DataType *current_data_type = &p_class_node->base_type; + while (current_data_type && current_data_type->kind == GDScriptParser::DataType::Kind::CLASS) { + GDScriptParser::ClassNode *current_class_node = current_data_type->class_type; + if (has_member_name_conflict_in_script_class(p_member_name, current_class_node)) { + push_error(vformat(R"(The member "%s" already exists in a parent class.)", p_member_name), + p_member_node); + return ERR_PARSE_ERROR; + } + current_data_type = ¤t_class_node->base_type; + } + + if (current_data_type && current_data_type->kind == GDScriptParser::DataType::Kind::NATIVE) { + if (current_data_type->native_type != StringName("")) { + return check_native_member_name_conflict( + p_member_name, + p_member_node, + current_data_type->native_type); + } + } + + return OK; +} + Error GDScriptAnalyzer::resolve_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive) { if (p_class->base_type.is_set()) { // Already resolved @@ -525,6 +595,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas switch (member.type) { case GDScriptParser::ClassNode::Member::VARIABLE: { + check_class_member_name_conflict(p_class, member.variable->identifier->name, member.variable); + GDScriptParser::DataType datatype; datatype.kind = GDScriptParser::DataType::VARIANT; datatype.type_source = GDScriptParser::DataType::UNDETECTED; @@ -598,6 +670,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas } } break; case GDScriptParser::ClassNode::Member::CONSTANT: { + check_class_member_name_conflict(p_class, member.constant->identifier->name, member.constant); + reduce_expression(member.constant->initializer); GDScriptParser::DataType specified_type; @@ -647,6 +721,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas } } break; case GDScriptParser::ClassNode::Member::SIGNAL: { + check_class_member_name_conflict(p_class, member.signal->identifier->name, member.signal); + for (int j = 0; j < member.signal->parameters.size(); j++) { GDScriptParser::DataType signal_type = resolve_datatype(member.signal->parameters[j]->datatype_specifier); signal_type.is_meta_type = false; @@ -666,6 +742,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas } } break; case GDScriptParser::ClassNode::Member::ENUM: { + check_class_member_name_conflict(p_class, member.m_enum->identifier->name, member.m_enum); + GDScriptParser::DataType enum_type; enum_type.type_source = GDScriptParser::DataType::ANNOTATED_EXPLICIT; enum_type.kind = GDScriptParser::DataType::ENUM; @@ -717,6 +795,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas break; case GDScriptParser::ClassNode::Member::ENUM_VALUE: { if (member.enum_value.custom_value) { + check_class_member_name_conflict(p_class, member.enum_value.identifier->name, member.enum_value.custom_value); + current_enum = member.enum_value.parent_enum; reduce_expression(member.enum_value.custom_value); current_enum = nullptr; @@ -730,6 +810,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas member.enum_value.resolved = true; } } else { + check_class_member_name_conflict(p_class, member.enum_value.identifier->name, member.enum_value.parent_enum); + if (member.enum_value.index > 0) { member.enum_value.value = member.enum_value.parent_enum->values[member.enum_value.index - 1].value + 1; } else { @@ -742,7 +824,8 @@ void GDScriptAnalyzer::resolve_class_interface(GDScriptParser::ClassNode *p_clas p_class->members.write[i].enum_value = member.enum_value; } break; case GDScriptParser::ClassNode::Member::CLASS: - break; // Done later. + check_class_member_name_conflict(p_class, member.m_class->identifier->name, member.m_class); + break; case GDScriptParser::ClassNode::Member::UNDEFINED: ERR_PRINT("Trying to resolve undefined member."); break; diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index 32bf049fa1..2e17e15452 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -44,6 +44,12 @@ class GDScriptAnalyzer { const GDScriptParser::EnumNode *current_enum = nullptr; List<const GDScriptParser::LambdaNode *> lambda_stack; + // Tests for detecting invalid overloading of script members + static _FORCE_INLINE_ bool has_member_name_conflict_in_script_class(const StringName &p_name, const GDScriptParser::ClassNode *p_current_class_node); + static _FORCE_INLINE_ bool has_member_name_conflict_in_native_type(const StringName &p_name, const StringName &p_native_type_string); + Error check_native_member_name_conflict(const StringName &p_member_name, const GDScriptParser::Node *p_member_node, const StringName &p_native_type_string); + Error check_class_member_name_conflict(const GDScriptParser::ClassNode *p_class_node, const StringName &p_member_name, const GDScriptParser::Node *p_member_node); + Error resolve_inheritance(GDScriptParser::ClassNode *p_class, bool p_recursive = true); GDScriptParser::DataType resolve_datatype(GDScriptParser::TypeNode *p_type); diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index ddf4f281b4..e5cbcc545f 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -2186,6 +2186,13 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar p_script->tool = parser->is_tool(); p_script->name = p_class->identifier ? p_class->identifier->name : ""; + if (p_script->name != "") { + if (ClassDB::class_exists(p_script->name) && ClassDB::is_class_exposed(p_script->name)) { + _set_error("The class '" + p_script->name + "' shadows a native class", p_class); + return ERR_ALREADY_EXISTS; + } + } + Ref<GDScriptNativeClass> native; GDScriptDataType base_type = _gdtype_from_datatype(p_class->base_type); @@ -2337,28 +2344,6 @@ Error GDScriptCompiler::_parse_class_level(GDScript *p_script, const GDScriptPar const GDScriptParser::SignalNode *signal = member.signal; StringName name = signal->identifier->name; - GDScript *c = p_script; - - while (c) { - if (c->_signals.has(name)) { - _set_error("Signal '" + name + "' redefined (in current or parent class)", p_class); - return ERR_ALREADY_EXISTS; - } - - if (c->base.is_valid()) { - c = c->base.ptr(); - } else { - c = nullptr; - } - } - - if (native.is_valid()) { - if (ClassDB::has_signal(native->get_name(), name)) { - _set_error("Signal '" + name + "' redefined (original in native class '" + String(native->get_name()) + "')", p_class); - return ERR_ALREADY_EXISTS; - } - } - Vector<StringName> parameters_names; parameters_names.resize(signal->parameters.size()); for (int j = 0; j < signal->parameters.size(); j++) { diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs index 213f84ad73..c8dd0ac9ce 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Mathf.cs @@ -110,19 +110,6 @@ namespace Godot } /// <summary> - /// Converts a 2D point expressed in the cartesian coordinate - /// system (X and Y axis) to the polar coordinate system - /// (a distance from the origin and an angle). - /// </summary> - /// <param name="x">The input X coordinate.</param> - /// <param name="y">The input Y coordinate.</param> - /// <returns>A <see cref="Vector2"/> with X representing the distance and Y representing the angle.</returns> - public static Vector2 Cartesian2Polar(real_t x, real_t y) - { - return new Vector2(Sqrt(x * x + y * y), Atan2(y, x)); - } - - /// <summary> /// Rounds `s` upward (towards positive infinity). /// </summary> /// <param name="s">The number to ceil.</param> @@ -436,19 +423,6 @@ namespace Godot } /// <summary> - /// Converts a 2D point expressed in the polar coordinate - /// system (a distance from the origin `r` and an angle `th`) - /// to the cartesian coordinate system (X and Y axis). - /// </summary> - /// <param name="r">The distance from the origin.</param> - /// <param name="th">The angle of the point.</param> - /// <returns>A <see cref="Vector2"/> representing the cartesian coordinate.</returns> - public static Vector2 Polar2Cartesian(real_t r, real_t th) - { - return new Vector2(r * Cos(th), r * Sin(th)); - } - - /// <summary> /// Performs a canonical Modulus operation, where the output is on the range `[0, b)`. /// </summary> /// <param name="a">The dividend, the primary input.</param> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs index 8bb5e90a68..b4c4ddabb9 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Vector2.cs @@ -603,6 +603,17 @@ namespace Godot y = v.y; } + /// <summary> + /// Creates a unit Vector2 rotated to the given angle. This is equivalent to doing + /// `Vector2(Mathf.Cos(angle), Mathf.Sin(angle))` or `Vector2.Right.Rotated(angle)`. + /// </summary> + /// <param name="angle">Angle of the vector, in radians.</param> + /// <returns>The resulting vector.</returns> + public static Vector2 FromAngle(real_t angle) + { + return new Vector2(Mathf.Cos(angle), Mathf.Sin(angle)); + } + public static Vector2 operator +(Vector2 left, Vector2 right) { left.x += right.x; diff --git a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml index 195d766c1d..55d0b392fa 100644 --- a/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml +++ b/modules/visual_script/doc_classes/VisualScriptBuiltinFunc.xml @@ -138,81 +138,75 @@ <constant name="MATH_DB2LINEAR" value="40" enum="BuiltinFunc"> Convert the input from decibel volume to linear volume. </constant> - <constant name="MATH_POLAR2CARTESIAN" value="41" enum="BuiltinFunc"> - Converts a 2D point expressed in the polar coordinate system (a distance from the origin [code]r[/code] and an angle [code]th[/code]) to the cartesian coordinate system (X and Y axis). + <constant name="MATH_WRAP" value="41" enum="BuiltinFunc"> </constant> - <constant name="MATH_CARTESIAN2POLAR" value="42" enum="BuiltinFunc"> - Converts a 2D point expressed in the cartesian coordinate system (X and Y axis) to the polar coordinate system (a distance from the origin and an angle). + <constant name="MATH_WRAPF" value="42" enum="BuiltinFunc"> </constant> - <constant name="MATH_WRAP" value="43" enum="BuiltinFunc"> - </constant> - <constant name="MATH_WRAPF" value="44" enum="BuiltinFunc"> - </constant> - <constant name="LOGIC_MAX" value="45" enum="BuiltinFunc"> + <constant name="LOGIC_MAX" value="43" enum="BuiltinFunc"> Return the greater of the two numbers, also known as their maximum. </constant> - <constant name="LOGIC_MIN" value="46" enum="BuiltinFunc"> + <constant name="LOGIC_MIN" value="44" enum="BuiltinFunc"> Return the lesser of the two numbers, also known as their minimum. </constant> - <constant name="LOGIC_CLAMP" value="47" enum="BuiltinFunc"> + <constant name="LOGIC_CLAMP" value="45" enum="BuiltinFunc"> Return the input clamped inside the given range, ensuring the result is never outside it. Equivalent to [code]min(max(input, range_low), range_high)[/code]. </constant> - <constant name="LOGIC_NEAREST_PO2" value="48" enum="BuiltinFunc"> + <constant name="LOGIC_NEAREST_PO2" value="46" enum="BuiltinFunc"> Return the nearest power of 2 to the input. </constant> - <constant name="OBJ_WEAKREF" value="49" enum="BuiltinFunc"> + <constant name="OBJ_WEAKREF" value="47" enum="BuiltinFunc"> Create a [WeakRef] from the input. </constant> - <constant name="TYPE_CONVERT" value="50" enum="BuiltinFunc"> + <constant name="TYPE_CONVERT" value="48" enum="BuiltinFunc"> Convert between types. </constant> - <constant name="TYPE_OF" value="51" enum="BuiltinFunc"> + <constant name="TYPE_OF" value="49" enum="BuiltinFunc"> Return the type of the input as an integer. Check [enum Variant.Type] for the integers that might be returned. </constant> - <constant name="TYPE_EXISTS" value="52" enum="BuiltinFunc"> + <constant name="TYPE_EXISTS" value="50" enum="BuiltinFunc"> Checks if a type is registered in the [ClassDB]. </constant> - <constant name="TEXT_CHAR" value="53" enum="BuiltinFunc"> + <constant name="TEXT_CHAR" value="51" enum="BuiltinFunc"> Return a character with the given ascii value. </constant> - <constant name="TEXT_STR" value="54" enum="BuiltinFunc"> + <constant name="TEXT_STR" value="52" enum="BuiltinFunc"> Convert the input to a string. </constant> - <constant name="TEXT_PRINT" value="55" enum="BuiltinFunc"> + <constant name="TEXT_PRINT" value="53" enum="BuiltinFunc"> Print the given string to the output window. </constant> - <constant name="TEXT_PRINTERR" value="56" enum="BuiltinFunc"> + <constant name="TEXT_PRINTERR" value="54" enum="BuiltinFunc"> Print the given string to the standard error output. </constant> - <constant name="TEXT_PRINTRAW" value="57" enum="BuiltinFunc"> + <constant name="TEXT_PRINTRAW" value="55" enum="BuiltinFunc"> Print the given string to the standard output, without adding a newline. </constant> - <constant name="VAR_TO_STR" value="58" enum="BuiltinFunc"> + <constant name="VAR_TO_STR" value="56" enum="BuiltinFunc"> Serialize a [Variant] to a string. </constant> - <constant name="STR_TO_VAR" value="59" enum="BuiltinFunc"> + <constant name="STR_TO_VAR" value="57" enum="BuiltinFunc"> Deserialize a [Variant] from a string serialized using [constant VAR_TO_STR]. </constant> - <constant name="VAR_TO_BYTES" value="60" enum="BuiltinFunc"> + <constant name="VAR_TO_BYTES" value="58" enum="BuiltinFunc"> Serialize a [Variant] to a [PackedByteArray]. </constant> - <constant name="BYTES_TO_VAR" value="61" enum="BuiltinFunc"> + <constant name="BYTES_TO_VAR" value="59" enum="BuiltinFunc"> Deserialize a [Variant] from a [PackedByteArray] serialized using [constant VAR_TO_BYTES]. </constant> - <constant name="MATH_SMOOTHSTEP" value="62" enum="BuiltinFunc"> + <constant name="MATH_SMOOTHSTEP" value="60" enum="BuiltinFunc"> Return a number smoothly interpolated between the first two inputs, based on the third input. Similar to [constant MATH_LERP], but interpolates faster at the beginning and slower at the end. Using Hermite interpolation formula: [codeblock] var t = clamp((weight - from) / (to - from), 0.0, 1.0) return t * t * (3.0 - 2.0 * t) [/codeblock] </constant> - <constant name="MATH_POSMOD" value="63" enum="BuiltinFunc"> + <constant name="MATH_POSMOD" value="61" enum="BuiltinFunc"> </constant> - <constant name="MATH_LERP_ANGLE" value="64" enum="BuiltinFunc"> + <constant name="MATH_LERP_ANGLE" value="62" enum="BuiltinFunc"> </constant> - <constant name="TEXT_ORD" value="65" enum="BuiltinFunc"> + <constant name="TEXT_ORD" value="63" enum="BuiltinFunc"> </constant> - <constant name="FUNC_MAX" value="66" enum="BuiltinFunc"> + <constant name="FUNC_MAX" value="64" enum="BuiltinFunc"> Represents the size of the [enum BuiltinFunc] enum. </constant> </constants> diff --git a/modules/visual_script/visual_script_builtin_funcs.cpp b/modules/visual_script/visual_script_builtin_funcs.cpp index c61c3ae272..2bd7220d15 100644 --- a/modules/visual_script/visual_script_builtin_funcs.cpp +++ b/modules/visual_script/visual_script_builtin_funcs.cpp @@ -79,8 +79,6 @@ const char *VisualScriptBuiltinFunc::func_name[VisualScriptBuiltinFunc::FUNC_MAX "rad2deg", "linear2db", "db2linear", - "polar2cartesian", - "cartesian2polar", "wrapi", "wrapf", "max", @@ -194,8 +192,6 @@ int VisualScriptBuiltinFunc::get_func_argument_count(BuiltinFunc p_func) { case MATH_SNAPPED: case MATH_RANDF_RANGE: case MATH_RANDI_RANGE: - case MATH_POLAR2CARTESIAN: - case MATH_CARTESIAN2POLAR: case LOGIC_MAX: case LOGIC_MIN: case TYPE_CONVERT: @@ -381,20 +377,6 @@ PropertyInfo VisualScriptBuiltinFunc::get_input_value_port_info(int p_idx) const case MATH_DB2LINEAR: { return PropertyInfo(Variant::FLOAT, "db"); } break; - case MATH_POLAR2CARTESIAN: { - if (p_idx == 0) { - return PropertyInfo(Variant::FLOAT, "r"); - } else { - return PropertyInfo(Variant::FLOAT, "th"); - } - } break; - case MATH_CARTESIAN2POLAR: { - if (p_idx == 0) { - return PropertyInfo(Variant::FLOAT, "x"); - } else { - return PropertyInfo(Variant::FLOAT, "y"); - } - } break; case MATH_WRAP: { if (p_idx == 0) { return PropertyInfo(Variant::INT, "value"); @@ -553,10 +535,6 @@ PropertyInfo VisualScriptBuiltinFunc::get_output_value_port_info(int p_idx) cons case MATH_DB2LINEAR: { t = Variant::FLOAT; } break; - case MATH_POLAR2CARTESIAN: - case MATH_CARTESIAN2POLAR: { - t = Variant::VECTOR2; - } break; case MATH_WRAP: { t = Variant::INT; } break; @@ -874,20 +852,6 @@ void VisualScriptBuiltinFunc::exec_func(BuiltinFunc p_func, const Variant **p_in VALIDATE_ARG_NUM(0); *r_return = Math::db2linear((double)*p_inputs[0]); } break; - case VisualScriptBuiltinFunc::MATH_POLAR2CARTESIAN: { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - double r = *p_inputs[0]; - double th = *p_inputs[1]; - *r_return = Vector2(r * Math::cos(th), r * Math::sin(th)); - } break; - case VisualScriptBuiltinFunc::MATH_CARTESIAN2POLAR: { - VALIDATE_ARG_NUM(0); - VALIDATE_ARG_NUM(1); - double x = *p_inputs[0]; - double y = *p_inputs[1]; - *r_return = Vector2(Math::sqrt(x * x + y * y), Math::atan2(y, x)); - } break; case VisualScriptBuiltinFunc::MATH_WRAP: { VALIDATE_ARG_NUM(0); VALIDATE_ARG_NUM(1); @@ -1229,8 +1193,6 @@ void VisualScriptBuiltinFunc::_bind_methods() { BIND_ENUM_CONSTANT(MATH_RAD2DEG); BIND_ENUM_CONSTANT(MATH_LINEAR2DB); BIND_ENUM_CONSTANT(MATH_DB2LINEAR); - BIND_ENUM_CONSTANT(MATH_POLAR2CARTESIAN); - BIND_ENUM_CONSTANT(MATH_CARTESIAN2POLAR); BIND_ENUM_CONSTANT(MATH_WRAP); BIND_ENUM_CONSTANT(MATH_WRAPF); BIND_ENUM_CONSTANT(LOGIC_MAX); @@ -1320,8 +1282,6 @@ void register_visual_script_builtin_func_node() { VisualScriptLanguage::singleton->add_register_func("functions/built_in/rad2deg", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_RAD2DEG>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/linear2db", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_LINEAR2DB>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/db2linear", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_DB2LINEAR>); - VisualScriptLanguage::singleton->add_register_func("functions/built_in/polar2cartesian", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_POLAR2CARTESIAN>); - VisualScriptLanguage::singleton->add_register_func("functions/built_in/cartesian2polar", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_CARTESIAN2POLAR>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/wrapi", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_WRAP>); VisualScriptLanguage::singleton->add_register_func("functions/built_in/wrapf", create_builtin_func_node<VisualScriptBuiltinFunc::MATH_WRAPF>); diff --git a/modules/visual_script/visual_script_builtin_funcs.h b/modules/visual_script/visual_script_builtin_funcs.h index f59a7a0f0c..26abc1e479 100644 --- a/modules/visual_script/visual_script_builtin_funcs.h +++ b/modules/visual_script/visual_script_builtin_funcs.h @@ -79,8 +79,6 @@ public: MATH_RAD2DEG, MATH_LINEAR2DB, MATH_DB2LINEAR, - MATH_POLAR2CARTESIAN, - MATH_CARTESIAN2POLAR, MATH_WRAP, MATH_WRAPF, LOGIC_MAX, diff --git a/modules/visual_script/visual_script_editor.h b/modules/visual_script/visual_script_editor.h index 7dfb4fa270..ab32aae7aa 100644 --- a/modules/visual_script/visual_script_editor.h +++ b/modules/visual_script/visual_script_editor.h @@ -311,6 +311,8 @@ public: virtual void tag_saved_version() override; virtual void reload(bool p_soft) override; virtual Array get_breakpoints() override; + virtual void set_breakpoint(int p_line, bool p_enable) override{}; + virtual void clear_breakpoints() override{}; virtual void add_callback(const String &p_function, PackedStringArray p_args) override; virtual void update_settings() override; virtual bool show_members_overview() override; diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index 5c1c3281a6..6b4a326ba7 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -977,6 +977,11 @@ void EditorExportPlatformAndroid::_fix_manifest(const Ref<EditorExportPreset> &p Vector<int> feature_versions; if (xr_mode_index == 1 /* XRMode.OVR */) { + // Set degrees of freedom + feature_names.push_back("android.hardware.vr.headtracking"); + feature_required_list.push_back(true); + feature_versions.push_back(1); + // Check for hand tracking int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required if (hand_tracking_index > 0) { diff --git a/platform/android/export/gradle_export_util.cpp b/platform/android/export/gradle_export_util.cpp index b9e28a7937..6fbdf73cd0 100644 --- a/platform/android/export/gradle_export_util.cpp +++ b/platform/android/export/gradle_export_util.cpp @@ -197,6 +197,8 @@ String _get_xr_features_tag(const Ref<EditorExportPreset> &p_preset) { String manifest_xr_features; bool uses_xr = (int)(p_preset->get("xr_features/xr_mode")) == 1; if (uses_xr) { + manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"android.hardware.vr.headtracking\" android:required=\"true\" android:version=\"1\" />\n"; + int hand_tracking_index = p_preset->get("xr_features/hand_tracking"); // 0: none, 1: optional, 2: required if (hand_tracking_index == 1) { manifest_xr_features += " <uses-feature tools:node=\"replace\" android:name=\"oculus.software.handtracking\" android:required=\"false\" />\n"; @@ -228,7 +230,9 @@ String _get_activity_tag(const Ref<EditorExportPreset> &p_preset) { "tools:replace=\"android:screenOrientation\" " "android:screenOrientation=\"%s\">\n", orientation); - if (!uses_xr) { + if (uses_xr) { + manifest_activity_text += " <meta-data tools:node=\"replace\" android:name=\"com.oculus.vr.focusaware\" android:value=\"true\" />\n"; + } else { manifest_activity_text += " <meta-data tools:node=\"remove\" android:name=\"com.oculus.vr.focusaware\" />\n"; } manifest_activity_text += " </activity>\n"; diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml index 00e01884cf..d7bf6cef30 100644 --- a/platform/android/java/app/AndroidManifest.xml +++ b/platform/android/java/app/AndroidManifest.xml @@ -49,6 +49,10 @@ <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> + + <!-- Enable access to OpenXR on Oculus mobile devices, no-op on other Android + platforms. --> + <category android:name="com.oculus.intent.category.VR" /> </intent-filter> </activity> diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.java b/platform/android/java/lib/src/org/godotengine/godot/Godot.java index 896b169953..70bc73b9ad 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.java +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.java @@ -295,7 +295,11 @@ public class Godot extends Fragment implements SensorEventListener, IDownloaderC for (GodotPlugin plugin : pluginRegistry.getAllPlugins()) { View pluginView = plugin.onMainCreate(activity); if (pluginView != null) { - containerLayout.addView(pluginView); + if (plugin.shouldBeOnTop()) { + containerLayout.addView(pluginView); + } else { + containerLayout.addView(pluginView, 0); + } } } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java index 2dc8359615..4536c21ed3 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java +++ b/platform/android/java/lib/src/org/godotengine/godot/plugin/GodotPlugin.java @@ -191,6 +191,9 @@ public abstract class GodotPlugin { * The plugin can return a non-null {@link View} layout in order to add it to the Godot view * hierarchy. * + * Use shouldBeOnTop() to set whether the plugin's {@link View} should be added on top or behind + * the main Godot view. + * * @see Activity#onCreate(Bundle) * @return the plugin's view to be included; null if no views should be included. */ @@ -311,6 +314,17 @@ public abstract class GodotPlugin { } /** + * Returns whether the plugin's {@link View} returned in onMainCreate() should be placed on + * top of the main Godot view. + * + * Returning false causes the plugin's {@link View} to be placed behind, which can be useful + * when used with transparency in order to let the Godot view handle inputs. + */ + public boolean shouldBeOnTop() { + return true; + } + + /** * Runs the specified action on the UI thread. If the current thread is the UI * thread, then the action is executed immediately. If the current thread is * not the UI thread, the action is posted to the event queue of the UI thread. diff --git a/scene/2d/physics_body_2d.cpp b/scene/2d/physics_body_2d.cpp index 8d8b187445..3518d434c3 100644 --- a/scene/2d/physics_body_2d.cpp +++ b/scene/2d/physics_body_2d.cpp @@ -259,15 +259,17 @@ bool StaticBody2D::is_sync_to_physics_enabled() const { return sync_to_physics; } -void StaticBody2D::_direct_state_changed(Object *p_state) { +void StaticBody2D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState2D *p_state) { + StaticBody2D *body = (StaticBody2D *)p_instance; + body->_body_state_changed(p_state); +} + +void StaticBody2D::_body_state_changed(PhysicsDirectBodyState2D *p_state) { if (!sync_to_physics) { return; } - PhysicsDirectBodyState2D *state = Object::cast_to<PhysicsDirectBodyState2D>(p_state); - ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState2D object as argument"); - - last_valid_transform = state->get_transform(); + last_valid_transform = p_state->get_transform(); set_notify_local_transform(false); set_global_transform(last_valid_transform); set_notify_local_transform(true); @@ -293,7 +295,7 @@ void StaticBody2D::_notification(int p_what) { // Used by sync to physics, send the new transform to the physics... Transform2D new_transform = get_global_transform(); - real_t delta_time = get_physics_process_delta_time(); + double delta_time = get_physics_process_delta_time(); new_transform.translate(constant_linear_velocity * delta_time); new_transform.set_rotation(new_transform.get_rotation() + constant_angular_velocity * delta_time); @@ -316,7 +318,7 @@ void StaticBody2D::_notification(int p_what) { Transform2D new_transform = get_global_transform(); - real_t delta_time = get_physics_process_delta_time(); + double delta_time = get_physics_process_delta_time(); new_transform.translate(constant_linear_velocity * delta_time); new_transform.set_rotation(new_transform.get_rotation() + constant_angular_velocity * delta_time); @@ -379,11 +381,11 @@ void StaticBody2D::_update_kinematic_motion() { #endif if (kinematic_motion && sync_to_physics) { - PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &StaticBody2D::_direct_state_changed)); + PhysicsServer2D::get_singleton()->body_set_state_sync_callback(get_rid(), this, _body_state_changed_callback); set_only_update_transform_changes(true); set_notify_local_transform(true); } else { - PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), Callable()); + PhysicsServer2D::get_singleton()->body_set_state_sync_callback(get_rid(), nullptr, nullptr); set_only_update_transform_changes(false); set_notify_local_transform(false); } @@ -511,26 +513,26 @@ struct _RigidBody2DInOut { int local_shape = 0; }; -void RigidBody2D::_direct_state_changed(Object *p_state) { -#ifdef DEBUG_ENABLED - state = Object::cast_to<PhysicsDirectBodyState2D>(p_state); - ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState2D object as argument"); -#else - state = (PhysicsDirectBodyState2D *)p_state; //trust it -#endif +void RigidBody2D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState2D *p_state) { + RigidBody2D *body = (RigidBody2D *)p_instance; + body->_body_state_changed(p_state); +} +void RigidBody2D::_body_state_changed(PhysicsDirectBodyState2D *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(p_state->get_transform()); } - linear_velocity = state->get_linear_velocity(); - angular_velocity = state->get_angular_velocity(); - if (sleeping != state->is_sleeping()) { - sleeping = state->is_sleeping(); + + linear_velocity = p_state->get_linear_velocity(); + angular_velocity = p_state->get_angular_velocity(); + + if (sleeping != p_state->is_sleeping()) { + sleeping = p_state->is_sleeping(); emit_signal(SceneStringNames::get_singleton()->sleeping_state_changed); } - GDVIRTUAL_CALL(_integrate_forces, state); + GDVIRTUAL_CALL(_integrate_forces, p_state); set_block_transform_notify(false); // want it back @@ -546,20 +548,18 @@ void RigidBody2D::_direct_state_changed(Object *p_state) { } } - _RigidBody2DInOut *toadd = (_RigidBody2DInOut *)alloca(state->get_contact_count() * sizeof(_RigidBody2DInOut)); + _RigidBody2DInOut *toadd = (_RigidBody2DInOut *)alloca(p_state->get_contact_count() * sizeof(_RigidBody2DInOut)); int toadd_count = 0; //state->get_contact_count(); RigidBody2D_RemoveAction *toremove = (RigidBody2D_RemoveAction *)alloca(rc * sizeof(RigidBody2D_RemoveAction)); int toremove_count = 0; //put the ones to add - for (int i = 0; i < state->get_contact_count(); i++) { - RID rid = state->get_contact_collider(i); - ObjectID obj = state->get_contact_collider_id(i); - int local_shape = state->get_contact_local_shape(i); - int shape = state->get_contact_collider_shape(i); - - //bool found=false; + for (int i = 0; i < p_state->get_contact_count(); i++) { + RID rid = p_state->get_contact_collider(i); + ObjectID obj = p_state->get_contact_collider_id(i); + int local_shape = p_state->get_contact_local_shape(i); + int shape = p_state->get_contact_collider_shape(i); Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(obj); if (!E) { @@ -612,8 +612,6 @@ void RigidBody2D::_direct_state_changed(Object *p_state) { contact_monitor->locked = false; } - - state = nullptr; } void RigidBody2D::set_mode(Mode p_mode) { @@ -709,25 +707,15 @@ real_t RigidBody2D::get_angular_damp() const { } void RigidBody2D::set_axis_velocity(const Vector2 &p_axis) { - Vector2 v = state ? state->get_linear_velocity() : linear_velocity; Vector2 axis = p_axis.normalized(); - v -= axis * axis.dot(v); - v += p_axis; - if (state) { - set_linear_velocity(v); - } else { - PhysicsServer2D::get_singleton()->body_set_axis_velocity(get_rid(), p_axis); - linear_velocity = v; - } + linear_velocity -= axis * axis.dot(linear_velocity); + linear_velocity += p_axis; + PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, linear_velocity); } void RigidBody2D::set_linear_velocity(const Vector2 &p_velocity) { linear_velocity = p_velocity; - if (state) { - state->set_linear_velocity(linear_velocity); - } else { - PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, linear_velocity); - } + PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, linear_velocity); } Vector2 RigidBody2D::get_linear_velocity() const { @@ -736,11 +724,7 @@ Vector2 RigidBody2D::get_linear_velocity() const { void RigidBody2D::set_angular_velocity(real_t p_velocity) { angular_velocity = p_velocity; - if (state) { - state->set_angular_velocity(angular_velocity); - } else { - PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY, angular_velocity); - } + PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY, angular_velocity); } real_t RigidBody2D::get_angular_velocity() const { @@ -1019,7 +1003,7 @@ void RigidBody2D::_bind_methods() { RigidBody2D::RigidBody2D() : PhysicsBody2D(PhysicsServer2D::BODY_MODE_DYNAMIC) { - PhysicsServer2D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &RigidBody2D::_direct_state_changed)); + PhysicsServer2D::get_singleton()->body_set_state_sync_callback(get_rid(), this, _body_state_changed_callback); } RigidBody2D::~RigidBody2D() { @@ -1045,14 +1029,19 @@ void RigidBody2D::_reload_physics_characteristics() { bool CharacterBody2D::move_and_slide() { // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky. - float delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); + double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); Vector2 current_platform_velocity = platform_velocity; if ((on_floor || on_wall) && platform_rid.is_valid()) { - bool excluded = (moving_platform_ignore_layers & platform_layer) != 0; + bool excluded = false; + if (on_floor) { + excluded = (moving_platform_floor_layers & platform_layer) == 0; + } else if (on_wall) { + excluded = (moving_platform_wall_layers & platform_layer) == 0; + } if (!excluded) { - // This approach makes sure there is less delay between the actual body velocity and the one we saved. + //this approach makes sure there is less delay between the actual body velocity and the one we saved PhysicsDirectBodyState2D *bs = PhysicsServer2D::get_singleton()->body_get_direct_state(platform_rid); if (bs) { Transform2D gt = get_global_transform(); @@ -1095,7 +1084,7 @@ bool CharacterBody2D::move_and_slide() { return motion_results.size() > 0; } -void CharacterBody2D::_move_and_slide_grounded(real_t p_delta, bool p_was_on_floor, const Vector2 &p_prev_platform_velocity) { +void CharacterBody2D::_move_and_slide_grounded(double p_delta, bool p_was_on_floor, const Vector2 &p_prev_platform_velocity) { Vector2 motion = linear_velocity * p_delta; Vector2 motion_slide_up = motion.slide(up_direction); @@ -1239,7 +1228,7 @@ void CharacterBody2D::_move_and_slide_grounded(real_t p_delta, bool p_was_on_flo } } -void CharacterBody2D::_move_and_slide_free(real_t p_delta) { +void CharacterBody2D::_move_and_slide_free(double p_delta) { Vector2 motion = linear_velocity * p_delta; platform_rid = RID(); @@ -1469,12 +1458,20 @@ void CharacterBody2D::set_slide_on_ceiling_enabled(bool p_enabled) { slide_on_ceiling = p_enabled; } -uint32_t CharacterBody2D::get_moving_platform_ignore_layers() const { - return moving_platform_ignore_layers; +uint32_t CharacterBody2D::get_moving_platform_floor_layers() const { + return moving_platform_floor_layers; +} + +void CharacterBody2D::set_moving_platform_floor_layers(uint32_t p_exclude_layers) { + moving_platform_floor_layers = p_exclude_layers; +} + +uint32_t CharacterBody2D::get_moving_platform_wall_layers() const { + return moving_platform_wall_layers; } -void CharacterBody2D::set_moving_platform_ignore_layers(uint32_t p_exclude_layers) { - moving_platform_ignore_layers = p_exclude_layers; +void CharacterBody2D::set_moving_platform_wall_layers(uint32_t p_exclude_layers) { + moving_platform_wall_layers = p_exclude_layers; } void CharacterBody2D::set_motion_mode(MotionMode p_mode) { @@ -1559,8 +1556,10 @@ void CharacterBody2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_slide_on_ceiling_enabled", "enabled"), &CharacterBody2D::set_slide_on_ceiling_enabled); ClassDB::bind_method(D_METHOD("is_slide_on_ceiling_enabled"), &CharacterBody2D::is_slide_on_ceiling_enabled); - ClassDB::bind_method(D_METHOD("set_moving_platform_ignore_layers", "exclude_layer"), &CharacterBody2D::set_moving_platform_ignore_layers); - ClassDB::bind_method(D_METHOD("get_moving_platform_ignore_layers"), &CharacterBody2D::get_moving_platform_ignore_layers); + ClassDB::bind_method(D_METHOD("set_moving_platform_floor_layers", "exclude_layer"), &CharacterBody2D::set_moving_platform_floor_layers); + ClassDB::bind_method(D_METHOD("get_moving_platform_floor_layers"), &CharacterBody2D::get_moving_platform_floor_layers); + ClassDB::bind_method(D_METHOD("set_moving_platform_wall_layers", "exclude_layer"), &CharacterBody2D::set_moving_platform_wall_layers); + ClassDB::bind_method(D_METHOD("get_moving_platform_wall_layers"), &CharacterBody2D::get_moving_platform_wall_layers); ClassDB::bind_method(D_METHOD("get_max_slides"), &CharacterBody2D::get_max_slides); ClassDB::bind_method(D_METHOD("set_max_slides", "max_slides"), &CharacterBody2D::set_max_slides); @@ -1602,7 +1601,8 @@ void CharacterBody2D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians"), "set_floor_max_angle", "get_floor_max_angle"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_snap_length", PROPERTY_HINT_RANGE, "0,1000,0.1"), "set_floor_snap_length", "get_floor_snap_length"); ADD_GROUP("Moving platform", "moving_platform"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_ignore_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_ignore_layers", "get_moving_platform_ignore_layers"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_floor_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_floor_layers", "get_moving_platform_floor_layers"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "moving_platform_wall_layers", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_moving_platform_wall_layers", "get_moving_platform_wall_layers"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin"); BIND_ENUM_CONSTANT(MOTION_MODE_GROUNDED); diff --git a/scene/2d/physics_body_2d.h b/scene/2d/physics_body_2d.h index 885f0ace05..1bf53ea53c 100644 --- a/scene/2d/physics_body_2d.h +++ b/scene/2d/physics_body_2d.h @@ -73,7 +73,8 @@ class StaticBody2D : public PhysicsBody2D { Transform2D last_valid_transform; - void _direct_state_changed(Object *p_state); + static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState2D *p_state); + void _body_state_changed(PhysicsDirectBodyState2D *p_state); protected: void _notification(int p_what); @@ -124,7 +125,6 @@ public: private: bool can_sleep = true; - PhysicsDirectBodyState2D *state = nullptr; Mode mode = MODE_DYNAMIC; real_t mass = 1.0; @@ -183,7 +183,9 @@ private: void _body_exit_tree(ObjectID p_id); void _body_inout(int p_status, const RID &p_body, ObjectID p_instance, int p_body_shape, int p_local_shape); - void _direct_state_changed(Object *p_state); + + static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState2D *p_state); + void _body_state_changed(PhysicsDirectBodyState2D *p_state); protected: void _notification(int p_what); @@ -310,7 +312,8 @@ private: float floor_snap_length = 0; real_t free_mode_min_slide_angle = Math::deg2rad((real_t)15.0); Vector2 up_direction = Vector2(0.0, -1.0); - uint32_t moving_platform_ignore_layers = 0; + uint32_t moving_platform_floor_layers = UINT32_MAX; + uint32_t moving_platform_wall_layers = 0; Vector2 linear_velocity; Vector2 floor_normal; @@ -350,14 +353,17 @@ private: real_t get_free_mode_min_slide_angle() const; void set_free_mode_min_slide_angle(real_t p_radians); - uint32_t get_moving_platform_ignore_layers() const; - void set_moving_platform_ignore_layers(const uint32_t p_exclude_layer); + uint32_t get_moving_platform_floor_layers() const; + void set_moving_platform_floor_layers(const uint32_t p_exclude_layer); + + uint32_t get_moving_platform_wall_layers() const; + void set_moving_platform_wall_layers(const uint32_t p_exclude_layer); void set_motion_mode(MotionMode p_mode); MotionMode get_motion_mode() const; - void _move_and_slide_free(real_t p_delta); - void _move_and_slide_grounded(real_t p_delta, bool p_was_on_floor, const Vector2 &p_prev_platform_velocity); + void _move_and_slide_free(double p_delta); + void _move_and_slide_grounded(double p_delta, bool p_was_on_floor, const Vector2 &p_prev_platform_velocity); Ref<KinematicCollision2D> _get_slide_collision(int p_bounce); Ref<KinematicCollision2D> _get_last_slide_collision(); diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index 06a4087b60..0356994cdb 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -283,18 +283,20 @@ bool StaticBody3D::is_sync_to_physics_enabled() const { return sync_to_physics; } -void StaticBody3D::_direct_state_changed(Object *p_state) { - PhysicsDirectBodyState3D *state = Object::cast_to<PhysicsDirectBodyState3D>(p_state); - ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument"); +void StaticBody3D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state) { + StaticBody3D *body = (StaticBody3D *)p_instance; + body->_body_state_changed(p_state); +} - linear_velocity = state->get_linear_velocity(); - angular_velocity = state->get_angular_velocity(); +void StaticBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) { + linear_velocity = p_state->get_linear_velocity(); + angular_velocity = p_state->get_angular_velocity(); if (!sync_to_physics) { return; } - last_valid_transform = state->get_transform(); + last_valid_transform = p_state->get_transform(); set_notify_local_transform(false); set_global_transform(last_valid_transform); set_notify_local_transform(true); @@ -347,7 +349,7 @@ void StaticBody3D::_notification(int p_what) { // Used by sync to physics, send the new transform to the physics... Transform3D new_transform = get_global_transform(); - real_t delta_time = get_physics_process_delta_time(); + double delta_time = get_physics_process_delta_time(); new_transform.origin += constant_linear_velocity * delta_time; real_t ang_vel = constant_angular_velocity.length(); @@ -378,7 +380,7 @@ void StaticBody3D::_notification(int p_what) { Transform3D new_transform = get_global_transform(); - real_t delta_time = get_physics_process_delta_time(); + double delta_time = get_physics_process_delta_time(); new_transform.origin += constant_linear_velocity * delta_time; real_t ang_vel = constant_angular_velocity.length(); @@ -458,13 +460,13 @@ void StaticBody3D::_update_kinematic_motion() { bool needs_physics_process = false; if (kinematic_motion) { - PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &StaticBody3D::_direct_state_changed)); + PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), this, &StaticBody3D::_body_state_changed_callback); if (!constant_angular_velocity.is_equal_approx(Vector3()) || !constant_linear_velocity.is_equal_approx(Vector3())) { needs_physics_process = true; } } else { - PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), Callable()); + PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), nullptr, nullptr); } set_physics_process_internal(needs_physics_process); @@ -582,25 +584,26 @@ struct _RigidBodyInOut { int local_shape = 0; }; -void RigidBody3D::_direct_state_changed(Object *p_state) { -#ifdef DEBUG_ENABLED - state = Object::cast_to<PhysicsDirectBodyState3D>(p_state); - ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument"); -#else - state = (PhysicsDirectBodyState3D *)p_state; //trust it -#endif +void RigidBody3D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state) { + RigidBody3D *body = (RigidBody3D *)p_instance; + body->_body_state_changed(p_state); +} +void RigidBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) { set_ignore_transform_notification(true); - set_global_transform(state->get_transform()); - linear_velocity = state->get_linear_velocity(); - angular_velocity = state->get_angular_velocity(); - inverse_inertia_tensor = state->get_inverse_inertia_tensor(); - if (sleeping != state->is_sleeping()) { - sleeping = state->is_sleeping(); + set_global_transform(p_state->get_transform()); + + linear_velocity = p_state->get_linear_velocity(); + angular_velocity = p_state->get_angular_velocity(); + + inverse_inertia_tensor = p_state->get_inverse_inertia_tensor(); + + if (sleeping != p_state->is_sleeping()) { + sleeping = p_state->is_sleeping(); emit_signal(SceneStringNames::get_singleton()->sleeping_state_changed); } - GDVIRTUAL_CALL(_integrate_forces, state); + GDVIRTUAL_CALL(_integrate_forces, p_state); set_ignore_transform_notification(false); _on_transform_changed(); @@ -617,18 +620,18 @@ void RigidBody3D::_direct_state_changed(Object *p_state) { } } - _RigidBodyInOut *toadd = (_RigidBodyInOut *)alloca(state->get_contact_count() * sizeof(_RigidBodyInOut)); + _RigidBodyInOut *toadd = (_RigidBodyInOut *)alloca(p_state->get_contact_count() * sizeof(_RigidBodyInOut)); int toadd_count = 0; //state->get_contact_count(); RigidBody3D_RemoveAction *toremove = (RigidBody3D_RemoveAction *)alloca(rc * sizeof(RigidBody3D_RemoveAction)); int toremove_count = 0; //put the ones to add - for (int i = 0; i < state->get_contact_count(); i++) { - RID rid = state->get_contact_collider(i); - ObjectID obj = state->get_contact_collider_id(i); - int local_shape = state->get_contact_local_shape(i); - int shape = state->get_contact_collider_shape(i); + for (int i = 0; i < p_state->get_contact_count(); i++) { + RID rid = p_state->get_contact_collider(i); + ObjectID obj = p_state->get_contact_collider_id(i); + int local_shape = p_state->get_contact_local_shape(i); + int shape = p_state->get_contact_collider_shape(i); //bool found=false; @@ -683,8 +686,6 @@ void RigidBody3D::_direct_state_changed(Object *p_state) { contact_monitor->locked = false; } - - state = nullptr; } void RigidBody3D::_notification(int p_what) { @@ -787,25 +788,15 @@ real_t RigidBody3D::get_angular_damp() const { } void RigidBody3D::set_axis_velocity(const Vector3 &p_axis) { - Vector3 v = state ? state->get_linear_velocity() : linear_velocity; Vector3 axis = p_axis.normalized(); - v -= axis * axis.dot(v); - v += p_axis; - if (state) { - set_linear_velocity(v); - } else { - PhysicsServer3D::get_singleton()->body_set_axis_velocity(get_rid(), p_axis); - linear_velocity = v; - } + linear_velocity -= axis * axis.dot(linear_velocity); + linear_velocity += p_axis; + PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY, linear_velocity); } void RigidBody3D::set_linear_velocity(const Vector3 &p_velocity) { linear_velocity = p_velocity; - if (state) { - state->set_linear_velocity(linear_velocity); - } else { - PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY, linear_velocity); - } + PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY, linear_velocity); } Vector3 RigidBody3D::get_linear_velocity() const { @@ -814,11 +805,7 @@ Vector3 RigidBody3D::get_linear_velocity() const { void RigidBody3D::set_angular_velocity(const Vector3 &p_velocity) { angular_velocity = p_velocity; - if (state) { - state->set_angular_velocity(angular_velocity); - } else { - PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY, angular_velocity); - } + PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY, angular_velocity); } Vector3 RigidBody3D::get_angular_velocity() const { @@ -1055,7 +1042,7 @@ void RigidBody3D::_bind_methods() { RigidBody3D::RigidBody3D() : PhysicsBody3D(PhysicsServer3D::BODY_MODE_DYNAMIC) { - PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &RigidBody3D::_direct_state_changed)); + PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), this, &RigidBody3D::_body_state_changed_callback); } RigidBody3D::~RigidBody3D() { @@ -1083,7 +1070,7 @@ bool CharacterBody3D::move_and_slide() { bool was_on_floor = on_floor; // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky - float delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); + double delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); for (int i = 0; i < 3; i++) { if (locked_axis & (1 << i)) { @@ -2252,23 +2239,19 @@ void PhysicalBone3D::_notification(int p_what) { } } -void PhysicalBone3D::_direct_state_changed(Object *p_state) { +void PhysicalBone3D::_body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state) { + PhysicalBone3D *bone = (PhysicalBone3D *)p_instance; + bone->_body_state_changed(p_state); +} + +void PhysicalBone3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) { if (!simulate_physics || !_internal_simulate_physics) { return; } - /// Update bone transform - - PhysicsDirectBodyState3D *state; - -#ifdef DEBUG_ENABLED - state = Object::cast_to<PhysicsDirectBodyState3D>(p_state); - ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument"); -#else - state = (PhysicsDirectBodyState3D *)p_state; //trust it -#endif + /// Update bone transform. - Transform3D global_transform(state->get_transform()); + Transform3D global_transform(p_state->get_transform()); set_ignore_transform_notification(true); set_global_transform(global_transform); @@ -2730,7 +2713,7 @@ void PhysicalBone3D::_start_physics_simulation() { set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC); PhysicsServer3D::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer()); PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask()); - PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &PhysicalBone3D::_direct_state_changed)); + PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), this, &PhysicalBone3D::_body_state_changed_callback); set_as_top_level(true); _internal_simulate_physics = true; } @@ -2749,7 +2732,7 @@ void PhysicalBone3D::_stop_physics_simulation() { PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), 0); } if (_internal_simulate_physics) { - PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), Callable()); + PhysicsServer3D::get_singleton()->body_set_state_sync_callback(get_rid(), nullptr, nullptr); parent_skeleton->set_bone_global_pose_override(bone_id, Transform3D(), 0.0, false); set_as_top_level(false); _internal_simulate_physics = false; diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h index 17e688451d..8c09f77846 100644 --- a/scene/3d/physics_body_3d.h +++ b/scene/3d/physics_body_3d.h @@ -86,7 +86,8 @@ class StaticBody3D : public PhysicsBody3D { Transform3D last_valid_transform; - void _direct_state_changed(Object *p_state); + static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state); + void _body_state_changed(PhysicsDirectBodyState3D *p_state); protected: void _notification(int p_what); @@ -136,7 +137,6 @@ public: protected: bool can_sleep = true; - PhysicsDirectBodyState3D *state = nullptr; Mode mode = MODE_DYNAMIC; real_t mass = 1.0; @@ -197,7 +197,8 @@ protected: void _body_exit_tree(ObjectID p_id); void _body_inout(int p_status, const RID &p_body, ObjectID p_instance, int p_body_shape, int p_local_shape); - virtual void _direct_state_changed(Object *p_state); + static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state); + virtual void _body_state_changed(PhysicsDirectBodyState3D *p_state); void _notification(int p_what); static void _bind_methods(); @@ -525,7 +526,8 @@ protected: bool _get(const StringName &p_name, Variant &r_ret) const; void _get_property_list(List<PropertyInfo> *p_list) const; void _notification(int p_what); - void _direct_state_changed(Object *p_state); + static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state); + void _body_state_changed(PhysicsDirectBodyState3D *p_state); static void _bind_methods(); diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp index 28ff3e9412..7eb189e890 100644 --- a/scene/3d/soft_body_3d.cpp +++ b/scene/3d/soft_body_3d.cpp @@ -355,6 +355,11 @@ void SoftBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_drag_coefficient", "drag_coefficient"), &SoftBody3D::set_drag_coefficient); ClassDB::bind_method(D_METHOD("get_drag_coefficient"), &SoftBody3D::get_drag_coefficient); + ClassDB::bind_method(D_METHOD("get_point_transform", "point_index"), &SoftBody3D::get_point_transform); + + ClassDB::bind_method(D_METHOD("set_point_pinned", "point_index", "pinned", "attachment_path"), &SoftBody3D::pin_point, DEFVAL(NodePath())); + ClassDB::bind_method(D_METHOD("is_point_pinned", "point_index"), &SoftBody3D::is_point_pinned); + ClassDB::bind_method(D_METHOD("set_ray_pickable", "ray_pickable"), &SoftBody3D::set_ray_pickable); ClassDB::bind_method(D_METHOD("is_ray_pickable"), &SoftBody3D::is_ray_pickable); diff --git a/scene/3d/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp index 92c0e09947..daeea81891 100644 --- a/scene/3d/vehicle_body_3d.cpp +++ b/scene/3d/vehicle_body_3d.cpp @@ -802,24 +802,21 @@ void VehicleBody3D::_update_friction(PhysicsDirectBodyState3D *s) { } } -void VehicleBody3D::_direct_state_changed(Object *p_state) { - RigidBody3D::_direct_state_changed(p_state); +void VehicleBody3D::_body_state_changed(PhysicsDirectBodyState3D *p_state) { + RigidBody3D::_body_state_changed(p_state); - state = Object::cast_to<PhysicsDirectBodyState3D>(p_state); - ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument"); - - real_t step = state->get_step(); + real_t step = p_state->get_step(); for (int i = 0; i < wheels.size(); i++) { - _update_wheel(i, state); + _update_wheel(i, p_state); } for (int i = 0; i < wheels.size(); i++) { - _ray_cast(i, state); - wheels[i]->set_transform(state->get_transform().inverse() * wheels[i]->m_worldTransform); + _ray_cast(i, p_state); + wheels[i]->set_transform(p_state->get_transform().inverse() * wheels[i]->m_worldTransform); } - _update_suspension(state); + _update_suspension(p_state); for (int i = 0; i < wheels.size(); i++) { //apply suspension force @@ -831,20 +828,20 @@ void VehicleBody3D::_direct_state_changed(Object *p_state) { suspensionForce = wheel.m_maxSuspensionForce; } Vector3 impulse = wheel.m_raycastInfo.m_contactNormalWS * suspensionForce * step; - Vector3 relative_position = wheel.m_raycastInfo.m_contactPointWS - state->get_transform().origin; + Vector3 relative_position = wheel.m_raycastInfo.m_contactPointWS - p_state->get_transform().origin; - state->apply_impulse(impulse, relative_position); + p_state->apply_impulse(impulse, relative_position); } - _update_friction(state); + _update_friction(p_state); for (int i = 0; i < wheels.size(); i++) { VehicleWheel3D &wheel = *wheels[i]; - Vector3 relpos = wheel.m_raycastInfo.m_hardPointWS - state->get_transform().origin; - Vector3 vel = state->get_linear_velocity() + (state->get_angular_velocity()).cross(relpos); // * mPos); + Vector3 relpos = wheel.m_raycastInfo.m_hardPointWS - p_state->get_transform().origin; + Vector3 vel = p_state->get_linear_velocity() + (p_state->get_angular_velocity()).cross(relpos); // * mPos); if (wheel.m_raycastInfo.m_isInContact) { - const Transform3D &chassisWorldTransform = state->get_transform(); + const Transform3D &chassisWorldTransform = p_state->get_transform(); Vector3 fwd( chassisWorldTransform.basis[0][Vector3::AXIS_Z], @@ -864,8 +861,6 @@ void VehicleBody3D::_direct_state_changed(Object *p_state) { wheel.m_deltaRotation *= real_t(0.99); //damping of rotation when not in contact } - - state = nullptr; } void VehicleBody3D::set_engine_force(real_t p_engine_force) { @@ -926,7 +921,5 @@ void VehicleBody3D::_bind_methods() { VehicleBody3D::VehicleBody3D() { exclude.insert(get_rid()); - //PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &VehicleBody3D::_direct_state_changed)); - set_mass(40); } diff --git a/scene/3d/vehicle_body_3d.h b/scene/3d/vehicle_body_3d.h index 2c10205ea3..f29c3d89b7 100644 --- a/scene/3d/vehicle_body_3d.h +++ b/scene/3d/vehicle_body_3d.h @@ -192,7 +192,8 @@ class VehicleBody3D : public RigidBody3D { static void _bind_methods(); - void _direct_state_changed(Object *p_state) override; + static void _body_state_changed_callback(void *p_instance, PhysicsDirectBodyState3D *p_state); + virtual void _body_state_changed(PhysicsDirectBodyState3D *p_state) override; public: void set_engine_force(real_t p_engine_force); diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index 481e211eb3..845a2ec6c7 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -434,14 +434,14 @@ void TabContainer::_notification(int p_what) { } int tab_width = tab_widths[i]; - if (get_tab_disabled(index)) { + if (index == current) { + x_current = x; + } else if (get_tab_disabled(index)) { if (rtl) { _draw_tab(tab_disabled, font_disabled_color, index, size.width - (tabs_ofs_cache + x) - tab_width); } else { _draw_tab(tab_disabled, font_disabled_color, index, tabs_ofs_cache + x); } - } else if (index == current) { - x_current = x; } else { if (rtl) { _draw_tab(tab_unselected, font_unselected_color, index, size.width - (tabs_ofs_cache + x) - tab_width); @@ -459,12 +459,13 @@ void TabContainer::_notification(int p_what) { panel->draw(canvas, Rect2(0, header_height, size.width, size.height - header_height)); } - // Draw selected tab in front. only draw selected tab when it's in visible range. + // Draw selected tab in front. Only draw selected tab when it's in visible range. if (tabs.size() > 0 && current - first_tab_cache < tab_widths.size() && current >= first_tab_cache) { + Ref<StyleBox> current_style_box = get_tab_disabled(current) ? tab_disabled : tab_selected; if (rtl) { - _draw_tab(tab_selected, font_selected_color, current, size.width - (tabs_ofs_cache + x_current) - tab_widths[current]); + _draw_tab(current_style_box, font_selected_color, current, size.width - (tabs_ofs_cache + x_current) - tab_widths[current]); } else { - _draw_tab(tab_selected, font_selected_color, current, tabs_ofs_cache + x_current); + _draw_tab(current_style_box, font_selected_color, current, tabs_ofs_cache + x_current); } } @@ -640,8 +641,8 @@ void TabContainer::_on_mouse_exited() { int TabContainer::_get_tab_width(int p_index) const { ERR_FAIL_INDEX_V(p_index, get_tab_count(), 0); - Control *control = Object::cast_to<Control>(_get_tabs()[p_index]); - if (!control || control->is_set_as_top_level() || get_tab_hidden(p_index)) { + Control *control = get_tab_control(p_index); + if (!control || get_tab_hidden(p_index)) { return 0; } diff --git a/scene/gui/texture_progress_bar.cpp b/scene/gui/texture_progress_bar.cpp index 5e5dec3579..286f01ee33 100644 --- a/scene/gui/texture_progress_bar.cpp +++ b/scene/gui/texture_progress_bar.cpp @@ -100,6 +100,15 @@ Ref<Texture2D> TextureProgressBar::get_progress_texture() const { return progress; } +void TextureProgressBar::set_progress_offset(Point2 p_offset) { + progress_offset = p_offset; + update(); +} + +Point2 TextureProgressBar::get_progress_offset() const { + return progress_offset; +} + void TextureProgressBar::set_tint_under(const Color &p_tint) { tint_under = p_tint; update(); @@ -360,6 +369,9 @@ void TextureProgressBar::draw_nine_patch_stretched(const Ref<Texture2D> &p_textu } } + if (p_texture == progress) { + dst_rect.position += progress_offset; + } p_texture->get_rect_region(dst_rect, src_rect, dst_rect, src_rect); RID ci = get_canvas_item(); @@ -403,20 +415,24 @@ void TextureProgressBar::_notification(int p_what) { Size2 s = progress->get_size(); switch (mode) { case FILL_LEFT_TO_RIGHT: { - Rect2 region = Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)); - draw_texture_rect_region(progress, region, region, tint_progress); + Rect2 region = Rect2(progress_offset, Size2(s.x * get_as_ratio(), s.y)); + Rect2 source = Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)); + draw_texture_rect_region(progress, region, source, tint_progress); } break; case FILL_RIGHT_TO_LEFT: { - Rect2 region = Rect2(Point2(s.x - s.x * get_as_ratio(), 0), Size2(s.x * get_as_ratio(), s.y)); - draw_texture_rect_region(progress, region, region, tint_progress); + Rect2 region = Rect2(progress_offset + Point2(s.x - s.x * get_as_ratio(), 0), Size2(s.x * get_as_ratio(), s.y)); + Rect2 source = Rect2(Point2(s.x - s.x * get_as_ratio(), 0), Size2(s.x * get_as_ratio(), s.y)); + draw_texture_rect_region(progress, region, source, tint_progress); } break; case FILL_TOP_TO_BOTTOM: { - Rect2 region = Rect2(Point2(), Size2(s.x, s.y * get_as_ratio())); - draw_texture_rect_region(progress, region, region, tint_progress); + Rect2 region = Rect2(progress_offset + Point2(), Size2(s.x, s.y * get_as_ratio())); + Rect2 source = Rect2(Point2(), Size2(s.x, s.y * get_as_ratio())); + draw_texture_rect_region(progress, region, source, tint_progress); } break; case FILL_BOTTOM_TO_TOP: { - Rect2 region = Rect2(Point2(0, s.y - s.y * get_as_ratio()), Size2(s.x, s.y * get_as_ratio())); - draw_texture_rect_region(progress, region, region, tint_progress); + Rect2 region = Rect2(progress_offset + Point2(0, s.y - s.y * get_as_ratio()), Size2(s.x, s.y * get_as_ratio())); + Rect2 source = Rect2(Point2(0, s.y - s.y * get_as_ratio()), Size2(s.x, s.y * get_as_ratio())); + draw_texture_rect_region(progress, region, source, tint_progress); } break; case FILL_CLOCKWISE: case FILL_COUNTER_CLOCKWISE: @@ -427,8 +443,9 @@ void TextureProgressBar::_notification(int p_what) { float val = get_as_ratio() * rad_max_degrees / 360; if (val == 1) { - Rect2 region = Rect2(Point2(), s); - draw_texture_rect(progress, region, false, tint_progress); + Rect2 region = Rect2(progress_offset, s); + Rect2 source = Rect2(Point2(), s); + draw_texture_rect_region(progress, region, source, tint_progress); } else if (val != 0) { Array pts; float direction = mode == FILL_COUNTER_CLOCKWISE ? -1 : 1; @@ -454,14 +471,14 @@ void TextureProgressBar::_notification(int p_what) { Vector<Point2> uvs; Vector<Point2> points; uvs.push_back(get_relative_center()); - points.push_back(Point2(s.x * get_relative_center().x, s.y * get_relative_center().y)); + points.push_back(progress_offset + Point2(s.x * get_relative_center().x, s.y * get_relative_center().y)); for (int i = 0; i < pts.size(); i++) { Point2 uv = unit_val_to_uv(pts[i]); if (uvs.find(uv) >= 0) { continue; } uvs.push_back(uv); - points.push_back(Point2(uv.x * s.x, uv.y * s.y)); + points.push_back(progress_offset + Point2(uv.x * s.x, uv.y * s.y)); } Vector<Color> colors; colors.push_back(tint_progress); @@ -484,17 +501,19 @@ void TextureProgressBar::_notification(int p_what) { } } break; case FILL_BILINEAR_LEFT_AND_RIGHT: { - Rect2 region = Rect2(Point2(s.x / 2 - s.x * get_as_ratio() / 2, 0), Size2(s.x * get_as_ratio(), s.y)); - draw_texture_rect_region(progress, region, region, tint_progress); + Rect2 region = Rect2(progress_offset + Point2(s.x / 2 - s.x * get_as_ratio() / 2, 0), Size2(s.x * get_as_ratio(), s.y)); + Rect2 source = Rect2(Point2(s.x / 2 - s.x * get_as_ratio() / 2, 0), Size2(s.x * get_as_ratio(), s.y)); + draw_texture_rect_region(progress, region, source, tint_progress); } break; case FILL_BILINEAR_TOP_AND_BOTTOM: { - Rect2 region = Rect2(Point2(0, s.y / 2 - s.y * get_as_ratio() / 2), Size2(s.x, s.y * get_as_ratio())); - draw_texture_rect_region(progress, region, region, tint_progress); + Rect2 region = Rect2(progress_offset + Point2(0, s.y / 2 - s.y * get_as_ratio() / 2), Size2(s.x, s.y * get_as_ratio())); + Rect2 source = Rect2(Point2(0, s.y / 2 - s.y * get_as_ratio() / 2), Size2(s.x, s.y * get_as_ratio())); + draw_texture_rect_region(progress, region, source, tint_progress); } break; case FILL_MODE_MAX: break; default: - draw_texture_rect_region(progress, Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), tint_progress); + draw_texture_rect_region(progress, Rect2(progress_offset, Size2(s.x * get_as_ratio(), s.y)), Rect2(Point2(), Size2(s.x * get_as_ratio(), s.y)), tint_progress); } } if (over.is_valid()) { @@ -585,6 +604,9 @@ void TextureProgressBar::_bind_methods() { ClassDB::bind_method(D_METHOD("set_tint_over", "tint"), &TextureProgressBar::set_tint_over); ClassDB::bind_method(D_METHOD("get_tint_over"), &TextureProgressBar::get_tint_over); + ClassDB::bind_method(D_METHOD("set_texture_progress_offset", "offset"), &TextureProgressBar::set_progress_offset); + ClassDB::bind_method(D_METHOD("get_texture_progress_offset"), &TextureProgressBar::get_progress_offset); + ClassDB::bind_method(D_METHOD("set_radial_initial_angle", "mode"), &TextureProgressBar::set_radial_initial_angle); ClassDB::bind_method(D_METHOD("get_radial_initial_angle"), &TextureProgressBar::get_radial_initial_angle); @@ -604,6 +626,7 @@ void TextureProgressBar::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_under", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_under_texture", "get_under_texture"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_over", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_over_texture", "get_over_texture"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture_progress", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_progress_texture", "get_progress_texture"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "texture_progress_offset"), "set_texture_progress_offset", "get_texture_progress_offset"); ADD_PROPERTY(PropertyInfo(Variant::INT, "fill_mode", PROPERTY_HINT_ENUM, "Left to Right,Right to Left,Top to Bottom,Bottom to Top,Clockwise,Counter Clockwise,Bilinear (Left and Right),Bilinear (Top and Bottom),Clockwise and Counter Clockwise"), "set_fill_mode", "get_fill_mode"); ADD_GROUP("Tint", "tint_"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "tint_under"), "set_tint_under", "get_tint_under"); diff --git a/scene/gui/texture_progress_bar.h b/scene/gui/texture_progress_bar.h index d147c43a26..c508f41387 100644 --- a/scene/gui/texture_progress_bar.h +++ b/scene/gui/texture_progress_bar.h @@ -61,6 +61,9 @@ public: void set_fill_mode(int p_fill); int get_fill_mode(); + void set_progress_offset(Point2 p_offset); + Point2 get_progress_offset() const; + void set_radial_initial_angle(float p_angle); float get_radial_initial_angle(); @@ -100,6 +103,7 @@ public: private: FillMode mode = FILL_LEFT_TO_RIGHT; + Point2 progress_offset; float rad_init_angle = 0.0; float rad_max_degrees = 360.0; Point2 rad_center_off; diff --git a/servers/physics_2d/body_2d_sw.cpp b/servers/physics_2d/body_2d_sw.cpp index ad47912b82..64f71fd25c 100644 --- a/servers/physics_2d/body_2d_sw.cpp +++ b/servers/physics_2d/body_2d_sw.cpp @@ -29,8 +29,9 @@ /*************************************************************************/ #include "body_2d_sw.h" + #include "area_2d_sw.h" -#include "physics_server_2d_sw.h" +#include "body_direct_state_2d_sw.h" #include "space_2d_sw.h" void Body2DSW::_update_inertia() { @@ -495,7 +496,7 @@ void Body2DSW::integrate_velocities(real_t p_step) { return; } - if (fi_callback) { + if (fi_callback_data || body_state_callback) { get_space()->body_add_to_state_query_list(&direct_state_query_list); } @@ -547,27 +548,27 @@ void Body2DSW::wakeup_neighbours() { } void Body2DSW::call_queries() { - if (fi_callback) { - PhysicsDirectBodyState2DSW *dbs = PhysicsDirectBodyState2DSW::singleton; - dbs->body = this; - - Variant v = dbs; - const Variant *vp[2] = { &v, &fi_callback->callback_udata }; - - Object *obj = fi_callback->callable.get_object(); - if (!obj) { + if (fi_callback_data) { + if (!fi_callback_data->callable.get_object()) { set_force_integration_callback(Callable()); } else { + Variant direct_state_variant = get_direct_state(); + const Variant *vp[2] = { &direct_state_variant, &fi_callback_data->udata }; + Callable::CallError ce; Variant rv; - if (fi_callback->callback_udata.get_type() != Variant::NIL) { - fi_callback->callable.call(vp, 2, rv, ce); + if (fi_callback_data->udata.get_type() != Variant::NIL) { + fi_callback_data->callable.call(vp, 2, rv, ce); } else { - fi_callback->callable.call(vp, 1, rv, ce); + fi_callback_data->callable.call(vp, 1, rv, ce); } } } + + if (body_state_callback) { + (body_state_callback)(body_state_callback_instance, get_direct_state()); + } } bool Body2DSW::sleep_test(real_t p_step) { @@ -587,17 +588,30 @@ bool Body2DSW::sleep_test(real_t p_step) { } } +void Body2DSW::set_state_sync_callback(void *p_instance, PhysicsServer2D::BodyStateCallback p_callback) { + body_state_callback_instance = p_instance; + body_state_callback = p_callback; +} + void Body2DSW::set_force_integration_callback(const Callable &p_callable, const Variant &p_udata) { - if (fi_callback) { - memdelete(fi_callback); - fi_callback = nullptr; + if (p_callable.get_object()) { + if (!fi_callback_data) { + fi_callback_data = memnew(ForceIntegrationCallbackData); + } + fi_callback_data->callable = p_callable; + fi_callback_data->udata = p_udata; + } else if (fi_callback_data) { + memdelete(fi_callback_data); + fi_callback_data = nullptr; } +} - if (p_callable.get_object()) { - fi_callback = memnew(ForceIntegrationCallback); - fi_callback->callable = p_callable; - fi_callback->callback_udata = p_udata; +PhysicsDirectBodyState2DSW *Body2DSW::get_direct_state() { + if (!direct_state) { + direct_state = memnew(PhysicsDirectBodyState2DSW); + direct_state->body = this; } + return direct_state; } Body2DSW::Body2DSW() : @@ -632,33 +646,13 @@ Body2DSW::Body2DSW() : still_time = 0; continuous_cd_mode = PhysicsServer2D::CCD_MODE_DISABLED; can_sleep = true; - fi_callback = nullptr; } Body2DSW::~Body2DSW() { - if (fi_callback) { - memdelete(fi_callback); - } -} - -PhysicsDirectBodyState2DSW *PhysicsDirectBodyState2DSW::singleton = nullptr; - -PhysicsDirectSpaceState2D *PhysicsDirectBodyState2DSW::get_space_state() { - return body->get_space()->get_direct_state(); -} - -Variant PhysicsDirectBodyState2DSW::get_contact_collider_shape_metadata(int p_contact_idx) const { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Variant()); - - if (!PhysicsServer2DSW::singletonsw->body_owner.owns(body->contacts[p_contact_idx].collider)) { - return Variant(); + if (fi_callback_data) { + memdelete(fi_callback_data); } - Body2DSW *other = PhysicsServer2DSW::singletonsw->body_owner.getornull(body->contacts[p_contact_idx].collider); - - int sidx = body->contacts[p_contact_idx].collider_shape; - if (sidx < 0 || sidx >= other->get_shape_count()) { - return Variant(); + if (direct_state) { + memdelete(direct_state); } - - return other->get_shape_metadata(sidx); } diff --git a/servers/physics_2d/body_2d_sw.h b/servers/physics_2d/body_2d_sw.h index 30e14eb9c7..9cd53ceca1 100644 --- a/servers/physics_2d/body_2d_sw.h +++ b/servers/physics_2d/body_2d_sw.h @@ -38,6 +38,7 @@ #include "core/templates/vset.h" class Constraint2DSW; +class PhysicsDirectBodyState2DSW; class Body2DSW : public CollisionObject2DSW { PhysicsServer2D::BodyMode mode; @@ -116,12 +117,17 @@ class Body2DSW : public CollisionObject2DSW { Vector<Contact> contacts; //no contacts by default int contact_count; - struct ForceIntegrationCallback { + void *body_state_callback_instance = nullptr; + PhysicsServer2D::BodyStateCallback body_state_callback = nullptr; + + struct ForceIntegrationCallbackData { Callable callable; - Variant callback_udata; + Variant udata; }; - ForceIntegrationCallback *fi_callback; + ForceIntegrationCallbackData *fi_callback_data = nullptr; + + PhysicsDirectBodyState2DSW *direct_state = nullptr; uint64_t island_step; @@ -130,8 +136,11 @@ class Body2DSW : public CollisionObject2DSW { friend class PhysicsDirectBodyState2DSW; // i give up, too many functions to expose public: + void set_state_sync_callback(void *p_instance, PhysicsServer2D::BodyStateCallback p_callback); void set_force_integration_callback(const Callable &p_callable, const Variant &p_udata = Variant()); + PhysicsDirectBodyState2DSW *get_direct_state(); + _FORCE_INLINE_ void add_area(Area2DSW *p_area) { int index = areas.find(AreaCMP(p_area)); if (index > -1) { @@ -332,87 +341,4 @@ void Body2DSW::add_contact(const Vector2 &p_local_pos, const Vector2 &p_local_no c[idx].collider_velocity_at_pos = p_collider_velocity_at_pos; } -class PhysicsDirectBodyState2DSW : public PhysicsDirectBodyState2D { - GDCLASS(PhysicsDirectBodyState2DSW, PhysicsDirectBodyState2D); - -public: - static PhysicsDirectBodyState2DSW *singleton; - Body2DSW *body; - real_t step; - - virtual Vector2 get_total_gravity() const override { return body->gravity; } // get gravity vector working on this body space/area - virtual real_t get_total_angular_damp() const override { return body->area_angular_damp; } // get density of this body space/area - virtual real_t get_total_linear_damp() const override { return body->area_linear_damp; } // get density of this body space/area - - virtual real_t get_inverse_mass() const override { return body->get_inv_mass(); } // get the mass - virtual real_t get_inverse_inertia() const override { return body->get_inv_inertia(); } // get density of this body space - - virtual void set_linear_velocity(const Vector2 &p_velocity) override { body->set_linear_velocity(p_velocity); } - virtual Vector2 get_linear_velocity() const override { return body->get_linear_velocity(); } - - virtual void set_angular_velocity(real_t p_velocity) override { body->set_angular_velocity(p_velocity); } - virtual real_t get_angular_velocity() const override { return body->get_angular_velocity(); } - - virtual void set_transform(const Transform2D &p_transform) override { body->set_state(PhysicsServer2D::BODY_STATE_TRANSFORM, p_transform); } - virtual Transform2D get_transform() const override { return body->get_transform(); } - - virtual Vector2 get_velocity_at_local_position(const Vector2 &p_position) const override { return body->get_velocity_in_local_point(p_position); } - - virtual void add_central_force(const Vector2 &p_force) override { body->add_central_force(p_force); } - virtual void add_force(const Vector2 &p_force, const Vector2 &p_position = Vector2()) override { body->add_force(p_force, p_position); } - virtual void add_torque(real_t p_torque) override { body->add_torque(p_torque); } - virtual void apply_central_impulse(const Vector2 &p_impulse) override { body->apply_central_impulse(p_impulse); } - virtual void apply_impulse(const Vector2 &p_impulse, const Vector2 &p_position = Vector2()) override { body->apply_impulse(p_impulse, p_position); } - virtual void apply_torque_impulse(real_t p_torque) override { body->apply_torque_impulse(p_torque); } - - virtual void set_sleep_state(bool p_enable) override { body->set_active(!p_enable); } - virtual bool is_sleeping() const override { return !body->is_active(); } - - virtual int get_contact_count() const override { return body->contact_count; } - - virtual Vector2 get_contact_local_position(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector2()); - return body->contacts[p_contact_idx].local_pos; - } - virtual Vector2 get_contact_local_normal(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector2()); - return body->contacts[p_contact_idx].local_normal; - } - virtual int get_contact_local_shape(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, -1); - return body->contacts[p_contact_idx].local_shape; - } - - virtual RID get_contact_collider(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, RID()); - return body->contacts[p_contact_idx].collider; - } - virtual Vector2 get_contact_collider_position(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector2()); - return body->contacts[p_contact_idx].collider_pos; - } - virtual ObjectID get_contact_collider_id(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, ObjectID()); - return body->contacts[p_contact_idx].collider_instance_id; - } - virtual int get_contact_collider_shape(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, 0); - return body->contacts[p_contact_idx].collider_shape; - } - virtual Variant get_contact_collider_shape_metadata(int p_contact_idx) const override; - - virtual Vector2 get_contact_collider_velocity_at_position(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector2()); - return body->contacts[p_contact_idx].collider_velocity_at_pos; - } - - virtual PhysicsDirectSpaceState2D *get_space_state() override; - - virtual real_t get_step() const override { return step; } - PhysicsDirectBodyState2DSW() { - singleton = this; - body = nullptr; - } -}; - #endif // BODY_2D_SW_H diff --git a/servers/physics_2d/body_direct_state_2d_sw.cpp b/servers/physics_2d/body_direct_state_2d_sw.cpp new file mode 100644 index 0000000000..1f68cca843 --- /dev/null +++ b/servers/physics_2d/body_direct_state_2d_sw.cpp @@ -0,0 +1,182 @@ +/*************************************************************************/ +/* body_direct_state_2d_sw.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 "body_direct_state_2d_sw.h" + +#include "body_2d_sw.h" +#include "physics_server_2d_sw.h" +#include "space_2d_sw.h" + +Vector2 PhysicsDirectBodyState2DSW::get_total_gravity() const { + return body->gravity; +} + +real_t PhysicsDirectBodyState2DSW::get_total_angular_damp() const { + return body->area_angular_damp; +} + +real_t PhysicsDirectBodyState2DSW::get_total_linear_damp() const { + return body->area_linear_damp; +} + +real_t PhysicsDirectBodyState2DSW::get_inverse_mass() const { + return body->get_inv_mass(); +} + +real_t PhysicsDirectBodyState2DSW::get_inverse_inertia() const { + return body->get_inv_inertia(); +} + +void PhysicsDirectBodyState2DSW::set_linear_velocity(const Vector2 &p_velocity) { + body->set_linear_velocity(p_velocity); +} + +Vector2 PhysicsDirectBodyState2DSW::get_linear_velocity() const { + return body->get_linear_velocity(); +} + +void PhysicsDirectBodyState2DSW::set_angular_velocity(real_t p_velocity) { + body->set_angular_velocity(p_velocity); +} + +real_t PhysicsDirectBodyState2DSW::get_angular_velocity() const { + return body->get_angular_velocity(); +} + +void PhysicsDirectBodyState2DSW::set_transform(const Transform2D &p_transform) { + body->set_state(PhysicsServer2D::BODY_STATE_TRANSFORM, p_transform); +} + +Transform2D PhysicsDirectBodyState2DSW::get_transform() const { + return body->get_transform(); +} + +Vector2 PhysicsDirectBodyState2DSW::get_velocity_at_local_position(const Vector2 &p_position) const { + return body->get_velocity_in_local_point(p_position); +} + +void PhysicsDirectBodyState2DSW::add_central_force(const Vector2 &p_force) { + body->add_central_force(p_force); +} + +void PhysicsDirectBodyState2DSW::add_force(const Vector2 &p_force, const Vector2 &p_position) { + body->add_force(p_force, p_position); +} + +void PhysicsDirectBodyState2DSW::add_torque(real_t p_torque) { + body->add_torque(p_torque); +} + +void PhysicsDirectBodyState2DSW::apply_central_impulse(const Vector2 &p_impulse) { + body->apply_central_impulse(p_impulse); +} + +void PhysicsDirectBodyState2DSW::apply_impulse(const Vector2 &p_impulse, const Vector2 &p_position) { + body->apply_impulse(p_impulse, p_position); +} + +void PhysicsDirectBodyState2DSW::apply_torque_impulse(real_t p_torque) { + body->apply_torque_impulse(p_torque); +} + +void PhysicsDirectBodyState2DSW::set_sleep_state(bool p_enable) { + body->set_active(!p_enable); +} + +bool PhysicsDirectBodyState2DSW::is_sleeping() const { + return !body->is_active(); +} + +int PhysicsDirectBodyState2DSW::get_contact_count() const { + return body->contact_count; +} + +Vector2 PhysicsDirectBodyState2DSW::get_contact_local_position(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector2()); + return body->contacts[p_contact_idx].local_pos; +} + +Vector2 PhysicsDirectBodyState2DSW::get_contact_local_normal(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector2()); + return body->contacts[p_contact_idx].local_normal; +} + +int PhysicsDirectBodyState2DSW::get_contact_local_shape(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, -1); + return body->contacts[p_contact_idx].local_shape; +} + +RID PhysicsDirectBodyState2DSW::get_contact_collider(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, RID()); + return body->contacts[p_contact_idx].collider; +} +Vector2 PhysicsDirectBodyState2DSW::get_contact_collider_position(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector2()); + return body->contacts[p_contact_idx].collider_pos; +} + +ObjectID PhysicsDirectBodyState2DSW::get_contact_collider_id(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, ObjectID()); + return body->contacts[p_contact_idx].collider_instance_id; +} + +int PhysicsDirectBodyState2DSW::get_contact_collider_shape(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, 0); + return body->contacts[p_contact_idx].collider_shape; +} + +Vector2 PhysicsDirectBodyState2DSW::get_contact_collider_velocity_at_position(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector2()); + return body->contacts[p_contact_idx].collider_velocity_at_pos; +} + +Variant PhysicsDirectBodyState2DSW::get_contact_collider_shape_metadata(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Variant()); + + if (!PhysicsServer2DSW::singletonsw->body_owner.owns(body->contacts[p_contact_idx].collider)) { + return Variant(); + } + Body2DSW *other = PhysicsServer2DSW::singletonsw->body_owner.getornull(body->contacts[p_contact_idx].collider); + + int sidx = body->contacts[p_contact_idx].collider_shape; + if (sidx < 0 || sidx >= other->get_shape_count()) { + return Variant(); + } + + return other->get_shape_metadata(sidx); +} + +PhysicsDirectSpaceState2D *PhysicsDirectBodyState2DSW::get_space_state() { + return body->get_space()->get_direct_state(); +} + +real_t PhysicsDirectBodyState2DSW::get_step() const { + return body->get_space()->get_last_step(); +} diff --git a/servers/physics_2d/body_direct_state_2d_sw.h b/servers/physics_2d/body_direct_state_2d_sw.h new file mode 100644 index 0000000000..aef9186086 --- /dev/null +++ b/servers/physics_2d/body_direct_state_2d_sw.h @@ -0,0 +1,91 @@ +/*************************************************************************/ +/* body_direct_state_2d_sw.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 BODY_DIRECT_STATE_2D_SW_H +#define BODY_DIRECT_STATE_2D_SW_H + +#include "servers/physics_server_2d.h" + +class Body2DSW; + +class PhysicsDirectBodyState2DSW : public PhysicsDirectBodyState2D { + GDCLASS(PhysicsDirectBodyState2DSW, PhysicsDirectBodyState2D); + +public: + Body2DSW *body = nullptr; + + virtual Vector2 get_total_gravity() const override; + virtual real_t get_total_angular_damp() const override; + virtual real_t get_total_linear_damp() const override; + + virtual real_t get_inverse_mass() const override; + virtual real_t get_inverse_inertia() const override; + + virtual void set_linear_velocity(const Vector2 &p_velocity) override; + virtual Vector2 get_linear_velocity() const override; + + virtual void set_angular_velocity(real_t p_velocity) override; + virtual real_t get_angular_velocity() const override; + + virtual void set_transform(const Transform2D &p_transform) override; + virtual Transform2D get_transform() const override; + + virtual Vector2 get_velocity_at_local_position(const Vector2 &p_position) const override; + + virtual void add_central_force(const Vector2 &p_force) override; + virtual void add_force(const Vector2 &p_force, const Vector2 &p_position = Vector2()) override; + virtual void add_torque(real_t p_torque) override; + virtual void apply_central_impulse(const Vector2 &p_impulse) override; + virtual void apply_impulse(const Vector2 &p_impulse, const Vector2 &p_position = Vector2()) override; + virtual void apply_torque_impulse(real_t p_torque) override; + + virtual void set_sleep_state(bool p_enable) override; + virtual bool is_sleeping() const override; + + virtual int get_contact_count() const override; + + virtual Vector2 get_contact_local_position(int p_contact_idx) const override; + virtual Vector2 get_contact_local_normal(int p_contact_idx) const override; + virtual int get_contact_local_shape(int p_contact_idx) const override; + + virtual RID get_contact_collider(int p_contact_idx) const override; + virtual Vector2 get_contact_collider_position(int p_contact_idx) const override; + virtual ObjectID get_contact_collider_id(int p_contact_idx) const override; + virtual int get_contact_collider_shape(int p_contact_idx) const override; + virtual Variant get_contact_collider_shape_metadata(int p_contact_idx) const override; + + virtual Vector2 get_contact_collider_velocity_at_position(int p_contact_idx) const override; + + virtual PhysicsDirectSpaceState2D *get_space_state() override; + + virtual real_t get_step() const override; +}; + +#endif // BODY_2D_SW_H diff --git a/servers/physics_2d/physics_server_2d_sw.cpp b/servers/physics_2d/physics_server_2d_sw.cpp index 813e5ddc17..85b5e40752 100644 --- a/servers/physics_2d/physics_server_2d_sw.cpp +++ b/servers/physics_2d/physics_server_2d_sw.cpp @@ -30,6 +30,7 @@ #include "physics_server_2d_sw.h" +#include "body_direct_state_2d_sw.h" #include "broad_phase_2d_bvh.h" #include "collision_solver_2d_sw.h" #include "core/config/project_settings.h" @@ -926,6 +927,12 @@ int PhysicsServer2DSW::body_get_max_contacts_reported(RID p_body) const { return body->get_max_contacts_reported(); } +void PhysicsServer2DSW::body_set_state_sync_callback(RID p_body, void *p_instance, BodyStateCallback p_callback) { + Body2DSW *body = body_owner.getornull(p_body); + ERR_FAIL_COND(!body); + body->set_state_sync_callback(p_instance, p_callback); +} + void PhysicsServer2DSW::body_set_force_integration_callback(RID p_body, const Callable &p_callable, const Variant &p_udata) { Body2DSW *body = body_owner.getornull(p_body); ERR_FAIL_COND(!body); @@ -960,17 +967,13 @@ bool PhysicsServer2DSW::body_test_motion(RID p_body, const Transform2D &p_from, PhysicsDirectBodyState2D *PhysicsServer2DSW::body_get_direct_state(RID p_body) { ERR_FAIL_COND_V_MSG((using_threads && !doing_sync), nullptr, "Body state is inaccessible right now, wait for iteration or physics process notification."); - if (!body_owner.owns(p_body)) { - return nullptr; - } - Body2DSW *body = body_owner.getornull(p_body); ERR_FAIL_COND_V(!body, nullptr); + ERR_FAIL_COND_V(!body->get_space(), nullptr); ERR_FAIL_COND_V_MSG(body->get_space()->is_locked(), nullptr, "Body state is inaccessible right now, wait for iteration or physics process notification."); - direct_state->body = body; - return direct_state; + return body->get_direct_state(); } /* JOINT API */ @@ -1234,10 +1237,8 @@ void PhysicsServer2DSW::set_collision_iterations(int p_iterations) { void PhysicsServer2DSW::init() { doing_sync = false; - last_step = 0.001; iterations = 8; // 8? stepper = memnew(Step2DSW); - direct_state = memnew(PhysicsDirectBodyState2DSW); }; void PhysicsServer2DSW::step(real_t p_step) { @@ -1247,8 +1248,6 @@ void PhysicsServer2DSW::step(real_t p_step) { _update_shapes(); - last_step = p_step; - PhysicsDirectBodyState2DSW::singleton->step = p_step; island_count = 0; active_objects = 0; collision_pairs = 0; @@ -1320,7 +1319,6 @@ void PhysicsServer2DSW::end_sync() { void PhysicsServer2DSW::finish() { memdelete(stepper); - memdelete(direct_state); }; void PhysicsServer2DSW::_update_shapes() { diff --git a/servers/physics_2d/physics_server_2d_sw.h b/servers/physics_2d/physics_server_2d_sw.h index c5b6d550d7..ef1306cf59 100644 --- a/servers/physics_2d/physics_server_2d_sw.h +++ b/servers/physics_2d/physics_server_2d_sw.h @@ -46,7 +46,6 @@ class PhysicsServer2DSW : public PhysicsServer2D { bool active; int iterations; bool doing_sync; - real_t last_step; int island_count; int active_objects; @@ -59,8 +58,6 @@ class PhysicsServer2DSW : public PhysicsServer2D { Step2DSW *stepper; Set<const Space2DSW *> active_spaces; - PhysicsDirectBodyState2DSW *direct_state; - mutable RID_PtrOwner<Shape2DSW, true> shape_owner; mutable RID_PtrOwner<Space2DSW, true> space_owner; mutable RID_PtrOwner<Area2DSW, true> area_owner; @@ -242,7 +239,9 @@ public: virtual void body_set_max_contacts_reported(RID p_body, int p_contacts) override; virtual int body_get_max_contacts_reported(RID p_body) const override; + virtual void body_set_state_sync_callback(RID p_body, void *p_instance, BodyStateCallback p_callback) override; virtual void body_set_force_integration_callback(RID p_body, const Callable &p_callable, const Variant &p_udata = Variant()) override; + virtual bool body_collide_shape(RID p_body, int p_body_shape, RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, Vector2 *r_results, int p_result_max, int &r_result_count) override; virtual void body_set_pickable(RID p_body, bool p_pickable) override; diff --git a/servers/physics_2d/physics_server_2d_wrap_mt.h b/servers/physics_2d/physics_server_2d_wrap_mt.h index 05d4ac64b0..6761e37daa 100644 --- a/servers/physics_2d/physics_server_2d_wrap_mt.h +++ b/servers/physics_2d/physics_server_2d_wrap_mt.h @@ -245,6 +245,7 @@ public: FUNC2(body_set_omit_force_integration, RID, bool); FUNC1RC(bool, body_is_omitting_force_integration, RID); + FUNC3(body_set_state_sync_callback, RID, void *, BodyStateCallback); FUNC3(body_set_force_integration_callback, RID, const Callable &, const Variant &); bool body_collide_shape(RID p_body, int p_body_shape, RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, Vector2 *r_results, int p_result_max, int &r_result_count) override { diff --git a/servers/physics_2d/space_2d_sw.cpp b/servers/physics_2d/space_2d_sw.cpp index c0493749e0..6c23f49928 100644 --- a/servers/physics_2d/space_2d_sw.cpp +++ b/servers/physics_2d/space_2d_sw.cpp @@ -634,7 +634,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co //fix for moving platforms (kinematic and dynamic), margin is increased by how much it moved in the given direction Vector2 lv = b->get_linear_velocity(); //compute displacement from linear velocity - Vector2 motion = lv * PhysicsDirectBodyState2DSW::singleton->step; + Vector2 motion = lv * last_step; real_t motion_len = motion.length(); motion.normalize(); cbk.valid_depth += motion_len * MAX(motion.dot(-cbk.valid_dir), 0.0); @@ -926,7 +926,7 @@ bool Space2DSW::test_body_motion(Body2DSW *p_body, const Transform2D &p_from, co //fix for moving platforms (kinematic and dynamic), margin is increased by how much it moved in the given direction Vector2 lv = b->get_linear_velocity(); //compute displacement from linear velocity - Vector2 motion = lv * PhysicsDirectBodyState2DSW::singleton->step; + Vector2 motion = lv * last_step; real_t motion_len = motion.length(); motion.normalize(); rcd.valid_depth += motion_len * MAX(motion.dot(-rcd.valid_dir), 0.0); diff --git a/servers/physics_2d/space_2d_sw.h b/servers/physics_2d/space_2d_sw.h index 392df9b025..5de071a475 100644 --- a/servers/physics_2d/space_2d_sw.h +++ b/servers/physics_2d/space_2d_sw.h @@ -117,6 +117,8 @@ private: bool locked; + real_t last_step = 0.001; + int island_count; int active_objects; int collision_pairs; @@ -172,6 +174,9 @@ public: void lock(); void unlock(); + real_t get_last_step() const { return last_step; } + void set_last_step(real_t p_step) { last_step = p_step; } + void set_param(PhysicsServer2D::SpaceParameter p_param, real_t p_value); real_t get_param(PhysicsServer2D::SpaceParameter p_param) const; diff --git a/servers/physics_2d/step_2d_sw.cpp b/servers/physics_2d/step_2d_sw.cpp index 8b30160cc1..0306ec5050 100644 --- a/servers/physics_2d/step_2d_sw.cpp +++ b/servers/physics_2d/step_2d_sw.cpp @@ -129,6 +129,8 @@ void Step2DSW::step(Space2DSW *p_space, real_t p_delta, int p_iterations) { p_space->setup(); //update inertias, etc + p_space->set_last_step(p_delta); + iterations = p_iterations; delta = p_delta; diff --git a/servers/physics_3d/body_3d_sw.cpp b/servers/physics_3d/body_3d_sw.cpp index 397a38079b..7f62e4eb85 100644 --- a/servers/physics_3d/body_3d_sw.cpp +++ b/servers/physics_3d/body_3d_sw.cpp @@ -29,7 +29,9 @@ /*************************************************************************/ #include "body_3d_sw.h" + #include "area_3d_sw.h" +#include "body_direct_state_3d_sw.h" #include "space_3d_sw.h" void Body3DSW::_update_inertia() { @@ -536,7 +538,7 @@ void Body3DSW::integrate_velocities(real_t p_step) { return; } - if (fi_callback) { + if (fi_callback_data || body_state_callback) { get_space()->body_add_to_state_query_list(&direct_state_query_list); } @@ -593,11 +595,6 @@ void Body3DSW::integrate_velocities(real_t p_step) { _set_inv_transform(get_transform().inverse()); _update_transform_dependant(); - - /* - if (fi_callback) { - get_space()->body_add_to_state_query_list(&direct_state_query_list); - */ } /* @@ -655,24 +652,23 @@ void Body3DSW::wakeup_neighbours() { } void Body3DSW::call_queries() { - if (fi_callback) { - PhysicsDirectBodyState3DSW *dbs = PhysicsDirectBodyState3DSW::singleton; - dbs->body = this; - - Variant v = dbs; - - Object *obj = fi_callback->callable.get_object(); - if (!obj) { + if (fi_callback_data) { + if (!fi_callback_data->callable.get_object()) { set_force_integration_callback(Callable()); } else { - const Variant *vp[2] = { &v, &fi_callback->udata }; + Variant direct_state_variant = get_direct_state(); + const Variant *vp[2] = { &direct_state_variant, &fi_callback_data->udata }; Callable::CallError ce; - int argc = (fi_callback->udata.get_type() == Variant::NIL) ? 1 : 2; + int argc = (fi_callback_data->udata.get_type() == Variant::NIL) ? 1 : 2; Variant rv; - fi_callback->callable.call(vp, argc, rv, ce); + fi_callback_data->callable.call(vp, argc, rv, ce); } } + + if (body_state_callback_instance) { + (body_state_callback)(body_state_callback_instance, get_direct_state()); + } } bool Body3DSW::sleep_test(real_t p_step) { @@ -692,22 +688,34 @@ bool Body3DSW::sleep_test(real_t p_step) { } } +void Body3DSW::set_state_sync_callback(void *p_instance, PhysicsServer3D::BodyStateCallback p_callback) { + body_state_callback_instance = p_instance; + body_state_callback = p_callback; +} + void Body3DSW::set_force_integration_callback(const Callable &p_callable, const Variant &p_udata) { - if (fi_callback) { - memdelete(fi_callback); - fi_callback = nullptr; + if (p_callable.get_object()) { + if (!fi_callback_data) { + fi_callback_data = memnew(ForceIntegrationCallbackData); + } + fi_callback_data->callable = p_callable; + fi_callback_data->udata = p_udata; + } else if (fi_callback_data) { + memdelete(fi_callback_data); + fi_callback_data = nullptr; } +} - if (p_callable.get_object()) { - fi_callback = memnew(ForceIntegrationCallback); - fi_callback->callable = p_callable; - fi_callback->udata = p_udata; +PhysicsDirectBodyState3DSW *Body3DSW::get_direct_state() { + if (!direct_state) { + direct_state = memnew(PhysicsDirectBodyState3DSW); + direct_state->body = this; } + return direct_state; } Body3DSW::Body3DSW() : CollisionObject3DSW(TYPE_BODY), - active_list(this), inertia_update_list(this), direct_state_query_list(this) { @@ -735,17 +743,13 @@ Body3DSW::Body3DSW() : still_time = 0; continuous_cd = false; can_sleep = true; - fi_callback = nullptr; } Body3DSW::~Body3DSW() { - if (fi_callback) { - memdelete(fi_callback); + if (fi_callback_data) { + memdelete(fi_callback_data); + } + if (direct_state) { + memdelete(direct_state); } -} - -PhysicsDirectBodyState3DSW *PhysicsDirectBodyState3DSW::singleton = nullptr; - -PhysicsDirectSpaceState3D *PhysicsDirectBodyState3DSW::get_space_state() { - return body->get_space()->get_direct_state(); } diff --git a/servers/physics_3d/body_3d_sw.h b/servers/physics_3d/body_3d_sw.h index 130be2d42f..f58f40652b 100644 --- a/servers/physics_3d/body_3d_sw.h +++ b/servers/physics_3d/body_3d_sw.h @@ -28,14 +28,15 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef BODY_SW_H -#define BODY_SW_H +#ifndef BODY_3D_SW_H +#define BODY_3D_SW_H #include "area_3d_sw.h" #include "collision_object_3d_sw.h" #include "core/templates/vset.h" class Constraint3DSW; +class PhysicsDirectBodyState3DSW; class Body3DSW : public CollisionObject3DSW { PhysicsServer3D::BodyMode mode; @@ -113,12 +114,17 @@ class Body3DSW : public CollisionObject3DSW { Vector<Contact> contacts; //no contacts by default int contact_count; - struct ForceIntegrationCallback { + void *body_state_callback_instance = nullptr; + PhysicsServer3D::BodyStateCallback body_state_callback = nullptr; + + struct ForceIntegrationCallbackData { Callable callable; Variant udata; }; - ForceIntegrationCallback *fi_callback; + ForceIntegrationCallbackData *fi_callback_data = nullptr; + + PhysicsDirectBodyState3DSW *direct_state = nullptr; uint64_t island_step; @@ -129,8 +135,11 @@ class Body3DSW : public CollisionObject3DSW { friend class PhysicsDirectBodyState3DSW; // i give up, too many functions to expose public: + void set_state_sync_callback(void *p_instance, PhysicsServer3D::BodyStateCallback p_callback); void set_force_integration_callback(const Callable &p_callable, const Variant &p_udata = Variant()); + PhysicsDirectBodyState3DSW *get_direct_state(); + _FORCE_INLINE_ void add_area(Area3DSW *p_area) { int index = areas.find(AreaCMP(p_area)); if (index > -1) { @@ -349,96 +358,4 @@ void Body3DSW::add_contact(const Vector3 &p_local_pos, const Vector3 &p_local_no c[idx].collider_velocity_at_pos = p_collider_velocity_at_pos; } -class PhysicsDirectBodyState3DSW : public PhysicsDirectBodyState3D { - GDCLASS(PhysicsDirectBodyState3DSW, PhysicsDirectBodyState3D); - -public: - static PhysicsDirectBodyState3DSW *singleton; - Body3DSW *body; - real_t step; - - virtual Vector3 get_total_gravity() const override { return body->gravity; } // get gravity vector working on this body space/area - virtual real_t get_total_angular_damp() const override { return body->area_angular_damp; } // get density of this body space/area - virtual real_t get_total_linear_damp() const override { return body->area_linear_damp; } // get density of this body space/area - - virtual Vector3 get_center_of_mass() const override { return body->get_center_of_mass(); } - virtual Basis get_principal_inertia_axes() const override { return body->get_principal_inertia_axes(); } - - virtual real_t get_inverse_mass() const override { return body->get_inv_mass(); } // get the mass - virtual Vector3 get_inverse_inertia() const override { return body->get_inv_inertia(); } // get density of this body space - virtual Basis get_inverse_inertia_tensor() const override { return body->get_inv_inertia_tensor(); } // get density of this body space - - virtual void set_linear_velocity(const Vector3 &p_velocity) override { body->set_linear_velocity(p_velocity); } - virtual Vector3 get_linear_velocity() const override { return body->get_linear_velocity(); } - - virtual void set_angular_velocity(const Vector3 &p_velocity) override { body->set_angular_velocity(p_velocity); } - virtual Vector3 get_angular_velocity() const override { return body->get_angular_velocity(); } - - virtual void set_transform(const Transform3D &p_transform) override { body->set_state(PhysicsServer3D::BODY_STATE_TRANSFORM, p_transform); } - virtual Transform3D get_transform() const override { return body->get_transform(); } - - virtual Vector3 get_velocity_at_local_position(const Vector3 &p_position) const override { return body->get_velocity_in_local_point(p_position); } - - virtual void add_central_force(const Vector3 &p_force) override { body->add_central_force(p_force); } - virtual void add_force(const Vector3 &p_force, const Vector3 &p_position = Vector3()) override { - body->add_force(p_force, p_position); - } - virtual void add_torque(const Vector3 &p_torque) override { body->add_torque(p_torque); } - virtual void apply_central_impulse(const Vector3 &p_impulse) override { body->apply_central_impulse(p_impulse); } - virtual void apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position = Vector3()) override { - body->apply_impulse(p_impulse, p_position); - } - virtual void apply_torque_impulse(const Vector3 &p_impulse) override { body->apply_torque_impulse(p_impulse); } - - virtual void set_sleep_state(bool p_sleep) override { body->set_active(!p_sleep); } - virtual bool is_sleeping() const override { return !body->is_active(); } - - virtual int get_contact_count() const override { return body->contact_count; } - - virtual Vector3 get_contact_local_position(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector3()); - return body->contacts[p_contact_idx].local_pos; - } - virtual Vector3 get_contact_local_normal(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector3()); - return body->contacts[p_contact_idx].local_normal; - } - virtual real_t get_contact_impulse(int p_contact_idx) const override { - return 0.0f; // Only implemented for bullet - } - virtual int get_contact_local_shape(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, -1); - return body->contacts[p_contact_idx].local_shape; - } - - virtual RID get_contact_collider(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, RID()); - return body->contacts[p_contact_idx].collider; - } - virtual Vector3 get_contact_collider_position(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector3()); - return body->contacts[p_contact_idx].collider_pos; - } - virtual ObjectID get_contact_collider_id(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, ObjectID()); - return body->contacts[p_contact_idx].collider_instance_id; - } - virtual int get_contact_collider_shape(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, 0); - return body->contacts[p_contact_idx].collider_shape; - } - virtual Vector3 get_contact_collider_velocity_at_position(int p_contact_idx) const override { - ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector3()); - return body->contacts[p_contact_idx].collider_velocity_at_pos; - } - - virtual PhysicsDirectSpaceState3D *get_space_state() override; - - virtual real_t get_step() const override { return step; } - PhysicsDirectBodyState3DSW() { - singleton = this; - body = nullptr; - } -}; - -#endif // BODY__SW_H +#endif // BODY_3D_SW_H diff --git a/servers/physics_3d/body_direct_state_3d_sw.cpp b/servers/physics_3d/body_direct_state_3d_sw.cpp new file mode 100644 index 0000000000..d197dd288d --- /dev/null +++ b/servers/physics_3d/body_direct_state_3d_sw.cpp @@ -0,0 +1,182 @@ +/*************************************************************************/ +/* body_direct_state_3d_sw.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 "body_direct_state_3d_sw.h" + +#include "body_3d_sw.h" +#include "space_3d_sw.h" + +Vector3 PhysicsDirectBodyState3DSW::get_total_gravity() const { + return body->gravity; +} + +real_t PhysicsDirectBodyState3DSW::get_total_angular_damp() const { + return body->area_angular_damp; +} + +real_t PhysicsDirectBodyState3DSW::get_total_linear_damp() const { + return body->area_linear_damp; +} + +Vector3 PhysicsDirectBodyState3DSW::get_center_of_mass() const { + return body->get_center_of_mass(); +} + +Basis PhysicsDirectBodyState3DSW::get_principal_inertia_axes() const { + return body->get_principal_inertia_axes(); +} + +real_t PhysicsDirectBodyState3DSW::get_inverse_mass() const { + return body->get_inv_mass(); +} + +Vector3 PhysicsDirectBodyState3DSW::get_inverse_inertia() const { + return body->get_inv_inertia(); +} + +Basis PhysicsDirectBodyState3DSW::get_inverse_inertia_tensor() const { + return body->get_inv_inertia_tensor(); +} + +void PhysicsDirectBodyState3DSW::set_linear_velocity(const Vector3 &p_velocity) { + body->set_linear_velocity(p_velocity); +} + +Vector3 PhysicsDirectBodyState3DSW::get_linear_velocity() const { + return body->get_linear_velocity(); +} + +void PhysicsDirectBodyState3DSW::set_angular_velocity(const Vector3 &p_velocity) { + body->set_angular_velocity(p_velocity); +} + +Vector3 PhysicsDirectBodyState3DSW::get_angular_velocity() const { + return body->get_angular_velocity(); +} + +void PhysicsDirectBodyState3DSW::set_transform(const Transform3D &p_transform) { + body->set_state(PhysicsServer3D::BODY_STATE_TRANSFORM, p_transform); +} + +Transform3D PhysicsDirectBodyState3DSW::get_transform() const { + return body->get_transform(); +} + +Vector3 PhysicsDirectBodyState3DSW::get_velocity_at_local_position(const Vector3 &p_position) const { + return body->get_velocity_in_local_point(p_position); +} + +void PhysicsDirectBodyState3DSW::add_central_force(const Vector3 &p_force) { + body->add_central_force(p_force); +} + +void PhysicsDirectBodyState3DSW::add_force(const Vector3 &p_force, const Vector3 &p_position) { + body->add_force(p_force, p_position); +} + +void PhysicsDirectBodyState3DSW::add_torque(const Vector3 &p_torque) { + body->add_torque(p_torque); +} + +void PhysicsDirectBodyState3DSW::apply_central_impulse(const Vector3 &p_impulse) { + body->apply_central_impulse(p_impulse); +} + +void PhysicsDirectBodyState3DSW::apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position) { + body->apply_impulse(p_impulse, p_position); +} + +void PhysicsDirectBodyState3DSW::apply_torque_impulse(const Vector3 &p_impulse) { + body->apply_torque_impulse(p_impulse); +} + +void PhysicsDirectBodyState3DSW::set_sleep_state(bool p_sleep) { + body->set_active(!p_sleep); +} + +bool PhysicsDirectBodyState3DSW::is_sleeping() const { + return !body->is_active(); +} + +int PhysicsDirectBodyState3DSW::get_contact_count() const { + return body->contact_count; +} + +Vector3 PhysicsDirectBodyState3DSW::get_contact_local_position(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector3()); + return body->contacts[p_contact_idx].local_pos; +} + +Vector3 PhysicsDirectBodyState3DSW::get_contact_local_normal(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector3()); + return body->contacts[p_contact_idx].local_normal; +} + +real_t PhysicsDirectBodyState3DSW::get_contact_impulse(int p_contact_idx) const { + return 0.0f; // Only implemented for bullet +} + +int PhysicsDirectBodyState3DSW::get_contact_local_shape(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, -1); + return body->contacts[p_contact_idx].local_shape; +} + +RID PhysicsDirectBodyState3DSW::get_contact_collider(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, RID()); + return body->contacts[p_contact_idx].collider; +} + +Vector3 PhysicsDirectBodyState3DSW::get_contact_collider_position(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector3()); + return body->contacts[p_contact_idx].collider_pos; +} + +ObjectID PhysicsDirectBodyState3DSW::get_contact_collider_id(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, ObjectID()); + return body->contacts[p_contact_idx].collider_instance_id; +} + +int PhysicsDirectBodyState3DSW::get_contact_collider_shape(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, 0); + return body->contacts[p_contact_idx].collider_shape; +} + +Vector3 PhysicsDirectBodyState3DSW::get_contact_collider_velocity_at_position(int p_contact_idx) const { + ERR_FAIL_INDEX_V(p_contact_idx, body->contact_count, Vector3()); + return body->contacts[p_contact_idx].collider_velocity_at_pos; +} + +PhysicsDirectSpaceState3D *PhysicsDirectBodyState3DSW::get_space_state() { + return body->get_space()->get_direct_state(); +} + +real_t PhysicsDirectBodyState3DSW::get_step() const { + return body->get_space()->get_last_step(); +} diff --git a/servers/physics_3d/body_direct_state_3d_sw.h b/servers/physics_3d/body_direct_state_3d_sw.h new file mode 100644 index 0000000000..5132376715 --- /dev/null +++ b/servers/physics_3d/body_direct_state_3d_sw.h @@ -0,0 +1,94 @@ +/*************************************************************************/ +/* body_direct_state_3d_sw.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 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 BODY_DIRECT_STATE_3D_SW_H +#define BODY_DIRECT_STATE_3D_SW_H + +#include "servers/physics_server_3d.h" + +class Body3DSW; + +class PhysicsDirectBodyState3DSW : public PhysicsDirectBodyState3D { + GDCLASS(PhysicsDirectBodyState3DSW, PhysicsDirectBodyState3D); + +public: + Body3DSW *body = nullptr; + + virtual Vector3 get_total_gravity() const override; + virtual real_t get_total_angular_damp() const override; + virtual real_t get_total_linear_damp() const override; + + virtual Vector3 get_center_of_mass() const override; + virtual Basis get_principal_inertia_axes() const override; + + virtual real_t get_inverse_mass() const override; + virtual Vector3 get_inverse_inertia() const override; + virtual Basis get_inverse_inertia_tensor() const override; + + virtual void set_linear_velocity(const Vector3 &p_velocity) override; + virtual Vector3 get_linear_velocity() const override; + + virtual void set_angular_velocity(const Vector3 &p_velocity) override; + virtual Vector3 get_angular_velocity() const override; + + virtual void set_transform(const Transform3D &p_transform) override; + virtual Transform3D get_transform() const override; + + virtual Vector3 get_velocity_at_local_position(const Vector3 &p_position) const override; + + virtual void add_central_force(const Vector3 &p_force) override; + virtual void add_force(const Vector3 &p_force, const Vector3 &p_position = Vector3()) override; + virtual void add_torque(const Vector3 &p_torque) override; + virtual void apply_central_impulse(const Vector3 &p_impulse) override; + virtual void apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position = Vector3()) override; + virtual void apply_torque_impulse(const Vector3 &p_impulse) override; + + virtual void set_sleep_state(bool p_sleep) override; + virtual bool is_sleeping() const override; + + virtual int get_contact_count() const override; + + virtual Vector3 get_contact_local_position(int p_contact_idx) const override; + virtual Vector3 get_contact_local_normal(int p_contact_idx) const override; + virtual real_t get_contact_impulse(int p_contact_idx) const override; + virtual int get_contact_local_shape(int p_contact_idx) const override; + + virtual RID get_contact_collider(int p_contact_idx) const override; + virtual Vector3 get_contact_collider_position(int p_contact_idx) const override; + virtual ObjectID get_contact_collider_id(int p_contact_idx) const override; + virtual int get_contact_collider_shape(int p_contact_idx) const override; + virtual Vector3 get_contact_collider_velocity_at_position(int p_contact_idx) const override; + + virtual PhysicsDirectSpaceState3D *get_space_state() override; + + virtual real_t get_step() const override; +}; + +#endif // BODY_DIRECT_STATE_3D_SW_H diff --git a/servers/physics_3d/physics_server_3d_sw.cpp b/servers/physics_3d/physics_server_3d_sw.cpp index fbc6f7eee8..a214e80c6c 100644 --- a/servers/physics_3d/physics_server_3d_sw.cpp +++ b/servers/physics_3d/physics_server_3d_sw.cpp @@ -30,6 +30,7 @@ #include "physics_server_3d_sw.h" +#include "body_direct_state_3d_sw.h" #include "broad_phase_3d_bvh.h" #include "core/debugger/engine_debugger.h" #include "core/os/os.h" @@ -842,6 +843,12 @@ int PhysicsServer3DSW::body_get_max_contacts_reported(RID p_body) const { return body->get_max_contacts_reported(); } +void PhysicsServer3DSW::body_set_state_sync_callback(RID p_body, void *p_instance, BodyStateCallback p_callback) { + Body3DSW *body = body_owner.getornull(p_body); + ERR_FAIL_COND(!body); + body->set_state_sync_callback(p_instance, p_callback); +} + void PhysicsServer3DSW::body_set_force_integration_callback(RID p_body, const Callable &p_callable, const Variant &p_udata) { Body3DSW *body = body_owner.getornull(p_body); ERR_FAIL_COND(!body); @@ -869,11 +876,12 @@ PhysicsDirectBodyState3D *PhysicsServer3DSW::body_get_direct_state(RID p_body) { ERR_FAIL_COND_V_MSG((using_threads && !doing_sync), nullptr, "Body state is inaccessible right now, wait for iteration or physics process notification."); Body3DSW *body = body_owner.getornull(p_body); - ERR_FAIL_COND_V(!body, nullptr); + ERR_FAIL_NULL_V(body, nullptr); + + ERR_FAIL_NULL_V(body->get_space(), nullptr); ERR_FAIL_COND_V_MSG(body->get_space()->is_locked(), nullptr, "Body state is inaccessible right now, wait for iteration or physics process notification."); - direct_state->body = body; - return direct_state; + return body->get_direct_state(); } /* SOFT BODY */ @@ -1578,10 +1586,8 @@ void PhysicsServer3DSW::set_collision_iterations(int p_iterations) { }; void PhysicsServer3DSW::init() { - last_step = 0.001; iterations = 8; // 8? stepper = memnew(Step3DSW); - direct_state = memnew(PhysicsDirectBodyState3DSW); }; void PhysicsServer3DSW::step(real_t p_step) { @@ -1593,9 +1599,6 @@ void PhysicsServer3DSW::step(real_t p_step) { _update_shapes(); - last_step = p_step; - PhysicsDirectBodyState3DSW::singleton->step = p_step; - island_count = 0; active_objects = 0; collision_pairs = 0; @@ -1671,7 +1674,6 @@ void PhysicsServer3DSW::end_sync() { void PhysicsServer3DSW::finish() { memdelete(stepper); - memdelete(direct_state); }; int PhysicsServer3DSW::get_process_info(ProcessInfo p_info) { diff --git a/servers/physics_3d/physics_server_3d_sw.h b/servers/physics_3d/physics_server_3d_sw.h index a18593c90c..f283f83112 100644 --- a/servers/physics_3d/physics_server_3d_sw.h +++ b/servers/physics_3d/physics_server_3d_sw.h @@ -44,7 +44,6 @@ class PhysicsServer3DSW : public PhysicsServer3D { friend class PhysicsDirectSpaceState3DSW; bool active; int iterations; - real_t last_step; int island_count; int active_objects; @@ -57,8 +56,6 @@ class PhysicsServer3DSW : public PhysicsServer3D { Step3DSW *stepper; Set<const Space3DSW *> active_spaces; - PhysicsDirectBodyState3DSW *direct_state; - mutable RID_PtrOwner<Shape3DSW, true> shape_owner; mutable RID_PtrOwner<Space3DSW, true> space_owner; mutable RID_PtrOwner<Area3DSW, true> area_owner; @@ -238,6 +235,7 @@ public: virtual void body_set_max_contacts_reported(RID p_body, int p_contacts) override; virtual int body_get_max_contacts_reported(RID p_body) const override; + virtual void body_set_state_sync_callback(RID p_body, void *p_instance, BodyStateCallback p_callback) override; virtual void body_set_force_integration_callback(RID p_body, const Callable &p_callable, const Variant &p_udata = Variant()) override; virtual void body_set_ray_pickable(RID p_body, bool p_enable) override; diff --git a/servers/physics_3d/physics_server_3d_wrap_mt.h b/servers/physics_3d/physics_server_3d_wrap_mt.h index 674c52d4a3..8865bd4bd2 100644 --- a/servers/physics_3d/physics_server_3d_wrap_mt.h +++ b/servers/physics_3d/physics_server_3d_wrap_mt.h @@ -246,6 +246,7 @@ public: FUNC2(body_set_omit_force_integration, RID, bool); FUNC1RC(bool, body_is_omitting_force_integration, RID); + FUNC3(body_set_state_sync_callback, RID, void *, BodyStateCallback); FUNC3(body_set_force_integration_callback, RID, const Callable &, const Variant &); FUNC2(body_set_ray_pickable, RID, bool); diff --git a/servers/physics_3d/space_3d_sw.h b/servers/physics_3d/space_3d_sw.h index 1c3d1cf9f6..eff494a7bc 100644 --- a/servers/physics_3d/space_3d_sw.h +++ b/servers/physics_3d/space_3d_sw.h @@ -112,6 +112,8 @@ private: bool locked; + real_t last_step = 0.001; + int island_count; int active_objects; int collision_pairs; @@ -174,6 +176,9 @@ public: void lock(); void unlock(); + real_t get_last_step() const { return last_step; } + void set_last_step(real_t p_step) { last_step = p_step; } + void set_param(PhysicsServer3D::SpaceParameter p_param, real_t p_value); real_t get_param(PhysicsServer3D::SpaceParameter p_param) const; diff --git a/servers/physics_3d/step_3d_sw.cpp b/servers/physics_3d/step_3d_sw.cpp index ba18bac611..d0604b0aa0 100644 --- a/servers/physics_3d/step_3d_sw.cpp +++ b/servers/physics_3d/step_3d_sw.cpp @@ -185,6 +185,8 @@ void Step3DSW::step(Space3DSW *p_space, real_t p_delta, int p_iterations) { p_space->setup(); //update inertias, etc + p_space->set_last_step(p_delta); + iterations = p_iterations; delta = p_delta; diff --git a/servers/physics_server_2d.h b/servers/physics_server_2d.h index 9afac0cbd1..021d7be7c0 100644 --- a/servers/physics_server_2d.h +++ b/servers/physics_server_2d.h @@ -457,6 +457,10 @@ public: virtual void body_set_omit_force_integration(RID p_body, bool p_omit) = 0; virtual bool body_is_omitting_force_integration(RID p_body) const = 0; + // Callback for C++ use only. + typedef void (*BodyStateCallback)(void *p_instance, PhysicsDirectBodyState2D *p_state); + virtual void body_set_state_sync_callback(RID p_body, void *p_instance, BodyStateCallback p_callback) = 0; + virtual void body_set_force_integration_callback(RID p_body, const Callable &p_callable, const Variant &p_udata = Variant()) = 0; virtual bool body_collide_shape(RID p_body, int p_body_shape, RID p_shape, const Transform2D &p_shape_xform, const Vector2 &p_motion, Vector2 *r_results, int p_result_max, int &r_result_count) = 0; diff --git a/servers/physics_server_3d.h b/servers/physics_server_3d.h index 8f83e360f2..a365802220 100644 --- a/servers/physics_server_3d.h +++ b/servers/physics_server_3d.h @@ -471,6 +471,10 @@ public: virtual void body_set_omit_force_integration(RID p_body, bool p_omit) = 0; virtual bool body_is_omitting_force_integration(RID p_body) const = 0; + // Callback for C++ use only. + typedef void (*BodyStateCallback)(void *p_instance, PhysicsDirectBodyState3D *p_state); + virtual void body_set_state_sync_callback(RID p_body, void *p_instance, BodyStateCallback p_callback) = 0; + virtual void body_set_force_integration_callback(RID p_body, const Callable &p_callable, const Variant &p_udata = Variant()) = 0; virtual void body_set_ray_pickable(RID p_body, bool p_enable) = 0; diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index 611f7c6494..1b730567d9 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -484,7 +484,7 @@ void RenderForwardClustered::_render_list_template(RenderingDevice::DrawListID p if (material_uniform_set != prev_material_uniform_set) { // Update uniform set. - if (RD::get_singleton()->uniform_set_is_valid(material_uniform_set)) { // Material may not have a uniform set. + if (material_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(material_uniform_set)) { // Material may not have a uniform set. RD::get_singleton()->draw_list_bind_uniform_set(draw_list, material_uniform_set, MATERIAL_UNIFORM_SET); } diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index 1b4052b622..276a44bc27 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -1982,7 +1982,7 @@ void RenderForwardMobile::_render_list_template(RenderingDevice::DrawListID p_dr if (material_uniform_set != prev_material_uniform_set) { // Update uniform set. - if (RD::get_singleton()->uniform_set_is_valid(material_uniform_set)) { // Material may not have a uniform set. + if (material_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(material_uniform_set)) { // Material may not have a uniform set. RD::get_singleton()->draw_list_bind_uniform_set(draw_list, material_uniform_set, MATERIAL_UNIFORM_SET); } diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index 6267f684e3..647c348d9f 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -1102,7 +1102,7 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co if (material_data->shader_data->version.is_valid() && material_data->shader_data->valid) { pipeline_variants = &material_data->shader_data->pipeline_variants; // Update uniform set. - if (RD::get_singleton()->uniform_set_is_valid(material_data->uniform_set)) { // Material may not have a uniform set. + if (material_data->uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(material_data->uniform_set)) { // Material may not have a uniform set. RD::get_singleton()->draw_list_bind_uniform_set(draw_list, material_data->uniform_set, MATERIAL_UNIFORM_SET); } } else { diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp index da84f615b2..c388da755c 100644 --- a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp @@ -288,7 +288,7 @@ void RendererSceneSkyRD::_render_sky(RD::DrawListID p_list, float p_time, RID p_ // Update uniform sets. { RD::get_singleton()->draw_list_bind_uniform_set(draw_list, sky_scene_state.uniform_set, 0); - if (RD::get_singleton()->uniform_set_is_valid(p_uniform_set)) { // Material may not have a uniform set. + if (p_uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(p_uniform_set)) { // Material may not have a uniform set. RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_uniform_set, 1); } RD::get_singleton()->draw_list_bind_uniform_set(draw_list, p_texture_set, 2); diff --git a/servers/rendering/renderer_rd/renderer_storage_rd.cpp b/servers/rendering/renderer_rd/renderer_storage_rd.cpp index 14a5a01054..ec0d25376f 100644 --- a/servers/rendering/renderer_rd/renderer_storage_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_storage_rd.cpp @@ -4848,7 +4848,7 @@ void RendererStorageRD::_particles_process(Particles *p_particles, double p_delt RD::get_singleton()->compute_list_bind_uniform_set(compute_list, p_particles->particles_material_uniform_set, 1); RD::get_singleton()->compute_list_bind_uniform_set(compute_list, p_particles->collision_textures_uniform_set, 2); - if (m->uniform_set.is_valid()) { + if (m->uniform_set.is_valid() && RD::get_singleton()->uniform_set_is_valid(m->uniform_set)) { RD::get_singleton()->compute_list_bind_uniform_set(compute_list, m->uniform_set, 3); } |