summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/core_bind.cpp7
-rw-r--r--core/core_bind.h1
-rw-r--r--core/input/input.cpp9
-rw-r--r--core/input/input.h4
-rw-r--r--doc/classes/Geometry3D.xml11
-rw-r--r--doc/classes/Input.xml14
-rw-r--r--doc/classes/PhysicsDirectSpaceState3D.xml2
-rw-r--r--doc/classes/RayCast3D.xml6
-rw-r--r--editor/plugins/curve_editor_plugin.cpp5
-rw-r--r--editor/plugins/curve_editor_plugin.h2
-rw-r--r--editor/plugins/gpu_particles_2d_editor_plugin.cpp7
-rw-r--r--editor/plugins/gpu_particles_3d_editor_plugin.cpp7
-rw-r--r--editor/plugins/sprite_2d_editor_plugin.cpp14
-rw-r--r--editor/scene_tree_dock.cpp112
-rw-r--r--editor/scene_tree_dock.h5
-rw-r--r--platform/linuxbsd/joypad_linux.cpp25
-rw-r--r--platform/windows/joypad_windows.cpp4
-rw-r--r--scene/3d/ray_cast_3d.cpp6
-rw-r--r--scene/3d/ray_cast_3d.h2
-rw-r--r--servers/physics_3d/godot_body_pair_3d.cpp4
-rw-r--r--servers/physics_3d/godot_collision_solver_3d.cpp3
-rw-r--r--servers/physics_3d/godot_shape_3d.cpp29
-rw-r--r--servers/physics_3d/godot_shape_3d.h25
-rw-r--r--servers/physics_3d/godot_soft_body_3d.cpp2
-rw-r--r--servers/physics_3d/godot_soft_body_3d.h2
-rw-r--r--servers/physics_3d/godot_space_3d.cpp6
-rw-r--r--servers/physics_server_3d.cpp1
-rw-r--r--servers/physics_server_3d.h1
28 files changed, 250 insertions, 66 deletions
diff --git a/core/core_bind.cpp b/core/core_bind.cpp
index a73b198be2..4e220d0839 100644
--- a/core/core_bind.cpp
+++ b/core/core_bind.cpp
@@ -961,6 +961,11 @@ Vector3 Geometry3D::get_closest_point_to_segment_uncapped(const Vector3 &p_point
return ::Geometry3D::get_closest_point_to_segment_uncapped(p_point, s);
}
+Vector3 Geometry3D::get_triangle_barycentric_coords(const Vector3 &p_point, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2) {
+ Vector3 res = ::Geometry3D::triangle_get_barycentric_coords(p_v0, p_v1, p_v2, p_point);
+ return res;
+}
+
Variant Geometry3D::ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2) {
Vector3 res;
if (::Geometry3D::ray_intersects_triangle(p_from, p_dir, p_v0, p_v1, p_v2, &res)) {
@@ -1034,6 +1039,8 @@ void Geometry3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_closest_point_to_segment_uncapped", "point", "s1", "s2"), &Geometry3D::get_closest_point_to_segment_uncapped);
+ ClassDB::bind_method(D_METHOD("get_triangle_barycentric_coords", "point", "a", "b", "c"), &Geometry3D::get_triangle_barycentric_coords);
+
ClassDB::bind_method(D_METHOD("ray_intersects_triangle", "from", "dir", "a", "b", "c"), &Geometry3D::ray_intersects_triangle);
ClassDB::bind_method(D_METHOD("segment_intersects_triangle", "from", "to", "a", "b", "c"), &Geometry3D::segment_intersects_triangle);
ClassDB::bind_method(D_METHOD("segment_intersects_sphere", "from", "to", "sphere_position", "sphere_radius"), &Geometry3D::segment_intersects_sphere);
diff --git a/core/core_bind.h b/core/core_bind.h
index dc0b2a1cf5..1cbbcdd251 100644
--- a/core/core_bind.h
+++ b/core/core_bind.h
@@ -326,6 +326,7 @@ public:
Vector<Vector3> get_closest_points_between_segments(const Vector3 &p1, const Vector3 &p2, const Vector3 &q1, const Vector3 &q2);
Vector3 get_closest_point_to_segment(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b);
Vector3 get_closest_point_to_segment_uncapped(const Vector3 &p_point, const Vector3 &p_a, const Vector3 &p_b);
+ Vector3 get_triangle_barycentric_coords(const Vector3 &p_point, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2);
Variant ray_intersects_triangle(const Vector3 &p_from, const Vector3 &p_dir, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2);
Variant segment_intersects_triangle(const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_v0, const Vector3 &p_v1, const Vector3 &p_v2);
diff --git a/core/input/input.cpp b/core/input/input.cpp
index d481acf005..4a32abfafa 100644
--- a/core/input/input.cpp
+++ b/core/input/input.cpp
@@ -113,6 +113,7 @@ void Input::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_joy_axis", "device", "axis"), &Input::get_joy_axis);
ClassDB::bind_method(D_METHOD("get_joy_name", "device"), &Input::get_joy_name);
ClassDB::bind_method(D_METHOD("get_joy_guid", "device"), &Input::get_joy_guid);
+ ClassDB::bind_method(D_METHOD("get_joy_info", "device"), &Input::get_joy_info);
ClassDB::bind_method(D_METHOD("should_ignore_device", "vendor_id", "product_id"), &Input::should_ignore_device);
ClassDB::bind_method(D_METHOD("get_connected_joypads"), &Input::get_connected_joypads);
ClassDB::bind_method(D_METHOD("get_joy_vibration_strength", "device"), &Input::get_joy_vibration_strength);
@@ -437,11 +438,12 @@ static String _hex_str(uint8_t p_byte) {
return ret;
}
-void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid) {
+void Input::joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid, Dictionary p_joypad_info) {
_THREAD_SAFE_METHOD_
Joypad js;
js.name = p_connected ? p_name : "";
js.uid = p_connected ? p_guid : "";
+ js.info = p_connected ? p_joypad_info : Dictionary();
if (p_connected) {
String uidname = p_guid;
@@ -1499,6 +1501,11 @@ String Input::get_joy_guid(int p_device) const {
return joy_names[p_device].uid;
}
+Dictionary Input::get_joy_info(int p_device) const {
+ ERR_FAIL_COND_V(!joy_names.has(p_device), Dictionary());
+ return joy_names[p_device].info;
+}
+
bool Input::should_ignore_device(int p_vendor_id, int p_product_id) const {
uint32_t full_id = (((uint32_t)p_vendor_id) << 16) | ((uint16_t)p_product_id);
return ignored_device_ids.has(full_id);
diff --git a/core/input/input.h b/core/input/input.h
index ec16871b72..c63a4e52e3 100644
--- a/core/input/input.h
+++ b/core/input/input.h
@@ -149,6 +149,7 @@ private:
HatMask last_hat = HatMask::CENTER;
int mapping = -1;
int hat_current = 0;
+ Dictionary info;
};
VelocityTrack mouse_velocity_track;
@@ -276,7 +277,7 @@ public:
Vector2 get_joy_vibration_strength(int p_device);
float get_joy_vibration_duration(int p_device);
uint64_t get_joy_vibration_timestamp(int p_device);
- void joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid = "");
+ void joy_connection_changed(int p_idx, bool p_connected, String p_name, String p_guid = "", Dictionary p_joypad_info = Dictionary());
Vector3 get_gravity() const;
Vector3 get_accelerometer() const;
@@ -332,6 +333,7 @@ public:
bool is_joy_known(int p_device);
String get_joy_guid(int p_device) const;
bool should_ignore_device(int p_vendor_id, int p_product_id) const;
+ Dictionary get_joy_info(int p_device) const;
void set_fallback_mapping(String p_guid);
void flush_buffered_events();
diff --git a/doc/classes/Geometry3D.xml b/doc/classes/Geometry3D.xml
index 014ebd2248..85b8965faf 100644
--- a/doc/classes/Geometry3D.xml
+++ b/doc/classes/Geometry3D.xml
@@ -73,6 +73,17 @@
Given the two 3D segments ([param p1], [param p2]) and ([param q1], [param q2]), finds those two points on the two segments that are closest to each other. Returns a [PackedVector3Array] that contains this point on ([param p1], [param p2]) as well the accompanying point on ([param q1], [param q2]).
</description>
</method>
+ <method name="get_triangle_barycentric_coords">
+ <return type="Vector3" />
+ <param index="0" name="point" type="Vector3" />
+ <param index="1" name="a" type="Vector3" />
+ <param index="2" name="b" type="Vector3" />
+ <param index="3" name="c" type="Vector3" />
+ <description>
+ Returns a [Vector3] containing weights based on how close a 3D position ([param point]) is to a triangle's different vertices ([param a], [param b] and [param c]). This is useful for interpolating between the data of different vertices in a triangle. One example use case is using this to smoothly rotate over a mesh instead of relying solely on face normals.
+ [url=https://en.wikipedia.org/wiki/Barycentric_coordinate_system]Here is a more detailed explanation of barycentric coordinates.[/url]
+ </description>
+ </method>
<method name="ray_intersects_triangle">
<return type="Variant" />
<param index="0" name="from" type="Vector3" />
diff --git a/doc/classes/Input.xml b/doc/classes/Input.xml
index c2a20827d2..6d907bbb28 100644
--- a/doc/classes/Input.xml
+++ b/doc/classes/Input.xml
@@ -121,6 +121,20 @@
Returns a SDL2-compatible device GUID on platforms that use gamepad remapping, e.g. [code]030000004c050000c405000000010000[/code]. Returns [code]"Default Gamepad"[/code] otherwise. Godot uses the [url=https://github.com/gabomdq/SDL_GameControllerDB]SDL2 game controller database[/url] to determine gamepad names and mappings based on this GUID.
</description>
</method>
+ <method name="get_joy_info" qualifiers="const">
+ <return type="Dictionary" />
+ <param index="0" name="device" type="int" />
+ <description>
+ Returns a dictionary with extra platform-specific information about the device, e.g. the raw gamepad name from the OS or the Steam Input index.
+ On Windows the dictionary contains the following fields:
+ [code]xinput_index[/code]: The index of the controller in the XInput system.
+ On Linux:
+ [code]raw_name[/code]: The name of the controller as it came from the OS, before getting renamed by the godot controller database.
+ [code]vendor_id[/code]: The USB vendor ID of the device.
+ [code]product_id[/code]: The USB product ID of the device.
+ [code]steam_input_index[/code]: The Steam Input gamepad index, if the device is not a Steam Input device this key won't be present.
+ </description>
+ </method>
<method name="get_joy_name">
<return type="String" />
<param index="0" name="device" type="int" />
diff --git a/doc/classes/PhysicsDirectSpaceState3D.xml b/doc/classes/PhysicsDirectSpaceState3D.xml
index 17324e49c6..b00dc4236d 100644
--- a/doc/classes/PhysicsDirectSpaceState3D.xml
+++ b/doc/classes/PhysicsDirectSpaceState3D.xml
@@ -67,6 +67,8 @@
[code]collider_id[/code]: The colliding object's ID.
[code]normal[/code]: The object's surface normal at the intersection point, or [code]Vector3(0, 0, 0)[/code] if the ray starts inside the shape and [member PhysicsRayQueryParameters3D.hit_from_inside] is [code]true[/code].
[code]position[/code]: The intersection point.
+ [code]face_index[/code]: The face index at the intersection point.
+ [b]Note:[/b] Returns a valid number only if the intersected shape is a [ConcavePolygonShape3D]. Otherwise, [code]-1[/code] is returned.
[code]rid[/code]: The intersecting object's [RID].
[code]shape[/code]: The shape index of the colliding shape.
If the ray did not intersect anything, then an empty dictionary is returned instead.
diff --git a/doc/classes/RayCast3D.xml b/doc/classes/RayCast3D.xml
index 83476a6d48..4c4db36aca 100644
--- a/doc/classes/RayCast3D.xml
+++ b/doc/classes/RayCast3D.xml
@@ -59,6 +59,12 @@
Returns the shape ID of the first object that the ray intersects, or [code]0[/code] if no object is intersecting the ray (i.e. [method is_colliding] returns [code]false[/code]).
</description>
</method>
+ <method name="get_collision_face_index" qualifiers="const">
+ <return type="int" />
+ <description>
+ Returns the collision object's face index at the collision point, or [code]-1[/code] if the shape intersecting the ray is not a [ConcavePolygonShape3D].
+ </description>
+ </method>
<method name="get_collision_mask_value" qualifiers="const">
<return type="bool" />
<param index="0" name="layer_number" type="int" />
diff --git a/editor/plugins/curve_editor_plugin.cpp b/editor/plugins/curve_editor_plugin.cpp
index 6228faaa72..6b5b0f9214 100644
--- a/editor/plugins/curve_editor_plugin.cpp
+++ b/editor/plugins/curve_editor_plugin.cpp
@@ -105,7 +105,7 @@ void CurveEdit::set_snap_count(int p_snap_count) {
}
Size2 CurveEdit::get_minimum_size() const {
- return Vector2(64, 135) * EDSCALE;
+ return Vector2(64, MAX(135, get_size().x * ASPECT_RATIO)) * EDSCALE;
}
void CurveEdit::_notification(int p_what) {
@@ -986,6 +986,9 @@ void CurveEditor::_notification(int p_what) {
snap_count_edit->set_value(curve->get_meta("_snap_count", DEFAULT_SNAP));
}
} break;
+ case NOTIFICATION_RESIZED:
+ curve_editor_rect->update_minimum_size();
+ break;
}
}
diff --git a/editor/plugins/curve_editor_plugin.h b/editor/plugins/curve_editor_plugin.h
index b8d24c5cbc..b6a74d9b93 100644
--- a/editor/plugins/curve_editor_plugin.h
+++ b/editor/plugins/curve_editor_plugin.h
@@ -104,6 +104,8 @@ private:
void _redraw();
private:
+ const float ASPECT_RATIO = 6.f / 13.f;
+
Transform2D _world_to_view;
Ref<Curve> curve;
diff --git a/editor/plugins/gpu_particles_2d_editor_plugin.cpp b/editor/plugins/gpu_particles_2d_editor_plugin.cpp
index 03d3ac1732..78bbc1484b 100644
--- a/editor/plugins/gpu_particles_2d_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_2d_editor_plugin.cpp
@@ -116,11 +116,8 @@ void GPUParticles2DEditorPlugin::_menu_callback(int p_idx) {
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Convert to CPUParticles2D"));
- ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", particles, cpu_particles, true, false);
- ur->add_do_reference(cpu_particles);
- ur->add_undo_method(SceneTreeDock::get_singleton(), "replace_node", cpu_particles, particles, false, false);
- ur->add_undo_reference(particles);
- ur->commit_action();
+ SceneTreeDock::get_singleton()->replace_node(particles, cpu_particles);
+ ur->commit_action(false);
} break;
case MENU_RESTART: {
diff --git a/editor/plugins/gpu_particles_3d_editor_plugin.cpp b/editor/plugins/gpu_particles_3d_editor_plugin.cpp
index f0b2e32c72..108f85152f 100644
--- a/editor/plugins/gpu_particles_3d_editor_plugin.cpp
+++ b/editor/plugins/gpu_particles_3d_editor_plugin.cpp
@@ -277,11 +277,8 @@ void GPUParticles3DEditor::_menu_option(int p_option) {
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Convert to CPUParticles3D"));
- ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, cpu_particles, true, false);
- ur->add_do_reference(cpu_particles);
- ur->add_undo_method(SceneTreeDock::get_singleton(), "replace_node", cpu_particles, node, false, false);
- ur->add_undo_reference(node);
- ur->commit_action();
+ SceneTreeDock::get_singleton()->replace_node(node, cpu_particles);
+ ur->commit_action(false);
} break;
case MENU_OPTION_RESTART: {
diff --git a/editor/plugins/sprite_2d_editor_plugin.cpp b/editor/plugins/sprite_2d_editor_plugin.cpp
index 0d00bfa8fd..f647bded95 100644
--- a/editor/plugins/sprite_2d_editor_plugin.cpp
+++ b/editor/plugins/sprite_2d_editor_plugin.cpp
@@ -346,11 +346,8 @@ void Sprite2DEditor::_convert_to_mesh_2d_node() {
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Convert to MeshInstance2D"));
- ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, mesh_instance, true, false);
- ur->add_do_reference(mesh_instance);
- ur->add_undo_method(SceneTreeDock::get_singleton(), "replace_node", mesh_instance, node, false, false);
- ur->add_undo_reference(node);
- ur->commit_action();
+ SceneTreeDock::get_singleton()->replace_node(node, mesh_instance);
+ ur->commit_action(false);
}
void Sprite2DEditor::_convert_to_polygon_2d_node() {
@@ -404,11 +401,8 @@ void Sprite2DEditor::_convert_to_polygon_2d_node() {
EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
ur->create_action(TTR("Convert to Polygon2D"));
- ur->add_do_method(SceneTreeDock::get_singleton(), "replace_node", node, polygon_2d_instance, true, false);
- ur->add_do_reference(polygon_2d_instance);
- ur->add_undo_method(SceneTreeDock::get_singleton(), "replace_node", polygon_2d_instance, node, false, false);
- ur->add_undo_reference(node);
- ur->commit_action();
+ SceneTreeDock::get_singleton()->replace_node(node, polygon_2d_instance);
+ ur->commit_action(false);
}
void Sprite2DEditor::_create_collision_polygon_2d_node() {
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 6adfbef06f..0ac3875ad6 100644
--- a/editor/scene_tree_dock.cpp
+++ b/editor/scene_tree_dock.cpp
@@ -2363,16 +2363,12 @@ void SceneTreeDock::_create() {
Variant c = create_dialog->instantiate_selected();
ERR_FAIL_COND(!c);
- Node *newnode = Object::cast_to<Node>(c);
- ERR_FAIL_COND(!newnode);
-
- ur->add_do_method(this, "replace_node", n, newnode, true, false);
- ur->add_do_reference(newnode);
- ur->add_undo_method(this, "replace_node", newnode, n, false, false);
- ur->add_undo_reference(n);
+ Node *new_node = Object::cast_to<Node>(c);
+ ERR_FAIL_COND(!new_node);
+ replace_node(n, new_node);
}
- ur->commit_action();
+ ur->commit_action(false);
} else if (current_option == TOOL_REPARENT_TO_NEW_NODE) {
List<Node *> selection = editor_selection->get_selected_node_list();
ERR_FAIL_COND(selection.size() <= 0);
@@ -2426,7 +2422,25 @@ void SceneTreeDock::_create() {
scene_tree->get_scene_tree()->grab_focus();
}
-void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node, bool p_keep_properties, bool p_remove_old) {
+void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node) {
+ EditorUndoRedoManager *ur = EditorUndoRedoManager::get_singleton();
+ ur->create_action(TTR("Change type of node(s)"), UndoRedo::MERGE_DISABLE, p_node);
+
+ ur->add_do_method(this, "replace_node", p_node, p_by_node, true, false);
+ ur->add_do_reference(p_by_node);
+
+ _replace_node(p_node, p_by_node, true, false);
+
+ ur->add_undo_method(this, "replace_node", p_by_node, p_node, false, false);
+ ur->add_undo_reference(p_node);
+
+ perform_node_replace(nullptr, p_node, p_by_node);
+
+ ur->commit_action(false);
+}
+
+void SceneTreeDock::_replace_node(Node *p_node, Node *p_by_node, bool p_keep_properties, bool p_remove_old) {
+ ERR_FAIL_COND_MSG(!p_node->is_inside_tree(), "_replace_node() can't be called on a node outside of tree. You might have called it twice.");
Node *n = p_node;
Node *newnode = p_by_node;
@@ -2499,6 +2513,84 @@ void SceneTreeDock::replace_node(Node *p_node, Node *p_by_node, bool p_keep_prop
}
}
+void SceneTreeDock::perform_node_replace(Node *p_base, Node *p_node, Node *p_by_node) {
+ if (!p_base) {
+ p_base = edited_scene;
+ }
+
+ if (!p_base) {
+ return;
+ }
+
+ // Renaming node used in node properties.
+ List<PropertyInfo> properties;
+ p_base->get_property_list(&properties);
+
+ for (const PropertyInfo &E : properties) {
+ if (!(E.usage & (PROPERTY_USAGE_STORAGE | PROPERTY_USAGE_EDITOR))) {
+ continue;
+ }
+ String propertyname = E.name;
+ Variant old_variant = p_base->get(propertyname);
+ Variant updated_variant = old_variant;
+ String warn_message;
+
+ if (_check_node_recursive(updated_variant, p_node, p_by_node, E.hint_string, warn_message)) {
+ EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton();
+ undo_redo->add_do_property(p_base, propertyname, updated_variant);
+ undo_redo->add_undo_property(p_base, propertyname, old_variant);
+ p_base->set(propertyname, updated_variant);
+ if (!warn_message.is_empty()) {
+ String node_path = (String(edited_scene->get_name()) + "/" + String(edited_scene->get_path_to(p_base))).trim_suffix("/.");
+ WARN_PRINT(warn_message + vformat(TTR("Removing the node from variable \"%s\" on node \"%s\"."), propertyname, node_path));
+ }
+ }
+ }
+
+ for (int i = 0; i < p_base->get_child_count(); i++) {
+ perform_node_replace(p_base->get_child(i), p_node, p_by_node);
+ }
+}
+
+bool SceneTreeDock::_check_node_recursive(Variant &r_variant, Node *p_node, Node *p_by_node, const String type_hint, String &r_warn_message) {
+ switch (r_variant.get_type()) {
+ case Variant::OBJECT: {
+ if (p_node == r_variant) {
+ if (p_by_node->is_class(type_hint) || EditorNode::get_singleton()->is_object_of_custom_type(p_by_node, type_hint)) {
+ r_variant = p_by_node;
+ } else {
+ r_variant = memnew(Object);
+ r_warn_message = vformat(TTR("The node's new type is incompatible with an exported variable (expected %s, but type is %s)."), type_hint, p_by_node->get_class());
+ }
+ return true;
+ }
+ } break;
+
+ case Variant::ARRAY: {
+ Array a = r_variant;
+ bool updated = false;
+ for (int i = 0; i < a.size(); i++) {
+ Variant value = a[i];
+ if (_check_node_recursive(value, p_node, p_by_node, type_hint.get_slice(":", 1), r_warn_message)) {
+ if (!updated) {
+ a = a.duplicate(); // Need to duplicate for undo-redo to work.
+ updated = true;
+ }
+ a[i] = value;
+ }
+ }
+ if (updated) {
+ r_variant = a;
+ return true;
+ }
+ } break;
+ default: {
+ }
+ }
+
+ return false;
+}
+
void SceneTreeDock::set_edited_scene(Node *p_scene) {
edited_scene = p_scene;
}
@@ -3635,7 +3727,7 @@ void SceneTreeDock::_bind_methods() {
ClassDB::bind_method(D_METHOD("instantiate"), &SceneTreeDock::instantiate);
ClassDB::bind_method(D_METHOD("get_tree_editor"), &SceneTreeDock::get_tree_editor);
- ClassDB::bind_method(D_METHOD("replace_node"), &SceneTreeDock::replace_node);
+ ClassDB::bind_method(D_METHOD("replace_node"), &SceneTreeDock::_replace_node);
ADD_SIGNAL(MethodInfo("remote_tree_selected"));
ADD_SIGNAL(MethodInfo("add_node_used"));
diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h
index 1db73df4c7..21424925c5 100644
--- a/editor/scene_tree_dock.h
+++ b/editor/scene_tree_dock.h
@@ -281,6 +281,8 @@ class SceneTreeDock : public VBoxContainer {
bool _update_node_path(Node *p_root_node, NodePath &r_node_path, HashMap<Node *, NodePath> *p_renames) const;
bool _check_node_path_recursive(Node *p_root_node, Variant &r_variant, HashMap<Node *, NodePath> *p_renames) const;
+ bool _check_node_recursive(Variant &r_variant, Node *p_node, Node *p_by_node, const String type_hint, String &r_warn_message);
+ void _replace_node(Node *p_node, Node *p_by_node, bool p_keep_properties = true, bool p_remove_old = true);
private:
static SceneTreeDock *singleton;
@@ -306,6 +308,7 @@ public:
void set_selected(Node *p_node, bool p_emit_selected = false);
void fill_path_renames(Node *p_node, Node *p_new_parent, HashMap<Node *, NodePath> *p_renames);
void perform_node_renames(Node *p_base, HashMap<Node *, NodePath> *p_renames, HashMap<Ref<Animation>, HashSet<int>> *r_rem_anims = nullptr);
+ void perform_node_replace(Node *p_base, Node *p_node, Node *p_by_node);
SceneTreeEditor *get_tree_editor() { return scene_tree; }
EditorData *get_editor_data() { return editor_data; }
@@ -315,7 +318,7 @@ public:
void show_tab_buttons();
void hide_tab_buttons();
- void replace_node(Node *p_node, Node *p_by_node, bool p_keep_properties = true, bool p_remove_old = true);
+ void replace_node(Node *p_node, Node *p_by_node);
void attach_script_to_selected(bool p_extend);
void open_script_dialog(Node *p_for_node, bool p_extend);
diff --git a/platform/linuxbsd/joypad_linux.cpp b/platform/linuxbsd/joypad_linux.cpp
index 342cff82e9..5d636240b7 100644
--- a/platform/linuxbsd/joypad_linux.cpp
+++ b/platform/linuxbsd/joypad_linux.cpp
@@ -56,6 +56,14 @@
static const char *ignore_str = "/dev/input/js";
#endif
+// On Linux with Steam Input Xbox 360 devices have an index appended to their device name, this index is
+// the Steam Input gamepad index
+#define VALVE_GAMEPAD_NAME_PREFIX "Microsoft X-Box 360 pad "
+// IDs used by Steam Input virtual controllers.
+// See https://partner.steamgames.com/doc/features/steam_controller/steam_input_gamepad_emulation_bestpractices
+#define VALVE_GAMEPAD_VID 0x28DE
+#define VALVE_GAMEPAD_PID 0x11FF
+
JoypadLinux::Joypad::~Joypad() {
for (int i = 0; i < MAX_ABS; i++) {
if (abs_info[i]) {
@@ -411,8 +419,23 @@ void JoypadLinux::open_joypad(const char *p_path) {
setup_joypad_properties(joypad);
sprintf(uid, "%04x%04x", BSWAP16(inpid.bustype), 0);
if (inpid.vendor && inpid.product && inpid.version) {
+ Dictionary joypad_info;
+ joypad_info["vendor_id"] = inpid.vendor;
+ joypad_info["product_id"] = inpid.product;
+ joypad_info["raw_name"] = name;
+
sprintf(uid + String(uid).length(), "%04x%04x%04x%04x%04x%04x", vendor, 0, product, 0, version, 0);
- input->joy_connection_changed(joy_num, true, name, uid);
+
+ if (inpid.vendor == VALVE_GAMEPAD_VID && inpid.product == VALVE_GAMEPAD_PID) {
+ if (name.begins_with(VALVE_GAMEPAD_NAME_PREFIX)) {
+ String idx_str = name.substr(strlen(VALVE_GAMEPAD_NAME_PREFIX));
+ if (idx_str.is_valid_int()) {
+ joypad_info["steam_input_index"] = idx_str.to_int();
+ }
+ }
+ }
+
+ input->joy_connection_changed(joy_num, true, name, uid, joypad_info);
} else {
String uidname = uid;
int uidlen = MIN(name.length(), 11);
diff --git a/platform/windows/joypad_windows.cpp b/platform/windows/joypad_windows.cpp
index 487cb56ba0..0ac6c2c8b0 100644
--- a/platform/windows/joypad_windows.cpp
+++ b/platform/windows/joypad_windows.cpp
@@ -318,7 +318,9 @@ void JoypadWindows::probe_joypads() {
x_joypads[i].ff_end_timestamp = 0;
x_joypads[i].vibrating = false;
attached_joypads[id] = true;
- input->joy_connection_changed(id, true, "XInput Gamepad", "__XINPUT_DEVICE__");
+ Dictionary joypad_info;
+ joypad_info["xinput_index"] = (int)i;
+ input->joy_connection_changed(id, true, "XInput Gamepad", "__XINPUT_DEVICE__", joypad_info);
}
} else if (x_joypads[i].attached) {
x_joypads[i].attached = false;
diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp
index 7b04669dad..13519ecec7 100644
--- a/scene/3d/ray_cast_3d.cpp
+++ b/scene/3d/ray_cast_3d.cpp
@@ -104,6 +104,10 @@ Vector3 RayCast3D::get_collision_normal() const {
return collision_normal;
}
+int RayCast3D::get_collision_face_index() const {
+ return collision_face_index;
+}
+
void RayCast3D::set_enabled(bool p_enabled) {
enabled = p_enabled;
update_gizmos();
@@ -232,6 +236,7 @@ void RayCast3D::_update_raycast_state() {
against_rid = rr.rid;
collision_point = rr.position;
collision_normal = rr.normal;
+ collision_face_index = rr.face_index;
against_shape = rr.shape;
} else {
collided = false;
@@ -321,6 +326,7 @@ void RayCast3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_collider_shape"), &RayCast3D::get_collider_shape);
ClassDB::bind_method(D_METHOD("get_collision_point"), &RayCast3D::get_collision_point);
ClassDB::bind_method(D_METHOD("get_collision_normal"), &RayCast3D::get_collision_normal);
+ ClassDB::bind_method(D_METHOD("get_collision_face_index"), &RayCast3D::get_collision_face_index);
ClassDB::bind_method(D_METHOD("add_exception_rid", "rid"), &RayCast3D::add_exception_rid);
ClassDB::bind_method(D_METHOD("add_exception", "node"), &RayCast3D::add_exception);
diff --git a/scene/3d/ray_cast_3d.h b/scene/3d/ray_cast_3d.h
index 1def7a0eca..7b7f698114 100644
--- a/scene/3d/ray_cast_3d.h
+++ b/scene/3d/ray_cast_3d.h
@@ -45,6 +45,7 @@ class RayCast3D : public Node3D {
int against_shape = 0;
Vector3 collision_point;
Vector3 collision_normal;
+ int collision_face_index = -1;
Vector3 target_position = Vector3(0, -1, 0);
HashSet<RID> exclude;
@@ -122,6 +123,7 @@ public:
int get_collider_shape() const;
Vector3 get_collision_point() const;
Vector3 get_collision_normal() const;
+ int get_collision_face_index() const;
void add_exception_rid(const RID &p_rid);
void add_exception(const CollisionObject3D *p_node);
diff --git a/servers/physics_3d/godot_body_pair_3d.cpp b/servers/physics_3d/godot_body_pair_3d.cpp
index a78bd0f888..84fae73616 100644
--- a/servers/physics_3d/godot_body_pair_3d.cpp
+++ b/servers/physics_3d/godot_body_pair_3d.cpp
@@ -168,7 +168,6 @@ void GodotBodyPair3D::validate_contacts() {
// adjust the velocity of A down so that it will just slightly intersect the collider instead of blowing right past it.
bool GodotBodyPair3D::_test_ccd(real_t p_step, GodotBody3D *p_A, int p_shape_A, const Transform3D &p_xform_A, GodotBody3D *p_B, int p_shape_B, const Transform3D &p_xform_B) {
GodotShape3D *shape_A_ptr = p_A->get_shape(p_shape_A);
- GodotShape3D *shape_B_ptr = p_B->get_shape(p_shape_B);
Vector3 motion = p_A->get_linear_velocity() * p_step;
real_t mlen = motion.length();
@@ -221,7 +220,8 @@ bool GodotBodyPair3D::_test_ccd(real_t p_step, GodotBody3D *p_A, int p_shape_A,
Vector3 local_to = from_inv.xform(to);
Vector3 rpos, rnorm;
- if (shape_B_ptr->intersect_segment(local_from, local_to, rpos, rnorm, true)) {
+ int fi = -1;
+ if (p_B->get_shape(p_shape_B)->intersect_segment(local_from, local_to, rpos, rnorm, fi, true)) {
float hit_length = local_from.distance_to(rpos);
if (hit_length < segment_hit_length) {
segment_support_idx = i;
diff --git a/servers/physics_3d/godot_collision_solver_3d.cpp b/servers/physics_3d/godot_collision_solver_3d.cpp
index 2de1d86de1..db48111eea 100644
--- a/servers/physics_3d/godot_collision_solver_3d.cpp
+++ b/servers/physics_3d/godot_collision_solver_3d.cpp
@@ -106,7 +106,8 @@ bool GodotCollisionSolver3D::solve_separation_ray(const GodotShape3D *p_shape_A,
to = ai.xform(to);
Vector3 p, n;
- if (!p_shape_B->intersect_segment(from, to, p, n, true)) {
+ int fi = -1;
+ if (!p_shape_B->intersect_segment(from, to, p, n, fi, true)) {
return false;
}
diff --git a/servers/physics_3d/godot_shape_3d.cpp b/servers/physics_3d/godot_shape_3d.cpp
index a2276d8dec..872d26aff6 100644
--- a/servers/physics_3d/godot_shape_3d.cpp
+++ b/servers/physics_3d/godot_shape_3d.cpp
@@ -126,7 +126,7 @@ Vector3 GodotWorldBoundaryShape3D::get_support(const Vector3 &p_normal) const {
return p_normal * 1e15;
}
-bool GodotWorldBoundaryShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
+bool GodotWorldBoundaryShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
bool inters = plane.intersects_segment(p_begin, p_end, &r_result);
if (inters) {
r_normal = plane.normal;
@@ -207,7 +207,7 @@ void GodotSeparationRayShape3D::get_supports(const Vector3 &p_normal, int p_max,
}
}
-bool GodotSeparationRayShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
+bool GodotSeparationRayShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
return false; //simply not possible
}
@@ -275,7 +275,7 @@ void GodotSphereShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector
r_type = FEATURE_POINT;
}
-bool GodotSphereShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
+bool GodotSphereShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
return Geometry3D::segment_intersects_sphere(p_begin, p_end, Vector3(), radius, &r_result, &r_normal);
}
@@ -417,7 +417,7 @@ void GodotBoxShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3 *
r_supports[0] = point;
}
-bool GodotBoxShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
+bool GodotBoxShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
AABB aabb_ext(-half_extents, half_extents * 2.0);
return aabb_ext.intersects_segment(p_begin, p_end, &r_result, &r_normal);
@@ -551,7 +551,7 @@ void GodotCapsuleShape3D::get_supports(const Vector3 &p_normal, int p_max, Vecto
}
}
-bool GodotCapsuleShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
+bool GodotCapsuleShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
Vector3 norm = (p_end - p_begin).normalized();
real_t min_d = 1e20;
@@ -743,7 +743,7 @@ void GodotCylinderShape3D::get_supports(const Vector3 &p_normal, int p_max, Vect
}
}
-bool GodotCylinderShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
+bool GodotCylinderShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
return Geometry3D::segment_intersects_cylinder(p_begin, p_end, height, radius, &r_result, &r_normal, 1);
}
@@ -975,7 +975,7 @@ void GodotConvexPolygonShape3D::get_supports(const Vector3 &p_normal, int p_max,
r_type = FEATURE_POINT;
}
-bool GodotConvexPolygonShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
+bool GodotConvexPolygonShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
const Geometry3D::MeshData::Face *faces = mesh.faces.ptr();
int fc = mesh.faces.size();
@@ -1252,7 +1252,7 @@ void GodotFaceShape3D::get_supports(const Vector3 &p_normal, int p_max, Vector3
r_supports[0] = vertex[vert_support_idx];
}
-bool GodotFaceShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
+bool GodotFaceShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
bool c = Geometry3D::segment_intersects_triangle(p_begin, p_end, vertex[0], vertex[1], vertex[2], &r_result);
if (c) {
r_normal = Plane(vertex[0], vertex[1], vertex[2]).normal;
@@ -1362,12 +1362,14 @@ void GodotConcavePolygonShape3D::_cull_segment(int p_idx, _SegmentCullParams *p_
Vector3 res;
Vector3 normal;
- if (face->intersect_segment(p_params->from, p_params->to, res, normal, true)) {
+ int face_index = params_bvh->face_index;
+ if (face->intersect_segment(p_params->from, p_params->to, res, normal, face_index, true)) {
real_t d = p_params->dir.dot(res) - p_params->dir.dot(p_params->from);
if ((d > 0) && (d < p_params->min_d)) {
p_params->min_d = d;
p_params->result = res;
p_params->normal = normal;
+ p_params->face_index = face_index;
p_params->collisions++;
}
}
@@ -1381,7 +1383,7 @@ void GodotConcavePolygonShape3D::_cull_segment(int p_idx, _SegmentCullParams *p_
}
}
-bool GodotConcavePolygonShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
+bool GodotConcavePolygonShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
if (faces.size() == 0) {
return false;
}
@@ -1411,6 +1413,7 @@ bool GodotConcavePolygonShape3D::intersect_segment(const Vector3 &p_begin, const
if (params.collisions > 0) {
r_result = params.result;
r_normal = params.normal;
+ r_face_index = params.face_index;
return true;
} else {
return false;
@@ -1734,9 +1737,11 @@ struct _HeightmapGridCullState {
_FORCE_INLINE_ bool _heightmap_face_cull_segment(_HeightmapSegmentCullParams &p_params) {
Vector3 res;
Vector3 normal;
- if (p_params.face->intersect_segment(p_params.from, p_params.to, res, normal, true)) {
+ int fi = -1;
+ if (p_params.face->intersect_segment(p_params.from, p_params.to, res, normal, fi, true)) {
p_params.result = res;
p_params.normal = normal;
+
return true;
}
@@ -1940,7 +1945,7 @@ bool GodotHeightMapShape3D::_intersect_grid_segment(ProcessFunction &p_process,
return false;
}
-bool GodotHeightMapShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, bool p_hit_back_faces) const {
+bool GodotHeightMapShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
if (heights.is_empty()) {
return false;
}
diff --git a/servers/physics_3d/godot_shape_3d.h b/servers/physics_3d/godot_shape_3d.h
index acc42f2fcb..dbd58ead68 100644
--- a/servers/physics_3d/godot_shape_3d.h
+++ b/servers/physics_3d/godot_shape_3d.h
@@ -80,7 +80,7 @@ public:
virtual Vector3 get_support(const Vector3 &p_normal) const;
virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const = 0;
virtual Vector3 get_closest_point_to(const Vector3 &p_point) const = 0;
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, bool p_hit_back_faces) const = 0;
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const = 0;
virtual bool intersect_point(const Vector3 &p_point) const = 0;
virtual Vector3 get_moment_of_inertia(real_t p_mass) const = 0;
@@ -126,7 +126,7 @@ public:
virtual Vector3 get_support(const Vector3 &p_normal) const override;
virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override { r_amount = 0; }
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override;
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override;
virtual bool intersect_point(const Vector3 &p_point) const override;
virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override;
virtual Vector3 get_moment_of_inertia(real_t p_mass) const override;
@@ -153,7 +153,7 @@ public:
virtual Vector3 get_support(const Vector3 &p_normal) const override;
virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override;
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override;
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override;
virtual bool intersect_point(const Vector3 &p_point) const override;
virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override;
@@ -180,7 +180,7 @@ public:
virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override;
virtual Vector3 get_support(const Vector3 &p_normal) const override;
virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override;
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override;
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override;
virtual bool intersect_point(const Vector3 &p_point) const override;
virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override;
@@ -205,7 +205,7 @@ public:
virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override;
virtual Vector3 get_support(const Vector3 &p_normal) const override;
virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override;
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override;
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override;
virtual bool intersect_point(const Vector3 &p_point) const override;
virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override;
@@ -234,7 +234,7 @@ public:
virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override;
virtual Vector3 get_support(const Vector3 &p_normal) const override;
virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override;
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override;
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override;
virtual bool intersect_point(const Vector3 &p_point) const override;
virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override;
@@ -263,7 +263,7 @@ public:
virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override;
virtual Vector3 get_support(const Vector3 &p_normal) const override;
virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override;
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override;
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override;
virtual bool intersect_point(const Vector3 &p_point) const override;
virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override;
@@ -290,7 +290,7 @@ public:
virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override;
virtual Vector3 get_support(const Vector3 &p_normal) const override;
virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override;
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override;
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override;
virtual bool intersect_point(const Vector3 &p_point) const override;
virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override;
@@ -347,6 +347,7 @@ struct GodotConcavePolygonShape3D : public GodotConcaveShape3D {
Vector3 result;
Vector3 normal;
+ int face_index = -1;
real_t min_d = 1e20;
int collisions = 0;
};
@@ -368,7 +369,7 @@ public:
virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override;
virtual Vector3 get_support(const Vector3 &p_normal) const override;
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override;
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override;
virtual bool intersect_point(const Vector3 &p_point) const override;
virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override;
@@ -431,7 +432,7 @@ public:
virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override;
virtual Vector3 get_support(const Vector3 &p_normal) const override;
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, bool p_hit_back_faces) const override;
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_point, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override;
virtual bool intersect_point(const Vector3 &p_point) const override;
virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override;
@@ -459,7 +460,7 @@ struct GodotFaceShape3D : public GodotShape3D {
virtual void project_range(const Vector3 &p_normal, const Transform3D &p_transform, real_t &r_min, real_t &r_max) const override;
virtual Vector3 get_support(const Vector3 &p_normal) const override;
virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override;
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override;
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override;
virtual bool intersect_point(const Vector3 &p_point) const override;
virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override;
@@ -498,7 +499,7 @@ struct GodotMotionShape3D : public GodotShape3D {
}
virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override { r_amount = 0; }
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override { return false; }
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override { return false; }
virtual bool intersect_point(const Vector3 &p_point) const override { return false; }
virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override { return p_point; }
diff --git a/servers/physics_3d/godot_soft_body_3d.cpp b/servers/physics_3d/godot_soft_body_3d.cpp
index 5c67807c48..4b35dd1500 100644
--- a/servers/physics_3d/godot_soft_body_3d.cpp
+++ b/servers/physics_3d/godot_soft_body_3d.cpp
@@ -1266,7 +1266,7 @@ struct _SoftBodyIntersectSegmentInfo {
}
};
-bool GodotSoftBodyShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const {
+bool GodotSoftBodyShape3D::intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const {
_SoftBodyIntersectSegmentInfo query_info;
query_info.soft_body = soft_body;
query_info.from = p_begin;
diff --git a/servers/physics_3d/godot_soft_body_3d.h b/servers/physics_3d/godot_soft_body_3d.h
index 20592f3411..e23f4bb9f5 100644
--- a/servers/physics_3d/godot_soft_body_3d.h
+++ b/servers/physics_3d/godot_soft_body_3d.h
@@ -259,7 +259,7 @@ public:
virtual Vector3 get_support(const Vector3 &p_normal) const override { return Vector3(); }
virtual void get_supports(const Vector3 &p_normal, int p_max, Vector3 *r_supports, int &r_amount, FeatureType &r_type) const override { r_amount = 0; }
- virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, bool p_hit_back_faces) const override;
+ virtual bool intersect_segment(const Vector3 &p_begin, const Vector3 &p_end, Vector3 &r_result, Vector3 &r_normal, int &r_face_index, bool p_hit_back_faces) const override;
virtual bool intersect_point(const Vector3 &p_point) const override;
virtual Vector3 get_closest_point_to(const Vector3 &p_point) const override;
virtual Vector3 get_moment_of_inertia(real_t p_mass) const override { return Vector3(); }
diff --git a/servers/physics_3d/godot_space_3d.cpp b/servers/physics_3d/godot_space_3d.cpp
index 35f6fa023d..92b5f31e41 100644
--- a/servers/physics_3d/godot_space_3d.cpp
+++ b/servers/physics_3d/godot_space_3d.cpp
@@ -120,6 +120,7 @@ bool GodotPhysicsDirectSpaceState3D::intersect_ray(const RayParameters &p_parame
bool collided = false;
Vector3 res_point, res_normal;
+ int res_face_index = -1;
int res_shape = -1;
const GodotCollisionObject3D *res_obj = nullptr;
real_t min_d = 1e10;
@@ -148,6 +149,7 @@ bool GodotPhysicsDirectSpaceState3D::intersect_ray(const RayParameters &p_parame
const GodotShape3D *shape = col_obj->get_shape(shape_idx);
Vector3 shape_point, shape_normal;
+ int shape_face_index = -1;
if (shape->intersect_point(local_from)) {
if (p_parameters.hit_from_inside) {
@@ -165,7 +167,7 @@ bool GodotPhysicsDirectSpaceState3D::intersect_ray(const RayParameters &p_parame
}
}
- if (shape->intersect_segment(local_from, local_to, shape_point, shape_normal, p_parameters.hit_back_faces)) {
+ if (shape->intersect_segment(local_from, local_to, shape_point, shape_normal, shape_face_index, p_parameters.hit_back_faces)) {
Transform3D xform = col_obj->get_transform() * col_obj->get_shape_transform(shape_idx);
shape_point = xform.xform(shape_point);
@@ -175,6 +177,7 @@ bool GodotPhysicsDirectSpaceState3D::intersect_ray(const RayParameters &p_parame
min_d = ld;
res_point = shape_point;
res_normal = inv_xform.basis.xform_inv(shape_normal).normalized();
+ res_face_index = shape_face_index;
res_shape = shape_idx;
res_obj = col_obj;
collided = true;
@@ -194,6 +197,7 @@ bool GodotPhysicsDirectSpaceState3D::intersect_ray(const RayParameters &p_parame
r_result.collider = nullptr;
}
r_result.normal = res_normal;
+ r_result.face_index = res_face_index;
r_result.position = res_point;
r_result.rid = res_obj->get_self();
r_result.shape = res_shape;
diff --git a/servers/physics_server_3d.cpp b/servers/physics_server_3d.cpp
index 6b8d3d1af6..8497bc78e2 100644
--- a/servers/physics_server_3d.cpp
+++ b/servers/physics_server_3d.cpp
@@ -362,6 +362,7 @@ Dictionary PhysicsDirectSpaceState3D::_intersect_ray(const Ref<PhysicsRayQueryPa
Dictionary d;
d["position"] = result.position;
d["normal"] = result.normal;
+ d["face_index"] = result.face_index;
d["collider_id"] = result.collider_id;
d["collider"] = result.collider;
d["shape"] = result.shape;
diff --git a/servers/physics_server_3d.h b/servers/physics_server_3d.h
index 33e9f8a7c9..cd96bf15fd 100644
--- a/servers/physics_server_3d.h
+++ b/servers/physics_server_3d.h
@@ -151,6 +151,7 @@ public:
struct RayResult {
Vector3 position;
Vector3 normal;
+ int face_index = -1;
RID rid;
ObjectID collider_id;
Object *collider = nullptr;