Add support for controlling physics nodes' behavior when disabled

New property disable_mode to set different behaviors:
Remove: remove from physics simulation
MakeStatic: change body mode to static (doesn't affect area and soft body)
KeepActive: do nothing

Extra change:
Handle disable/enable node state with specific notifications, in order
to differentiate global pause from disabled nodes.
This commit is contained in:
PouleyKetchoupp 2021-06-17 18:09:40 -07:00
parent 92f20fd70e
commit 5cbdc7a0ac
16 changed files with 638 additions and 198 deletions

View file

@ -266,6 +266,9 @@
The physics layers this CollisionObject2D scans. Collision objects can scan one or more of 32 different layers. See also [member collision_layer].
[b]Note:[/b] A contact is detected if object A is in any of the layers that object B scans, or object B is in any layers that object A scans. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
</member>
<member name="disable_mode" type="int" setter="set_disable_mode" getter="get_disable_mode" enum="CollisionObject2D.DisableMode" default="0">
Defines the behavior in physics when [member Node.process_mode] is set to [constant Node.PROCESS_MODE_DISABLED]. See [enum DisableMode] for more details about the different modes.
</member>
<member name="input_pickable" type="bool" setter="set_pickable" getter="is_pickable" default="true">
If [code]true[/code], this object is pickable. A pickable object can detect the mouse pointer entering/leaving, and if the mouse is inside it, report input events. Requires at least one [code]collision_layer[/code] bit to be set.
</member>
@ -294,5 +297,16 @@
</signal>
</signals>
<constants>
<constant name="DISABLE_MODE_REMOVE" value="0" enum="DisableMode">
When [member Node.process_mode] is set to [constant Node.PROCESS_MODE_DISABLED], remove from the physics simulation to stop all physics interactions with this [CollisionObject2D].
Automatically re-added to the physics simulation when the [Node] is processed again.
</constant>
<constant name="DISABLE_MODE_MAKE_STATIC" value="1" enum="DisableMode">
When [member Node.process_mode] is set to [constant Node.PROCESS_MODE_DISABLED], make the body static. Doesn't affect [Area2D]. [PhysicsBody2D] can't be affected by forces or other bodies while static.
Automatically set [PhysicsBody2D] back to its original mode when the [Node] is processed again.
</constant>
<constant name="DISABLE_MODE_KEEP_ACTIVE" value="2" enum="DisableMode">
When [member Node.process_mode] is set to [constant Node.PROCESS_MODE_DISABLED], do not affect the physics simulation.
</constant>
</constants>
</class>

View file

@ -230,6 +230,9 @@
The physics layers this CollisionObject3D scans. Collision objects can scan one or more of 32 different layers. See also [member collision_layer].
[b]Note:[/b] A contact is detected if object A is in any of the layers that object B scans, or object B is in any layers that object A scans. See [url=https://docs.godotengine.org/en/latest/tutorials/physics/physics_introduction.html#collision-layers-and-masks]Collision layers and masks[/url] in the documentation for more information.
</member>
<member name="disable_mode" type="int" setter="set_disable_mode" getter="get_disable_mode" enum="CollisionObject3D.DisableMode" default="0">
Defines the behavior in physics when [member Node.process_mode] is set to [constant Node.PROCESS_MODE_DISABLED]. See [enum DisableMode] for more details about the different modes.
</member>
<member name="input_capture_on_drag" type="bool" setter="set_capture_input_on_drag" getter="get_capture_input_on_drag" default="false">
If [code]true[/code], the [CollisionObject3D] will continue to receive input events as the mouse is dragged across its shapes.
</member>
@ -265,5 +268,16 @@
</signal>
</signals>
<constants>
<constant name="DISABLE_MODE_REMOVE" value="0" enum="DisableMode">
When [member Node.process_mode] is set to [constant Node.PROCESS_MODE_DISABLED], remove from the physics simulation to stop all physics interactions with this [CollisionObject3D].
Automatically re-added to the physics simulation when the [Node] is processed again.
</constant>
<constant name="DISABLE_MODE_MAKE_STATIC" value="1" enum="DisableMode">
When [member Node.process_mode] is set to [constant Node.PROCESS_MODE_DISABLED], make the body static. Doesn't affect [Area2D]. [PhysicsBody3D] can't be affected by forces or other bodies while static.
Automatically set [PhysicsBody3D] back to its original mode when the [Node] is processed again.
</constant>
<constant name="DISABLE_MODE_KEEP_ACTIVE" value="2" enum="DisableMode">
When [member Node.process_mode] is set to [constant Node.PROCESS_MODE_DISABLED], do not affect the physics simulation.
</constant>
</constants>
</class>

View file

@ -894,6 +894,12 @@
<constant name="NOTIFICATION_POST_ENTER_TREE" value="27">
Notification received when the node is ready, just before [constant NOTIFICATION_READY] is received. Unlike the latter, it's sent every time the node enters tree, instead of only once.
</constant>
<constant name="NOTIFICATION_DISABLED" value="28">
Notification received when the node is disabled. See [constant PROCESS_MODE_DISABLED].
</constant>
<constant name="NOTIFICATION_ENABLED" value="29">
Notification received when the node is enabled again after being disabled. See [constant PROCESS_MODE_DISABLED].
</constant>
<constant name="NOTIFICATION_EDITOR_PRE_SAVE" value="9001">
Notification received right before the scene with the node is saved in the editor. This notification is only sent in the Godot editor and will not occur in exported projects.
</constant>

View file

@ -93,6 +93,9 @@
</member>
<member name="damping_coefficient" type="float" setter="set_damping_coefficient" getter="get_damping_coefficient" default="0.01">
</member>
<member name="disable_mode" type="int" setter="set_disable_mode" getter="get_disable_mode" enum="SoftBody3D.DisableMode" default="0">
Defines the behavior in physics when [member Node.process_mode] is set to [constant Node.PROCESS_MODE_DISABLED]. See [enum DisableMode] for more details about the different modes.
</member>
<member name="drag_coefficient" type="float" setter="set_drag_coefficient" getter="get_drag_coefficient" default="0.0">
</member>
<member name="linear_stiffness" type="float" setter="set_linear_stiffness" getter="get_linear_stiffness" default="0.5">
@ -113,5 +116,12 @@
</member>
</members>
<constants>
<constant name="DISABLE_MODE_REMOVE" value="0" enum="DisableMode">
When [member Node.process_mode] is set to [constant Node.PROCESS_MODE_DISABLED], remove from the physics simulation to stop all physics interactions with this [SoftBody3D].
Automatically re-added to the physics simulation when the [Node] is processed again.
</constant>
<constant name="DISABLE_MODE_KEEP_ACTIVE" value="1" enum="DisableMode">
When [member Node.process_mode] is set to [constant Node.PROCESS_MODE_DISABLED], do not affect the physics simulation.
</constant>
</constants>
</class>

View file

@ -31,7 +31,6 @@
#include "collision_object_2d.h"
#include "scene/scene_string_names.h"
#include "servers/physics_server_2d.h"
void CollisionObject2D::_notification(int p_what) {
switch (p_what) {
@ -44,16 +43,22 @@ void CollisionObject2D::_notification(int p_what) {
PhysicsServer2D::get_singleton()->body_set_state(rid, PhysicsServer2D::BODY_STATE_TRANSFORM, global_transform);
}
RID space = get_world_2d()->get_space();
if (area) {
PhysicsServer2D::get_singleton()->area_set_space(rid, space);
} else {
PhysicsServer2D::get_singleton()->body_set_space(rid, space);
bool disabled = !is_enabled();
if (disabled && (disable_mode != DISABLE_MODE_REMOVE)) {
_apply_disabled();
}
if (!disabled || (disable_mode != DISABLE_MODE_REMOVE)) {
RID space = get_world_2d()->get_space();
if (area) {
PhysicsServer2D::get_singleton()->area_set_space(rid, space);
} else {
PhysicsServer2D::get_singleton()->body_set_space(rid, space);
}
}
_update_pickable();
//get space
} break;
case NOTIFICATION_ENTER_CANVAS: {
@ -67,6 +72,7 @@ void CollisionObject2D::_notification(int p_what) {
case NOTIFICATION_VISIBILITY_CHANGED: {
_update_pickable();
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
if (only_update_transform_changes) {
return;
@ -79,15 +85,22 @@ void CollisionObject2D::_notification(int p_what) {
} else {
PhysicsServer2D::get_singleton()->body_set_state(rid, PhysicsServer2D::BODY_STATE_TRANSFORM, global_transform);
}
} break;
case NOTIFICATION_EXIT_TREE: {
if (area) {
PhysicsServer2D::get_singleton()->area_set_space(rid, RID());
} else {
PhysicsServer2D::get_singleton()->body_set_space(rid, RID());
bool disabled = !is_enabled();
if (!disabled || (disable_mode != DISABLE_MODE_REMOVE)) {
if (area) {
PhysicsServer2D::get_singleton()->area_set_space(rid, RID());
} else {
PhysicsServer2D::get_singleton()->body_set_space(rid, RID());
}
}
if (disabled && (disable_mode != DISABLE_MODE_REMOVE)) {
_apply_enabled();
}
} break;
case NOTIFICATION_EXIT_CANVAS: {
@ -97,6 +110,14 @@ void CollisionObject2D::_notification(int p_what) {
PhysicsServer2D::get_singleton()->body_attach_canvas_instance_id(rid, ObjectID());
}
} break;
case NOTIFICATION_DISABLED: {
_apply_disabled();
} break;
case NOTIFICATION_ENABLED: {
_apply_enabled();
} break;
}
}
@ -158,6 +179,79 @@ bool CollisionObject2D::get_collision_mask_bit(int p_bit) const {
return get_collision_mask() & (1 << p_bit);
}
void CollisionObject2D::set_disable_mode(DisableMode p_mode) {
if (disable_mode == p_mode) {
return;
}
bool disabled = is_inside_tree() && !is_enabled();
if (disabled) {
// Cancel previous disable mode.
_apply_enabled();
}
disable_mode = p_mode;
if (disabled) {
// Apply new disable mode.
_apply_disabled();
}
}
CollisionObject2D::DisableMode CollisionObject2D::get_disable_mode() const {
return disable_mode;
}
void CollisionObject2D::_apply_disabled() {
switch (disable_mode) {
case DISABLE_MODE_REMOVE: {
if (is_inside_tree()) {
if (area) {
PhysicsServer2D::get_singleton()->area_set_space(rid, RID());
} else {
PhysicsServer2D::get_singleton()->body_set_space(rid, RID());
}
}
} break;
case DISABLE_MODE_MAKE_STATIC: {
if (!area && (body_mode != PhysicsServer2D::BODY_MODE_STATIC)) {
PhysicsServer2D::get_singleton()->body_set_mode(rid, PhysicsServer2D::BODY_MODE_STATIC);
}
} break;
case DISABLE_MODE_KEEP_ACTIVE: {
// Nothing to do.
} break;
}
}
void CollisionObject2D::_apply_enabled() {
switch (disable_mode) {
case DISABLE_MODE_REMOVE: {
if (is_inside_tree()) {
RID space = get_world_2d()->get_space();
if (area) {
PhysicsServer2D::get_singleton()->area_set_space(rid, space);
} else {
PhysicsServer2D::get_singleton()->body_set_space(rid, space);
}
}
} break;
case DISABLE_MODE_MAKE_STATIC: {
if (!area && (body_mode != PhysicsServer2D::BODY_MODE_STATIC)) {
PhysicsServer2D::get_singleton()->body_set_mode(rid, body_mode);
}
} break;
case DISABLE_MODE_KEEP_ACTIVE: {
// Nothing to do.
} break;
}
}
uint32_t CollisionObject2D::create_shape_owner(Object *p_owner) {
ShapeData sd;
uint32_t id;
@ -412,6 +506,22 @@ bool CollisionObject2D::is_only_update_transform_changes_enabled() const {
return only_update_transform_changes;
}
void CollisionObject2D::set_body_mode(PhysicsServer2D::BodyMode p_mode) {
ERR_FAIL_COND(area);
if (body_mode == p_mode) {
return;
}
body_mode = p_mode;
if (is_inside_tree() && !is_enabled() && (disable_mode == DISABLE_MODE_MAKE_STATIC)) {
return;
}
PhysicsServer2D::get_singleton()->body_set_mode(rid, p_mode);
}
void CollisionObject2D::_update_pickable() {
if (!is_inside_tree()) {
return;
@ -445,6 +555,8 @@ void CollisionObject2D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &CollisionObject2D::get_collision_layer_bit);
ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &CollisionObject2D::set_collision_mask_bit);
ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &CollisionObject2D::get_collision_mask_bit);
ClassDB::bind_method(D_METHOD("set_disable_mode", "mode"), &CollisionObject2D::set_disable_mode);
ClassDB::bind_method(D_METHOD("get_disable_mode"), &CollisionObject2D::get_disable_mode);
ClassDB::bind_method(D_METHOD("set_pickable", "enabled"), &CollisionObject2D::set_pickable);
ClassDB::bind_method(D_METHOD("is_pickable"), &CollisionObject2D::is_pickable);
ClassDB::bind_method(D_METHOD("create_shape_owner", "owner"), &CollisionObject2D::create_shape_owner);
@ -473,12 +585,18 @@ void CollisionObject2D::_bind_methods() {
ADD_SIGNAL(MethodInfo("mouse_entered"));
ADD_SIGNAL(MethodInfo("mouse_exited"));
ADD_PROPERTY(PropertyInfo(Variant::INT, "disable_mode", PROPERTY_HINT_ENUM, "Remove,MakeStatic,KeepActive"), "set_disable_mode", "get_disable_mode");
ADD_GROUP("Collision", "collision_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_layer", "get_collision_layer");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_collision_mask", "get_collision_mask");
ADD_GROUP("Input", "input_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "input_pickable"), "set_pickable", "is_pickable");
BIND_ENUM_CONSTANT(DISABLE_MODE_REMOVE);
BIND_ENUM_CONSTANT(DISABLE_MODE_MAKE_STATIC);
BIND_ENUM_CONSTANT(DISABLE_MODE_KEEP_ACTIVE);
}
CollisionObject2D::CollisionObject2D(RID p_rid, bool p_area) {
@ -493,6 +611,7 @@ CollisionObject2D::CollisionObject2D(RID p_rid, bool p_area) {
PhysicsServer2D::get_singleton()->area_attach_object_instance_id(rid, get_instance_id());
} else {
PhysicsServer2D::get_singleton()->body_attach_object_instance_id(rid, get_instance_id());
PhysicsServer2D::get_singleton()->body_set_mode(rid, body_mode);
}
}

View file

@ -33,10 +33,19 @@
#include "scene/2d/node_2d.h"
#include "scene/resources/shape_2d.h"
#include "servers/physics_server_2d.h"
class CollisionObject2D : public Node2D {
GDCLASS(CollisionObject2D, Node2D);
public:
enum DisableMode {
DISABLE_MODE_REMOVE,
DISABLE_MODE_MAKE_STATIC,
DISABLE_MODE_KEEP_ACTIVE,
};
private:
uint32_t collision_layer = 1;
uint32_t collision_mask = 1;
@ -44,6 +53,10 @@ class CollisionObject2D : public Node2D {
RID rid;
bool pickable = false;
DisableMode disable_mode = DISABLE_MODE_REMOVE;
PhysicsServer2D::BodyMode body_mode = PhysicsServer2D::BODY_MODE_STATIC;
struct ShapeData {
Object *owner = nullptr;
Transform2D xform;
@ -64,6 +77,9 @@ class CollisionObject2D : public Node2D {
Map<uint32_t, ShapeData> shapes;
bool only_update_transform_changes = false; //this is used for sync physics in CharacterBody2D
void _apply_disabled();
void _apply_enabled();
protected:
CollisionObject2D(RID p_rid, bool p_area);
@ -79,6 +95,8 @@ protected:
void set_only_update_transform_changes(bool p_enable);
bool is_only_update_transform_changes_enabled() const;
void set_body_mode(PhysicsServer2D::BodyMode p_mode);
public:
void set_collision_layer(uint32_t p_layer);
uint32_t get_collision_layer() const;
@ -92,6 +110,9 @@ public:
void set_collision_mask_bit(int p_bit, bool p_value);
bool get_collision_mask_bit(int p_bit) const;
void set_disable_mode(DisableMode p_mode);
DisableMode get_disable_mode() const;
uint32_t create_shape_owner(Object *p_owner);
void remove_shape_owner(uint32_t owner);
void get_shape_owners(List<uint32_t> *r_owners);
@ -131,4 +152,6 @@ public:
~CollisionObject2D();
};
VARIANT_ENUM_CAST(CollisionObject2D::DisableMode);
#endif // COLLISION_OBJECT_2D_H

View file

@ -31,33 +31,37 @@
#include "physical_bone_2d.h"
void PhysicalBone2D::_notification(int p_what) {
if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
// Position the RigidBody in the correct position
if (follow_bone_when_simulating) {
_position_at_bone2d();
}
switch (p_what) {
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
// Position the RigidBody in the correct position.
if (follow_bone_when_simulating) {
_position_at_bone2d();
}
// Keep the child joint in the correct position.
if (child_joint && auto_configure_joint) {
child_joint->set_global_position(get_global_position());
}
} else if (p_what == NOTIFICATION_READY) {
_find_skeleton_parent();
_find_joint_child();
// Keep the child joint in the correct position.
if (child_joint && auto_configure_joint) {
child_joint->set_global_position(get_global_position());
}
} break;
// Configure joint
if (child_joint && auto_configure_joint) {
_auto_configure_joint();
}
case NOTIFICATION_READY: {
_find_skeleton_parent();
_find_joint_child();
// Simulate physics if set
if (simulate_physics) {
_start_physics_simulation();
} else {
_stop_physics_simulation();
}
// Configure joint.
if (child_joint && auto_configure_joint) {
_auto_configure_joint();
}
set_physics_process_internal(true);
// Simulate physics if set.
if (simulate_physics) {
_start_physics_simulation();
} else {
_stop_physics_simulation();
}
set_physics_process_internal(true);
} break;
}
}
@ -156,16 +160,16 @@ void PhysicalBone2D::_start_physics_simulation() {
// Apply the correct mode
RigidBody2D::Mode rigid_mode = get_mode();
if (rigid_mode == RigidBody2D::MODE_STATIC) {
PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_STATIC);
set_body_mode(PhysicsServer2D::BODY_MODE_STATIC);
} else if (rigid_mode == RigidBody2D::MODE_DYNAMIC) {
PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_DYNAMIC);
set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC);
} else if (rigid_mode == RigidBody2D::MODE_KINEMATIC) {
PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_KINEMATIC);
set_body_mode(PhysicsServer2D::BODY_MODE_KINEMATIC);
} else if (rigid_mode == RigidBody2D::MODE_DYNAMIC_LOCKED) {
PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_DYNAMIC_LOCKED);
set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC_LOCKED);
} else {
// Default to Rigid
PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BodyMode::BODY_MODE_DYNAMIC);
// Default to Dynamic.
set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC);
}
_internal_simulate_physics = true;

View file

@ -49,7 +49,7 @@ void PhysicsBody2D::_bind_methods() {
PhysicsBody2D::PhysicsBody2D(PhysicsServer2D::BodyMode p_mode) :
CollisionObject2D(PhysicsServer2D::get_singleton()->body_create(), false) {
PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), p_mode);
set_body_mode(p_mode);
set_pickable(false);
}
@ -186,9 +186,9 @@ void StaticBody2D::set_kinematic_motion_enabled(bool p_enabled) {
kinematic_motion = p_enabled;
if (kinematic_motion) {
PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BODY_MODE_KINEMATIC);
set_body_mode(PhysicsServer2D::BODY_MODE_KINEMATIC);
} else {
PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BODY_MODE_STATIC);
set_body_mode(PhysicsServer2D::BODY_MODE_STATIC);
}
_update_kinematic_motion();
@ -199,28 +199,30 @@ bool StaticBody2D::is_kinematic_motion_enabled() const {
}
void StaticBody2D::_notification(int p_what) {
if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
switch (p_what) {
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
return;
}
if (Engine::get_singleton()->is_editor_hint()) {
return;
}
#endif
ERR_FAIL_COND(!kinematic_motion);
ERR_FAIL_COND(!kinematic_motion);
real_t delta_time = get_physics_process_delta_time();
real_t delta_time = get_physics_process_delta_time();
Transform2D new_transform = get_global_transform();
Transform2D new_transform = get_global_transform();
new_transform.translate(constant_linear_velocity * delta_time);
new_transform.set_rotation(new_transform.get_rotation() + constant_angular_velocity * delta_time);
new_transform.translate(constant_linear_velocity * delta_time);
new_transform.set_rotation(new_transform.get_rotation() + constant_angular_velocity * delta_time);
PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_TRANSFORM, new_transform);
PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_TRANSFORM, new_transform);
// Propagate transform change to node.
set_block_transform_notify(true);
set_global_transform(new_transform);
set_block_transform_notify(false);
// Propagate transform change to node.
set_block_transform_notify(true);
set_global_transform(new_transform);
set_block_transform_notify(false);
} break;
}
}
@ -495,18 +497,18 @@ void RigidBody2D::set_mode(Mode p_mode) {
mode = p_mode;
switch (p_mode) {
case MODE_DYNAMIC: {
PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BODY_MODE_DYNAMIC);
set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC);
} break;
case MODE_STATIC: {
PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BODY_MODE_STATIC);
set_body_mode(PhysicsServer2D::BODY_MODE_STATIC);
} break;
case MODE_KINEMATIC: {
PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BODY_MODE_KINEMATIC);
set_body_mode(PhysicsServer2D::BODY_MODE_KINEMATIC);
} break;
case MODE_DYNAMIC_LOCKED: {
PhysicsServer2D::get_singleton()->body_set_mode(get_rid(), PhysicsServer2D::BODY_MODE_DYNAMIC_LOCKED);
set_body_mode(PhysicsServer2D::BODY_MODE_DYNAMIC_LOCKED);
} break;
}
@ -762,18 +764,19 @@ bool RigidBody2D::is_contact_monitor_enabled() const {
void RigidBody2D::_notification(int p_what) {
#ifdef TOOLS_ENABLED
if (p_what == NOTIFICATION_ENTER_TREE) {
if (Engine::get_singleton()->is_editor_hint()) {
set_notify_local_transform(true); //used for warnings and only in editor
}
}
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
if (Engine::get_singleton()->is_editor_hint()) {
set_notify_local_transform(true); //used for warnings and only in editor
}
} break;
if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) {
if (Engine::get_singleton()->is_editor_hint()) {
update_configuration_warnings();
}
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
if (Engine::get_singleton()->is_editor_hint()) {
update_configuration_warnings();
}
} break;
}
#endif
}
@ -1232,26 +1235,28 @@ void CharacterBody2D::set_up_direction(const Vector2 &p_up_direction) {
}
void CharacterBody2D::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
last_valid_transform = get_global_transform();
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
last_valid_transform = get_global_transform();
// Reset move_and_slide() data.
on_floor = false;
on_floor_body = RID();
on_ceiling = false;
on_wall = false;
motion_results.clear();
floor_velocity = Vector2();
}
// Reset move_and_slide() data.
on_floor = false;
on_floor_body = RID();
on_ceiling = false;
on_wall = false;
motion_results.clear();
floor_velocity = Vector2();
} break;
if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) {
//used by sync to physics, send the new transform to the physics
Transform2D new_transform = get_global_transform();
PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_TRANSFORM, new_transform);
//but then revert changes
set_notify_local_transform(false);
set_global_transform(last_valid_transform);
set_notify_local_transform(true);
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
// Used by sync to physics, send the new transform to the physics.
Transform2D new_transform = get_global_transform();
PhysicsServer2D::get_singleton()->body_set_state(get_rid(), PhysicsServer2D::BODY_STATE_TRANSFORM, new_transform);
// But then revert changes.
set_notify_local_transform(false);
set_global_transform(last_valid_transform);
set_notify_local_transform(true);
} break;
}
}

View file

@ -32,7 +32,6 @@
#include "core/config/engine.h"
#include "scene/scene_string_names.h"
#include "servers/physics_server_3d.h"
void CollisionObject3D::_notification(int p_what) {
switch (p_what) {
@ -59,15 +58,22 @@ void CollisionObject3D::_notification(int p_what) {
PhysicsServer3D::get_singleton()->body_set_state(rid, PhysicsServer3D::BODY_STATE_TRANSFORM, get_global_transform());
}
RID space = get_world_3d()->get_space();
if (area) {
PhysicsServer3D::get_singleton()->area_set_space(rid, space);
} else {
PhysicsServer3D::get_singleton()->body_set_space(rid, space);
bool disabled = !is_enabled();
if (disabled && (disable_mode != DISABLE_MODE_REMOVE)) {
_apply_disabled();
}
if (!disabled || (disable_mode != DISABLE_MODE_REMOVE)) {
RID space = get_world_3d()->get_space();
if (area) {
PhysicsServer3D::get_singleton()->area_set_space(rid, space);
} else {
PhysicsServer3D::get_singleton()->body_set_space(rid, space);
}
}
_update_pickable();
//get space
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
@ -78,19 +84,34 @@ void CollisionObject3D::_notification(int p_what) {
}
_on_transform_changed();
} break;
case NOTIFICATION_VISIBILITY_CHANGED: {
_update_pickable();
} break;
case NOTIFICATION_EXIT_WORLD: {
if (area) {
PhysicsServer3D::get_singleton()->area_set_space(rid, RID());
} else {
PhysicsServer3D::get_singleton()->body_set_space(rid, RID());
bool disabled = !is_enabled();
if (!disabled || (disable_mode != DISABLE_MODE_REMOVE)) {
if (area) {
PhysicsServer3D::get_singleton()->area_set_space(rid, RID());
} else {
PhysicsServer3D::get_singleton()->body_set_space(rid, RID());
}
}
if (disabled && (disable_mode != DISABLE_MODE_REMOVE)) {
_apply_enabled();
}
} break;
case NOTIFICATION_DISABLED: {
_apply_disabled();
} break;
case NOTIFICATION_ENABLED: {
_apply_enabled();
} break;
}
}
@ -153,6 +174,79 @@ bool CollisionObject3D::get_collision_mask_bit(int p_bit) const {
return get_collision_mask() & (1 << p_bit);
}
void CollisionObject3D::set_disable_mode(DisableMode p_mode) {
if (disable_mode == p_mode) {
return;
}
bool disabled = is_inside_tree() && !is_enabled();
if (disabled) {
// Cancel previous disable mode.
_apply_enabled();
}
disable_mode = p_mode;
if (disabled) {
// Apply new disable mode.
_apply_disabled();
}
}
CollisionObject3D::DisableMode CollisionObject3D::get_disable_mode() const {
return disable_mode;
}
void CollisionObject3D::_apply_disabled() {
switch (disable_mode) {
case DISABLE_MODE_REMOVE: {
if (is_inside_tree()) {
if (area) {
PhysicsServer3D::get_singleton()->area_set_space(rid, RID());
} else {
PhysicsServer3D::get_singleton()->body_set_space(rid, RID());
}
}
} break;
case DISABLE_MODE_MAKE_STATIC: {
if (!area && (body_mode != PhysicsServer3D::BODY_MODE_STATIC)) {
PhysicsServer3D::get_singleton()->body_set_mode(rid, PhysicsServer3D::BODY_MODE_STATIC);
}
} break;
case DISABLE_MODE_KEEP_ACTIVE: {
// Nothing to do.
} break;
}
}
void CollisionObject3D::_apply_enabled() {
switch (disable_mode) {
case DISABLE_MODE_REMOVE: {
if (is_inside_tree()) {
RID space = get_world_3d()->get_space();
if (area) {
PhysicsServer3D::get_singleton()->area_set_space(rid, space);
} else {
PhysicsServer3D::get_singleton()->body_set_space(rid, space);
}
}
} break;
case DISABLE_MODE_MAKE_STATIC: {
if (!area && (body_mode != PhysicsServer3D::BODY_MODE_STATIC)) {
PhysicsServer3D::get_singleton()->body_set_mode(rid, body_mode);
}
} break;
case DISABLE_MODE_KEEP_ACTIVE: {
// Nothing to do.
} break;
}
}
void CollisionObject3D::_input_event(Node *p_camera, const Ref<InputEvent> &p_input_event, const Vector3 &p_pos, const Vector3 &p_normal, int p_shape) {
if (get_script_instance()) {
get_script_instance()->call(SceneStringNames::get_singleton()->_input_event, p_camera, p_input_event, p_pos, p_normal, p_shape);
@ -174,6 +268,22 @@ void CollisionObject3D::_mouse_exit() {
emit_signal(SceneStringNames::get_singleton()->mouse_exited);
}
void CollisionObject3D::set_body_mode(PhysicsServer3D::BodyMode p_mode) {
ERR_FAIL_COND(area);
if (body_mode == p_mode) {
return;
}
body_mode = p_mode;
if (is_inside_tree() && !is_enabled() && (disable_mode == DISABLE_MODE_MAKE_STATIC)) {
return;
}
PhysicsServer3D::get_singleton()->body_set_mode(rid, p_mode);
}
void CollisionObject3D::_update_pickable() {
if (!is_inside_tree()) {
return;
@ -305,6 +415,8 @@ void CollisionObject3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &CollisionObject3D::get_collision_layer_bit);
ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &CollisionObject3D::set_collision_mask_bit);
ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &CollisionObject3D::get_collision_mask_bit);
ClassDB::bind_method(D_METHOD("set_disable_mode", "mode"), &CollisionObject3D::set_disable_mode);
ClassDB::bind_method(D_METHOD("get_disable_mode"), &CollisionObject3D::get_disable_mode);
ClassDB::bind_method(D_METHOD("set_ray_pickable", "ray_pickable"), &CollisionObject3D::set_ray_pickable);
ClassDB::bind_method(D_METHOD("is_ray_pickable"), &CollisionObject3D::is_ray_pickable);
ClassDB::bind_method(D_METHOD("set_capture_input_on_drag", "enable"), &CollisionObject3D::set_capture_input_on_drag);
@ -332,6 +444,8 @@ void CollisionObject3D::_bind_methods() {
ADD_SIGNAL(MethodInfo("mouse_entered"));
ADD_SIGNAL(MethodInfo("mouse_exited"));
ADD_PROPERTY(PropertyInfo(Variant::INT, "disable_mode", PROPERTY_HINT_ENUM, "Remove,MakeStatic,KeepActive"), "set_disable_mode", "get_disable_mode");
ADD_GROUP("Collision", "collision_");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_layer", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_layer", "get_collision_layer");
ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask");
@ -339,6 +453,10 @@ void CollisionObject3D::_bind_methods() {
ADD_GROUP("Input", "input_");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "input_ray_pickable"), "set_ray_pickable", "is_ray_pickable");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "input_capture_on_drag"), "set_capture_input_on_drag", "get_capture_input_on_drag");
BIND_ENUM_CONSTANT(DISABLE_MODE_REMOVE);
BIND_ENUM_CONSTANT(DISABLE_MODE_MAKE_STATIC);
BIND_ENUM_CONSTANT(DISABLE_MODE_KEEP_ACTIVE);
}
uint32_t CollisionObject3D::create_shape_owner(Object *p_owner) {
@ -540,8 +658,8 @@ CollisionObject3D::CollisionObject3D(RID p_rid, bool p_area) {
PhysicsServer3D::get_singleton()->area_attach_object_instance_id(rid, get_instance_id());
} else {
PhysicsServer3D::get_singleton()->body_attach_object_instance_id(rid, get_instance_id());
PhysicsServer3D::get_singleton()->body_set_mode(rid, body_mode);
}
//set_transform_notify(true);
}
void CollisionObject3D::set_capture_input_on_drag(bool p_capture) {

View file

@ -33,10 +33,19 @@
#include "scene/3d/node_3d.h"
#include "scene/resources/shape_3d.h"
#include "servers/physics_server_3d.h"
class CollisionObject3D : public Node3D {
GDCLASS(CollisionObject3D, Node3D);
public:
enum DisableMode {
DISABLE_MODE_REMOVE,
DISABLE_MODE_MAKE_STATIC,
DISABLE_MODE_KEEP_ACTIVE,
};
private:
uint32_t collision_layer = 1;
uint32_t collision_mask = 1;
@ -44,6 +53,10 @@ class CollisionObject3D : public Node3D {
RID rid;
DisableMode disable_mode = DISABLE_MODE_REMOVE;
PhysicsServer3D::BodyMode body_mode = PhysicsServer3D::BODY_MODE_STATIC;
struct ShapeData {
Object *owner = nullptr;
Transform3D xform;
@ -76,6 +89,9 @@ class CollisionObject3D : public Node3D {
void _update_debug_shapes();
void _clear_debug_shapes();
void _apply_disabled();
void _apply_enabled();
protected:
CollisionObject3D(RID p_rid, bool p_area);
@ -89,6 +105,8 @@ protected:
virtual void _mouse_enter();
virtual void _mouse_exit();
void set_body_mode(PhysicsServer3D::BodyMode p_mode);
public:
void set_collision_layer(uint32_t p_layer);
uint32_t get_collision_layer() const;
@ -102,6 +120,9 @@ public:
void set_collision_mask_bit(int p_bit, bool p_value);
bool get_collision_mask_bit(int p_bit) const;
void set_disable_mode(DisableMode p_mode);
DisableMode get_disable_mode() const;
uint32_t create_shape_owner(Object *p_owner);
void remove_shape_owner(uint32_t owner);
void get_shape_owners(List<uint32_t> *r_owners);
@ -138,4 +159,6 @@ public:
~CollisionObject3D();
};
VARIANT_ENUM_CAST(CollisionObject3D::DisableMode);
#endif // COLLISION_OBJECT__H

View file

@ -65,7 +65,7 @@ void PhysicsBody3D::_bind_methods() {
PhysicsBody3D::PhysicsBody3D(PhysicsServer3D::BodyMode p_mode) :
CollisionObject3D(PhysicsServer3D::get_singleton()->body_create(), false) {
PhysicsServer3D::get_singleton()->body_set_mode(get_rid(), p_mode);
set_body_mode(p_mode);
}
PhysicsBody3D::~PhysicsBody3D() {
@ -200,9 +200,9 @@ void StaticBody3D::set_kinematic_motion_enabled(bool p_enabled) {
kinematic_motion = p_enabled;
if (kinematic_motion) {
PhysicsServer3D::get_singleton()->body_set_mode(get_rid(), PhysicsServer3D::BODY_MODE_KINEMATIC);
set_body_mode(PhysicsServer3D::BODY_MODE_KINEMATIC);
} else {
PhysicsServer3D::get_singleton()->body_set_mode(get_rid(), PhysicsServer3D::BODY_MODE_STATIC);
set_body_mode(PhysicsServer3D::BODY_MODE_STATIC);
}
_update_kinematic_motion();
@ -249,35 +249,37 @@ Vector3 StaticBody3D::get_angular_velocity() const {
}
void StaticBody3D::_notification(int p_what) {
if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) {
switch (p_what) {
case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: {
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
return;
}
if (Engine::get_singleton()->is_editor_hint()) {
return;
}
#endif
ERR_FAIL_COND(!kinematic_motion);
ERR_FAIL_COND(!kinematic_motion);
real_t delta_time = get_physics_process_delta_time();
real_t delta_time = get_physics_process_delta_time();
Transform3D new_transform = get_global_transform();
new_transform.origin += constant_linear_velocity * delta_time;
Transform3D new_transform = get_global_transform();
new_transform.origin += constant_linear_velocity * delta_time;
real_t ang_vel = constant_angular_velocity.length();
if (!Math::is_zero_approx(ang_vel)) {
Vector3 ang_vel_axis = constant_angular_velocity / ang_vel;
Basis rot(ang_vel_axis, ang_vel * delta_time);
new_transform.basis = rot * new_transform.basis;
new_transform.orthonormalize();
}
real_t ang_vel = constant_angular_velocity.length();
if (!Math::is_zero_approx(ang_vel)) {
Vector3 ang_vel_axis = constant_angular_velocity / ang_vel;
Basis rot(ang_vel_axis, ang_vel * delta_time);
new_transform.basis = rot * new_transform.basis;
new_transform.orthonormalize();
}
PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_TRANSFORM, new_transform);
PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_TRANSFORM, new_transform);
// Propagate transform change to node.
set_ignore_transform_notification(true);
set_global_transform(new_transform);
set_ignore_transform_notification(false);
_on_transform_changed();
// Propagate transform change to node.
set_ignore_transform_notification(true);
set_global_transform(new_transform);
set_ignore_transform_notification(false);
_on_transform_changed();
} break;
}
}
@ -565,18 +567,19 @@ void RigidBody3D::_direct_state_changed(Object *p_state) {
void RigidBody3D::_notification(int p_what) {
#ifdef TOOLS_ENABLED
if (p_what == NOTIFICATION_ENTER_TREE) {
if (Engine::get_singleton()->is_editor_hint()) {
set_notify_local_transform(true); //used for warnings and only in editor
}
}
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
if (Engine::get_singleton()->is_editor_hint()) {
set_notify_local_transform(true); //used for warnings and only in editor
}
} break;
if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) {
if (Engine::get_singleton()->is_editor_hint()) {
update_configuration_warnings();
}
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
if (Engine::get_singleton()->is_editor_hint()) {
update_configuration_warnings();
}
} break;
}
#endif
}
@ -584,18 +587,16 @@ void RigidBody3D::set_mode(Mode p_mode) {
mode = p_mode;
switch (p_mode) {
case MODE_DYNAMIC: {
PhysicsServer3D::get_singleton()->body_set_mode(get_rid(), PhysicsServer3D::BODY_MODE_DYNAMIC);
set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC);
} break;
case MODE_STATIC: {
PhysicsServer3D::get_singleton()->body_set_mode(get_rid(), PhysicsServer3D::BODY_MODE_STATIC);
set_body_mode(PhysicsServer3D::BODY_MODE_STATIC);
} break;
case MODE_DYNAMIC_LOCKED: {
PhysicsServer3D::get_singleton()->body_set_mode(get_rid(), PhysicsServer3D::BODY_MODE_DYNAMIC_LOCKED);
set_body_mode(PhysicsServer3D::BODY_MODE_DYNAMIC_LOCKED);
} break;
case MODE_KINEMATIC: {
PhysicsServer3D::get_singleton()->body_set_mode(get_rid(), PhysicsServer3D::BODY_MODE_KINEMATIC);
set_body_mode(PhysicsServer3D::BODY_MODE_KINEMATIC);
} break;
}
update_configuration_warnings();
@ -1233,14 +1234,16 @@ void CharacterBody3D::set_up_direction(const Vector3 &p_up_direction) {
}
void CharacterBody3D::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
// Reset move_and_slide() data.
on_floor = false;
on_floor_body = RID();
on_ceiling = false;
on_wall = false;
motion_results.clear();
floor_velocity = Vector3();
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
// Reset move_and_slide() data.
on_floor = false;
on_floor_body = RID();
on_ceiling = false;
on_wall = false;
motion_results.clear();
floor_velocity = Vector3();
} break;
}
}
@ -2095,7 +2098,8 @@ void PhysicalBone3D::_notification(int p_what) {
_reload_joint();
}
break;
case NOTIFICATION_EXIT_TREE:
case NOTIFICATION_EXIT_TREE: {
if (parent_skeleton) {
if (-1 != bone_id) {
parent_skeleton->unbind_physical_bone_from_bone(bone_id);
@ -2105,12 +2109,13 @@ void PhysicalBone3D::_notification(int p_what) {
}
parent_skeleton = nullptr;
PhysicsServer3D::get_singleton()->joint_clear(joint);
break;
case NOTIFICATION_TRANSFORM_CHANGED:
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
if (Engine::get_singleton()->is_editor_hint()) {
update_offset();
}
break;
} break;
}
}
@ -2605,7 +2610,7 @@ void PhysicalBone3D::_start_physics_simulation() {
return;
}
reset_to_rest_position();
PhysicsServer3D::get_singleton()->body_set_mode(get_rid(), PhysicsServer3D::BODY_MODE_DYNAMIC);
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));
@ -2618,11 +2623,11 @@ void PhysicalBone3D::_stop_physics_simulation() {
return;
}
if (parent_skeleton->get_animate_physical_bones()) {
PhysicsServer3D::get_singleton()->body_set_mode(get_rid(), PhysicsServer3D::BODY_MODE_KINEMATIC);
set_body_mode(PhysicsServer3D::BODY_MODE_KINEMATIC);
PhysicsServer3D::get_singleton()->body_set_collision_layer(get_rid(), get_collision_layer());
PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), get_collision_mask());
} else {
PhysicsServer3D::get_singleton()->body_set_mode(get_rid(), PhysicsServer3D::BODY_MODE_STATIC);
set_body_mode(PhysicsServer3D::BODY_MODE_STATIC);
PhysicsServer3D::get_singleton()->body_set_collision_layer(get_rid(), 0);
PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), 0);
}

View file

@ -266,12 +266,13 @@ void SoftBody3D::_notification(int p_what) {
PhysicsServer3D::get_singleton()->soft_body_set_space(physics_rid, space);
prepare_physics_server();
} break;
case NOTIFICATION_READY: {
if (!parent_collision_ignore.is_empty()) {
add_collision_exception_with(get_node(parent_collision_ignore));
}
} break;
case NOTIFICATION_TRANSFORM_CHANGED: {
if (Engine::get_singleton()->is_editor_hint()) {
_reset_points_offsets();
@ -285,27 +286,36 @@ void SoftBody3D::_notification(int p_what) {
set_as_top_level(true);
set_transform(Transform3D());
set_notify_transform(true);
} break;
case NOTIFICATION_VISIBILITY_CHANGED: {
_update_pickable();
} break;
case NOTIFICATION_EXIT_WORLD: {
PhysicsServer3D::get_singleton()->soft_body_set_space(physics_rid, RID());
} break;
}
case NOTIFICATION_DISABLED: {
if (is_inside_tree() && (disable_mode == DISABLE_MODE_REMOVE)) {
prepare_physics_server();
}
} break;
case NOTIFICATION_ENABLED: {
if (is_inside_tree() && (disable_mode == DISABLE_MODE_REMOVE)) {
prepare_physics_server();
}
} break;
#ifdef TOOLS_ENABLED
if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) {
if (Engine::get_singleton()->is_editor_hint()) {
update_configuration_warnings();
}
}
case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: {
if (Engine::get_singleton()->is_editor_hint()) {
update_configuration_warnings();
}
} break;
#endif
}
}
void SoftBody3D::_bind_methods() {
@ -326,6 +336,9 @@ void SoftBody3D::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_parent_collision_ignore", "parent_collision_ignore"), &SoftBody3D::set_parent_collision_ignore);
ClassDB::bind_method(D_METHOD("get_parent_collision_ignore"), &SoftBody3D::get_parent_collision_ignore);
ClassDB::bind_method(D_METHOD("set_disable_mode", "mode"), &SoftBody3D::set_disable_mode);
ClassDB::bind_method(D_METHOD("get_disable_mode"), &SoftBody3D::get_disable_mode);
ClassDB::bind_method(D_METHOD("get_collision_exceptions"), &SoftBody3D::get_collision_exceptions);
ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &SoftBody3D::add_collision_exception_with);
ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &SoftBody3D::remove_collision_exception_with);
@ -364,6 +377,11 @@ void SoftBody3D::_bind_methods() {
ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "drag_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_coefficient", "get_drag_coefficient");
ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ray_pickable"), "set_ray_pickable", "is_ray_pickable");
ADD_PROPERTY(PropertyInfo(Variant::INT, "disable_mode", PROPERTY_HINT_ENUM, "Remove,KeepActive"), "set_disable_mode", "get_disable_mode");
BIND_ENUM_CONSTANT(DISABLE_MODE_REMOVE);
BIND_ENUM_CONSTANT(DISABLE_MODE_KEEP_ACTIVE);
}
TypedArray<String> SoftBody3D::get_configuration_warnings() const {
@ -421,6 +439,7 @@ void SoftBody3D::_draw_soft_mesh() {
}
void SoftBody3D::prepare_physics_server() {
#ifdef TOOLS_ENABLED
if (Engine::get_singleton()->is_editor_hint()) {
if (get_mesh().is_valid()) {
PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, get_mesh());
@ -430,8 +449,9 @@ void SoftBody3D::prepare_physics_server() {
return;
}
#endif
if (get_mesh().is_valid()) {
if (get_mesh().is_valid() && (is_enabled() || (disable_mode != DISABLE_MODE_REMOVE))) {
become_mesh_owner();
PhysicsServer3D::get_singleton()->soft_body_set_mesh(physics_rid, get_mesh());
RS::get_singleton()->connect("frame_pre_draw", callable_mp(this, &SoftBody3D::_draw_soft_mesh));
@ -527,6 +547,28 @@ bool SoftBody3D::get_collision_layer_bit(int p_bit) const {
return get_collision_layer() & (1 << p_bit);
}
void SoftBody3D::set_disable_mode(DisableMode p_mode) {
if (disable_mode == p_mode) {
return;
}
bool inside_tree = is_inside_tree();
if (inside_tree && (disable_mode == DISABLE_MODE_REMOVE)) {
prepare_physics_server();
}
disable_mode = p_mode;
if (inside_tree && (disable_mode == DISABLE_MODE_REMOVE)) {
prepare_physics_server();
}
}
SoftBody3D::DisableMode SoftBody3D::get_disable_mode() const {
return disable_mode;
}
void SoftBody3D::set_parent_collision_ignore(const NodePath &p_parent_collision_ignore) {
parent_collision_ignore = p_parent_collision_ignore;
}

View file

@ -67,6 +67,11 @@ class SoftBody3D : public MeshInstance3D {
GDCLASS(SoftBody3D, MeshInstance3D);
public:
enum DisableMode {
DISABLE_MODE_REMOVE,
DISABLE_MODE_KEEP_ACTIVE,
};
struct PinnedPoint {
int point_index = -1;
NodePath spatial_attachment_path;
@ -83,6 +88,8 @@ private:
RID physics_rid;
DisableMode disable_mode = DISABLE_MODE_REMOVE;
bool mesh_owner = false;
uint32_t collision_mask = 1;
uint32_t collision_layer = 1;
@ -137,6 +144,9 @@ public:
void set_collision_layer_bit(int p_bit, bool p_value);
bool get_collision_layer_bit(int p_bit) const;
void set_disable_mode(DisableMode p_mode);
DisableMode get_disable_mode() const;
void set_parent_collision_ignore(const NodePath &p_parent_collision_ignore);
const NodePath &get_parent_collision_ignore() const;
@ -192,4 +202,6 @@ private:
int _has_pinned_point(int p_point_index) const;
};
VARIANT_ENUM_CAST(SoftBody3D::DisableMode);
#endif // SOFT_PHYSICS_BODY_H

View file

@ -79,26 +79,29 @@ public:
};
void VehicleWheel3D::_notification(int p_what) {
if (p_what == NOTIFICATION_ENTER_TREE) {
VehicleBody3D *cb = Object::cast_to<VehicleBody3D>(get_parent());
if (!cb) {
return;
}
body = cb;
local_xform = get_transform();
cb->wheels.push_back(this);
switch (p_what) {
case NOTIFICATION_ENTER_TREE: {
VehicleBody3D *cb = Object::cast_to<VehicleBody3D>(get_parent());
if (!cb) {
return;
}
body = cb;
local_xform = get_transform();
cb->wheels.push_back(this);
m_chassisConnectionPointCS = get_transform().origin;
m_wheelDirectionCS = -get_transform().basis.get_axis(Vector3::AXIS_Y).normalized();
m_wheelAxleCS = get_transform().basis.get_axis(Vector3::AXIS_X).normalized();
}
if (p_what == NOTIFICATION_EXIT_TREE) {
VehicleBody3D *cb = Object::cast_to<VehicleBody3D>(get_parent());
if (!cb) {
return;
}
cb->wheels.erase(this);
body = nullptr;
m_chassisConnectionPointCS = get_transform().origin;
m_wheelDirectionCS = -get_transform().basis.get_axis(Vector3::AXIS_Y).normalized();
m_wheelAxleCS = get_transform().basis.get_axis(Vector3::AXIS_X).normalized();
} break;
case NOTIFICATION_EXIT_TREE: {
VehicleBody3D *cb = Object::cast_to<VehicleBody3D>(get_parent());
if (!cb) {
return;
}
cb->wheels.erase(this);
body = nullptr;
} break;
}
}

View file

@ -402,6 +402,7 @@ void Node::set_process_mode(ProcessMode p_mode) {
}
bool prev_can_process = can_process();
bool prev_enabled = _is_enabled();
data.process_mode = p_mode;
@ -416,6 +417,7 @@ void Node::set_process_mode(ProcessMode p_mode) {
}
bool next_can_process = can_process();
bool next_enabled = _is_enabled();
int pause_notification = 0;
@ -425,7 +427,16 @@ void Node::set_process_mode(ProcessMode p_mode) {
pause_notification = NOTIFICATION_UNPAUSED;
}
_propagate_process_owner(data.process_owner, pause_notification);
int enabled_notification = 0;
if (prev_enabled && !next_enabled) {
enabled_notification = NOTIFICATION_DISABLED;
} else if (!prev_enabled && next_enabled) {
enabled_notification = NOTIFICATION_ENABLED;
}
_propagate_process_owner(data.process_owner, pause_notification, enabled_notification);
#ifdef TOOLS_ENABLED
// This is required for the editor to update the visibility of disabled nodes
// It's very expensive during runtime to change, so editor-only
@ -454,17 +465,21 @@ Node::ProcessMode Node::get_process_mode() const {
return data.process_mode;
}
void Node::_propagate_process_owner(Node *p_owner, int p_notification) {
void Node::_propagate_process_owner(Node *p_owner, int p_pause_notification, int p_enabled_notification) {
data.process_owner = p_owner;
if (p_notification != 0) {
notification(p_notification);
if (p_pause_notification != 0) {
notification(p_pause_notification);
}
if (p_enabled_notification != 0) {
notification(p_enabled_notification);
}
for (int i = 0; i < data.children.size(); i++) {
Node *c = data.children[i];
if (c->data.process_mode == PROCESS_MODE_INHERIT) {
c->_propagate_process_owner(p_owner, p_notification);
c->_propagate_process_owner(p_owner, p_pause_notification, p_enabled_notification);
}
}
}
@ -668,6 +683,27 @@ bool Node::_can_process(bool p_paused) const {
}
}
bool Node::_is_enabled() const {
ProcessMode process_mode;
if (data.process_mode == PROCESS_MODE_INHERIT) {
if (!data.process_owner) {
process_mode = PROCESS_MODE_PAUSABLE;
} else {
process_mode = data.process_owner->data.process_mode;
}
} else {
process_mode = data.process_mode;
}
return (process_mode != PROCESS_MODE_DISABLED);
}
bool Node::is_enabled() const {
ERR_FAIL_COND_V(!is_inside_tree(), false);
return _is_enabled();
}
float Node::get_physics_process_delta_time() const {
if (data.tree) {
return data.tree->get_physics_process_time();
@ -2619,6 +2655,8 @@ void Node::_bind_methods() {
BIND_CONSTANT(NOTIFICATION_INTERNAL_PROCESS);
BIND_CONSTANT(NOTIFICATION_INTERNAL_PHYSICS_PROCESS);
BIND_CONSTANT(NOTIFICATION_POST_ENTER_TREE);
BIND_CONSTANT(NOTIFICATION_DISABLED);
BIND_CONSTANT(NOTIFICATION_ENABLED);
BIND_CONSTANT(NOTIFICATION_EDITOR_PRE_SAVE);
BIND_CONSTANT(NOTIFICATION_EDITOR_POST_SAVE);

View file

@ -163,7 +163,7 @@ private:
void _propagate_after_exit_tree();
void _propagate_validate_owner();
void _print_stray_nodes();
void _propagate_process_owner(Node *p_owner, int p_notification);
void _propagate_process_owner(Node *p_owner, int p_pause_notification, int p_enabled_notification);
Array _get_node_and_resource(const NodePath &p_path);
void _duplicate_signals(const Node *p_original, Node *p_copy) const;
@ -181,6 +181,7 @@ private:
void _propagate_pause_notification(bool p_enable);
_FORCE_INLINE_ bool _can_process(bool p_paused) const;
_FORCE_INLINE_ bool _is_enabled() const;
protected:
void _block() { data.blocked++; }
@ -224,6 +225,8 @@ public:
NOTIFICATION_INTERNAL_PROCESS = 25,
NOTIFICATION_INTERNAL_PHYSICS_PROCESS = 26,
NOTIFICATION_POST_ENTER_TREE = 27,
NOTIFICATION_DISABLED = 28,
NOTIFICATION_ENABLED = 29,
//keep these linked to node
NOTIFICATION_WM_MOUSE_ENTER = 1002,
@ -380,6 +383,7 @@ public:
ProcessMode get_process_mode() const;
bool can_process() const;
bool can_process_notification(int p_what) const;
bool is_enabled() const;
void request_ready();