diff options
Diffstat (limited to 'scene/3d')
93 files changed, 8571 insertions, 5865 deletions
diff --git a/scene/3d/SCsub b/scene/3d/SCsub index ce69e8aa19..fc61250247 100644 --- a/scene/3d/SCsub +++ b/scene/3d/SCsub @@ -2,8 +2,4 @@ Import("env") -if env["disable_3d"]: - env.add_source_files(env.scene_sources, "node_3d.cpp") - env.add_source_files(env.scene_sources, "skeleton_3d.cpp") -else: - env.add_source_files(env.scene_sources, "*.cpp") +env.add_source_files(env.scene_sources, "*.cpp") diff --git a/scene/3d/area_3d.cpp b/scene/3d/area_3d.cpp index dc35f069c0..943586f43c 100644 --- a/scene/3d/area_3d.cpp +++ b/scene/3d/area_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,7 +32,6 @@ #include "scene/scene_string_names.h" #include "servers/audio_server.h" -#include "servers/physics_server_3d.h" void Area3D::set_space_override_mode(SpaceOverride p_mode) { space_override = p_mode; @@ -106,6 +105,61 @@ real_t Area3D::get_priority() const { return priority; } +void Area3D::set_wind_force_magnitude(real_t p_wind_force_magnitude) { + wind_force_magnitude = p_wind_force_magnitude; + if (is_inside_tree()) { + _initialize_wind(); + } +} + +real_t Area3D::get_wind_force_magnitude() const { + return wind_force_magnitude; +} + +void Area3D::set_wind_attenuation_factor(real_t p_wind_force_attenuation_factor) { + wind_attenuation_factor = p_wind_force_attenuation_factor; + if (is_inside_tree()) { + _initialize_wind(); + } +} + +real_t Area3D::get_wind_attenuation_factor() const { + return wind_attenuation_factor; +} + +void Area3D::set_wind_source_path(const NodePath &p_wind_source_path) { + wind_source_path = p_wind_source_path; + if (is_inside_tree()) { + _initialize_wind(); + } +} + +const NodePath &Area3D::get_wind_source_path() const { + return wind_source_path; +} + +void Area3D::_initialize_wind() { + real_t temp_magnitude = 0.0; + Vector3 wind_direction(0., 0., 0.); + Vector3 wind_source(0., 0., 0.); + + // Overwrite with area-specified info if available + if (!wind_source_path.is_empty()) { + Node3D *p_wind_source = Object::cast_to<Node3D>(get_node(wind_source_path)); + ERR_FAIL_NULL(p_wind_source); + Transform3D global_transform = p_wind_source->get_transform(); + wind_direction = -global_transform.basis.get_axis(Vector3::AXIS_Z).normalized(); + wind_source = global_transform.origin; + temp_magnitude = wind_force_magnitude; + } + + // Set force, source and direction in the physics server. + PhysicsServer3D::get_singleton()->area_set_param(get_rid(), PhysicsServer3D::AREA_PARAM_WIND_ATTENUATION_FACTOR, wind_attenuation_factor); + PhysicsServer3D::get_singleton()->area_set_param(get_rid(), PhysicsServer3D::AREA_PARAM_WIND_SOURCE, wind_source); + PhysicsServer3D::get_singleton()->area_set_param(get_rid(), PhysicsServer3D::AREA_PARAM_WIND_DIRECTION, wind_direction); + PhysicsServer3D::get_singleton()->area_set_param(get_rid(), PhysicsServer3D::AREA_PARAM_WIND_FORCE_MAGNITUDE, temp_magnitude); +} + void Area3D::_body_enter_tree(ObjectID p_id) { Object *obj = ObjectDB::get_instance(p_id); Node *node = Object::cast_to<Node>(obj); @@ -118,7 +172,7 @@ void Area3D::_body_enter_tree(ObjectID p_id) { E->get().in_tree = true; emit_signal(SceneStringNames::get_singleton()->body_entered, node); for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->body_shape_entered, p_id, node, E->get().shapes[i].body_shape, E->get().shapes[i].area_shape); + emit_signal(SceneStringNames::get_singleton()->body_shape_entered, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].area_shape); } } @@ -132,7 +186,7 @@ void Area3D::_body_exit_tree(ObjectID p_id) { E->get().in_tree = false; emit_signal(SceneStringNames::get_singleton()->body_exited, node); for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->body_shape_exited, p_id, node, E->get().shapes[i].body_shape, E->get().shapes[i].area_shape); + emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].area_shape); } } @@ -154,6 +208,7 @@ void Area3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i if (body_in) { if (!E) { E = body_map.insert(objid, BodyState()); + E->get().rid = p_body; E->get().rc = 0; E->get().in_tree = node && node->is_inside_tree(); if (node) { @@ -170,7 +225,7 @@ void Area3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i } if (E->get().in_tree) { - emit_signal(SceneStringNames::get_singleton()->body_shape_entered, objid, node, p_body_shape, p_area_shape); + emit_signal(SceneStringNames::get_singleton()->body_shape_entered, p_body, node, p_body_shape, p_area_shape); } } else { @@ -180,25 +235,19 @@ void Area3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, i E->get().shapes.erase(ShapePair(p_body_shape, p_area_shape)); } - bool eraseit = false; - + bool in_tree = E->get().in_tree; if (E->get().rc == 0) { + body_map.erase(E); if (node) { node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_body_enter_tree)); node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_body_exit_tree)); - if (E->get().in_tree) { + if (in_tree) { emit_signal(SceneStringNames::get_singleton()->body_exited, obj); } } - - eraseit = true; } - if (node && E->get().in_tree) { - emit_signal(SceneStringNames::get_singleton()->body_shape_exited, objid, obj, p_body_shape, p_area_shape); - } - - if (eraseit) { - body_map.erase(E); + if (node && in_tree) { + emit_signal(SceneStringNames::get_singleton()->body_shape_exited, p_body, obj, p_body_shape, p_area_shape); } } @@ -217,7 +266,7 @@ void Area3D::_clear_monitoring() { Object *obj = ObjectDB::get_instance(E->key()); Node *node = Object::cast_to<Node>(obj); - if (!node) { //node may have been deleted in previous frame or at other legiminate point + if (!node) { //node may have been deleted in previous frame or at other legitimate point continue; } //ERR_CONTINUE(!node); @@ -230,7 +279,7 @@ void Area3D::_clear_monitoring() { } for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->key(), node, E->get().shapes[i].body_shape, E->get().shapes[i].area_shape); + emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].area_shape); } emit_signal(SceneStringNames::get_singleton()->body_exited, node); @@ -246,7 +295,7 @@ void Area3D::_clear_monitoring() { Object *obj = ObjectDB::get_instance(E->key()); Node *node = Object::cast_to<Node>(obj); - if (!node) { //node may have been deleted in previous frame or at other legiminate point + if (!node) { //node may have been deleted in previous frame or at other legitimate point continue; } //ERR_CONTINUE(!node); @@ -259,7 +308,7 @@ void Area3D::_clear_monitoring() { } for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->area_shape_exited, E->key(), node, E->get().shapes[i].area_shape, E->get().shapes[i].self_shape); + emit_signal(SceneStringNames::get_singleton()->area_shape_exited, E->get().rid, node, E->get().shapes[i].area_shape, E->get().shapes[i].self_shape); } emit_signal(SceneStringNames::get_singleton()->area_exited, obj); @@ -270,6 +319,8 @@ void Area3D::_clear_monitoring() { void Area3D::_notification(int p_what) { if (p_what == NOTIFICATION_EXIT_TREE) { _clear_monitoring(); + } else if (p_what == NOTIFICATION_ENTER_TREE) { + _initialize_wind(); } } @@ -304,7 +355,7 @@ void Area3D::_area_enter_tree(ObjectID p_id) { E->get().in_tree = true; emit_signal(SceneStringNames::get_singleton()->area_entered, node); for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->area_shape_entered, p_id, node, E->get().shapes[i].area_shape, E->get().shapes[i].self_shape); + emit_signal(SceneStringNames::get_singleton()->area_shape_entered, E->get().rid, node, E->get().shapes[i].area_shape, E->get().shapes[i].self_shape); } } @@ -318,7 +369,7 @@ void Area3D::_area_exit_tree(ObjectID p_id) { E->get().in_tree = false; emit_signal(SceneStringNames::get_singleton()->area_exited, node); for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->area_shape_exited, p_id, node, E->get().shapes[i].area_shape, E->get().shapes[i].self_shape); + emit_signal(SceneStringNames::get_singleton()->area_shape_exited, E->get().rid, node, E->get().shapes[i].area_shape, E->get().shapes[i].self_shape); } } @@ -340,6 +391,7 @@ void Area3D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i if (area_in) { if (!E) { E = area_map.insert(objid, AreaState()); + E->get().rid = p_area; E->get().rc = 0; E->get().in_tree = node && node->is_inside_tree(); if (node) { @@ -356,7 +408,7 @@ void Area3D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i } if (!node || E->get().in_tree) { - emit_signal(SceneStringNames::get_singleton()->area_shape_entered, objid, node, p_area_shape, p_self_shape); + emit_signal(SceneStringNames::get_singleton()->area_shape_entered, p_area, node, p_area_shape, p_self_shape); } } else { @@ -366,25 +418,19 @@ void Area3D::_area_inout(int p_status, const RID &p_area, ObjectID p_instance, i E->get().shapes.erase(AreaShapePair(p_area_shape, p_self_shape)); } - bool eraseit = false; - + bool in_tree = E->get().in_tree; if (E->get().rc == 0) { + area_map.erase(E); if (node) { node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &Area3D::_area_enter_tree)); node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Area3D::_area_exit_tree)); - if (E->get().in_tree) { + if (in_tree) { emit_signal(SceneStringNames::get_singleton()->area_exited, obj); } } - - eraseit = true; - } - if (!node || E->get().in_tree) { - emit_signal(SceneStringNames::get_singleton()->area_shape_exited, objid, obj, p_area_shape, p_self_shape); } - - if (eraseit) { - area_map.erase(E); + if (!node || in_tree) { + emit_signal(SceneStringNames::get_singleton()->area_shape_exited, p_area, obj, p_area_shape, p_self_shape); } } @@ -463,52 +509,6 @@ bool Area3D::overlaps_body(Node *p_body) const { return E->get().in_tree; } -void Area3D::set_collision_mask(uint32_t p_mask) { - collision_mask = p_mask; - PhysicsServer3D::get_singleton()->area_set_collision_mask(get_rid(), p_mask); -} - -uint32_t Area3D::get_collision_mask() const { - return collision_mask; -} - -void Area3D::set_collision_layer(uint32_t p_layer) { - collision_layer = p_layer; - PhysicsServer3D::get_singleton()->area_set_collision_layer(get_rid(), p_layer); -} - -uint32_t Area3D::get_collision_layer() const { - return collision_layer; -} - -void Area3D::set_collision_mask_bit(int p_bit, bool p_value) { - uint32_t mask = get_collision_mask(); - if (p_value) { - mask |= 1 << p_bit; - } else { - mask &= ~(1 << p_bit); - } - set_collision_mask(mask); -} - -bool Area3D::get_collision_mask_bit(int p_bit) const { - return get_collision_mask() & (1 << p_bit); -} - -void Area3D::set_collision_layer_bit(int p_bit, bool p_value) { - uint32_t layer = get_collision_layer(); - if (p_value) { - layer |= 1 << p_bit; - } else { - layer &= ~(1 << p_bit); - } - set_collision_layer(layer); -} - -bool Area3D::get_collision_layer_bit(int p_bit) const { - return get_collision_layer() & (1 << p_bit); -} - void Area3D::set_audio_bus_override(bool p_override) { audio_bus_override = p_override; } @@ -517,11 +517,11 @@ bool Area3D::is_overriding_audio_bus() const { return audio_bus_override; } -void Area3D::set_audio_bus(const StringName &p_audio_bus) { +void Area3D::set_audio_bus_name(const StringName &p_audio_bus) { audio_bus = p_audio_bus; } -StringName Area3D::get_audio_bus() const { +StringName Area3D::get_audio_bus_name() const { for (int i = 0; i < AudioServer::get_singleton()->get_bus_count(); i++) { if (AudioServer::get_singleton()->get_bus_name(i) == audio_bus) { return audio_bus; @@ -607,17 +607,14 @@ void Area3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_priority", "priority"), &Area3D::set_priority); ClassDB::bind_method(D_METHOD("get_priority"), &Area3D::get_priority); - ClassDB::bind_method(D_METHOD("set_collision_mask", "collision_mask"), &Area3D::set_collision_mask); - ClassDB::bind_method(D_METHOD("get_collision_mask"), &Area3D::get_collision_mask); - - ClassDB::bind_method(D_METHOD("set_collision_layer", "collision_layer"), &Area3D::set_collision_layer); - ClassDB::bind_method(D_METHOD("get_collision_layer"), &Area3D::get_collision_layer); + ClassDB::bind_method(D_METHOD("set_wind_force_magnitude", "wind_force_magnitude"), &Area3D::set_wind_force_magnitude); + ClassDB::bind_method(D_METHOD("get_wind_force_magnitude"), &Area3D::get_wind_force_magnitude); - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &Area3D::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &Area3D::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_wind_attenuation_factor", "wind_attenuation_factor"), &Area3D::set_wind_attenuation_factor); + ClassDB::bind_method(D_METHOD("get_wind_attenuation_factor"), &Area3D::get_wind_attenuation_factor); - ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &Area3D::set_collision_layer_bit); - ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &Area3D::get_collision_layer_bit); + ClassDB::bind_method(D_METHOD("set_wind_source_path", "wind_source_path"), &Area3D::set_wind_source_path); + ClassDB::bind_method(D_METHOD("get_wind_source_path"), &Area3D::get_wind_source_path); ClassDB::bind_method(D_METHOD("set_monitorable", "enable"), &Area3D::set_monitorable); ClassDB::bind_method(D_METHOD("is_monitorable"), &Area3D::is_monitorable); @@ -637,8 +634,8 @@ void Area3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_audio_bus_override", "enable"), &Area3D::set_audio_bus_override); ClassDB::bind_method(D_METHOD("is_overriding_audio_bus"), &Area3D::is_overriding_audio_bus); - ClassDB::bind_method(D_METHOD("set_audio_bus", "name"), &Area3D::set_audio_bus); - ClassDB::bind_method(D_METHOD("get_audio_bus"), &Area3D::get_audio_bus); + ClassDB::bind_method(D_METHOD("set_audio_bus_name", "name"), &Area3D::set_audio_bus_name); + ClassDB::bind_method(D_METHOD("get_audio_bus_name"), &Area3D::get_audio_bus_name); ClassDB::bind_method(D_METHOD("set_use_reverb_bus", "enable"), &Area3D::set_use_reverb_bus); ClassDB::bind_method(D_METHOD("is_using_reverb_bus"), &Area3D::is_using_reverb_bus); @@ -652,32 +649,36 @@ void Area3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_reverb_uniformity", "amount"), &Area3D::set_reverb_uniformity); ClassDB::bind_method(D_METHOD("get_reverb_uniformity"), &Area3D::get_reverb_uniformity); - ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "area_shape"))); - ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "area_shape"))); - ADD_SIGNAL(MethodInfo("body_entered", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); - ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); + ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); + ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); + ADD_SIGNAL(MethodInfo("body_entered", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"))); + ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node3D"))); - ADD_SIGNAL(MethodInfo("area_shape_entered", PropertyInfo(Variant::INT, "area_id"), PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area3D"), PropertyInfo(Variant::INT, "area_shape"), PropertyInfo(Variant::INT, "self_shape"))); - ADD_SIGNAL(MethodInfo("area_shape_exited", PropertyInfo(Variant::INT, "area_id"), PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area3D"), PropertyInfo(Variant::INT, "area_shape"), PropertyInfo(Variant::INT, "self_shape"))); + ADD_SIGNAL(MethodInfo("area_shape_entered", PropertyInfo(Variant::RID, "area_rid"), PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area3D"), PropertyInfo(Variant::INT, "area_shape"), PropertyInfo(Variant::INT, "local_shape"))); + ADD_SIGNAL(MethodInfo("area_shape_exited", PropertyInfo(Variant::RID, "area_rid"), PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area3D"), PropertyInfo(Variant::INT, "area_shape"), PropertyInfo(Variant::INT, "local_shape"))); ADD_SIGNAL(MethodInfo("area_entered", PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area3D"))); ADD_SIGNAL(MethodInfo("area_exited", PropertyInfo(Variant::OBJECT, "area", PROPERTY_HINT_RESOURCE_TYPE, "Area3D"))); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitoring"), "set_monitoring", "is_monitoring"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitorable"), "set_monitorable", "is_monitorable"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,128,1"), "set_priority", "get_priority"); + + ADD_GROUP("Physics Overrides", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "space_override", PROPERTY_HINT_ENUM, "Disabled,Combine,Combine-Replace,Replace,Replace-Combine"), "set_space_override_mode", "get_space_override_mode"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "gravity_point"), "set_gravity_is_point", "is_gravity_a_point"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_distance_scale", PROPERTY_HINT_EXP_RANGE, "0,1024,0.001,or_greater"), "set_gravity_distance_scale", "get_gravity_distance_scale"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_distance_scale", PROPERTY_HINT_RANGE, "0,1024,0.001,or_greater,exp"), "set_gravity_distance_scale", "get_gravity_distance_scale"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity_vec"), "set_gravity_vector", "get_gravity_vector"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity", PROPERTY_HINT_RANGE, "-1024,1024,0.01"), "set_gravity", "get_gravity"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity", PROPERTY_HINT_RANGE, "-32,32,0.001,or_lesser,or_greater"), "set_gravity", "get_gravity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_damp", PROPERTY_HINT_RANGE, "0,100,0.001,or_greater"), "set_angular_damp", "get_angular_damp"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "priority", PROPERTY_HINT_RANGE, "0,128,1"), "set_priority", "get_priority"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitoring"), "set_monitoring", "is_monitoring"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "monitorable"), "set_monitorable", "is_monitorable"); - 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"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wind_force_magnitude", PROPERTY_HINT_RANGE, "0,10,0.001,or_greater"), "set_wind_force_magnitude", "get_wind_force_magnitude"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "wind_attenuation_factor", PROPERTY_HINT_RANGE, "0.0,3.0,0.001,or_greater"), "set_wind_attenuation_factor", "get_wind_attenuation_factor"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "wind_source_path", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Node3D"), "set_wind_source_path", "get_wind_source_path"); + ADD_GROUP("Audio Bus", "audio_bus_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "audio_bus_override"), "set_audio_bus_override", "is_overriding_audio_bus"); - ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "audio_bus_name", PROPERTY_HINT_ENUM, ""), "set_audio_bus", "get_audio_bus"); + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "audio_bus_name", PROPERTY_HINT_ENUM, ""), "set_audio_bus_name", "get_audio_bus_name"); + ADD_GROUP("Reverb Bus", "reverb_bus_"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "reverb_bus_enable"), "set_use_reverb_bus", "is_using_reverb_bus"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "reverb_bus_name", PROPERTY_HINT_ENUM, ""), "set_reverb_bus", "get_reverb_bus"); @@ -693,29 +694,10 @@ void Area3D::_bind_methods() { Area3D::Area3D() : CollisionObject3D(PhysicsServer3D::get_singleton()->area_create(), true) { - space_override = SPACE_OVERRIDE_DISABLED; set_gravity(9.8); - locked = false; set_gravity_vector(Vector3(0, -1, 0)); - gravity_is_point = false; - gravity_distance_scale = 0; - linear_damp = 0.1; - angular_damp = 0.1; - priority = 0; - monitoring = false; - monitorable = false; - collision_mask = 1; - collision_layer = 1; set_monitoring(true); set_monitorable(true); - - audio_bus_override = false; - audio_bus = "Master"; - - use_reverb_bus = false; - reverb_bus = "Master"; - reverb_amount = 0.0; - reverb_uniformity = 0.0; } Area3D::~Area3D() { diff --git a/scene/3d/area_3d.h b/scene/3d/area_3d.h index 07d24f39a7..847d1c5966 100644 --- a/scene/3d/area_3d.h +++ b/scene/3d/area_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,7 +31,7 @@ #ifndef AREA_3D_H #define AREA_3D_H -#include "core/vset.h" +#include "core/templates/vset.h" #include "scene/3d/collision_object_3d.h" class Area3D : public CollisionObject3D { @@ -47,19 +47,20 @@ public: }; private: - SpaceOverride space_override; + SpaceOverride space_override = SPACE_OVERRIDE_DISABLED; Vector3 gravity_vec; real_t gravity; - bool gravity_is_point; - real_t gravity_distance_scale; - real_t angular_damp; - real_t linear_damp; - uint32_t collision_mask; - uint32_t collision_layer; - int priority; - bool monitoring; - bool monitorable; - bool locked; + bool gravity_is_point = false; + real_t gravity_distance_scale = 0.0; + real_t angular_damp = 0.1; + real_t linear_damp = 0.1; + int priority = 0; + real_t wind_force_magnitude = 0.0; + real_t wind_attenuation_factor = 0.0; + NodePath wind_source_path; + bool monitoring = false; + bool monitorable = false; + bool locked = false; void _body_inout(int p_status, const RID &p_body, ObjectID p_instance, int p_body_shape, int p_area_shape); @@ -67,8 +68,8 @@ private: void _body_exit_tree(ObjectID p_id); struct ShapePair { - int body_shape; - int area_shape; + int body_shape = 0; + int area_shape = 0; bool operator<(const ShapePair &p_sp) const { if (body_shape == p_sp.body_shape) { return area_shape < p_sp.area_shape; @@ -85,8 +86,9 @@ private: }; struct BodyState { - int rc; - bool in_tree; + RID rid; + int rc = 0; + bool in_tree = false; VSet<ShapePair> shapes; }; @@ -98,8 +100,8 @@ private: void _area_exit_tree(ObjectID p_id); struct AreaShapePair { - int area_shape; - int self_shape; + int area_shape = 0; + int self_shape = 0; bool operator<(const AreaShapePair &p_sp) const { if (area_shape == p_sp.area_shape) { return self_shape < p_sp.self_shape; @@ -116,24 +118,27 @@ private: }; struct AreaState { - int rc; - bool in_tree; + RID rid; + int rc = 0; + bool in_tree = false; VSet<AreaShapePair> shapes; }; Map<ObjectID, AreaState> area_map; void _clear_monitoring(); - bool audio_bus_override; - StringName audio_bus; + bool audio_bus_override = false; + StringName audio_bus = "Master"; - bool use_reverb_bus; - StringName reverb_bus; - float reverb_amount; - float reverb_uniformity; + bool use_reverb_bus = false; + StringName reverb_bus = "Master"; + float reverb_amount = 0.0; + float reverb_uniformity = 0.0; void _validate_property(PropertyInfo &property) const override; + void _initialize_wind(); + protected: void _notification(int p_what); static void _bind_methods(); @@ -163,24 +168,21 @@ public: void set_priority(real_t p_priority); real_t get_priority() const; + void set_wind_force_magnitude(real_t p_wind_force_magnitude); + real_t get_wind_force_magnitude() const; + + void set_wind_attenuation_factor(real_t p_wind_attenuation_factor); + real_t get_wind_attenuation_factor() const; + + void set_wind_source_path(const NodePath &p_wind_source_path); + const NodePath &get_wind_source_path() const; + void set_monitoring(bool p_enable); bool is_monitoring() const; void set_monitorable(bool p_enable); bool is_monitorable() const; - void set_collision_mask(uint32_t p_mask); - uint32_t get_collision_mask() const; - - void set_collision_layer(uint32_t p_layer); - uint32_t get_collision_layer() const; - - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; - - void set_collision_layer_bit(int p_bit, bool p_value); - bool get_collision_layer_bit(int p_bit) const; - TypedArray<Node3D> get_overlapping_bodies() const; TypedArray<Area3D> get_overlapping_areas() const; //function for script @@ -190,8 +192,8 @@ public: void set_audio_bus_override(bool p_override); bool is_overriding_audio_bus() const; - void set_audio_bus(const StringName &p_audio_bus); - StringName get_audio_bus() const; + void set_audio_bus_name(const StringName &p_audio_bus); + StringName get_audio_bus_name() const; void set_use_reverb_bus(bool p_enable); bool is_using_reverb_bus() const; diff --git a/scene/3d/audio_stream_player_3d.cpp b/scene/3d/audio_stream_player_3d.cpp index 6e4db8f382..16d5b9da3e 100644 --- a/scene/3d/audio_stream_player_3d.cpp +++ b/scene/3d/audio_stream_player_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,11 +30,10 @@ #include "audio_stream_player_3d.h" -#include "core/engine.h" #include "scene/3d/area_3d.h" #include "scene/3d/camera_3d.h" #include "scene/3d/listener_3d.h" -#include "scene/main/window.h" +#include "scene/main/viewport.h" // Based on "A Novel Multichannel Panning Method for Standard and Arbitrary Loudspeaker Configurations" by Ramy Sadek and Chris Kyriakakis (2004) // Speaker-Placement Correction Amplitude Panning (SPCAP) @@ -42,8 +41,8 @@ class Spcap { private: struct Speaker { Vector3 direction; - real_t effective_number_of_speakers; // precalculated - mutable real_t squared_gain; // temporary + real_t effective_number_of_speakers = 0; // precalculated + mutable real_t squared_gain = 0; // temporary }; Vector<Speaker> speakers; @@ -96,7 +95,7 @@ static const Vector3 speaker_directions[7] = { Vector3(1.0, 0.0, 0.0).normalized(), // side-right }; -void AudioStreamPlayer3D::_calc_output_vol(const Vector3 &source_dir, real_t tightness, AudioStreamPlayer3D::Output &output) { +void AudioStreamPlayer3D::_calc_output_vol(const Vector3 &source_dir, real_t tightness, Vector<AudioFrame> &output) { unsigned int speaker_count = 0; // only main speakers (no LFE) switch (AudioServer::get_singleton()->get_speaker_mode()) { case AudioServer::SPEAKER_MODE_STEREO: @@ -119,182 +118,94 @@ void AudioStreamPlayer3D::_calc_output_vol(const Vector3 &source_dir, real_t tig switch (AudioServer::get_singleton()->get_speaker_mode()) { case AudioServer::SPEAKER_SURROUND_71: - output.vol[3].l = volumes[5]; // side-left - output.vol[3].r = volumes[6]; // side-right + output.write[3].l = volumes[5]; // side-left + output.write[3].r = volumes[6]; // side-right [[fallthrough]]; case AudioServer::SPEAKER_SURROUND_51: - output.vol[2].l = volumes[3]; // rear-left - output.vol[2].r = volumes[4]; // rear-right + output.write[2].l = volumes[3]; // rear-left + output.write[2].r = volumes[4]; // rear-right [[fallthrough]]; case AudioServer::SPEAKER_SURROUND_31: - output.vol[1].r = 1.0; // LFE - always full power - output.vol[1].l = volumes[2]; // center + output.write[1].r = 1.0; // LFE - always full power + output.write[1].l = volumes[2]; // center [[fallthrough]]; case AudioServer::SPEAKER_MODE_STEREO: - output.vol[0].r = volumes[1]; // front-right - output.vol[0].l = volumes[0]; // front-left + output.write[0].r = volumes[1]; // front-right + output.write[0].l = volumes[0]; // front-left break; } } -void AudioStreamPlayer3D::_mix_audio() { - if (!stream_playback.is_valid() || !active || - (stream_paused && !stream_paused_fade_out)) { - return; - } +void AudioStreamPlayer3D::_calc_reverb_vol(Area3D *area, Vector3 listener_area_pos, Vector<AudioFrame> direct_path_vol, Vector<AudioFrame> &reverb_vol) { + reverb_vol.resize(4); + reverb_vol.write[0] = AudioFrame(0, 0); + reverb_vol.write[1] = AudioFrame(0, 0); + reverb_vol.write[2] = AudioFrame(0, 0); + reverb_vol.write[3] = AudioFrame(0, 0); - bool started = false; - if (setseek >= 0.0) { - stream_playback->start(setseek); - setseek = -1.0; //reset seek - started = true; - } + float uniformity = area->get_reverb_uniformity(); + float area_send = area->get_reverb_amount(); - //get data - AudioFrame *buffer = mix_buffer.ptrw(); - int buffer_size = mix_buffer.size(); + if (uniformity > 0.0) { + float distance = listener_area_pos.length(); + float attenuation = Math::db2linear(_get_attenuation_db(distance)); - if (stream_paused_fade_out) { - // Short fadeout ramp - buffer_size = MIN(buffer_size, 128); - } - - // Mix if we're not paused or we're fading out - if ((output_count > 0 || out_of_range_mode == OUT_OF_RANGE_MIX)) { - float output_pitch_scale = 0.0; - if (output_count) { - //used for doppler, not realistic but good enough - for (int i = 0; i < output_count; i++) { - output_pitch_scale += outputs[i].pitch_scale; - } - output_pitch_scale /= float(output_count); - } else { - output_pitch_scale = 1.0; - } + // Determine the fraction of sound that would come from each speaker if they were all driven uniformly. + float center_val[3] = { 0.5f, 0.25f, 0.16666f }; + int channel_count = AudioServer::get_singleton()->get_channel_count(); + AudioFrame center_frame(center_val[channel_count - 1], center_val[channel_count - 1]); - stream_playback->mix(buffer, pitch_scale * output_pitch_scale, buffer_size); - } + if (attenuation < 1.0) { + //pan the uniform sound + Vector3 rev_pos = listener_area_pos; + rev_pos.y = 0; + rev_pos.normalize(); - //write all outputs - for (int i = 0; i < output_count; i++) { - Output current = outputs[i]; - - //see if current output exists, to keep volume ramp - bool found = false; - for (int j = i; j < prev_output_count; j++) { - if (prev_outputs[j].viewport == current.viewport) { - if (j != i) { - SWAP(prev_outputs[j], prev_outputs[i]); - } - found = true; - break; + if (channel_count >= 1) { + // Stereo pair + float c = rev_pos.x * 0.5 + 0.5; + reverb_vol.write[0].l = 1.0 - c; + reverb_vol.write[0].r = c; } - } - bool interpolate_filter = !started; + if (channel_count >= 3) { + // Center pair + Side pair + float xl = Vector3(-1, 0, -1).normalized().dot(rev_pos) * 0.5 + 0.5; + float xr = Vector3(1, 0, -1).normalized().dot(rev_pos) * 0.5 + 0.5; - if (!found) { - //create new if was not used before - if (prev_output_count < MAX_OUTPUTS) { - prev_outputs[prev_output_count] = prev_outputs[i]; //may be owned by another viewport - prev_output_count++; + reverb_vol.write[1].l = xl; + reverb_vol.write[1].r = xr; + reverb_vol.write[2].l = 1.0 - xr; + reverb_vol.write[2].r = 1.0 - xl; } - prev_outputs[i] = current; - interpolate_filter = false; - } - - //mix! - - int buffers = AudioServer::get_singleton()->get_channel_count(); - - for (int k = 0; k < buffers; k++) { - AudioFrame target_volume = stream_paused_fade_out ? AudioFrame(0.f, 0.f) : current.vol[k]; - AudioFrame vol_prev = stream_paused_fade_in ? AudioFrame(0.f, 0.f) : prev_outputs[i].vol[k]; - AudioFrame vol_inc = (target_volume - vol_prev) / float(buffer_size); - AudioFrame vol = vol_prev; - if (!AudioServer::get_singleton()->thread_has_channel_mix_buffer(current.bus_index, k)) { - continue; //may have been deleted, will be updated on process + if (channel_count >= 4) { + // Rear pair + // FIXME: Not sure what math should be done here + float c = rev_pos.x * 0.5 + 0.5; + reverb_vol.write[3].l = 1.0 - c; + reverb_vol.write[3].r = c; } - AudioFrame *target = AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.bus_index, k); - current.filter.set_mode(AudioFilterSW::HIGHSHELF); - current.filter.set_sampling_rate(AudioServer::get_singleton()->get_mix_rate()); - current.filter.set_cutoff(attenuation_filter_cutoff_hz); - current.filter.set_resonance(1); - current.filter.set_stages(1); - current.filter.set_gain(current.filter_gain); - - if (interpolate_filter) { - current.filter_process[k * 2 + 0] = prev_outputs[i].filter_process[k * 2 + 0]; - current.filter_process[k * 2 + 1] = prev_outputs[i].filter_process[k * 2 + 1]; - - current.filter_process[k * 2 + 0].set_filter(¤t.filter, false); - current.filter_process[k * 2 + 1].set_filter(¤t.filter, false); - - current.filter_process[k * 2 + 0].update_coeffs(buffer_size); - current.filter_process[k * 2 + 1].update_coeffs(buffer_size); - for (int j = 0; j < buffer_size; j++) { - AudioFrame f = buffer[j] * vol; - current.filter_process[k * 2 + 0].process_one_interp(f.l); - current.filter_process[k * 2 + 1].process_one_interp(f.r); - - target[j] += f; - vol += vol_inc; - } - } else { - current.filter_process[k * 2 + 0].set_filter(¤t.filter); - current.filter_process[k * 2 + 1].set_filter(¤t.filter); - - current.filter_process[k * 2 + 0].update_coeffs(); - current.filter_process[k * 2 + 1].update_coeffs(); - for (int j = 0; j < buffer_size; j++) { - AudioFrame f = buffer[j] * vol; - current.filter_process[k * 2 + 0].process_one(f.l); - current.filter_process[k * 2 + 1].process_one(f.r); - - target[j] += f; - vol += vol_inc; - } + for (int i = 0; i < channel_count; i++) { + reverb_vol.write[i] = reverb_vol[i].lerp(center_frame, attenuation); } - - if (current.reverb_bus_index >= 0) { - if (!AudioServer::get_singleton()->thread_has_channel_mix_buffer(current.reverb_bus_index, k)) { - continue; //may have been deleted, will be updated on process - } - - AudioFrame *rtarget = AudioServer::get_singleton()->thread_get_channel_mix_buffer(current.reverb_bus_index, k); - - if (current.reverb_bus_index == prev_outputs[i].reverb_bus_index) { - AudioFrame rvol_inc = (current.reverb_vol[k] - prev_outputs[i].reverb_vol[k]) / float(buffer_size); - AudioFrame rvol = prev_outputs[i].reverb_vol[k]; - - for (int j = 0; j < buffer_size; j++) { - rtarget[j] += buffer[j] * rvol; - rvol += rvol_inc; - } - } else { - AudioFrame rvol = current.reverb_vol[k]; - for (int j = 0; j < buffer_size; j++) { - rtarget[j] += buffer[j] * rvol; - } - } + } else { + for (int i = 0; i < channel_count; i++) { + reverb_vol.write[i] = center_frame; } } - prev_outputs[i] = current; - } - - prev_output_count = output_count; + for (int i = 0; i < channel_count; i++) { + reverb_vol.write[i] = direct_path_vol[i].lerp(reverb_vol[i] * attenuation, uniformity); + reverb_vol.write[i] *= area_send; + } - //stream is no longer active, disable this. - if (!stream_playback->is_playing()) { - active = false; + } else { + for (int i = 0; i < 4; i++) { + reverb_vol.write[i] = direct_path_vol[i] * area_send; + } } - - output_ready = false; - stream_paused_fade_in = false; - stream_paused_fade_out = false; } float AudioStreamPlayer3D::_get_attenuation_db(float p_distance) const { @@ -327,20 +238,18 @@ float AudioStreamPlayer3D::_get_attenuation_db(float p_distance) const { return att; } -void _update_sound() { -} - void AudioStreamPlayer3D::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { velocity_tracker->reset(get_global_transform().origin); - AudioServer::get_singleton()->add_callback(_mix_audios, this); + AudioServer::get_singleton()->add_listener_changed_callback(_listener_changed_cb, this); if (autoplay && !Engine::get_singleton()->is_editor_hint()) { play(); } } if (p_what == NOTIFICATION_EXIT_TREE) { - AudioServer::get_singleton()->remove_callback(_mix_audios, this); + stop(); + AudioServer::get_singleton()->remove_listener_changed_callback(_listener_changed_cb, this); } if (p_what == NOTIFICATION_PAUSED) { @@ -363,284 +272,227 @@ void AudioStreamPlayer3D::_notification(int p_what) { if (p_what == NOTIFICATION_INTERNAL_PHYSICS_PROCESS) { //update anything related to position first, if possible of course - if (!output_ready) { - Vector3 linear_velocity; + if (!stream_playback.is_valid()) { + return; + } + //start playing if requested - //compute linear velocity for doppler - if (doppler_tracking != DOPPLER_TRACKING_DISABLED) { - linear_velocity = velocity_tracker->get_tracked_linear_velocity(); - } + if (setplay.get() >= 0) { + Vector<AudioFrame> volume_vector = _update_panning(); + AudioServer::get_singleton()->start_playback_stream(stream_playback, _get_actual_bus(), volume_vector, setplay.get()); + active.set(); + setplay.set(-1); + } + + if (active.is_set() && last_mix_count != AudioServer::get_singleton()->get_mix_count()) { + _update_panning(); + last_mix_count = AudioServer::get_singleton()->get_mix_count(); + } + + // Stop playing if no longer active. + if (!active.is_set()) { + set_physics_process_internal(false); + emit_signal(SNAME("finished")); + } + } +} + +Area3D *AudioStreamPlayer3D::_get_overriding_area() { + //check if any area is diverting sound into a bus + Ref<World3D> world_3d = get_world_3d(); + ERR_FAIL_COND_V(world_3d.is_null(), nullptr); + + Vector3 global_pos = get_global_transform().origin; + + PhysicsDirectSpaceState3D *space_state = PhysicsServer3D::get_singleton()->space_get_direct_state(world_3d->get_space()); - Ref<World3D> world_3d = get_world_3d(); - ERR_FAIL_COND(world_3d.is_null()); + PhysicsDirectSpaceState3D::ShapeResult sr[MAX_INTERSECT_AREAS]; - int new_output_count = 0; + int areas = space_state->intersect_point(global_pos, sr, MAX_INTERSECT_AREAS, Set<RID>(), area_mask, false, true); - Vector3 global_pos = get_global_transform().origin; + for (int i = 0; i < areas; i++) { + if (!sr[i].collider) { + continue; + } - int bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus); + Area3D *tarea = Object::cast_to<Area3D>(sr[i].collider); + if (!tarea) { + continue; + } - //check if any area is diverting sound into a bus + if (!tarea->is_overriding_audio_bus() && !tarea->is_using_reverb_bus()) { + continue; + } - PhysicsDirectSpaceState3D *space_state = PhysicsServer3D::get_singleton()->space_get_direct_state(world_3d->get_space()); + return tarea; + } + return nullptr; +} - PhysicsDirectSpaceState3D::ShapeResult sr[MAX_INTERSECT_AREAS]; +StringName AudioStreamPlayer3D::_get_actual_bus() { + if (!stream_playback.is_valid()) { + return SNAME("Master"); + } + Area3D *overriding_area = _get_overriding_area(); + if (overriding_area && overriding_area->is_overriding_audio_bus() && !overriding_area->is_using_reverb_bus()) { + return overriding_area->get_audio_bus_name(); + } + return bus; +} + +Vector<AudioFrame> AudioStreamPlayer3D::_update_panning() { + Vector<AudioFrame> output_volume_vector; + output_volume_vector.resize(4); + for (AudioFrame &frame : output_volume_vector) { + frame = AudioFrame(0, 0); + } - int areas = space_state->intersect_point(global_pos, sr, MAX_INTERSECT_AREAS, Set<RID>(), area_mask, false, true); - Area3D *area = nullptr; + ERR_FAIL_COND_V(stream_playback.is_null(), output_volume_vector); - for (int i = 0; i < areas; i++) { - if (!sr[i].collider) { - continue; - } + Vector3 linear_velocity; + + //compute linear velocity for doppler + if (doppler_tracking != DOPPLER_TRACKING_DISABLED) { + linear_velocity = velocity_tracker->get_tracked_linear_velocity(); + } - Area3D *tarea = Object::cast_to<Area3D>(sr[i].collider); - if (!tarea) { - continue; - } + Vector3 global_pos = get_global_transform().origin; - if (!tarea->is_overriding_audio_bus() && !tarea->is_using_reverb_bus()) { - continue; - } + Ref<World3D> world_3d = get_world_3d(); + ERR_FAIL_COND_V(world_3d.is_null(), output_volume_vector); - area = tarea; - break; + Set<Camera3D *> cameras = world_3d->get_cameras(); + cameras.insert(get_viewport()->get_camera_3d()); + + PhysicsDirectSpaceState3D *space_state = PhysicsServer3D::get_singleton()->space_get_direct_state(world_3d->get_space()); + + for (Camera3D *camera : cameras) { + Viewport *vp = camera->get_viewport(); + if (!vp->is_audio_listener_3d()) { + continue; + } + + bool listener_is_camera = true; + Node3D *listener_node = camera; + + Listener3D *listener = vp->get_listener_3d(); + if (listener) { + listener_node = listener; + listener_is_camera = false; + } + + Vector3 local_pos = listener_node->get_global_transform().orthonormalized().affine_inverse().xform(global_pos); + + float dist = local_pos.length(); + + Vector3 area_sound_pos; + Vector3 listener_area_pos; + + Area3D *area = _get_overriding_area(); + + if (area && area->is_using_reverb_bus() && area->get_reverb_uniformity() > 0) { + area_sound_pos = space_state->get_closest_point_to_object_volume(area->get_rid(), listener_node->get_global_transform().origin); + listener_area_pos = listener_node->get_global_transform().affine_inverse().xform(area_sound_pos); + } + + if (max_distance > 0) { + float total_max = max_distance; + + if (area && area->is_using_reverb_bus() && area->get_reverb_uniformity() > 0) { + total_max = MAX(total_max, listener_area_pos.length()); + } + if (total_max > max_distance) { + continue; //can't hear this sound in this listener } + } - List<Camera3D *> cameras; - world_3d->get_camera_list(&cameras); - - for (List<Camera3D *>::Element *E = cameras.front(); E; E = E->next()) { - Camera3D *camera = E->get(); - Viewport *vp = camera->get_viewport(); - if (!vp->is_audio_listener()) { - continue; - } - - bool listener_is_camera = true; - Node3D *listener_node = camera; - - Listener3D *listener = vp->get_listener(); - if (listener) { - listener_node = listener; - listener_is_camera = false; - } - - Vector3 local_pos = listener_node->get_global_transform().orthonormalized().affine_inverse().xform(global_pos); - - float dist = local_pos.length(); - - Vector3 area_sound_pos; - Vector3 listener_area_pos; - - if (area && area->is_using_reverb_bus() && area->get_reverb_uniformity() > 0) { - area_sound_pos = space_state->get_closest_point_to_object_volume(area->get_rid(), listener_node->get_global_transform().origin); - listener_area_pos = listener_node->get_global_transform().affine_inverse().xform(area_sound_pos); - } - - if (max_distance > 0) { - float total_max = max_distance; - - if (area && area->is_using_reverb_bus() && area->get_reverb_uniformity() > 0) { - total_max = MAX(total_max, listener_area_pos.length()); - } - if (total_max > max_distance) { - continue; //can't hear this sound in this listener - } - } - - float multiplier = Math::db2linear(_get_attenuation_db(dist)); - if (max_distance > 0) { - multiplier *= MAX(0, 1.0 - (dist / max_distance)); - } - - Output output; - output.bus_index = bus_index; - output.reverb_bus_index = -1; //no reverb by default - output.viewport = vp; - - float db_att = (1.0 - MIN(1.0, multiplier)) * attenuation_filter_db; - - if (emission_angle_enabled) { - Vector3 listenertopos = global_pos - listener_node->get_global_transform().origin; - float c = listenertopos.normalized().dot(get_global_transform().basis.get_axis(2).normalized()); //it's z negative - float angle = Math::rad2deg(Math::acos(c)); - if (angle > emission_angle) { - db_att -= -emission_angle_filter_attenuation_db; - } - } - - output.filter_gain = Math::db2linear(db_att); - - //TODO: The lower the second parameter (tightness) the more the sound will "enclose" the listener (more undirected / playing from - // speakers not facing the source) - this could be made distance dependent. - _calc_output_vol(local_pos.normalized(), 4.0, output); - - unsigned int cc = AudioServer::get_singleton()->get_channel_count(); - for (unsigned int k = 0; k < cc; k++) { - output.vol[k] *= multiplier; - } - - bool filled_reverb = false; - int vol_index_max = AudioServer::get_singleton()->get_speaker_mode() + 1; - - if (area) { - if (area->is_overriding_audio_bus()) { - //override audio bus - StringName bus_name = area->get_audio_bus(); - output.bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus_name); - } - - if (area->is_using_reverb_bus()) { - filled_reverb = true; - StringName bus_name = area->get_reverb_bus(); - output.reverb_bus_index = AudioServer::get_singleton()->thread_find_bus_index(bus_name); - - float uniformity = area->get_reverb_uniformity(); - float area_send = area->get_reverb_amount(); - - if (uniformity > 0.0) { - float distance = listener_area_pos.length(); - float attenuation = Math::db2linear(_get_attenuation_db(distance)); - - //float dist_att_db = -20 * Math::log(dist + 0.00001); //logarithmic attenuation, like in real life - - float center_val[3] = { 0.5f, 0.25f, 0.16666f }; - AudioFrame center_frame(center_val[vol_index_max - 1], center_val[vol_index_max - 1]); - - if (attenuation < 1.0) { - //pan the uniform sound - Vector3 rev_pos = listener_area_pos; - rev_pos.y = 0; - rev_pos.normalize(); - - if (cc >= 1) { - // Stereo pair - float c = rev_pos.x * 0.5 + 0.5; - output.reverb_vol[0].l = 1.0 - c; - output.reverb_vol[0].r = c; - } - - if (cc >= 3) { - // Center pair + Side pair - float xl = Vector3(-1, 0, -1).normalized().dot(rev_pos) * 0.5 + 0.5; - float xr = Vector3(1, 0, -1).normalized().dot(rev_pos) * 0.5 + 0.5; - - output.reverb_vol[1].l = xl; - output.reverb_vol[1].r = xr; - output.reverb_vol[2].l = 1.0 - xr; - output.reverb_vol[2].r = 1.0 - xl; - } - - if (cc >= 4) { - // Rear pair - // FIXME: Not sure what math should be done here - float c = rev_pos.x * 0.5 + 0.5; - output.reverb_vol[3].l = 1.0 - c; - output.reverb_vol[3].r = c; - } - - for (int i = 0; i < vol_index_max; i++) { - output.reverb_vol[i] = output.reverb_vol[i].lerp(center_frame, attenuation); - } - } else { - for (int i = 0; i < vol_index_max; i++) { - output.reverb_vol[i] = center_frame; - } - } - - for (int i = 0; i < vol_index_max; i++) { - output.reverb_vol[i] = output.vol[i].lerp(output.reverb_vol[i] * attenuation, uniformity); - output.reverb_vol[i] *= area_send; - } - - } else { - for (int i = 0; i < vol_index_max; i++) { - output.reverb_vol[i] = output.vol[i] * area_send; - } - } - } - } - - if (doppler_tracking != DOPPLER_TRACKING_DISABLED) { - Vector3 listener_velocity; - - if (listener_is_camera) { - listener_velocity = camera->get_doppler_tracked_velocity(); - } - - Vector3 local_velocity = listener_node->get_global_transform().orthonormalized().basis.xform_inv(linear_velocity - listener_velocity); - - if (local_velocity == Vector3()) { - output.pitch_scale = 1.0; - } else { - float approaching = local_pos.normalized().dot(local_velocity.normalized()); - float velocity = local_velocity.length(); - float speed_of_sound = 343.0; - - output.pitch_scale = speed_of_sound / (speed_of_sound + velocity * approaching); - output.pitch_scale = CLAMP(output.pitch_scale, (1 / 8.0), 8.0); //avoid crazy stuff - } - - } else { - output.pitch_scale = 1.0; - } - - if (!filled_reverb) { - for (int i = 0; i < vol_index_max; i++) { - output.reverb_vol[i] = AudioFrame(0, 0); - } - } - - outputs[new_output_count] = output; - new_output_count++; - if (new_output_count == MAX_OUTPUTS) { - break; - } + float multiplier = Math::db2linear(_get_attenuation_db(dist)); + if (max_distance > 0) { + multiplier *= MAX(0, 1.0 - (dist / max_distance)); + } + + float db_att = (1.0 - MIN(1.0, multiplier)) * attenuation_filter_db; + + if (emission_angle_enabled) { + Vector3 listenertopos = global_pos - listener_node->get_global_transform().origin; + float c = listenertopos.normalized().dot(get_global_transform().basis.get_axis(2).normalized()); //it's z negative + float angle = Math::rad2deg(Math::acos(c)); + if (angle > emission_angle) { + db_att -= -emission_angle_filter_attenuation_db; } + } - output_count = new_output_count; - output_ready = true; + AudioServer::get_singleton()->set_playback_highshelf_params(stream_playback, Math::db2linear(db_att), attenuation_filter_cutoff_hz); + //TODO: The lower the second parameter (tightness) the more the sound will "enclose" the listener (more undirected / playing from + // speakers not facing the source) - this could be made distance dependent. + _calc_output_vol(local_pos.normalized(), 4.0, output_volume_vector); + + for (unsigned int k = 0; k < 4; k++) { + output_volume_vector.write[k] = multiplier * output_volume_vector[k]; } - //start playing if requested - if (setplay >= 0.0) { - setseek = setplay; - active = true; - setplay = -1; - //do not update, this makes it easier to animate (will shut off otherwise) - ///_change_notify("playing"); //update property in editor + Map<StringName, Vector<AudioFrame>> bus_volumes; + if (area) { + if (area->is_overriding_audio_bus()) { + //override audio bus + bus_volumes[area->get_audio_bus_name()] = output_volume_vector; + } + + if (area->is_using_reverb_bus()) { + StringName reverb_bus_name = area->get_reverb_bus(); + Vector<AudioFrame> reverb_vol; + _calc_reverb_vol(area, listener_area_pos, output_volume_vector, reverb_vol); + bus_volumes[reverb_bus_name] = reverb_vol; + } + } else { + bus_volumes[bus] = output_volume_vector; } + AudioServer::get_singleton()->set_playback_bus_volumes_linear(stream_playback, bus_volumes); - //stop playing if no longer active - if (!active) { - set_physics_process_internal(false); - //do not update, this makes it easier to animate (will shut off otherwise) - //_change_notify("playing"); //update property in editor - emit_signal("finished"); + if (doppler_tracking != DOPPLER_TRACKING_DISABLED) { + Vector3 listener_velocity; + + if (listener_is_camera) { + listener_velocity = camera->get_doppler_tracked_velocity(); + } + + Vector3 local_velocity = listener_node->get_global_transform().orthonormalized().basis.xform_inv(linear_velocity - listener_velocity); + + if (local_velocity == Vector3()) { + AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, pitch_scale); + } else { + float approaching = local_pos.normalized().dot(local_velocity.normalized()); + float velocity = local_velocity.length(); + float speed_of_sound = 343.0; + + float doppler_pitch_scale = pitch_scale * speed_of_sound / (speed_of_sound + velocity * approaching); + doppler_pitch_scale = CLAMP(doppler_pitch_scale, (1 / 8.0), 8.0); //avoid crazy stuff + + AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, doppler_pitch_scale); + } + } else { + AudioServer::get_singleton()->set_playback_pitch_scale(stream_playback, pitch_scale); } } + return output_volume_vector; } void AudioStreamPlayer3D::set_stream(Ref<AudioStream> p_stream) { - AudioServer::get_singleton()->lock(); - - mix_buffer.resize(AudioServer::get_singleton()->thread_get_mix_buffer_size()); - if (stream_playback.is_valid()) { + stop(); stream_playback.unref(); stream.unref(); - active = false; - setseek = -1; } if (p_stream.is_valid()) { - stream = p_stream; stream_playback = p_stream->instance_playback(); + if (stream_playback.is_valid()) { + stream = p_stream; + } else { + stream.unref(); + } } - AudioServer::get_singleton()->unlock(); - if (p_stream.is_valid() && stream_playback.is_null()) { stream.unref(); } @@ -684,36 +536,30 @@ float AudioStreamPlayer3D::get_pitch_scale() const { } void AudioStreamPlayer3D::play(float p_from_pos) { - if (!is_playing()) { - // Reset the prev_output_count if the stream is stopped - prev_output_count = 0; - } - if (stream_playback.is_valid()) { - active = true; - setplay = p_from_pos; - output_ready = false; + setplay.set(p_from_pos); set_physics_process_internal(true); } } void AudioStreamPlayer3D::seek(float p_seconds) { - if (stream_playback.is_valid()) { - setseek = p_seconds; + if (stream_playback.is_valid() && active.is_set()) { + play(p_seconds); } } void AudioStreamPlayer3D::stop() { if (stream_playback.is_valid()) { - active = false; + active.clear(); + AudioServer::get_singleton()->stop_playback_stream(stream_playback); set_physics_process_internal(false); - setplay = -1; + setplay.set(-1); } } bool AudioStreamPlayer3D::is_playing() const { if (stream_playback.is_valid()) { - return active; // && stream_playback->is_playing(); + return active.is_set() || setplay.get() >= 0; } return false; @@ -721,7 +567,7 @@ bool AudioStreamPlayer3D::is_playing() const { float AudioStreamPlayer3D::get_playback_position() { if (stream_playback.is_valid()) { - return stream_playback->get_playback_position(); + return AudioServer::get_singleton()->get_playback_position(stream_playback); } return 0; @@ -740,7 +586,7 @@ StringName AudioStreamPlayer3D::get_bus() const { return bus; } } - return "Master"; + return SNAME("Master"); } void AudioStreamPlayer3D::set_autoplay(bool p_enable) { @@ -760,7 +606,7 @@ void AudioStreamPlayer3D::_set_playing(bool p_enable) { } bool AudioStreamPlayer3D::_is_active() const { - return active; + return active.is_set(); } void AudioStreamPlayer3D::_validate_property(PropertyInfo &property) const { @@ -779,7 +625,7 @@ void AudioStreamPlayer3D::_validate_property(PropertyInfo &property) const { } void AudioStreamPlayer3D::_bus_layout_changed() { - _change_notify(); + notify_property_list_changed(); } void AudioStreamPlayer3D::set_max_distance(float p_metres) { @@ -801,7 +647,7 @@ uint32_t AudioStreamPlayer3D::get_area_mask() const { void AudioStreamPlayer3D::set_emission_angle_enabled(bool p_enable) { emission_angle_enabled = p_enable; - update_gizmo(); + update_gizmos(); } bool AudioStreamPlayer3D::is_emission_angle_enabled() const { @@ -811,8 +657,7 @@ bool AudioStreamPlayer3D::is_emission_angle_enabled() const { void AudioStreamPlayer3D::set_emission_angle(float p_angle) { ERR_FAIL_COND(p_angle < 0 || p_angle > 90); emission_angle = p_angle; - update_gizmo(); - _change_notify("emission_angle"); + update_gizmos(); } float AudioStreamPlayer3D::get_emission_angle() const { @@ -884,15 +729,16 @@ AudioStreamPlayer3D::DopplerTracking AudioStreamPlayer3D::get_doppler_tracking() } void AudioStreamPlayer3D::set_stream_paused(bool p_pause) { - if (p_pause != stream_paused) { - stream_paused = p_pause; - stream_paused_fade_in = !stream_paused; - stream_paused_fade_out = stream_paused; + if (stream_playback.is_valid()) { + AudioServer::get_singleton()->set_playback_paused(stream_playback, p_pause); } } bool AudioStreamPlayer3D::get_stream_paused() const { - return stream_paused; + if (stream_playback.is_valid()) { + return AudioServer::get_singleton()->is_playback_paused(stream_playback); + } + return false; } Ref<AudioStreamPlayback> AudioStreamPlayer3D::get_stream_playback() { @@ -967,21 +813,21 @@ void AudioStreamPlayer3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_stream_playback"), &AudioStreamPlayer3D::get_stream_playback); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "stream", PROPERTY_HINT_RESOURCE_TYPE, "AudioStream"), "set_stream", "get_stream"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "attenuation_model", PROPERTY_HINT_ENUM, "Inverse,InverseSquare,Log,Disabled"), "set_attenuation_model", "get_attenuation_model"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "attenuation_model", PROPERTY_HINT_ENUM, "Inverse,Inverse Square,Log,Disabled"), "set_attenuation_model", "get_attenuation_model"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "unit_db", PROPERTY_HINT_RANGE, "-80,80"), "set_unit_db", "get_unit_db"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "unit_size", PROPERTY_HINT_RANGE, "0.1,100,0.1"), "set_unit_size", "get_unit_size"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "unit_size", PROPERTY_HINT_RANGE, "0.1,100,0.01,or_greater"), "set_unit_size", "get_unit_size"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_db", PROPERTY_HINT_RANGE, "-24,6"), "set_max_db", "get_max_db"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pitch_scale", PROPERTY_HINT_RANGE, "0.01,4,0.01,or_greater"), "set_pitch_scale", "get_pitch_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "_set_playing", "is_playing"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "autoplay"), "set_autoplay", "is_autoplay_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "stream_paused", PROPERTY_HINT_NONE, ""), "set_stream_paused", "get_stream_paused"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_EXP_RANGE, "0,4096,1,or_greater"), "set_max_distance", "get_max_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "0,4096,1,or_greater,exp"), "set_max_distance", "get_max_distance"); ADD_PROPERTY(PropertyInfo(Variant::INT, "out_of_range_mode", PROPERTY_HINT_ENUM, "Mix,Pause"), "set_out_of_range_mode", "get_out_of_range_mode"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bus", PROPERTY_HINT_ENUM, ""), "set_bus", "get_bus"); ADD_PROPERTY(PropertyInfo(Variant::INT, "area_mask", PROPERTY_HINT_LAYERS_2D_PHYSICS), "set_area_mask", "get_area_mask"); ADD_GROUP("Emission Angle", "emission_angle"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emission_angle_enabled"), "set_emission_angle_enabled", "is_emission_angle_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_angle_degrees", PROPERTY_HINT_RANGE, "0.1,90,0.1"), "set_emission_angle", "get_emission_angle"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_angle_degrees", PROPERTY_HINT_RANGE, "0.1,90,0.1,degrees"), "set_emission_angle", "get_emission_angle"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_angle_filter_attenuation_db", PROPERTY_HINT_RANGE, "-80,0,0.1"), "set_emission_angle_filter_attenuation_db", "get_emission_angle_filter_attenuation_db"); ADD_GROUP("Attenuation Filter", "attenuation_filter_"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "attenuation_filter_cutoff_hz", PROPERTY_HINT_RANGE, "1,20500,1"), "set_attenuation_filter_cutoff_hz", "get_attenuation_filter_cutoff_hz"); @@ -1005,32 +851,7 @@ void AudioStreamPlayer3D::_bind_methods() { } AudioStreamPlayer3D::AudioStreamPlayer3D() { - unit_db = 0; - unit_size = 1; - attenuation_model = ATTENUATION_INVERSE_DISTANCE; - max_db = 3; - pitch_scale = 1.0; - autoplay = false; - setseek = -1; - active = false; - output_count = 0; - prev_output_count = 0; - max_distance = 0; - setplay = -1; - output_ready = false; - area_mask = 1; - emission_angle = 45; - emission_angle_enabled = false; - emission_angle_filter_attenuation_db = -12; - attenuation_filter_cutoff_hz = 5000; - attenuation_filter_db = -24; - out_of_range_mode = OUT_OF_RANGE_MIX; - doppler_tracking = DOPPLER_TRACKING_DISABLED; - stream_paused = false; - stream_paused_fade_in = false; - stream_paused_fade_out = false; - - velocity_tracker.instance(); + velocity_tracker.instantiate(); AudioServer::get_singleton()->connect("bus_layout_changed", callable_mp(this, &AudioStreamPlayer3D::_bus_layout_changed)); set_disable_scale(true); } diff --git a/scene/3d/audio_stream_player_3d.h b/scene/3d/audio_stream_player_3d.h index 339475b469..f915abad2b 100644 --- a/scene/3d/audio_stream_player_3d.h +++ b/scene/3d/audio_stream_player_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,6 +31,7 @@ #ifndef AUDIO_STREAM_PLAYER_3D_H #define AUDIO_STREAM_PLAYER_3D_H +#include "scene/3d/area_3d.h" #include "scene/3d/node_3d.h" #include "scene/3d/velocity_tracker_3d.h" #include "servers/audio/audio_filter_sw.h" @@ -67,76 +68,51 @@ private: }; - struct Output { - AudioFilterSW filter; - AudioFilterSW::Processor filter_process[8]; - AudioFrame vol[4]; - float filter_gain; - float pitch_scale; - int bus_index; - int reverb_bus_index; - AudioFrame reverb_vol[4]; - Viewport *viewport; //pointer only used for reference to previous mix - - Output() { - filter_gain = 0; - viewport = nullptr; - reverb_bus_index = -1; - bus_index = -1; - } - }; + Ref<AudioStreamPlayback> stream_playback; + Ref<AudioStream> stream; - Output outputs[MAX_OUTPUTS]; - volatile int output_count; - volatile bool output_ready; + SafeFlag active; + SafeNumeric<float> setplay{ -1.0 }; - //these are used by audio thread to have a reference of previous volumes (for ramping volume and avoiding clicks) - Output prev_outputs[MAX_OUTPUTS]; - int prev_output_count; + AttenuationModel attenuation_model = ATTENUATION_INVERSE_DISTANCE; + float unit_db = 0.0; + float unit_size = 10.0; + float max_db = 3.0; + float pitch_scale = 1.0; + bool autoplay = false; + StringName bus = "Master"; - Ref<AudioStreamPlayback> stream_playback; - Ref<AudioStream> stream; - Vector<AudioFrame> mix_buffer; - - volatile float setseek; - volatile bool active; - volatile float setplay; - - AttenuationModel attenuation_model; - float unit_db; - float unit_size; - float max_db; - float pitch_scale; - bool autoplay; - bool stream_paused; - bool stream_paused_fade_in; - bool stream_paused_fade_out; - StringName bus; - - static void _calc_output_vol(const Vector3 &source_dir, real_t tightness, Output &output); - void _mix_audio(); - static void _mix_audios(void *self) { reinterpret_cast<AudioStreamPlayer3D *>(self)->_mix_audio(); } + uint64_t last_mix_count = -1; + + static void _calc_output_vol(const Vector3 &source_dir, real_t tightness, Vector<AudioFrame> &output); + + void _calc_reverb_vol(Area3D *area, Vector3 listener_area_pos, Vector<AudioFrame> direct_path_vol, Vector<AudioFrame> &reverb_vol); + + static void _listener_changed_cb(void *self) { reinterpret_cast<AudioStreamPlayer3D *>(self)->_update_panning(); } void _set_playing(bool p_enable); bool _is_active() const; + StringName _get_actual_bus(); + Area3D *_get_overriding_area(); + Vector<AudioFrame> _update_panning(); void _bus_layout_changed(); - uint32_t area_mask; + uint32_t area_mask = 1; - bool emission_angle_enabled; - float emission_angle; - float emission_angle_filter_attenuation_db; - float attenuation_filter_cutoff_hz; - float attenuation_filter_db; + bool emission_angle_enabled = false; + float emission_angle = 45.0; + float emission_angle_filter_attenuation_db = -12.0; + float attenuation_filter_cutoff_hz = 5000.0; + float attenuation_filter_db = -24.0; - float max_distance; + float max_distance = 0.0; Ref<VelocityTracker3D> velocity_tracker; - DopplerTracking doppler_tracking; + DopplerTracking doppler_tracking = DOPPLER_TRACKING_DISABLED; - OutOfRangeMode out_of_range_mode; + OutOfRangeMode out_of_range_mode = OUT_OF_RANGE_MIX; float _get_attenuation_db(float p_distance) const; diff --git a/scene/3d/bone_attachment_3d.cpp b/scene/3d/bone_attachment_3d.cpp index 68303bbfe5..70361f4787 100644 --- a/scene/3d/bone_attachment_3d.cpp +++ b/scene/3d/bone_attachment_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,7 +32,15 @@ void BoneAttachment3D::_validate_property(PropertyInfo &property) const { if (property.name == "bone_name") { - Skeleton3D *parent = Object::cast_to<Skeleton3D>(get_parent()); + // Because it is a constant function, we cannot use the _get_skeleton_3d function. + const Skeleton3D *parent = nullptr; + if (use_external_skeleton) { + if (external_skeleton_node_cache.is_valid()) { + parent = Object::cast_to<Skeleton3D>(ObjectDB::get_instance(external_skeleton_node_cache)); + } + } else { + parent = Object::cast_to<Skeleton3D>(get_parent()); + } if (parent) { String names; @@ -52,65 +60,346 @@ void BoneAttachment3D::_validate_property(PropertyInfo &property) const { } } +bool BoneAttachment3D::_set(const StringName &p_path, const Variant &p_value) { + if (p_path == SNAME("override_pose")) { + set_override_pose(p_value); + } else if (p_path == SNAME("override_mode")) { + set_override_mode(p_value); + } else if (p_path == SNAME("use_external_skeleton")) { + set_use_external_skeleton(p_value); + } else if (p_path == SNAME("external_skeleton")) { + set_external_skeleton(p_value); + } + + return true; +} + +bool BoneAttachment3D::_get(const StringName &p_path, Variant &r_ret) const { + if (p_path == SNAME("override_pose")) { + r_ret = get_override_pose(); + } else if (p_path == SNAME("override_mode")) { + r_ret = get_override_mode(); + } else if (p_path == SNAME("use_external_skeleton")) { + r_ret = get_use_external_skeleton(); + } else if (p_path == SNAME("external_skeleton")) { + r_ret = get_external_skeleton(); + } + + return true; +} + +void BoneAttachment3D::_get_property_list(List<PropertyInfo> *p_list) const { + p_list->push_back(PropertyInfo(Variant::BOOL, "override_pose", PROPERTY_HINT_NONE, "")); + if (override_pose) { + p_list->push_back(PropertyInfo(Variant::INT, "override_mode", PROPERTY_HINT_ENUM, "Global Pose Override, Local Pose Override, Custom Pose")); + } + + p_list->push_back(PropertyInfo(Variant::BOOL, "use_external_skeleton", PROPERTY_HINT_NONE, "")); + if (use_external_skeleton) { + p_list->push_back(PropertyInfo(Variant::NODE_PATH, "external_skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton3D")); + } +} + +TypedArray<String> BoneAttachment3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node3D::get_configuration_warnings(); + + if (use_external_skeleton) { + if (external_skeleton_node_cache.is_null()) { + warnings.append(TTR("External Skeleton3D node not set! Please set a path to an external Skeleton3D node.")); + } + } else { + Skeleton3D *parent = Object::cast_to<Skeleton3D>(get_parent()); + if (!parent) { + warnings.append(TTR("Parent node is not a Skeleton3D node! Please use an extenral Skeleton3D if you intend to use the BoneAttachment3D without it being a child of a Skeleton3D node.")); + } + } + + if (bone_idx == -1) { + warnings.append(TTR("BoneAttachment3D node is not bound to any bones! Please select a bone to attach this node.")); + } + + return warnings; +} + +void BoneAttachment3D::_update_external_skeleton_cache() { + external_skeleton_node_cache = ObjectID(); + if (has_node(external_skeleton_node)) { + Node *node = get_node(external_skeleton_node); + ERR_FAIL_COND_MSG(!node, "Cannot update external skeleton cache: Node cannot be found!"); + + // Make sure it's a skeleton3D + Skeleton3D *sk = Object::cast_to<Skeleton3D>(node); + ERR_FAIL_COND_MSG(!sk, "Cannot update external skeleton cache: Skeleton3D Nodepath does not point to a Skeleton3D node!"); + + external_skeleton_node_cache = node->get_instance_id(); + } else { + if (external_skeleton_node.is_empty()) { + BoneAttachment3D *parent_attachment = Object::cast_to<BoneAttachment3D>(get_parent()); + if (parent_attachment) { + parent_attachment->_update_external_skeleton_cache(); + if (parent_attachment->has_node(parent_attachment->external_skeleton_node)) { + Node *node = parent_attachment->get_node(parent_attachment->external_skeleton_node); + ERR_FAIL_COND_MSG(!node, "Cannot update external skeleton cache: Parent's Skeleton3D node cannot be found!"); + + // Make sure it's a skeleton3D + Skeleton3D *sk = Object::cast_to<Skeleton3D>(node); + ERR_FAIL_COND_MSG(!sk, "Cannot update external skeleton cache: Parent Skeleton3D Nodepath does not point to a Skeleton3D node!"); + + external_skeleton_node_cache = node->get_instance_id(); + external_skeleton_node = get_path_to(node); + } + } + } + } +} + void BoneAttachment3D::_check_bind() { - Skeleton3D *sk = Object::cast_to<Skeleton3D>(get_parent()); - if (sk) { - int idx = sk->find_bone(bone_name); - if (idx != -1) { - sk->bind_child_node_to_bone(idx, this); - set_transform(sk->get_bone_global_pose(idx)); + Skeleton3D *sk = _get_skeleton3d(); + + if (sk && !bound) { + if (bone_idx <= -1) { + bone_idx = sk->find_bone(bone_name); + } + if (bone_idx != -1) { + sk->call_deferred("connect", "bone_pose_changed", callable_mp(this, &BoneAttachment3D::on_bone_pose_update)); bound = true; + call_deferred(SNAME("on_bone_pose_update"), bone_idx); + } + } +} + +Skeleton3D *BoneAttachment3D::_get_skeleton3d() { + if (use_external_skeleton) { + if (external_skeleton_node_cache.is_valid()) { + return Object::cast_to<Skeleton3D>(ObjectDB::get_instance(external_skeleton_node_cache)); + } else { + _update_external_skeleton_cache(); + if (external_skeleton_node_cache.is_valid()) { + return Object::cast_to<Skeleton3D>(ObjectDB::get_instance(external_skeleton_node_cache)); + } } + } else { + return Object::cast_to<Skeleton3D>(get_parent()); } + return nullptr; } void BoneAttachment3D::_check_unbind() { if (bound) { - Skeleton3D *sk = Object::cast_to<Skeleton3D>(get_parent()); + Skeleton3D *sk = _get_skeleton3d(); + if (sk) { - int idx = sk->find_bone(bone_name); - if (idx != -1) { - sk->unbind_child_node_from_bone(idx, this); - } + sk->disconnect(SNAME("bone_pose_changed"), callable_mp(this, &BoneAttachment3D::on_bone_pose_update)); } bound = false; } } +void BoneAttachment3D::_transform_changed() { + if (!is_inside_tree()) { + return; + } + + if (override_pose) { + Skeleton3D *sk = _get_skeleton3d(); + + ERR_FAIL_COND_MSG(!sk, "Cannot override pose: Skeleton not found!"); + ERR_FAIL_INDEX_MSG(bone_idx, sk->get_bone_count(), "Cannot override pose: Bone index is out of range!"); + + Transform3D our_trans = get_transform(); + if (use_external_skeleton) { + our_trans = sk->world_transform_to_global_pose(get_global_transform()); + } + + if (override_mode == OVERRIDE_MODES::MODE_GLOBAL_POSE) { + sk->set_bone_global_pose_override(bone_idx, our_trans, 1.0, true); + } else if (override_mode == OVERRIDE_MODES::MODE_LOCAL_POSE) { + sk->set_bone_local_pose_override(bone_idx, sk->global_pose_to_local_pose(bone_idx, our_trans), 1.0, true); + } else if (override_mode == OVERRIDE_MODES::MODE_CUSTOM_POSE) { + sk->set_bone_custom_pose(bone_idx, sk->global_pose_to_local_pose(bone_idx, our_trans)); + } + } +} + void BoneAttachment3D::set_bone_name(const String &p_name) { + bone_name = p_name; + Skeleton3D *sk = _get_skeleton3d(); + if (sk) { + set_bone_idx(sk->find_bone(bone_name)); + } +} + +String BoneAttachment3D::get_bone_name() const { + return bone_name; +} + +void BoneAttachment3D::set_bone_idx(const int &p_idx) { if (is_inside_tree()) { _check_unbind(); } - bone_name = p_name; + bone_idx = p_idx; + + Skeleton3D *sk = _get_skeleton3d(); + if (sk) { + if (bone_idx <= -1 || bone_idx >= sk->get_bone_count()) { + WARN_PRINT("Bone index out of range! Cannot connect BoneAttachment to node!"); + bone_idx = -1; + } else { + bone_name = sk->get_bone_name(bone_idx); + } + } if (is_inside_tree()) { _check_bind(); } + + notify_property_list_changed(); } -String BoneAttachment3D::get_bone_name() const { - return bone_name; +int BoneAttachment3D::get_bone_idx() const { + return bone_idx; +} + +void BoneAttachment3D::set_override_pose(bool p_override) { + override_pose = p_override; + set_notify_local_transform(override_pose); + set_process_internal(override_pose); + + if (!override_pose) { + Skeleton3D *sk = _get_skeleton3d(); + if (sk) { + if (override_mode == OVERRIDE_MODES::MODE_GLOBAL_POSE) { + sk->set_bone_global_pose_override(bone_idx, Transform3D(), 0.0, false); + } else if (override_mode == OVERRIDE_MODES::MODE_LOCAL_POSE) { + sk->set_bone_local_pose_override(bone_idx, Transform3D(), 0.0, false); + } else if (override_mode == OVERRIDE_MODES::MODE_CUSTOM_POSE) { + sk->set_bone_custom_pose(bone_idx, Transform3D()); + } + } + _transform_changed(); + } + notify_property_list_changed(); +} + +bool BoneAttachment3D::get_override_pose() const { + return override_pose; +} + +void BoneAttachment3D::set_override_mode(int p_mode) { + if (override_pose) { + Skeleton3D *sk = _get_skeleton3d(); + if (sk) { + if (override_mode == OVERRIDE_MODES::MODE_GLOBAL_POSE) { + sk->set_bone_global_pose_override(bone_idx, Transform3D(), 0.0, false); + } else if (override_mode == OVERRIDE_MODES::MODE_LOCAL_POSE) { + sk->set_bone_local_pose_override(bone_idx, Transform3D(), 0.0, false); + } else if (override_mode == OVERRIDE_MODES::MODE_CUSTOM_POSE) { + sk->set_bone_custom_pose(bone_idx, Transform3D()); + } + } + override_mode = p_mode; + _transform_changed(); + return; + } + override_mode = p_mode; +} + +int BoneAttachment3D::get_override_mode() const { + return override_mode; +} + +void BoneAttachment3D::set_use_external_skeleton(bool p_use_external) { + use_external_skeleton = p_use_external; + + if (use_external_skeleton) { + _check_unbind(); + _update_external_skeleton_cache(); + _check_bind(); + _transform_changed(); + } + + notify_property_list_changed(); +} + +bool BoneAttachment3D::get_use_external_skeleton() const { + return use_external_skeleton; +} + +void BoneAttachment3D::set_external_skeleton(NodePath p_path) { + external_skeleton_node = p_path; + _update_external_skeleton_cache(); + notify_property_list_changed(); +} + +NodePath BoneAttachment3D::get_external_skeleton() const { + return external_skeleton_node; } void BoneAttachment3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { + if (use_external_skeleton) { + _update_external_skeleton_cache(); + } _check_bind(); } break; case NOTIFICATION_EXIT_TREE: { _check_unbind(); } break; + case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { + _transform_changed(); + } break; + case NOTIFICATION_INTERNAL_PROCESS: { + if (_override_dirty) { + _override_dirty = false; + } + } + } +} + +void BoneAttachment3D::on_bone_pose_update(int p_bone_index) { + if (bone_idx == p_bone_index) { + Skeleton3D *sk = _get_skeleton3d(); + if (sk) { + if (!override_pose) { + if (use_external_skeleton) { + set_global_transform(sk->global_pose_to_world_transform(sk->get_bone_global_pose(bone_idx))); + } else { + set_transform(sk->get_bone_global_pose(bone_idx)); + } + } else { + if (!_override_dirty) { + _transform_changed(); + _override_dirty = true; + } + } + } } } BoneAttachment3D::BoneAttachment3D() { - bound = false; } void BoneAttachment3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bone_name", "bone_name"), &BoneAttachment3D::set_bone_name); ClassDB::bind_method(D_METHOD("get_bone_name"), &BoneAttachment3D::get_bone_name); + ClassDB::bind_method(D_METHOD("set_bone_idx", "bone_idx"), &BoneAttachment3D::set_bone_idx); + ClassDB::bind_method(D_METHOD("get_bone_idx"), &BoneAttachment3D::get_bone_idx); + + ClassDB::bind_method(D_METHOD("on_bone_pose_update", "bone_index"), &BoneAttachment3D::on_bone_pose_update); + + ClassDB::bind_method(D_METHOD("set_override_pose", "override_pose"), &BoneAttachment3D::set_override_pose); + ClassDB::bind_method(D_METHOD("get_override_pose"), &BoneAttachment3D::get_override_pose); + ClassDB::bind_method(D_METHOD("set_override_mode", "override_mode"), &BoneAttachment3D::set_override_mode); + ClassDB::bind_method(D_METHOD("get_override_mode"), &BoneAttachment3D::get_override_mode); + + ClassDB::bind_method(D_METHOD("set_use_external_skeleton", "use_external_skeleton"), &BoneAttachment3D::set_use_external_skeleton); + ClassDB::bind_method(D_METHOD("get_use_external_skeleton"), &BoneAttachment3D::get_use_external_skeleton); + ClassDB::bind_method(D_METHOD("set_external_skeleton", "external_skeleton"), &BoneAttachment3D::set_external_skeleton); + ClassDB::bind_method(D_METHOD("get_external_skeleton"), &BoneAttachment3D::get_external_skeleton); + ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "bone_name"), "set_bone_name", "get_bone_name"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "bone_idx"), "set_bone_idx", "get_bone_idx"); } diff --git a/scene/3d/bone_attachment_3d.h b/scene/3d/bone_attachment_3d.h index 70b871430f..cf681cace8 100644 --- a/scene/3d/bone_attachment_3d.h +++ b/scene/3d/bone_attachment_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,22 +36,61 @@ class BoneAttachment3D : public Node3D { GDCLASS(BoneAttachment3D, Node3D); - bool bound; + bool bound = false; String bone_name; + int bone_idx = -1; + + bool override_pose = false; + int override_mode = 0; + bool _override_dirty = false; + + enum OVERRIDE_MODES { + MODE_GLOBAL_POSE, + MODE_LOCAL_POSE, + MODE_CUSTOM_POSE + }; + + bool use_external_skeleton = false; + NodePath external_skeleton_node; + ObjectID external_skeleton_node_cache; void _check_bind(); void _check_unbind(); + void _transform_changed(); + void _update_external_skeleton_cache(); + Skeleton3D *_get_skeleton3d(); + protected: virtual void _validate_property(PropertyInfo &property) const override; + bool _get(const StringName &p_path, Variant &r_ret) const; + bool _set(const StringName &p_path, const Variant &p_value); + void _get_property_list(List<PropertyInfo> *p_list) const; void _notification(int p_what); static void _bind_methods(); public: + virtual TypedArray<String> get_configuration_warnings() const override; + void set_bone_name(const String &p_name); String get_bone_name() const; + void set_bone_idx(const int &p_idx); + int get_bone_idx() const; + + void set_override_pose(bool p_override); + bool get_override_pose() const; + void set_override_mode(int p_mode); + int get_override_mode() const; + + void set_use_external_skeleton(bool p_external_skeleton); + bool get_use_external_skeleton() const; + void set_external_skeleton(NodePath p_skeleton); + NodePath get_external_skeleton() const; + + virtual void on_bone_pose_update(int p_bone_index); + BoneAttachment3D(); }; diff --git a/scene/3d/camera_3d.cpp b/scene/3d/camera_3d.cpp index ecbaca7bd1..3ada9072c2 100644 --- a/scene/3d/camera_3d.cpp +++ b/scene/3d/camera_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,10 +31,8 @@ #include "camera_3d.h" #include "collision_object_3d.h" -#include "core/engine.h" #include "core/math/camera_matrix.h" -#include "scene/resources/material.h" -#include "scene/resources/surface_tool.h" +#include "scene/main/viewport.h" void Camera3D::_update_audio_listener_state() { } @@ -85,32 +83,28 @@ void Camera3D::_update_camera() { // here goes listener stuff /* if (viewport_ptr && is_inside_scene() && is_current()) - get_viewport()->_camera_transform_changed_notify(); + get_viewport()->_camera_3d_transform_changed_notify(); */ if (get_tree()->is_node_being_edited(this) || !is_current()) { return; } - get_viewport()->_camera_transform_changed_notify(); - - if (get_world_3d().is_valid()) { - get_world_3d()->_update_camera(this); - } + get_viewport()->_camera_3d_transform_changed_notify(); } void Camera3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_WORLD: { - // Needs to track the Viewport because it's needed on NOTIFICATION_EXIT_WORLD + // Needs to track the Viewport because it's needed on NOTIFICATION_EXIT_WORLD // and Spatial will handle it first, including clearing its reference to the Viewport, // therefore making it impossible to subclasses to access it viewport = get_viewport(); ERR_FAIL_COND(!viewport); - bool first_camera = viewport->_camera_add(this); + bool first_camera = viewport->_camera_3d_add(this); if (current || first_camera) { - viewport->_camera_set(this); + viewport->_camera_3d_set(this); } } break; @@ -132,7 +126,7 @@ void Camera3D::_notification(int p_what) { } if (viewport) { - viewport->_camera_remove(this); + viewport->_camera_3d_remove(this); viewport = nullptr; } @@ -150,14 +144,14 @@ void Camera3D::_notification(int p_what) { } } -Transform Camera3D::get_camera_transform() const { - Transform tr = get_global_transform().orthonormalized(); +Transform3D Camera3D::get_camera_transform() const { + Transform3D tr = get_global_transform().orthonormalized(); tr.origin += tr.basis.get_axis(1) * v_offset; tr.origin += tr.basis.get_axis(0) * h_offset; return tr; } -void Camera3D::set_perspective(float p_fovy_degrees, float p_z_near, float p_z_far) { +void Camera3D::set_perspective(real_t p_fovy_degrees, real_t p_z_near, real_t p_z_far) { if (!force_change && fov == p_fovy_degrees && p_z_near == near && p_z_far == far && mode == PROJECTION_PERSPECTIVE) { return; } @@ -168,11 +162,11 @@ void Camera3D::set_perspective(float p_fovy_degrees, float p_z_near, float p_z_f mode = PROJECTION_PERSPECTIVE; RenderingServer::get_singleton()->camera_set_perspective(camera, fov, near, far); - update_gizmo(); + update_gizmos(); force_change = false; } -void Camera3D::set_orthogonal(float p_size, float p_z_near, float p_z_far) { +void Camera3D::set_orthogonal(real_t p_size, real_t p_z_near, real_t p_z_far) { if (!force_change && size == p_size && p_z_near == near && p_z_far == far && mode == PROJECTION_ORTHOGONAL) { return; } @@ -185,10 +179,10 @@ void Camera3D::set_orthogonal(float p_size, float p_z_near, float p_z_far) { force_change = false; RenderingServer::get_singleton()->camera_set_orthogonal(camera, size, near, far); - update_gizmo(); + update_gizmos(); } -void Camera3D::set_frustum(float p_size, Vector2 p_offset, float p_z_near, float p_z_far) { +void Camera3D::set_frustum(real_t p_size, Vector2 p_offset, real_t p_z_near, real_t p_z_far) { if (!force_change && size == p_size && frustum_offset == p_offset && p_z_near == near && p_z_far == far && mode == PROJECTION_FRUSTUM) { return; } @@ -202,14 +196,14 @@ void Camera3D::set_frustum(float p_size, Vector2 p_offset, float p_z_near, float force_change = false; RenderingServer::get_singleton()->camera_set_frustum(camera, size, frustum_offset, near, far); - update_gizmo(); + update_gizmos(); } void Camera3D::set_projection(Camera3D::Projection p_mode) { if (p_mode == PROJECTION_PERSPECTIVE || p_mode == PROJECTION_ORTHOGONAL || p_mode == PROJECTION_FRUSTUM) { mode = p_mode; _update_camera_mode(); - _change_notify(); + notify_property_list_changed(); } } @@ -224,7 +218,7 @@ void Camera3D::make_current() { return; } - get_viewport()->_camera_set(this); + get_viewport()->_camera_3d_set(this); //get_scene()->call_group(SceneMainLoop::GROUP_CALL_REALTIME,camera_group,"_camera_make_current",this); } @@ -235,11 +229,11 @@ void Camera3D::clear_current(bool p_enable_next) { return; } - if (get_viewport()->get_camera() == this) { - get_viewport()->_camera_set(nullptr); + if (get_viewport()->get_camera_3d() == this) { + get_viewport()->_camera_3d_set(nullptr); if (p_enable_next) { - get_viewport()->_camera_make_next_current(this); + get_viewport()->_camera_3d_make_next_current(this); } } } @@ -254,7 +248,7 @@ void Camera3D::set_current(bool p_current) { bool Camera3D::is_current() const { if (is_inside_tree() && !get_tree()->is_node_being_edited(this)) { - return get_viewport()->get_camera() == this; + return get_viewport()->get_camera_3d() == this; } else { return current; } @@ -299,7 +293,7 @@ Vector3 Camera3D::project_ray_origin(const Point2 &p_pos) const { return get_camera_transform().origin; } else { Vector2 pos = cpos / viewport_size; - float vsize, hsize; + real_t vsize, hsize; if (keep_aspect == KEEP_WIDTH) { vsize = size / viewport_size.aspect(); hsize = size; @@ -318,7 +312,7 @@ Vector3 Camera3D::project_ray_origin(const Point2 &p_pos) const { }; bool Camera3D::is_position_behind(const Vector3 &p_pos) const { - Transform t = get_global_transform(); + Transform3D t = get_global_transform(); Vector3 eyedir = -t.basis.get_axis(2).normalized(); return eyedir.dot(p_pos - t.origin) < near; } @@ -337,7 +331,7 @@ Vector<Vector3> Camera3D::get_near_plane_points() const { } Vector3 endpoints[8]; - cm.get_endpoints(Transform(), endpoints); + cm.get_endpoints(Transform3D(), endpoints); Vector<Vector3> points; points.push_back(Vector3()); @@ -372,7 +366,7 @@ Point2 Camera3D::unproject_position(const Vector3 &p_pos) const { return res; } -Vector3 Camera3D::project_position(const Point2 &p_point, float p_z_depth) const { +Vector3 Camera3D::project_position(const Point2 &p_point, real_t p_z_depth) const { ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector3(), "Camera is not inside scene."); if (p_z_depth == 0 && mode != PROJECTION_ORTHOGONAL) { @@ -432,7 +426,7 @@ void Camera3D::set_keep_aspect_mode(KeepAspect p_aspect) { keep_aspect = p_aspect; RenderingServer::get_singleton()->camera_set_use_vertical_aspect(camera, p_aspect == KEEP_WIDTH); _update_camera_mode(); - _change_notify(); + notify_property_list_changed(); } Camera3D::KeepAspect Camera3D::get_keep_aspect_mode() const { @@ -476,13 +470,13 @@ void Camera3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_fov"), &Camera3D::get_fov); ClassDB::bind_method(D_METHOD("get_frustum_offset"), &Camera3D::get_frustum_offset); ClassDB::bind_method(D_METHOD("get_size"), &Camera3D::get_size); - ClassDB::bind_method(D_METHOD("get_zfar"), &Camera3D::get_zfar); - ClassDB::bind_method(D_METHOD("get_znear"), &Camera3D::get_znear); + ClassDB::bind_method(D_METHOD("get_far"), &Camera3D::get_far); + ClassDB::bind_method(D_METHOD("get_near"), &Camera3D::get_near); ClassDB::bind_method(D_METHOD("set_fov"), &Camera3D::set_fov); ClassDB::bind_method(D_METHOD("set_frustum_offset"), &Camera3D::set_frustum_offset); ClassDB::bind_method(D_METHOD("set_size"), &Camera3D::set_size); - ClassDB::bind_method(D_METHOD("set_zfar"), &Camera3D::set_zfar); - ClassDB::bind_method(D_METHOD("set_znear"), &Camera3D::set_znear); + ClassDB::bind_method(D_METHOD("set_far"), &Camera3D::set_far); + ClassDB::bind_method(D_METHOD("set_near"), &Camera3D::set_near); ClassDB::bind_method(D_METHOD("get_projection"), &Camera3D::get_projection); ClassDB::bind_method(D_METHOD("set_projection"), &Camera3D::set_projection); ClassDB::bind_method(D_METHOD("set_h_offset", "ofs"), &Camera3D::set_h_offset); @@ -500,10 +494,11 @@ void Camera3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_doppler_tracking", "mode"), &Camera3D::set_doppler_tracking); ClassDB::bind_method(D_METHOD("get_doppler_tracking"), &Camera3D::get_doppler_tracking); ClassDB::bind_method(D_METHOD("get_frustum"), &Camera3D::get_frustum); + ClassDB::bind_method(D_METHOD("is_position_in_frustum", "world_point"), &Camera3D::is_position_in_frustum); ClassDB::bind_method(D_METHOD("get_camera_rid"), &Camera3D::get_camera); - ClassDB::bind_method(D_METHOD("set_cull_mask_bit", "layer", "enable"), &Camera3D::set_cull_mask_bit); - ClassDB::bind_method(D_METHOD("get_cull_mask_bit", "layer"), &Camera3D::get_cull_mask_bit); + ClassDB::bind_method(D_METHOD("set_cull_mask_value", "layer_number", "value"), &Camera3D::set_cull_mask_value); + ClassDB::bind_method(D_METHOD("get_cull_mask_value", "layer_number"), &Camera3D::get_cull_mask_value); //ClassDB::bind_method(D_METHOD("_camera_make_current"),&Camera::_camera_make_current ); @@ -516,11 +511,11 @@ void Camera3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "doppler_tracking", PROPERTY_HINT_ENUM, "Disabled,Idle,Physics"), "set_doppler_tracking", "get_doppler_tracking"); ADD_PROPERTY(PropertyInfo(Variant::INT, "projection", PROPERTY_HINT_ENUM, "Perspective,Orthogonal,Frustum"), "set_projection", "get_projection"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "current"), "set_current", "is_current"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fov", PROPERTY_HINT_RANGE, "1,179,0.1"), "set_fov", "get_fov"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "fov", PROPERTY_HINT_RANGE, "1,179,0.1,degrees"), "set_fov", "get_fov"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "size", PROPERTY_HINT_RANGE, "0.1,16384,0.01"), "set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frustum_offset"), "set_frustum_offset", "get_frustum_offset"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "near", PROPERTY_HINT_EXP_RANGE, "0.01,8192,0.01,or_greater"), "set_znear", "get_znear"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "far", PROPERTY_HINT_EXP_RANGE, "0.1,8192,0.1,or_greater"), "set_zfar", "get_zfar"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "near", PROPERTY_HINT_RANGE, "0.001,10,0.001,or_greater,exp"), "set_near", "get_near"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "far", PROPERTY_HINT_RANGE, "0.01,4000,0.01,or_greater,exp"), "set_far", "get_far"); BIND_ENUM_CONSTANT(PROJECTION_PERSPECTIVE); BIND_ENUM_CONSTANT(PROJECTION_ORTHOGONAL); @@ -534,15 +529,15 @@ void Camera3D::_bind_methods() { BIND_ENUM_CONSTANT(DOPPLER_TRACKING_PHYSICS_STEP); } -float Camera3D::get_fov() const { +real_t Camera3D::get_fov() const { return fov; } -float Camera3D::get_size() const { +real_t Camera3D::get_size() const { return size; } -float Camera3D::get_znear() const { +real_t Camera3D::get_near() const { return near; } @@ -550,7 +545,7 @@ Vector2 Camera3D::get_frustum_offset() const { return frustum_offset; } -float Camera3D::get_zfar() const { +real_t Camera3D::get_far() const { return far; } @@ -558,22 +553,20 @@ Camera3D::Projection Camera3D::get_projection() const { return mode; } -void Camera3D::set_fov(float p_fov) { +void Camera3D::set_fov(real_t p_fov) { ERR_FAIL_COND(p_fov < 1 || p_fov > 179); fov = p_fov; _update_camera_mode(); - _change_notify("fov"); } -void Camera3D::set_size(float p_size) { +void Camera3D::set_size(real_t p_size) { ERR_FAIL_COND(p_size < 0.1 || p_size > 16384); size = p_size; _update_camera_mode(); - _change_notify("size"); } -void Camera3D::set_znear(float p_znear) { - near = p_znear; +void Camera3D::set_near(real_t p_near) { + near = p_near; _update_camera_mode(); } @@ -582,8 +575,8 @@ void Camera3D::set_frustum_offset(Vector2 p_offset) { _update_camera_mode(); } -void Camera3D::set_zfar(float p_zfar) { - far = p_zfar; +void Camera3D::set_far(real_t p_far) { + far = p_far; _update_camera_mode(); } @@ -597,18 +590,22 @@ uint32_t Camera3D::get_cull_mask() const { return layers; } -void Camera3D::set_cull_mask_bit(int p_layer, bool p_enable) { - ERR_FAIL_INDEX(p_layer, 32); - if (p_enable) { - set_cull_mask(layers | (1 << p_layer)); +void Camera3D::set_cull_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Render layer number must be between 1 and 20 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 20, "Render layer number must be between 1 and 20 inclusive."); + uint32_t mask = get_cull_mask(); + if (p_value) { + mask |= 1 << (p_layer_number - 1); } else { - set_cull_mask(layers & (~(1 << p_layer))); + mask &= ~(1 << (p_layer_number - 1)); } + set_cull_mask(mask); } -bool Camera3D::get_cull_mask_bit(int p_layer) const { - ERR_FAIL_INDEX_V(p_layer, 32, false); - return (layers & (1 << p_layer)); +bool Camera3D::get_cull_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Render layer number must be between 1 and 20 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 20, false, "Render layer number must be between 1 and 20 inclusive."); + return layers & (1 << (p_layer_number - 1)); } Vector<Plane> Camera3D::get_frustum() const { @@ -625,21 +622,31 @@ Vector<Plane> Camera3D::get_frustum() const { return cm.get_projection_planes(get_camera_transform()); } -void Camera3D::set_v_offset(float p_offset) { +bool Camera3D::is_position_in_frustum(const Vector3 &p_position) const { + Vector<Plane> frustum = get_frustum(); + for (int i = 0; i < frustum.size(); i++) { + if (frustum[i].is_point_over(p_position)) { + return false; + } + } + return true; +} + +void Camera3D::set_v_offset(real_t p_offset) { v_offset = p_offset; _update_camera(); } -float Camera3D::get_v_offset() const { +real_t Camera3D::get_v_offset() const { return v_offset; } -void Camera3D::set_h_offset(float p_offset) { +void Camera3D::set_h_offset(real_t p_offset) { h_offset = p_offset; _update_camera(); } -float Camera3D::get_h_offset() const { +real_t Camera3D::get_h_offset() const { return h_offset; } @@ -653,24 +660,10 @@ Vector3 Camera3D::get_doppler_tracked_velocity() const { Camera3D::Camera3D() { camera = RenderingServer::get_singleton()->camera_create(); - size = 1; - fov = 0; - frustum_offset = Vector2(); - near = 0; - far = 0; - current = false; - viewport = nullptr; - force_change = false; - mode = PROJECTION_PERSPECTIVE; - set_perspective(75.0, 0.05, 100.0); - keep_aspect = KEEP_HEIGHT; - layers = 0xfffff; - v_offset = 0; - h_offset = 0; + set_perspective(75.0, 0.05, 4000.0); RenderingServer::get_singleton()->camera_set_cull_mask(camera, layers); //active=false; - velocity_tracker.instance(); - doppler_tracking = DOPPLER_TRACKING_DISABLED; + velocity_tracker.instantiate(); set_notify_transform(true); set_disable_scale(true); } @@ -681,29 +674,29 @@ Camera3D::~Camera3D() { //////////////////////////////////////// -void ClippedCamera3D::set_margin(float p_margin) { +void ClippedCamera3D::set_margin(real_t p_margin) { margin = p_margin; } -float ClippedCamera3D::get_margin() const { +real_t ClippedCamera3D::get_margin() const { return margin; } -void ClippedCamera3D::set_process_mode(ProcessMode p_mode) { - if (process_mode == p_mode) { +void ClippedCamera3D::set_process_callback(ClipProcessCallback p_mode) { + if (process_callback == p_mode) { return; } - process_mode = p_mode; - set_process_internal(process_mode == CLIP_PROCESS_IDLE); - set_physics_process_internal(process_mode == CLIP_PROCESS_PHYSICS); + process_callback = p_mode; + set_process_internal(process_callback == CLIP_PROCESS_IDLE); + set_physics_process_internal(process_callback == CLIP_PROCESS_PHYSICS); } -ClippedCamera3D::ProcessMode ClippedCamera3D::get_process_mode() const { - return process_mode; +ClippedCamera3D::ClipProcessCallback ClippedCamera3D::get_process_callback() const { + return process_callback; } -Transform ClippedCamera3D::get_camera_transform() const { - Transform t = Camera3D::get_camera_transform(); +Transform3D ClippedCamera3D::get_camera_transform() const { + Transform3D t = Camera3D::get_camera_transform(); t.origin += -t.basis.get_axis(Vector3::AXIS_Z).normalized() * clip_offset; return t; } @@ -731,7 +724,7 @@ void ClippedCamera3D::_notification(int p_what) { Vector3 ray_from = parent_plane.project(cam_pos); - clip_offset = 0; //reset by defau;t + clip_offset = 0; //reset by default { //check if points changed Vector<Vector3> local_points = get_near_plane_points(); @@ -751,11 +744,11 @@ void ClippedCamera3D::_notification(int p_what) { } } - Transform xf = get_global_transform(); + Transform3D xf = get_global_transform(); xf.origin = ray_from; xf.orthonormalize(); - float closest_safe = 1.0f, closest_unsafe = 1.0f; + real_t closest_safe = 1.0f, closest_unsafe = 1.0f; if (dspace->cast_motion(pyramid_shape, xf, cam_pos - ray_from, margin, closest_safe, closest_unsafe, exclude, collision_mask, clip_to_bodies, clip_to_areas)) { clip_offset = cam_pos.distance_to(ray_from + (cam_pos - ray_from) * closest_safe); } @@ -764,7 +757,7 @@ void ClippedCamera3D::_notification(int p_what) { } if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { - update_gizmo(); + update_gizmos(); } } @@ -776,18 +769,22 @@ uint32_t ClippedCamera3D::get_collision_mask() const { return collision_mask; } -void ClippedCamera3D::set_collision_mask_bit(int p_bit, bool p_value) { +void ClippedCamera3D::set_collision_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { - mask |= 1 << p_bit; + mask |= 1 << (p_layer_number - 1); } else { - mask &= ~(1 << p_bit); + mask &= ~(1 << (p_layer_number - 1)); } set_collision_mask(mask); } -bool ClippedCamera3D::get_collision_mask_bit(int p_bit) const { - return get_collision_mask() & (1 << p_bit); +bool ClippedCamera3D::get_collision_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_mask() & (1 << (p_layer_number - 1)); } void ClippedCamera3D::add_exception_rid(const RID &p_rid) { @@ -820,7 +817,7 @@ void ClippedCamera3D::clear_exceptions() { exclude.clear(); } -float ClippedCamera3D::get_clip_offset() const { +real_t ClippedCamera3D::get_clip_offset() const { return clip_offset; } @@ -844,14 +841,14 @@ void ClippedCamera3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_margin", "margin"), &ClippedCamera3D::set_margin); ClassDB::bind_method(D_METHOD("get_margin"), &ClippedCamera3D::get_margin); - ClassDB::bind_method(D_METHOD("set_process_mode", "process_mode"), &ClippedCamera3D::set_process_mode); - ClassDB::bind_method(D_METHOD("get_process_mode"), &ClippedCamera3D::get_process_mode); + ClassDB::bind_method(D_METHOD("set_process_callback", "process_callback"), &ClippedCamera3D::set_process_callback); + ClassDB::bind_method(D_METHOD("get_process_callback"), &ClippedCamera3D::get_process_callback); ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &ClippedCamera3D::set_collision_mask); ClassDB::bind_method(D_METHOD("get_collision_mask"), &ClippedCamera3D::get_collision_mask); - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &ClippedCamera3D::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &ClippedCamera3D::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &ClippedCamera3D::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &ClippedCamera3D::get_collision_mask_value); ClassDB::bind_method(D_METHOD("add_exception_rid", "rid"), &ClippedCamera3D::add_exception_rid); ClassDB::bind_method(D_METHOD("add_exception", "node"), &ClippedCamera3D::add_exception); @@ -870,7 +867,7 @@ void ClippedCamera3D::_bind_methods() { ClassDB::bind_method(D_METHOD("clear_exceptions"), &ClippedCamera3D::clear_exceptions); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0,32,0.01"), "set_margin", "get_margin"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "process_mode", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_process_mode", "get_process_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "process_callback", PROPERTY_HINT_ENUM, "Physics,Idle"), "set_process_callback", "get_process_callback"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collision_mask", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collision_mask", "get_collision_mask"); ADD_GROUP("Clip To", "clip_to"); @@ -882,16 +879,10 @@ void ClippedCamera3D::_bind_methods() { } ClippedCamera3D::ClippedCamera3D() { - margin = 0; - clip_offset = 0; - process_mode = CLIP_PROCESS_PHYSICS; set_physics_process_internal(true); - collision_mask = 1; set_notify_local_transform(Engine::get_singleton()->is_editor_hint()); points.resize(5); pyramid_shape = PhysicsServer3D::get_singleton()->shape_create(PhysicsServer3D::SHAPE_CONVEX_POLYGON); - clip_to_areas = false; - clip_to_bodies = true; } ClippedCamera3D::~ClippedCamera3D() { diff --git a/scene/3d/camera_3d.h b/scene/3d/camera_3d.h index 6a778d45a2..3b704944b0 100644 --- a/scene/3d/camera_3d.h +++ b/scene/3d/camera_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,16 +33,12 @@ #include "scene/3d/node_3d.h" #include "scene/3d/velocity_tracker_3d.h" -#include "scene/main/window.h" -#include "scene/resources/camera_effects.h" -#include "scene/resources/environment.h" class Camera3D : public Node3D { GDCLASS(Camera3D, Node3D); public: enum Projection { - PROJECTION_PERSPECTIVE, PROJECTION_ORTHOGONAL, PROJECTION_FRUSTUM @@ -58,26 +54,27 @@ public: }; private: - bool force_change; - bool current; - Viewport *viewport; + bool force_change = false; + bool current = false; + Viewport *viewport = nullptr; - Projection mode; + Projection mode = PROJECTION_PERSPECTIVE; - float fov; - float size; + real_t fov = 0.0; + real_t size = 1.0; Vector2 frustum_offset; - float near, far; - float v_offset; - float h_offset; - KeepAspect keep_aspect; + real_t near = 0.0; + real_t far = 0.0; + real_t v_offset = 0.0; + real_t h_offset = 0.0; + KeepAspect keep_aspect = KEEP_HEIGHT; RID camera; RID scenario_id; // String camera_group; - uint32_t layers; + uint32_t layers = 0xfffff; Ref<Environment> environment; Ref<CameraEffects> effects; @@ -88,7 +85,7 @@ private: friend class Viewport; void _update_audio_listener_state(); - DopplerTracking doppler_tracking; + DopplerTracking doppler_tracking = DOPPLER_TRACKING_DISABLED; Ref<VelocityTracker3D> velocity_tracker; protected: @@ -103,15 +100,13 @@ protected: public: enum { - NOTIFICATION_BECAME_CURRENT = 50, NOTIFICATION_LOST_CURRENT = 51 }; - void set_perspective(float p_fovy_degrees, float p_z_near, float p_z_far); - void set_orthogonal(float p_size, float p_z_near, float p_z_far); - void set_frustum(float p_size, Vector2 p_offset, float p_z_near, - float p_z_far); + void set_perspective(real_t p_fovy_degrees, real_t p_z_near, real_t p_z_far); + void set_orthogonal(real_t p_size, real_t p_z_near, real_t p_z_far); + void set_frustum(real_t p_size, Vector2 p_offset, real_t p_z_near, real_t p_z_far); void set_projection(Camera3D::Projection p_mode); void make_current(); @@ -121,39 +116,39 @@ public: RID get_camera() const; - float get_fov() const; - float get_size() const; - float get_zfar() const; - float get_znear() const; + real_t get_fov() const; + real_t get_size() const; + real_t get_far() const; + real_t get_near() const; Vector2 get_frustum_offset() const; Projection get_projection() const; - void set_fov(float p_fov); - void set_size(float p_size); - void set_zfar(float p_zfar); - void set_znear(float p_znear); + void set_fov(real_t p_fov); + void set_size(real_t p_size); + void set_far(real_t p_far); + void set_near(real_t p_near); void set_frustum_offset(Vector2 p_offset); - virtual Transform get_camera_transform() const; + virtual Transform3D get_camera_transform() const; virtual Vector3 project_ray_normal(const Point2 &p_pos) const; virtual Vector3 project_ray_origin(const Point2 &p_pos) const; virtual Vector3 project_local_ray_normal(const Point2 &p_pos) const; virtual Point2 unproject_position(const Vector3 &p_pos) const; bool is_position_behind(const Vector3 &p_pos) const; - virtual Vector3 project_position(const Point2 &p_point, - float p_z_depth) const; + virtual Vector3 project_position(const Point2 &p_point, real_t p_z_depth) const; Vector<Vector3> get_near_plane_points() const; void set_cull_mask(uint32_t p_layers); uint32_t get_cull_mask() const; - void set_cull_mask_bit(int p_layer, bool p_enable); - bool get_cull_mask_bit(int p_layer) const; + void set_cull_mask_value(int p_layer_number, bool p_enable); + bool get_cull_mask_value(int p_layer_number) const; virtual Vector<Plane> get_frustum() const; + bool is_position_in_frustum(const Vector3 &p_position) const; void set_environment(const Ref<Environment> &p_environment); Ref<Environment> get_environment() const; @@ -164,11 +159,11 @@ public: void set_keep_aspect_mode(KeepAspect p_aspect); KeepAspect get_keep_aspect_mode() const; - void set_v_offset(float p_offset); - float get_v_offset() const; + void set_v_offset(real_t p_offset); + real_t get_v_offset() const; - void set_h_offset(float p_offset); - float get_h_offset() const; + void set_h_offset(real_t p_offset); + real_t get_h_offset() const; void set_doppler_tracking(DopplerTracking p_tracking); DopplerTracking get_doppler_tracking() const; @@ -187,19 +182,19 @@ class ClippedCamera3D : public Camera3D { GDCLASS(ClippedCamera3D, Camera3D); public: - enum ProcessMode { + enum ClipProcessCallback { CLIP_PROCESS_PHYSICS, CLIP_PROCESS_IDLE, }; private: - ProcessMode process_mode; + ClipProcessCallback process_callback = CLIP_PROCESS_PHYSICS; RID pyramid_shape; - float margin; - float clip_offset; - uint32_t collision_mask; - bool clip_to_areas; - bool clip_to_bodies; + real_t margin = 0.0; + real_t clip_offset = 0.0; + uint32_t collision_mask = 1; + bool clip_to_areas = false; + bool clip_to_bodies = true; Set<RID> exclude; @@ -208,7 +203,7 @@ private: protected: void _notification(int p_what); static void _bind_methods(); - virtual Transform get_camera_transform() const override; + virtual Transform3D get_camera_transform() const override; public: void set_clip_to_areas(bool p_clip); @@ -217,17 +212,17 @@ public: void set_clip_to_bodies(bool p_clip); bool is_clip_to_bodies_enabled() const; - void set_margin(float p_margin); - float get_margin() const; + void set_margin(real_t p_margin); + real_t get_margin() const; - void set_process_mode(ProcessMode p_mode); - ProcessMode get_process_mode() const; + void set_process_callback(ClipProcessCallback p_mode); + ClipProcessCallback get_process_callback() const; void set_collision_mask(uint32_t p_mask); uint32_t get_collision_mask() const; - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; + void set_collision_mask_value(int p_layer_number, bool p_value); + bool get_collision_mask_value(int p_layer_number) const; void add_exception_rid(const RID &p_rid); void add_exception(const Object *p_object); @@ -235,11 +230,11 @@ public: void remove_exception(const Object *p_object); void clear_exceptions(); - float get_clip_offset() const; + real_t get_clip_offset() const; ClippedCamera3D(); ~ClippedCamera3D(); }; -VARIANT_ENUM_CAST(ClippedCamera3D::ProcessMode); +VARIANT_ENUM_CAST(ClippedCamera3D::ClipProcessCallback); #endif diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index 356992e922..e2f953974a 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,10 +31,25 @@ #include "collision_object_3d.h" #include "scene/scene_string_names.h" -#include "servers/physics_server_3d.h" void CollisionObject3D::_notification(int p_what) { switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + if (_are_collision_shapes_visible()) { + debug_shape_old_transform = get_global_transform(); + for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { + debug_shapes_to_update.insert(E->key()); + } + _update_debug_shapes(); + } + } break; + + case NOTIFICATION_EXIT_TREE: { + if (debug_shapes_count > 0) { + _clear_debug_shapes(); + } + } break; + case NOTIFICATION_ENTER_WORLD: { if (area) { PhysicsServer3D::get_singleton()->area_set_transform(rid, get_global_transform()); @@ -42,44 +57,205 @@ 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: { + if (only_update_transform_changes) { + return; + } + if (area) { PhysicsServer3D::get_singleton()->area_set_transform(rid, get_global_transform()); } else { PhysicsServer3D::get_singleton()->body_set_state(rid, PhysicsServer3D::BODY_STATE_TRANSFORM, get_global_transform()); } + _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; } } -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); +void CollisionObject3D::set_collision_layer(uint32_t p_layer) { + collision_layer = p_layer; + if (area) { + PhysicsServer3D::get_singleton()->area_set_collision_layer(get_rid(), p_layer); + } else { + PhysicsServer3D::get_singleton()->body_set_collision_layer(get_rid(), p_layer); + } +} + +uint32_t CollisionObject3D::get_collision_layer() const { + return collision_layer; +} + +void CollisionObject3D::set_collision_mask(uint32_t p_mask) { + collision_mask = p_mask; + if (area) { + PhysicsServer3D::get_singleton()->area_set_collision_mask(get_rid(), p_mask); + } else { + PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), p_mask); + } +} + +uint32_t CollisionObject3D::get_collision_mask() const { + return collision_mask; +} + +void CollisionObject3D::set_collision_layer_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); + uint32_t collision_layer = get_collision_layer(); + if (p_value) { + collision_layer |= 1 << (p_layer_number - 1); + } else { + collision_layer &= ~(1 << (p_layer_number - 1)); + } + set_collision_layer(collision_layer); +} + +bool CollisionObject3D::get_collision_layer_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_layer() & (1 << (p_layer_number - 1)); +} + +void CollisionObject3D::set_collision_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); + uint32_t mask = get_collision_mask(); + if (p_value) { + mask |= 1 << (p_layer_number - 1); + } else { + mask &= ~(1 << (p_layer_number - 1)); + } + set_collision_mask(mask); +} + +bool CollisionObject3D::get_collision_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_mask() & (1 << (p_layer_number - 1)); +} + +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_call(Camera3D *p_camera, const Ref<InputEvent> &p_input_event, const Vector3 &p_pos, const Vector3 &p_normal, int p_shape) { + GDVIRTUAL_CALL(_input_event, p_camera, p_input_event, p_pos, p_normal, p_shape); emit_signal(SceneStringNames::get_singleton()->input_event, p_camera, p_input_event, p_pos, p_normal, p_shape); } @@ -97,6 +273,30 @@ 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::set_only_update_transform_changes(bool p_enable) { + only_update_transform_changes = p_enable; +} + +bool CollisionObject3D::is_only_update_transform_changes_enabled() const { + return only_update_transform_changes; +} + void CollisionObject3D::_update_pickable() { if (!is_inside_tree()) { return; @@ -110,6 +310,106 @@ void CollisionObject3D::_update_pickable() { } } +bool CollisionObject3D::_are_collision_shapes_visible() { + return is_inside_tree() && get_tree()->is_debugging_collisions_hint() && !Engine::get_singleton()->is_editor_hint(); +} + +void CollisionObject3D::_update_shape_data(uint32_t p_owner) { + if (_are_collision_shapes_visible()) { + if (debug_shapes_to_update.is_empty()) { + callable_mp(this, &CollisionObject3D::_update_debug_shapes).call_deferred({}, 0); + } + debug_shapes_to_update.insert(p_owner); + } +} + +void CollisionObject3D::_shape_changed(const Ref<Shape3D> &p_shape) { + for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { + ShapeData &shapedata = E->get(); + ShapeData::ShapeBase *shapes = shapedata.shapes.ptrw(); + for (int i = 0; i < shapedata.shapes.size(); i++) { + ShapeData::ShapeBase &s = shapes[i]; + if (s.shape == p_shape && s.debug_shape.is_valid()) { + Ref<Mesh> mesh = s.shape->get_debug_mesh(); + RS::get_singleton()->instance_set_base(s.debug_shape, mesh->get_rid()); + } + } + } +} + +void CollisionObject3D::_update_debug_shapes() { + if (!is_inside_tree()) { + debug_shapes_to_update.clear(); + return; + } + + for (Set<uint32_t>::Element *shapedata_idx = debug_shapes_to_update.front(); shapedata_idx; shapedata_idx = shapedata_idx->next()) { + if (shapes.has(shapedata_idx->get())) { + ShapeData &shapedata = shapes[shapedata_idx->get()]; + ShapeData::ShapeBase *shapes = shapedata.shapes.ptrw(); + for (int i = 0; i < shapedata.shapes.size(); i++) { + ShapeData::ShapeBase &s = shapes[i]; + if (s.shape.is_null() || shapedata.disabled) { + if (s.debug_shape.is_valid()) { + RS::get_singleton()->free(s.debug_shape); + s.debug_shape = RID(); + --debug_shapes_count; + } + continue; + } + + if (s.debug_shape.is_null()) { + s.debug_shape = RS::get_singleton()->instance_create(); + RS::get_singleton()->instance_set_scenario(s.debug_shape, get_world_3d()->get_scenario()); + + if (!s.shape->is_connected("changed", callable_mp(this, &CollisionObject3D::_shape_changed))) { + s.shape->connect("changed", callable_mp(this, &CollisionObject3D::_shape_changed), + varray(s.shape), CONNECT_DEFERRED); + } + + ++debug_shapes_count; + } + + Ref<Mesh> mesh = s.shape->get_debug_mesh(); + RS::get_singleton()->instance_set_base(s.debug_shape, mesh->get_rid()); + RS::get_singleton()->instance_set_transform(s.debug_shape, get_global_transform() * shapedata.xform); + } + } + } + debug_shapes_to_update.clear(); +} + +void CollisionObject3D::_clear_debug_shapes() { + for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { + ShapeData &shapedata = E->get(); + ShapeData::ShapeBase *shapes = shapedata.shapes.ptrw(); + for (int i = 0; i < shapedata.shapes.size(); i++) { + ShapeData::ShapeBase &s = shapes[i]; + if (s.debug_shape.is_valid()) { + RS::get_singleton()->free(s.debug_shape); + s.debug_shape = RID(); + if (s.shape.is_valid() && s.shape->is_connected("changed", callable_mp(this, &CollisionObject3D::_update_shape_data))) { + s.shape->disconnect("changed", callable_mp(this, &CollisionObject3D::_update_shape_data)); + } + } + } + } + debug_shapes_count = 0; +} + +void CollisionObject3D::_on_transform_changed() { + if (debug_shapes_count > 0 && !debug_shape_old_transform.is_equal_approx(get_global_transform())) { + debug_shape_old_transform = get_global_transform(); + for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { + ShapeData &shapedata = E->get(); + const ShapeData::ShapeBase *shapes = shapedata.shapes.ptr(); + for (int i = 0; i < shapedata.shapes.size(); i++) { + RS::get_singleton()->instance_set_transform(shapes[i].debug_shape, debug_shape_old_transform * shapedata.xform); + } + } + } +} + void CollisionObject3D::set_ray_pickable(bool p_ray_pickable) { ray_pickable = p_ray_pickable; _update_pickable(); @@ -120,6 +420,16 @@ bool CollisionObject3D::is_ray_pickable() const { } void CollisionObject3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_collision_layer", "layer"), &CollisionObject3D::set_collision_layer); + ClassDB::bind_method(D_METHOD("get_collision_layer"), &CollisionObject3D::get_collision_layer); + ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &CollisionObject3D::set_collision_mask); + ClassDB::bind_method(D_METHOD("get_collision_mask"), &CollisionObject3D::get_collision_mask); + ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &CollisionObject3D::set_collision_layer_value); + ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &CollisionObject3D::get_collision_layer_value); + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &CollisionObject3D::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &CollisionObject3D::get_collision_mask_value); + 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); @@ -141,14 +451,25 @@ void CollisionObject3D::_bind_methods() { ClassDB::bind_method(D_METHOD("shape_owner_clear_shapes", "owner_id"), &CollisionObject3D::shape_owner_clear_shapes); ClassDB::bind_method(D_METHOD("shape_find_owner", "shape_index"), &CollisionObject3D::shape_find_owner); - BIND_VMETHOD(MethodInfo("_input_event", PropertyInfo(Variant::OBJECT, "camera"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::VECTOR3, "click_position"), PropertyInfo(Variant::VECTOR3, "click_normal"), PropertyInfo(Variant::INT, "shape_idx"))); + GDVIRTUAL_BIND(_input_event, "camera", "event", "position", "normal", "shape_idx"); - ADD_SIGNAL(MethodInfo("input_event", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::VECTOR3, "click_position"), PropertyInfo(Variant::VECTOR3, "click_normal"), PropertyInfo(Variant::INT, "shape_idx"))); + ADD_SIGNAL(MethodInfo("input_event", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::OBJECT, "event", PROPERTY_HINT_RESOURCE_TYPE, "InputEvent"), PropertyInfo(Variant::VECTOR3, "position"), PropertyInfo(Variant::VECTOR3, "normal"), PropertyInfo(Variant::INT, "shape_idx"))); 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"); + + 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) { @@ -180,7 +501,11 @@ void CollisionObject3D::shape_owner_set_disabled(uint32_t p_owner, bool p_disabl ERR_FAIL_COND(!shapes.has(p_owner)); ShapeData &sd = shapes[p_owner]; + if (sd.disabled == p_disabled) { + return; + } sd.disabled = p_disabled; + for (int i = 0; i < sd.shapes.size(); i++) { if (area) { PhysicsServer3D::get_singleton()->area_set_shape_disabled(rid, sd.shapes[i].index, p_disabled); @@ -188,6 +513,7 @@ void CollisionObject3D::shape_owner_set_disabled(uint32_t p_owner, bool p_disabl PhysicsServer3D::get_singleton()->body_set_shape_disabled(rid, sd.shapes[i].index, p_disabled); } } + _update_shape_data(p_owner); } bool CollisionObject3D::is_shape_owner_disabled(uint32_t p_owner) const { @@ -211,7 +537,7 @@ Array CollisionObject3D::_get_shape_owners() { return ret; } -void CollisionObject3D::shape_owner_set_transform(uint32_t p_owner, const Transform &p_transform) { +void CollisionObject3D::shape_owner_set_transform(uint32_t p_owner, const Transform3D &p_transform) { ERR_FAIL_COND(!shapes.has(p_owner)); ShapeData &sd = shapes[p_owner]; @@ -223,10 +549,11 @@ void CollisionObject3D::shape_owner_set_transform(uint32_t p_owner, const Transf PhysicsServer3D::get_singleton()->body_set_shape_transform(rid, sd.shapes[i].index, p_transform); } } -} -Transform CollisionObject3D::shape_owner_get_transform(uint32_t p_owner) const { - ERR_FAIL_COND_V(!shapes.has(p_owner), Transform()); + _update_shape_data(p_owner); +} +Transform3D CollisionObject3D::shape_owner_get_transform(uint32_t p_owner) const { + ERR_FAIL_COND_V(!shapes.has(p_owner), Transform3D()); return shapes[p_owner].xform; } @@ -245,6 +572,7 @@ void CollisionObject3D::shape_owner_add_shape(uint32_t p_owner, const Ref<Shape3 ShapeData::ShapeBase s; s.index = total_subshapes; s.shape = p_shape; + if (area) { PhysicsServer3D::get_singleton()->area_add_shape(rid, p_shape->get_rid(), sd.xform, sd.disabled); } else { @@ -253,6 +581,8 @@ void CollisionObject3D::shape_owner_add_shape(uint32_t p_owner, const Ref<Shape3 sd.shapes.push_back(s); total_subshapes++; + + _update_shape_data(p_owner); } int CollisionObject3D::shape_owner_get_shape_count(uint32_t p_owner) const { @@ -279,13 +609,23 @@ void CollisionObject3D::shape_owner_remove_shape(uint32_t p_owner, int p_shape) ERR_FAIL_COND(!shapes.has(p_owner)); ERR_FAIL_INDEX(p_shape, shapes[p_owner].shapes.size()); - int index_to_remove = shapes[p_owner].shapes[p_shape].index; + ShapeData::ShapeBase &s = shapes[p_owner].shapes.write[p_shape]; + int index_to_remove = s.index; + if (area) { PhysicsServer3D::get_singleton()->area_remove_shape(rid, index_to_remove); } else { PhysicsServer3D::get_singleton()->body_remove_shape(rid, index_to_remove); } + if (s.debug_shape.is_valid()) { + RS::get_singleton()->free(s.debug_shape); + if (s.shape.is_valid() && s.shape->is_connected("changed", callable_mp(this, &CollisionObject3D::_shape_changed))) { + s.shape->disconnect("changed", callable_mp(this, &CollisionObject3D::_shape_changed)); + } + --debug_shapes_count; + } + shapes[p_owner].shapes.remove(p_shape); for (Map<uint32_t, ShapeData>::Element *E = shapes.front(); E; E = E->next()) { @@ -325,17 +665,14 @@ uint32_t CollisionObject3D::shape_find_owner(int p_shape_index) const { CollisionObject3D::CollisionObject3D(RID p_rid, bool p_area) { rid = p_rid; area = p_area; - capture_input_on_drag = false; - ray_pickable = true; set_notify_transform(true); - total_subshapes = 0; if (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) { @@ -346,22 +683,17 @@ bool CollisionObject3D::get_capture_input_on_drag() const { return capture_input_on_drag; } -String CollisionObject3D::get_configuration_warning() const { - String warning = Node3D::get_configuration_warning(); +TypedArray<String> CollisionObject3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); - if (shapes.empty()) { - if (!warning.empty()) { - warning += "\n\n"; - } - warning += TTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape3D or CollisionPolygon3D as a child to define its shape."); + if (shapes.is_empty()) { + warnings.push_back(TTR("This node has no shape, so it can't collide or interact with other objects.\nConsider adding a CollisionShape3D or CollisionPolygon3D as a child to define its shape.")); } - return warning; + return warnings; } CollisionObject3D::CollisionObject3D() { - capture_input_on_drag = false; - ray_pickable = true; set_notify_transform(true); //owner= diff --git a/scene/3d/collision_object_3d.h b/scene/3d/collision_object_3d.h index 39e7df40a8..1c7e205888 100644 --- a/scene/3d/collision_object_3d.h +++ b/scene/3d/collision_object_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,60 +31,110 @@ #ifndef COLLISION_OBJECT_3D_H #define COLLISION_OBJECT_3D_H +#include "scene/3d/camera_3d.h" #include "scene/3d/node_3d.h" -#include "scene/resources/shape_3d.h" class CollisionObject3D : public Node3D { GDCLASS(CollisionObject3D, Node3D); - bool area; +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; + + bool area = false; RID rid; + DisableMode disable_mode = DISABLE_MODE_REMOVE; + + PhysicsServer3D::BodyMode body_mode = PhysicsServer3D::BODY_MODE_STATIC; + struct ShapeData { - Object *owner; - Transform xform; + Object *owner = nullptr; + Transform3D xform; struct ShapeBase { + RID debug_shape; Ref<Shape3D> shape; - int index; + int index = 0; }; Vector<ShapeBase> shapes; - bool disabled; - - ShapeData() { - disabled = false; - owner = nullptr; - } + bool disabled = false; }; - int total_subshapes; + int total_subshapes = 0; Map<uint32_t, ShapeData> shapes; - bool capture_input_on_drag; - bool ray_pickable; + bool only_update_transform_changes = false; // This is used for sync to physics. + + bool capture_input_on_drag = false; + bool ray_pickable = true; + + Set<uint32_t> debug_shapes_to_update; + int debug_shapes_count = 0; + Transform3D debug_shape_old_transform; void _update_pickable(); + bool _are_collision_shapes_visible(); + void _update_shape_data(uint32_t p_owner); + void _shape_changed(const Ref<Shape3D> &p_shape); + void _update_debug_shapes(); + void _clear_debug_shapes(); + + void _apply_disabled(); + void _apply_enabled(); + protected: CollisionObject3D(RID p_rid, bool p_area); void _notification(int p_what); static void _bind_methods(); + + void _on_transform_changed(); + friend class Viewport; - virtual void _input_event(Node *p_camera, const Ref<InputEvent> &p_input_event, const Vector3 &p_pos, const Vector3 &p_normal, int p_shape); + virtual void _input_event_call(Camera3D *p_camera, const Ref<InputEvent> &p_input_event, const Vector3 &p_pos, const Vector3 &p_normal, int p_shape); virtual void _mouse_enter(); virtual void _mouse_exit(); + void set_body_mode(PhysicsServer3D::BodyMode p_mode); + + void set_only_update_transform_changes(bool p_enable); + bool is_only_update_transform_changes_enabled() const; + + GDVIRTUAL5(_input_event, Camera3D *, Ref<InputEvent>, Vector3, Vector3, int) public: + void set_collision_layer(uint32_t p_layer); + uint32_t get_collision_layer() const; + + void set_collision_mask(uint32_t p_mask); + uint32_t get_collision_mask() const; + + void set_collision_layer_value(int p_layer_number, bool p_value); + bool get_collision_layer_value(int p_layer_number) const; + + void set_collision_mask_value(int p_layer_number, bool p_value); + bool get_collision_mask_value(int p_layer_number) 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); Array _get_shape_owners(); - void shape_owner_set_transform(uint32_t p_owner, const Transform &p_transform); - Transform shape_owner_get_transform(uint32_t p_owner) const; + void shape_owner_set_transform(uint32_t p_owner, const Transform3D &p_transform); + Transform3D shape_owner_get_transform(uint32_t p_owner) const; Object *shape_owner_get_owner(uint32_t p_owner) const; void shape_owner_set_disabled(uint32_t p_owner, bool p_disabled); @@ -108,10 +158,12 @@ public: _FORCE_INLINE_ RID get_rid() const { return rid; } - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; CollisionObject3D(); ~CollisionObject3D(); }; +VARIANT_ENUM_CAST(CollisionObject3D::DisableMode); + #endif // COLLISION_OBJECT__H diff --git a/scene/3d/collision_polygon_3d.cpp b/scene/3d/collision_polygon_3d.cpp index b8a4ab74ee..d5835586f9 100644 --- a/scene/3d/collision_polygon_3d.cpp +++ b/scene/3d/collision_polygon_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,7 +32,6 @@ #include "collision_object_3d.h" #include "core/math/geometry_2d.h" -#include "scene/resources/concave_polygon_shape_3d.h" #include "scene/resources/convex_polygon_shape_3d.h" void CollisionPolygon3D::_build_polygon() { @@ -70,6 +69,7 @@ void CollisionPolygon3D::_build_polygon() { } convex->set_points(cp); + convex->set_margin(margin); parent->shape_owner_add_shape(owner_id, convex); parent->shape_owner_set_disabled(owner_id, disabled); } @@ -120,8 +120,8 @@ void CollisionPolygon3D::set_polygon(const Vector<Point2> &p_polygon) { if (parent) { _build_polygon(); } - update_configuration_warning(); - update_gizmo(); + update_configuration_warnings(); + update_gizmos(); } Vector<Point2> CollisionPolygon3D::get_polygon() const { @@ -132,19 +132,19 @@ AABB CollisionPolygon3D::get_item_rect() const { return aabb; } -void CollisionPolygon3D::set_depth(float p_depth) { +void CollisionPolygon3D::set_depth(real_t p_depth) { depth = p_depth; _build_polygon(); - update_gizmo(); + update_gizmos(); } -float CollisionPolygon3D::get_depth() const { +real_t CollisionPolygon3D::get_depth() const { return depth; } void CollisionPolygon3D::set_disabled(bool p_disabled) { disabled = p_disabled; - update_gizmo(); + update_gizmos(); if (parent) { parent->shape_owner_set_disabled(owner_id, p_disabled); @@ -155,24 +155,29 @@ bool CollisionPolygon3D::is_disabled() const { return disabled; } -String CollisionPolygon3D::get_configuration_warning() const { - String warning = Node3D::get_configuration_warning(); +real_t CollisionPolygon3D::get_margin() const { + return margin; +} + +void CollisionPolygon3D::set_margin(real_t p_margin) { + margin = p_margin; + if (parent) { + _build_polygon(); + } +} + +TypedArray<String> CollisionPolygon3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (!Object::cast_to<CollisionObject3D>(get_parent())) { - if (!warning.empty()) { - warning += "\n\n"; - } - warning += TTR("CollisionPolygon3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidBody3D, KinematicBody3D, etc. to give them a shape."); + warnings.push_back(TTR("CollisionPolygon3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidBody3D, CharacterBody3D, etc. to give them a shape.")); } - if (polygon.empty()) { - if (!warning.empty()) { - warning += "\n\n"; - } - warning += TTR("An empty CollisionPolygon3D has no effect on collision."); + if (polygon.is_empty()) { + warnings.push_back(TTR("An empty CollisionPolygon3D has no effect on collision.")); } - return warning; + return warnings; } bool CollisionPolygon3D::_is_editable_3d_polygon() const { @@ -189,18 +194,17 @@ void CollisionPolygon3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_disabled", "disabled"), &CollisionPolygon3D::set_disabled); ClassDB::bind_method(D_METHOD("is_disabled"), &CollisionPolygon3D::is_disabled); + ClassDB::bind_method(D_METHOD("set_margin", "margin"), &CollisionPolygon3D::set_margin); + ClassDB::bind_method(D_METHOD("get_margin"), &CollisionPolygon3D::get_margin); + ClassDB::bind_method(D_METHOD("_is_editable_3d_polygon"), &CollisionPolygon3D::_is_editable_3d_polygon); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "depth"), "set_depth", "get_depth"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR2_ARRAY, "polygon"), "set_polygon", "get_polygon"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin", PROPERTY_HINT_RANGE, "0.001,10,0.001"), "set_margin", "get_margin"); } CollisionPolygon3D::CollisionPolygon3D() { - aabb = AABB(Vector3(-1, -1, -1), Vector3(2, 2, 2)); - depth = 1.0; set_notify_local_transform(true); - parent = nullptr; - owner_id = 0; - disabled = false; } diff --git a/scene/3d/collision_polygon_3d.h b/scene/3d/collision_polygon_3d.h index bab85b6011..73b8a8e0e3 100644 --- a/scene/3d/collision_polygon_3d.h +++ b/scene/3d/collision_polygon_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -37,16 +37,17 @@ class CollisionObject3D; class CollisionPolygon3D : public Node3D { GDCLASS(CollisionPolygon3D, Node3D); + real_t margin = 0.04; protected: - float depth; - AABB aabb; + real_t depth = 1.0; + AABB aabb = AABB(Vector3(-1, -1, -1), Vector3(2, 2, 2)); Vector<Point2> polygon; - uint32_t owner_id; - CollisionObject3D *parent; + uint32_t owner_id = 0; + CollisionObject3D *parent = nullptr; - bool disabled; + bool disabled = false; void _build_polygon(); @@ -59,8 +60,8 @@ protected: static void _bind_methods(); public: - void set_depth(float p_depth); - float get_depth() const; + void set_depth(real_t p_depth); + real_t get_depth() const; void set_polygon(const Vector<Point2> &p_polygon); Vector<Point2> get_polygon() const; @@ -70,7 +71,10 @@ public: virtual AABB get_item_rect() const; - String get_configuration_warning() const override; + real_t get_margin() const; + void set_margin(real_t p_margin); + + TypedArray<String> get_configuration_warnings() const override; CollisionPolygon3D(); }; diff --git a/scene/3d/collision_shape_3d.cpp b/scene/3d/collision_shape_3d.cpp index e1c691b89a..d4668a24f2 100644 --- a/scene/3d/collision_shape_3d.cpp +++ b/scene/3d/collision_shape_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,19 +30,10 @@ #include "collision_shape_3d.h" -#include "core/math/quick_hull.h" #include "mesh_instance_3d.h" #include "physics_body_3d.h" -#include "scene/resources/box_shape_3d.h" -#include "scene/resources/capsule_shape_3d.h" #include "scene/resources/concave_polygon_shape_3d.h" #include "scene/resources/convex_polygon_shape_3d.h" -#include "scene/resources/ray_shape_3d.h" -#include "scene/resources/sphere_shape_3d.h" -#include "scene/resources/world_margin_shape_3d.h" -#include "servers/rendering_server.h" - -//TODO: Implement CylinderShape and HeightMapShape? void CollisionShape3D::make_convex_from_siblings() { Node *p = get_parent(); @@ -60,7 +51,7 @@ void CollisionShape3D::make_convex_from_siblings() { if (m.is_valid()) { for (int j = 0; j < m->get_surface_count(); j++) { Array a = m->surface_get_arrays(j); - if (!a.empty()) { + if (!a.is_empty()) { Vector<Vector3> v = a[RenderingServer::ARRAY_VERTEX]; for (int k = 0; k < v.size(); k++) { vertices.append(mi->get_transform().xform(v[k])); @@ -100,9 +91,6 @@ void CollisionShape3D::_notification(int p_what) { if (parent) { _update_in_shape_owner(); } - if (get_tree()->is_debugging_collisions_hint()) { - _update_debug_shape(); - } } break; case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { if (parent) { @@ -120,37 +108,28 @@ void CollisionShape3D::_notification(int p_what) { } void CollisionShape3D::resource_changed(RES res) { - update_gizmo(); + update_gizmos(); } -String CollisionShape3D::get_configuration_warning() const { - String warning = Node3D::get_configuration_warning(); +TypedArray<String> CollisionShape3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (!Object::cast_to<CollisionObject3D>(get_parent())) { - if (!warning.empty()) { - warning += "\n\n"; - } - warning += TTR("CollisionShape3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidBody3D, KinematicBody3D, etc. to give them a shape."); + warnings.push_back(TTR("CollisionShape3D only serves to provide a collision shape to a CollisionObject3D derived node. Please only use it as a child of Area3D, StaticBody3D, RigidBody3D, CharacterBody3D, etc. to give them a shape.")); } if (!shape.is_valid()) { - if (!warning.empty()) { - warning += "\n\n"; - } - warning += TTR("A shape must be provided for CollisionShape3D to function. Please create a shape resource for it."); + warnings.push_back(TTR("A shape must be provided for CollisionShape3D to function. Please create a shape resource for it.")); } if (shape.is_valid() && Object::cast_to<RigidBody3D>(get_parent()) && Object::cast_to<ConcavePolygonShape3D>(*shape) && Object::cast_to<RigidBody3D>(get_parent())->get_mode() != RigidBody3D::MODE_STATIC) { - if (!warning.empty()) { - warning += "\n\n"; - } - warning += TTR("ConcavePolygonShape3D doesn't support RigidBody3D in another mode than static."); + warnings.push_back(TTR("ConcavePolygonShape3D doesn't support RigidBody3D in another mode than static.")); } - return warning; + return warnings; } void CollisionShape3D::_bind_methods() { @@ -163,23 +142,22 @@ void CollisionShape3D::_bind_methods() { ClassDB::bind_method(D_METHOD("make_convex_from_siblings"), &CollisionShape3D::make_convex_from_siblings); ClassDB::set_method_flags("CollisionShape3D", "make_convex_from_siblings", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); - ClassDB::bind_method(D_METHOD("_update_debug_shape"), &CollisionShape3D::_update_debug_shape); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "shape", PROPERTY_HINT_RESOURCE_TYPE, "Shape3D"), "set_shape", "get_shape"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "disabled"), "set_disabled", "is_disabled"); } void CollisionShape3D::set_shape(const Ref<Shape3D> &p_shape) { + if (p_shape == shape) { + return; + } if (!shape.is_null()) { shape->unregister_owner(this); - shape->disconnect("changed", callable_mp(this, &CollisionShape3D::_shape_changed)); } shape = p_shape; if (!shape.is_null()) { shape->register_owner(this); - shape->connect("changed", callable_mp(this, &CollisionShape3D::_shape_changed)); } - update_gizmo(); + update_gizmos(); if (parent) { parent->shape_owner_clear_shapes(owner_id); if (shape.is_valid()) { @@ -187,10 +165,11 @@ void CollisionShape3D::set_shape(const Ref<Shape3D> &p_shape) { } } - if (is_inside_tree()) { - _shape_changed(); + if (is_inside_tree() && parent) { + // If this is a heightfield shape our center may have changed + _update_in_shape_owner(true); } - update_configuration_warning(); + update_configuration_warnings(); } Ref<Shape3D> CollisionShape3D::get_shape() const { @@ -199,7 +178,7 @@ Ref<Shape3D> CollisionShape3D::get_shape() const { void CollisionShape3D::set_disabled(bool p_disabled) { disabled = p_disabled; - update_gizmo(); + update_gizmos(); if (parent) { parent->shape_owner_set_disabled(owner_id, p_disabled); } @@ -211,10 +190,6 @@ bool CollisionShape3D::is_disabled() const { CollisionShape3D::CollisionShape3D() { //indicator = RenderingServer::get_singleton()->mesh_create(); - disabled = false; - debug_shape = nullptr; - parent = nullptr; - owner_id = 0; set_notify_local_transform(true); } @@ -224,35 +199,3 @@ CollisionShape3D::~CollisionShape3D() { } //RenderingServer::get_singleton()->free(indicator); } - -void CollisionShape3D::_update_debug_shape() { - debug_shape_dirty = false; - - if (debug_shape) { - debug_shape->queue_delete(); - debug_shape = nullptr; - } - - Ref<Shape3D> s = get_shape(); - if (s.is_null()) { - return; - } - - Ref<Mesh> mesh = s->get_debug_mesh(); - MeshInstance3D *mi = memnew(MeshInstance3D); - mi->set_mesh(mesh); - add_child(mi); - debug_shape = mi; -} - -void CollisionShape3D::_shape_changed() { - // If this is a heightfield shape our center may have changed - if (parent) { - _update_in_shape_owner(true); - } - - if (is_inside_tree() && get_tree()->is_debugging_collisions_hint() && !debug_shape_dirty) { - debug_shape_dirty = true; - call_deferred("_update_debug_shape"); - } -} diff --git a/scene/3d/collision_shape_3d.h b/scene/3d/collision_shape_3d.h index 35f40d27b1..cb7fe21eae 100644 --- a/scene/3d/collision_shape_3d.h +++ b/scene/3d/collision_shape_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,6 +33,7 @@ #include "scene/3d/node_3d.h" #include "scene/resources/shape_3d.h" + class CollisionObject3D; class CollisionShape3D : public Node3D { GDCLASS(CollisionShape3D, Node3D); @@ -40,19 +41,13 @@ class CollisionShape3D : public Node3D { Ref<Shape3D> shape; - uint32_t owner_id; - CollisionObject3D *parent; - - Node *debug_shape; - bool debug_shape_dirty; + uint32_t owner_id = 0; + CollisionObject3D *parent = nullptr; void resource_changed(RES res); - bool disabled; + bool disabled = false; protected: - void _update_debug_shape(); - void _shape_changed(); - void _update_in_shape_owner(bool p_xform_only = false); protected: @@ -68,7 +63,7 @@ public: void set_disabled(bool p_disabled); bool is_disabled() const; - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; CollisionShape3D(); ~CollisionShape3D(); diff --git a/scene/3d/cpu_particles_3d.cpp b/scene/3d/cpu_particles_3d.cpp index c977e0d4aa..48ef41e015 100644 --- a/scene/3d/cpu_particles_3d.cpp +++ b/scene/3d/cpu_particles_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,14 +32,14 @@ #include "scene/3d/camera_3d.h" #include "scene/3d/gpu_particles_3d.h" +#include "scene/main/viewport.h" #include "scene/resources/particles_material.h" -#include "servers/rendering_server.h" AABB CPUParticles3D::get_aabb() const { return AABB(); } -Vector<Face3> CPUParticles3D::get_faces(uint32_t p_usage_flags) const { +Vector<Face3> CPUParticles3D::get_faces(uint32_t p_usage_particle_flags) const { return Vector<Face3>(); } @@ -73,12 +73,12 @@ void CPUParticles3D::set_amount(int p_amount) { } particle_data.resize((12 + 4 + 4) * p_amount); - RS::get_singleton()->multimesh_allocate(multimesh, p_amount, RS::MULTIMESH_TRANSFORM_3D, true, true); + RS::get_singleton()->multimesh_allocate_data(multimesh, p_amount, RS::MULTIMESH_TRANSFORM_3D, true, true); particle_order.resize(p_amount); } -void CPUParticles3D::set_lifetime(float p_lifetime) { +void CPUParticles3D::set_lifetime(double p_lifetime) { ERR_FAIL_COND_MSG(p_lifetime <= 0, "Particles lifetime must be greater than 0."); lifetime = p_lifetime; } @@ -87,19 +87,19 @@ void CPUParticles3D::set_one_shot(bool p_one_shot) { one_shot = p_one_shot; } -void CPUParticles3D::set_pre_process_time(float p_time) { +void CPUParticles3D::set_pre_process_time(double p_time) { pre_process_time = p_time; } -void CPUParticles3D::set_explosiveness_ratio(float p_ratio) { +void CPUParticles3D::set_explosiveness_ratio(real_t p_ratio) { explosiveness_ratio = p_ratio; } -void CPUParticles3D::set_randomness_ratio(float p_ratio) { +void CPUParticles3D::set_randomness_ratio(real_t p_ratio) { randomness_ratio = p_ratio; } -void CPUParticles3D::set_lifetime_randomness(float p_random) { +void CPUParticles3D::set_lifetime_randomness(double p_random) { lifetime_randomness = p_random; } @@ -107,7 +107,7 @@ void CPUParticles3D::set_use_local_coordinates(bool p_enable) { local_coords = p_enable; } -void CPUParticles3D::set_speed_scale(float p_scale) { +void CPUParticles3D::set_speed_scale(double p_scale) { speed_scale = p_scale; } @@ -119,7 +119,7 @@ int CPUParticles3D::get_amount() const { return particles.size(); } -float CPUParticles3D::get_lifetime() const { +double CPUParticles3D::get_lifetime() const { return lifetime; } @@ -127,19 +127,19 @@ bool CPUParticles3D::get_one_shot() const { return one_shot; } -float CPUParticles3D::get_pre_process_time() const { +double CPUParticles3D::get_pre_process_time() const { return pre_process_time; } -float CPUParticles3D::get_explosiveness_ratio() const { +real_t CPUParticles3D::get_explosiveness_ratio() const { return explosiveness_ratio; } -float CPUParticles3D::get_randomness_ratio() const { +real_t CPUParticles3D::get_randomness_ratio() const { return randomness_ratio; } -float CPUParticles3D::get_lifetime_randomness() const { +double CPUParticles3D::get_lifetime_randomness() const { return lifetime_randomness; } @@ -147,11 +147,12 @@ bool CPUParticles3D::get_use_local_coordinates() const { return local_coords; } -float CPUParticles3D::get_speed_scale() const { +double CPUParticles3D::get_speed_scale() const { return speed_scale; } void CPUParticles3D::set_draw_order(DrawOrder p_order) { + ERR_FAIL_INDEX(p_order, DRAW_ORDER_MAX); draw_order = p_order; } @@ -188,8 +189,8 @@ bool CPUParticles3D::get_fractional_delta() const { return fractional_delta; } -String CPUParticles3D::get_configuration_warning() const { - String warnings = GeometryInstance3D::get_configuration_warning(); +TypedArray<String> CPUParticles3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); bool mesh_found = false; bool anim_material_found = false; @@ -208,18 +209,12 @@ String CPUParticles3D::get_configuration_warning() const { anim_material_found = anim_material_found || (spat && spat->get_billboard_mode() == StandardMaterial3D::BILLBOARD_PARTICLES); if (!mesh_found) { - if (warnings != String()) { - warnings += "\n"; - } - warnings += "- " + TTR("Nothing is visible because no mesh has been assigned."); + warnings.push_back(TTR("Nothing is visible because no mesh has been assigned.")); } - if (!anim_material_found && (get_param(PARAM_ANIM_SPEED) != 0.0 || get_param(PARAM_ANIM_OFFSET) != 0.0 || + if (!anim_material_found && (get_param_max(PARAM_ANIM_SPEED) != 0.0 || get_param_max(PARAM_ANIM_OFFSET) != 0.0 || get_param_curve(PARAM_ANIM_SPEED).is_valid() || get_param_curve(PARAM_ANIM_OFFSET).is_valid())) { - if (warnings != String()) { - warnings += "\n"; - } - warnings += "- " + TTR("CPUParticles3D animation requires the usage of a StandardMaterial3D whose Billboard Mode is set to \"Particle Billboard\"."); + warnings.push_back(TTR("CPUParticles3D animation requires the usage of a StandardMaterial3D whose Billboard Mode is set to \"Particle Billboard\".")); } return warnings; @@ -252,47 +247,52 @@ Vector3 CPUParticles3D::get_direction() const { return direction; } -void CPUParticles3D::set_spread(float p_spread) { +void CPUParticles3D::set_spread(real_t p_spread) { spread = p_spread; } -float CPUParticles3D::get_spread() const { +real_t CPUParticles3D::get_spread() const { return spread; } -void CPUParticles3D::set_flatness(float p_flatness) { +void CPUParticles3D::set_flatness(real_t p_flatness) { flatness = p_flatness; } -float CPUParticles3D::get_flatness() const { +real_t CPUParticles3D::get_flatness() const { return flatness; } -void CPUParticles3D::set_param(Parameter p_param, float p_value) { +void CPUParticles3D::set_param_min(Parameter p_param, real_t p_value) { ERR_FAIL_INDEX(p_param, PARAM_MAX); - parameters[p_param] = p_value; + parameters_min[p_param] = p_value; + if (parameters_min[p_param] > parameters_max[p_param]) { + set_param_max(p_param, p_value); + } } -float CPUParticles3D::get_param(Parameter p_param) const { +real_t CPUParticles3D::get_param_min(Parameter p_param) const { ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); - return parameters[p_param]; + return parameters_min[p_param]; } -void CPUParticles3D::set_param_randomness(Parameter p_param, float p_value) { +void CPUParticles3D::set_param_max(Parameter p_param, real_t p_value) { ERR_FAIL_INDEX(p_param, PARAM_MAX); - - randomness[p_param] = p_value; + parameters_max[p_param] = p_value; + if (parameters_min[p_param] > parameters_max[p_param]) { + set_param_min(p_param, p_value); + } } -float CPUParticles3D::get_param_randomness(Parameter p_param) const { +real_t CPUParticles3D::get_param_max(Parameter p_param) const { ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); - return randomness[p_param]; + return parameters_max[p_param]; } -static void _adjust_curve_range(const Ref<Curve> &p_curve, float p_min, float p_max) { +static void _adjust_curve_range(const Ref<Curve> &p_curve, real_t p_min, real_t p_max) { Ref<Curve> curve = p_curve; if (!curve.is_valid()) { return; @@ -368,17 +368,17 @@ Ref<Gradient> CPUParticles3D::get_color_ramp() const { return color_ramp; } -void CPUParticles3D::set_particle_flag(Flags p_flag, bool p_enable) { - ERR_FAIL_INDEX(p_flag, FLAG_MAX); - flags[p_flag] = p_enable; - if (p_flag == FLAG_DISABLE_Z) { - _change_notify(); +void CPUParticles3D::set_particle_flag(ParticleFlags p_particle_flag, bool p_enable) { + ERR_FAIL_INDEX(p_particle_flag, PARTICLE_FLAG_MAX); + particle_flags[p_particle_flag] = p_enable; + if (p_particle_flag == PARTICLE_FLAG_DISABLE_Z) { + notify_property_list_changed(); } } -bool CPUParticles3D::get_particle_flag(Flags p_flag) const { - ERR_FAIL_INDEX_V(p_flag, FLAG_MAX, false); - return flags[p_flag]; +bool CPUParticles3D::get_particle_flag(ParticleFlags p_particle_flag) const { + ERR_FAIL_INDEX_V(p_particle_flag, PARTICLE_FLAG_MAX, false); + return particle_flags[p_particle_flag]; } void CPUParticles3D::set_emission_shape(EmissionShape p_shape) { @@ -386,7 +386,7 @@ void CPUParticles3D::set_emission_shape(EmissionShape p_shape) { emission_shape = p_shape; } -void CPUParticles3D::set_emission_sphere_radius(float p_radius) { +void CPUParticles3D::set_emission_sphere_radius(real_t p_radius) { emission_sphere_radius = p_radius; } @@ -406,7 +406,40 @@ void CPUParticles3D::set_emission_colors(const Vector<Color> &p_colors) { emission_colors = p_colors; } -float CPUParticles3D::get_emission_sphere_radius() const { +void CPUParticles3D::set_emission_ring_axis(Vector3 p_axis) { + emission_ring_axis = p_axis; +} + +void CPUParticles3D::set_emission_ring_height(real_t p_height) { + emission_ring_height = p_height; +} + +void CPUParticles3D::set_emission_ring_radius(real_t p_radius) { + emission_ring_radius = p_radius; +} + +void CPUParticles3D::set_emission_ring_inner_radius(real_t p_radius) { + emission_ring_inner_radius = p_radius; +} + +void CPUParticles3D::set_scale_curve_x(Ref<Curve> p_scale_curve) { + scale_curve_x = p_scale_curve; +} + +void CPUParticles3D::set_scale_curve_y(Ref<Curve> p_scale_curve) { + scale_curve_y = p_scale_curve; +} + +void CPUParticles3D::set_scale_curve_z(Ref<Curve> p_scale_curve) { + scale_curve_z = p_scale_curve; +} + +void CPUParticles3D::set_split_scale(bool p_split_scale) { + split_scale = p_split_scale; + notify_property_list_changed(); +} + +real_t CPUParticles3D::get_emission_sphere_radius() const { return emission_sphere_radius; } @@ -426,6 +459,22 @@ Vector<Color> CPUParticles3D::get_emission_colors() const { return emission_colors; } +Vector3 CPUParticles3D::get_emission_ring_axis() const { + return emission_ring_axis; +} + +real_t CPUParticles3D::get_emission_ring_height() const { + return emission_ring_height; +} + +real_t CPUParticles3D::get_emission_ring_radius() const { + return emission_ring_radius; +} + +real_t CPUParticles3D::get_emission_ring_inner_radius() const { + return emission_ring_inner_radius; +} + CPUParticles3D::EmissionShape CPUParticles3D::get_emission_shape() const { return emission_shape; } @@ -438,29 +487,49 @@ Vector3 CPUParticles3D::get_gravity() const { return gravity; } -void CPUParticles3D::_validate_property(PropertyInfo &property) const { - if (property.name == "color" && color_ramp.is_valid()) { - property.usage = 0; - } +Ref<Curve> CPUParticles3D::get_scale_curve_x() const { + return scale_curve_x; +} + +Ref<Curve> CPUParticles3D::get_scale_curve_y() const { + return scale_curve_y; +} + +Ref<Curve> CPUParticles3D::get_scale_curve_z() const { + return scale_curve_z; +} + +bool CPUParticles3D::get_split_scale() { + return split_scale; +} +void CPUParticles3D::_validate_property(PropertyInfo &property) const { if (property.name == "emission_sphere_radius" && emission_shape != EMISSION_SHAPE_SPHERE) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "emission_box_extents" && emission_shape != EMISSION_SHAPE_BOX) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } - if ((property.name == "emission_point_texture" || property.name == "emission_color_texture") && (emission_shape < EMISSION_SHAPE_POINTS)) { - property.usage = 0; + if ((property.name == "emission_point_texture" || property.name == "emission_color_texture" || property.name == "emission_points") && (emission_shape != EMISSION_SHAPE_POINTS && (emission_shape != EMISSION_SHAPE_DIRECTED_POINTS))) { + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "emission_normals" && emission_shape != EMISSION_SHAPE_DIRECTED_POINTS) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; + } + + if (property.name.begins_with("emission_ring_") && emission_shape != EMISSION_SHAPE_RING) { + property.usage = PROPERTY_USAGE_NONE; + } + + if (property.name.begins_with("orbit_") && !particle_flags[PARTICLE_FLAG_DISABLE_Z]) { + property.usage = PROPERTY_USAGE_NONE; } - if (property.name.begins_with("orbit_") && !flags[FLAG_DISABLE_Z]) { - property.usage = 0; + if (property.name.begins_with("scale_curve_") && !split_scale) { + property.usage = PROPERTY_USAGE_NONE; } } @@ -471,7 +540,7 @@ static uint32_t idhash(uint32_t x) { return x; } -static float rand_from_seed(uint32_t &seed) { +static real_t rand_from_seed(uint32_t &seed) { int k; int s = int(seed); if (s == 0) { @@ -483,7 +552,7 @@ static float rand_from_seed(uint32_t &seed) { s += 2147483647; } seed = uint32_t(s); - return float(seed % uint32_t(65536)) / 65535.0; + return (seed % uint32_t(65536)) / 65535.0; } void CPUParticles3D::_update_internal() { @@ -492,7 +561,7 @@ void CPUParticles3D::_update_internal() { return; } - float delta = get_process_delta_time(); + double delta = get_process_delta_time(); if (emitting) { inactive_time = 0; } else { @@ -514,14 +583,14 @@ void CPUParticles3D::_update_internal() { bool processed = false; if (time == 0 && pre_process_time > 0.0) { - float frame_time; + double frame_time; if (fixed_fps > 0) { frame_time = 1.0 / fixed_fps; } else { frame_time = 1.0 / 30.0; } - float todo = pre_process_time; + double todo = pre_process_time; while (todo >= 0) { _particles_process(frame_time); @@ -531,16 +600,16 @@ void CPUParticles3D::_update_internal() { } if (fixed_fps > 0) { - float frame_time = 1.0 / fixed_fps; - float decr = frame_time; + double frame_time = 1.0 / fixed_fps; + double decr = frame_time; - float ldelta = delta; + double ldelta = delta; if (ldelta > 0.1) { //avoid recursive stalls if fps goes below 10 ldelta = 0.1; } else if (ldelta <= 0.0) { //unlikely but.. ldelta = 0.001; } - float todo = frame_remainder + ldelta; + double todo = frame_remainder + ldelta; while (todo >= frame_time) { _particles_process(frame_time); @@ -560,7 +629,7 @@ void CPUParticles3D::_update_internal() { } } -void CPUParticles3D::_particles_process(float p_delta) { +void CPUParticles3D::_particles_process(double p_delta) { p_delta *= speed_scale; int pcount = particles.size(); @@ -568,25 +637,25 @@ void CPUParticles3D::_particles_process(float p_delta) { Particle *parray = w; - float prev_time = time; + double prev_time = time; time += p_delta; if (time > lifetime) { time = Math::fmod(time, lifetime); cycle++; if (one_shot && cycle > 0) { set_emitting(false); - _change_notify(); + notify_property_list_changed(); } } - Transform emission_xform; + Transform3D emission_xform; Basis velocity_xform; if (!local_coords) { emission_xform = get_global_transform(); velocity_xform = emission_xform.basis; } - float system_phase = time / lifetime; + double system_phase = time / lifetime; for (int i = 0; i < pcount; i++) { Particle &p = parray[i]; @@ -595,12 +664,12 @@ void CPUParticles3D::_particles_process(float p_delta) { continue; } - float local_delta = p_delta; + double local_delta = p_delta; // The phase is a ratio between 0 (birth) and 1 (end of life) for each particle. // While we use time in tests later on, for randomness we use the phase as done in the // original shader code, and we later multiply by lifetime to get the time. - float restart_phase = float(i) / float(pcount); + double restart_phase = double(i) / double(pcount); if (randomness_ratio > 0.0) { uint32_t seed = cycle; @@ -609,12 +678,12 @@ void CPUParticles3D::_particles_process(float p_delta) { } seed *= uint32_t(pcount); seed += uint32_t(i); - float random = float(idhash(seed) % uint32_t(65536)) / 65536.0; - restart_phase += randomness_ratio * random * 1.0 / float(pcount); + double random = double(idhash(seed) % uint32_t(65536)) / 65536.0; + restart_phase += randomness_ratio * random * 1.0 / double(pcount); } restart_phase *= (1.0 - explosiveness_ratio); - float restart_time = restart_phase * lifetime; + double restart_time = restart_phase * lifetime; bool restart = false; if (time > prev_time) { @@ -646,6 +715,8 @@ void CPUParticles3D::_particles_process(float p_delta) { restart = true; } + float tv = 0.0; + if (restart) { if (!emitting) { p.active = false; @@ -653,19 +724,19 @@ void CPUParticles3D::_particles_process(float p_delta) { } p.active = true; - /*float tex_linear_velocity = 0; + /*real_t tex_linear_velocity = 0; if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(0); }*/ - float tex_angle = 0.0; + real_t tex_angle = 0.0; if (curve_parameters[PARAM_ANGLE].is_valid()) { - tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(0); + tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(tv); } - float tex_anim_offset = 0.0; + real_t tex_anim_offset = 0.0; if (curve_parameters[PARAM_ANGLE].is_valid()) { - tex_anim_offset = curve_parameters[PARAM_ANGLE]->interpolate(0); + tex_anim_offset = curve_parameters[PARAM_ANGLE]->interpolate(tv); } p.seed = Math::rand(); @@ -675,28 +746,41 @@ void CPUParticles3D::_particles_process(float p_delta) { p.hue_rot_rand = Math::randf(); p.anim_offset_rand = Math::randf(); - if (flags[FLAG_DISABLE_Z]) { - float angle1_rad = Math::atan2(direction.y, direction.x) + (Math::randf() * 2.0 - 1.0) * Math_PI * spread / 180.0; + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { + real_t angle1_rad = Math::atan2(direction.y, direction.x) + Math::deg2rad((Math::randf() * 2.0 - 1.0) * spread); Vector3 rot = Vector3(Math::cos(angle1_rad), Math::sin(angle1_rad), 0.0); - p.velocity = rot * parameters[PARAM_INITIAL_LINEAR_VELOCITY] * Math::lerp(1.0f, float(Math::randf()), randomness[PARAM_INITIAL_LINEAR_VELOCITY]); + p.velocity = rot * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], Math::randf()); } else { //initiate velocity spread in 3D - float angle1_rad = Math::atan2(direction.x, direction.z) + (Math::randf() * 2.0 - 1.0) * Math_PI * spread / 180.0; - float angle2_rad = Math::atan2(direction.y, Math::abs(direction.z)) + (Math::randf() * 2.0 - 1.0) * (1.0 - flatness) * Math_PI * spread / 180.0; + real_t angle1_rad = Math::deg2rad((Math::randf() * (real_t)2.0 - (real_t)1.0) * spread); + real_t angle2_rad = Math::deg2rad((Math::randf() * (real_t)2.0 - (real_t)1.0) * ((real_t)1.0 - flatness) * spread); Vector3 direction_xz = Vector3(Math::sin(angle1_rad), 0, Math::cos(angle1_rad)); Vector3 direction_yz = Vector3(0, Math::sin(angle2_rad), Math::cos(angle2_rad)); - direction_yz.z = direction_yz.z / MAX(0.0001, Math::sqrt(ABS(direction_yz.z))); //better uniform distribution - Vector3 direction = Vector3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z); - direction.normalize(); - p.velocity = direction * parameters[PARAM_INITIAL_LINEAR_VELOCITY] * Math::lerp(1.0f, float(Math::randf()), randomness[PARAM_INITIAL_LINEAR_VELOCITY]); + Vector3 spread_direction = Vector3(direction_xz.x * direction_yz.z, direction_yz.y, direction_xz.z * direction_yz.z); + Vector3 direction_nrm = direction; + if (direction_nrm.length_squared() > 0) { + direction_nrm.normalize(); + } else { + direction_nrm = Vector3(0, 0, 1); + } + // rotate spread to direction + Vector3 binormal = Vector3(0.0, 1.0, 0.0).cross(direction_nrm); + if (binormal.length_squared() < 0.00000001) { + // direction is parallel to Y. Choose Z as the binormal. + binormal = Vector3(0.0, 0.0, 1.0); + } + binormal.normalize(); + Vector3 normal = binormal.cross(direction_nrm); + spread_direction = binormal * spread_direction.x + normal * spread_direction.y + direction_nrm * spread_direction.z; + p.velocity = spread_direction * Math::lerp(parameters_min[PARAM_INITIAL_LINEAR_VELOCITY], parameters_max[PARAM_INITIAL_LINEAR_VELOCITY], float(Math::randf())); } - float base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp(1.0f, p.angle_rand, randomness[PARAM_ANGLE]); + real_t base_angle = tex_angle * Math::lerp(parameters_min[PARAM_ANGLE], parameters_max[PARAM_ANGLE], p.angle_rand); p.custom[0] = Math::deg2rad(base_angle); //angle p.custom[1] = 0.0; //phase - p.custom[2] = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]); //animation offset (0-1) - p.transform = Transform(); + p.custom[2] = tex_anim_offset * Math::lerp(parameters_min[PARAM_ANIM_OFFSET], parameters_max[PARAM_ANIM_OFFSET], p.anim_offset_rand); //animation offset (0-1) + p.transform = Transform3D(); p.time = 0; p.lifetime = lifetime * (1.0 - Math::randf() * lifetime_randomness); p.base_color = Color(1, 1, 1, 1); @@ -706,8 +790,9 @@ void CPUParticles3D::_particles_process(float p_delta) { //do none } break; case EMISSION_SHAPE_SPHERE: { - float s = 2.0 * Math::randf() - 1.0, t = 2.0 * Math_PI * Math::randf(); - float radius = emission_sphere_radius * Math::sqrt(1.0 - s * s); + real_t s = 2.0 * Math::randf() - 1.0; + real_t t = Math_TAU * Math::randf(); + real_t radius = emission_sphere_radius * Math::sqrt(1.0 - s * s); p.transform.origin = Vector3(radius * Math::cos(t), radius * Math::sin(t), emission_sphere_radius * s); } break; case EMISSION_SHAPE_BOX: { @@ -725,12 +810,12 @@ void CPUParticles3D::_particles_process(float p_delta) { p.transform.origin = emission_points.get(random_idx); if (emission_shape == EMISSION_SHAPE_DIRECTED_POINTS && emission_normals.size() == pc) { - if (flags[FLAG_DISABLE_Z]) { + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { Vector3 normal = emission_normals.get(random_idx); Vector2 normal_2d(normal.x, normal.y); Transform2D m2; m2.set_axis(0, normal_2d); - m2.set_axis(1, normal_2d.tangent()); + m2.set_axis(1, normal_2d.orthogonal()); Vector2 velocity_2d(p.velocity.x, p.velocity.y); velocity_2d = m2.basis_xform(velocity_2d); p.velocity.x = velocity_2d.x; @@ -752,6 +837,21 @@ void CPUParticles3D::_particles_process(float p_delta) { p.base_color = emission_colors.get(random_idx); } } break; + case EMISSION_SHAPE_RING: { + real_t ring_random_angle = Math::randf() * Math_TAU; + real_t ring_random_radius = Math::randf() * (emission_ring_radius - emission_ring_inner_radius) + emission_ring_inner_radius; + Vector3 axis = emission_ring_axis.normalized(); + Vector3 ortho_axis = Vector3(); + if (axis == Vector3(1.0, 0.0, 0.0)) { + ortho_axis = Vector3(0.0, 1.0, 0.0).cross(axis); + } else { + ortho_axis = Vector3(1.0, 0.0, 0.0).cross(axis); + } + ortho_axis = ortho_axis.normalized(); + ortho_axis.rotate(axis, ring_random_angle); + ortho_axis = ortho_axis.normalized(); + p.transform.origin = ortho_axis * ring_random_radius + (Math::randf() * emission_ring_height - emission_ring_height / 2.0) * axis; + } break; case EMISSION_SHAPE_MAX: { // Max value for validity check. break; } @@ -762,7 +862,7 @@ void CPUParticles3D::_particles_process(float p_delta) { p.transform = emission_xform * p.transform; } - if (flags[FLAG_DISABLE_Z]) { + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { p.velocity.z = 0.0; p.transform.origin.z = 0.0; } @@ -771,91 +871,92 @@ void CPUParticles3D::_particles_process(float p_delta) { continue; } else if (p.time > p.lifetime) { p.active = false; + tv = 1.0; } else { uint32_t alt_seed = p.seed; p.time += local_delta; p.custom[1] = p.time / lifetime; + tv = p.time / p.lifetime; - float tex_linear_velocity = 0.0; + real_t tex_linear_velocity = 0.0; if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { - tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(p.custom[1]); + tex_linear_velocity = curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY]->interpolate(tv); } - float tex_orbit_velocity = 0.0; - if (flags[FLAG_DISABLE_Z]) { + real_t tex_orbit_velocity = 0.0; + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { if (curve_parameters[PARAM_ORBIT_VELOCITY].is_valid()) { - tex_orbit_velocity = curve_parameters[PARAM_ORBIT_VELOCITY]->interpolate(p.custom[1]); + tex_orbit_velocity = curve_parameters[PARAM_ORBIT_VELOCITY]->interpolate(tv); } } - float tex_angular_velocity = 0.0; + real_t tex_angular_velocity = 0.0; if (curve_parameters[PARAM_ANGULAR_VELOCITY].is_valid()) { - tex_angular_velocity = curve_parameters[PARAM_ANGULAR_VELOCITY]->interpolate(p.custom[1]); + tex_angular_velocity = curve_parameters[PARAM_ANGULAR_VELOCITY]->interpolate(tv); } - float tex_linear_accel = 0.0; + real_t tex_linear_accel = 0.0; if (curve_parameters[PARAM_LINEAR_ACCEL].is_valid()) { - tex_linear_accel = curve_parameters[PARAM_LINEAR_ACCEL]->interpolate(p.custom[1]); + tex_linear_accel = curve_parameters[PARAM_LINEAR_ACCEL]->interpolate(tv); } - float tex_tangential_accel = 0.0; + real_t tex_tangential_accel = 0.0; if (curve_parameters[PARAM_TANGENTIAL_ACCEL].is_valid()) { - tex_tangential_accel = curve_parameters[PARAM_TANGENTIAL_ACCEL]->interpolate(p.custom[1]); + tex_tangential_accel = curve_parameters[PARAM_TANGENTIAL_ACCEL]->interpolate(tv); } - float tex_radial_accel = 0.0; + real_t tex_radial_accel = 0.0; if (curve_parameters[PARAM_RADIAL_ACCEL].is_valid()) { - tex_radial_accel = curve_parameters[PARAM_RADIAL_ACCEL]->interpolate(p.custom[1]); + tex_radial_accel = curve_parameters[PARAM_RADIAL_ACCEL]->interpolate(tv); } - float tex_damping = 0.0; + real_t tex_damping = 0.0; if (curve_parameters[PARAM_DAMPING].is_valid()) { - tex_damping = curve_parameters[PARAM_DAMPING]->interpolate(p.custom[1]); + tex_damping = curve_parameters[PARAM_DAMPING]->interpolate(tv); } - float tex_angle = 0.0; + real_t tex_angle = 0.0; if (curve_parameters[PARAM_ANGLE].is_valid()) { - tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(p.custom[1]); + tex_angle = curve_parameters[PARAM_ANGLE]->interpolate(tv); } - float tex_anim_speed = 0.0; + real_t tex_anim_speed = 0.0; if (curve_parameters[PARAM_ANIM_SPEED].is_valid()) { - tex_anim_speed = curve_parameters[PARAM_ANIM_SPEED]->interpolate(p.custom[1]); + tex_anim_speed = curve_parameters[PARAM_ANIM_SPEED]->interpolate(tv); } - float tex_anim_offset = 0.0; + real_t tex_anim_offset = 0.0; if (curve_parameters[PARAM_ANIM_OFFSET].is_valid()) { - tex_anim_offset = curve_parameters[PARAM_ANIM_OFFSET]->interpolate(p.custom[1]); + tex_anim_offset = curve_parameters[PARAM_ANIM_OFFSET]->interpolate(tv); } Vector3 force = gravity; Vector3 position = p.transform.origin; - if (flags[FLAG_DISABLE_Z]) { + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { position.z = 0.0; } //apply linear acceleration - force += p.velocity.length() > 0.0 ? p.velocity.normalized() * (parameters[PARAM_LINEAR_ACCEL] + tex_linear_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_LINEAR_ACCEL]) : Vector3(); + force += p.velocity.length() > 0.0 ? p.velocity.normalized() * tex_linear_accel * Math::lerp(parameters_min[PARAM_LINEAR_ACCEL], parameters_max[PARAM_LINEAR_ACCEL], rand_from_seed(alt_seed)) : Vector3(); //apply radial acceleration Vector3 org = emission_xform.origin; Vector3 diff = position - org; - force += diff.length() > 0.0 ? diff.normalized() * (parameters[PARAM_RADIAL_ACCEL] + tex_radial_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_RADIAL_ACCEL]) : Vector3(); - //apply tangential acceleration; - if (flags[FLAG_DISABLE_Z]) { + force += diff.length() > 0.0 ? diff.normalized() * (tex_radial_accel)*Math::lerp(parameters_min[PARAM_RADIAL_ACCEL], parameters_max[PARAM_RADIAL_ACCEL], rand_from_seed(alt_seed)) : Vector3(); + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { Vector2 yx = Vector2(diff.y, diff.x); Vector2 yx2 = (yx * Vector2(-1.0, 1.0)).normalized(); - force += yx.length() > 0.0 ? Vector3(yx2.x, yx2.y, 0.0) * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector3(); + force += yx.length() > 0.0 ? Vector3(yx2.x, yx2.y, 0.0) * (tex_tangential_accel * Math::lerp(parameters_min[PARAM_TANGENTIAL_ACCEL], parameters_max[PARAM_TANGENTIAL_ACCEL], rand_from_seed(alt_seed))) : Vector3(); } else { Vector3 crossDiff = diff.normalized().cross(gravity.normalized()); - force += crossDiff.length() > 0.0 ? crossDiff.normalized() * ((parameters[PARAM_TANGENTIAL_ACCEL] + tex_tangential_accel) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_TANGENTIAL_ACCEL])) : Vector3(); + force += crossDiff.length() > 0.0 ? crossDiff.normalized() * (tex_tangential_accel * Math::lerp(parameters_min[PARAM_TANGENTIAL_ACCEL], parameters_max[PARAM_TANGENTIAL_ACCEL], rand_from_seed(alt_seed))) : Vector3(); } //apply attractor forces p.velocity += force * local_delta; //orbit velocity - if (flags[FLAG_DISABLE_Z]) { - float orbit_amount = (parameters[PARAM_ORBIT_VELOCITY] + tex_orbit_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ORBIT_VELOCITY]); + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { + real_t orbit_amount = tex_orbit_velocity * Math::lerp(parameters_min[PARAM_ORBIT_VELOCITY], parameters_max[PARAM_ORBIT_VELOCITY], rand_from_seed(alt_seed)); if (orbit_amount != 0.0) { - float ang = orbit_amount * local_delta * Math_PI * 2.0; + real_t ang = orbit_amount * local_delta * Math_TAU; // Not sure why the ParticlesMaterial code uses a clockwise rotation matrix, // but we use -ang here to reproduce its behavior. Transform2D rot = Transform2D(-ang, Vector2()); @@ -867,9 +968,10 @@ void CPUParticles3D::_particles_process(float p_delta) { if (curve_parameters[PARAM_INITIAL_LINEAR_VELOCITY].is_valid()) { p.velocity = p.velocity.normalized() * tex_linear_velocity; } - if (parameters[PARAM_DAMPING] + tex_damping > 0.0) { - float v = p.velocity.length(); - float damp = (parameters[PARAM_DAMPING] + tex_damping) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_DAMPING]); + + if (parameters_max[PARAM_DAMPING] + tex_damping > 0.0) { + real_t v = p.velocity.length(); + real_t damp = tex_damping * Math::lerp(parameters_min[PARAM_DAMPING], parameters_max[PARAM_DAMPING], rand_from_seed(alt_seed)); v -= damp * local_delta; if (v < 0.0) { p.velocity = Vector3(); @@ -877,27 +979,48 @@ void CPUParticles3D::_particles_process(float p_delta) { p.velocity = p.velocity.normalized() * v; } } - float base_angle = (parameters[PARAM_ANGLE] + tex_angle) * Math::lerp(1.0f, p.angle_rand, randomness[PARAM_ANGLE]); - base_angle += p.custom[1] * lifetime * (parameters[PARAM_ANGULAR_VELOCITY] + tex_angular_velocity) * Math::lerp(1.0f, rand_from_seed(alt_seed) * 2.0f - 1.0f, randomness[PARAM_ANGULAR_VELOCITY]); + real_t base_angle = (tex_angle)*Math::lerp(parameters_min[PARAM_ANGLE], parameters_max[PARAM_ANGLE], p.angle_rand); + base_angle += p.custom[1] * lifetime * tex_angular_velocity * Math::lerp(parameters_min[PARAM_ANGULAR_VELOCITY], parameters_max[PARAM_ANGULAR_VELOCITY], rand_from_seed(alt_seed)); p.custom[0] = Math::deg2rad(base_angle); //angle - p.custom[2] = (parameters[PARAM_ANIM_OFFSET] + tex_anim_offset) * Math::lerp(1.0f, p.anim_offset_rand, randomness[PARAM_ANIM_OFFSET]) + p.custom[1] * (parameters[PARAM_ANIM_SPEED] + tex_anim_speed) * Math::lerp(1.0f, rand_from_seed(alt_seed), randomness[PARAM_ANIM_SPEED]); //angle + p.custom[2] = tex_anim_offset * Math::lerp(parameters_min[PARAM_ANIM_OFFSET], parameters_max[PARAM_ANIM_OFFSET], p.anim_offset_rand) + p.custom[1] * tex_anim_speed * Math::lerp(parameters_min[PARAM_ANIM_SPEED], parameters_max[PARAM_ANIM_SPEED], rand_from_seed(alt_seed)); //angle } //apply color //apply hue rotation - float tex_scale = 1.0; - if (curve_parameters[PARAM_SCALE].is_valid()) { - tex_scale = curve_parameters[PARAM_SCALE]->interpolate(p.custom[1]); + Vector3 tex_scale = Vector3(1.0, 1.0, 1.0); + if (split_scale) { + if (scale_curve_x.is_valid()) { + tex_scale.x = scale_curve_x->interpolate(tv); + } else { + tex_scale.x = 1.0; + } + if (scale_curve_y.is_valid()) { + tex_scale.y = scale_curve_y->interpolate(tv); + } else { + tex_scale.y = 1.0; + } + if (scale_curve_z.is_valid()) { + tex_scale.z = scale_curve_z->interpolate(tv); + } else { + tex_scale.z = 1.0; + } + } else { + if (curve_parameters[PARAM_SCALE].is_valid()) { + float tmp_scale = curve_parameters[PARAM_SCALE]->interpolate(tv); + tex_scale.x = tmp_scale; + tex_scale.y = tmp_scale; + tex_scale.z = tmp_scale; + } } - float tex_hue_variation = 0.0; + real_t tex_hue_variation = 0.0; if (curve_parameters[PARAM_HUE_VARIATION].is_valid()) { - tex_hue_variation = curve_parameters[PARAM_HUE_VARIATION]->interpolate(p.custom[1]); + tex_hue_variation = curve_parameters[PARAM_HUE_VARIATION]->interpolate(tv); } - float hue_rot_angle = (parameters[PARAM_HUE_VARIATION] + tex_hue_variation) * Math_PI * 2.0 * Math::lerp(1.0f, p.hue_rot_rand * 2.0f - 1.0f, randomness[PARAM_HUE_VARIATION]); - float hue_rot_c = Math::cos(hue_rot_angle); - float hue_rot_s = Math::sin(hue_rot_angle); + real_t hue_rot_angle = (tex_hue_variation)*Math_TAU * Math::lerp(parameters_min[PARAM_HUE_VARIATION], parameters_max[PARAM_HUE_VARIATION], p.hue_rot_rand); + real_t hue_rot_c = Math::cos(hue_rot_angle); + real_t hue_rot_s = Math::sin(hue_rot_angle); Basis hue_rot_mat; { @@ -911,7 +1034,7 @@ void CPUParticles3D::_particles_process(float p_delta) { } if (color_ramp.is_valid()) { - p.color = color_ramp->get_color_at_offset(p.custom[1]) * color; + p.color = color_ramp->get_color_at_offset(tv) * color; } else { p.color = color; } @@ -923,8 +1046,8 @@ void CPUParticles3D::_particles_process(float p_delta) { p.color *= p.base_color; - if (flags[FLAG_DISABLE_Z]) { - if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) { + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { + if (particle_flags[PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY]) { if (p.velocity.length() > 0.0) { p.transform.basis.set_axis(1, p.velocity.normalized()); } else { @@ -941,7 +1064,7 @@ void CPUParticles3D::_particles_process(float p_delta) { } else { //orient particle Y towards velocity - if (flags[FLAG_ALIGN_Y_TO_VELOCITY]) { + if (particle_flags[PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY]) { if (p.velocity.length() > 0.0) { p.transform.basis.set_axis(1, p.velocity.normalized()); } else { @@ -959,21 +1082,29 @@ void CPUParticles3D::_particles_process(float p_delta) { } //turn particle by rotation in Y - if (flags[FLAG_ROTATE_Y]) { + if (particle_flags[PARTICLE_FLAG_ROTATE_Y]) { Basis rot_y(Vector3(0, 1, 0), p.custom[0]); p.transform.basis = p.transform.basis * rot_y; } } + p.transform.basis = p.transform.basis.orthonormalized(); //scale by scale - float base_scale = tex_scale * Math::lerp(parameters[PARAM_SCALE], 1.0f, p.scale_rand * randomness[PARAM_SCALE]); - if (base_scale < 0.000001) { - base_scale = 0.000001; + + Vector3 base_scale = tex_scale * Math::lerp(parameters_min[PARAM_SCALE], parameters_max[PARAM_SCALE], p.scale_rand); + if (base_scale.x < CMP_EPSILON) { + base_scale.x = CMP_EPSILON; + } + if (base_scale.y < CMP_EPSILON) { + base_scale.y = CMP_EPSILON; + } + if (base_scale.z < CMP_EPSILON) { + base_scale.z = CMP_EPSILON; } - p.transform.basis.scale(Vector3(1, 1, 1) * base_scale); + p.transform.basis.scale(base_scale); - if (flags[FLAG_DISABLE_Z]) { + if (particle_flags[PARTICLE_FLAG_DISABLE_Z]) { p.velocity.z = 0.0; p.transform.origin.z = 0.0; } @@ -1006,7 +1137,8 @@ void CPUParticles3D::_update_particle_data_buffer() { sorter.compare.particles = r; sorter.sort(order, pc); } else if (draw_order == DRAW_ORDER_VIEW_DEPTH) { - Camera3D *c = get_viewport()->get_camera(); + ERR_FAIL_NULL(get_viewport()); + Camera3D *c = get_viewport()->get_camera_3d(); if (c) { Vector3 dir = c->get_global_transform().basis.get_axis(2); //far away to close @@ -1029,7 +1161,7 @@ void CPUParticles3D::_update_particle_data_buffer() { for (int i = 0; i < pc; i++) { int idx = order ? order[i] : i; - Transform t = r[idx].transform; + Transform3D t = r[idx].transform; if (!local_coords) { t = inv_emission_transform * t; @@ -1049,7 +1181,7 @@ void CPUParticles3D::_update_particle_data_buffer() { ptr[10] = t.basis.elements[2][2]; ptr[11] = t.origin.z; } else { - zeromem(ptr, sizeof(float) * 12); + memset(ptr, 0, sizeof(Transform3D)); } Color c = r[idx].color; @@ -1067,7 +1199,7 @@ void CPUParticles3D::_update_particle_data_buffer() { ptr += 20; } - can_update = true; + can_update.set(); } void CPUParticles3D::_set_redraw(bool p_redraw) { @@ -1096,9 +1228,9 @@ void CPUParticles3D::_set_redraw(bool p_redraw) { void CPUParticles3D::_update_render_thread() { MutexLock lock(update_mutex); - if (can_update) { + if (can_update.is_set()) { RS::get_singleton()->multimesh_set_buffer(multimesh, particle_data); - can_update = false; //wait for next time + can_update.clear(); //wait for next time } } @@ -1138,7 +1270,7 @@ void CPUParticles3D::_notification(int p_what) { float *ptr = w; for (int i = 0; i < pc; i++) { - Transform t = inv_emission_transform * r[i].transform; + Transform3D t = inv_emission_transform * r[i].transform; if (r[i].active) { ptr[0] = t.basis.elements[0][0]; @@ -1154,13 +1286,13 @@ void CPUParticles3D::_notification(int p_what) { ptr[10] = t.basis.elements[2][2]; ptr[11] = t.origin.z; } else { - zeromem(ptr, sizeof(float) * 12); + memset(ptr, 0, sizeof(float) * 12); } ptr += 20; } - can_update = true; + can_update.set(); } } } @@ -1199,25 +1331,32 @@ void CPUParticles3D::convert_from_particles(Node *p_particles) { set_color_ramp(gt->get_gradient()); } - set_particle_flag(FLAG_ALIGN_Y_TO_VELOCITY, material->get_flag(ParticlesMaterial::FLAG_ALIGN_Y_TO_VELOCITY)); - set_particle_flag(FLAG_ROTATE_Y, material->get_flag(ParticlesMaterial::FLAG_ROTATE_Y)); - set_particle_flag(FLAG_DISABLE_Z, material->get_flag(ParticlesMaterial::FLAG_DISABLE_Z)); + set_particle_flag(PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY, material->get_particle_flag(ParticlesMaterial::PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY)); + set_particle_flag(PARTICLE_FLAG_ROTATE_Y, material->get_particle_flag(ParticlesMaterial::PARTICLE_FLAG_ROTATE_Y)); + set_particle_flag(PARTICLE_FLAG_DISABLE_Z, material->get_particle_flag(ParticlesMaterial::PARTICLE_FLAG_DISABLE_Z)); set_emission_shape(EmissionShape(material->get_emission_shape())); set_emission_sphere_radius(material->get_emission_sphere_radius()); set_emission_box_extents(material->get_emission_box_extents()); + Ref<CurveXYZTexture> scale3D = material->get_param_texture(ParticlesMaterial::PARAM_SCALE); + if (scale3D.is_valid()) { + split_scale = true; + scale_curve_x = scale3D->get_curve_x(); + scale_curve_y = scale3D->get_curve_y(); + scale_curve_z = scale3D->get_curve_z(); + } set_gravity(material->get_gravity()); set_lifetime_randomness(material->get_lifetime_randomness()); #define CONVERT_PARAM(m_param) \ - set_param(m_param, material->get_param(ParticlesMaterial::m_param)); \ + set_param_min(m_param, material->get_param_min(ParticlesMaterial::m_param)); \ { \ Ref<CurveTexture> ctex = material->get_param_texture(ParticlesMaterial::m_param); \ if (ctex.is_valid()) \ set_param_curve(m_param, ctex->get_curve()); \ } \ - set_param_randomness(m_param, material->get_param_randomness(ParticlesMaterial::m_param)); + set_param_max(m_param, material->get_param_max(ParticlesMaterial::m_param)); CONVERT_PARAM(PARAM_INITIAL_LINEAR_VELOCITY); CONVERT_PARAM(PARAM_ANGULAR_VELOCITY); @@ -1272,11 +1411,11 @@ void CPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("restart"), &CPUParticles3D::restart); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_EXP_RANGE, "1,1000000,1"), "set_amount", "get_amount"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount"); ADD_GROUP("Time", ""); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_EXP_RANGE, "0.01,600.0,0.01,or_greater"), "set_lifetime", "get_lifetime"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater,exp"), "set_lifetime", "get_lifetime"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_EXP_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01,exp"), "set_pre_process_time", "get_pre_process_time"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_speed_scale", "get_speed_scale"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio"); @@ -1303,11 +1442,11 @@ void CPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_flatness", "amount"), &CPUParticles3D::set_flatness); ClassDB::bind_method(D_METHOD("get_flatness"), &CPUParticles3D::get_flatness); - ClassDB::bind_method(D_METHOD("set_param", "param", "value"), &CPUParticles3D::set_param); - ClassDB::bind_method(D_METHOD("get_param", "param"), &CPUParticles3D::get_param); + ClassDB::bind_method(D_METHOD("set_param_min", "param", "value"), &CPUParticles3D::set_param_min); + ClassDB::bind_method(D_METHOD("get_param_min", "param"), &CPUParticles3D::get_param_min); - ClassDB::bind_method(D_METHOD("set_param_randomness", "param", "randomness"), &CPUParticles3D::set_param_randomness); - ClassDB::bind_method(D_METHOD("get_param_randomness", "param"), &CPUParticles3D::get_param_randomness); + ClassDB::bind_method(D_METHOD("set_param_max", "param", "value"), &CPUParticles3D::set_param_max); + ClassDB::bind_method(D_METHOD("get_param_max", "param"), &CPUParticles3D::get_param_max); ClassDB::bind_method(D_METHOD("set_param_curve", "param", "curve"), &CPUParticles3D::set_param_curve); ClassDB::bind_method(D_METHOD("get_param_curve", "param"), &CPUParticles3D::get_param_curve); @@ -1318,8 +1457,8 @@ void CPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_color_ramp", "ramp"), &CPUParticles3D::set_color_ramp); ClassDB::bind_method(D_METHOD("get_color_ramp"), &CPUParticles3D::get_color_ramp); - ClassDB::bind_method(D_METHOD("set_particle_flag", "flag", "enable"), &CPUParticles3D::set_particle_flag); - ClassDB::bind_method(D_METHOD("get_particle_flag", "flag"), &CPUParticles3D::get_particle_flag); + ClassDB::bind_method(D_METHOD("set_particle_flag", "particle_flag", "enable"), &CPUParticles3D::set_particle_flag); + ClassDB::bind_method(D_METHOD("get_particle_flag", "particle_flag"), &CPUParticles3D::get_particle_flag); ClassDB::bind_method(D_METHOD("set_emission_shape", "shape"), &CPUParticles3D::set_emission_shape); ClassDB::bind_method(D_METHOD("get_emission_shape"), &CPUParticles3D::get_emission_shape); @@ -1339,22 +1478,50 @@ void CPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_emission_colors", "array"), &CPUParticles3D::set_emission_colors); ClassDB::bind_method(D_METHOD("get_emission_colors"), &CPUParticles3D::get_emission_colors); + ClassDB::bind_method(D_METHOD("set_emission_ring_axis", "axis"), &CPUParticles3D::set_emission_ring_axis); + ClassDB::bind_method(D_METHOD("get_emission_ring_axis"), &CPUParticles3D::get_emission_ring_axis); + + ClassDB::bind_method(D_METHOD("set_emission_ring_height", "height"), &CPUParticles3D::set_emission_ring_height); + ClassDB::bind_method(D_METHOD("get_emission_ring_height"), &CPUParticles3D::get_emission_ring_height); + + ClassDB::bind_method(D_METHOD("set_emission_ring_radius", "radius"), &CPUParticles3D::set_emission_ring_radius); + ClassDB::bind_method(D_METHOD("get_emission_ring_radius"), &CPUParticles3D::get_emission_ring_radius); + + ClassDB::bind_method(D_METHOD("set_emission_ring_inner_radius", "inner_radius"), &CPUParticles3D::set_emission_ring_inner_radius); + ClassDB::bind_method(D_METHOD("get_emission_ring_inner_radius"), &CPUParticles3D::get_emission_ring_inner_radius); + ClassDB::bind_method(D_METHOD("get_gravity"), &CPUParticles3D::get_gravity); ClassDB::bind_method(D_METHOD("set_gravity", "accel_vec"), &CPUParticles3D::set_gravity); + ClassDB::bind_method(D_METHOD("get_split_scale"), &CPUParticles3D::get_split_scale); + ClassDB::bind_method(D_METHOD("set_split_scale", "split_scale"), &CPUParticles3D::set_split_scale); + + ClassDB::bind_method(D_METHOD("get_scale_curve_x"), &CPUParticles3D::get_scale_curve_x); + ClassDB::bind_method(D_METHOD("set_scale_curve_x", "scale_curve"), &CPUParticles3D::set_scale_curve_x); + + ClassDB::bind_method(D_METHOD("get_scale_curve_y"), &CPUParticles3D::get_scale_curve_y); + ClassDB::bind_method(D_METHOD("set_scale_curve_y", "scale_curve"), &CPUParticles3D::set_scale_curve_y); + + ClassDB::bind_method(D_METHOD("get_scale_curve_z"), &CPUParticles3D::get_scale_curve_z); + ClassDB::bind_method(D_METHOD("set_scale_curve_z", "scale_curve"), &CPUParticles3D::set_scale_curve_z); + ClassDB::bind_method(D_METHOD("convert_from_particles", "particles"), &CPUParticles3D::convert_from_particles); ADD_GROUP("Emission Shape", "emission_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_emission_shape", "get_emission_shape"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "emission_shape", PROPERTY_HINT_ENUM, "Point,Sphere,Box,Points,Directed Points,Ring", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_UPDATE_ALL_IF_MODIFIED), "set_emission_shape", "get_emission_shape"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_sphere_radius", PROPERTY_HINT_RANGE, "0.01,128,0.01"), "set_emission_sphere_radius", "get_emission_sphere_radius"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_box_extents"), "set_emission_box_extents", "get_emission_box_extents"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "emission_points"), "set_emission_points", "get_emission_points"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "emission_normals"), "set_emission_normals", "get_emission_normals"); ADD_PROPERTY(PropertyInfo(Variant::PACKED_COLOR_ARRAY, "emission_colors"), "set_emission_colors", "get_emission_colors"); - ADD_GROUP("Flags", "flag_"); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_align_y"), "set_particle_flag", "get_particle_flag", FLAG_ALIGN_Y_TO_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_rotate_y"), "set_particle_flag", "get_particle_flag", FLAG_ROTATE_Y); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "flag_disable_z"), "set_particle_flag", "get_particle_flag", FLAG_DISABLE_Z); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "emission_ring_axis"), "set_emission_ring_axis", "get_emission_ring_axis"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_height"), "set_emission_ring_height", "get_emission_ring_height"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_radius"), "set_emission_ring_radius", "get_emission_ring_radius"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_ring_inner_radius"), "set_emission_ring_inner_radius", "get_emission_ring_inner_radius"); + ADD_GROUP("Particle Flags", "particle_flag_"); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_align_y"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_rotate_y"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_ROTATE_Y); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "particle_flag_disable_z"), "set_particle_flag", "get_particle_flag", PARTICLE_FLAG_DISABLE_Z); ADD_GROUP("Direction", ""); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "direction"), "set_direction", "get_direction"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "spread", PROPERTY_HINT_RANGE, "0,180,0.01"), "set_spread", "get_spread"); @@ -1362,54 +1529,58 @@ void CPUParticles3D::_bind_methods() { ADD_GROUP("Gravity", ""); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "gravity"), "set_gravity", "get_gravity"); ADD_GROUP("Initial Velocity", "initial_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param", "get_param", PARAM_INITIAL_LINEAR_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_INITIAL_LINEAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity_min", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param_min", "get_param_min", PARAM_INITIAL_LINEAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "initial_velocity_max", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param_max", "get_param_max", PARAM_INITIAL_LINEAR_VELOCITY); ADD_GROUP("Angular Velocity", "angular_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGULAR_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGULAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_min", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_ANGULAR_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_velocity_max", PROPERTY_HINT_RANGE, "-720,720,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_ANGULAR_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angular_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANGULAR_VELOCITY); ADD_GROUP("Orbit Velocity", "orbit_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_ORBIT_VELOCITY); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ORBIT_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_min", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_ORBIT_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "orbit_velocity_max", PROPERTY_HINT_RANGE, "-1000,1000,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_ORBIT_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "orbit_velocity_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ORBIT_VELOCITY); ADD_GROUP("Linear Accel", "linear_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_LINEAR_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_LINEAR_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_LINEAR_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_LINEAR_ACCEL); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "linear_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_LINEAR_ACCEL); ADD_GROUP("Radial Accel", "radial_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_RADIAL_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_RADIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_RADIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "radial_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_RADIAL_ACCEL); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "radial_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_RADIAL_ACCEL); ADD_GROUP("Tangential Accel", "tangential_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param", "get_param", PARAM_TANGENTIAL_ACCEL); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_TANGENTIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_min", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_min", "get_param_min", PARAM_TANGENTIAL_ACCEL); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "tangential_accel_max", PROPERTY_HINT_RANGE, "-100,100,0.01,or_lesser,or_greater"), "set_param_max", "get_param_max", PARAM_TANGENTIAL_ACCEL); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "tangential_accel_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_TANGENTIAL_ACCEL); ADD_GROUP("Damping", ""); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_param", "get_param", PARAM_DAMPING); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping_min", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_param_min", "get_param_min", PARAM_DAMPING); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "damping_max", PROPERTY_HINT_RANGE, "0,100,0.01"), "set_param_max", "get_param_max", PARAM_DAMPING); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "damping_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_DAMPING); ADD_GROUP("Angle", ""); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater"), "set_param", "get_param", PARAM_ANGLE); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANGLE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_min", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater,degrees"), "set_param_min", "get_param_min", PARAM_ANGLE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angle_max", PROPERTY_HINT_RANGE, "-720,720,0.1,or_lesser,or_greater,degrees"), "set_param_max", "get_param_max", PARAM_ANGLE); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "angle_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANGLE); ADD_GROUP("Scale", ""); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "scale_amount", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param", "get_param", PARAM_SCALE); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "scale_amount_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_SCALE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "scale_amount_min", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param_min", "get_param_min", PARAM_SCALE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "scale_amount_max", PROPERTY_HINT_RANGE, "0,1000,0.01,or_greater"), "set_param_max", "get_param_max", PARAM_SCALE); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "scale_amount_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_SCALE); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "split_scale"), "set_split_scale", "get_split_scale"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "scale_curve_x", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_scale_curve_x", "get_scale_curve_x"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "scale_curve_y", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_scale_curve_y", "get_scale_curve_y"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "scale_curve_z", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_scale_curve_z", "get_scale_curve_z"); ADD_GROUP("Color", ""); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "color"), "set_color", "get_color"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "color_ramp", PROPERTY_HINT_RESOURCE_TYPE, "Gradient"), "set_color_ramp", "get_color_ramp"); ADD_GROUP("Hue Variation", "hue_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param", "get_param", PARAM_HUE_VARIATION); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_HUE_VARIATION); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_min", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param_min", "get_param_min", PARAM_HUE_VARIATION); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "hue_variation_max", PROPERTY_HINT_RANGE, "-1,1,0.01"), "set_param_max", "get_param_max", PARAM_HUE_VARIATION); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "hue_variation_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_HUE_VARIATION); ADD_GROUP("Animation", "anim_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater"), "set_param", "get_param", PARAM_ANIM_SPEED); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_SPEED); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_min", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater,or_lesser"), "set_param_min", "get_param_min", PARAM_ANIM_SPEED); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_speed_max", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater,or_lesser"), "set_param_max", "get_param_max", PARAM_ANIM_SPEED); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_speed_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANIM_SPEED); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_ANIM_OFFSET); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_random", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_randomness", "get_param_randomness", PARAM_ANIM_OFFSET); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_min", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_min", "get_param_min", PARAM_ANIM_OFFSET); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "anim_offset_max", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param_max", "get_param_max", PARAM_ANIM_OFFSET); ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "anim_offset_curve", PROPERTY_HINT_RESOURCE_TYPE, "Curve"), "set_param_curve", "get_param_curve", PARAM_ANIM_OFFSET); BIND_ENUM_CONSTANT(PARAM_INITIAL_LINEAR_VELOCITY); @@ -1426,27 +1597,21 @@ void CPUParticles3D::_bind_methods() { BIND_ENUM_CONSTANT(PARAM_ANIM_OFFSET); BIND_ENUM_CONSTANT(PARAM_MAX); - BIND_ENUM_CONSTANT(FLAG_ALIGN_Y_TO_VELOCITY); - BIND_ENUM_CONSTANT(FLAG_ROTATE_Y); - BIND_ENUM_CONSTANT(FLAG_DISABLE_Z); - BIND_ENUM_CONSTANT(FLAG_MAX); + BIND_ENUM_CONSTANT(PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY); + BIND_ENUM_CONSTANT(PARTICLE_FLAG_ROTATE_Y); + BIND_ENUM_CONSTANT(PARTICLE_FLAG_DISABLE_Z); + BIND_ENUM_CONSTANT(PARTICLE_FLAG_MAX); BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINT); BIND_ENUM_CONSTANT(EMISSION_SHAPE_SPHERE); BIND_ENUM_CONSTANT(EMISSION_SHAPE_BOX); BIND_ENUM_CONSTANT(EMISSION_SHAPE_POINTS); BIND_ENUM_CONSTANT(EMISSION_SHAPE_DIRECTED_POINTS); + BIND_ENUM_CONSTANT(EMISSION_SHAPE_RING); BIND_ENUM_CONSTANT(EMISSION_SHAPE_MAX); } CPUParticles3D::CPUParticles3D() { - time = 0; - inactive_time = 0; - frame_remainder = 0; - cycle = 0; - redraw = false; - emitting = false; - set_notify_transform(true); multimesh = RenderingServer::get_singleton()->multimesh_create(); @@ -1454,51 +1619,46 @@ CPUParticles3D::CPUParticles3D() { set_base(multimesh); set_emitting(true); - set_one_shot(false); set_amount(8); - set_lifetime(1); - set_fixed_fps(0); - set_fractional_delta(true); - set_pre_process_time(0); - set_explosiveness_ratio(0); - set_randomness_ratio(0); - set_lifetime_randomness(0); - set_use_local_coordinates(true); - - set_draw_order(DRAW_ORDER_INDEX); - set_speed_scale(1); - - set_direction(Vector3(1, 0, 0)); - set_spread(45); - set_flatness(0); - set_param(PARAM_INITIAL_LINEAR_VELOCITY, 0); - set_param(PARAM_ANGULAR_VELOCITY, 0); - set_param(PARAM_ORBIT_VELOCITY, 0); - set_param(PARAM_LINEAR_ACCEL, 0); - set_param(PARAM_RADIAL_ACCEL, 0); - set_param(PARAM_TANGENTIAL_ACCEL, 0); - set_param(PARAM_DAMPING, 0); - set_param(PARAM_ANGLE, 0); - set_param(PARAM_SCALE, 1); - set_param(PARAM_HUE_VARIATION, 0); - set_param(PARAM_ANIM_SPEED, 0); - set_param(PARAM_ANIM_OFFSET, 0); + + set_param_min(PARAM_INITIAL_LINEAR_VELOCITY, 0); + set_param_min(PARAM_ANGULAR_VELOCITY, 0); + set_param_min(PARAM_ORBIT_VELOCITY, 0); + set_param_min(PARAM_LINEAR_ACCEL, 0); + set_param_min(PARAM_RADIAL_ACCEL, 0); + set_param_min(PARAM_TANGENTIAL_ACCEL, 0); + set_param_min(PARAM_DAMPING, 0); + set_param_min(PARAM_ANGLE, 0); + set_param_min(PARAM_SCALE, 1); + set_param_min(PARAM_HUE_VARIATION, 0); + set_param_min(PARAM_ANIM_SPEED, 0); + set_param_min(PARAM_ANIM_OFFSET, 0); + set_param_max(PARAM_INITIAL_LINEAR_VELOCITY, 0); + set_param_max(PARAM_ANGULAR_VELOCITY, 0); + set_param_max(PARAM_ORBIT_VELOCITY, 0); + set_param_max(PARAM_LINEAR_ACCEL, 0); + set_param_max(PARAM_RADIAL_ACCEL, 0); + set_param_max(PARAM_TANGENTIAL_ACCEL, 0); + set_param_max(PARAM_DAMPING, 0); + set_param_max(PARAM_ANGLE, 0); + set_param_max(PARAM_SCALE, 1); + set_param_max(PARAM_HUE_VARIATION, 0); + set_param_max(PARAM_ANIM_SPEED, 0); + set_param_max(PARAM_ANIM_OFFSET, 0); set_emission_shape(EMISSION_SHAPE_POINT); set_emission_sphere_radius(1); set_emission_box_extents(Vector3(1, 1, 1)); + set_emission_ring_axis(Vector3(0, 0, 1.0)); + set_emission_ring_height(1); + set_emission_ring_radius(1); + set_emission_ring_inner_radius(0); set_gravity(Vector3(0, -9.8, 0)); - for (int i = 0; i < PARAM_MAX; i++) { - set_param_randomness(Parameter(i), 0); + for (int i = 0; i < PARTICLE_FLAG_MAX; i++) { + particle_flags[i] = false; } - for (int i = 0; i < FLAG_MAX; i++) { - flags[i] = false; - } - - can_update = false; - set_color(Color(1, 1, 1, 1)); } diff --git a/scene/3d/cpu_particles_3d.h b/scene/3d/cpu_particles_3d.h index f44d0dfcfa..160814ead4 100644 --- a/scene/3d/cpu_particles_3d.h +++ b/scene/3d/cpu_particles_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,7 +31,6 @@ #ifndef CPU_PARTICLES_H #define CPU_PARTICLES_H -#include "core/rid.h" #include "scene/3d/visual_instance_3d.h" class CPUParticles3D : public GeometryInstance3D { @@ -43,10 +42,10 @@ public: DRAW_ORDER_INDEX, DRAW_ORDER_LIFETIME, DRAW_ORDER_VIEW_DEPTH, + DRAW_ORDER_MAX }; enum Parameter { - PARAM_INITIAL_LINEAR_VELOCITY, PARAM_ANGULAR_VELOCITY, PARAM_ORBIT_VELOCITY, @@ -62,11 +61,11 @@ public: PARAM_MAX }; - enum Flags { - FLAG_ALIGN_Y_TO_VELOCITY, - FLAG_ROTATE_Y, - FLAG_DISABLE_Z, - FLAG_MAX + enum ParticleFlags { + PARTICLE_FLAG_ALIGN_Y_TO_VELOCITY, + PARTICLE_FLAG_ROTATE_Y, + PARTICLE_FLAG_DISABLE_Z, + PARTICLE_FLAG_MAX }; enum EmissionShape { @@ -75,34 +74,35 @@ public: EMISSION_SHAPE_BOX, EMISSION_SHAPE_POINTS, EMISSION_SHAPE_DIRECTED_POINTS, + EMISSION_SHAPE_RING, EMISSION_SHAPE_MAX }; private: - bool emitting; + bool emitting = false; struct Particle { - Transform transform; + Transform3D transform; Color color; - float custom[4]; + real_t custom[4] = {}; Vector3 velocity; - bool active; - float angle_rand; - float scale_rand; - float hue_rot_rand; - float anim_offset_rand; - float time; - float lifetime; + bool active = false; + real_t angle_rand = 0.0; + real_t scale_rand = 0.0; + real_t hue_rot_rand = 0.0; + real_t anim_offset_rand = 0.0; + double time = 0.0; + double lifetime = 0.0; Color base_color; - uint32_t seed; + uint32_t seed = 0; }; - float time; - float inactive_time; - float frame_remainder; - int cycle; - bool redraw; + double time = 0.0; + double inactive_time = 0.0; + double frame_remainder = 0.0; + int cycle = 0; + bool redraw = false; RID multimesh; @@ -111,7 +111,7 @@ private: Vector<int> particle_order; struct SortLifetime { - const Particle *particles; + const Particle *particles = nullptr; bool operator()(int p_a, int p_b) const { return particles[p_a].time > particles[p_b].time; @@ -119,7 +119,7 @@ private: }; struct SortAxis { - const Particle *particles; + const Particle *particles = nullptr; Vector3 axis; bool operator()(int p_a, int p_b) const { return axis.dot(particles[p_a].transform.origin) < axis.dot(particles[p_b].transform.origin); @@ -128,53 +128,62 @@ private: // - bool one_shot; + bool one_shot = false; - float lifetime; - float pre_process_time; - float explosiveness_ratio; - float randomness_ratio; - float lifetime_randomness; - float speed_scale; - bool local_coords; - int fixed_fps; - bool fractional_delta; + double lifetime = 1.0; + double pre_process_time = 0.0; + real_t explosiveness_ratio = 0.0; + real_t randomness_ratio = 0.0; + double lifetime_randomness = 0.0; + double speed_scale = 1.0; + bool local_coords = true; + int fixed_fps = 0; + bool fractional_delta = true; - Transform inv_emission_transform; + Transform3D inv_emission_transform; - volatile bool can_update; + SafeFlag can_update; - DrawOrder draw_order; + DrawOrder draw_order = DRAW_ORDER_INDEX; Ref<Mesh> mesh; //////// - Vector3 direction; - float spread; - float flatness; + Vector3 direction = Vector3(1, 0, 0); + real_t spread = 45.0; + real_t flatness = 0.0; - float parameters[PARAM_MAX]; - float randomness[PARAM_MAX]; + real_t parameters_min[PARAM_MAX]; + real_t parameters_max[PARAM_MAX] = {}; Ref<Curve> curve_parameters[PARAM_MAX]; - Color color; + Color color = Color(1, 1, 1, 1); Ref<Gradient> color_ramp; - bool flags[FLAG_MAX]; + bool particle_flags[PARTICLE_FLAG_MAX] = {}; - EmissionShape emission_shape; - float emission_sphere_radius; - Vector3 emission_box_extents; + EmissionShape emission_shape = EMISSION_SHAPE_POINT; + real_t emission_sphere_radius = 1.0; + Vector3 emission_box_extents = Vector3(1, 1, 1); Vector<Vector3> emission_points; Vector<Vector3> emission_normals; Vector<Color> emission_colors; - int emission_point_count; + int emission_point_count = 0; + Vector3 emission_ring_axis; + real_t emission_ring_height; + real_t emission_ring_radius; + real_t emission_ring_inner_radius; + + Ref<Curve> scale_curve_x; + Ref<Curve> scale_curve_y; + Ref<Curve> scale_curve_z; + bool split_scale = false; - Vector3 gravity; + Vector3 gravity = Vector3(0, -9.8, 0); void _update_internal(); - void _particles_process(float p_delta); + void _particles_process(double p_delta); void _update_particle_data_buffer(); Mutex update_mutex; @@ -194,27 +203,27 @@ public: void set_emitting(bool p_emitting); void set_amount(int p_amount); - void set_lifetime(float p_lifetime); + void set_lifetime(double p_lifetime); void set_one_shot(bool p_one_shot); - void set_pre_process_time(float p_time); - void set_explosiveness_ratio(float p_ratio); - void set_randomness_ratio(float p_ratio); - void set_lifetime_randomness(float p_random); + void set_pre_process_time(double p_time); + void set_explosiveness_ratio(real_t p_ratio); + void set_randomness_ratio(real_t p_ratio); + void set_lifetime_randomness(double p_random); void set_visibility_aabb(const AABB &p_aabb); void set_use_local_coordinates(bool p_enable); - void set_speed_scale(float p_scale); + void set_speed_scale(double p_scale); bool is_emitting() const; int get_amount() const; - float get_lifetime() const; + double get_lifetime() const; bool get_one_shot() const; - float get_pre_process_time() const; - float get_explosiveness_ratio() const; - float get_randomness_ratio() const; - float get_lifetime_randomness() const; + double get_pre_process_time() const; + real_t get_explosiveness_ratio() const; + real_t get_randomness_ratio() const; + double get_lifetime_randomness() const; AABB get_visibility_aabb() const; bool get_use_local_coordinates() const; - float get_speed_scale() const; + double get_speed_scale() const; void set_fixed_fps(int p_count); int get_fixed_fps() const; @@ -236,17 +245,17 @@ public: void set_direction(Vector3 p_direction); Vector3 get_direction() const; - void set_spread(float p_spread); - float get_spread() const; + void set_spread(real_t p_spread); + real_t get_spread() const; - void set_flatness(float p_flatness); - float get_flatness() const; + void set_flatness(real_t p_flatness); + real_t get_flatness() const; - void set_param(Parameter p_param, float p_value); - float get_param(Parameter p_param) const; + void set_param_min(Parameter p_param, real_t p_value); + real_t get_param_min(Parameter p_param) const; - void set_param_randomness(Parameter p_param, float p_value); - float get_param_randomness(Parameter p_param) const; + void set_param_max(Parameter p_param, real_t p_value); + real_t get_param_max(Parameter p_param) const; void set_param_curve(Parameter p_param, const Ref<Curve> &p_curve); Ref<Curve> get_param_curve(Parameter p_param) const; @@ -257,29 +266,45 @@ public: void set_color_ramp(const Ref<Gradient> &p_ramp); Ref<Gradient> get_color_ramp() const; - void set_particle_flag(Flags p_flag, bool p_enable); - bool get_particle_flag(Flags p_flag) const; + void set_particle_flag(ParticleFlags p_particle_flag, bool p_enable); + bool get_particle_flag(ParticleFlags p_particle_flag) const; void set_emission_shape(EmissionShape p_shape); - void set_emission_sphere_radius(float p_radius); + void set_emission_sphere_radius(real_t p_radius); void set_emission_box_extents(Vector3 p_extents); void set_emission_points(const Vector<Vector3> &p_points); void set_emission_normals(const Vector<Vector3> &p_normals); void set_emission_colors(const Vector<Color> &p_colors); void set_emission_point_count(int p_count); + void set_emission_ring_axis(Vector3 p_axis); + void set_emission_ring_height(real_t p_height); + void set_emission_ring_radius(real_t p_radius); + void set_emission_ring_inner_radius(real_t p_radius); + void set_scale_curve_x(Ref<Curve> p_scale_curve); + void set_scale_curve_y(Ref<Curve> p_scale_curve); + void set_scale_curve_z(Ref<Curve> p_scale_curve); + void set_split_scale(bool p_split_scale); EmissionShape get_emission_shape() const; - float get_emission_sphere_radius() const; + real_t get_emission_sphere_radius() const; Vector3 get_emission_box_extents() const; Vector<Vector3> get_emission_points() const; Vector<Vector3> get_emission_normals() const; Vector<Color> get_emission_colors() const; int get_emission_point_count() const; + Vector3 get_emission_ring_axis() const; + real_t get_emission_ring_height() const; + real_t get_emission_ring_radius() const; + real_t get_emission_ring_inner_radius() const; + Ref<Curve> get_scale_curve_x() const; + Ref<Curve> get_scale_curve_y() const; + Ref<Curve> get_scale_curve_z() const; + bool get_split_scale(); void set_gravity(const Vector3 &p_gravity); Vector3 get_gravity() const; - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; void restart(); @@ -291,7 +316,7 @@ public: VARIANT_ENUM_CAST(CPUParticles3D::DrawOrder) VARIANT_ENUM_CAST(CPUParticles3D::Parameter) -VARIANT_ENUM_CAST(CPUParticles3D::Flags) +VARIANT_ENUM_CAST(CPUParticles3D::ParticleFlags) VARIANT_ENUM_CAST(CPUParticles3D::EmissionShape) #endif // CPU_PARTICLES_H diff --git a/scene/3d/decal.cpp b/scene/3d/decal.cpp index fb72e10171..c94a99a203 100644 --- a/scene/3d/decal.cpp +++ b/scene/3d/decal.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,8 +33,7 @@ void Decal::set_extents(const Vector3 &p_extents) { extents = p_extents; RS::get_singleton()->decal_set_extents(decal, p_extents); - update_gizmo(); - _change_notify("extents"); + update_gizmos(); } Vector3 Decal::get_extents() const { @@ -46,6 +45,7 @@ void Decal::set_texture(DecalTexture p_type, const Ref<Texture2D> &p_texture) { textures[p_type] = p_texture; RID texture_rid = p_texture.is_valid() ? p_texture->get_rid() : RID(); RS::get_singleton()->decal_set_texture(decal, RS::DecalTexture(p_type), texture_rid); + update_configuration_warnings(); } Ref<Texture2D> Decal::get_texture(DecalTexture p_type) const { @@ -53,48 +53,48 @@ Ref<Texture2D> Decal::get_texture(DecalTexture p_type) const { return textures[p_type]; } -void Decal::set_emission_energy(float p_energy) { +void Decal::set_emission_energy(real_t p_energy) { emission_energy = p_energy; RS::get_singleton()->decal_set_emission_energy(decal, emission_energy); } -float Decal::get_emission_energy() const { +real_t Decal::get_emission_energy() const { return emission_energy; } -void Decal::set_albedo_mix(float p_mix) { +void Decal::set_albedo_mix(real_t p_mix) { albedo_mix = p_mix; RS::get_singleton()->decal_set_albedo_mix(decal, albedo_mix); } -float Decal::get_albedo_mix() const { +real_t Decal::get_albedo_mix() const { return albedo_mix; } -void Decal::set_upper_fade(float p_fade) { +void Decal::set_upper_fade(real_t p_fade) { upper_fade = p_fade; RS::get_singleton()->decal_set_fade(decal, upper_fade, lower_fade); } -float Decal::get_upper_fade() const { +real_t Decal::get_upper_fade() const { return upper_fade; } -void Decal::set_lower_fade(float p_fade) { +void Decal::set_lower_fade(real_t p_fade) { lower_fade = p_fade; RS::get_singleton()->decal_set_fade(decal, upper_fade, lower_fade); } -float Decal::get_lower_fade() const { +real_t Decal::get_lower_fade() const { return lower_fade; } -void Decal::set_normal_fade(float p_fade) { +void Decal::set_normal_fade(real_t p_fade) { normal_fade = p_fade; RS::get_singleton()->decal_set_normal_fade(decal, normal_fade); } -float Decal::get_normal_fade() const { +real_t Decal::get_normal_fade() const { return normal_fade; } @@ -110,33 +110,35 @@ Color Decal::get_modulate() const { void Decal::set_enable_distance_fade(bool p_enable) { distance_fade_enabled = p_enable; RS::get_singleton()->decal_set_distance_fade(decal, distance_fade_enabled, distance_fade_begin, distance_fade_length); + notify_property_list_changed(); } bool Decal::is_distance_fade_enabled() const { return distance_fade_enabled; } -void Decal::set_distance_fade_begin(float p_distance) { +void Decal::set_distance_fade_begin(real_t p_distance) { distance_fade_begin = p_distance; RS::get_singleton()->decal_set_distance_fade(decal, distance_fade_enabled, distance_fade_begin, distance_fade_length); } -float Decal::get_distance_fade_begin() const { +real_t Decal::get_distance_fade_begin() const { return distance_fade_begin; } -void Decal::set_distance_fade_length(float p_length) { +void Decal::set_distance_fade_length(real_t p_length) { distance_fade_length = p_length; RS::get_singleton()->decal_set_distance_fade(decal, distance_fade_enabled, distance_fade_begin, distance_fade_length); } -float Decal::get_distance_fade_length() const { +real_t Decal::get_distance_fade_length() const { return distance_fade_length; } void Decal::set_cull_mask(uint32_t p_layers) { cull_mask = p_layers; RS::get_singleton()->decal_set_cull_mask(decal, cull_mask); + update_configuration_warnings(); } uint32_t Decal::get_cull_mask() const { @@ -154,6 +156,33 @@ Vector<Face3> Decal::get_faces(uint32_t p_usage_flags) const { return Vector<Face3>(); } +void Decal::_validate_property(PropertyInfo &property) const { + if (!distance_fade_enabled && (property.name == "distance_fade_begin" || property.name == "distance_fade_length")) { + property.usage = PROPERTY_USAGE_NOEDITOR; + } +} + +TypedArray<String> Decal::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); + + if (textures[TEXTURE_ALBEDO].is_null() && textures[TEXTURE_NORMAL].is_null() && textures[TEXTURE_ORM].is_null() && textures[TEXTURE_EMISSION].is_null()) { + warnings.push_back(TTR("The decal has no textures loaded into any of its texture properties, and will therefore not be visible.")); + } + + if ((textures[TEXTURE_NORMAL].is_valid() || textures[TEXTURE_ORM].is_valid()) && textures[TEXTURE_ALBEDO].is_null()) { + warnings.push_back(TTR("The decal has a Normal and/or ORM texture, but no Albedo texture is set.\nAn Albedo texture with an alpha channel is required to blend the normal/ORM maps onto the underlying surface.\nIf you don't want the Albedo texture to be visible, set Albedo Mix to 0.")); + } + + if (cull_mask == 0) { + // NOTE: This warning will not be emitted if none of the 20 checkboxes + // exposed in the editor are checked. This is because there are + // currently 12 unexposed layers in the editor inspector. + warnings.push_back(TTR("The decal's Cull Mask has no bits enabled, which means the decal will not paint objects on any layer.\nTo resolve this, enable at least one bit in the Cull Mask property.")); + } + + return warnings; +} + void Decal::_bind_methods() { ClassDB::bind_method(D_METHOD("set_extents", "extents"), &Decal::set_extents); ClassDB::bind_method(D_METHOD("get_extents"), &Decal::get_extents); @@ -201,7 +230,9 @@ void Decal::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "emission_energy", PROPERTY_HINT_RANGE, "0,128,0.01"), "set_emission_energy", "get_emission_energy"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "modulate"), "set_modulate", "get_modulate"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "albedo_mix", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_albedo_mix", "get_albedo_mix"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "normal_fade", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_normal_fade", "get_normal_fade"); + // A Normal Fade of 1.0 causes the decal to be invisible even if fully perpendicular to a surface. + // Due to this, limit Normal Fade to 0.999. + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "normal_fade", PROPERTY_HINT_RANGE, "0,0.999,0.001"), "set_normal_fade", "get_normal_fade"); ADD_GROUP("Vertical Fade", ""); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "upper_fade", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_upper_fade", "get_upper_fade"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lower_fade", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_lower_fade", "get_lower_fade"); @@ -220,18 +251,6 @@ void Decal::_bind_methods() { } Decal::Decal() { - extents = Vector3(1, 1, 1); - emission_energy = 1.0; - modulate = Color(1, 1, 1, 1); - albedo_mix = 1.0; - cull_mask = (1 << 20) - 1; - upper_fade = 0.3; - lower_fade = 0.3; - normal_fade = 0; - distance_fade_enabled = false; - distance_fade_begin = 10; - distance_fade_length = 1; - decal = RenderingServer::get_singleton()->decal_create(); RS::get_singleton()->instance_set_base(get_instance(), decal); } diff --git a/scene/3d/decal.h b/scene/3d/decal.h index e821461772..e9bda3276d 100644 --- a/scene/3d/decal.h +++ b/scene/3d/decal.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,8 +32,6 @@ #define DECAL_H #include "scene/3d/visual_instance_3d.h" -#include "scene/resources/texture.h" -#include "servers/rendering_server.h" class Decal : public VisualInstance3D { GDCLASS(Decal, VisualInstance3D); @@ -49,55 +47,58 @@ public: private: RID decal; - Vector3 extents; + Vector3 extents = Vector3(1, 1, 1); Ref<Texture2D> textures[TEXTURE_MAX]; - float emission_energy; - float albedo_mix; - Color modulate; - uint32_t cull_mask; - float normal_fade; - float upper_fade; - float lower_fade; - bool distance_fade_enabled; - float distance_fade_begin; - float distance_fade_length; + real_t emission_energy = 1.0; + real_t albedo_mix = 1.0; + Color modulate = Color(1, 1, 1, 1); + uint32_t cull_mask = (1 << 20) - 1; + real_t normal_fade = 0.0; + real_t upper_fade = 0.3; + real_t lower_fade = 0.3; + bool distance_fade_enabled = false; + real_t distance_fade_begin = 10.0; + real_t distance_fade_length = 1.0; protected: static void _bind_methods(); + void _validate_property(PropertyInfo &property) const override; public: + virtual TypedArray<String> get_configuration_warnings() const override; + void set_extents(const Vector3 &p_extents); Vector3 get_extents() const; void set_texture(DecalTexture p_type, const Ref<Texture2D> &p_texture); Ref<Texture2D> get_texture(DecalTexture p_type) const; - void set_emission_energy(float p_energy); - float get_emission_energy() const; + void set_emission_energy(real_t p_energy); + real_t get_emission_energy() const; - void set_albedo_mix(float p_mix); - float get_albedo_mix() const; + void set_albedo_mix(real_t p_mix); + real_t get_albedo_mix() const; void set_modulate(Color p_modulate); Color get_modulate() const; - void set_upper_fade(float p_energy); - float get_upper_fade() const; + void set_upper_fade(real_t p_energy); + real_t get_upper_fade() const; - void set_lower_fade(float p_fade); - float get_lower_fade() const; + void set_lower_fade(real_t p_fade); + real_t get_lower_fade() const; - void set_normal_fade(float p_fade); - float get_normal_fade() const; + void set_normal_fade(real_t p_fade); + real_t get_normal_fade() const; void set_enable_distance_fade(bool p_enable); bool is_distance_fade_enabled() const; - void set_distance_fade_begin(float p_distance); - float get_distance_fade_begin() const; + void set_distance_fade_begin(real_t p_distance); + real_t get_distance_fade_begin() const; - void set_distance_fade_length(float p_length); - float get_distance_fade_length() const; + void set_distance_fade_length(real_t p_length); + real_t get_distance_fade_length() const; void set_cull_mask(uint32_t p_layers); uint32_t get_cull_mask() const; diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index da5c99a873..baf28ae102 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,11 +30,8 @@ #include "gpu_particles_3d.h" -#include "core/os/os.h" #include "scene/resources/particles_material.h" -#include "servers/rendering_server.h" - AABB GPUParticles3D::get_aabb() const { return AABB(); } @@ -59,7 +56,7 @@ void GPUParticles3D::set_amount(int p_amount) { RS::get_singleton()->particles_set_amount(particles, amount); } -void GPUParticles3D::set_lifetime(float p_lifetime) { +void GPUParticles3D::set_lifetime(double p_lifetime) { ERR_FAIL_COND_MSG(p_lifetime <= 0, "Particles lifetime must be greater than 0."); lifetime = p_lifetime; RS::get_singleton()->particles_set_lifetime(particles, lifetime); @@ -81,17 +78,17 @@ void GPUParticles3D::set_one_shot(bool p_one_shot) { } } -void GPUParticles3D::set_pre_process_time(float p_time) { +void GPUParticles3D::set_pre_process_time(double p_time) { pre_process_time = p_time; RS::get_singleton()->particles_set_pre_process_time(particles, pre_process_time); } -void GPUParticles3D::set_explosiveness_ratio(float p_ratio) { +void GPUParticles3D::set_explosiveness_ratio(real_t p_ratio) { explosiveness_ratio = p_ratio; RS::get_singleton()->particles_set_explosiveness_ratio(particles, explosiveness_ratio); } -void GPUParticles3D::set_randomness_ratio(float p_ratio) { +void GPUParticles3D::set_randomness_ratio(real_t p_ratio) { randomness_ratio = p_ratio; RS::get_singleton()->particles_set_randomness_ratio(particles, randomness_ratio); } @@ -99,8 +96,7 @@ void GPUParticles3D::set_randomness_ratio(float p_ratio) { void GPUParticles3D::set_visibility_aabb(const AABB &p_aabb) { visibility_aabb = p_aabb; RS::get_singleton()->particles_set_custom_aabb(particles, visibility_aabb); - update_gizmo(); - _change_notify("visibility_aabb"); + update_gizmos(); } void GPUParticles3D::set_use_local_coordinates(bool p_enable) { @@ -116,14 +112,19 @@ void GPUParticles3D::set_process_material(const Ref<Material> &p_material) { } RS::get_singleton()->particles_set_process_material(particles, material_rid); - update_configuration_warning(); + update_configuration_warnings(); } -void GPUParticles3D::set_speed_scale(float p_scale) { +void GPUParticles3D::set_speed_scale(double p_scale) { speed_scale = p_scale; RS::get_singleton()->particles_set_speed_scale(particles, p_scale); } +void GPUParticles3D::set_collision_base_size(real_t p_size) { + collision_base_size = p_size; + RS::get_singleton()->particles_set_collision_base_size(particles, p_size); +} + bool GPUParticles3D::is_emitting() const { return RS::get_singleton()->particles_get_emitting(particles); } @@ -132,7 +133,7 @@ int GPUParticles3D::get_amount() const { return amount; } -float GPUParticles3D::get_lifetime() const { +double GPUParticles3D::get_lifetime() const { return lifetime; } @@ -140,15 +141,15 @@ bool GPUParticles3D::get_one_shot() const { return one_shot; } -float GPUParticles3D::get_pre_process_time() const { +double GPUParticles3D::get_pre_process_time() const { return pre_process_time; } -float GPUParticles3D::get_explosiveness_ratio() const { +real_t GPUParticles3D::get_explosiveness_ratio() const { return explosiveness_ratio; } -float GPUParticles3D::get_randomness_ratio() const { +real_t GPUParticles3D::get_randomness_ratio() const { return randomness_ratio; } @@ -164,24 +165,51 @@ Ref<Material> GPUParticles3D::get_process_material() const { return process_material; } -float GPUParticles3D::get_speed_scale() const { +double GPUParticles3D::get_speed_scale() const { return speed_scale; } +real_t GPUParticles3D::get_collision_base_size() const { + return collision_base_size; +} + void GPUParticles3D::set_draw_order(DrawOrder p_order) { draw_order = p_order; RS::get_singleton()->particles_set_draw_order(particles, RS::ParticlesDrawOrder(p_order)); } +void GPUParticles3D::set_trail_enabled(bool p_enabled) { + trail_enabled = p_enabled; + RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_length); + update_configuration_warnings(); +} + +void GPUParticles3D::set_trail_length(double p_seconds) { + ERR_FAIL_COND(p_seconds < 0.001); + trail_length = p_seconds; + RS::get_singleton()->particles_set_trails(particles, trail_enabled, trail_length); +} + +bool GPUParticles3D::is_trail_enabled() const { + return trail_enabled; +} + +double GPUParticles3D::get_trail_length() const { + return trail_length; +} + GPUParticles3D::DrawOrder GPUParticles3D::get_draw_order() const { return draw_order; } void GPUParticles3D::set_draw_passes(int p_count) { ERR_FAIL_COND(p_count < 1); + for (int i = p_count; i < draw_passes.size(); i++) { + set_draw_pass_mesh(i, Ref<Mesh>()); + } draw_passes.resize(p_count); RS::get_singleton()->particles_set_draw_passes(particles, p_count); - _change_notify(); + notify_property_list_changed(); } int GPUParticles3D::get_draw_passes() const { @@ -191,8 +219,16 @@ int GPUParticles3D::get_draw_passes() const { void GPUParticles3D::set_draw_pass_mesh(int p_pass, const Ref<Mesh> &p_mesh) { ERR_FAIL_INDEX(p_pass, draw_passes.size()); + if (Engine::get_singleton()->is_editor_hint() && draw_passes.write[p_pass].is_valid()) { + draw_passes.write[p_pass]->disconnect("changed", callable_mp((Node *)this, &Node::update_configuration_warnings)); + } + draw_passes.write[p_pass] = p_mesh; + if (Engine::get_singleton()->is_editor_hint() && draw_passes.write[p_pass].is_valid()) { + draw_passes.write[p_pass]->connect("changed", callable_mp((Node *)this, &Node::update_configuration_warnings), varray(), CONNECT_DEFERRED); + } + RID mesh_rid; if (p_mesh.is_valid()) { mesh_rid = p_mesh->get_rid(); @@ -200,7 +236,8 @@ void GPUParticles3D::set_draw_pass_mesh(int p_pass, const Ref<Mesh> &p_mesh) { RS::get_singleton()->particles_set_draw_pass_mesh(particles, p_pass, mesh_rid); - update_configuration_warning(); + _skinning_changed(); + update_configuration_warnings(); } Ref<Mesh> GPUParticles3D::get_draw_pass_mesh(int p_pass) const { @@ -227,13 +264,22 @@ bool GPUParticles3D::get_fractional_delta() const { return fractional_delta; } -String GPUParticles3D::get_configuration_warning() const { +void GPUParticles3D::set_interpolate(bool p_enable) { + interpolate = p_enable; + RS::get_singleton()->particles_set_interpolate(particles, p_enable); +} + +bool GPUParticles3D::get_interpolate() const { + return interpolate; +} + +TypedArray<String> GPUParticles3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); + if (RenderingServer::get_singleton()->is_low_end()) { - return TTR("GPU-based particles are not supported by the GLES2 video driver.\nUse the CPUParticles3D node instead. You can use the \"Convert to CPUParticles3D\" option for this purpose."); + warnings.push_back(TTR("GPU-based particles are not supported by the GLES2 video driver.\nUse the CPUParticles3D node instead. You can use the \"Convert to CPUParticles3D\" option for this purpose.")); } - String warnings = GeometryInstance3D::get_configuration_warning(); - bool meshes_found = false; bool anim_material_found = false; @@ -242,7 +288,7 @@ String GPUParticles3D::get_configuration_warning() const { meshes_found = true; for (int j = 0; j < draw_passes[i]->get_surface_count(); j++) { anim_material_found = Object::cast_to<ShaderMaterial>(draw_passes[i]->surface_get_material(j).ptr()) != nullptr; - StandardMaterial3D *spat = Object::cast_to<StandardMaterial3D>(draw_passes[i]->surface_get_material(j).ptr()); + BaseMaterial3D *spat = Object::cast_to<BaseMaterial3D>(draw_passes[i]->surface_get_material(j).ptr()); anim_material_found = anim_material_found || (spat && spat->get_billboard_mode() == StandardMaterial3D::BILLBOARD_PARTICLES); } if (anim_material_found) { @@ -252,30 +298,73 @@ String GPUParticles3D::get_configuration_warning() const { } anim_material_found = anim_material_found || Object::cast_to<ShaderMaterial>(get_material_override().ptr()) != nullptr; - StandardMaterial3D *spat = Object::cast_to<StandardMaterial3D>(get_material_override().ptr()); - anim_material_found = anim_material_found || (spat && spat->get_billboard_mode() == StandardMaterial3D::BILLBOARD_PARTICLES); + { + BaseMaterial3D *spat = Object::cast_to<BaseMaterial3D>(get_material_override().ptr()); + anim_material_found = anim_material_found || (spat && spat->get_billboard_mode() == BaseMaterial3D::BILLBOARD_PARTICLES); + } if (!meshes_found) { - if (warnings != String()) { - warnings += "\n"; - } - warnings += "- " + TTR("Nothing is visible because meshes have not been assigned to draw passes."); + warnings.push_back(TTR("Nothing is visible because meshes have not been assigned to draw passes.")); } if (process_material.is_null()) { - if (warnings != String()) { - warnings += "\n"; - } - warnings += "- " + TTR("A material to process the particles is not assigned, so no behavior is imprinted."); + warnings.push_back(TTR("A material to process the particles is not assigned, so no behavior is imprinted.")); } else { const ParticlesMaterial *process = Object::cast_to<ParticlesMaterial>(process_material.ptr()); if (!anim_material_found && process && - (process->get_param(ParticlesMaterial::PARAM_ANIM_SPEED) != 0.0 || process->get_param(ParticlesMaterial::PARAM_ANIM_OFFSET) != 0.0 || + (process->get_param_max(ParticlesMaterial::PARAM_ANIM_SPEED) != 0.0 || process->get_param_max(ParticlesMaterial::PARAM_ANIM_OFFSET) != 0.0 || process->get_param_texture(ParticlesMaterial::PARAM_ANIM_SPEED).is_valid() || process->get_param_texture(ParticlesMaterial::PARAM_ANIM_OFFSET).is_valid())) { - if (warnings != String()) { - warnings += "\n"; + warnings.push_back(TTR("Particles animation requires the usage of a BaseMaterial3D whose Billboard Mode is set to \"Particle Billboard\".")); + } + } + + if (trail_enabled) { + int dp_count = 0; + bool missing_trails = false; + bool no_materials = false; + + for (int i = 0; i < draw_passes.size(); i++) { + Ref<Mesh> draw_pass = draw_passes[i]; + if (draw_pass.is_valid() && draw_pass->get_builtin_bind_pose_count() > 0) { + dp_count++; } - warnings += "- " + TTR("Particles animation requires the usage of a StandardMaterial3D whose Billboard Mode is set to \"Particle Billboard\"."); + + if (draw_pass.is_valid()) { + int mats_found = 0; + for (int j = 0; j < draw_passes[i]->get_surface_count(); j++) { + BaseMaterial3D *spat = Object::cast_to<BaseMaterial3D>(draw_passes[i]->surface_get_material(j).ptr()); + if (spat) { + mats_found++; + } + if (spat && !spat->get_flag(BaseMaterial3D::FLAG_PARTICLE_TRAILS_MODE)) { + missing_trails = true; + } + } + + if (mats_found != draw_passes[i]->get_surface_count()) { + no_materials = true; + } + } + } + + BaseMaterial3D *spat = Object::cast_to<BaseMaterial3D>(get_material_override().ptr()); + if (spat) { + no_materials = false; + } + if (spat && !spat->get_flag(BaseMaterial3D::FLAG_PARTICLE_TRAILS_MODE)) { + missing_trails = true; + } + + if (dp_count && skin.is_valid()) { + warnings.push_back(TTR("Using Trail meshes with a skin causes Skin to override Trail poses. Suggest removing the Skin.")); + } else if (dp_count == 0 && skin.is_null()) { + warnings.push_back(TTR("Trails active, but neither Trail meshes or a Skin were found.")); + } else if (dp_count > 1) { + warnings.push_back(TTR("Only one Trail mesh is supported. If you want to use more than a single mesh, a Skin is needed (see documentation).")); + } + + if ((dp_count || !skin.is_null()) && (missing_trails || no_materials)) { + warnings.push_back(TTR("Trails enabled, but one or more mesh materials are either missing or not set for trails rendering.")); } } @@ -295,13 +384,13 @@ void GPUParticles3D::_validate_property(PropertyInfo &property) const { if (property.name.begins_with("draw_pass_")) { int index = property.name.get_slicec('_', 2).to_int() - 1; if (index >= draw_passes.size()) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; return; } } } -void GPUParticles3D::emit_particle(const Transform &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) { +void GPUParticles3D::emit_particle(const Transform3D &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags) { RS::get_singleton()->particles_emit(particles, p_transform, p_velocity, p_color, p_custom, p_emit_flags); } @@ -340,11 +429,11 @@ void GPUParticles3D::_notification(int p_what) { } } - // Use internal process when emitting and one_shot are on so that when + // Use internal process when emitting and one_shot is on so that when // the shot ends the editor can properly update if (p_what == NOTIFICATION_INTERNAL_PROCESS) { if (one_shot && !is_emitting()) { - _change_notify(); + notify_property_list_changed(); set_process_internal(false); } } @@ -367,6 +456,48 @@ void GPUParticles3D::_notification(int p_what) { } } +void GPUParticles3D::_skinning_changed() { + Vector<Transform3D> xforms; + if (skin.is_valid()) { + xforms.resize(skin->get_bind_count()); + for (int i = 0; i < skin->get_bind_count(); i++) { + xforms.write[i] = skin->get_bind_pose(i); + } + } else { + for (int i = 0; i < draw_passes.size(); i++) { + Ref<Mesh> draw_pass = draw_passes[i]; + if (draw_pass.is_valid() && draw_pass->get_builtin_bind_pose_count() > 0) { + xforms.resize(draw_pass->get_builtin_bind_pose_count()); + for (int j = 0; j < draw_pass->get_builtin_bind_pose_count(); j++) { + xforms.write[i] = draw_pass->get_builtin_bind_pose(j); + } + break; + } + } + } + + RS::get_singleton()->particles_set_trail_bind_poses(particles, xforms); + update_configuration_warnings(); +} + +void GPUParticles3D::set_skin(const Ref<Skin> &p_skin) { + skin = p_skin; + _skinning_changed(); +} + +Ref<Skin> GPUParticles3D::get_skin() const { + return skin; +} + +void GPUParticles3D::set_transform_align(TransformAlign p_align) { + ERR_FAIL_INDEX(uint32_t(p_align), 4); + transform_align = p_align; + RS::get_singleton()->particles_set_transform_align(particles, RS::ParticlesTransformAlign(transform_align)); +} +GPUParticles3D::TransformAlign GPUParticles3D::get_transform_align() const { + return transform_align; +} + void GPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_emitting", "emitting"), &GPUParticles3D::set_emitting); ClassDB::bind_method(D_METHOD("set_amount", "amount"), &GPUParticles3D::set_amount); @@ -379,8 +510,10 @@ void GPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_use_local_coordinates", "enable"), &GPUParticles3D::set_use_local_coordinates); ClassDB::bind_method(D_METHOD("set_fixed_fps", "fps"), &GPUParticles3D::set_fixed_fps); ClassDB::bind_method(D_METHOD("set_fractional_delta", "enable"), &GPUParticles3D::set_fractional_delta); + ClassDB::bind_method(D_METHOD("set_interpolate", "enable"), &GPUParticles3D::set_interpolate); ClassDB::bind_method(D_METHOD("set_process_material", "material"), &GPUParticles3D::set_process_material); ClassDB::bind_method(D_METHOD("set_speed_scale", "scale"), &GPUParticles3D::set_speed_scale); + ClassDB::bind_method(D_METHOD("set_collision_base_size", "size"), &GPUParticles3D::set_collision_base_size); ClassDB::bind_method(D_METHOD("is_emitting"), &GPUParticles3D::is_emitting); ClassDB::bind_method(D_METHOD("get_amount"), &GPUParticles3D::get_amount); @@ -393,8 +526,10 @@ void GPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_use_local_coordinates"), &GPUParticles3D::get_use_local_coordinates); ClassDB::bind_method(D_METHOD("get_fixed_fps"), &GPUParticles3D::get_fixed_fps); ClassDB::bind_method(D_METHOD("get_fractional_delta"), &GPUParticles3D::get_fractional_delta); + ClassDB::bind_method(D_METHOD("get_interpolate"), &GPUParticles3D::get_interpolate); ClassDB::bind_method(D_METHOD("get_process_material"), &GPUParticles3D::get_process_material); ClassDB::bind_method(D_METHOD("get_speed_scale"), &GPUParticles3D::get_speed_scale); + ClassDB::bind_method(D_METHOD("get_collision_base_size"), &GPUParticles3D::get_collision_base_size); ClassDB::bind_method(D_METHOD("set_draw_order", "order"), &GPUParticles3D::set_draw_order); @@ -406,6 +541,9 @@ void GPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_draw_passes"), &GPUParticles3D::get_draw_passes); ClassDB::bind_method(D_METHOD("get_draw_pass_mesh", "pass"), &GPUParticles3D::get_draw_pass_mesh); + ClassDB::bind_method(D_METHOD("set_skin", "skin"), &GPUParticles3D::set_skin); + ClassDB::bind_method(D_METHOD("get_skin"), &GPUParticles3D::get_skin); + ClassDB::bind_method(D_METHOD("restart"), &GPUParticles3D::restart); ClassDB::bind_method(D_METHOD("capture_aabb"), &GPUParticles3D::capture_aabb); @@ -414,22 +552,38 @@ void GPUParticles3D::_bind_methods() { ClassDB::bind_method(D_METHOD("emit_particle", "xform", "velocity", "color", "custom", "flags"), &GPUParticles3D::emit_particle); + ClassDB::bind_method(D_METHOD("set_trail_enabled", "enabled"), &GPUParticles3D::set_trail_enabled); + ClassDB::bind_method(D_METHOD("set_trail_length", "secs"), &GPUParticles3D::set_trail_length); + + ClassDB::bind_method(D_METHOD("is_trail_enabled"), &GPUParticles3D::is_trail_enabled); + ClassDB::bind_method(D_METHOD("get_trail_length"), &GPUParticles3D::get_trail_length); + + ClassDB::bind_method(D_METHOD("set_transform_align", "align"), &GPUParticles3D::set_transform_align); + ClassDB::bind_method(D_METHOD("get_transform_align"), &GPUParticles3D::get_transform_align); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "emitting"), "set_emitting", "is_emitting"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_EXP_RANGE, "1,1000000,1"), "set_amount", "get_amount"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "amount", PROPERTY_HINT_RANGE, "1,1000000,1,exp"), "set_amount", "get_amount"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "sub_emitter", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "GPUParticles3D"), "set_sub_emitter", "get_sub_emitter"); ADD_GROUP("Time", ""); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_EXP_RANGE, "0.01,600.0,0.01,or_greater"), "set_lifetime", "get_lifetime"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lifetime", PROPERTY_HINT_RANGE, "0.01,600.0,0.01,or_greater,exp"), "set_lifetime", "get_lifetime"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "one_shot"), "set_one_shot", "get_one_shot"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_EXP_RANGE, "0.00,600.0,0.01"), "set_pre_process_time", "get_pre_process_time"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "preprocess", PROPERTY_HINT_RANGE, "0.00,600.0,0.01,exp"), "set_pre_process_time", "get_pre_process_time"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "speed_scale", PROPERTY_HINT_RANGE, "0,64,0.01"), "set_speed_scale", "get_speed_scale"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "explosiveness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_explosiveness_ratio", "get_explosiveness_ratio"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "randomness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_randomness_ratio", "get_randomness_ratio"); ADD_PROPERTY(PropertyInfo(Variant::INT, "fixed_fps", PROPERTY_HINT_RANGE, "0,1000,1"), "set_fixed_fps", "get_fixed_fps"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interpolate"), "set_interpolate", "get_interpolate"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "fract_delta"), "set_fractional_delta", "get_fractional_delta"); + ADD_GROUP("Collision", "collision_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision_base_size", PROPERTY_HINT_RANGE, "0,128,0.01,or_greater"), "set_collision_base_size", "get_collision_base_size"); ADD_GROUP("Drawing", ""); ADD_PROPERTY(PropertyInfo(Variant::AABB, "visibility_aabb"), "set_visibility_aabb", "get_visibility_aabb"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "local_coords"), "set_use_local_coordinates", "get_use_local_coordinates"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_order", PROPERTY_HINT_ENUM, "Index,Lifetime,View Depth"), "set_draw_order", "get_draw_order"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "draw_order", PROPERTY_HINT_ENUM, "Index,Lifetime,Reverse Lifetime,View Depth"), "set_draw_order", "get_draw_order"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "transform_align", PROPERTY_HINT_ENUM, "Disabled,ZBillboard,YToVelocity,ZBillboardYToVelocity"), "set_transform_align", "get_transform_align"); + ADD_GROUP("Trails", "trail_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "trail_enabled"), "set_trail_enabled", "is_trail_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "trail_length_secs", PROPERTY_HINT_RANGE, "0.01,10,0.01"), "set_trail_length", "get_trail_length"); ADD_GROUP("Process Material", ""); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "process_material", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,ParticlesMaterial"), "set_process_material", "get_process_material"); ADD_GROUP("Draw Passes", "draw_"); @@ -437,9 +591,11 @@ void GPUParticles3D::_bind_methods() { for (int i = 0; i < MAX_DRAW_PASSES; i++) { ADD_PROPERTYI(PropertyInfo(Variant::OBJECT, "draw_pass_" + itos(i + 1), PROPERTY_HINT_RESOURCE_TYPE, "Mesh"), "set_draw_pass_mesh", "get_draw_pass_mesh", i); } + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "draw_skin", PROPERTY_HINT_RESOURCE_TYPE, "Skin"), "set_skin", "get_skin"); BIND_ENUM_CONSTANT(DRAW_ORDER_INDEX); BIND_ENUM_CONSTANT(DRAW_ORDER_LIFETIME); + BIND_ENUM_CONSTANT(DRAW_ORDER_REVERSE_LIFETIME); BIND_ENUM_CONSTANT(DRAW_ORDER_VIEW_DEPTH); BIND_ENUM_CONSTANT(EMIT_FLAG_POSITION); @@ -449,26 +605,36 @@ void GPUParticles3D::_bind_methods() { BIND_ENUM_CONSTANT(EMIT_FLAG_CUSTOM); BIND_CONSTANT(MAX_DRAW_PASSES); + + BIND_ENUM_CONSTANT(TRANSFORM_ALIGN_DISABLED); + BIND_ENUM_CONSTANT(TRANSFORM_ALIGN_Z_BILLBOARD); + BIND_ENUM_CONSTANT(TRANSFORM_ALIGN_Y_TO_VELOCITY); + BIND_ENUM_CONSTANT(TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY); } GPUParticles3D::GPUParticles3D() { particles = RS::get_singleton()->particles_create(); + RS::get_singleton()->particles_set_mode(particles, RS::PARTICLES_MODE_3D); set_base(particles); one_shot = false; // Needed so that set_emitting doesn't access uninitialized values set_emitting(true); set_one_shot(false); set_amount(8); set_lifetime(1); - set_fixed_fps(0); + set_fixed_fps(30); set_fractional_delta(true); + set_interpolate(true); set_pre_process_time(0); set_explosiveness_ratio(0); set_randomness_ratio(0); + set_trail_length(0.3); set_visibility_aabb(AABB(Vector3(-4, -4, -4), Vector3(8, 8, 8))); set_use_local_coordinates(true); set_draw_passes(1); set_draw_order(DRAW_ORDER_INDEX); set_speed_scale(1); + set_collision_base_size(collision_base_size); + set_transform_align(TRANSFORM_ALIGN_DISABLED); } GPUParticles3D::~GPUParticles3D() { diff --git a/scene/3d/gpu_particles_3d.h b/scene/3d/gpu_particles_3d.h index 0d8dadd31d..5e96f660da 100644 --- a/scene/3d/gpu_particles_3d.h +++ b/scene/3d/gpu_particles_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,9 +31,8 @@ #ifndef PARTICLES_H #define PARTICLES_H -#include "core/rid.h" #include "scene/3d/visual_instance_3d.h" -#include "scene/resources/material.h" +#include "scene/resources/skin.h" class GPUParticles3D : public GeometryInstance3D { private: @@ -43,9 +42,17 @@ public: enum DrawOrder { DRAW_ORDER_INDEX, DRAW_ORDER_LIFETIME, + DRAW_ORDER_REVERSE_LIFETIME, DRAW_ORDER_VIEW_DEPTH, }; + enum TransformAlign { + TRANSFORM_ALIGN_DISABLED, + TRANSFORM_ALIGN_Z_BILLBOARD, + TRANSFORM_ALIGN_Y_TO_VELOCITY, + TRANSFORM_ALIGN_Z_BILLBOARD_Y_TO_VELOCITY + }; + enum { MAX_DRAW_PASSES = 4 }; @@ -55,25 +62,35 @@ private: bool one_shot; int amount; - float lifetime; - float pre_process_time; - float explosiveness_ratio; - float randomness_ratio; - float speed_scale; + double lifetime; + double pre_process_time; + real_t explosiveness_ratio; + real_t randomness_ratio; + double speed_scale; AABB visibility_aabb; bool local_coords; int fixed_fps; bool fractional_delta; + bool interpolate = true; NodePath sub_emitter; + real_t collision_base_size = 0.01; + + bool trail_enabled = false; + double trail_length = 0.3; + + TransformAlign transform_align = TRANSFORM_ALIGN_DISABLED; Ref<Material> process_material; DrawOrder draw_order; Vector<Ref<Mesh>> draw_passes; + Ref<Skin> skin; void _attach_sub_emitter(); + void _skinning_changed(); + protected: static void _bind_methods(); void _notification(int p_what); @@ -85,27 +102,33 @@ public: void set_emitting(bool p_emitting); void set_amount(int p_amount); - void set_lifetime(float p_lifetime); + void set_lifetime(double p_lifetime); void set_one_shot(bool p_one_shot); - void set_pre_process_time(float p_time); - void set_explosiveness_ratio(float p_ratio); - void set_randomness_ratio(float p_ratio); + void set_pre_process_time(double p_time); + void set_explosiveness_ratio(real_t p_ratio); + void set_randomness_ratio(real_t p_ratio); void set_visibility_aabb(const AABB &p_aabb); void set_use_local_coordinates(bool p_enable); void set_process_material(const Ref<Material> &p_material); - void set_speed_scale(float p_scale); + void set_speed_scale(double p_scale); + void set_collision_base_size(real_t p_ratio); + void set_trail_enabled(bool p_enabled); + void set_trail_length(double p_seconds); bool is_emitting() const; int get_amount() const; - float get_lifetime() const; + double get_lifetime() const; bool get_one_shot() const; - float get_pre_process_time() const; - float get_explosiveness_ratio() const; - float get_randomness_ratio() const; + double get_pre_process_time() const; + real_t get_explosiveness_ratio() const; + real_t get_randomness_ratio() const; AABB get_visibility_aabb() const; bool get_use_local_coordinates() const; Ref<Material> get_process_material() const; - float get_speed_scale() const; + double get_speed_scale() const; + real_t get_collision_base_size() const; + bool is_trail_enabled() const; + double get_trail_length() const; void set_fixed_fps(int p_count); int get_fixed_fps() const; @@ -113,6 +136,9 @@ public: void set_fractional_delta(bool p_enable); bool get_fractional_delta() const; + void set_interpolate(bool p_enable); + bool get_interpolate() const; + void set_draw_order(DrawOrder p_order); DrawOrder get_draw_order() const; @@ -122,11 +148,17 @@ public: void set_draw_pass_mesh(int p_pass, const Ref<Mesh> &p_mesh); Ref<Mesh> get_draw_pass_mesh(int p_pass) const; - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; void set_sub_emitter(const NodePath &p_path); NodePath get_sub_emitter() const; + void set_skin(const Ref<Skin> &p_skin); + Ref<Skin> get_skin() const; + + void set_transform_align(TransformAlign p_align); + TransformAlign get_transform_align() const; + void restart(); enum EmitFlags { @@ -137,7 +169,7 @@ public: EMIT_FLAG_CUSTOM = RS::PARTICLES_EMIT_FLAG_CUSTOM }; - void emit_particle(const Transform &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags); + void emit_particle(const Transform3D &p_transform, const Vector3 &p_velocity, const Color &p_color, const Color &p_custom, uint32_t p_emit_flags); AABB capture_aabb() const; GPUParticles3D(); @@ -145,6 +177,7 @@ public: }; VARIANT_ENUM_CAST(GPUParticles3D::DrawOrder) +VARIANT_ENUM_CAST(GPUParticles3D::TransformAlign) VARIANT_ENUM_CAST(GPUParticles3D::EmitFlags) #endif // PARTICLES_H diff --git a/scene/3d/gpu_particles_collision_3d.cpp b/scene/3d/gpu_particles_collision_3d.cpp new file mode 100644 index 0000000000..a34a30913e --- /dev/null +++ b/scene/3d/gpu_particles_collision_3d.cpp @@ -0,0 +1,898 @@ +/*************************************************************************/ +/* gpu_particles_collision_3d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "gpu_particles_collision_3d.h" + +#include "mesh_instance_3d.h" +#include "scene/3d/camera_3d.h" +#include "scene/main/viewport.h" + +void GPUParticlesCollision3D::set_cull_mask(uint32_t p_cull_mask) { + cull_mask = p_cull_mask; + RS::get_singleton()->particles_collision_set_cull_mask(collision, p_cull_mask); +} + +uint32_t GPUParticlesCollision3D::get_cull_mask() const { + return cull_mask; +} + +void GPUParticlesCollision3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_cull_mask", "mask"), &GPUParticlesCollision3D::set_cull_mask); + ClassDB::bind_method(D_METHOD("get_cull_mask"), &GPUParticlesCollision3D::get_cull_mask); + + ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask"); +} + +GPUParticlesCollision3D::GPUParticlesCollision3D(RS::ParticlesCollisionType p_type) { + collision = RS::get_singleton()->particles_collision_create(); + RS::get_singleton()->particles_collision_set_collision_type(collision, p_type); + set_base(collision); +} + +GPUParticlesCollision3D::~GPUParticlesCollision3D() { + RS::get_singleton()->free(collision); +} + +///////////////////////////////// + +void GPUParticlesCollisionSphere::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_radius", "radius"), &GPUParticlesCollisionSphere::set_radius); + ClassDB::bind_method(D_METHOD("get_radius"), &GPUParticlesCollisionSphere::get_radius); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_radius", "get_radius"); +} + +void GPUParticlesCollisionSphere::set_radius(real_t p_radius) { + radius = p_radius; + RS::get_singleton()->particles_collision_set_sphere_radius(_get_collision(), radius); + update_gizmos(); +} + +real_t GPUParticlesCollisionSphere::get_radius() const { + return radius; +} + +AABB GPUParticlesCollisionSphere::get_aabb() const { + return AABB(Vector3(-radius, -radius, -radius), Vector3(radius * 2, radius * 2, radius * 2)); +} + +GPUParticlesCollisionSphere::GPUParticlesCollisionSphere() : + GPUParticlesCollision3D(RS::PARTICLES_COLLISION_TYPE_SPHERE_COLLIDE) { +} + +GPUParticlesCollisionSphere::~GPUParticlesCollisionSphere() { +} + +/////////////////////////// + +void GPUParticlesCollisionBox::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesCollisionBox::set_extents); + ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesCollisionBox::get_extents); + + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents"); +} + +void GPUParticlesCollisionBox::set_extents(const Vector3 &p_extents) { + extents = p_extents; + RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents); + update_gizmos(); +} + +Vector3 GPUParticlesCollisionBox::get_extents() const { + return extents; +} + +AABB GPUParticlesCollisionBox::get_aabb() const { + return AABB(-extents, extents * 2); +} + +GPUParticlesCollisionBox::GPUParticlesCollisionBox() : + GPUParticlesCollision3D(RS::PARTICLES_COLLISION_TYPE_BOX_COLLIDE) { +} + +GPUParticlesCollisionBox::~GPUParticlesCollisionBox() { +} + +/////////////////////////////// +/////////////////////////// + +void GPUParticlesCollisionSDF::_find_meshes(const AABB &p_aabb, Node *p_at_node, List<PlotMesh> &plot_meshes) { + MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_at_node); + if (mi && mi->is_visible_in_tree()) { + Ref<Mesh> mesh = mi->get_mesh(); + if (mesh.is_valid()) { + AABB aabb = mesh->get_aabb(); + + Transform3D xf = get_global_transform().affine_inverse() * mi->get_global_transform(); + + if (p_aabb.intersects(xf.xform(aabb))) { + PlotMesh pm; + pm.local_xform = xf; + pm.mesh = mesh; + plot_meshes.push_back(pm); + } + } + } + + Node3D *s = Object::cast_to<Node3D>(p_at_node); + if (s) { + if (s->is_visible_in_tree()) { + Array meshes = p_at_node->call("get_meshes"); + for (int i = 0; i < meshes.size(); i += 2) { + Transform3D mxf = meshes[i]; + Ref<Mesh> mesh = meshes[i + 1]; + if (!mesh.is_valid()) { + continue; + } + + AABB aabb = mesh->get_aabb(); + + Transform3D xf = get_global_transform().affine_inverse() * (s->get_global_transform() * mxf); + + if (p_aabb.intersects(xf.xform(aabb))) { + PlotMesh pm; + pm.local_xform = xf; + pm.mesh = mesh; + plot_meshes.push_back(pm); + } + } + } + } + + for (int i = 0; i < p_at_node->get_child_count(); i++) { + Node *child = p_at_node->get_child(i); + _find_meshes(p_aabb, child, plot_meshes); + } +} + +uint32_t GPUParticlesCollisionSDF::_create_bvh(LocalVector<BVH> &bvh_tree, FacePos *p_faces, uint32_t p_face_count, const Face3 *p_triangles, float p_thickness) { + if (p_face_count == 1) { + return BVH::LEAF_BIT | p_faces[0].index; + } + + uint32_t index = bvh_tree.size(); + { + BVH bvh; + + for (uint32_t i = 0; i < p_face_count; i++) { + const Face3 &f = p_triangles[p_faces[i].index]; + AABB aabb(f.vertex[0], Vector3()); + aabb.expand_to(f.vertex[1]); + aabb.expand_to(f.vertex[2]); + if (p_thickness > 0.0) { + Vector3 normal = p_triangles[p_faces[i].index].get_plane().normal; + aabb.expand_to(f.vertex[0] - normal * p_thickness); + aabb.expand_to(f.vertex[1] - normal * p_thickness); + aabb.expand_to(f.vertex[2] - normal * p_thickness); + } + if (i == 0) { + bvh.bounds = aabb; + } else { + bvh.bounds.merge_with(aabb); + } + } + bvh_tree.push_back(bvh); + } + + uint32_t middle = p_face_count / 2; + + SortArray<FacePos, FaceSort> s; + s.compare.axis = bvh_tree[index].bounds.get_longest_axis_index(); + s.sort(p_faces, p_face_count); + + uint32_t left = _create_bvh(bvh_tree, p_faces, middle, p_triangles, p_thickness); + uint32_t right = _create_bvh(bvh_tree, p_faces + middle, p_face_count - middle, p_triangles, p_thickness); + + bvh_tree[index].children[0] = left; + bvh_tree[index].children[1] = right; + + return index; +} + +static _FORCE_INLINE_ real_t Vector3_dot2(const Vector3 &p_vec3) { + return p_vec3.dot(p_vec3); +} + +void GPUParticlesCollisionSDF::_find_closest_distance(const Vector3 &p_pos, const BVH *bvh, uint32_t p_bvh_cell, const Face3 *triangles, float thickness, float &closest_distance) { + if (p_bvh_cell & BVH::LEAF_BIT) { + p_bvh_cell &= BVH::LEAF_MASK; //remove bit + + Vector3 point = p_pos; + Plane p = triangles[p_bvh_cell].get_plane(); + float d = p.distance_to(point); + float inside_d = 1e20; + if (d < 0 && d > -thickness) { + //inside planes, do this in 2D + + Vector3 x_axis = (triangles[p_bvh_cell].vertex[0] - triangles[p_bvh_cell].vertex[1]).normalized(); + Vector3 y_axis = p.normal.cross(x_axis).normalized(); + + Vector2 points[3]; + for (int i = 0; i < 3; i++) { + points[i] = Vector2(x_axis.dot(triangles[p_bvh_cell].vertex[i]), y_axis.dot(triangles[p_bvh_cell].vertex[i])); + } + + Vector2 p2d = Vector2(x_axis.dot(point), y_axis.dot(point)); + + { + // https://www.shadertoy.com/view/XsXSz4 + + Vector2 e0 = points[1] - points[0]; + Vector2 e1 = points[2] - points[1]; + Vector2 e2 = points[0] - points[2]; + + Vector2 v0 = p2d - points[0]; + Vector2 v1 = p2d - points[1]; + Vector2 v2 = p2d - points[2]; + + Vector2 pq0 = v0 - e0 * CLAMP(v0.dot(e0) / e0.dot(e0), 0.0, 1.0); + Vector2 pq1 = v1 - e1 * CLAMP(v1.dot(e1) / e1.dot(e1), 0.0, 1.0); + Vector2 pq2 = v2 - e2 * CLAMP(v2.dot(e2) / e2.dot(e2), 0.0, 1.0); + + float s = SGN(e0.x * e2.y - e0.y * e2.x); + Vector2 d2 = Vector2(pq0.dot(pq0), s * (v0.x * e0.y - v0.y * e0.x)).min(Vector2(pq1.dot(pq1), s * (v1.x * e1.y - v1.y * e1.x))).min(Vector2(pq2.dot(pq2), s * (v2.x * e2.y - v2.y * e2.x))); + + inside_d = -Math::sqrt(d2.x) * SGN(d2.y); + } + + //make sure distance to planes is not shorter if inside + if (inside_d < 0) { + inside_d = MAX(inside_d, d); + inside_d = MAX(inside_d, -(thickness + d)); + } + + closest_distance = MIN(closest_distance, inside_d); + } else { + if (d < 0) { + point -= p.normal * thickness; //flatten + } + + // https://iquilezles.org/www/articles/distfunctions/distfunctions.htm + Vector3 a = triangles[p_bvh_cell].vertex[0]; + Vector3 b = triangles[p_bvh_cell].vertex[1]; + Vector3 c = triangles[p_bvh_cell].vertex[2]; + + Vector3 ba = b - a; + Vector3 pa = point - a; + Vector3 cb = c - b; + Vector3 pb = point - b; + Vector3 ac = a - c; + Vector3 pc = point - c; + Vector3 nor = ba.cross(ac); + + inside_d = Math::sqrt( + (SGN(ba.cross(nor).dot(pa)) + + SGN(cb.cross(nor).dot(pb)) + + SGN(ac.cross(nor).dot(pc)) < + 2.0) ? + MIN(MIN( + Vector3_dot2(ba * CLAMP(ba.dot(pa) / Vector3_dot2(ba), 0.0, 1.0) - pa), + Vector3_dot2(cb * CLAMP(cb.dot(pb) / Vector3_dot2(cb), 0.0, 1.0) - pb)), + Vector3_dot2(ac * CLAMP(ac.dot(pc) / Vector3_dot2(ac), 0.0, 1.0) - pc)) : + nor.dot(pa) * nor.dot(pa) / Vector3_dot2(nor)); + + closest_distance = MIN(closest_distance, inside_d); + } + + } else { + bool pass = true; + if (!bvh[p_bvh_cell].bounds.has_point(p_pos)) { + //outside, find closest point + Vector3 he = bvh[p_bvh_cell].bounds.size * 0.5; + Vector3 center = bvh[p_bvh_cell].bounds.position + he; + + Vector3 rel = (p_pos - center).abs(); + Vector3 closest(MIN(rel.x, he.x), MIN(rel.y, he.y), MIN(rel.z, he.z)); + float d = rel.distance_to(closest); + + if (d >= closest_distance) { + pass = false; //already closer than this aabb, discard + } + } + + if (pass) { + _find_closest_distance(p_pos, bvh, bvh[p_bvh_cell].children[0], triangles, thickness, closest_distance); + _find_closest_distance(p_pos, bvh, bvh[p_bvh_cell].children[1], triangles, thickness, closest_distance); + } + } +} + +void GPUParticlesCollisionSDF::_compute_sdf_z(uint32_t p_z, ComputeSDFParams *params) { + int32_t z_ofs = p_z * params->size.y * params->size.x; + for (int32_t y = 0; y < params->size.y; y++) { + int32_t y_ofs = z_ofs + y * params->size.x; + for (int32_t x = 0; x < params->size.x; x++) { + int32_t x_ofs = y_ofs + x; + float &cell = params->cells[x_ofs]; + + Vector3 pos = params->cell_offset + Vector3(x, y, p_z) * params->cell_size; + + cell = 1e20; + + _find_closest_distance(pos, params->bvh, 0, params->triangles, params->thickness, cell); + } + } +} + +void GPUParticlesCollisionSDF::_compute_sdf(ComputeSDFParams *params) { + ThreadWorkPool work_pool; + work_pool.init(); + work_pool.begin_work(params->size.z, this, &GPUParticlesCollisionSDF::_compute_sdf_z, params); + while (!work_pool.is_done_dispatching()) { + OS::get_singleton()->delay_usec(10000); + bake_step_function(work_pool.get_work_index() * 100 / params->size.z, "Baking SDF"); + } + work_pool.end_work(); + work_pool.finish(); +} + +Vector3i GPUParticlesCollisionSDF::get_estimated_cell_size() const { + static const int subdivs[RESOLUTION_MAX] = { 16, 32, 64, 128, 256, 512 }; + int subdiv = subdivs[get_resolution()]; + + AABB aabb(-extents, extents * 2); + + float cell_size = aabb.get_longest_axis_size() / float(subdiv); + + Vector3i sdf_size = Vector3i(aabb.size / cell_size); + sdf_size.x = MAX(1, sdf_size.x); + sdf_size.y = MAX(1, sdf_size.y); + sdf_size.z = MAX(1, sdf_size.z); + return sdf_size; +} + +Ref<Image> GPUParticlesCollisionSDF::bake() { + static const int subdivs[RESOLUTION_MAX] = { 16, 32, 64, 128, 256, 512 }; + int subdiv = subdivs[get_resolution()]; + + AABB aabb(-extents, extents * 2); + + float cell_size = aabb.get_longest_axis_size() / float(subdiv); + + Vector3i sdf_size = Vector3i(aabb.size / cell_size); + sdf_size.x = MAX(1, sdf_size.x); + sdf_size.y = MAX(1, sdf_size.y); + sdf_size.z = MAX(1, sdf_size.z); + + if (bake_begin_function) { + bake_begin_function(100); + } + + aabb.size = Vector3(sdf_size) * cell_size; + + List<PlotMesh> plot_meshes; + _find_meshes(aabb, get_parent(), plot_meshes); + + LocalVector<Face3> faces; + + if (bake_step_function) { + bake_step_function(0, "Finding Meshes"); + } + + for (const PlotMesh &pm : plot_meshes) { + for (int i = 0; i < pm.mesh->get_surface_count(); i++) { + if (pm.mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) { + continue; //only triangles + } + + Array a = pm.mesh->surface_get_arrays(i); + + Vector<Vector3> vertices = a[Mesh::ARRAY_VERTEX]; + const Vector3 *vr = vertices.ptr(); + Vector<int> index = a[Mesh::ARRAY_INDEX]; + + if (index.size()) { + int facecount = index.size() / 3; + const int *ir = index.ptr(); + + for (int j = 0; j < facecount; j++) { + Face3 face; + + for (int k = 0; k < 3; k++) { + face.vertex[k] = pm.local_xform.xform(vr[ir[j * 3 + k]]); + } + + //test against original bounds + if (!Geometry3D::triangle_box_overlap(aabb.position + aabb.size * 0.5, aabb.size * 0.5, face.vertex)) { + continue; + } + + faces.push_back(face); + } + + } else { + int facecount = vertices.size() / 3; + + for (int j = 0; j < facecount; j++) { + Face3 face; + + for (int k = 0; k < 3; k++) { + face.vertex[k] = pm.local_xform.xform(vr[j * 3 + k]); + } + + //test against original bounds + if (!Geometry3D::triangle_box_overlap(aabb.position + aabb.size * 0.5, aabb.size * 0.5, face.vertex)) { + continue; + } + + faces.push_back(face); + } + } + } + } + + //compute bvh + + ERR_FAIL_COND_V(faces.size() <= 1, Ref<Image>()); + + LocalVector<FacePos> face_pos; + + face_pos.resize(faces.size()); + + float th = cell_size * thickness; + + for (uint32_t i = 0; i < faces.size(); i++) { + face_pos[i].index = i; + face_pos[i].center = (faces[i].vertex[0] + faces[i].vertex[1] + faces[i].vertex[2]) / 2; + if (th > 0.0) { + face_pos[i].center -= faces[i].get_plane().normal * th * 0.5; + } + } + + if (bake_step_function) { + bake_step_function(0, "Creating BVH"); + } + + LocalVector<BVH> bvh; + + _create_bvh(bvh, face_pos.ptr(), face_pos.size(), faces.ptr(), th); + + Vector<uint8_t> data; + data.resize(sdf_size.z * sdf_size.y * sdf_size.x * sizeof(float)); + + if (bake_step_function) { + bake_step_function(0, "Baking SDF"); + } + + ComputeSDFParams params; + params.cells = (float *)data.ptrw(); + params.size = sdf_size; + params.cell_size = cell_size; + params.cell_offset = aabb.position + Vector3(cell_size * 0.5, cell_size * 0.5, cell_size * 0.5); + params.bvh = bvh.ptr(); + params.triangles = faces.ptr(); + params.thickness = th; + _compute_sdf(¶ms); + + Ref<Image> ret; + ret.instantiate(); + ret->create(sdf_size.x, sdf_size.y * sdf_size.z, false, Image::FORMAT_RF, data); + ret->convert(Image::FORMAT_RH); //convert to half, save space + ret->set_meta("depth", sdf_size.z); //hack, make sure to add to the docs of this function + + if (bake_end_function) { + bake_end_function(); + } + + return ret; +} + +void GPUParticlesCollisionSDF::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesCollisionSDF::set_extents); + ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesCollisionSDF::get_extents); + + ClassDB::bind_method(D_METHOD("set_resolution", "resolution"), &GPUParticlesCollisionSDF::set_resolution); + ClassDB::bind_method(D_METHOD("get_resolution"), &GPUParticlesCollisionSDF::get_resolution); + + ClassDB::bind_method(D_METHOD("set_texture", "texture"), &GPUParticlesCollisionSDF::set_texture); + ClassDB::bind_method(D_METHOD("get_texture"), &GPUParticlesCollisionSDF::get_texture); + + ClassDB::bind_method(D_METHOD("set_thickness", "thickness"), &GPUParticlesCollisionSDF::set_thickness); + ClassDB::bind_method(D_METHOD("get_thickness"), &GPUParticlesCollisionSDF::get_thickness); + + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "resolution", PROPERTY_HINT_ENUM, "16,32,64,128,256,512"), "set_resolution", "get_resolution"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "thickness", PROPERTY_HINT_RANGE, "0.0,2.0,0.01"), "set_thickness", "get_thickness"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture3D"), "set_texture", "get_texture"); + + BIND_ENUM_CONSTANT(RESOLUTION_16); + BIND_ENUM_CONSTANT(RESOLUTION_32); + BIND_ENUM_CONSTANT(RESOLUTION_64); + BIND_ENUM_CONSTANT(RESOLUTION_128); + BIND_ENUM_CONSTANT(RESOLUTION_256); + BIND_ENUM_CONSTANT(RESOLUTION_512); + BIND_ENUM_CONSTANT(RESOLUTION_MAX); +} + +void GPUParticlesCollisionSDF::set_thickness(float p_thickness) { + thickness = p_thickness; +} + +float GPUParticlesCollisionSDF::get_thickness() const { + return thickness; +} + +void GPUParticlesCollisionSDF::set_extents(const Vector3 &p_extents) { + extents = p_extents; + RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents); + update_gizmos(); +} + +Vector3 GPUParticlesCollisionSDF::get_extents() const { + return extents; +} + +void GPUParticlesCollisionSDF::set_resolution(Resolution p_resolution) { + resolution = p_resolution; + update_gizmos(); +} + +GPUParticlesCollisionSDF::Resolution GPUParticlesCollisionSDF::get_resolution() const { + return resolution; +} + +void GPUParticlesCollisionSDF::set_texture(const Ref<Texture3D> &p_texture) { + texture = p_texture; + RID tex = texture.is_valid() ? texture->get_rid() : RID(); + RS::get_singleton()->particles_collision_set_field_texture(_get_collision(), tex); +} + +Ref<Texture3D> GPUParticlesCollisionSDF::get_texture() const { + return texture; +} + +AABB GPUParticlesCollisionSDF::get_aabb() const { + return AABB(-extents, extents * 2); +} + +GPUParticlesCollisionSDF::BakeBeginFunc GPUParticlesCollisionSDF::bake_begin_function = nullptr; +GPUParticlesCollisionSDF::BakeStepFunc GPUParticlesCollisionSDF::bake_step_function = nullptr; +GPUParticlesCollisionSDF::BakeEndFunc GPUParticlesCollisionSDF::bake_end_function = nullptr; + +GPUParticlesCollisionSDF::GPUParticlesCollisionSDF() : + GPUParticlesCollision3D(RS::PARTICLES_COLLISION_TYPE_SDF_COLLIDE) { +} + +GPUParticlesCollisionSDF::~GPUParticlesCollisionSDF() { +} + +//////////////////////////// +//////////////////////////// + +void GPUParticlesCollisionHeightField::_notification(int p_what) { + if (p_what == NOTIFICATION_INTERNAL_PROCESS) { + if (update_mode == UPDATE_MODE_ALWAYS) { + RS::get_singleton()->particles_collision_height_field_update(_get_collision()); + } + + if (follow_camera_mode && get_viewport()) { + Camera3D *cam = get_viewport()->get_camera_3d(); + if (cam) { + Transform3D xform = get_global_transform(); + Vector3 x_axis = xform.basis.get_axis(Vector3::AXIS_X).normalized(); + Vector3 z_axis = xform.basis.get_axis(Vector3::AXIS_Z).normalized(); + float x_len = xform.basis.get_scale().x; + float z_len = xform.basis.get_scale().z; + + Vector3 cam_pos = cam->get_global_transform().origin; + Transform3D new_xform = xform; + + while (x_axis.dot(cam_pos - new_xform.origin) > x_len) { + new_xform.origin += x_axis * x_len; + } + while (x_axis.dot(cam_pos - new_xform.origin) < -x_len) { + new_xform.origin -= x_axis * x_len; + } + + while (z_axis.dot(cam_pos - new_xform.origin) > z_len) { + new_xform.origin += z_axis * z_len; + } + while (z_axis.dot(cam_pos - new_xform.origin) < -z_len) { + new_xform.origin -= z_axis * z_len; + } + + if (new_xform != xform) { + set_global_transform(new_xform); + RS::get_singleton()->particles_collision_height_field_update(_get_collision()); + } + } + } + } + + if (p_what == NOTIFICATION_TRANSFORM_CHANGED) { + RS::get_singleton()->particles_collision_height_field_update(_get_collision()); + } +} + +void GPUParticlesCollisionHeightField::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesCollisionHeightField::set_extents); + ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesCollisionHeightField::get_extents); + + ClassDB::bind_method(D_METHOD("set_resolution", "resolution"), &GPUParticlesCollisionHeightField::set_resolution); + ClassDB::bind_method(D_METHOD("get_resolution"), &GPUParticlesCollisionHeightField::get_resolution); + + ClassDB::bind_method(D_METHOD("set_update_mode", "update_mode"), &GPUParticlesCollisionHeightField::set_update_mode); + ClassDB::bind_method(D_METHOD("get_update_mode"), &GPUParticlesCollisionHeightField::get_update_mode); + + ClassDB::bind_method(D_METHOD("set_follow_camera_mode", "enabled"), &GPUParticlesCollisionHeightField::set_follow_camera_mode); + ClassDB::bind_method(D_METHOD("is_follow_camera_mode_enabled"), &GPUParticlesCollisionHeightField::is_follow_camera_mode_enabled); + + ClassDB::bind_method(D_METHOD("set_follow_camera_push_ratio", "ratio"), &GPUParticlesCollisionHeightField::set_follow_camera_push_ratio); + ClassDB::bind_method(D_METHOD("get_follow_camera_push_ratio"), &GPUParticlesCollisionHeightField::get_follow_camera_push_ratio); + + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "resolution", PROPERTY_HINT_ENUM, "256,512,1024,2048,4096,8192"), "set_resolution", "get_resolution"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "WhenMoved,Always"), "set_update_mode", "get_update_mode"); + ADD_GROUP("Follow Camera", "follow_camera_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "follow_camera_enabled"), "set_follow_camera_mode", "is_follow_camera_mode_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "follow_camera_push_ratio", PROPERTY_HINT_RANGE, "0.01,1,0.01"), "set_follow_camera_push_ratio", "get_follow_camera_push_ratio"); + + BIND_ENUM_CONSTANT(RESOLUTION_256); + BIND_ENUM_CONSTANT(RESOLUTION_512); + BIND_ENUM_CONSTANT(RESOLUTION_1024); + BIND_ENUM_CONSTANT(RESOLUTION_2048); + BIND_ENUM_CONSTANT(RESOLUTION_4096); + BIND_ENUM_CONSTANT(RESOLUTION_8192); + BIND_ENUM_CONSTANT(RESOLUTION_MAX); + + BIND_ENUM_CONSTANT(UPDATE_MODE_WHEN_MOVED); + BIND_ENUM_CONSTANT(UPDATE_MODE_ALWAYS); +} + +void GPUParticlesCollisionHeightField::set_follow_camera_push_ratio(float p_follow_camera_push_ratio) { + follow_camera_push_ratio = p_follow_camera_push_ratio; +} + +float GPUParticlesCollisionHeightField::get_follow_camera_push_ratio() const { + return follow_camera_push_ratio; +} + +void GPUParticlesCollisionHeightField::set_extents(const Vector3 &p_extents) { + extents = p_extents; + RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents); + update_gizmos(); + RS::get_singleton()->particles_collision_height_field_update(_get_collision()); +} + +Vector3 GPUParticlesCollisionHeightField::get_extents() const { + return extents; +} + +void GPUParticlesCollisionHeightField::set_resolution(Resolution p_resolution) { + resolution = p_resolution; + RS::get_singleton()->particles_collision_set_height_field_resolution(_get_collision(), RS::ParticlesCollisionHeightfieldResolution(resolution)); + update_gizmos(); + RS::get_singleton()->particles_collision_height_field_update(_get_collision()); +} + +GPUParticlesCollisionHeightField::Resolution GPUParticlesCollisionHeightField::get_resolution() const { + return resolution; +} + +void GPUParticlesCollisionHeightField::set_update_mode(UpdateMode p_update_mode) { + update_mode = p_update_mode; + set_process_internal(follow_camera_mode || update_mode == UPDATE_MODE_ALWAYS); +} + +GPUParticlesCollisionHeightField::UpdateMode GPUParticlesCollisionHeightField::get_update_mode() const { + return update_mode; +} + +void GPUParticlesCollisionHeightField::set_follow_camera_mode(bool p_enabled) { + follow_camera_mode = p_enabled; + set_process_internal(follow_camera_mode || update_mode == UPDATE_MODE_ALWAYS); +} + +bool GPUParticlesCollisionHeightField::is_follow_camera_mode_enabled() const { + return follow_camera_mode; +} + +AABB GPUParticlesCollisionHeightField::get_aabb() const { + return AABB(-extents, extents * 2); +} + +GPUParticlesCollisionHeightField::GPUParticlesCollisionHeightField() : + GPUParticlesCollision3D(RS::PARTICLES_COLLISION_TYPE_HEIGHTFIELD_COLLIDE) { +} + +GPUParticlesCollisionHeightField::~GPUParticlesCollisionHeightField() { +} + +//////////////////////////// +//////////////////////////// + +void GPUParticlesAttractor3D::set_cull_mask(uint32_t p_cull_mask) { + cull_mask = p_cull_mask; + RS::get_singleton()->particles_collision_set_cull_mask(collision, p_cull_mask); +} + +uint32_t GPUParticlesAttractor3D::get_cull_mask() const { + return cull_mask; +} + +void GPUParticlesAttractor3D::set_strength(real_t p_strength) { + strength = p_strength; + RS::get_singleton()->particles_collision_set_attractor_strength(collision, p_strength); +} + +real_t GPUParticlesAttractor3D::get_strength() const { + return strength; +} + +void GPUParticlesAttractor3D::set_attenuation(real_t p_attenuation) { + attenuation = p_attenuation; + RS::get_singleton()->particles_collision_set_attractor_attenuation(collision, p_attenuation); +} + +real_t GPUParticlesAttractor3D::get_attenuation() const { + return attenuation; +} + +void GPUParticlesAttractor3D::set_directionality(real_t p_directionality) { + directionality = p_directionality; + RS::get_singleton()->particles_collision_set_attractor_directionality(collision, p_directionality); + update_gizmos(); +} + +real_t GPUParticlesAttractor3D::get_directionality() const { + return directionality; +} + +void GPUParticlesAttractor3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_cull_mask", "mask"), &GPUParticlesAttractor3D::set_cull_mask); + ClassDB::bind_method(D_METHOD("get_cull_mask"), &GPUParticlesAttractor3D::get_cull_mask); + + ClassDB::bind_method(D_METHOD("set_strength", "strength"), &GPUParticlesAttractor3D::set_strength); + ClassDB::bind_method(D_METHOD("get_strength"), &GPUParticlesAttractor3D::get_strength); + + ClassDB::bind_method(D_METHOD("set_attenuation", "attenuation"), &GPUParticlesAttractor3D::set_attenuation); + ClassDB::bind_method(D_METHOD("get_attenuation"), &GPUParticlesAttractor3D::get_attenuation); + + ClassDB::bind_method(D_METHOD("set_directionality", "amount"), &GPUParticlesAttractor3D::set_directionality); + ClassDB::bind_method(D_METHOD("get_directionality"), &GPUParticlesAttractor3D::get_directionality); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "strength", PROPERTY_HINT_RANGE, "-128,128,0.01,or_greater,or_lesser"), "set_strength", "get_strength"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "attenuation", PROPERTY_HINT_EXP_EASING, "0,8,0.01"), "set_attenuation", "get_attenuation"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "directionality", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_directionality", "get_directionality"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask"); +} + +GPUParticlesAttractor3D::GPUParticlesAttractor3D(RS::ParticlesCollisionType p_type) { + collision = RS::get_singleton()->particles_collision_create(); + RS::get_singleton()->particles_collision_set_collision_type(collision, p_type); + set_base(collision); +} +GPUParticlesAttractor3D::~GPUParticlesAttractor3D() { + RS::get_singleton()->free(collision); +} + +///////////////////////////////// + +void GPUParticlesAttractorSphere::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_radius", "radius"), &GPUParticlesAttractorSphere::set_radius); + ClassDB::bind_method(D_METHOD("get_radius"), &GPUParticlesAttractorSphere::get_radius); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "radius", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_radius", "get_radius"); +} + +void GPUParticlesAttractorSphere::set_radius(real_t p_radius) { + radius = p_radius; + RS::get_singleton()->particles_collision_set_sphere_radius(_get_collision(), radius); + update_gizmos(); +} + +real_t GPUParticlesAttractorSphere::get_radius() const { + return radius; +} + +AABB GPUParticlesAttractorSphere::get_aabb() const { + return AABB(Vector3(-radius, -radius, -radius), Vector3(radius * 2, radius * 2, radius * 2)); +} + +GPUParticlesAttractorSphere::GPUParticlesAttractorSphere() : + GPUParticlesAttractor3D(RS::PARTICLES_COLLISION_TYPE_SPHERE_ATTRACT) { +} + +GPUParticlesAttractorSphere::~GPUParticlesAttractorSphere() { +} + +/////////////////////////// + +void GPUParticlesAttractorBox::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesAttractorBox::set_extents); + ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesAttractorBox::get_extents); + + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents"); +} + +void GPUParticlesAttractorBox::set_extents(const Vector3 &p_extents) { + extents = p_extents; + RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents); + update_gizmos(); +} + +Vector3 GPUParticlesAttractorBox::get_extents() const { + return extents; +} + +AABB GPUParticlesAttractorBox::get_aabb() const { + return AABB(-extents, extents * 2); +} + +GPUParticlesAttractorBox::GPUParticlesAttractorBox() : + GPUParticlesAttractor3D(RS::PARTICLES_COLLISION_TYPE_BOX_ATTRACT) { +} + +GPUParticlesAttractorBox::~GPUParticlesAttractorBox() { +} + +/////////////////////////// + +void GPUParticlesAttractorVectorField::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GPUParticlesAttractorVectorField::set_extents); + ClassDB::bind_method(D_METHOD("get_extents"), &GPUParticlesAttractorVectorField::get_extents); + + ClassDB::bind_method(D_METHOD("set_texture", "texture"), &GPUParticlesAttractorVectorField::set_texture); + ClassDB::bind_method(D_METHOD("get_texture"), &GPUParticlesAttractorVectorField::get_texture); + + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents", PROPERTY_HINT_RANGE, "0.01,1024,0.01,or_greater"), "set_extents", "get_extents"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture3D"), "set_texture", "get_texture"); +} + +void GPUParticlesAttractorVectorField::set_extents(const Vector3 &p_extents) { + extents = p_extents; + RS::get_singleton()->particles_collision_set_box_extents(_get_collision(), extents); + update_gizmos(); +} + +Vector3 GPUParticlesAttractorVectorField::get_extents() const { + return extents; +} + +void GPUParticlesAttractorVectorField::set_texture(const Ref<Texture3D> &p_texture) { + texture = p_texture; + RID tex = texture.is_valid() ? texture->get_rid() : RID(); + RS::get_singleton()->particles_collision_set_field_texture(_get_collision(), tex); +} + +Ref<Texture3D> GPUParticlesAttractorVectorField::get_texture() const { + return texture; +} + +AABB GPUParticlesAttractorVectorField::get_aabb() const { + return AABB(-extents, extents * 2); +} + +GPUParticlesAttractorVectorField::GPUParticlesAttractorVectorField() : + GPUParticlesAttractor3D(RS::PARTICLES_COLLISION_TYPE_VECTOR_FIELD_ATTRACT) { +} + +GPUParticlesAttractorVectorField::~GPUParticlesAttractorVectorField() { +} diff --git a/scene/3d/gpu_particles_collision_3d.h b/scene/3d/gpu_particles_collision_3d.h new file mode 100644 index 0000000000..fbf68ed6df --- /dev/null +++ b/scene/3d/gpu_particles_collision_3d.h @@ -0,0 +1,340 @@ +/*************************************************************************/ +/* gpu_particles_collision_3d.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#ifndef GPU_PARTICLES_COLLISION_3D_H +#define GPU_PARTICLES_COLLISION_3D_H + +#include "core/templates/local_vector.h" +#include "scene/3d/visual_instance_3d.h" + +class GPUParticlesCollision3D : public VisualInstance3D { + GDCLASS(GPUParticlesCollision3D, VisualInstance3D); + + uint32_t cull_mask = 0xFFFFFFFF; + RID collision; + +protected: + _FORCE_INLINE_ RID _get_collision() { return collision; } + static void _bind_methods(); + + GPUParticlesCollision3D(RS::ParticlesCollisionType p_type); + +public: + void set_cull_mask(uint32_t p_cull_mask); + uint32_t get_cull_mask() const; + + virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override { return Vector<Face3>(); } + + ~GPUParticlesCollision3D(); +}; + +class GPUParticlesCollisionSphere : public GPUParticlesCollision3D { + GDCLASS(GPUParticlesCollisionSphere, GPUParticlesCollision3D); + + real_t radius = 1.0; + +protected: + static void _bind_methods(); + +public: + void set_radius(real_t p_radius); + real_t get_radius() const; + + virtual AABB get_aabb() const override; + + GPUParticlesCollisionSphere(); + ~GPUParticlesCollisionSphere(); +}; + +class GPUParticlesCollisionBox : public GPUParticlesCollision3D { + GDCLASS(GPUParticlesCollisionBox, GPUParticlesCollision3D); + + Vector3 extents = Vector3(1, 1, 1); + +protected: + static void _bind_methods(); + +public: + void set_extents(const Vector3 &p_extents); + Vector3 get_extents() const; + + virtual AABB get_aabb() const override; + + GPUParticlesCollisionBox(); + ~GPUParticlesCollisionBox(); +}; + +class GPUParticlesCollisionSDF : public GPUParticlesCollision3D { + GDCLASS(GPUParticlesCollisionSDF, GPUParticlesCollision3D); + +public: + enum Resolution { + RESOLUTION_16, + RESOLUTION_32, + RESOLUTION_64, + RESOLUTION_128, + RESOLUTION_256, + RESOLUTION_512, + RESOLUTION_MAX, + }; + + typedef void (*BakeBeginFunc)(int); + typedef void (*BakeStepFunc)(int, const String &); + typedef void (*BakeEndFunc)(); + +private: + Vector3 extents = Vector3(1, 1, 1); + Resolution resolution = RESOLUTION_64; + Ref<Texture3D> texture; + float thickness = 1.0; + + struct PlotMesh { + Ref<Mesh> mesh; + Transform3D local_xform; + }; + + void _find_meshes(const AABB &p_aabb, Node *p_at_node, List<PlotMesh> &plot_meshes); + + struct BVH { + enum { + LEAF_BIT = 1 << 30, + LEAF_MASK = LEAF_BIT - 1 + }; + AABB bounds; + uint32_t children[2] = {}; + }; + + struct FacePos { + Vector3 center; + uint32_t index = 0; + }; + + struct FaceSort { + uint32_t axis = 0; + bool operator()(const FacePos &p_left, const FacePos &p_right) const { + return p_left.center[axis] < p_right.center[axis]; + } + }; + + uint32_t _create_bvh(LocalVector<BVH> &bvh_tree, FacePos *p_faces, uint32_t p_face_count, const Face3 *p_triangles, float p_thickness); + + struct ComputeSDFParams { + float *cells = nullptr; + Vector3i size; + float cell_size = 0.0; + Vector3 cell_offset; + const BVH *bvh = nullptr; + const Face3 *triangles = nullptr; + float thickness = 0.0; + }; + + void _find_closest_distance(const Vector3 &p_pos, const BVH *bvh, uint32_t p_bvh_cell, const Face3 *triangles, float thickness, float &closest_distance); + void _compute_sdf_z(uint32_t p_z, ComputeSDFParams *params); + void _compute_sdf(ComputeSDFParams *params); + +protected: + static void _bind_methods(); + +public: + void set_thickness(float p_thickness); + float get_thickness() const; + + void set_extents(const Vector3 &p_extents); + Vector3 get_extents() const; + + void set_resolution(Resolution p_resolution); + Resolution get_resolution() const; + + void set_texture(const Ref<Texture3D> &p_texture); + Ref<Texture3D> get_texture() const; + + Vector3i get_estimated_cell_size() const; + Ref<Image> bake(); + + virtual AABB get_aabb() const override; + + static BakeBeginFunc bake_begin_function; + static BakeStepFunc bake_step_function; + static BakeEndFunc bake_end_function; + + GPUParticlesCollisionSDF(); + ~GPUParticlesCollisionSDF(); +}; + +VARIANT_ENUM_CAST(GPUParticlesCollisionSDF::Resolution) + +class GPUParticlesCollisionHeightField : public GPUParticlesCollision3D { + GDCLASS(GPUParticlesCollisionHeightField, GPUParticlesCollision3D); + +public: + enum Resolution { + RESOLUTION_256, + RESOLUTION_512, + RESOLUTION_1024, + RESOLUTION_2048, + RESOLUTION_4096, + RESOLUTION_8192, + RESOLUTION_MAX, + }; + + enum UpdateMode { + UPDATE_MODE_WHEN_MOVED, + UPDATE_MODE_ALWAYS, + }; + +private: + Vector3 extents = Vector3(1, 1, 1); + Resolution resolution = RESOLUTION_1024; + bool follow_camera_mode = false; + float follow_camera_push_ratio = 0.1; + + UpdateMode update_mode = UPDATE_MODE_WHEN_MOVED; + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + void set_extents(const Vector3 &p_extents); + Vector3 get_extents() const; + + void set_resolution(Resolution p_resolution); + Resolution get_resolution() const; + + void set_update_mode(UpdateMode p_update_mode); + UpdateMode get_update_mode() const; + + void set_follow_camera_mode(bool p_enabled); + bool is_follow_camera_mode_enabled() const; + + void set_follow_camera_push_ratio(float p_ratio); + float get_follow_camera_push_ratio() const; + + virtual AABB get_aabb() const override; + + GPUParticlesCollisionHeightField(); + ~GPUParticlesCollisionHeightField(); +}; + +VARIANT_ENUM_CAST(GPUParticlesCollisionHeightField::Resolution) +VARIANT_ENUM_CAST(GPUParticlesCollisionHeightField::UpdateMode) + +class GPUParticlesAttractor3D : public VisualInstance3D { + GDCLASS(GPUParticlesAttractor3D, VisualInstance3D); + + uint32_t cull_mask = 0xFFFFFFFF; + RID collision; + real_t strength = 1.0; + real_t attenuation = 1.0; + real_t directionality = 0.0; + +protected: + _FORCE_INLINE_ RID _get_collision() { return collision; } + static void _bind_methods(); + + GPUParticlesAttractor3D(RS::ParticlesCollisionType p_type); + +public: + void set_cull_mask(uint32_t p_cull_mask); + uint32_t get_cull_mask() const; + + void set_strength(real_t p_strength); + real_t get_strength() const; + + void set_attenuation(real_t p_attenuation); + real_t get_attenuation() const; + + void set_directionality(real_t p_directionality); + real_t get_directionality() const; + + virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override { return Vector<Face3>(); } + + ~GPUParticlesAttractor3D(); +}; + +class GPUParticlesAttractorSphere : public GPUParticlesAttractor3D { + GDCLASS(GPUParticlesAttractorSphere, GPUParticlesAttractor3D); + + real_t radius = 1.0; + +protected: + static void _bind_methods(); + +public: + void set_radius(real_t p_radius); + real_t get_radius() const; + + virtual AABB get_aabb() const override; + + GPUParticlesAttractorSphere(); + ~GPUParticlesAttractorSphere(); +}; + +class GPUParticlesAttractorBox : public GPUParticlesAttractor3D { + GDCLASS(GPUParticlesAttractorBox, GPUParticlesAttractor3D); + + Vector3 extents = Vector3(1, 1, 1); + +protected: + static void _bind_methods(); + +public: + void set_extents(const Vector3 &p_extents); + Vector3 get_extents() const; + + virtual AABB get_aabb() const override; + + GPUParticlesAttractorBox(); + ~GPUParticlesAttractorBox(); +}; + +class GPUParticlesAttractorVectorField : public GPUParticlesAttractor3D { + GDCLASS(GPUParticlesAttractorVectorField, GPUParticlesAttractor3D); + + Vector3 extents = Vector3(1, 1, 1); + Ref<Texture3D> texture; + +protected: + static void _bind_methods(); + +public: + void set_extents(const Vector3 &p_extents); + Vector3 get_extents() const; + + void set_texture(const Ref<Texture3D> &p_texture); + Ref<Texture3D> get_texture() const; + + virtual AABB get_aabb() const override; + + GPUParticlesAttractorVectorField(); + ~GPUParticlesAttractorVectorField(); +}; + +#endif // GPU_PARTICLES_COLLISION_3D_H diff --git a/scene/3d/immediate_geometry_3d.cpp b/scene/3d/immediate_geometry_3d.cpp deleted file mode 100644 index 7ccfd527a1..0000000000 --- a/scene/3d/immediate_geometry_3d.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/*************************************************************************/ -/* immediate_geometry_3d.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "immediate_geometry_3d.h" - -void ImmediateGeometry3D::begin(Mesh::PrimitiveType p_primitive, const Ref<Texture2D> &p_texture) { - RS::get_singleton()->immediate_begin(im, (RS::PrimitiveType)p_primitive, p_texture.is_valid() ? p_texture->get_rid() : RID()); - if (p_texture.is_valid()) { - cached_textures.push_back(p_texture); - } -} - -void ImmediateGeometry3D::set_normal(const Vector3 &p_normal) { - RS::get_singleton()->immediate_normal(im, p_normal); -} - -void ImmediateGeometry3D::set_tangent(const Plane &p_tangent) { - RS::get_singleton()->immediate_tangent(im, p_tangent); -} - -void ImmediateGeometry3D::set_color(const Color &p_color) { - RS::get_singleton()->immediate_color(im, p_color); -} - -void ImmediateGeometry3D::set_uv(const Vector2 &p_uv) { - RS::get_singleton()->immediate_uv(im, p_uv); -} - -void ImmediateGeometry3D::set_uv2(const Vector2 &p_uv2) { - RS::get_singleton()->immediate_uv2(im, p_uv2); -} - -void ImmediateGeometry3D::add_vertex(const Vector3 &p_vertex) { - RS::get_singleton()->immediate_vertex(im, p_vertex); - if (empty) { - aabb.position = p_vertex; - aabb.size = Vector3(); - empty = false; - } else { - aabb.expand_to(p_vertex); - } -} - -void ImmediateGeometry3D::end() { - RS::get_singleton()->immediate_end(im); -} - -void ImmediateGeometry3D::clear() { - RS::get_singleton()->immediate_clear(im); - empty = true; - cached_textures.clear(); -} - -AABB ImmediateGeometry3D::get_aabb() const { - return aabb; -} - -Vector<Face3> ImmediateGeometry3D::get_faces(uint32_t p_usage_flags) const { - return Vector<Face3>(); -} - -void ImmediateGeometry3D::add_sphere(int p_lats, int p_lons, float p_radius, bool p_add_uv) { - for (int i = 1; i <= p_lats; i++) { - double lat0 = Math_PI * (-0.5 + (double)(i - 1) / p_lats); - double z0 = Math::sin(lat0); - double zr0 = Math::cos(lat0); - - double lat1 = Math_PI * (-0.5 + (double)i / p_lats); - double z1 = Math::sin(lat1); - double zr1 = Math::cos(lat1); - - for (int j = p_lons; j >= 1; j--) { - double lng0 = 2 * Math_PI * (double)(j - 1) / p_lons; - double x0 = Math::cos(lng0); - double y0 = Math::sin(lng0); - - double lng1 = 2 * Math_PI * (double)(j) / p_lons; - double x1 = Math::cos(lng1); - double y1 = Math::sin(lng1); - - Vector3 v[4] = { - Vector3(x1 * zr0, z0, y1 * zr0), - Vector3(x1 * zr1, z1, y1 * zr1), - Vector3(x0 * zr1, z1, y0 * zr1), - Vector3(x0 * zr0, z0, y0 * zr0) - }; - -#define ADD_POINT(m_idx) \ - if (p_add_uv) { \ - set_uv(Vector2(Math::atan2(v[m_idx].x, v[m_idx].z) / Math_PI * 0.5 + 0.5, v[m_idx].y * 0.5 + 0.5)); \ - set_tangent(Plane(Vector3(-v[m_idx].z, v[m_idx].y, v[m_idx].x), 1)); \ - } \ - set_normal(v[m_idx]); \ - add_vertex(v[m_idx] * p_radius); - - ADD_POINT(0); - ADD_POINT(1); - ADD_POINT(2); - - ADD_POINT(2); - ADD_POINT(3); - ADD_POINT(0); - } - } -} - -void ImmediateGeometry3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("begin", "primitive", "texture"), &ImmediateGeometry3D::begin, DEFVAL(Ref<Texture2D>())); - ClassDB::bind_method(D_METHOD("set_normal", "normal"), &ImmediateGeometry3D::set_normal); - ClassDB::bind_method(D_METHOD("set_tangent", "tangent"), &ImmediateGeometry3D::set_tangent); - ClassDB::bind_method(D_METHOD("set_color", "color"), &ImmediateGeometry3D::set_color); - ClassDB::bind_method(D_METHOD("set_uv", "uv"), &ImmediateGeometry3D::set_uv); - ClassDB::bind_method(D_METHOD("set_uv2", "uv"), &ImmediateGeometry3D::set_uv2); - ClassDB::bind_method(D_METHOD("add_vertex", "position"), &ImmediateGeometry3D::add_vertex); - ClassDB::bind_method(D_METHOD("add_sphere", "lats", "lons", "radius", "add_uv"), &ImmediateGeometry3D::add_sphere, DEFVAL(true)); - ClassDB::bind_method(D_METHOD("end"), &ImmediateGeometry3D::end); - ClassDB::bind_method(D_METHOD("clear"), &ImmediateGeometry3D::clear); -} - -ImmediateGeometry3D::ImmediateGeometry3D() { - im = RenderingServer::get_singleton()->immediate_create(); - set_base(im); - empty = true; -} - -ImmediateGeometry3D::~ImmediateGeometry3D() { - RenderingServer::get_singleton()->free(im); -} diff --git a/scene/3d/light_3d.cpp b/scene/3d/light_3d.cpp index 46c3f18c38..ab417fafdd 100644 --- a/scene/3d/light_3d.cpp +++ b/scene/3d/light_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,34 +30,26 @@ #include "light_3d.h" -#include "core/engine.h" -#include "core/project_settings.h" -#include "scene/resources/surface_tool.h" - bool Light3D::_can_gizmo_scale() const { return false; } -void Light3D::set_param(Param p_param, float p_value) { +void Light3D::set_param(Param p_param, real_t p_value) { ERR_FAIL_INDEX(p_param, PARAM_MAX); param[p_param] = p_value; RS::get_singleton()->light_set_param(light, RS::LightParam(p_param), p_value); if (p_param == PARAM_SPOT_ANGLE || p_param == PARAM_RANGE) { - update_gizmo(); + update_gizmos(); if (p_param == PARAM_SPOT_ANGLE) { - _change_notify("spot_angle"); - update_configuration_warning(); - } else if (p_param == PARAM_RANGE) { - _change_notify("omni_range"); - _change_notify("spot_range"); + update_configuration_warnings(); } } } -float Light3D::get_param(Param p_param) const { +real_t Light3D::get_param(Param p_param) const { ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); return param[p_param]; } @@ -67,8 +59,10 @@ void Light3D::set_shadow(bool p_enable) { RS::get_singleton()->light_set_shadow(light, p_enable); if (type == RenderingServer::LIGHT_SPOT || type == RenderingServer::LIGHT_OMNI) { - update_configuration_warning(); + update_configuration_warnings(); } + + notify_property_list_changed(); } bool Light3D::has_shadow() const { @@ -97,7 +91,7 @@ void Light3D::set_color(const Color &p_color) { color = p_color; RS::get_singleton()->light_set_color(light, p_color); // The gizmo color depends on the light color, so update it. - update_gizmo(); + update_gizmos(); } Color Light3D::get_color() const { @@ -130,8 +124,8 @@ AABB Light3D::get_aabb() const { return AABB(Vector3(-1, -1, -1) * param[PARAM_RANGE], Vector3(2, 2, 2) * param[PARAM_RANGE]); } else if (type == RenderingServer::LIGHT_SPOT) { - float len = param[PARAM_RANGE]; - float size = Math::tan(Math::deg2rad(param[PARAM_SPOT_ANGLE])) * len; + real_t len = param[PARAM_RANGE]; + real_t size = Math::tan(Math::deg2rad(param[PARAM_SPOT_ANGLE])) * len; return AABB(Vector3(-size, -size, -len), Vector3(size * 2, size * 2, len)); } @@ -155,7 +149,7 @@ void Light3D::set_projector(const Ref<Texture2D> &p_texture) { projector = p_texture; RID tex_id = projector.is_valid() ? projector->get_rid() : RID(); RS::get_singleton()->light_set_projector(light, tex_id); - update_configuration_warning(); + update_configuration_warnings(); } Ref<Texture2D> Light3D::get_projector() const { @@ -184,8 +178,6 @@ void Light3D::_update_visibility() { #endif RS::get_singleton()->instance_set_visible(get_instance(), is_visible_in_tree() && editor_ok); - - _change_notify("geometry/visible"); } void Light3D::_notification(int p_what) { @@ -208,16 +200,24 @@ bool Light3D::is_editor_only() const { } void Light3D::_validate_property(PropertyInfo &property) const { + if (!shadow && (property.name == "shadow_color" || property.name == "shadow_bias" || property.name == "shadow_normal_bias" || property.name == "shadow_reverse_cull_face" || property.name == "shadow_transmittance_bias" || property.name == "shadow_fog_fade" || property.name == "shadow_blur")) { + property.usage = PROPERTY_USAGE_NOEDITOR; + } + if (get_light_type() == RS::LIGHT_DIRECTIONAL && property.name == "light_size") { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; + } + + if (get_light_type() == RS::LIGHT_DIRECTIONAL && property.name == "light_specular") { + property.usage = PROPERTY_USAGE_NONE; } if (get_light_type() == RS::LIGHT_DIRECTIONAL && property.name == "light_projector") { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (get_light_type() != RS::LIGHT_DIRECTIONAL && property.name == "light_angular_distance") { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } } @@ -257,7 +257,7 @@ void Light3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_param", "get_param", PARAM_ENERGY); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_indirect_energy", PROPERTY_HINT_RANGE, "0,16,0.01,or_greater"), "set_param", "get_param", PARAM_INDIRECT_ENERGY); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_projector", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_projector", "get_projector"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_size", PROPERTY_HINT_RANGE, "0,64,0.01,or_greater"), "set_param", "get_param", PARAM_SIZE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_size", PROPERTY_HINT_RANGE, "0,1,0.01,or_greater"), "set_param", "get_param", PARAM_SIZE); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_angular_distance", PROPERTY_HINT_RANGE, "0,90,0.01"), "set_param", "get_param", PARAM_SIZE); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "light_negative"), "set_negative", "is_negative"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "light_specular", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SPECULAR); @@ -320,10 +320,6 @@ Light3D::Light3D(RenderingServer::LightType p_type) { RS::get_singleton()->instance_set_base(get_instance(), light); - reverse_cull = false; - bake_mode = BAKE_DYNAMIC; - - editor_only = false; set_color(Color(1, 1, 1, 1)); set_shadow(false); set_negative(false); @@ -344,17 +340,16 @@ Light3D::Light3D(RenderingServer::LightType p_type) { set_param(PARAM_SHADOW_FADE_START, 0.8); set_param(PARAM_SHADOW_PANCAKE_SIZE, 20.0); set_param(PARAM_SHADOW_BLUR, 1.0); - set_param(PARAM_SHADOW_BIAS, 0.02); + set_param(PARAM_SHADOW_BIAS, 0.03); set_param(PARAM_SHADOW_NORMAL_BIAS, 1.0); set_param(PARAM_TRANSMITTANCE_BIAS, 0.05); - set_param(PARAM_SHADOW_VOLUMETRIC_FOG_FADE, 1.0); + set_param(PARAM_SHADOW_VOLUMETRIC_FOG_FADE, 0.1); set_param(PARAM_SHADOW_FADE_START, 1); set_disable_scale(true); } Light3D::Light3D() { - type = RenderingServer::LIGHT_DIRECTIONAL; - ERR_PRINT("Light3D should not be instanced directly; use the DirectionalLight3D, OmniLight3D or SpotLight3D subtypes instead."); + ERR_PRINT("Light3D should not be instantiated directly; use the DirectionalLight3D, OmniLight3D or SpotLight3D subtypes instead."); } Light3D::~Light3D() { @@ -376,15 +371,6 @@ DirectionalLight3D::ShadowMode DirectionalLight3D::get_shadow_mode() const { return shadow_mode; } -void DirectionalLight3D::set_shadow_depth_range(ShadowDepthRange p_range) { - shadow_depth_range = p_range; - RS::get_singleton()->light_directional_set_shadow_depth_range_mode(light, RS::LightDirectionalShadowDepthRangeMode(p_range)); -} - -DirectionalLight3D::ShadowDepthRange DirectionalLight3D::get_shadow_depth_range() const { - return shadow_depth_range; -} - void DirectionalLight3D::set_blend_splits(bool p_enable) { blend_splits = p_enable; RS::get_singleton()->light_directional_set_blend_splits(light, p_enable); @@ -394,16 +380,25 @@ bool DirectionalLight3D::is_blend_splits_enabled() const { return blend_splits; } +void DirectionalLight3D::set_sky_only(bool p_sky_only) { + sky_only = p_sky_only; + RS::get_singleton()->light_directional_set_sky_only(light, p_sky_only); +} + +bool DirectionalLight3D::is_sky_only() const { + return sky_only; +} + void DirectionalLight3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_shadow_mode", "mode"), &DirectionalLight3D::set_shadow_mode); ClassDB::bind_method(D_METHOD("get_shadow_mode"), &DirectionalLight3D::get_shadow_mode); - ClassDB::bind_method(D_METHOD("set_shadow_depth_range", "mode"), &DirectionalLight3D::set_shadow_depth_range); - ClassDB::bind_method(D_METHOD("get_shadow_depth_range"), &DirectionalLight3D::get_shadow_depth_range); - ClassDB::bind_method(D_METHOD("set_blend_splits", "enabled"), &DirectionalLight3D::set_blend_splits); ClassDB::bind_method(D_METHOD("is_blend_splits_enabled"), &DirectionalLight3D::is_blend_splits_enabled); + ClassDB::bind_method(D_METHOD("set_sky_only", "priority"), &DirectionalLight3D::set_sky_only); + ClassDB::bind_method(D_METHOD("is_sky_only"), &DirectionalLight3D::is_sky_only); + ADD_GROUP("Directional Shadow", "directional_shadow_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "directional_shadow_mode", PROPERTY_HINT_ENUM, "Orthogonal (Fast),PSSM 2 Splits (Average),PSSM 4 Splits (Slow)"), "set_shadow_mode", "get_shadow_mode"); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_split_1", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_param", "get_param", PARAM_SHADOW_SPLIT_1_OFFSET); @@ -411,16 +406,14 @@ void DirectionalLight3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_split_3", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_param", "get_param", PARAM_SHADOW_SPLIT_3_OFFSET); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_fade_start", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_param", "get_param", PARAM_SHADOW_FADE_START); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "directional_shadow_blend_splits"), "set_blend_splits", "is_blend_splits_enabled"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "directional_shadow_depth_range", PROPERTY_HINT_ENUM, "Stable,Optimized"), "set_shadow_depth_range", "get_shadow_depth_range"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_max_distance", PROPERTY_HINT_EXP_RANGE, "0,8192,0.1,or_greater"), "set_param", "get_param", PARAM_SHADOW_MAX_DISTANCE); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_pancake_size", PROPERTY_HINT_EXP_RANGE, "0,1024,0.1,or_greater"), "set_param", "get_param", PARAM_SHADOW_PANCAKE_SIZE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_max_distance", PROPERTY_HINT_RANGE, "0,8192,0.1,or_greater,exp"), "set_param", "get_param", PARAM_SHADOW_MAX_DISTANCE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "directional_shadow_pancake_size", PROPERTY_HINT_RANGE, "0,1024,0.1,or_greater,exp"), "set_param", "get_param", PARAM_SHADOW_PANCAKE_SIZE); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_in_sky_only"), "set_sky_only", "is_sky_only"); BIND_ENUM_CONSTANT(SHADOW_ORTHOGONAL); BIND_ENUM_CONSTANT(SHADOW_PARALLEL_2_SPLITS); BIND_ENUM_CONSTANT(SHADOW_PARALLEL_4_SPLITS); - - BIND_ENUM_CONSTANT(SHADOW_DEPTH_RANGE_STABLE); - BIND_ENUM_CONSTANT(SHADOW_DEPTH_RANGE_OPTIMIZED); } DirectionalLight3D::DirectionalLight3D() : @@ -428,10 +421,8 @@ DirectionalLight3D::DirectionalLight3D() : set_param(PARAM_SHADOW_MAX_DISTANCE, 100); set_param(PARAM_SHADOW_FADE_START, 0.8); // Increase the default shadow bias to better suit most scenes. - // Leave normal bias untouched as it doesn't benefit DirectionalLight3D as much as OmniLight3D. - set_param(PARAM_SHADOW_BIAS, 0.05); + set_param(PARAM_SHADOW_BIAS, 0.1); set_shadow_mode(SHADOW_PARALLEL_4_SPLITS); - set_shadow_depth_range(SHADOW_DEPTH_RANGE_STABLE); blend_splits = false; } @@ -444,17 +435,14 @@ OmniLight3D::ShadowMode OmniLight3D::get_shadow_mode() const { return shadow_mode; } -String OmniLight3D::get_configuration_warning() const { - String warning = Light3D::get_configuration_warning(); +TypedArray<String> OmniLight3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (!has_shadow() && get_projector().is_valid()) { - if (!warning.empty()) { - warning += "\n\n"; - } - warning += TTR("Projector texture only works with shadows active."); + warnings.push_back(TTR("Projector texture only works with shadows active.")); } - return warning; + return warnings; } void OmniLight3D::_bind_methods() { @@ -462,7 +450,7 @@ void OmniLight3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_shadow_mode"), &OmniLight3D::get_shadow_mode); ADD_GROUP("Omni", "omni_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "omni_range", PROPERTY_HINT_EXP_RANGE, "0,4096,0.1,or_greater"), "set_param", "get_param", PARAM_RANGE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "omni_range", PROPERTY_HINT_RANGE, "0,4096,0.1,or_greater,exp"), "set_param", "get_param", PARAM_RANGE); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "omni_attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_param", "get_param", PARAM_ATTENUATION); ADD_PROPERTY(PropertyInfo(Variant::INT, "omni_shadow_mode", PROPERTY_HINT_ENUM, "Dual Paraboloid,Cube"), "set_shadow_mode", "get_shadow_mode"); @@ -474,34 +462,27 @@ OmniLight3D::OmniLight3D() : Light3D(RenderingServer::LIGHT_OMNI) { set_shadow_mode(SHADOW_CUBE); // Increase the default shadow biases to better suit most scenes. - set_param(PARAM_SHADOW_BIAS, 0.1); - set_param(PARAM_SHADOW_NORMAL_BIAS, 2.0); + set_param(PARAM_SHADOW_BIAS, 0.2); } -String SpotLight3D::get_configuration_warning() const { - String warning = Light3D::get_configuration_warning(); +TypedArray<String> SpotLight3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (has_shadow() && get_param(PARAM_SPOT_ANGLE) >= 90.0) { - if (!warning.empty()) { - warning += "\n\n"; - } - warning += TTR("A SpotLight3D with an angle wider than 90 degrees cannot cast shadows."); + warnings.push_back(TTR("A SpotLight3D with an angle wider than 90 degrees cannot cast shadows.")); } if (!has_shadow() && get_projector().is_valid()) { - if (!warning.empty()) { - warning += "\n\n"; - } - warning += TTR("Projector texture only works with shadows active."); + warnings.push_back(TTR("Projector texture only works with shadows active.")); } - return warning; + return warnings; } void SpotLight3D::_bind_methods() { ADD_GROUP("Spot", "spot_"); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "spot_range", PROPERTY_HINT_EXP_RANGE, "0,4096,0.1,or_greater"), "set_param", "get_param", PARAM_RANGE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "spot_range", PROPERTY_HINT_RANGE, "0,4096,0.1,or_greater,exp"), "set_param", "get_param", PARAM_RANGE); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "spot_attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_param", "get_param", PARAM_ATTENUATION); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "spot_angle", PROPERTY_HINT_RANGE, "0,180,0.1"), "set_param", "get_param", PARAM_SPOT_ANGLE); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "spot_angle", PROPERTY_HINT_RANGE, "0,180,0.1,degrees"), "set_param", "get_param", PARAM_SPOT_ANGLE); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "spot_angle_attenuation", PROPERTY_HINT_EXP_EASING, "attenuation"), "set_param", "get_param", PARAM_SPOT_ATTENUATION); } diff --git a/scene/3d/light_3d.h b/scene/3d/light_3d.h index 69c0478b86..ecea60339f 100644 --- a/scene/3d/light_3d.h +++ b/scene/3d/light_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,8 +32,6 @@ #define LIGHT_3D_H #include "scene/3d/visual_instance_3d.h" -#include "scene/resources/texture.h" -#include "servers/rendering_server.h" class Light3D : public VisualInstance3D { GDCLASS(Light3D, VisualInstance3D); @@ -71,16 +69,16 @@ public: private: Color color; - float param[PARAM_MAX]; + real_t param[PARAM_MAX] = {}; Color shadow_color; - bool shadow; - bool negative; - bool reverse_cull; - uint32_t cull_mask; - RS::LightType type; - bool editor_only; + bool shadow = false; + bool negative = false; + bool reverse_cull = false; + uint32_t cull_mask = 0; + RS::LightType type = RenderingServer::LIGHT_DIRECTIONAL; + bool editor_only = false; void _update_visibility(); - BakeMode bake_mode; + BakeMode bake_mode = BAKE_DYNAMIC; Ref<Texture2D> projector; // bind helpers @@ -102,8 +100,8 @@ public: void set_editor_only(bool p_editor_only); bool is_editor_only() const; - void set_param(Param p_param, float p_value); - float get_param(Param p_param) const; + void set_param(Param p_param, real_t p_value); + real_t get_param(Param p_param) const; void set_shadow(bool p_enable); bool has_shadow() const; @@ -149,15 +147,10 @@ public: SHADOW_PARALLEL_4_SPLITS, }; - enum ShadowDepthRange { - SHADOW_DEPTH_RANGE_STABLE = RS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_STABLE, - SHADOW_DEPTH_RANGE_OPTIMIZED = RS::LIGHT_DIRECTIONAL_SHADOW_DEPTH_RANGE_OPTIMIZED, - }; - private: bool blend_splits; ShadowMode shadow_mode; - ShadowDepthRange shadow_depth_range; + bool sky_only = false; protected: static void _bind_methods(); @@ -166,17 +159,16 @@ public: void set_shadow_mode(ShadowMode p_mode); ShadowMode get_shadow_mode() const; - void set_shadow_depth_range(ShadowDepthRange p_range); - ShadowDepthRange get_shadow_depth_range() const; - void set_blend_splits(bool p_enable); bool is_blend_splits_enabled() const; + void set_sky_only(bool p_sky_only); + bool is_sky_only() const; + DirectionalLight3D(); }; VARIANT_ENUM_CAST(DirectionalLight3D::ShadowMode) -VARIANT_ENUM_CAST(DirectionalLight3D::ShadowDepthRange) class OmniLight3D : public Light3D { GDCLASS(OmniLight3D, Light3D); @@ -198,7 +190,7 @@ public: void set_shadow_mode(ShadowMode p_mode); ShadowMode get_shadow_mode() const; - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; OmniLight3D(); }; @@ -212,7 +204,7 @@ protected: static void _bind_methods(); public: - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; SpotLight3D() : Light3D(RenderingServer::LIGHT_SPOT) {} diff --git a/scene/3d/baked_lightmap.cpp b/scene/3d/lightmap_gi.cpp index e867891ec0..7dd083e314 100644 --- a/scene/3d/baked_lightmap.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -1,12 +1,12 @@ /*************************************************************************/ -/* baked_lightmap.cpp */ +/* lightmap_gi.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,19 +28,14 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "baked_lightmap.h" +#include "lightmap_gi.h" #include "core/io/config_file.h" -#include "core/io/resource_saver.h" -#include "core/math/camera_matrix.h" #include "core/math/delaunay_3d.h" -#include "core/os/dir_access.h" -#include "core/os/file_access.h" -#include "core/os/os.h" -#include "core/sort_array.h" #include "lightmap_probe.h" +#include "scene/3d/mesh_instance_3d.h" -void BakedLightmapData::add_user(const NodePath &p_path, const Rect2 &p_uv_scale, int p_slice_index, int32_t p_sub_instance) { +void LightmapGIData::add_user(const NodePath &p_path, const Rect2 &p_uv_scale, int p_slice_index, int32_t p_sub_instance) { User user; user.path = p_path; user.uv_scale = p_uv_scale; @@ -49,35 +44,36 @@ void BakedLightmapData::add_user(const NodePath &p_path, const Rect2 &p_uv_scale users.push_back(user); } -int BakedLightmapData::get_user_count() const { +int LightmapGIData::get_user_count() const { return users.size(); } -NodePath BakedLightmapData::get_user_path(int p_user) const { +NodePath LightmapGIData::get_user_path(int p_user) const { ERR_FAIL_INDEX_V(p_user, users.size(), NodePath()); return users[p_user].path; } -int32_t BakedLightmapData::get_user_sub_instance(int p_user) const { +int32_t LightmapGIData::get_user_sub_instance(int p_user) const { ERR_FAIL_INDEX_V(p_user, users.size(), -1); return users[p_user].sub_instance; } -Rect2 BakedLightmapData::get_user_lightmap_uv_scale(int p_user) const { +Rect2 LightmapGIData::get_user_lightmap_uv_scale(int p_user) const { ERR_FAIL_INDEX_V(p_user, users.size(), Rect2()); return users[p_user].uv_scale; } -int BakedLightmapData::get_user_lightmap_slice_index(int p_user) const { +int LightmapGIData::get_user_lightmap_slice_index(int p_user) const { ERR_FAIL_INDEX_V(p_user, users.size(), -1); return users[p_user].slice_index; } -void BakedLightmapData::clear_users() { +void LightmapGIData::clear_users() { users.clear(); } -void BakedLightmapData::_set_user_data(const Array &p_data) { +void LightmapGIData::_set_user_data(const Array &p_data) { + ERR_FAIL_COND(p_data.size() <= 0); ERR_FAIL_COND((p_data.size() % 4) != 0); for (int i = 0; i < p_data.size(); i += 4) { @@ -85,7 +81,7 @@ void BakedLightmapData::_set_user_data(const Array &p_data) { } } -Array BakedLightmapData::_get_user_data() const { +Array LightmapGIData::_get_user_data() const { Array ret; for (int i = 0; i < users.size(); i++) { ret.push_back(users[i].path); @@ -96,33 +92,33 @@ Array BakedLightmapData::_get_user_data() const { return ret; } -RID BakedLightmapData::get_rid() const { +RID LightmapGIData::get_rid() const { return lightmap; } -void BakedLightmapData::clear() { +void LightmapGIData::clear() { users.clear(); } -void BakedLightmapData::set_light_texture(const Ref<TextureLayered> &p_light_texture) { +void LightmapGIData::set_light_texture(const Ref<TextureLayered> &p_light_texture) { light_texture = p_light_texture; RS::get_singleton()->lightmap_set_textures(lightmap, light_texture.is_valid() ? light_texture->get_rid() : RID(), uses_spherical_harmonics); } -Ref<TextureLayered> BakedLightmapData::get_light_texture() const { +Ref<TextureLayered> LightmapGIData::get_light_texture() const { return light_texture; } -void BakedLightmapData::set_uses_spherical_harmonics(bool p_enable) { +void LightmapGIData::set_uses_spherical_harmonics(bool p_enable) { uses_spherical_harmonics = p_enable; RS::get_singleton()->lightmap_set_textures(lightmap, light_texture.is_valid() ? light_texture->get_rid() : RID(), uses_spherical_harmonics); } -bool BakedLightmapData::is_using_spherical_harmonics() const { +bool LightmapGIData::is_using_spherical_harmonics() const { return uses_spherical_harmonics; } -void BakedLightmapData::set_capture_data(const AABB &p_bounds, bool p_interior, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) { +void LightmapGIData::set_capture_data(const AABB &p_bounds, bool p_interior, const PackedVector3Array &p_points, const PackedColorArray &p_point_sh, const PackedInt32Array &p_tetrahedra, const PackedInt32Array &p_bsp_tree) { if (p_points.size()) { int pc = p_points.size(); ERR_FAIL_COND(pc * 9 != p_point_sh.size()); @@ -140,31 +136,31 @@ void BakedLightmapData::set_capture_data(const AABB &p_bounds, bool p_interior, bounds = p_bounds; } -PackedVector3Array BakedLightmapData::get_capture_points() const { +PackedVector3Array LightmapGIData::get_capture_points() const { return RS::get_singleton()->lightmap_get_probe_capture_points(lightmap); } -PackedColorArray BakedLightmapData::get_capture_sh() const { +PackedColorArray LightmapGIData::get_capture_sh() const { return RS::get_singleton()->lightmap_get_probe_capture_sh(lightmap); } -PackedInt32Array BakedLightmapData::get_capture_tetrahedra() const { +PackedInt32Array LightmapGIData::get_capture_tetrahedra() const { return RS::get_singleton()->lightmap_get_probe_capture_tetrahedra(lightmap); } -PackedInt32Array BakedLightmapData::get_capture_bsp_tree() const { +PackedInt32Array LightmapGIData::get_capture_bsp_tree() const { return RS::get_singleton()->lightmap_get_probe_capture_bsp_tree(lightmap); } -AABB BakedLightmapData::get_capture_bounds() const { +AABB LightmapGIData::get_capture_bounds() const { return bounds; } -bool BakedLightmapData::is_interior() const { +bool LightmapGIData::is_interior() const { return interior; } -void BakedLightmapData::_set_probe_data(const Dictionary &p_data) { +void LightmapGIData::_set_probe_data(const Dictionary &p_data) { ERR_FAIL_COND(!p_data.has("bounds")); ERR_FAIL_COND(!p_data.has("points")); ERR_FAIL_COND(!p_data.has("tetrahedra")); @@ -174,7 +170,7 @@ void BakedLightmapData::_set_probe_data(const Dictionary &p_data) { set_capture_data(p_data["bounds"], p_data["interior"], p_data["points"], p_data["sh"], p_data["tetrahedra"], p_data["bsp"]); } -Dictionary BakedLightmapData::_get_probe_data() const { +Dictionary LightmapGIData::_get_probe_data() const { Dictionary d; d["bounds"] = get_capture_bounds(); d["points"] = get_capture_points(); @@ -185,23 +181,23 @@ Dictionary BakedLightmapData::_get_probe_data() const { return d; } -void BakedLightmapData::_bind_methods() { - ClassDB::bind_method(D_METHOD("_set_user_data", "data"), &BakedLightmapData::_set_user_data); - ClassDB::bind_method(D_METHOD("_get_user_data"), &BakedLightmapData::_get_user_data); +void LightmapGIData::_bind_methods() { + ClassDB::bind_method(D_METHOD("_set_user_data", "data"), &LightmapGIData::_set_user_data); + ClassDB::bind_method(D_METHOD("_get_user_data"), &LightmapGIData::_get_user_data); - ClassDB::bind_method(D_METHOD("set_light_texture", "light_texture"), &BakedLightmapData::set_light_texture); - ClassDB::bind_method(D_METHOD("get_light_texture"), &BakedLightmapData::get_light_texture); + ClassDB::bind_method(D_METHOD("set_light_texture", "light_texture"), &LightmapGIData::set_light_texture); + ClassDB::bind_method(D_METHOD("get_light_texture"), &LightmapGIData::get_light_texture); - ClassDB::bind_method(D_METHOD("set_uses_spherical_harmonics", "uses_spherical_harmonics"), &BakedLightmapData::set_uses_spherical_harmonics); - ClassDB::bind_method(D_METHOD("is_using_spherical_harmonics"), &BakedLightmapData::is_using_spherical_harmonics); + ClassDB::bind_method(D_METHOD("set_uses_spherical_harmonics", "uses_spherical_harmonics"), &LightmapGIData::set_uses_spherical_harmonics); + ClassDB::bind_method(D_METHOD("is_using_spherical_harmonics"), &LightmapGIData::is_using_spherical_harmonics); - ClassDB::bind_method(D_METHOD("add_user", "path", "lightmap", "offset"), &BakedLightmapData::add_user); - ClassDB::bind_method(D_METHOD("get_user_count"), &BakedLightmapData::get_user_count); - ClassDB::bind_method(D_METHOD("get_user_path", "user_idx"), &BakedLightmapData::get_user_path); - ClassDB::bind_method(D_METHOD("clear_users"), &BakedLightmapData::clear_users); + ClassDB::bind_method(D_METHOD("add_user", "path", "uv_scale", "slice_index", "sub_instance"), &LightmapGIData::add_user); + ClassDB::bind_method(D_METHOD("get_user_count"), &LightmapGIData::get_user_count); + ClassDB::bind_method(D_METHOD("get_user_path", "user_idx"), &LightmapGIData::get_user_path); + ClassDB::bind_method(D_METHOD("clear_users"), &LightmapGIData::clear_users); - ClassDB::bind_method(D_METHOD("_set_probe_data", "data"), &BakedLightmapData::_set_probe_data); - ClassDB::bind_method(D_METHOD("_get_probe_data"), &BakedLightmapData::_get_probe_data); + ClassDB::bind_method(D_METHOD("_set_probe_data", "data"), &LightmapGIData::_set_probe_data); + ClassDB::bind_method(D_METHOD("_get_probe_data"), &LightmapGIData::_get_probe_data); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_texture", PROPERTY_HINT_RESOURCE_TYPE, "TextureLayered"), "set_light_texture", "get_light_texture"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "uses_spherical_harmonics", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "set_uses_spherical_harmonics", "is_using_spherical_harmonics"); @@ -209,17 +205,17 @@ void BakedLightmapData::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "probe_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_probe_data", "_get_probe_data"); } -BakedLightmapData::BakedLightmapData() { +LightmapGIData::LightmapGIData() { lightmap = RS::get_singleton()->lightmap_create(); } -BakedLightmapData::~BakedLightmapData() { +LightmapGIData::~LightmapGIData() { RS::get_singleton()->free(lightmap); } /////////////////////////// -void BakedLightmap::_find_meshes_and_lights(Node *p_at_node, Vector<MeshesFound> &meshes, Vector<LightsFound> &lights, Vector<Vector3> &probes) { +void LightmapGI::_find_meshes_and_lights(Node *p_at_node, Vector<MeshesFound> &meshes, Vector<LightsFound> &lights, Vector<Vector3> &probes) { MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_at_node); if (mi && mi->get_gi_mode() == GeometryInstance3D::GI_MODE_BAKED && mi->is_visible_in_tree()) { Ref<Mesh> mesh = mi->get_mesh(); @@ -258,7 +254,7 @@ void BakedLightmap::_find_meshes_and_lights(Node *p_at_node, Vector<MeshesFound> if (all_override.is_valid()) { mf.overrides.push_back(all_override); } else { - mf.overrides.push_back(mi->get_surface_material(i)); + mf.overrides.push_back(mi->get_surface_override_material(i)); } } @@ -272,7 +268,7 @@ void BakedLightmap::_find_meshes_and_lights(Node *p_at_node, Vector<MeshesFound> if (!mi && s) { Array bmeshes = p_at_node->call("get_bake_bmeshes"); if (bmeshes.size() && (bmeshes.size() & 1) == 0) { - Transform xf = get_global_transform().affine_inverse() * s->get_global_transform(); + Transform3D xf = get_global_transform().affine_inverse() * s->get_global_transform(); for (int i = 0; i < bmeshes.size(); i += 2) { Ref<Mesh> mesh = bmeshes[i]; if (!mesh.is_valid()) { @@ -281,7 +277,7 @@ void BakedLightmap::_find_meshes_and_lights(Node *p_at_node, Vector<MeshesFound> MeshesFound mf; - Transform mesh_xf = bmeshes[i + 1]; + Transform3D mesh_xf = bmeshes[i + 1]; mf.xform = xf * mesh_xf; mf.node_path = get_path_to(s); mf.subindex = i / 2; @@ -305,7 +301,7 @@ void BakedLightmap::_find_meshes_and_lights(Node *p_at_node, Vector<MeshesFound> LightmapProbe *probe = Object::cast_to<LightmapProbe>(p_at_node); if (probe) { - Transform xf = get_global_transform().affine_inverse() * probe->get_global_transform(); + Transform3D xf = get_global_transform().affine_inverse() * probe->get_global_transform(); probes.push_back(xf.origin); } @@ -319,7 +315,7 @@ void BakedLightmap::_find_meshes_and_lights(Node *p_at_node, Vector<MeshesFound> } } -int BakedLightmap::_bsp_get_simplex_side(const Vector<Vector3> &p_points, const LocalVector<BSPSimplex> &p_simplices, const Plane &p_plane, uint32_t p_simplex) const { +int LightmapGI::_bsp_get_simplex_side(const Vector<Vector3> &p_points, const LocalVector<BSPSimplex> &p_simplices, const Plane &p_plane, uint32_t p_simplex) const { int over = 0; int under = 0; int coplanar = 0; @@ -347,7 +343,7 @@ int BakedLightmap::_bsp_get_simplex_side(const Vector<Vector3> &p_points, const //#define DEBUG_BSP -int32_t BakedLightmap::_compute_bsp_tree(const Vector<Vector3> &p_points, const LocalVector<Plane> &p_planes, LocalVector<int32_t> &planes_tested, const LocalVector<BSPSimplex> &p_simplices, const LocalVector<int32_t> &p_simplex_indices, LocalVector<BSPNode> &bsp_nodes) { +int32_t LightmapGI::_compute_bsp_tree(const Vector<Vector3> &p_points, const LocalVector<Plane> &p_planes, LocalVector<int32_t> &planes_tested, const LocalVector<BSPSimplex> &p_simplices, const LocalVector<int32_t> &p_simplex_indices, LocalVector<BSPNode> &bsp_nodes) { //if we reach here, it means there is more than one simplex int32_t node_index = (int32_t)bsp_nodes.size(); bsp_nodes.push_back(BSPNode()); @@ -448,7 +444,7 @@ int32_t BakedLightmap::_compute_bsp_tree(const Vector<Vector3> &p_points, const ERR_FAIL_COND_V(p_simplex_indices.size() <= 1, 0); //should not happen, this is a bug // Failed to separate the tetrahedrons using planes - // this means Delaunay borked at some point. + // this means Delaunay broke at some point. // Luckily, because we are using tetrahedrons, we can resort to // less precise but still working ways to generate the separating plane // this will most likely look bad when interpolating, but at least it will not crash. @@ -510,7 +506,7 @@ int32_t BakedLightmap::_compute_bsp_tree(const Vector<Vector3> &p_points, const node.plane = best_plane; if (indices_under.size() == 0) { - //noting to do here + //nothing to do here node.under = BSPNode::EMPTY_LEAF; } else if (indices_under.size() == 1) { node.under = -(indices_under[0] + 1); @@ -519,7 +515,7 @@ int32_t BakedLightmap::_compute_bsp_tree(const Vector<Vector3> &p_points, const } if (indices_over.size() == 0) { - //noting to do here + //nothing to do here node.over = BSPNode::EMPTY_LEAF; } else if (indices_over.size() == 1) { node.over = -(indices_over[0] + 1); @@ -532,7 +528,7 @@ int32_t BakedLightmap::_compute_bsp_tree(const Vector<Vector3> &p_points, const return node_index; } -bool BakedLightmap::_lightmap_bake_step_function(float p_completion, const String &p_text, void *ud, bool p_refresh) { +bool LightmapGI::_lightmap_bake_step_function(float p_completion, const String &p_text, void *ud, bool p_refresh) { BakeStepUD *bsud = (BakeStepUD *)ud; bool ret = false; if (bsud->func) { @@ -541,7 +537,7 @@ bool BakedLightmap::_lightmap_bake_step_function(float p_completion, const Strin return ret; } -void BakedLightmap::_plot_triangle_into_octree(GenProbesOctree *p_cell, float p_cell_size, const Vector3 *p_triangle) { +void LightmapGI::_plot_triangle_into_octree(GenProbesOctree *p_cell, float p_cell_size, const Vector3 *p_triangle) { for (int i = 0; i < 8; i++) { Vector3i pos = p_cell->offset; uint32_t half_size = p_cell->size / 2; @@ -577,7 +573,7 @@ void BakedLightmap::_plot_triangle_into_octree(GenProbesOctree *p_cell, float p_ } } -void BakedLightmap::_gen_new_positions_from_octree(const GenProbesOctree *p_cell, float p_cell_size, const Vector<Vector3> &probe_positions, LocalVector<Vector3> &new_probe_positions, HashMap<Vector3i, bool, Vector3iHash> &positions_used, const AABB &p_bounds) { +void LightmapGI::_gen_new_positions_from_octree(const GenProbesOctree *p_cell, float p_cell_size, const Vector<Vector3> &probe_positions, LocalVector<Vector3> &new_probe_positions, HashMap<Vector3i, bool, Vector3iHash> &positions_used, const AABB &p_bounds) { for (int i = 0; i < 8; i++) { Vector3i pos = p_cell->offset; if (i & 1) { @@ -598,7 +594,7 @@ void BakedLightmap::_gen_new_positions_from_octree(const GenProbesOctree *p_cell const Vector3 *pp = probe_positions.ptr(); bool exists = false; for (int j = 0; j < ppcount; j++) { - if (pp[j].distance_to(real_pos) < CMP_EPSILON) { + if (pp[j].is_equal_approx(real_pos)) { exists = true; break; } @@ -617,11 +613,7 @@ void BakedLightmap::_gen_new_positions_from_octree(const GenProbesOctree *p_cell } } -BakedLightmap::BakeError BakedLightmap::bake(Node *p_from_node, String p_image_data_path, Lightmapper::BakeStepFunc p_bake_step, void *p_bake_userdata) { - if (p_image_data_path == "" && (get_light_data().is_null() || !get_light_data()->get_path().is_resource_file())) { - return BAKE_ERROR_NO_SAVE_PATH; - } - +LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_path, Lightmapper::BakeStepFunc p_bake_step, void *p_bake_userdata) { if (p_image_data_path == "") { if (get_light_data().is_null()) { return BAKE_ERROR_NO_SAVE_PATH; @@ -659,7 +651,7 @@ BakedLightmap::BakeError BakedLightmap::bake(Node *p_from_node, String p_image_d } // create mesh data for insert - //get the base material textures, help compute altlas size and bounds + //get the base material textures, help compute atlas size and bounds for (int m_i = 0; m_i < meshes_found.size(); m_i++) { if (p_bake_step) { float p = (float)(m_i) / meshes_found.size(); @@ -678,7 +670,7 @@ BakedLightmap::BakeError BakedLightmap::bake(Node *p_from_node, String p_image_d } TypedArray<Image> images = RS::get_singleton()->bake_render_uv2(mf.mesh->get_rid(), overrides, lightmap_size); - ERR_FAIL_COND_V(images.empty(), BAKE_ERROR_CANT_CREATE_IMAGE); + ERR_FAIL_COND_V(images.is_empty(), BAKE_ERROR_CANT_CREATE_IMAGE); Ref<Image> albedo = images[RS::BAKE_CHANNEL_ALBEDO_ALPHA]; Ref<Image> orm = images[RS::BAKE_CHANNEL_ORM]; @@ -720,7 +712,7 @@ BakedLightmap::BakeError BakedLightmap::bake(Node *p_from_node, String p_image_d w_albedo[i + 3] = 255; } - md.albedo_on_uv2.instance(); + md.albedo_on_uv2.instantiate(); md.albedo_on_uv2->create(lightmap_size.width, lightmap_size.height, false, Image::FORMAT_RGBA8, albedom); } @@ -890,7 +882,7 @@ BakedLightmap::BakeError BakedLightmap::bake(Node *p_from_node, String p_image_d } for (int i = 0; i < lights_found.size(); i++) { Light3D *light = lights_found[i].light; - Transform xf = lights_found[i].xform; + Transform3D xf = lights_found[i].xform; if (Object::cast_to<DirectionalLight3D>(light)) { DirectionalLight3D *l = Object::cast_to<DirectionalLight3D>(light); @@ -943,7 +935,7 @@ BakedLightmap::BakeError BakedLightmap::bake(Node *p_from_node, String p_image_d } break; case ENVIRONMENT_MODE_CUSTOM_COLOR: { - environment_image.instance(); + environment_image.instantiate(); environment_image->create(128, 64, false, Image::FORMAT_RGBAF); Color c = environment_custom_color; c.r *= environment_custom_energy; @@ -973,9 +965,9 @@ BakedLightmap::BakeError BakedLightmap::bake(Node *p_from_node, String p_image_d for (int i = 0; i < lightmapper->get_bake_texture_count(); i++) { images.push_back(lightmapper->get_bake_texture(i)); } - //we assume they are all the same, so lets create a large one for saving + //we assume they are all the same, so let's create a large one for saving Ref<Image> large_image; - large_image.instance(); + large_image.instantiate(); large_image->create(images[0]->get_width(), images[0]->get_height() * images.size(), false, images[0]->get_format()); @@ -987,7 +979,7 @@ BakedLightmap::BakeError BakedLightmap::bake(Node *p_from_node, String p_image_d Ref<ConfigFile> config; - config.instance(); + config.instantiate(); if (FileAccess::exists(base_path + ".import")) { config->load(base_path + ".import"); } @@ -1014,13 +1006,13 @@ BakedLightmap::BakeError BakedLightmap::bake(Node *p_from_node, String p_image_d /* POSTBAKE: Save Light Data */ - Ref<BakedLightmapData> data; + Ref<LightmapGIData> data; if (get_light_data().is_valid()) { data = get_light_data(); - set_light_data(Ref<BakedLightmapData>()); //clear + set_light_data(Ref<LightmapGIData>()); //clear data->clear(); } else { - data.instance(); + data.instantiate(); } data->set_light_texture(texture); @@ -1186,7 +1178,7 @@ BakedLightmap::BakeError BakedLightmap::bake(Node *p_from_node, String p_image_d return BAKE_ERROR_OK; } -void BakedLightmap::_notification(int p_what) { +void LightmapGI::_notification(int p_what) { if (p_what == NOTIFICATION_POST_ENTER_TREE) { if (light_data.is_valid()) { _assign_lightmaps(); @@ -1200,7 +1192,7 @@ void BakedLightmap::_notification(int p_what) { } } -void BakedLightmap::_assign_lightmaps() { +void LightmapGI::_assign_lightmaps() { ERR_FAIL_COND(!light_data.is_valid()); for (int i = 0; i < light_data->get_user_count(); i++) { @@ -1219,7 +1211,7 @@ void BakedLightmap::_assign_lightmaps() { } } -void BakedLightmap::_clear_lightmaps() { +void LightmapGI::_clear_lightmaps() { ERR_FAIL_COND(!light_data.is_valid()); for (int i = 0; i < light_data->get_user_count(); i++) { Node *node = get_node(light_data->get_user_path(i)); @@ -1237,7 +1229,7 @@ void BakedLightmap::_clear_lightmaps() { } } -void BakedLightmap::set_light_data(const Ref<BakedLightmapData> &p_data) { +void LightmapGI::set_light_data(const Ref<LightmapGIData> &p_data) { if (light_data.is_valid()) { if (is_inside_tree()) { _clear_lightmaps(); @@ -1253,174 +1245,174 @@ void BakedLightmap::set_light_data(const Ref<BakedLightmapData> &p_data) { } } - update_gizmo(); + update_gizmos(); } -Ref<BakedLightmapData> BakedLightmap::get_light_data() const { +Ref<LightmapGIData> LightmapGI::get_light_data() const { return light_data; } -void BakedLightmap::set_bake_quality(BakeQuality p_quality) { +void LightmapGI::set_bake_quality(BakeQuality p_quality) { bake_quality = p_quality; } -BakedLightmap::BakeQuality BakedLightmap::get_bake_quality() const { +LightmapGI::BakeQuality LightmapGI::get_bake_quality() const { return bake_quality; } -AABB BakedLightmap::get_aabb() const { +AABB LightmapGI::get_aabb() const { return AABB(); } -Vector<Face3> BakedLightmap::get_faces(uint32_t p_usage_flags) const { +Vector<Face3> LightmapGI::get_faces(uint32_t p_usage_flags) const { return Vector<Face3>(); } -void BakedLightmap::set_use_denoiser(bool p_enable) { +void LightmapGI::set_use_denoiser(bool p_enable) { use_denoiser = p_enable; } -bool BakedLightmap::is_using_denoiser() const { +bool LightmapGI::is_using_denoiser() const { return use_denoiser; } -void BakedLightmap::set_directional(bool p_enable) { +void LightmapGI::set_directional(bool p_enable) { directional = p_enable; } -bool BakedLightmap::is_directional() const { +bool LightmapGI::is_directional() const { return directional; } -void BakedLightmap::set_interior(bool p_enable) { +void LightmapGI::set_interior(bool p_enable) { interior = p_enable; } -bool BakedLightmap::is_interior() const { +bool LightmapGI::is_interior() const { return interior; } -void BakedLightmap::set_environment_mode(EnvironmentMode p_mode) { +void LightmapGI::set_environment_mode(EnvironmentMode p_mode) { environment_mode = p_mode; - _change_notify(); + notify_property_list_changed(); } -BakedLightmap::EnvironmentMode BakedLightmap::get_environment_mode() const { +LightmapGI::EnvironmentMode LightmapGI::get_environment_mode() const { return environment_mode; } -void BakedLightmap::set_environment_custom_sky(const Ref<Sky> &p_sky) { +void LightmapGI::set_environment_custom_sky(const Ref<Sky> &p_sky) { environment_custom_sky = p_sky; } -Ref<Sky> BakedLightmap::get_environment_custom_sky() const { +Ref<Sky> LightmapGI::get_environment_custom_sky() const { return environment_custom_sky; } -void BakedLightmap::set_environment_custom_color(const Color &p_color) { +void LightmapGI::set_environment_custom_color(const Color &p_color) { environment_custom_color = p_color; } -Color BakedLightmap::get_environment_custom_color() const { +Color LightmapGI::get_environment_custom_color() const { return environment_custom_color; } -void BakedLightmap::set_environment_custom_energy(float p_energy) { +void LightmapGI::set_environment_custom_energy(float p_energy) { environment_custom_energy = p_energy; } -float BakedLightmap::get_environment_custom_energy() const { +float LightmapGI::get_environment_custom_energy() const { return environment_custom_energy; } -void BakedLightmap::set_bounces(int p_bounces) { +void LightmapGI::set_bounces(int p_bounces) { ERR_FAIL_COND(p_bounces < 0 || p_bounces > 16); bounces = p_bounces; } -int BakedLightmap::get_bounces() const { +int LightmapGI::get_bounces() const { return bounces; } -void BakedLightmap::set_bias(float p_bias) { +void LightmapGI::set_bias(float p_bias) { ERR_FAIL_COND(p_bias < 0.00001); bias = p_bias; } -float BakedLightmap::get_bias() const { +float LightmapGI::get_bias() const { return bias; } -void BakedLightmap::set_max_texture_size(int p_size) { +void LightmapGI::set_max_texture_size(int p_size) { ERR_FAIL_COND(p_size < 2048); max_texture_size = p_size; } -int BakedLightmap::get_max_texture_size() const { +int LightmapGI::get_max_texture_size() const { return max_texture_size; } -void BakedLightmap::set_generate_probes(GenerateProbes p_generate_probes) { +void LightmapGI::set_generate_probes(GenerateProbes p_generate_probes) { gen_probes = p_generate_probes; } -BakedLightmap::GenerateProbes BakedLightmap::get_generate_probes() const { +LightmapGI::GenerateProbes LightmapGI::get_generate_probes() const { return gen_probes; } -void BakedLightmap::_validate_property(PropertyInfo &property) const { +void LightmapGI::_validate_property(PropertyInfo &property) const { if (property.name == "environment_custom_sky" && environment_mode != ENVIRONMENT_MODE_CUSTOM_SKY) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "environment_custom_color" && environment_mode != ENVIRONMENT_MODE_CUSTOM_COLOR) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } if (property.name == "environment_custom_energy" && environment_mode != ENVIRONMENT_MODE_CUSTOM_COLOR && environment_mode != ENVIRONMENT_MODE_CUSTOM_SKY) { - property.usage = 0; + property.usage = PROPERTY_USAGE_NONE; } } -void BakedLightmap::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_light_data", "data"), &BakedLightmap::set_light_data); - ClassDB::bind_method(D_METHOD("get_light_data"), &BakedLightmap::get_light_data); +void LightmapGI::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_light_data", "data"), &LightmapGI::set_light_data); + ClassDB::bind_method(D_METHOD("get_light_data"), &LightmapGI::get_light_data); - ClassDB::bind_method(D_METHOD("set_bake_quality", "bake_quality"), &BakedLightmap::set_bake_quality); - ClassDB::bind_method(D_METHOD("get_bake_quality"), &BakedLightmap::get_bake_quality); + ClassDB::bind_method(D_METHOD("set_bake_quality", "bake_quality"), &LightmapGI::set_bake_quality); + ClassDB::bind_method(D_METHOD("get_bake_quality"), &LightmapGI::get_bake_quality); - ClassDB::bind_method(D_METHOD("set_bounces", "bounces"), &BakedLightmap::set_bounces); - ClassDB::bind_method(D_METHOD("get_bounces"), &BakedLightmap::get_bounces); + ClassDB::bind_method(D_METHOD("set_bounces", "bounces"), &LightmapGI::set_bounces); + ClassDB::bind_method(D_METHOD("get_bounces"), &LightmapGI::get_bounces); - ClassDB::bind_method(D_METHOD("set_generate_probes", "subdivision"), &BakedLightmap::set_generate_probes); - ClassDB::bind_method(D_METHOD("get_generate_probes"), &BakedLightmap::get_generate_probes); + ClassDB::bind_method(D_METHOD("set_generate_probes", "subdivision"), &LightmapGI::set_generate_probes); + ClassDB::bind_method(D_METHOD("get_generate_probes"), &LightmapGI::get_generate_probes); - ClassDB::bind_method(D_METHOD("set_bias", "bias"), &BakedLightmap::set_bias); - ClassDB::bind_method(D_METHOD("get_bias"), &BakedLightmap::get_bias); + ClassDB::bind_method(D_METHOD("set_bias", "bias"), &LightmapGI::set_bias); + ClassDB::bind_method(D_METHOD("get_bias"), &LightmapGI::get_bias); - ClassDB::bind_method(D_METHOD("set_environment_mode", "mode"), &BakedLightmap::set_environment_mode); - ClassDB::bind_method(D_METHOD("get_environment_mode"), &BakedLightmap::get_environment_mode); + ClassDB::bind_method(D_METHOD("set_environment_mode", "mode"), &LightmapGI::set_environment_mode); + ClassDB::bind_method(D_METHOD("get_environment_mode"), &LightmapGI::get_environment_mode); - ClassDB::bind_method(D_METHOD("set_environment_custom_sky", "sky"), &BakedLightmap::set_environment_custom_sky); - ClassDB::bind_method(D_METHOD("get_environment_custom_sky"), &BakedLightmap::get_environment_custom_sky); + ClassDB::bind_method(D_METHOD("set_environment_custom_sky", "sky"), &LightmapGI::set_environment_custom_sky); + ClassDB::bind_method(D_METHOD("get_environment_custom_sky"), &LightmapGI::get_environment_custom_sky); - ClassDB::bind_method(D_METHOD("set_environment_custom_color", "color"), &BakedLightmap::set_environment_custom_color); - ClassDB::bind_method(D_METHOD("get_environment_custom_color"), &BakedLightmap::get_environment_custom_color); + ClassDB::bind_method(D_METHOD("set_environment_custom_color", "color"), &LightmapGI::set_environment_custom_color); + ClassDB::bind_method(D_METHOD("get_environment_custom_color"), &LightmapGI::get_environment_custom_color); - ClassDB::bind_method(D_METHOD("set_environment_custom_energy", "energy"), &BakedLightmap::set_environment_custom_energy); - ClassDB::bind_method(D_METHOD("get_environment_custom_energy"), &BakedLightmap::get_environment_custom_energy); + ClassDB::bind_method(D_METHOD("set_environment_custom_energy", "energy"), &LightmapGI::set_environment_custom_energy); + ClassDB::bind_method(D_METHOD("get_environment_custom_energy"), &LightmapGI::get_environment_custom_energy); - ClassDB::bind_method(D_METHOD("set_max_texture_size", "max_texture_size"), &BakedLightmap::set_max_texture_size); - ClassDB::bind_method(D_METHOD("get_max_texture_size"), &BakedLightmap::get_max_texture_size); + ClassDB::bind_method(D_METHOD("set_max_texture_size", "max_texture_size"), &LightmapGI::set_max_texture_size); + ClassDB::bind_method(D_METHOD("get_max_texture_size"), &LightmapGI::get_max_texture_size); - ClassDB::bind_method(D_METHOD("set_use_denoiser", "use_denoiser"), &BakedLightmap::set_use_denoiser); - ClassDB::bind_method(D_METHOD("is_using_denoiser"), &BakedLightmap::is_using_denoiser); + ClassDB::bind_method(D_METHOD("set_use_denoiser", "use_denoiser"), &LightmapGI::set_use_denoiser); + ClassDB::bind_method(D_METHOD("is_using_denoiser"), &LightmapGI::is_using_denoiser); - ClassDB::bind_method(D_METHOD("set_interior", "enable"), &BakedLightmap::set_interior); - ClassDB::bind_method(D_METHOD("is_interior"), &BakedLightmap::is_interior); + ClassDB::bind_method(D_METHOD("set_interior", "enable"), &LightmapGI::set_interior); + ClassDB::bind_method(D_METHOD("is_interior"), &LightmapGI::is_interior); - ClassDB::bind_method(D_METHOD("set_directional", "directional"), &BakedLightmap::set_directional); - ClassDB::bind_method(D_METHOD("is_directional"), &BakedLightmap::is_directional); + ClassDB::bind_method(D_METHOD("set_directional", "directional"), &LightmapGI::set_directional); + ClassDB::bind_method(D_METHOD("is_directional"), &LightmapGI::is_directional); - // ClassDB::bind_method(D_METHOD("bake", "from_node"), &BakedLightmap::bake, DEFVAL(Variant())); + // ClassDB::bind_method(D_METHOD("bake", "from_node"), &LightmapGI::bake, DEFVAL(Variant())); ADD_GROUP("Tweaks", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "quality", PROPERTY_HINT_ENUM, "Low,Medium,High,Ultra"), "set_bake_quality", "get_bake_quality"); @@ -1438,7 +1430,7 @@ void BakedLightmap::_bind_methods() { ADD_GROUP("Gen Probes", "generate_probes_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "generate_probes_subdiv", PROPERTY_HINT_ENUM, "Disabled,4,8,16,32"), "set_generate_probes", "get_generate_probes"); ADD_GROUP("Data", ""); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_data", PROPERTY_HINT_RESOURCE_TYPE, "BakedLightmapData"), "set_light_data", "get_light_data"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "light_data", PROPERTY_HINT_RESOURCE_TYPE, "LightmapGIData"), "set_light_data", "get_light_data"); BIND_ENUM_CONSTANT(BAKE_QUALITY_LOW); BIND_ENUM_CONSTANT(BAKE_QUALITY_MEDIUM); @@ -1465,18 +1457,5 @@ void BakedLightmap::_bind_methods() { BIND_ENUM_CONSTANT(ENVIRONMENT_MODE_CUSTOM_COLOR); } -BakedLightmap::BakedLightmap() { - environment_mode = ENVIRONMENT_MODE_DISABLED; - environment_custom_color = Color(0.2, 0.7, 1.0); - environment_custom_energy = 1.0; - - bake_quality = BAKE_QUALITY_MEDIUM; - interior = false; - directional = false; - - gen_probes = GENERATE_PROBES_DISABLED; - use_denoiser = true; - bounces = 1; - bias = 0.0005; - max_texture_size = 16384; +LightmapGI::LightmapGI() { } diff --git a/scene/3d/baked_lightmap.h b/scene/3d/lightmap_gi.h index bebb579252..e73350fd64 100644 --- a/scene/3d/baked_lightmap.h +++ b/scene/3d/lightmap_gi.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* baked_lightmap.h */ +/* lightmap_gi.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,19 +28,16 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef BAKED_LIGHTMAP_H -#define BAKED_LIGHTMAP_H +#ifndef LIGHTMAP_GI_H +#define LIGHTMAP_GI_H -#include "core/local_vector.h" +#include "core/templates/local_vector.h" #include "scene/3d/light_3d.h" #include "scene/3d/lightmapper.h" -#include "scene/3d/mesh_instance_3d.h" -#include "scene/3d/multimesh_instance_3d.h" #include "scene/3d/visual_instance_3d.h" -#include "scene/resources/sky.h" -class BakedLightmapData : public Resource { - GDCLASS(BakedLightmapData, Resource); +class LightmapGIData : public Resource { + GDCLASS(LightmapGIData, Resource); RES_BASE_EXTENSION("lmbake") Ref<TextureLayered> light_texture; @@ -53,9 +50,9 @@ class BakedLightmapData : public Resource { struct User { NodePath path; - int32_t sub_instance; + int32_t sub_instance = 0; Rect2 uv_scale; - int slice_index; + int slice_index = 0; }; Vector<User> users; @@ -95,12 +92,12 @@ public: void clear(); virtual RID get_rid() const override; - BakedLightmapData(); - ~BakedLightmapData(); + LightmapGIData(); + ~LightmapGIData(); }; -class BakedLightmap : public VisualInstance3D { - GDCLASS(BakedLightmap, VisualInstance3D); +class LightmapGI : public VisualInstance3D { + GDCLASS(LightmapGI, VisualInstance3D); public: enum BakeQuality { @@ -136,32 +133,32 @@ public: }; private: - BakeQuality bake_quality; - bool use_denoiser; - int bounces; - float bias; - int max_texture_size; - bool interior; - EnvironmentMode environment_mode; + BakeQuality bake_quality = BAKE_QUALITY_MEDIUM; + bool use_denoiser = true; + int bounces = 1; + float bias = 0.0005; + int max_texture_size = 16384; + bool interior = false; + EnvironmentMode environment_mode = ENVIRONMENT_MODE_DISABLED; Ref<Sky> environment_custom_sky; - Color environment_custom_color; - float environment_custom_energy; - bool directional; - GenerateProbes gen_probes; + Color environment_custom_color = Color(0.2, 0.7, 1.0); + float environment_custom_energy = 1.0; + bool directional = false; + GenerateProbes gen_probes = GENERATE_PROBES_DISABLED; - Ref<BakedLightmapData> light_data; + Ref<LightmapGIData> light_data; struct LightsFound { - Transform xform; - Light3D *light; + Transform3D xform; + Light3D *light = nullptr; }; struct MeshesFound { - Transform xform; + Transform3D xform; NodePath node_path; - int32_t subindex; + int32_t subindex = 0; Ref<Mesh> mesh; - int32_t lightmap_scale; + int32_t lightmap_scale = 0; Vector<Ref<Material>> overrides; }; @@ -172,19 +169,20 @@ private: struct BakeTimeData { String text; - int pass; - uint64_t last_step; + int pass = 0; + uint64_t last_step = 0; }; struct BSPSimplex { - int vertices[4]; - int planes[4]; + int vertices[4] = {}; + int planes[4] = {}; }; struct BSPNode { static const int32_t EMPTY_LEAF = INT32_MIN; Plane plane; - int32_t over = EMPTY_LEAF, under = EMPTY_LEAF; + int32_t over = EMPTY_LEAF; + int32_t under = EMPTY_LEAF; }; int _bsp_get_simplex_side(const Vector<Vector3> &p_points, const LocalVector<BSPSimplex> &p_simplices, const Plane &p_plane, uint32_t p_simplex) const; @@ -192,16 +190,16 @@ private: struct BakeStepUD { Lightmapper::BakeStepFunc func; - void *ud; - float from_percent; - float to_percent; + void *ud = nullptr; + float from_percent = 0.0; + float to_percent = 0.0; }; static bool _lightmap_bake_step_function(float p_completion, const String &p_text, void *ud, bool p_refresh); struct GenProbesOctree { Vector3i offset; - uint32_t size; + uint32_t size = 0; GenProbesOctree *children[8] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; ~GenProbesOctree() { for (int i = 0; i < 8; i++) { @@ -229,8 +227,8 @@ protected: void _notification(int p_what); public: - void set_light_data(const Ref<BakedLightmapData> &p_data); - Ref<BakedLightmapData> get_light_data() const; + void set_light_data(const Ref<LightmapGIData> &p_data); + Ref<LightmapGIData> get_light_data() const; void set_bake_quality(BakeQuality p_quality); BakeQuality get_bake_quality() const; @@ -272,12 +270,12 @@ public: Vector<Face3> get_faces(uint32_t p_usage_flags) const override; BakeError bake(Node *p_from_node, String p_image_data_path = "", Lightmapper::BakeStepFunc p_bake_step = nullptr, void *p_bake_userdata = nullptr); - BakedLightmap(); + LightmapGI(); }; -VARIANT_ENUM_CAST(BakedLightmap::BakeQuality); -VARIANT_ENUM_CAST(BakedLightmap::GenerateProbes); -VARIANT_ENUM_CAST(BakedLightmap::BakeError); -VARIANT_ENUM_CAST(BakedLightmap::EnvironmentMode); +VARIANT_ENUM_CAST(LightmapGI::BakeQuality); +VARIANT_ENUM_CAST(LightmapGI::GenerateProbes); +VARIANT_ENUM_CAST(LightmapGI::BakeError); +VARIANT_ENUM_CAST(LightmapGI::EnvironmentMode); #endif // BAKED_LIGHTMAP_H diff --git a/scene/3d/lightmap_probe.cpp b/scene/3d/lightmap_probe.cpp index ee21934b80..830b97ffab 100644 --- a/scene/3d/lightmap_probe.cpp +++ b/scene/3d/lightmap_probe.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/lightmap_probe.h b/scene/3d/lightmap_probe.h index c4bd33556f..df87ed49dd 100644 --- a/scene/3d/lightmap_probe.h +++ b/scene/3d/lightmap_probe.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/lightmapper.cpp b/scene/3d/lightmapper.cpp index 26faf5154c..9e5078ba95 100644 --- a/scene/3d/lightmapper.cpp +++ b/scene/3d/lightmapper.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -39,6 +39,15 @@ Ref<LightmapDenoiser> LightmapDenoiser::create() { return Ref<LightmapDenoiser>(); } +LightmapRaycaster *(*LightmapRaycaster::create_function)() = nullptr; + +Ref<LightmapRaycaster> LightmapRaycaster::create() { + if (create_function) { + return Ref<LightmapRaycaster>(create_function()); + } + return Ref<LightmapRaycaster>(); +} + Lightmapper::CreateFunc Lightmapper::create_custom = nullptr; Lightmapper::CreateFunc Lightmapper::create_gpu = nullptr; Lightmapper::CreateFunc Lightmapper::create_cpu = nullptr; diff --git a/scene/3d/lightmapper.h b/scene/3d/lightmapper.h index ccf9bde279..d028628901 100644 --- a/scene/3d/lightmapper.h +++ b/scene/3d/lightmapper.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,11 +31,22 @@ #ifndef LIGHTMAPPER_H #define LIGHTMAPPER_H -#include "scene/resources/mesh.h" -#include "servers/rendering/rendering_device.h" +#include "core/object/ref_counted.h" -class LightmapDenoiser : public Reference { - GDCLASS(LightmapDenoiser, Reference) +class Image; + +#if !defined(__aligned) + +#if defined(_WIN32) && defined(_MSC_VER) +#define __aligned(...) __declspec(align(__VA_ARGS__)) +#else +#define __aligned(...) __attribute__((aligned(__VA_ARGS__))) +#endif + +#endif + +class LightmapDenoiser : public RefCounted { + GDCLASS(LightmapDenoiser, RefCounted) protected: static LightmapDenoiser *(*create_function)(); @@ -44,8 +55,75 @@ public: static Ref<LightmapDenoiser> create(); }; -class Lightmapper : public Reference { - GDCLASS(Lightmapper, Reference) +class LightmapRaycaster : public RefCounted { + GDCLASS(LightmapRaycaster, RefCounted) +protected: + static LightmapRaycaster *(*create_function)(); + +public: + // compatible with embree3 rays + struct __aligned(16) Ray { + const static unsigned int INVALID_GEOMETRY_ID = ((unsigned int)-1); // from rtcore_common.h + + /*! Default construction does nothing. */ + _FORCE_INLINE_ Ray() : + geomID(INVALID_GEOMETRY_ID) {} + + /*! Constructs a ray from origin, direction, and ray segment. Near + * has to be smaller than far. */ + _FORCE_INLINE_ Ray(const Vector3 &org, + const Vector3 &dir, + float tnear = 0.0f, + float tfar = INFINITY) : + org(org), + tnear(tnear), + dir(dir), + time(0.0f), + tfar(tfar), + mask(-1), + u(0.0), + v(0.0), + primID(INVALID_GEOMETRY_ID), + geomID(INVALID_GEOMETRY_ID), + instID(INVALID_GEOMETRY_ID) {} + + /*! Tests if we hit something. */ + _FORCE_INLINE_ explicit operator bool() const { return geomID != INVALID_GEOMETRY_ID; } + + public: + Vector3 org; //!< Ray origin + tnear + float tnear; //!< Start of ray segment + Vector3 dir; //!< Ray direction + tfar + float time; //!< Time of this ray for motion blur. + float tfar; //!< End of ray segment + unsigned int mask; //!< used to mask out objects during traversal + unsigned int id; //!< ray ID + unsigned int flags; //!< ray flags + + Vector3 normal; //!< Not normalized geometry normal + float u; //!< Barycentric u coordinate of hit + float v; //!< Barycentric v coordinate of hit + unsigned int primID; //!< primitive ID + unsigned int geomID; //!< geometry ID + unsigned int instID; //!< instance ID + }; + + virtual bool intersect(Ray &p_ray) = 0; + + virtual void intersect(Vector<Ray> &r_rays) = 0; + + virtual void add_mesh(const Vector<Vector3> &p_vertices, const Vector<Vector3> &p_normals, const Vector<Vector2> &p_uv2s, unsigned int p_id) = 0; + virtual void set_mesh_alpha_texture(Ref<Image> p_alpha_texture, unsigned int p_id) = 0; + virtual void commit() = 0; + + virtual void set_mesh_filter(const Set<int> &p_mesh_ids) = 0; + virtual void clear_mesh_filter() = 0; + + static Ref<LightmapRaycaster> create(); +}; + +class Lightmapper : public RefCounted { + GDCLASS(Lightmapper, RefCounted) public: enum GenerateProbes { GENERATE_PROBES_DISABLED, diff --git a/scene/3d/listener_3d.cpp b/scene/3d/listener_3d.cpp index 0a5b9ad09f..1c52933ee5 100644 --- a/scene/3d/listener_3d.cpp +++ b/scene/3d/listener_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,7 +30,7 @@ #include "listener_3d.h" -#include "scene/resources/mesh.h" +#include "scene/main/viewport.h" void Listener3D::_update_audio_listener_state() { } @@ -73,14 +73,14 @@ void Listener3D::_get_property_list(List<PropertyInfo> *p_list) const { void Listener3D::_update_listener() { if (is_inside_tree() && is_current()) { - get_viewport()->_listener_transform_changed_notify(); + get_viewport()->_listener_transform_3d_changed_notify(); } } void Listener3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_WORLD: { - bool first_listener = get_viewport()->_listener_add(this); + bool first_listener = get_viewport()->_listener_3d_add(this); if (!get_tree()->is_node_being_edited(this) && (current || first_listener)) { make_current(); } @@ -99,13 +99,13 @@ void Listener3D::_notification(int p_what) { } } - get_viewport()->_listener_remove(this); + get_viewport()->_listener_3d_remove(this); } break; } } -Transform Listener3D::get_listener_transform() const { +Transform3D Listener3D::get_listener_transform() const { return get_global_transform().orthonormalized(); } @@ -116,7 +116,7 @@ void Listener3D::make_current() { return; } - get_viewport()->_listener_set(this); + get_viewport()->_listener_3d_set(this); } void Listener3D::clear_current() { @@ -125,15 +125,15 @@ void Listener3D::clear_current() { return; } - if (get_viewport()->get_listener() == this) { - get_viewport()->_listener_set(nullptr); - get_viewport()->_listener_make_next_current(this); + if (get_viewport()->get_listener_3d() == this) { + get_viewport()->_listener_3d_set(nullptr); + get_viewport()->_listener_3d_make_next_current(this); } } bool Listener3D::is_current() const { if (is_inside_tree() && !get_tree()->is_node_being_edited(this)) { - return get_viewport()->get_listener() == this; + return get_viewport()->get_listener_3d() == this; } else { return current; } @@ -159,8 +159,6 @@ void Listener3D::_bind_methods() { } Listener3D::Listener3D() { - current = false; - force_change = false; set_notify_transform(true); //active=false; } diff --git a/scene/3d/listener_3d.h b/scene/3d/listener_3d.h index 4b6923d6e8..25eacf5135 100644 --- a/scene/3d/listener_3d.h +++ b/scene/3d/listener_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,14 +32,13 @@ #define LISTENER_3D_H #include "scene/3d/node_3d.h" -#include "scene/main/window.h" class Listener3D : public Node3D { GDCLASS(Listener3D, Node3D); private: - bool force_change; - bool current; + bool force_change = false; + bool current = false; RID scenario_id; @@ -65,7 +64,7 @@ public: void clear_current(); bool is_current() const; - virtual Transform get_listener_transform() const; + virtual Transform3D get_listener_transform() const; void set_visible_layers(uint32_t p_layers); uint32_t get_visible_layers() const; diff --git a/scene/3d/mesh_instance_3d.cpp b/scene/3d/mesh_instance_3d.cpp index 13f40aed4f..de6925244a 100644 --- a/scene/3d/mesh_instance_3d.cpp +++ b/scene/3d/mesh_instance_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,8 +33,6 @@ #include "collision_shape_3d.h" #include "core/core_string_names.h" #include "physics_body_3d.h" -#include "scene/resources/material.h" -#include "skeleton_3d.h" bool MeshInstance3D::_set(const StringName &p_name, const Variant &p_value) { //this is not _too_ bad performance wise, really. it only arrives here if the property was not set anywhere else. @@ -51,13 +49,13 @@ bool MeshInstance3D::_set(const StringName &p_name, const Variant &p_value) { return true; } - if (p_name.operator String().begins_with("material/")) { + if (p_name.operator String().begins_with("surface_material_override/")) { int idx = p_name.operator String().get_slicec('/', 1).to_int(); - if (idx >= materials.size() || idx < 0) { + if (idx >= surface_override_materials.size() || idx < 0) { return false; } - set_surface_material(idx, p_value); + set_surface_override_material(idx, p_value); return true; } @@ -75,12 +73,12 @@ bool MeshInstance3D::_get(const StringName &p_name, Variant &r_ret) const { return true; } - if (p_name.operator String().begins_with("material/")) { + if (p_name.operator String().begins_with("surface_material_override/")) { int idx = p_name.operator String().get_slicec('/', 1).to_int(); - if (idx >= materials.size() || idx < 0) { + if (idx >= surface_override_materials.size() || idx < 0) { return false; } - r_ret = materials[idx]; + r_ret = surface_override_materials[idx]; return true; } return false; @@ -94,13 +92,13 @@ void MeshInstance3D::_get_property_list(List<PropertyInfo> *p_list) const { ls.sort(); - for (List<String>::Element *E = ls.front(); E; E = E->next()) { - p_list->push_back(PropertyInfo(Variant::FLOAT, E->get(), PROPERTY_HINT_RANGE, "0,1,0.00001")); + for (const String &E : ls) { + p_list->push_back(PropertyInfo(Variant::FLOAT, E, PROPERTY_HINT_RANGE, "-1,1,0.00001")); } if (mesh.is_valid()) { for (int i = 0; i < mesh->get_surface_count(); i++) { - p_list->push_back(PropertyInfo(Variant::OBJECT, "material/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,StandardMaterial3D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE)); + p_list->push_back(PropertyInfo(Variant::OBJECT, "surface_material_override/" + itos(i), PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE)); } } } @@ -112,7 +110,6 @@ void MeshInstance3D::set_mesh(const Ref<Mesh> &p_mesh) { if (mesh.is_valid()) { mesh->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &MeshInstance3D::_mesh_changed)); - materials.clear(); } mesh = p_mesh; @@ -127,16 +124,16 @@ void MeshInstance3D::set_mesh(const Ref<Mesh> &p_mesh) { } mesh->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &MeshInstance3D::_mesh_changed)); - materials.resize(mesh->get_surface_count()); + surface_override_materials.resize(mesh->get_surface_count()); set_base(mesh->get_rid()); } else { set_base(RID()); } - update_gizmo(); + update_gizmos(); - _change_notify(); + notify_property_list_changed(); } Ref<Mesh> MeshInstance3D::get_mesh() const { @@ -153,7 +150,7 @@ void MeshInstance3D::_resolve_skeleton_path() { if (skin_internal.is_null()) { //a skin was created for us skin_internal = new_skin_reference->get_skin(); - _change_notify(); + notify_property_list_changed(); } } } @@ -242,12 +239,12 @@ void MeshInstance3D::create_trimesh_collision() { } } -Node *MeshInstance3D::create_convex_collision_node() { +Node *MeshInstance3D::create_convex_collision_node(bool p_clean, bool p_simplify) { if (mesh.is_null()) { return nullptr; } - Ref<Shape3D> shape = mesh->create_convex_shape(); + Ref<Shape3D> shape = mesh->create_convex_shape(p_clean, p_simplify); if (shape.is_null()) { return nullptr; } @@ -259,8 +256,8 @@ Node *MeshInstance3D::create_convex_collision_node() { return static_body; } -void MeshInstance3D::create_convex_collision() { - StaticBody3D *static_body = Object::cast_to<StaticBody3D>(create_convex_collision_node()); +void MeshInstance3D::create_convex_collision(bool p_clean, bool p_simplify) { + StaticBody3D *static_body = Object::cast_to<StaticBody3D>(create_convex_collision_node(p_clean, p_simplify)); ERR_FAIL_COND(!static_body); static_body->set_name(String(get_name()) + "_col"); @@ -272,32 +269,67 @@ void MeshInstance3D::create_convex_collision() { } } +Node *MeshInstance3D::create_multiple_convex_collisions_node() { + if (mesh.is_null()) { + return nullptr; + } + + Vector<Ref<Shape3D>> shapes = mesh->convex_decompose(); + if (!shapes.size()) { + return nullptr; + } + + StaticBody3D *static_body = memnew(StaticBody3D); + for (int i = 0; i < shapes.size(); i++) { + CollisionShape3D *cshape = memnew(CollisionShape3D); + cshape->set_shape(shapes[i]); + static_body->add_child(cshape); + } + return static_body; +} + +void MeshInstance3D::create_multiple_convex_collisions() { + StaticBody3D *static_body = Object::cast_to<StaticBody3D>(create_multiple_convex_collisions_node()); + ERR_FAIL_COND(!static_body); + static_body->set_name(String(get_name()) + "_col"); + + add_child(static_body); + if (get_owner()) { + static_body->set_owner(get_owner()); + int count = static_body->get_child_count(); + for (int i = 0; i < count; i++) { + CollisionShape3D *cshape = Object::cast_to<CollisionShape3D>(static_body->get_child(i)); + cshape->set_owner(get_owner()); + } + } +} + void MeshInstance3D::_notification(int p_what) { if (p_what == NOTIFICATION_ENTER_TREE) { _resolve_skeleton_path(); } } -int MeshInstance3D::get_surface_material_count() const { - return materials.size(); +int MeshInstance3D::get_surface_override_material_count() const { + return surface_override_materials.size(); } -void MeshInstance3D::set_surface_material(int p_surface, const Ref<Material> &p_material) { - ERR_FAIL_INDEX(p_surface, materials.size()); +void MeshInstance3D::set_surface_override_material(int p_surface, const Ref<Material> &p_material) { + ERR_FAIL_INDEX(p_surface, surface_override_materials.size()); - materials.write[p_surface] = p_material; + surface_override_materials.write[p_surface] = p_material; - if (materials[p_surface].is_valid()) { - RS::get_singleton()->instance_set_surface_material(get_instance(), p_surface, materials[p_surface]->get_rid()); + if (surface_override_materials[p_surface].is_valid()) { + RS::get_singleton()->instance_set_surface_override_material(get_instance(), p_surface, surface_override_materials[p_surface]->get_rid()); } else { - RS::get_singleton()->instance_set_surface_material(get_instance(), p_surface, RID()); + RS::get_singleton()->instance_set_surface_override_material(get_instance(), p_surface, RID()); } } -Ref<Material> MeshInstance3D::get_surface_material(int p_surface) const { - ERR_FAIL_INDEX_V(p_surface, materials.size(), Ref<Material>()); +Ref<Material> MeshInstance3D::get_surface_override_material(int p_surface) const { + ERR_FAIL_INDEX_V(p_surface, surface_override_materials.size(), Ref<Material>()); - return materials[p_surface]; + return surface_override_materials[p_surface]; } Ref<Material> MeshInstance3D::get_active_material(int p_surface) const { @@ -306,7 +338,7 @@ Ref<Material> MeshInstance3D::get_active_material(int p_surface) const { return material_override; } - Ref<Material> surface_material = get_surface_material(p_surface); + Ref<Material> surface_material = get_surface_override_material(p_surface); if (surface_material.is_valid()) { return surface_material; } @@ -320,7 +352,9 @@ Ref<Material> MeshInstance3D::get_active_material(int p_surface) const { } void MeshInstance3D::_mesh_changed() { - materials.resize(mesh->get_surface_count()); + ERR_FAIL_COND(mesh.is_null()); + surface_override_materials.resize(mesh->get_surface_count()); + update_gizmos(); } void MeshInstance3D::create_debug_tangents() { @@ -369,14 +403,14 @@ void MeshInstance3D::create_debug_tangents() { if (lines.size()) { Ref<StandardMaterial3D> sm; - sm.instance(); + sm.instantiate(); sm->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); sm->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); sm->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); Ref<ArrayMesh> am; - am.instance(); + am.instantiate(); Array a; a.resize(Mesh::ARRAY_MAX); a[Mesh::ARRAY_VERTEX] = lines; @@ -391,7 +425,7 @@ void MeshInstance3D::create_debug_tangents() { add_child(mi); #ifdef TOOLS_ENABLED - if (this == get_tree()->get_edited_scene_root()) { + if (is_inside_tree() && this == get_tree()->get_edited_scene_root()) { mi->set_owner(this); } else { mi->set_owner(get_owner()); @@ -408,15 +442,17 @@ void MeshInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_skin", "skin"), &MeshInstance3D::set_skin); ClassDB::bind_method(D_METHOD("get_skin"), &MeshInstance3D::get_skin); - ClassDB::bind_method(D_METHOD("get_surface_material_count"), &MeshInstance3D::get_surface_material_count); - ClassDB::bind_method(D_METHOD("set_surface_material", "surface", "material"), &MeshInstance3D::set_surface_material); - ClassDB::bind_method(D_METHOD("get_surface_material", "surface"), &MeshInstance3D::get_surface_material); + ClassDB::bind_method(D_METHOD("get_surface_override_material_count"), &MeshInstance3D::get_surface_override_material_count); + ClassDB::bind_method(D_METHOD("set_surface_override_material", "surface", "material"), &MeshInstance3D::set_surface_override_material); + ClassDB::bind_method(D_METHOD("get_surface_override_material", "surface"), &MeshInstance3D::get_surface_override_material); ClassDB::bind_method(D_METHOD("get_active_material", "surface"), &MeshInstance3D::get_active_material); ClassDB::bind_method(D_METHOD("create_trimesh_collision"), &MeshInstance3D::create_trimesh_collision); ClassDB::set_method_flags("MeshInstance3D", "create_trimesh_collision", METHOD_FLAGS_DEFAULT); - ClassDB::bind_method(D_METHOD("create_convex_collision"), &MeshInstance3D::create_convex_collision); + ClassDB::bind_method(D_METHOD("create_convex_collision", "clean", "simplify"), &MeshInstance3D::create_convex_collision, DEFVAL(true), DEFVAL(false)); ClassDB::set_method_flags("MeshInstance3D", "create_convex_collision", METHOD_FLAGS_DEFAULT); + ClassDB::bind_method(D_METHOD("create_multiple_convex_collisions"), &MeshInstance3D::create_multiple_convex_collisions); + ClassDB::set_method_flags("MeshInstance3D", "create_multiple_convex_collisions", METHOD_FLAGS_DEFAULT); ClassDB::bind_method(D_METHOD("create_debug_tangents"), &MeshInstance3D::create_debug_tangents); ClassDB::set_method_flags("MeshInstance3D", "create_debug_tangents", METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); @@ -429,7 +465,6 @@ void MeshInstance3D::_bind_methods() { } MeshInstance3D::MeshInstance3D() { - skeleton_path = NodePath(".."); } MeshInstance3D::~MeshInstance3D() { diff --git a/scene/3d/mesh_instance_3d.h b/scene/3d/mesh_instance_3d.h index 4434e305e8..beb7f6cf95 100644 --- a/scene/3d/mesh_instance_3d.h +++ b/scene/3d/mesh_instance_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,10 +31,10 @@ #ifndef MESH_INSTANCE_H #define MESH_INSTANCE_H -#include "scene/3d/skeleton_3d.h" #include "scene/3d/visual_instance_3d.h" -#include "scene/resources/mesh.h" -#include "scene/resources/skin.h" + +class Skin; +class SkinReference; class MeshInstance3D : public GeometryInstance3D { GDCLASS(MeshInstance3D, GeometryInstance3D); @@ -44,19 +44,15 @@ protected: Ref<Skin> skin; Ref<Skin> skin_internal; Ref<SkinReference> skin_ref; - NodePath skeleton_path; + NodePath skeleton_path = NodePath(".."); struct BlendShapeTrack { - int idx; - float value; - BlendShapeTrack() { - idx = 0; - value = 0; - } + int idx = 0; + float value = 0.0; }; Map<StringName, BlendShapeTrack> blend_shape_tracks; - Vector<Ref<Material>> materials; + Vector<Ref<Material>> surface_override_materials; void _mesh_changed(); void _resolve_skeleton_path(); @@ -79,16 +75,19 @@ public: void set_skeleton_path(const NodePath &p_skeleton); NodePath get_skeleton_path(); - int get_surface_material_count() const; - void set_surface_material(int p_surface, const Ref<Material> &p_material); - Ref<Material> get_surface_material(int p_surface) const; + int get_surface_override_material_count() const; + void set_surface_override_material(int p_surface, const Ref<Material> &p_material); + Ref<Material> get_surface_override_material(int p_surface) const; Ref<Material> get_active_material(int p_surface) const; Node *create_trimesh_collision_node(); void create_trimesh_collision(); - Node *create_convex_collision_node(); - void create_convex_collision(); + Node *create_convex_collision_node(bool p_clean = true, bool p_simplify = false); + void create_convex_collision(bool p_clean = true, bool p_simplify = false); + + Node *create_multiple_convex_collisions_node(); + void create_multiple_convex_collisions(); void create_debug_tangents(); diff --git a/scene/3d/multimesh_instance_3d.cpp b/scene/3d/multimesh_instance_3d.cpp index 88dff111f7..2adef115cf 100644 --- a/scene/3d/multimesh_instance_3d.cpp +++ b/scene/3d/multimesh_instance_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/multimesh_instance_3d.h b/scene/3d/multimesh_instance_3d.h index 6e075b7f7f..63735fd3a6 100644 --- a/scene/3d/multimesh_instance_3d.h +++ b/scene/3d/multimesh_instance_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/navigation_3d.cpp b/scene/3d/navigation_3d.cpp deleted file mode 100644 index 851966db2b..0000000000 --- a/scene/3d/navigation_3d.cpp +++ /dev/null @@ -1,119 +0,0 @@ -/*************************************************************************/ -/* navigation_3d.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "navigation_3d.h" - -#include "servers/navigation_server_3d.h" - -Vector<Vector3> Navigation3D::get_simple_path(const Vector3 &p_start, const Vector3 &p_end, bool p_optimize) const { - return NavigationServer3D::get_singleton()->map_get_path(map, p_start, p_end, p_optimize); -} - -Vector3 Navigation3D::get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision) const { - return NavigationServer3D::get_singleton()->map_get_closest_point_to_segment(map, p_from, p_to, p_use_collision); -} - -Vector3 Navigation3D::get_closest_point(const Vector3 &p_point) const { - return NavigationServer3D::get_singleton()->map_get_closest_point(map, p_point); -} - -Vector3 Navigation3D::get_closest_point_normal(const Vector3 &p_point) const { - return NavigationServer3D::get_singleton()->map_get_closest_point_normal(map, p_point); -} - -RID Navigation3D::get_closest_point_owner(const Vector3 &p_point) const { - return NavigationServer3D::get_singleton()->map_get_closest_point_owner(map, p_point); -} - -void Navigation3D::set_up_vector(const Vector3 &p_up) { - up = p_up; - NavigationServer3D::get_singleton()->map_set_up(map, up); -} - -Vector3 Navigation3D::get_up_vector() const { - return up; -} - -void Navigation3D::set_cell_size(float p_cell_size) { - cell_size = p_cell_size; - NavigationServer3D::get_singleton()->map_set_cell_size(map, cell_size); -} - -void Navigation3D::set_edge_connection_margin(float p_edge_connection_margin) { - edge_connection_margin = p_edge_connection_margin; - NavigationServer3D::get_singleton()->map_set_edge_connection_margin(map, edge_connection_margin); -} - -void Navigation3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_rid"), &Navigation3D::get_rid); - - ClassDB::bind_method(D_METHOD("get_simple_path", "start", "end", "optimize"), &Navigation3D::get_simple_path, DEFVAL(true)); - ClassDB::bind_method(D_METHOD("get_closest_point_to_segment", "start", "end", "use_collision"), &Navigation3D::get_closest_point_to_segment, DEFVAL(false)); - ClassDB::bind_method(D_METHOD("get_closest_point", "to_point"), &Navigation3D::get_closest_point); - ClassDB::bind_method(D_METHOD("get_closest_point_normal", "to_point"), &Navigation3D::get_closest_point_normal); - ClassDB::bind_method(D_METHOD("get_closest_point_owner", "to_point"), &Navigation3D::get_closest_point_owner); - - ClassDB::bind_method(D_METHOD("set_up_vector", "up"), &Navigation3D::set_up_vector); - ClassDB::bind_method(D_METHOD("get_up_vector"), &Navigation3D::get_up_vector); - - ClassDB::bind_method(D_METHOD("set_cell_size", "cell_size"), &Navigation3D::set_cell_size); - ClassDB::bind_method(D_METHOD("get_cell_size"), &Navigation3D::get_cell_size); - - ClassDB::bind_method(D_METHOD("set_edge_connection_margin", "margin"), &Navigation3D::set_edge_connection_margin); - ClassDB::bind_method(D_METHOD("get_edge_connection_margin"), &Navigation3D::get_edge_connection_margin); - - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "up_vector"), "set_up_vector", "get_up_vector"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "cell_size"), "set_cell_size", "get_cell_size"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "edge_connection_margin"), "set_edge_connection_margin", "get_edge_connection_margin"); -} - -void Navigation3D::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_READY: { - NavigationServer3D::get_singleton()->map_set_active(map, true); - } break; - case NOTIFICATION_EXIT_TREE: { - NavigationServer3D::get_singleton()->map_set_active(map, false); - } break; - } -} - -Navigation3D::Navigation3D() { - map = NavigationServer3D::get_singleton()->map_create(); - - set_cell_size(0.3); - set_edge_connection_margin(5.0); // Five meters, depends a lot on the agent's radius - - up = Vector3(0, 1, 0); -} - -Navigation3D::~Navigation3D() { - NavigationServer3D::get_singleton()->free(map); -} diff --git a/scene/3d/navigation_3d.h b/scene/3d/navigation_3d.h deleted file mode 100644 index 890caed171..0000000000 --- a/scene/3d/navigation_3d.h +++ /dev/null @@ -1,78 +0,0 @@ -/*************************************************************************/ -/* navigation_3d.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#ifndef NAVIGATION_3D_H -#define NAVIGATION_3D_H - -#include "scene/3d/navigation_region_3d.h" -#include "scene/3d/node_3d.h" - -class Navigation3D : public Node3D { - GDCLASS(Navigation3D, Node3D); - - RID map; - - Vector3 up; - real_t cell_size; - real_t edge_connection_margin; - -protected: - static void _bind_methods(); - void _notification(int p_what); - -public: - RID get_rid() const { - return map; - } - - void set_up_vector(const Vector3 &p_up); - Vector3 get_up_vector() const; - - void set_cell_size(float p_cell_size); - float get_cell_size() const { - return cell_size; - } - - void set_edge_connection_margin(float p_edge_connection_margin); - float get_edge_connection_margin() const { - return edge_connection_margin; - } - - Vector<Vector3> get_simple_path(const Vector3 &p_start, const Vector3 &p_end, bool p_optimize = true) const; - Vector3 get_closest_point_to_segment(const Vector3 &p_from, const Vector3 &p_to, bool p_use_collision = false) const; - Vector3 get_closest_point(const Vector3 &p_point) const; - Vector3 get_closest_point_normal(const Vector3 &p_point) const; - RID get_closest_point_owner(const Vector3 &p_point) const; - - Navigation3D(); - ~Navigation3D(); -}; - -#endif // NAVIGATION_H diff --git a/scene/3d/navigation_agent_3d.cpp b/scene/3d/navigation_agent_3d.cpp index 422b566867..c2d5c757db 100644 --- a/scene/3d/navigation_agent_3d.cpp +++ b/scene/3d/navigation_agent_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,11 +30,11 @@ #include "navigation_agent_3d.h" -#include "core/engine.h" -#include "scene/3d/navigation_3d.h" #include "servers/navigation_server_3d.h" void NavigationAgent3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_rid"), &NavigationAgent3D::get_rid); + ClassDB::bind_method(D_METHOD("set_target_desired_distance", "desired_distance"), &NavigationAgent3D::set_target_desired_distance); ClassDB::bind_method(D_METHOD("get_target_desired_distance"), &NavigationAgent3D::get_target_desired_distance); @@ -47,9 +47,6 @@ void NavigationAgent3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_ignore_y", "ignore"), &NavigationAgent3D::set_ignore_y); ClassDB::bind_method(D_METHOD("get_ignore_y"), &NavigationAgent3D::get_ignore_y); - ClassDB::bind_method(D_METHOD("set_navigation", "navigation"), &NavigationAgent3D::set_navigation_node); - ClassDB::bind_method(D_METHOD("get_navigation"), &NavigationAgent3D::get_navigation_node); - ClassDB::bind_method(D_METHOD("set_neighbor_dist", "neighbor_dist"), &NavigationAgent3D::set_neighbor_dist); ClassDB::bind_method(D_METHOD("get_neighbor_dist"), &NavigationAgent3D::get_neighbor_dist); @@ -99,41 +96,21 @@ void NavigationAgent3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: { agent_parent = Object::cast_to<Node3D>(get_parent()); - - NavigationServer3D::get_singleton()->agent_set_callback(agent, this, "_avoidance_done"); - - // Search the navigation node and set it - { - Navigation3D *nav = nullptr; - Node *p = get_parent(); - while (p != nullptr) { - nav = Object::cast_to<Navigation3D>(p); - if (nav != nullptr) { - p = nullptr; - } else { - p = p->get_parent(); - } - } - - set_navigation(nav); + if (agent_parent != nullptr) { + // place agent on navigation map first or else the RVO agent callback creation fails silently later + NavigationServer3D::get_singleton()->agent_set_map(get_rid(), agent_parent->get_world_3d()->get_navigation_map()); + NavigationServer3D::get_singleton()->agent_set_callback(agent, this, "_avoidance_done"); } - set_physics_process_internal(true); } break; case NOTIFICATION_EXIT_TREE: { agent_parent = nullptr; - set_navigation(nullptr); set_physics_process_internal(false); } break; case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { if (agent_parent) { NavigationServer3D::get_singleton()->agent_set_position(agent, agent_parent->get_global_transform().origin); - if (!target_reached) { - if (distance_to_target() < target_desired_distance) { - emit_signal("target_reached"); - target_reached = true; - } - } + _check_distance_to_target(); } } break; } @@ -154,25 +131,6 @@ NavigationAgent3D::~NavigationAgent3D() { agent = RID(); // Pointless } -void NavigationAgent3D::set_navigation(Navigation3D *p_nav) { - if (navigation == p_nav) { - return; // Pointless - } - - navigation = p_nav; - NavigationServer3D::get_singleton()->agent_set_map(agent, navigation == nullptr ? RID() : navigation->get_rid()); -} - -void NavigationAgent3D::set_navigation_node(Node *p_nav) { - Navigation3D *nav = Object::cast_to<Navigation3D>(p_nav); - ERR_FAIL_COND(nav == nullptr); - set_navigation(nav); -} - -Node *NavigationAgent3D::get_navigation_node() const { - return Object::cast_to<Node>(navigation); -} - void NavigationAgent3D::set_target_desired_distance(real_t p_dd) { target_desired_distance = p_dd; } @@ -283,27 +241,24 @@ void NavigationAgent3D::_avoidance_done(Vector3 p_new_velocity) { } velocity_submitted = false; - emit_signal("velocity_computed", p_new_velocity); + emit_signal(SNAME("velocity_computed"), p_new_velocity); } -String NavigationAgent3D::get_configuration_warning() const { - String warning = Node::get_configuration_warning(); +TypedArray<String> NavigationAgent3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (!Object::cast_to<Node3D>(get_parent())) { - if (!warning.empty()) { - warning += "\n\n"; - } - warning += TTR("The NavigationAgent3D can be used only under a spatial node."); + warnings.push_back(TTR("The NavigationAgent3D can be used only under a spatial node.")); } - return warning; + return warnings; } void NavigationAgent3D::update_navigation() { if (agent_parent == nullptr) { return; } - if (navigation == nullptr) { + if (!agent_parent->is_inside_tree()) { return; } if (update_frame_id == Engine::get_singleton()->get_physics_frames()) { @@ -337,10 +292,10 @@ void NavigationAgent3D::update_navigation() { } if (reload_path) { - navigation_path = NavigationServer3D::get_singleton()->map_get_path(navigation->get_rid(), o, target_location, true); + navigation_path = NavigationServer3D::get_singleton()->map_get_path(agent_parent->get_world_3d()->get_navigation_map(), o, target_location, true); navigation_finished = false; nav_path_index = 0; - emit_signal("path_changed"); + emit_signal(SNAME("path_changed")); } if (navigation_path.size() == 0) { @@ -353,11 +308,21 @@ void NavigationAgent3D::update_navigation() { while (o.distance_to(navigation_path[nav_path_index] - Vector3(0, navigation_height_offset, 0)) < target_desired_distance) { nav_path_index += 1; if (nav_path_index == navigation_path.size()) { + _check_distance_to_target(); nav_path_index -= 1; navigation_finished = true; - emit_signal("navigation_finished"); + emit_signal(SNAME("navigation_finished")); break; } } } } + +void NavigationAgent3D::_check_distance_to_target() { + if (!target_reached) { + if (distance_to_target() < target_desired_distance) { + emit_signal(SNAME("target_reached")); + target_reached = true; + } + } +} diff --git a/scene/3d/navigation_agent_3d.h b/scene/3d/navigation_agent_3d.h index e80367ea50..bebfdc5f7e 100644 --- a/scene/3d/navigation_agent_3d.h +++ b/scene/3d/navigation_agent_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,17 +31,14 @@ #ifndef NAVIGATION_AGENT_H #define NAVIGATION_AGENT_H -#include "core/vector.h" #include "scene/main/node.h" class Node3D; -class Navigation3D; class NavigationAgent3D : public Node { GDCLASS(NavigationAgent3D, Node); Node3D *agent_parent = nullptr; - Navigation3D *navigation = nullptr; RID agent; @@ -76,14 +73,6 @@ public: NavigationAgent3D(); virtual ~NavigationAgent3D(); - void set_navigation(Navigation3D *p_nav); - const Navigation3D *get_navigation() const { - return navigation; - } - - void set_navigation_node(Node *p_nav); - Node *get_navigation_node() const; - RID get_rid() const { return agent; } @@ -153,10 +142,11 @@ public: void set_velocity(Vector3 p_velocity); void _avoidance_done(Vector3 p_new_velocity); - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; private: void update_navigation(); + void _check_distance_to_target(); }; #endif diff --git a/scene/3d/navigation_obstacle_3d.cpp b/scene/3d/navigation_obstacle_3d.cpp index adbff06ed6..20ffc3b00e 100644 --- a/scene/3d/navigation_obstacle_3d.cpp +++ b/scene/3d/navigation_obstacle_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,55 +31,38 @@ #include "navigation_obstacle_3d.h" #include "scene/3d/collision_shape_3d.h" -#include "scene/3d/navigation_3d.h" #include "scene/3d/physics_body_3d.h" #include "servers/navigation_server_3d.h" void NavigationObstacle3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_navigation", "navigation"), &NavigationObstacle3D::set_navigation_node); - ClassDB::bind_method(D_METHOD("get_navigation"), &NavigationObstacle3D::get_navigation_node); } void NavigationObstacle3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_READY: { - update_agent_shape(); - - // Search the navigation node and set it - { - Navigation3D *nav = nullptr; - Node *p = get_parent(); - while (p != nullptr) { - nav = Object::cast_to<Navigation3D>(p); - if (nav != nullptr) { - p = nullptr; - } else { - p = p->get_parent(); - } - } - - set_navigation(nav); - } - set_physics_process_internal(true); } break; case NOTIFICATION_EXIT_TREE: { - set_navigation(nullptr); set_physics_process_internal(false); } break; + case NOTIFICATION_PARENTED: { + parent_node3d = Object::cast_to<Node3D>(get_parent()); + update_agent_shape(); + } break; + case NOTIFICATION_UNPARENTED: { + parent_node3d = nullptr; + } break; case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { - Node3D *spatial = Object::cast_to<Node3D>(get_parent()); - if (spatial) { - NavigationServer3D::get_singleton()->agent_set_position(agent, spatial->get_global_transform().origin); - } - - PhysicsBody3D *rigid = Object::cast_to<PhysicsBody3D>(get_parent()); - if (rigid) { - Vector3 v = rigid->get_linear_velocity(); - NavigationServer3D::get_singleton()->agent_set_velocity(agent, v); - NavigationServer3D::get_singleton()->agent_set_target_velocity(agent, v); + if (parent_node3d) { + NavigationServer3D::get_singleton()->agent_set_position(agent, parent_node3d->get_global_transform().origin); + + PhysicsBody3D *rigid = Object::cast_to<PhysicsBody3D>(get_parent()); + if (rigid) { + Vector3 v = rigid->get_linear_velocity(); + NavigationServer3D::get_singleton()->agent_set_velocity(agent, v); + NavigationServer3D::get_singleton()->agent_set_target_velocity(agent, v); + } } - } break; } } @@ -93,73 +76,49 @@ NavigationObstacle3D::~NavigationObstacle3D() { agent = RID(); // Pointless } -void NavigationObstacle3D::set_navigation(Navigation3D *p_nav) { - if (navigation == p_nav) { - return; // Pointless - } - - navigation = p_nav; - NavigationServer3D::get_singleton()->agent_set_map(agent, navigation == nullptr ? RID() : navigation->get_rid()); -} - -void NavigationObstacle3D::set_navigation_node(Node *p_nav) { - Navigation3D *nav = Object::cast_to<Navigation3D>(p_nav); - ERR_FAIL_COND(nav == nullptr); - set_navigation(nav); -} - -Node *NavigationObstacle3D::get_navigation_node() const { - return Object::cast_to<Node>(navigation); -} - -String NavigationObstacle3D::get_configuration_warning() const { - String warning = Node::get_configuration_warning(); +TypedArray<String> NavigationObstacle3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (!Object::cast_to<Node3D>(get_parent())) { - if (!warning.empty()) { - warning += "\n\n"; - } - warning += TTR("The NavigationObstacle3D only serves to provide collision avoidance to a spatial object."); + warnings.push_back(TTR("The NavigationObstacle3D only serves to provide collision avoidance to a spatial object.")); } - return warning; + return warnings; } void NavigationObstacle3D::update_agent_shape() { - Node *node = get_parent(); - - // Estimate the radius of this physics body - real_t radius = 0.0; - for (int i(0); i < node->get_child_count(); i++) { - // For each collision shape - CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(node->get_child(i)); - if (cs) { - // Take the distance between the Body center to the shape center - real_t r = cs->get_transform().origin.length(); - if (cs->get_shape().is_valid()) { - // and add the enclosing shape radius - r += cs->get_shape()->get_enclosing_radius(); + if (parent_node3d) { + // Estimate the radius of this physics body + real_t radius = 0.0; + for (int i(0); i < parent_node3d->get_child_count(); i++) { + // For each collision shape + CollisionShape3D *cs = Object::cast_to<CollisionShape3D>(parent_node3d->get_child(i)); + if (cs) { + // Take the distance between the Body center to the shape center + real_t r = cs->get_transform().origin.length(); + if (cs->get_shape().is_valid()) { + // and add the enclosing shape radius + r += cs->get_shape()->get_enclosing_radius(); + } + Vector3 s = cs->get_global_transform().basis.get_scale(); + r *= MAX(s.x, MAX(s.y, s.z)); + // Takes the biggest radius + radius = MAX(radius, r); } - Vector3 s = cs->get_global_transform().basis.get_scale(); - r *= MAX(s.x, MAX(s.y, s.z)); - // Takes the biggest radius - radius = MAX(radius, r); } - } - Node3D *spa = Object::cast_to<Node3D>(node); - if (spa) { - Vector3 s = spa->get_global_transform().basis.get_scale(); + + Vector3 s = parent_node3d->get_global_transform().basis.get_scale(); radius *= MAX(s.x, MAX(s.y, s.z)); - } - if (radius == 0.0) { - radius = 1.0; // Never a 0 radius - } + if (radius == 0.0) { + radius = 1.0; // Never a 0 radius + } - // Initialize the Agent as an object - NavigationServer3D::get_singleton()->agent_set_neighbor_dist(agent, 0.0); - NavigationServer3D::get_singleton()->agent_set_max_neighbors(agent, 0); - NavigationServer3D::get_singleton()->agent_set_time_horizon(agent, 0.0); - NavigationServer3D::get_singleton()->agent_set_radius(agent, radius); - NavigationServer3D::get_singleton()->agent_set_max_speed(agent, 0.0); + // Initialize the Agent as an object + NavigationServer3D::get_singleton()->agent_set_neighbor_dist(agent, 0.0); + NavigationServer3D::get_singleton()->agent_set_max_neighbors(agent, 0); + NavigationServer3D::get_singleton()->agent_set_time_horizon(agent, 0.0); + NavigationServer3D::get_singleton()->agent_set_radius(agent, radius); + NavigationServer3D::get_singleton()->agent_set_max_speed(agent, 0.0); + } } diff --git a/scene/3d/navigation_obstacle_3d.h b/scene/3d/navigation_obstacle_3d.h index f2dc9182ca..ab0b158303 100644 --- a/scene/3d/navigation_obstacle_3d.h +++ b/scene/3d/navigation_obstacle_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,15 +31,12 @@ #ifndef NAVIGATION_OBSTACLE_H #define NAVIGATION_OBSTACLE_H -#include "scene/main/node.h" - -class Navigation3D; +#include "scene/3d/node_3d.h" class NavigationObstacle3D : public Node { GDCLASS(NavigationObstacle3D, Node); - Navigation3D *navigation = nullptr; - + Node3D *parent_node3d = nullptr; RID agent; protected: @@ -50,19 +47,11 @@ public: NavigationObstacle3D(); virtual ~NavigationObstacle3D(); - void set_navigation(Navigation3D *p_nav); - const Navigation3D *get_navigation() const { - return navigation; - } - - void set_navigation_node(Node *p_nav); - Node *get_navigation_node() const; - RID get_rid() const { return agent; } - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; private: void update_agent_shape(); diff --git a/scene/3d/navigation_region_3d.cpp b/scene/3d/navigation_region_3d.cpp index 3d3467583d..8a51a259f7 100644 --- a/scene/3d/navigation_region_3d.cpp +++ b/scene/3d/navigation_region_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,9 +30,7 @@ #include "navigation_region_3d.h" -#include "core/os/thread.h" #include "mesh_instance_3d.h" -#include "navigation_3d.h" #include "servers/navigation_server_3d.h" void NavigationRegion3D::set_enabled(bool p_enabled) { @@ -48,9 +46,7 @@ void NavigationRegion3D::set_enabled(bool p_enabled) { if (!enabled) { NavigationServer3D::get_singleton()->region_set_map(region, RID()); } else { - if (navigation) { - NavigationServer3D::get_singleton()->region_set_map(region, navigation->get_rid()); - } + NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map()); } if (debug_view) { @@ -62,29 +58,28 @@ void NavigationRegion3D::set_enabled(bool p_enabled) { } } - update_gizmo(); + update_gizmos(); } bool NavigationRegion3D::is_enabled() const { return enabled; } +void NavigationRegion3D::set_layers(uint32_t p_layers) { + NavigationServer3D::get_singleton()->region_set_layers(region, p_layers); +} + +uint32_t NavigationRegion3D::get_layers() const { + return NavigationServer3D::get_singleton()->region_get_layers(region); +} + ///////////////////////////// void NavigationRegion3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - Node3D *c = this; - while (c) { - navigation = Object::cast_to<Navigation3D>(c); - if (navigation) { - if (enabled) { - NavigationServer3D::get_singleton()->region_set_map(region, navigation->get_rid()); - } - break; - } - - c = c->get_parent_spatial(); + if (enabled) { + NavigationServer3D::get_singleton()->region_set_map(region, get_world_3d()->get_navigation_map()); } if (navmesh.is_valid() && get_tree()->is_debugging_navigation_hint()) { @@ -105,15 +100,12 @@ void NavigationRegion3D::_notification(int p_what) { } break; case NOTIFICATION_EXIT_TREE: { - if (navigation) { - NavigationServer3D::get_singleton()->region_set_map(region, RID()); - } + NavigationServer3D::get_singleton()->region_set_map(region, RID()); if (debug_view) { debug_view->queue_delete(); debug_view = nullptr; } - navigation = nullptr; } break; } } @@ -124,13 +116,13 @@ void NavigationRegion3D::set_navigation_mesh(const Ref<NavigationMesh> &p_navmes } if (navmesh.is_valid()) { - navmesh->remove_change_receptor(this); + navmesh->disconnect("changed", callable_mp(this, &NavigationRegion3D::_navigation_changed)); } navmesh = p_navmesh; if (navmesh.is_valid()) { - navmesh->add_change_receptor(this); + navmesh->connect("changed", callable_mp(this, &NavigationRegion3D::_navigation_changed)); } NavigationServer3D::get_singleton()->region_set_navmesh(region, p_navmesh); @@ -139,10 +131,10 @@ void NavigationRegion3D::set_navigation_mesh(const Ref<NavigationMesh> &p_navmes Object::cast_to<MeshInstance3D>(debug_view)->set_mesh(navmesh->get_debug_mesh()); } - emit_signal("navigation_mesh_changed"); + emit_signal(SNAME("navigation_mesh_changed")); - update_gizmo(); - update_configuration_warning(); + update_gizmos(); + update_configuration_warnings(); } Ref<NavigationMesh> NavigationRegion3D::get_navigation_mesh() const { @@ -150,7 +142,7 @@ Ref<NavigationMesh> NavigationRegion3D::get_navigation_mesh() const { } struct BakeThreadsArgs { - NavigationRegion3D *nav_region; + NavigationRegion3D *nav_region = nullptr; }; void _bake_navigation_mesh(void *p_user_data) { @@ -160,57 +152,40 @@ void _bake_navigation_mesh(void *p_user_data) { Ref<NavigationMesh> nav_mesh = args->nav_region->get_navigation_mesh()->duplicate(); NavigationServer3D::get_singleton()->region_bake_navmesh(nav_mesh, args->nav_region); - args->nav_region->call_deferred("_bake_finished", nav_mesh); + args->nav_region->call_deferred(SNAME("_bake_finished"), nav_mesh); memdelete(args); } else { ERR_PRINT("Can't bake the navigation mesh if the `NavigationMesh` resource doesn't exist"); - args->nav_region->call_deferred("_bake_finished", Ref<NavigationMesh>()); + args->nav_region->call_deferred(SNAME("_bake_finished"), Ref<NavigationMesh>()); memdelete(args); } } void NavigationRegion3D::bake_navigation_mesh() { - ERR_FAIL_COND(bake_thread != nullptr); + ERR_FAIL_COND(bake_thread.is_started()); BakeThreadsArgs *args = memnew(BakeThreadsArgs); args->nav_region = this; - bake_thread = Thread::create(_bake_navigation_mesh, args); - ERR_FAIL_COND(bake_thread == nullptr); + bake_thread.start(_bake_navigation_mesh, args); } void NavigationRegion3D::_bake_finished(Ref<NavigationMesh> p_nav_mesh) { set_navigation_mesh(p_nav_mesh); - bake_thread = nullptr; + bake_thread.wait_to_finish(); + emit_signal(SNAME("bake_finished")); } -String NavigationRegion3D::get_configuration_warning() const { - if (!is_visible_in_tree() || !is_inside_tree()) { - return String(); - } - - String warning = Node3D::get_configuration_warning(); - - if (!navmesh.is_valid()) { - if (!warning.empty()) { - warning += "\n\n"; - } - warning += TTR("A NavigationMesh resource must be set or created for this node to work."); - } +TypedArray<String> NavigationRegion3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); - const Node3D *c = this; - while (c) { - if (Object::cast_to<Navigation3D>(c)) { - return warning; + if (is_visible_in_tree() && is_inside_tree()) { + if (!navmesh.is_valid()) { + warnings.push_back(TTR("A NavigationMesh resource must be set or created for this node to work.")); } - - c = Object::cast_to<Node3D>(c->get_parent()); } - if (!warning.empty()) { - warning += "\n\n"; - } - return warning + TTR("NavigationRegion3D must be a child or grandchild to a Navigation3D node. It only provides navigation data."); + return warnings; } void NavigationRegion3D::_bind_methods() { @@ -220,19 +195,23 @@ void NavigationRegion3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &NavigationRegion3D::set_enabled); ClassDB::bind_method(D_METHOD("is_enabled"), &NavigationRegion3D::is_enabled); + ClassDB::bind_method(D_METHOD("set_layers", "layers"), &NavigationRegion3D::set_layers); + ClassDB::bind_method(D_METHOD("get_layers"), &NavigationRegion3D::get_layers); + ClassDB::bind_method(D_METHOD("bake_navigation_mesh"), &NavigationRegion3D::bake_navigation_mesh); ClassDB::bind_method(D_METHOD("_bake_finished", "nav_mesh"), &NavigationRegion3D::_bake_finished); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "navmesh", PROPERTY_HINT_RESOURCE_TYPE, "NavigationMesh"), "set_navigation_mesh", "get_navigation_mesh"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "layers", PROPERTY_HINT_LAYERS_3D_NAVIGATION), "set_layers", "get_layers"); ADD_SIGNAL(MethodInfo("navigation_mesh_changed")); ADD_SIGNAL(MethodInfo("bake_finished")); } -void NavigationRegion3D::_changed_callback(Object *p_changed, const char *p_prop) { - update_gizmo(); - update_configuration_warning(); +void NavigationRegion3D::_navigation_changed() { + update_gizmos(); + update_configuration_warnings(); } NavigationRegion3D::NavigationRegion3D() { @@ -242,7 +221,7 @@ NavigationRegion3D::NavigationRegion3D() { NavigationRegion3D::~NavigationRegion3D() { if (navmesh.is_valid()) { - navmesh->remove_change_receptor(this); + navmesh->disconnect("changed", callable_mp(this, &NavigationRegion3D::_navigation_changed)); } NavigationServer3D::get_singleton()->free(region); } diff --git a/scene/3d/navigation_region_3d.h b/scene/3d/navigation_region_3d.h index 4b1d59206b..ec7761ef93 100644 --- a/scene/3d/navigation_region_3d.h +++ b/scene/3d/navigation_region_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,11 +32,8 @@ #define NAVIGATION_REGION_H #include "scene/3d/node_3d.h" -#include "scene/resources/mesh.h" #include "scene/resources/navigation_mesh.h" -class Navigation3D; - class NavigationRegion3D : public Node3D { GDCLASS(NavigationRegion3D, Node3D); @@ -44,19 +41,22 @@ class NavigationRegion3D : public Node3D { RID region; Ref<NavigationMesh> navmesh; - Navigation3D *navigation = nullptr; Node *debug_view = nullptr; - Thread *bake_thread = nullptr; + Thread bake_thread; + + void _navigation_changed(); protected: void _notification(int p_what); static void _bind_methods(); - void _changed_callback(Object *p_changed, const char *p_prop) override; public: void set_enabled(bool p_enabled); bool is_enabled() const; + void set_layers(uint32_t p_layers); + uint32_t get_layers() const; + void set_navigation_mesh(const Ref<NavigationMesh> &p_navmesh); Ref<NavigationMesh> get_navigation_mesh() const; @@ -65,7 +65,7 @@ public: void bake_navigation_mesh(); void _bake_finished(Ref<NavigationMesh> p_nav_mesh); - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; NavigationRegion3D(); ~NavigationRegion3D(); diff --git a/scene/3d/node_3d.cpp b/scene/3d/node_3d.cpp index 35c4b9d08d..12470939f5 100644 --- a/scene/3d/node_3d.cpp +++ b/scene/3d/node_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,10 +30,9 @@ #include "node_3d.h" -#include "core/engine.h" -#include "core/message_queue.h" -#include "scene/main/scene_tree.h" -#include "scene/main/window.h" +#include "core/object/message_queue.h" +#include "scene/3d/visual_instance_3d.h" +#include "scene/main/viewport.h" #include "scene/scene_string_names.h" /* @@ -48,7 +47,7 @@ a) If above is invalid, don't keep invalidating upwards 2) If a node sets a GLOBAL, it is converted to LOCAL (and forces validation of everything pending below) - drawback: setting/reading globals is useful and used very very often, and using affine inverses is slow + drawback: setting/reading globals is useful and used very often, and using affine inverses is slow --- @@ -75,7 +74,7 @@ Node3DGizmo::Node3DGizmo() { void Node3D::_notify_dirty() { #ifdef TOOLS_ENABLED - if ((data.gizmo.is_valid() || data.notify_transform) && !data.ignore_notification && !xform_change.in_list()) { + if ((!data.gizmos.is_empty() || data.notify_transform) && !data.ignore_notification && !xform_change.in_list()) { #else if (data.notify_transform && !data.ignore_notification && !xform_change.in_list()) { @@ -102,14 +101,14 @@ void Node3D::_propagate_transform_changed(Node3D *p_origin) { data.children_lock++; - for (List<Node3D *>::Element *E = data.children.front(); E; E = E->next()) { - if (E->get()->data.top_level_active) { - continue; //don't propagate to a toplevel + for (Node3D *&E : data.children) { + if (E->data.top_level_active) { + continue; //don't propagate to a top_level } - E->get()->_propagate_transform_changed(p_origin); + E->_propagate_transform_changed(p_origin); } #ifdef TOOLS_ENABLED - if ((data.gizmo.is_valid() || data.notify_transform) && !data.ignore_notification && !xform_change.in_list()) { + if ((!data.gizmos.is_empty() || data.notify_transform) && !data.ignore_notification && !xform_change.in_list()) { #else if (data.notify_transform && !data.ignore_notification && !xform_change.in_list()) { #endif @@ -148,6 +147,7 @@ void Node3D::_notification(int p_what) { _notify_dirty(); notification(NOTIFICATION_ENTER_WORLD); + _update_visibility_parent(true); } break; case NOTIFICATION_EXIT_TREE: { @@ -161,6 +161,7 @@ void Node3D::_notification(int p_what) { data.parent = nullptr; data.C = nullptr; data.top_level_active = false; + _update_visibility_parent(true); } break; case NOTIFICATION_ENTER_WORLD: { data.inside_world = true; @@ -178,15 +179,14 @@ void Node3D::_notification(int p_what) { } #ifdef TOOLS_ENABLED if (Engine::get_singleton()->is_editor_hint() && get_tree()->is_node_being_edited(this)) { - //get_scene()->call_group(SceneMainLoop::GROUP_CALL_REALTIME,SceneStringNames::get_singleton()->_spatial_editor_group,SceneStringNames::get_singleton()->_request_gizmo,this); get_tree()->call_group_flags(0, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_request_gizmo, this); - if (!data.gizmo_disabled) { - if (data.gizmo.is_valid()) { - data.gizmo->create(); + if (!data.gizmos_disabled) { + for (int i = 0; i < data.gizmos.size(); i++) { + data.gizmos.write[i]->create(); if (is_visible_in_tree()) { - data.gizmo->redraw(); + data.gizmos.write[i]->redraw(); } - data.gizmo->transform(); + data.gizmos.write[i]->transform(); } } } @@ -195,10 +195,7 @@ void Node3D::_notification(int p_what) { } break; case NOTIFICATION_EXIT_WORLD: { #ifdef TOOLS_ENABLED - if (data.gizmo.is_valid()) { - data.gizmo->free(); - data.gizmo.unref(); - } + clear_gizmos(); #endif if (get_script_instance()) { @@ -212,8 +209,8 @@ void Node3D::_notification(int p_what) { case NOTIFICATION_TRANSFORM_CHANGED: { #ifdef TOOLS_ENABLED - if (data.gizmo.is_valid()) { - data.gizmo->transform(); + for (int i = 0; i < data.gizmos.size(); i++) { + data.gizmos.write[i]->transform(); } #endif } break; @@ -223,38 +220,33 @@ void Node3D::_notification(int p_what) { } } -void Node3D::set_transform(const Transform &p_transform) { +void Node3D::set_transform(const Transform3D &p_transform) { data.local_transform = p_transform; data.dirty |= DIRTY_VECTORS; - _change_notify("translation"); - _change_notify("rotation"); - _change_notify("rotation_degrees"); - _change_notify("scale"); _propagate_transform_changed(this); if (data.notify_local_transform) { notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED); } } -void Node3D::set_global_transform(const Transform &p_transform) { - Transform xform = +void Node3D::set_global_transform(const Transform3D &p_transform) { + Transform3D xform = (data.parent && !data.top_level_active) ? - data.parent->get_global_transform().affine_inverse() * p_transform : - p_transform; + data.parent->get_global_transform().affine_inverse() * p_transform : + p_transform; set_transform(xform); } -Transform Node3D::get_transform() const { +Transform3D Node3D::get_transform() const { if (data.dirty & DIRTY_LOCAL) { _update_local_transform(); } return data.local_transform; } - -Transform Node3D::get_global_transform() const { - ERR_FAIL_COND_V(!is_inside_tree(), Transform()); +Transform3D Node3D::get_global_transform() const { + ERR_FAIL_COND_V(!is_inside_tree(), Transform3D()); if (data.dirty & DIRTY_GLOBAL) { if (data.dirty & DIRTY_LOCAL) { @@ -278,25 +270,28 @@ Transform Node3D::get_global_transform() const { } #ifdef TOOLS_ENABLED -Transform Node3D::get_global_gizmo_transform() const { +Transform3D Node3D::get_global_gizmo_transform() const { return get_global_transform(); } -Transform Node3D::get_local_gizmo_transform() const { +Transform3D Node3D::get_local_gizmo_transform() const { return get_transform(); } #endif -Node3D *Node3D::get_parent_spatial() const { - return data.parent; +Node3D *Node3D::get_parent_node_3d() const { + if (data.top_level) { + return nullptr; + } + + return Object::cast_to<Node3D>(get_parent()); } -Transform Node3D::get_relative_transform(const Node *p_parent) const { - if (p_parent == this) { - return Transform(); - } +Transform3D Node3D::get_relative_transform(const Node *p_parent) const { + if (p_parent == this) + return Transform3D(); - ERR_FAIL_COND_V(!data.parent, Transform()); + ERR_FAIL_COND_V(!data.parent, Transform3D()); if (p_parent == data.parent) { return get_transform(); @@ -305,9 +300,8 @@ Transform Node3D::get_relative_transform(const Node *p_parent) const { } } -void Node3D::set_translation(const Vector3 &p_translation) { - data.local_transform.origin = p_translation; - _change_notify("transform"); +void Node3D::set_position(const Vector3 &p_position) { + data.local_transform.origin = p_position; _propagate_transform_changed(this); if (data.notify_local_transform) { notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED); @@ -322,17 +316,12 @@ void Node3D::set_rotation(const Vector3 &p_euler_rad) { data.rotation = p_euler_rad; data.dirty |= DIRTY_LOCAL; - _change_notify("transform"); _propagate_transform_changed(this); if (data.notify_local_transform) { notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED); } } -void Node3D::set_rotation_degrees(const Vector3 &p_euler_deg) { - set_rotation(p_euler_deg * Math_PI / 180.0); -} - void Node3D::set_scale(const Vector3 &p_scale) { if (data.dirty & DIRTY_VECTORS) { data.rotation = data.local_transform.basis.get_rotation(); @@ -341,14 +330,13 @@ void Node3D::set_scale(const Vector3 &p_scale) { data.scale = p_scale; data.dirty |= DIRTY_LOCAL; - _change_notify("transform"); _propagate_transform_changed(this); if (data.notify_local_transform) { notification(NOTIFICATION_LOCAL_TRANSFORM_CHANGED); } } -Vector3 Node3D::get_translation() const { +Vector3 Node3D::get_position() const { return data.local_transform.origin; } @@ -363,10 +351,6 @@ Vector3 Node3D::get_rotation() const { return data.rotation; } -Vector3 Node3D::get_rotation_degrees() const { - return get_rotation() * 180.0 / Math_PI; -} - Vector3 Node3D::get_scale() const { if (data.dirty & DIRTY_VECTORS) { data.scale = data.local_transform.basis.get_scale(); @@ -378,80 +362,119 @@ Vector3 Node3D::get_scale() const { return data.scale; } -void Node3D::update_gizmo() { +void Node3D::update_gizmos() { #ifdef TOOLS_ENABLED if (!is_inside_world()) { return; } - if (!data.gizmo.is_valid()) { - get_tree()->call_group_flags(SceneTree::GROUP_CALL_REALTIME, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_request_gizmo, this); + + if (data.gizmos.is_empty()) { + return; } - if (!data.gizmo.is_valid()) { + data.gizmos_dirty = true; + MessageQueue::get_singleton()->push_callable(callable_mp(this, &Node3D::_update_gizmos)); +#endif +} + +void Node3D::clear_subgizmo_selection() { +#ifdef TOOLS_ENABLED + if (!is_inside_world()) { return; } - if (data.gizmo_dirty) { + + if (data.gizmos.is_empty()) { return; } - data.gizmo_dirty = true; - MessageQueue::get_singleton()->push_call(this, "_update_gizmo"); + + if (Engine::get_singleton()->is_editor_hint() && get_tree()->is_node_being_edited(this)) { + get_tree()->call_group_flags(0, SceneStringNames::get_singleton()->_spatial_editor_group, SceneStringNames::get_singleton()->_clear_subgizmo_selection, this); + } #endif } -void Node3D::set_gizmo(const Ref<Node3DGizmo> &p_gizmo) { +void Node3D::add_gizmo(Ref<Node3DGizmo> p_gizmo) { #ifdef TOOLS_ENABLED - if (data.gizmo_disabled) { + if (data.gizmos_disabled || p_gizmo.is_null()) { return; } - if (data.gizmo.is_valid() && is_inside_world()) { - data.gizmo->free(); - } - data.gizmo = p_gizmo; - if (data.gizmo.is_valid() && is_inside_world()) { - data.gizmo->create(); + data.gizmos.push_back(p_gizmo); + + if (p_gizmo.is_valid() && is_inside_world()) { + p_gizmo->create(); if (is_visible_in_tree()) { - data.gizmo->redraw(); + p_gizmo->redraw(); } - data.gizmo->transform(); + p_gizmo->transform(); + } +#endif +} + +void Node3D::remove_gizmo(Ref<Node3DGizmo> p_gizmo) { +#ifdef TOOLS_ENABLED + + int idx = data.gizmos.find(p_gizmo); + if (idx != -1) { + p_gizmo->free(); + data.gizmos.remove(idx); + } +#endif +} + +void Node3D::clear_gizmos() { +#ifdef TOOLS_ENABLED + for (int i = 0; i < data.gizmos.size(); i++) { + data.gizmos.write[i]->free(); } + data.gizmos.clear(); +#endif +} + +Array Node3D::get_gizmos_bind() const { + Array ret; +#ifdef TOOLS_ENABLED + for (int i = 0; i < data.gizmos.size(); i++) { + ret.push_back(Variant(data.gizmos[i].ptr())); + } #endif + + return ret; } -Ref<Node3DGizmo> Node3D::get_gizmo() const { +Vector<Ref<Node3DGizmo>> Node3D::get_gizmos() const { #ifdef TOOLS_ENABLED - return data.gizmo; + return data.gizmos; #else - return Ref<Node3DGizmo>(); + return Vector<Ref<Node3DGizmo>>(); #endif } -void Node3D::_update_gizmo() { +void Node3D::_update_gizmos() { #ifdef TOOLS_ENABLED - if (!is_inside_world()) { + if (data.gizmos_disabled || !is_inside_world() || !data.gizmos_dirty) { return; } - data.gizmo_dirty = false; - if (data.gizmo.is_valid()) { + data.gizmos_dirty = false; + for (int i = 0; i < data.gizmos.size(); i++) { if (is_visible_in_tree()) { - data.gizmo->redraw(); + data.gizmos.write[i]->redraw(); } else { - data.gizmo->clear(); + data.gizmos.write[i]->clear(); } } #endif } #ifdef TOOLS_ENABLED -void Node3D::set_disable_gizmo(bool p_enabled) { - data.gizmo_disabled = p_enabled; - if (!p_enabled && data.gizmo.is_valid()) { - data.gizmo = Ref<Node3DGizmo>(); +void Node3D::set_disable_gizmos(bool p_enabled) { + data.gizmos_disabled = p_enabled; + if (!p_enabled) { + clear_gizmos(); } } - #endif void Node3D::set_disable_scale(bool p_enabled) { @@ -495,15 +518,14 @@ Ref<World3D> Node3D::get_world_3d() const { void Node3D::_propagate_visibility_changed() { notification(NOTIFICATION_VISIBILITY_CHANGED); emit_signal(SceneStringNames::get_singleton()->visibility_changed); - _change_notify("visible"); #ifdef TOOLS_ENABLED - if (data.gizmo.is_valid()) { - _update_gizmo(); + if (!data.gizmos.is_empty()) { + data.gizmos_dirty = true; + _update_gizmos(); } #endif - for (List<Node3D *>::Element *E = data.children.front(); E; E = E->next()) { - Node3D *c = E->get(); + for (Node3D *c : data.children) { if (!c || !c->data.visible) { continue; } @@ -564,104 +586,102 @@ bool Node3D::is_visible() const { return data.visible; } -void Node3D::rotate_object_local(const Vector3 &p_axis, float p_angle) { - Transform t = get_transform(); +void Node3D::rotate_object_local(const Vector3 &p_axis, real_t p_angle) { + Transform3D t = get_transform(); t.basis.rotate_local(p_axis, p_angle); set_transform(t); } -void Node3D::rotate(const Vector3 &p_axis, float p_angle) { - Transform t = get_transform(); +void Node3D::rotate(const Vector3 &p_axis, real_t p_angle) { + Transform3D t = get_transform(); t.basis.rotate(p_axis, p_angle); set_transform(t); } -void Node3D::rotate_x(float p_angle) { - Transform t = get_transform(); +void Node3D::rotate_x(real_t p_angle) { + Transform3D t = get_transform(); t.basis.rotate(Vector3(1, 0, 0), p_angle); set_transform(t); } -void Node3D::rotate_y(float p_angle) { - Transform t = get_transform(); +void Node3D::rotate_y(real_t p_angle) { + Transform3D t = get_transform(); t.basis.rotate(Vector3(0, 1, 0), p_angle); set_transform(t); } -void Node3D::rotate_z(float p_angle) { - Transform t = get_transform(); +void Node3D::rotate_z(real_t p_angle) { + Transform3D t = get_transform(); t.basis.rotate(Vector3(0, 0, 1), p_angle); set_transform(t); } void Node3D::translate(const Vector3 &p_offset) { - Transform t = get_transform(); + Transform3D t = get_transform(); t.translate(p_offset); set_transform(t); } void Node3D::translate_object_local(const Vector3 &p_offset) { - Transform t = get_transform(); + Transform3D t = get_transform(); - Transform s; + Transform3D s; s.translate(p_offset); set_transform(t * s); } void Node3D::scale(const Vector3 &p_ratio) { - Transform t = get_transform(); + Transform3D t = get_transform(); t.basis.scale(p_ratio); set_transform(t); } void Node3D::scale_object_local(const Vector3 &p_scale) { - Transform t = get_transform(); + Transform3D t = get_transform(); t.basis.scale_local(p_scale); set_transform(t); } -void Node3D::global_rotate(const Vector3 &p_axis, float p_angle) { - Transform t = get_global_transform(); +void Node3D::global_rotate(const Vector3 &p_axis, real_t p_angle) { + Transform3D t = get_global_transform(); t.basis.rotate(p_axis, p_angle); set_global_transform(t); } void Node3D::global_scale(const Vector3 &p_scale) { - Transform t = get_global_transform(); + Transform3D t = get_global_transform(); t.basis.scale(p_scale); set_global_transform(t); } void Node3D::global_translate(const Vector3 &p_offset) { - Transform t = get_global_transform(); + Transform3D t = get_global_transform(); t.origin += p_offset; set_global_transform(t); } void Node3D::orthonormalize() { - Transform t = get_transform(); + Transform3D t = get_transform(); t.orthonormalize(); set_transform(t); } void Node3D::set_identity() { - set_transform(Transform()); + set_transform(Transform3D()); } void Node3D::look_at(const Vector3 &p_target, const Vector3 &p_up) { - Vector3 origin(get_global_transform().origin); + Vector3 origin = get_global_transform().origin; look_at_from_position(origin, p_target, p_up); } void Node3D::look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up) { - ERR_FAIL_COND_MSG(p_pos == p_target, "Node origin and target are in the same position, look_at() failed."); - ERR_FAIL_COND_MSG(p_up.cross(p_target - p_pos) == Vector3(), "Up vector and direction between node origin and target are aligned, look_at() failed."); + ERR_FAIL_COND_MSG(p_pos.is_equal_approx(p_target), "Node origin and target are in the same position, look_at() failed."); + ERR_FAIL_COND_MSG(p_up.is_equal_approx(Vector3()), "The up vector can't be zero, look_at() failed."); + ERR_FAIL_COND_MSG(p_up.cross(p_target - p_pos).is_equal_approx(Vector3()), "Up vector and direction between node origin and target are aligned, look_at() failed."); - Transform lookat; - lookat.origin = p_pos; - - Vector3 original_scale(get_scale()); - lookat = lookat.looking_at(p_target, p_up); + Transform3D lookat = Transform3D(Basis::looking_at(p_target - p_pos, p_up), p_pos); + Vector3 original_scale = get_scale(); set_global_transform(lookat); set_scale(original_scale); } @@ -700,34 +720,79 @@ void Node3D::force_update_transform() { notification(NOTIFICATION_TRANSFORM_CHANGED); } +void Node3D::_update_visibility_parent(bool p_update_root) { + RID new_parent; + + if (!visibility_parent_path.is_empty()) { + if (!p_update_root) { + return; + } + Node *parent = get_node_or_null(visibility_parent_path); + ERR_FAIL_COND_MSG(!parent, "Can't find visibility parent node at path: " + visibility_parent_path); + ERR_FAIL_COND_MSG(parent == this, "The visibility parent can't be the same node."); + GeometryInstance3D *gi = Object::cast_to<GeometryInstance3D>(parent); + ERR_FAIL_COND_MSG(!gi, "The visibility parent node must be a GeometryInstance3D, at path: " + visibility_parent_path); + new_parent = gi ? gi->get_instance() : RID(); + } else if (data.parent) { + new_parent = data.parent->data.visibility_parent; + } + + if (new_parent == data.visibility_parent) { + return; + } + + data.visibility_parent = new_parent; + + VisualInstance3D *vi = Object::cast_to<VisualInstance3D>(this); + if (vi) { + RS::get_singleton()->instance_set_visibility_parent(vi->get_instance(), data.visibility_parent); + } + + for (Node3D *c : data.children) { + c->_update_visibility_parent(false); + } +} + +void Node3D::set_visibility_parent(const NodePath &p_path) { + visibility_parent_path = p_path; + if (is_inside_tree()) { + _update_visibility_parent(true); + } +} + +NodePath Node3D::get_visibility_parent() const { + return visibility_parent_path; +} + void Node3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_transform", "local"), &Node3D::set_transform); ClassDB::bind_method(D_METHOD("get_transform"), &Node3D::get_transform); - ClassDB::bind_method(D_METHOD("set_translation", "translation"), &Node3D::set_translation); - ClassDB::bind_method(D_METHOD("get_translation"), &Node3D::get_translation); + ClassDB::bind_method(D_METHOD("set_position", "position"), &Node3D::set_position); + ClassDB::bind_method(D_METHOD("get_position"), &Node3D::get_position); ClassDB::bind_method(D_METHOD("set_rotation", "euler"), &Node3D::set_rotation); ClassDB::bind_method(D_METHOD("get_rotation"), &Node3D::get_rotation); - ClassDB::bind_method(D_METHOD("set_rotation_degrees", "euler_degrees"), &Node3D::set_rotation_degrees); - ClassDB::bind_method(D_METHOD("get_rotation_degrees"), &Node3D::get_rotation_degrees); ClassDB::bind_method(D_METHOD("set_scale", "scale"), &Node3D::set_scale); ClassDB::bind_method(D_METHOD("get_scale"), &Node3D::get_scale); ClassDB::bind_method(D_METHOD("set_global_transform", "global"), &Node3D::set_global_transform); ClassDB::bind_method(D_METHOD("get_global_transform"), &Node3D::get_global_transform); - ClassDB::bind_method(D_METHOD("get_parent_spatial"), &Node3D::get_parent_spatial); + ClassDB::bind_method(D_METHOD("get_parent_node_3d"), &Node3D::get_parent_node_3d); ClassDB::bind_method(D_METHOD("set_ignore_transform_notification", "enabled"), &Node3D::set_ignore_transform_notification); - ClassDB::bind_method(D_METHOD("set_as_toplevel", "enable"), &Node3D::set_as_top_level); - ClassDB::bind_method(D_METHOD("is_set_as_toplevel"), &Node3D::is_set_as_top_level); + ClassDB::bind_method(D_METHOD("set_as_top_level", "enable"), &Node3D::set_as_top_level); + ClassDB::bind_method(D_METHOD("is_set_as_top_level"), &Node3D::is_set_as_top_level); ClassDB::bind_method(D_METHOD("set_disable_scale", "disable"), &Node3D::set_disable_scale); ClassDB::bind_method(D_METHOD("is_scale_disabled"), &Node3D::is_scale_disabled); ClassDB::bind_method(D_METHOD("get_world_3d"), &Node3D::get_world_3d); ClassDB::bind_method(D_METHOD("force_update_transform"), &Node3D::force_update_transform); - ClassDB::bind_method(D_METHOD("_update_gizmo"), &Node3D::_update_gizmo); + ClassDB::bind_method(D_METHOD("set_visibility_parent", "path"), &Node3D::set_visibility_parent); + ClassDB::bind_method(D_METHOD("get_visibility_parent"), &Node3D::get_visibility_parent); - ClassDB::bind_method(D_METHOD("update_gizmo"), &Node3D::update_gizmo); - ClassDB::bind_method(D_METHOD("set_gizmo", "gizmo"), &Node3D::set_gizmo); - ClassDB::bind_method(D_METHOD("get_gizmo"), &Node3D::get_gizmo); + ClassDB::bind_method(D_METHOD("update_gizmos"), &Node3D::update_gizmos); + ClassDB::bind_method(D_METHOD("add_gizmo", "gizmo"), &Node3D::add_gizmo); + ClassDB::bind_method(D_METHOD("get_gizmos"), &Node3D::get_gizmos_bind); + ClassDB::bind_method(D_METHOD("clear_gizmos"), &Node3D::clear_gizmos); + ClassDB::bind_method(D_METHOD("clear_subgizmo_selection"), &Node3D::clear_subgizmo_selection); ClassDB::bind_method(D_METHOD("set_visible", "visible"), &Node3D::set_visible); ClassDB::bind_method(D_METHOD("is_visible"), &Node3D::is_visible); @@ -755,8 +820,8 @@ void Node3D::_bind_methods() { ClassDB::bind_method(D_METHOD("orthonormalize"), &Node3D::orthonormalize); ClassDB::bind_method(D_METHOD("set_identity"), &Node3D::set_identity); - ClassDB::bind_method(D_METHOD("look_at", "target", "up"), &Node3D::look_at); - ClassDB::bind_method(D_METHOD("look_at_from_position", "position", "target", "up"), &Node3D::look_at_from_position); + ClassDB::bind_method(D_METHOD("look_at", "target", "up"), &Node3D::look_at, DEFVAL(Vector3(0, 1, 0))); + ClassDB::bind_method(D_METHOD("look_at_from_position", "position", "target", "up"), &Node3D::look_at_from_position, DEFVAL(Vector3(0, 1, 0))); ClassDB::bind_method(D_METHOD("to_local", "global_point"), &Node3D::to_local); ClassDB::bind_method(D_METHOD("to_global", "local_point"), &Node3D::to_global); @@ -766,46 +831,21 @@ void Node3D::_bind_methods() { BIND_CONSTANT(NOTIFICATION_EXIT_WORLD); BIND_CONSTANT(NOTIFICATION_VISIBILITY_CHANGED); - //ADD_PROPERTY( PropertyInfo(Variant::TRANSFORM,"transform/global",PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR ), "set_global_transform", "get_global_transform") ; + //ADD_PROPERTY( PropertyInfo(Variant::TRANSFORM3D,"transform/global",PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR ), "set_global_transform", "get_global_transform") ; ADD_GROUP("Transform", ""); - ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "global_transform", PROPERTY_HINT_NONE, "", 0), "set_global_transform", "get_global_transform"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "translation", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_translation", "get_translation"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation_degrees", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_rotation_degrees", "get_rotation_degrees"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation", PROPERTY_HINT_NONE, "", 0), "set_rotation", "get_rotation"); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "global_transform", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NONE), "set_global_transform", "get_global_transform"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "position", PROPERTY_HINT_RANGE, "-99999,99999,0,or_greater,or_lesser,noslider,suffix:m", PROPERTY_USAGE_EDITOR), "set_position", "get_position"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "rotation", PROPERTY_HINT_RANGE, "-360,360,0.1,or_lesser,or_greater,radians", PROPERTY_USAGE_EDITOR), "set_rotation", "get_rotation"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "scale", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_scale", "get_scale"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "toplevel"), "set_as_toplevel", "is_set_as_toplevel"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "top_level"), "set_as_top_level", "is_set_as_top_level"); ADD_GROUP("Matrix", ""); - ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "transform", PROPERTY_HINT_NONE, ""), "set_transform", "get_transform"); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "transform", PROPERTY_HINT_NONE, ""), "set_transform", "get_transform"); ADD_GROUP("Visibility", ""); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "visible"), "set_visible", "is_visible"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "gizmo", PROPERTY_HINT_RESOURCE_TYPE, "Node3DGizmo", 0), "set_gizmo", "get_gizmo"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "visibility_parent", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "GeometryInstance3D"), "set_visibility_parent", "get_visibility_parent"); ADD_SIGNAL(MethodInfo("visibility_changed")); } Node3D::Node3D() : - xform_change(this) { - data.dirty = DIRTY_NONE; - data.children_lock = 0; - - data.ignore_notification = false; - data.top_level = false; - data.top_level_active = false; - data.scale = Vector3(1, 1, 1); - data.viewport = nullptr; - data.inside_world = false; - data.visible = true; - data.disable_scale = false; - -#ifdef TOOLS_ENABLED - data.gizmo_disabled = false; - data.gizmo_dirty = false; -#endif - data.notify_local_transform = false; - data.notify_transform = false; - data.parent = nullptr; - data.C = nullptr; -} - -Node3D::~Node3D() { -} + xform_change(this) {} diff --git a/scene/3d/node_3d.h b/scene/3d/node_3d.h index 229e0f2c8c..0fd0c4e205 100644 --- a/scene/3d/node_3d.h +++ b/scene/3d/node_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,10 +32,9 @@ #define NODE_3D_H #include "scene/main/node.h" -#include "scene/main/scene_tree.h" -class Node3DGizmo : public Reference { - GDCLASS(Node3DGizmo, Reference); +class Node3DGizmo : public RefCounted { + GDCLASS(Node3DGizmo, RefCounted); public: virtual void create() = 0; @@ -62,45 +61,52 @@ class Node3D : public Node { mutable SelfList<Node> xform_change; struct Data { - mutable Transform global_transform; - mutable Transform local_transform; + mutable Transform3D global_transform; + mutable Transform3D local_transform; mutable Vector3 rotation; - mutable Vector3 scale; + mutable Vector3 scale = Vector3(1, 1, 1); - mutable int dirty; + mutable int dirty = DIRTY_NONE; - Viewport *viewport; + Viewport *viewport = nullptr; - bool top_level_active; - bool top_level; - bool inside_world; + bool top_level_active = false; + bool top_level = false; + bool inside_world = false; - int children_lock; - Node3D *parent; + RID visibility_parent; + + int children_lock = 0; + Node3D *parent = nullptr; List<Node3D *> children; - List<Node3D *>::Element *C; + List<Node3D *>::Element *C = nullptr; - bool ignore_notification; - bool notify_local_transform; - bool notify_transform; + bool ignore_notification = false; + bool notify_local_transform = false; + bool notify_transform = false; - bool visible; - bool disable_scale; + bool visible = true; + bool disable_scale = false; #ifdef TOOLS_ENABLED - Ref<Node3DGizmo> gizmo; - bool gizmo_disabled; - bool gizmo_dirty; + Vector<Ref<Node3DGizmo>> gizmos; + bool gizmos_disabled = false; + bool gizmos_dirty = false; #endif } data; - void _update_gizmo(); + NodePath visibility_parent_path; + + void _update_gizmos(); void _notify_dirty(); void _propagate_transform_changed(Node3D *p_origin); void _propagate_visibility_changed(); + void _propagate_visibility_parent(); + void _update_visibility_parent(bool p_update_root); + protected: _FORCE_INLINE_ void set_ignore_transform_notification(bool p_ignore) { data.ignore_notification = p_ignore; } @@ -111,7 +117,6 @@ protected: public: enum { - NOTIFICATION_TRANSFORM_CHANGED = SceneTree::NOTIFICATION_TRANSFORM_CHANGED, NOTIFICATION_ENTER_WORLD = 41, NOTIFICATION_EXIT_WORLD = 42, @@ -119,29 +124,27 @@ public: NOTIFICATION_LOCAL_TRANSFORM_CHANGED = 44, }; - Node3D *get_parent_spatial() const; + Node3D *get_parent_node_3d() const; Ref<World3D> get_world_3d() const; - void set_translation(const Vector3 &p_translation); + void set_position(const Vector3 &p_position); void set_rotation(const Vector3 &p_euler_rad); - void set_rotation_degrees(const Vector3 &p_euler_deg); void set_scale(const Vector3 &p_scale); - Vector3 get_translation() const; + Vector3 get_position() const; Vector3 get_rotation() const; - Vector3 get_rotation_degrees() const; Vector3 get_scale() const; - void set_transform(const Transform &p_transform); - void set_global_transform(const Transform &p_transform); + void set_transform(const Transform3D &p_transform); + void set_global_transform(const Transform3D &p_transform); - Transform get_transform() const; - Transform get_global_transform() const; + Transform3D get_transform() const; + Transform3D get_global_transform() const; #ifdef TOOLS_ENABLED - virtual Transform get_global_gizmo_transform() const; - virtual Transform get_local_gizmo_transform() const; + virtual Transform3D get_global_gizmo_transform() const; + virtual Transform3D get_local_gizmo_transform() const; #endif void set_as_top_level(bool p_enabled); @@ -150,32 +153,36 @@ public: void set_disable_scale(bool p_enabled); bool is_scale_disabled() const; - void set_disable_gizmo(bool p_enabled); - void update_gizmo(); - void set_gizmo(const Ref<Node3DGizmo> &p_gizmo); - Ref<Node3DGizmo> get_gizmo() const; + void set_disable_gizmos(bool p_enabled); + void update_gizmos(); + void clear_subgizmo_selection(); + Vector<Ref<Node3DGizmo>> get_gizmos() const; + Array get_gizmos_bind() const; + void add_gizmo(Ref<Node3DGizmo> p_gizmo); + void remove_gizmo(Ref<Node3DGizmo> p_gizmo); + void clear_gizmos(); _FORCE_INLINE_ bool is_inside_world() const { return data.inside_world; } - Transform get_relative_transform(const Node *p_parent) const; + Transform3D get_relative_transform(const Node *p_parent) const; - void rotate(const Vector3 &p_axis, float p_angle); - void rotate_x(float p_angle); - void rotate_y(float p_angle); - void rotate_z(float p_angle); + void rotate(const Vector3 &p_axis, real_t p_angle); + void rotate_x(real_t p_angle); + void rotate_y(real_t p_angle); + void rotate_z(real_t p_angle); void translate(const Vector3 &p_offset); void scale(const Vector3 &p_ratio); - void rotate_object_local(const Vector3 &p_axis, float p_angle); + void rotate_object_local(const Vector3 &p_axis, real_t p_angle); void scale_object_local(const Vector3 &p_scale); void translate_object_local(const Vector3 &p_offset); - void global_rotate(const Vector3 &p_axis, float p_angle); + void global_rotate(const Vector3 &p_axis, real_t p_angle); void global_scale(const Vector3 &p_scale); void global_translate(const Vector3 &p_offset); - void look_at(const Vector3 &p_target, const Vector3 &p_up); - void look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up); + void look_at(const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0)); + void look_at_from_position(const Vector3 &p_pos, const Vector3 &p_target, const Vector3 &p_up = Vector3(0, 1, 0)); Vector3 to_local(Vector3 p_global) const; Vector3 to_global(Vector3 p_local) const; @@ -197,8 +204,10 @@ public: void force_update_transform(); + void set_visibility_parent(const NodePath &p_path); + NodePath get_visibility_parent() const; + Node3D(); - ~Node3D(); }; #endif // NODE_3D_H diff --git a/scene/3d/occluder_instance_3d.cpp b/scene/3d/occluder_instance_3d.cpp new file mode 100644 index 0000000000..f3e174c01b --- /dev/null +++ b/scene/3d/occluder_instance_3d.cpp @@ -0,0 +1,367 @@ +/*************************************************************************/ +/* occluder_instance_3d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "occluder_instance_3d.h" +#include "core/core_string_names.h" +#include "scene/3d/mesh_instance_3d.h" + +RID Occluder3D::get_rid() const { + if (!occluder.is_valid()) { + occluder = RS::get_singleton()->occluder_create(); + RS::get_singleton()->occluder_set_mesh(occluder, vertices, indices); + } + return occluder; +} + +void Occluder3D::set_vertices(PackedVector3Array p_vertices) { + vertices = p_vertices; + if (occluder.is_valid()) { + RS::get_singleton()->occluder_set_mesh(occluder, vertices, indices); + } + _update_changes(); +} + +PackedVector3Array Occluder3D::get_vertices() const { + return vertices; +} + +void Occluder3D::set_indices(PackedInt32Array p_indices) { + indices = p_indices; + if (occluder.is_valid()) { + RS::get_singleton()->occluder_set_mesh(occluder, vertices, indices); + } + _update_changes(); +} + +PackedInt32Array Occluder3D::get_indices() const { + return indices; +} + +void Occluder3D::_update_changes() { + aabb = AABB(); + + const Vector3 *ptr = vertices.ptr(); + for (int i = 0; i < vertices.size(); i++) { + aabb.expand_to(ptr[i]); + } + + debug_lines.clear(); + debug_mesh.unref(); + + emit_changed(); +} + +Vector<Vector3> Occluder3D::get_debug_lines() const { + if (!debug_lines.is_empty()) { + return debug_lines; + } + + if (indices.size() % 3 != 0) { + return Vector<Vector3>(); + } + + for (int i = 0; i < indices.size() / 3; i++) { + for (int j = 0; j < 3; j++) { + int a = indices[i * 3 + j]; + int b = indices[i * 3 + (j + 1) % 3]; + ERR_FAIL_INDEX_V_MSG(a, vertices.size(), Vector<Vector3>(), "Occluder indices are out of range."); + ERR_FAIL_INDEX_V_MSG(b, vertices.size(), Vector<Vector3>(), "Occluder indices are out of range."); + debug_lines.push_back(vertices[a]); + debug_lines.push_back(vertices[b]); + } + } + return debug_lines; +} + +Ref<ArrayMesh> Occluder3D::get_debug_mesh() const { + if (debug_mesh.is_valid()) { + return debug_mesh; + } + + if (indices.size() % 3 != 0) { + return debug_mesh; + } + + Array arrays; + arrays.resize(Mesh::ARRAY_MAX); + arrays[Mesh::ARRAY_VERTEX] = vertices; + arrays[Mesh::ARRAY_INDEX] = indices; + + debug_mesh.instantiate(); + debug_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, arrays); + return debug_mesh; +} + +AABB Occluder3D::get_aabb() const { + return aabb; +} + +void Occluder3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_vertices", "vertices"), &Occluder3D::set_vertices); + ClassDB::bind_method(D_METHOD("get_vertices"), &Occluder3D::get_vertices); + + ClassDB::bind_method(D_METHOD("set_indices", "indices"), &Occluder3D::set_indices); + ClassDB::bind_method(D_METHOD("get_indices"), &Occluder3D::get_indices); + + ADD_PROPERTY(PropertyInfo(Variant::PACKED_VECTOR3_ARRAY, "vertices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_vertices", "get_vertices"); + ADD_PROPERTY(PropertyInfo(Variant::PACKED_INT32_ARRAY, "indices", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_indices", "get_indices"); +} + +Occluder3D::Occluder3D() { +} + +Occluder3D::~Occluder3D() { + if (occluder.is_valid()) { + RS::get_singleton()->free(occluder); + } +} +///////////////////////////////////////////////// + +AABB OccluderInstance3D::get_aabb() const { + if (occluder.is_valid()) { + return occluder->get_aabb(); + } + return AABB(); +} + +Vector<Face3> OccluderInstance3D::get_faces(uint32_t p_usage_flags) const { + return Vector<Face3>(); +} + +void OccluderInstance3D::set_occluder(const Ref<Occluder3D> &p_occluder) { + if (occluder == p_occluder) { + return; + } + + if (occluder.is_valid()) { + occluder->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &OccluderInstance3D::_occluder_changed)); + } + + occluder = p_occluder; + + if (occluder.is_valid()) { + set_base(occluder->get_rid()); + occluder->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &OccluderInstance3D::_occluder_changed)); + } else { + set_base(RID()); + } + + update_gizmos(); + update_configuration_warnings(); +} + +void OccluderInstance3D::_occluder_changed() { + update_gizmos(); + update_configuration_warnings(); +} + +Ref<Occluder3D> OccluderInstance3D::get_occluder() const { + return occluder; +} + +void OccluderInstance3D::set_bake_mask(uint32_t p_mask) { + bake_mask = p_mask; + update_configuration_warnings(); +} + +uint32_t OccluderInstance3D::get_bake_mask() const { + return bake_mask; +} + +void OccluderInstance3D::set_bake_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Render layer number must be between 1 and 20 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 20, "Render layer number must be between 1 and 20 inclusive."); + uint32_t mask = get_bake_mask(); + if (p_value) { + mask |= 1 << (p_layer_number - 1); + } else { + mask &= ~(1 << (p_layer_number - 1)); + } + set_bake_mask(mask); +} + +bool OccluderInstance3D::get_bake_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Render layer number must be between 1 and 20 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 20, false, "Render layer number must be between 1 and 20 inclusive."); + return bake_mask & (1 << (p_layer_number - 1)); +} + +bool OccluderInstance3D::_bake_material_check(Ref<Material> p_material) { + StandardMaterial3D *standard_mat = Object::cast_to<StandardMaterial3D>(p_material.ptr()); + if (standard_mat && standard_mat->get_transparency() != StandardMaterial3D::TRANSPARENCY_DISABLED) { + return false; + } + return true; +} + +void OccluderInstance3D::_bake_node(Node *p_node, PackedVector3Array &r_vertices, PackedInt32Array &r_indices) { + MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_node); + if (mi && mi->is_visible_in_tree()) { + Ref<Mesh> mesh = mi->get_mesh(); + bool valid = true; + + if (mesh.is_null()) { + valid = false; + } + + if (valid && !_bake_material_check(mi->get_material_override())) { + valid = false; + } + + if ((mi->get_layer_mask() & bake_mask) == 0) { + valid = false; + } + + if (valid) { + Transform3D global_to_local = get_global_transform().affine_inverse() * mi->get_global_transform(); + + for (int i = 0; i < mesh->get_surface_count(); i++) { + if (mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) { + continue; + } + + if (mi->get_surface_override_material(i).is_valid()) { + if (!_bake_material_check(mi->get_surface_override_material(i))) { + continue; + } + } else { + if (!_bake_material_check(mesh->surface_get_material(i))) { + continue; + } + } + + Array arrays = mesh->surface_get_arrays(i); + + int vertex_offset = r_vertices.size(); + PackedVector3Array vertices = arrays[Mesh::ARRAY_VERTEX]; + r_vertices.resize(r_vertices.size() + vertices.size()); + + Vector3 *vtx_ptr = r_vertices.ptrw(); + for (int j = 0; j < vertices.size(); j++) { + vtx_ptr[vertex_offset + j] = global_to_local.xform(vertices[j]); + } + + int index_offset = r_indices.size(); + PackedInt32Array indices = arrays[Mesh::ARRAY_INDEX]; + r_indices.resize(r_indices.size() + indices.size()); + + int *idx_ptr = r_indices.ptrw(); + for (int j = 0; j < indices.size(); j++) { + idx_ptr[index_offset + j] = vertex_offset + indices[j]; + } + } + } + } + + for (int i = 0; i < p_node->get_child_count(); i++) { + Node *child = p_node->get_child(i); + if (!child->get_owner()) { + continue; //maybe a helper + } + + _bake_node(child, r_vertices, r_indices); + } +} + +OccluderInstance3D::BakeError OccluderInstance3D::bake(Node *p_from_node, String p_occluder_path) { + if (p_occluder_path == "") { + if (get_occluder().is_null()) { + return BAKE_ERROR_NO_SAVE_PATH; + } + } + + PackedVector3Array vertices; + PackedInt32Array indices; + + _bake_node(p_from_node, vertices, indices); + + if (vertices.is_empty() || indices.is_empty()) { + return BAKE_ERROR_NO_MESHES; + } + + Ref<Occluder3D> occ; + if (get_occluder().is_valid()) { + occ = get_occluder(); + } else { + occ.instantiate(); + occ->set_path(p_occluder_path); + } + + occ->set_vertices(vertices); + occ->set_indices(indices); + set_occluder(occ); + + return BAKE_ERROR_OK; +} + +TypedArray<String> OccluderInstance3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); + + if (!bool(GLOBAL_GET("rendering/occlusion_culling/use_occlusion_culling"))) { + warnings.push_back(TTR("Occlusion culling is disabled in the Project Settings, which means occlusion culling won't be performed in the root viewport.\nTo resolve this, open the Project Settings and enable Rendering > Occlusion Culling > Use Occlusion Culling.")); + } + + if (bake_mask == 0) { + // NOTE: This warning will not be emitted if none of the 20 checkboxes + // exposed in the editor are checked. This is because there are + // currently 12 unexposed layers in the editor inspector. + warnings.push_back(TTR("The Bake Mask has no bits enabled, which means baking will not produce any occluder meshes for this OccluderInstance3D.\nTo resolve this, enable at least one bit in the Bake Mask property.")); + } + + if (occluder.is_null()) { + warnings.push_back(TTR("No occluder mesh is defined in the Occluder property, so no occlusion culling will be performed using this OccluderInstance3D.\nTo resolve this, select the OccluderInstance3D then use the Bake Occluders button at the top of the 3D editor viewport.")); + } else if (occluder->get_vertices().size() < 3) { + // Using the "New Occluder" dropdown button won't result in a correct occluder, + // so warn the user about this. + warnings.push_back(TTR("The occluder mesh has less than 3 vertices, so no occlusion culling will be performed using this OccluderInstance3D.\nTo generate a proper occluder mesh, select the OccluderInstance3D then use the Bake Occluders button at the top of the 3D editor viewport.")); + } + + return warnings; +} + +void OccluderInstance3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_bake_mask", "mask"), &OccluderInstance3D::set_bake_mask); + ClassDB::bind_method(D_METHOD("get_bake_mask"), &OccluderInstance3D::get_bake_mask); + ClassDB::bind_method(D_METHOD("set_bake_mask_value", "layer_number", "value"), &OccluderInstance3D::set_bake_mask_value); + ClassDB::bind_method(D_METHOD("get_bake_mask_value", "layer_number"), &OccluderInstance3D::get_bake_mask_value); + + ClassDB::bind_method(D_METHOD("set_occluder", "occluder"), &OccluderInstance3D::set_occluder); + ClassDB::bind_method(D_METHOD("get_occluder"), &OccluderInstance3D::get_occluder); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "occluder", PROPERTY_HINT_RESOURCE_TYPE, "Occluder3D"), "set_occluder", "get_occluder"); + ADD_GROUP("Bake", "bake_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "bake_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_bake_mask", "get_bake_mask"); +} + +OccluderInstance3D::OccluderInstance3D() { +} + +OccluderInstance3D::~OccluderInstance3D() { +} diff --git a/scene/3d/immediate_geometry_3d.h b/scene/3d/occluder_instance_3d.h index 1403936e22..173614b80c 100644 --- a/scene/3d/immediate_geometry_3d.h +++ b/scene/3d/occluder_instance_3d.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* immediate_geometry_3d.h */ +/* occluder_instance_3d.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,45 +28,84 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef IMMEDIATE_GEOMETRY_3D_H -#define IMMEDIATE_GEOMETRY_3D_H +#ifndef OCCLUDER_INSTANCE_3D_H +#define OCCLUDER_INSTANCE_3D_H #include "scene/3d/visual_instance_3d.h" -#include "scene/resources/mesh.h" -class ImmediateGeometry3D : public GeometryInstance3D { - GDCLASS(ImmediateGeometry3D, GeometryInstance3D); +class Occluder3D : public Resource { + GDCLASS(Occluder3D, Resource); + RES_BASE_EXTENSION("occ"); - RID im; - //a list of textures drawn need to be kept, to avoid references - // in RenderingServer from becoming invalid if the texture is no longer used - List<Ref<Texture2D>> cached_textures; - bool empty; + mutable RID occluder; + mutable Ref<ArrayMesh> debug_mesh; + mutable Vector<Vector3> debug_lines; AABB aabb; + PackedVector3Array vertices; + PackedInt32Array indices; + + void _update_changes(); + protected: static void _bind_methods(); public: - void begin(Mesh::PrimitiveType p_primitive, const Ref<Texture2D> &p_texture = Ref<Texture2D>()); - void set_normal(const Vector3 &p_normal); - void set_tangent(const Plane &p_tangent); - void set_color(const Color &p_color); - void set_uv(const Vector2 &p_uv); - void set_uv2(const Vector2 &p_uv2); + void set_vertices(PackedVector3Array p_vertices); + PackedVector3Array get_vertices() const; + + void set_indices(PackedInt32Array p_indices); + PackedInt32Array get_indices() const; - void add_vertex(const Vector3 &p_vertex); + Vector<Vector3> get_debug_lines() const; + Ref<ArrayMesh> get_debug_mesh() const; + AABB get_aabb() const; + + virtual RID get_rid() const override; + Occluder3D(); + ~Occluder3D(); +}; - void end(); - void clear(); +class OccluderInstance3D : public VisualInstance3D { + GDCLASS(OccluderInstance3D, Node3D); - void add_sphere(int p_lats, int p_lons, float p_radius, bool p_add_uv = true); +private: + Ref<Occluder3D> occluder; + uint32_t bake_mask = 0xFFFFFFFF; + + void _occluder_changed(); + + bool _bake_material_check(Ref<Material> p_material); + void _bake_node(Node *p_node, PackedVector3Array &r_vertices, PackedInt32Array &r_indices); + +protected: + static void _bind_methods(); + +public: + virtual TypedArray<String> get_configuration_warnings() const override; + + enum BakeError { + BAKE_ERROR_OK, + BAKE_ERROR_NO_SAVE_PATH, + BAKE_ERROR_NO_MESHES, + }; + + void set_occluder(const Ref<Occluder3D> &p_occluder); + Ref<Occluder3D> get_occluder() const; virtual AABB get_aabb() const override; virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override; - ImmediateGeometry3D(); - ~ImmediateGeometry3D(); + void set_bake_mask(uint32_t p_mask); + uint32_t get_bake_mask() const; + + void set_bake_mask_value(int p_layer_number, bool p_enable); + bool get_bake_mask_value(int p_layer_number) const; + + BakeError bake(Node *p_from_node, String p_occluder_path = ""); + + OccluderInstance3D(); + ~OccluderInstance3D(); }; -#endif // IMMEDIATE_GEOMETRY_H +#endif diff --git a/scene/3d/path_3d.cpp b/scene/3d/path_3d.cpp index d0d2d6f9f4..9ea37e4bfa 100644 --- a/scene/3d/path_3d.cpp +++ b/scene/3d/path_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,18 +30,15 @@ #include "path_3d.h" -#include "core/engine.h" -#include "scene/scene_string_names.h" - void Path3D::_notification(int p_what) { } void Path3D::_curve_changed() { if (is_inside_tree() && Engine::get_singleton()->is_editor_hint()) { - update_gizmo(); + update_gizmos(); } if (is_inside_tree()) { - emit_signal("curve_changed"); + emit_signal(SNAME("curve_changed")); } // update the configuration warnings of all children of type PathFollow @@ -50,7 +47,7 @@ void Path3D::_curve_changed() { for (int i = 0; i < get_child_count(); i++) { PathFollow3D *child = Object::cast_to<PathFollow3D>(get_child(i)); if (child) { - child->update_configuration_warning(); + child->update_configuration_warnings(); } } } @@ -94,31 +91,39 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { return; } - if (delta_offset == 0) { - return; - } - - float bl = c->get_baked_length(); + real_t bl = c->get_baked_length(); if (bl == 0.0) { return; } - float bi = c->get_bake_interval(); - float o_next = offset + bi; + real_t bi = c->get_bake_interval(); + real_t o_next = offset + bi; + real_t o_prev = offset - bi; if (loop) { o_next = Math::fposmod(o_next, bl); - } else if (rotation_mode == ROTATION_ORIENTED && o_next >= bl) { - o_next = bl; + o_prev = Math::fposmod(o_prev, bl); + } else if (rotation_mode == ROTATION_ORIENTED) { + if (o_next >= bl) { + o_next = bl; + } + if (o_prev <= 0) { + o_prev = 0; + } } Vector3 pos = c->interpolate_baked(offset, cubic); - Transform t = get_transform(); + Transform3D t = get_transform(); // Vector3 pos_offset = Vector3(h_offset, v_offset, 0); not used in all cases // will be replaced by "Vector3(h_offset, v_offset, 0)" where it was formerly used if (rotation_mode == ROTATION_ORIENTED) { Vector3 forward = c->interpolate_baked(o_next, cubic) - pos; + // Try with the previous position + if (forward.length_squared() < CMP_EPSILON2) { + forward = pos - c->interpolate_baked(o_prev, cubic); + } + if (forward.length_squared() < CMP_EPSILON2) { forward = Vector3(0, 0, 1); } else { @@ -156,13 +161,13 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { t.origin = pos; - if (p_update_xyz_rot) { // Only update rotation if some parameter has changed - i.e. not on addition to scene tree + if (p_update_xyz_rot && delta_offset != 0) { // Only update rotation if some parameter has changed - i.e. not on addition to scene tree. Vector3 t_prev = (pos - c->interpolate_baked(offset - delta_offset, cubic)).normalized(); Vector3 t_cur = (c->interpolate_baked(offset + delta_offset, cubic) - pos).normalized(); Vector3 axis = t_prev.cross(t_cur); - float dot = t_prev.dot(t_cur); - float angle = Math::acos(CLAMP(dot, -1, 1)); + real_t dot = t_prev.dot(t_cur); + real_t angle = Math::acos(CLAMP(dot, -1, 1)); if (likely(!Math::is_zero_approx(angle))) { if (rotation_mode == ROTATION_Y) { @@ -181,7 +186,7 @@ void PathFollow3D::_update_transform(bool p_update_xyz_rot) { } // do the additional tilting - float tilt_angle = c->interpolate_baked_tilt(offset); + real_t tilt_angle = c->interpolate_baked_tilt(offset); Vector3 tilt_axis = t_cur; // not sure what tilt is supposed to do, is this correct?? if (likely(!Math::is_zero_approx(Math::abs(tilt_angle)))) { @@ -236,7 +241,7 @@ bool PathFollow3D::get_cubic_interpolation() const { void PathFollow3D::_validate_property(PropertyInfo &property) const { if (property.name == "offset") { - float max = 10000; + real_t max = 10000; if (path && path->get_curve().is_valid()) { max = path->get_curve()->get_baked_length(); } @@ -245,29 +250,21 @@ void PathFollow3D::_validate_property(PropertyInfo &property) const { } } -String PathFollow3D::get_configuration_warning() const { - if (!is_visible_in_tree() || !is_inside_tree()) { - return String(); - } - - String warning = Node3D::get_configuration_warning(); +TypedArray<String> PathFollow3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); - if (!Object::cast_to<Path3D>(get_parent())) { - if (!warning.empty()) { - warning += "\n\n"; - } - warning += TTR("PathFollow3D only works when set as a child of a Path3D node."); - } else { - Path3D *path = Object::cast_to<Path3D>(get_parent()); - if (path->get_curve().is_valid() && !path->get_curve()->is_up_vector_enabled() && rotation_mode == ROTATION_ORIENTED) { - if (!warning.empty()) { - warning += "\n\n"; + if (is_visible_in_tree() && is_inside_tree()) { + if (!Object::cast_to<Path3D>(get_parent())) { + warnings.push_back(TTR("PathFollow3D only works when set as a child of a Path3D node.")); + } else { + Path3D *path = Object::cast_to<Path3D>(get_parent()); + if (path->get_curve().is_valid() && !path->get_curve()->is_up_vector_enabled() && rotation_mode == ROTATION_ORIENTED) { + warnings.push_back(TTR("PathFollow3D's ROTATION_ORIENTED requires \"Up Vector\" to be enabled in its parent Path3D's Curve resource.")); } - warning += TTR("PathFollow3D's ROTATION_ORIENTED requires \"Up Vector\" to be enabled in its parent Path3D's Curve resource."); } } - return warning; + return warnings; } void PathFollow3D::_bind_methods() { @@ -307,13 +304,13 @@ void PathFollow3D::_bind_methods() { BIND_ENUM_CONSTANT(ROTATION_ORIENTED); } -void PathFollow3D::set_offset(float p_offset) { +void PathFollow3D::set_offset(real_t p_offset) { delta_offset = p_offset - offset; offset = p_offset; if (path) { if (path->get_curve().is_valid()) { - float path_length = path->get_curve()->get_baked_length(); + real_t path_length = path->get_curve()->get_baked_length(); if (loop) { offset = Math::fposmod(offset, path_length); @@ -327,43 +324,41 @@ void PathFollow3D::set_offset(float p_offset) { _update_transform(); } - _change_notify("offset"); - _change_notify("unit_offset"); } -void PathFollow3D::set_h_offset(float p_h_offset) { +void PathFollow3D::set_h_offset(real_t p_h_offset) { h_offset = p_h_offset; if (path) { _update_transform(); } } -float PathFollow3D::get_h_offset() const { +real_t PathFollow3D::get_h_offset() const { return h_offset; } -void PathFollow3D::set_v_offset(float p_v_offset) { +void PathFollow3D::set_v_offset(real_t p_v_offset) { v_offset = p_v_offset; if (path) { _update_transform(); } } -float PathFollow3D::get_v_offset() const { +real_t PathFollow3D::get_v_offset() const { return v_offset; } -float PathFollow3D::get_offset() const { +real_t PathFollow3D::get_offset() const { return offset; } -void PathFollow3D::set_unit_offset(float p_unit_offset) { +void PathFollow3D::set_unit_offset(real_t p_unit_offset) { if (path && path->get_curve().is_valid() && path->get_curve()->get_baked_length()) { set_offset(p_unit_offset * path->get_curve()->get_baked_length()); } } -float PathFollow3D::get_unit_offset() const { +real_t PathFollow3D::get_unit_offset() const { if (path && path->get_curve().is_valid() && path->get_curve()->get_baked_length()) { return get_offset() / path->get_curve()->get_baked_length(); } else { @@ -374,7 +369,7 @@ float PathFollow3D::get_unit_offset() const { void PathFollow3D::set_rotation_mode(RotationMode p_rotation_mode) { rotation_mode = p_rotation_mode; - update_configuration_warning(); + update_configuration_warnings(); _update_transform(); } @@ -389,14 +384,3 @@ void PathFollow3D::set_loop(bool p_loop) { bool PathFollow3D::has_loop() const { return loop; } - -PathFollow3D::PathFollow3D() { - offset = 0; - delta_offset = 0; - h_offset = 0; - v_offset = 0; - path = nullptr; - rotation_mode = ROTATION_XYZ; - cubic = true; - loop = true; -} diff --git a/scene/3d/path_3d.h b/scene/3d/path_3d.h index 1aa9f7ffd2..1ffe291100 100644 --- a/scene/3d/path_3d.h +++ b/scene/3d/path_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -57,7 +57,6 @@ class PathFollow3D : public Node3D { public: enum RotationMode { - ROTATION_NONE, ROTATION_Y, ROTATION_XY, @@ -66,14 +65,14 @@ public: }; private: - Path3D *path; - real_t delta_offset; // change in offset since last _update_transform - real_t offset; - real_t h_offset; - real_t v_offset; - bool cubic; - bool loop; - RotationMode rotation_mode; + Path3D *path = nullptr; + real_t delta_offset = 0.0; // Change in offset since last _update_transform. + real_t offset = 0.0; + real_t h_offset = 0.0; + real_t v_offset = 0.0; + bool cubic = true; + bool loop = true; + RotationMode rotation_mode = ROTATION_XYZ; void _update_transform(bool p_update_xyz_rot = true); @@ -84,17 +83,17 @@ protected: static void _bind_methods(); public: - void set_offset(float p_offset); - float get_offset() const; + void set_offset(real_t p_offset); + real_t get_offset() const; - void set_h_offset(float p_h_offset); - float get_h_offset() const; + void set_h_offset(real_t p_h_offset); + real_t get_h_offset() const; - void set_v_offset(float p_v_offset); - float get_v_offset() const; + void set_v_offset(real_t p_v_offset); + real_t get_v_offset() const; - void set_unit_offset(float p_unit_offset); - float get_unit_offset() const; + void set_unit_offset(real_t p_unit_offset); + real_t get_unit_offset() const; void set_loop(bool p_loop); bool has_loop() const; @@ -105,9 +104,9 @@ public: void set_cubic_interpolation(bool p_enable); bool get_cubic_interpolation() const; - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; - PathFollow3D(); + PathFollow3D() {} }; VARIANT_ENUM_CAST(PathFollow3D::RotationMode); diff --git a/scene/3d/physics_body_3d.cpp b/scene/3d/physics_body_3d.cpp index 193d016010..06a4087b60 100644 --- a/scene/3d/physics_body_3d.cpp +++ b/scene/3d/physics_body_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,83 +31,48 @@ #include "physics_body_3d.h" #include "core/core_string_names.h" -#include "core/engine.h" -#include "core/list.h" -#include "core/method_bind_ext.gen.inc" -#include "core/object.h" -#include "core/rid.h" -#include "scene/3d/collision_shape_3d.h" #include "scene/scene_string_names.h" -#include "servers/navigation_server_3d.h" #ifdef TOOLS_ENABLED #include "editor/plugins/node_3d_editor_plugin.h" #endif -Vector3 PhysicsBody3D::get_linear_velocity() const { - return Vector3(); -} - -Vector3 PhysicsBody3D::get_angular_velocity() const { - return Vector3(); -} - -float PhysicsBody3D::get_inverse_mass() const { - return 0; -} - -void PhysicsBody3D::set_collision_layer(uint32_t p_layer) { - collision_layer = p_layer; - PhysicsServer3D::get_singleton()->body_set_collision_layer(get_rid(), p_layer); -} - -uint32_t PhysicsBody3D::get_collision_layer() const { - return collision_layer; -} +void PhysicsBody3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "test_only", "safe_margin"), &PhysicsBody3D::_move, DEFVAL(false), DEFVAL(0.001)); + ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "collision", "safe_margin"), &PhysicsBody3D::test_move, DEFVAL(Variant()), DEFVAL(0.001)); -void PhysicsBody3D::set_collision_mask(uint32_t p_mask) { - collision_mask = p_mask; - PhysicsServer3D::get_singleton()->body_set_collision_mask(get_rid(), p_mask); -} + ClassDB::bind_method(D_METHOD("set_axis_lock", "axis", "lock"), &PhysicsBody3D::set_axis_lock); + ClassDB::bind_method(D_METHOD("get_axis_lock", "axis"), &PhysicsBody3D::get_axis_lock); -uint32_t PhysicsBody3D::get_collision_mask() const { - return collision_mask; -} + ClassDB::bind_method(D_METHOD("get_collision_exceptions"), &PhysicsBody3D::get_collision_exceptions); + ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &PhysicsBody3D::add_collision_exception_with); + ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &PhysicsBody3D::remove_collision_exception_with); -void PhysicsBody3D::set_collision_mask_bit(int p_bit, bool p_value) { - uint32_t mask = get_collision_mask(); - if (p_value) { - mask |= 1 << p_bit; - } else { - mask &= ~(1 << p_bit); - } - set_collision_mask(mask); + ADD_GROUP("Axis Lock", "axis_lock_"); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_linear_x"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_LINEAR_X); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_linear_y"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_LINEAR_Y); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_linear_z"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_LINEAR_Z); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_angular_x"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_ANGULAR_X); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_angular_y"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_ANGULAR_Y); + ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_angular_z"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_ANGULAR_Z); } -bool PhysicsBody3D::get_collision_mask_bit(int p_bit) const { - return get_collision_mask() & (1 << p_bit); +PhysicsBody3D::PhysicsBody3D(PhysicsServer3D::BodyMode p_mode) : + CollisionObject3D(PhysicsServer3D::get_singleton()->body_create(), false) { + set_body_mode(p_mode); } -void PhysicsBody3D::set_collision_layer_bit(int p_bit, bool p_value) { - uint32_t mask = get_collision_layer(); - if (p_value) { - mask |= 1 << p_bit; - } else { - mask &= ~(1 << p_bit); +PhysicsBody3D::~PhysicsBody3D() { + if (motion_cache.is_valid()) { + motion_cache->owner = nullptr; } - set_collision_layer(mask); -} - -bool PhysicsBody3D::get_collision_layer_bit(int p_bit) const { - return get_collision_layer() & (1 << p_bit); } TypedArray<PhysicsBody3D> PhysicsBody3D::get_collision_exceptions() { List<RID> exceptions; PhysicsServer3D::get_singleton()->body_get_collision_exceptions(get_rid(), &exceptions); Array ret; - for (List<RID>::Element *E = exceptions.front(); E; E = E->next()) { - RID body = E->get(); + for (const RID &body : exceptions) { ObjectID instance_id = PhysicsServer3D::get_singleton()->body_get_object_instance_id(body); Object *obj = ObjectDB::get_instance(instance_id); PhysicsBody3D *physics_body = Object::cast_to<PhysicsBody3D>(obj); @@ -130,40 +95,113 @@ void PhysicsBody3D::remove_collision_exception_with(Node *p_node) { PhysicsServer3D::get_singleton()->body_remove_collision_exception(get_rid(), collision_object->get_rid()); } -void PhysicsBody3D::_set_layers(uint32_t p_mask) { - set_collision_layer(p_mask); - set_collision_mask(p_mask); +Ref<KinematicCollision3D> PhysicsBody3D::_move(const Vector3 &p_motion, bool p_test_only, real_t p_margin) { + PhysicsServer3D::MotionResult result; + if (move_and_collide(p_motion, result, p_margin, p_test_only)) { + if (motion_cache.is_null()) { + motion_cache.instantiate(); + motion_cache->owner = this; + } + + motion_cache->result = result; + + return motion_cache; + } + + return Ref<KinematicCollision3D>(); } -uint32_t PhysicsBody3D::_get_layers() const { - return get_collision_layer(); +bool PhysicsBody3D::move_and_collide(const Vector3 &p_motion, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_test_only, bool p_cancel_sliding, bool p_collide_separation_ray, const Set<RID> &p_exclude) { + Transform3D gt = get_global_transform(); + bool colliding = PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_margin, &r_result, p_collide_separation_ray, p_exclude); + + // Restore direction of motion to be along original motion, + // in order to avoid sliding due to recovery, + // but only if collision depth is low enough to avoid tunneling. + if (p_cancel_sliding) { + real_t motion_length = p_motion.length(); + real_t precision = 0.001; + + if (colliding) { + // Can't just use margin as a threshold because collision depth is calculated on unsafe motion, + // so even in normal resting cases the depth can be a bit more than the margin. + precision += motion_length * (r_result.collision_unsafe_fraction - r_result.collision_safe_fraction); + + if (r_result.collision_depth > (real_t)p_margin + precision) { + p_cancel_sliding = false; + } + } + + if (p_cancel_sliding) { + // When motion is null, recovery is the resulting motion. + Vector3 motion_normal; + if (motion_length > CMP_EPSILON) { + motion_normal = p_motion / motion_length; + } + + // Check depth of recovery. + real_t projected_length = r_result.travel.dot(motion_normal); + Vector3 recovery = r_result.travel - motion_normal * projected_length; + real_t recovery_length = recovery.length(); + // Fixes cases where canceling slide causes the motion to go too deep into the ground, + // because we're only taking rest information into account and not general recovery. + if (recovery_length < (real_t)p_margin + precision) { + // Apply adjustment to motion. + r_result.travel = motion_normal * projected_length; + r_result.remainder = p_motion - r_result.travel; + } + } + } + + for (int i = 0; i < 3; i++) { + if (locked_axis & (1 << i)) { + r_result.travel[i] = 0; + } + } + + if (!p_test_only) { + gt.origin += r_result.travel; + set_global_transform(gt); + } + + return colliding; } -void PhysicsBody3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_collision_layer", "layer"), &PhysicsBody3D::set_collision_layer); - ClassDB::bind_method(D_METHOD("get_collision_layer"), &PhysicsBody3D::get_collision_layer); +bool PhysicsBody3D::test_move(const Transform3D &p_from, const Vector3 &p_motion, const Ref<KinematicCollision3D> &r_collision, real_t p_margin) { + ERR_FAIL_COND_V(!is_inside_tree(), false); - ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &PhysicsBody3D::set_collision_mask); - ClassDB::bind_method(D_METHOD("get_collision_mask"), &PhysicsBody3D::get_collision_mask); + PhysicsServer3D::MotionResult *r = nullptr; + if (r_collision.is_valid()) { + // Needs const_cast because method bindings don't support non-const Ref. + r = const_cast<PhysicsServer3D::MotionResult *>(&r_collision->result); + } - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &PhysicsBody3D::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &PhysicsBody3D::get_collision_mask_bit); + return PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), p_from, p_motion, p_margin, r); +} + +void PhysicsBody3D::set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock) { + if (p_lock) { + locked_axis |= p_axis; + } else { + locked_axis &= (~p_axis); + } + PhysicsServer3D::get_singleton()->body_set_axis_lock(get_rid(), p_axis, p_lock); +} - ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &PhysicsBody3D::set_collision_layer_bit); - ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &PhysicsBody3D::get_collision_layer_bit); +bool PhysicsBody3D::get_axis_lock(PhysicsServer3D::BodyAxis p_axis) const { + return (locked_axis & p_axis); +} - ClassDB::bind_method(D_METHOD("_set_layers", "mask"), &PhysicsBody3D::_set_layers); - ClassDB::bind_method(D_METHOD("_get_layers"), &PhysicsBody3D::_get_layers); +Vector3 PhysicsBody3D::get_linear_velocity() const { + return Vector3(); +} - 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"); +Vector3 PhysicsBody3D::get_angular_velocity() const { + return Vector3(); } -PhysicsBody3D::PhysicsBody3D(PhysicsServer3D::BodyMode p_mode) : - CollisionObject3D(PhysicsServer3D::get_singleton()->body_create(p_mode), false) { - collision_layer = 1; - collision_mask = 1; +real_t PhysicsBody3D::get_inverse_mass() const { + return 0; } void StaticBody3D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { @@ -185,14 +223,102 @@ Ref<PhysicsMaterial> StaticBody3D::get_physics_material_override() const { return physics_material_override; } +void StaticBody3D::set_kinematic_motion_enabled(bool p_enabled) { + if (p_enabled == kinematic_motion) { + return; + } + + kinematic_motion = p_enabled; + + if (kinematic_motion) { + set_body_mode(PhysicsServer3D::BODY_MODE_KINEMATIC); + } else { + set_body_mode(PhysicsServer3D::BODY_MODE_STATIC); + } + +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + update_configuration_warnings(); + return; + } +#endif + + _update_kinematic_motion(); +} + +bool StaticBody3D::is_kinematic_motion_enabled() const { + return kinematic_motion; +} + void StaticBody3D::set_constant_linear_velocity(const Vector3 &p_vel) { constant_linear_velocity = p_vel; - PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY, constant_linear_velocity); + + if (kinematic_motion) { + _update_kinematic_motion(); + } else { + PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_LINEAR_VELOCITY, constant_linear_velocity); + } +} + +void StaticBody3D::set_sync_to_physics(bool p_enable) { + if (sync_to_physics == p_enable) { + return; + } + + sync_to_physics = p_enable; + +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + update_configuration_warnings(); + return; + } +#endif + + if (kinematic_motion) { + _update_kinematic_motion(); + } +} + +bool StaticBody3D::is_sync_to_physics_enabled() const { + return sync_to_physics; +} + +void StaticBody3D::_direct_state_changed(Object *p_state) { + PhysicsDirectBodyState3D *state = Object::cast_to<PhysicsDirectBodyState3D>(p_state); + ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument"); + + linear_velocity = state->get_linear_velocity(); + angular_velocity = state->get_angular_velocity(); + + if (!sync_to_physics) { + return; + } + + last_valid_transform = state->get_transform(); + set_notify_local_transform(false); + set_global_transform(last_valid_transform); + set_notify_local_transform(true); + _on_transform_changed(); +} + +TypedArray<String> StaticBody3D::get_configuration_warnings() const { + TypedArray<String> warnings = PhysicsBody3D::get_configuration_warnings(); + + if (sync_to_physics && !kinematic_motion) { + warnings.push_back(TTR("Sync to physics works only when kinematic motion is enabled.")); + } + + return warnings; } void StaticBody3D::set_constant_angular_velocity(const Vector3 &p_vel) { constant_angular_velocity = p_vel; - PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY, constant_angular_velocity); + + if (kinematic_motion) { + _update_kinematic_motion(); + } else { + PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::BODY_STATE_ANGULAR_VELOCITY, constant_angular_velocity); + } } Vector3 StaticBody3D::get_constant_linear_velocity() const { @@ -203,30 +329,108 @@ Vector3 StaticBody3D::get_constant_angular_velocity() const { return constant_angular_velocity; } +Vector3 StaticBody3D::get_linear_velocity() const { + return linear_velocity; +} + +Vector3 StaticBody3D::get_angular_velocity() const { + return angular_velocity; +} + +void StaticBody3D::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + last_valid_transform = get_global_transform(); + } break; + + case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { + // Used by sync to physics, send the new transform to the physics... + Transform3D new_transform = get_global_transform(); + + real_t delta_time = get_physics_process_delta_time(); + 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(); + } + + PhysicsServer3D::get_singleton()->body_set_state(get_rid(), PhysicsServer3D::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); + _on_transform_changed(); + } break; + + case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + return; + } +#endif + + ERR_FAIL_COND(!kinematic_motion); + + Transform3D new_transform = get_global_transform(); + + real_t delta_time = get_physics_process_delta_time(); + 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(); + } + + if (sync_to_physics) { + // Propagate transform change to node. + set_global_transform(new_transform); + } else { + 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(); + } + } break; + } +} + void StaticBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_constant_linear_velocity", "vel"), &StaticBody3D::set_constant_linear_velocity); ClassDB::bind_method(D_METHOD("set_constant_angular_velocity", "vel"), &StaticBody3D::set_constant_angular_velocity); ClassDB::bind_method(D_METHOD("get_constant_linear_velocity"), &StaticBody3D::get_constant_linear_velocity); ClassDB::bind_method(D_METHOD("get_constant_angular_velocity"), &StaticBody3D::get_constant_angular_velocity); + ClassDB::bind_method(D_METHOD("set_kinematic_motion_enabled", "enabled"), &StaticBody3D::set_kinematic_motion_enabled); + ClassDB::bind_method(D_METHOD("is_kinematic_motion_enabled"), &StaticBody3D::is_kinematic_motion_enabled); + ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &StaticBody3D::set_physics_material_override); ClassDB::bind_method(D_METHOD("get_physics_material_override"), &StaticBody3D::get_physics_material_override); - ClassDB::bind_method(D_METHOD("get_collision_exceptions"), &PhysicsBody3D::get_collision_exceptions); - ClassDB::bind_method(D_METHOD("add_collision_exception_with", "body"), &PhysicsBody3D::add_collision_exception_with); - ClassDB::bind_method(D_METHOD("remove_collision_exception_with", "body"), &PhysicsBody3D::remove_collision_exception_with); + ClassDB::bind_method(D_METHOD("set_sync_to_physics", "enable"), &StaticBody3D::set_sync_to_physics); + ClassDB::bind_method(D_METHOD("is_sync_to_physics_enabled"), &StaticBody3D::is_sync_to_physics_enabled); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_linear_velocity"), "set_constant_linear_velocity", "get_constant_linear_velocity"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "constant_angular_velocity"), "set_constant_angular_velocity", "get_constant_angular_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "kinematic_motion"), "set_kinematic_motion_enabled", "is_kinematic_motion_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sync_to_physics"), "set_sync_to_physics", "is_sync_to_physics_enabled"); } StaticBody3D::StaticBody3D() : PhysicsBody3D(PhysicsServer3D::BODY_MODE_STATIC) { } -StaticBody3D::~StaticBody3D() {} - void StaticBody3D::_reload_physics_characteristics() { if (physics_material_override.is_null()) { PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_BOUNCE, 0); @@ -237,6 +441,35 @@ void StaticBody3D::_reload_physics_characteristics() { } } +void StaticBody3D::_update_kinematic_motion() { +#ifdef TOOLS_ENABLED + if (Engine::get_singleton()->is_editor_hint()) { + return; + } +#endif + + if (kinematic_motion && sync_to_physics) { + set_only_update_transform_changes(true); + set_notify_local_transform(true); + } else { + set_only_update_transform_changes(false); + set_notify_local_transform(false); + } + + bool needs_physics_process = false; + if (kinematic_motion) { + PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &StaticBody3D::_direct_state_changed)); + + if (!constant_angular_velocity.is_equal_approx(Vector3()) || !constant_linear_velocity.is_equal_approx(Vector3())) { + needs_physics_process = true; + } + } else { + PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), Callable()); + } + + set_physics_process_internal(needs_physics_process); +} + void RigidBody3D::_body_enter_tree(ObjectID p_id) { Object *obj = ObjectDB::get_instance(p_id); Node *node = Object::cast_to<Node>(obj); @@ -254,7 +487,7 @@ void RigidBody3D::_body_enter_tree(ObjectID p_id) { emit_signal(SceneStringNames::get_singleton()->body_entered, node); for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->body_shape_entered, p_id, node, E->get().shapes[i].body_shape, E->get().shapes[i].local_shape); + emit_signal(SceneStringNames::get_singleton()->body_shape_entered, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].local_shape); } contact_monitor->locked = false; @@ -275,13 +508,13 @@ void RigidBody3D::_body_exit_tree(ObjectID p_id) { emit_signal(SceneStringNames::get_singleton()->body_exited, node); for (int i = 0; i < E->get().shapes.size(); i++) { - emit_signal(SceneStringNames::get_singleton()->body_shape_exited, p_id, node, E->get().shapes[i].body_shape, E->get().shapes[i].local_shape); + emit_signal(SceneStringNames::get_singleton()->body_shape_exited, E->get().rid, node, E->get().shapes[i].body_shape, E->get().shapes[i].local_shape); } contact_monitor->locked = false; } -void RigidBody3D::_body_inout(int p_status, ObjectID p_instance, int p_body_shape, int p_local_shape) { +void RigidBody3D::_body_inout(int p_status, const RID &p_body, ObjectID p_instance, int p_body_shape, int p_local_shape) { bool body_in = p_status == 1; ObjectID objid = p_instance; @@ -296,6 +529,7 @@ void RigidBody3D::_body_inout(int p_status, ObjectID p_instance, int p_body_shap if (body_in) { if (!E) { E = contact_monitor->body_map.insert(objid, BodyState()); + E->get().rid = p_body; //E->get().rc=0; E->get().in_tree = node && node->is_inside_tree(); if (node) { @@ -312,7 +546,7 @@ void RigidBody3D::_body_inout(int p_status, ObjectID p_instance, int p_body_shap } if (E->get().in_tree) { - emit_signal(SceneStringNames::get_singleton()->body_shape_entered, objid, node, p_body_shape, p_local_shape); + emit_signal(SceneStringNames::get_singleton()->body_shape_entered, p_body, node, p_body_shape, p_local_shape); } } else { @@ -324,7 +558,7 @@ void RigidBody3D::_body_inout(int p_status, ObjectID p_instance, int p_body_shap bool in_tree = E->get().in_tree; - if (E->get().shapes.empty()) { + if (E->get().shapes.is_empty()) { if (node) { node->disconnect(SceneStringNames::get_singleton()->tree_entered, callable_mp(this, &RigidBody3D::_body_enter_tree)); node->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &RigidBody3D::_body_exit_tree)); @@ -336,20 +570,22 @@ void RigidBody3D::_body_inout(int p_status, ObjectID p_instance, int p_body_shap contact_monitor->body_map.erase(E); } if (node && in_tree) { - emit_signal(SceneStringNames::get_singleton()->body_shape_exited, objid, obj, p_body_shape, p_local_shape); + emit_signal(SceneStringNames::get_singleton()->body_shape_exited, p_body, obj, p_body_shape, p_local_shape); } } } struct _RigidBodyInOut { + RID rid; ObjectID id; - int shape; - int local_shape; + int shape = 0; + int local_shape = 0; }; void RigidBody3D::_direct_state_changed(Object *p_state) { #ifdef DEBUG_ENABLED state = Object::cast_to<PhysicsDirectBodyState3D>(p_state); + ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument"); #else state = (PhysicsDirectBodyState3D *)p_state; //trust it #endif @@ -363,10 +599,11 @@ void RigidBody3D::_direct_state_changed(Object *p_state) { sleeping = state->is_sleeping(); emit_signal(SceneStringNames::get_singleton()->sleeping_state_changed); } - if (get_script_instance()) { - get_script_instance()->call("_integrate_forces", state); - } + + GDVIRTUAL_CALL(_integrate_forces, state); + set_ignore_transform_notification(false); + _on_transform_changed(); if (contact_monitor) { contact_monitor->locked = true; @@ -388,6 +625,7 @@ void RigidBody3D::_direct_state_changed(Object *p_state) { //put the ones to add for (int i = 0; i < state->get_contact_count(); i++) { + RID rid = state->get_contact_collider(i); ObjectID obj = state->get_contact_collider_id(i); int local_shape = state->get_contact_local_shape(i); int shape = state->get_contact_collider_shape(i); @@ -396,6 +634,7 @@ void RigidBody3D::_direct_state_changed(Object *p_state) { Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.find(obj); if (!E) { + toadd[toadd_count].rid = rid; toadd[toadd_count].local_shape = local_shape; toadd[toadd_count].id = obj; toadd[toadd_count].shape = shape; @@ -406,6 +645,7 @@ void RigidBody3D::_direct_state_changed(Object *p_state) { ShapePair sp(shape, local_shape); int idx = E->get().shapes.find(sp); if (idx == -1) { + toadd[toadd_count].rid = rid; toadd[toadd_count].local_shape = local_shape; toadd[toadd_count].id = obj; toadd[toadd_count].shape = shape; @@ -421,6 +661,7 @@ void RigidBody3D::_direct_state_changed(Object *p_state) { for (Map<ObjectID, BodyState>::Element *E = contact_monitor->body_map.front(); E; E = E->next()) { for (int i = 0; i < E->get().shapes.size(); i++) { if (!E->get().shapes[i].tagged) { + toremove[toremove_count].rid = E->get().rid; toremove[toremove_count].body_id = E->key(); toremove[toremove_count].pair = E->get().shapes[i]; toremove_count++; @@ -428,16 +669,16 @@ void RigidBody3D::_direct_state_changed(Object *p_state) { } } - //process remotions + //process removals for (int i = 0; i < toremove_count; i++) { - _body_inout(0, toremove[i].body_id, toremove[i].pair.body_shape, toremove[i].pair.local_shape); + _body_inout(0, toremove[i].rid, toremove[i].body_id, toremove[i].pair.body_shape, toremove[i].pair.local_shape); } - //process aditions + //process additions for (int i = 0; i < toadd_count; i++) { - _body_inout(1, toadd[i].id, toadd[i].shape, toadd[i].local_shape); + _body_inout(1, toremove[i].rid, toadd[i].id, toadd[i].shape, toadd[i].local_shape); } contact_monitor->locked = false; @@ -448,40 +689,39 @@ 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_warning(); - } + case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { + if (Engine::get_singleton()->is_editor_hint()) { + update_configuration_warnings(); + } + } break; } - #endif } void RigidBody3D::set_mode(Mode p_mode) { mode = p_mode; switch (p_mode) { - case MODE_RIGID: { - PhysicsServer3D::get_singleton()->body_set_mode(get_rid(), PhysicsServer3D::BODY_MODE_RIGID); + case 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_CHARACTER: { - PhysicsServer3D::get_singleton()->body_set_mode(get_rid(), PhysicsServer3D::BODY_MODE_CHARACTER); - + case 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_warning(); + update_configuration_warnings(); } RigidBody3D::Mode RigidBody3D::get_mode() const { @@ -491,8 +731,6 @@ RigidBody3D::Mode RigidBody3D::get_mode() const { void RigidBody3D::set_mass(real_t p_mass) { ERR_FAIL_COND(p_mass <= 0); mass = p_mass; - _change_notify("mass"); - _change_notify("weight"); PhysicsServer3D::get_singleton()->body_set_param(get_rid(), PhysicsServer3D::BODY_PARAM_MASS, mass); } @@ -500,14 +738,6 @@ real_t RigidBody3D::get_mass() const { return mass; } -void RigidBody3D::set_weight(real_t p_weight) { - set_mass(p_weight / real_t(GLOBAL_DEF("physics/3d/default_gravity", 9.8))); -} - -real_t RigidBody3D::get_weight() const { - return mass * real_t(GLOBAL_DEF("physics/3d/default_gravity", 9.8)); -} - void RigidBody3D::set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override) { if (physics_material_override.is_valid()) { if (physics_material_override->is_connected(CoreStringNames::get_singleton()->changed, callable_mp(this, &RigidBody3D::_reload_physics_characteristics))) { @@ -595,7 +825,7 @@ Vector3 RigidBody3D::get_angular_velocity() const { return angular_velocity; } -Basis RigidBody3D::get_inverse_inertia_tensor() { +Basis RigidBody3D::get_inverse_inertia_tensor() const { return inverse_inertia_tensor; } @@ -705,14 +935,6 @@ bool RigidBody3D::is_contact_monitor_enabled() const { return contact_monitor != nullptr; } -void RigidBody3D::set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock) { - PhysicsServer3D::get_singleton()->body_set_axis_lock(get_rid(), p_axis, p_lock); -} - -bool RigidBody3D::get_axis_lock(PhysicsServer3D::BodyAxis p_axis) const { - return PhysicsServer3D::get_singleton()->body_is_axis_locked(get_rid(), p_axis); -} - Array RigidBody3D::get_colliding_bodies() const { ERR_FAIL_COND_V(!contact_monitor, Array()); @@ -731,19 +953,16 @@ Array RigidBody3D::get_colliding_bodies() const { return ret; } -String RigidBody3D::get_configuration_warning() const { - Transform t = get_transform(); +TypedArray<String> RigidBody3D::get_configuration_warnings() const { + Transform3D t = get_transform(); - String warning = CollisionObject3D::get_configuration_warning(); + TypedArray<String> warnings = Node::get_configuration_warnings(); - if ((get_mode() == MODE_RIGID || get_mode() == MODE_CHARACTER) && (ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(2).length() - 1.0) > 0.05)) { - if (!warning.empty()) { - warning += "\n\n"; - } - warning += TTR("Size changes to RigidBody3D (in character or rigid modes) will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."); + if ((get_mode() == MODE_DYNAMIC || get_mode() == MODE_DYNAMIC_LOCKED) && (ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(2).length() - 1.0) > 0.05)) { + warnings.push_back(TTR("Size changes to RigidBody3D (in dynamic modes) will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); } - return warning; + return warnings; } void RigidBody3D::_bind_methods() { @@ -753,9 +972,6 @@ void RigidBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_mass", "mass"), &RigidBody3D::set_mass); ClassDB::bind_method(D_METHOD("get_mass"), &RigidBody3D::get_mass); - ClassDB::bind_method(D_METHOD("set_weight", "weight"), &RigidBody3D::set_weight); - ClassDB::bind_method(D_METHOD("get_weight"), &RigidBody3D::get_weight); - ClassDB::bind_method(D_METHOD("set_physics_material_override", "physics_material_override"), &RigidBody3D::set_physics_material_override); ClassDB::bind_method(D_METHOD("get_physics_material_override"), &RigidBody3D::get_physics_material_override); @@ -804,18 +1020,12 @@ void RigidBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_can_sleep", "able_to_sleep"), &RigidBody3D::set_can_sleep); ClassDB::bind_method(D_METHOD("is_able_to_sleep"), &RigidBody3D::is_able_to_sleep); - ClassDB::bind_method(D_METHOD("_direct_state_changed"), &RigidBody3D::_direct_state_changed); - - ClassDB::bind_method(D_METHOD("set_axis_lock", "axis", "lock"), &RigidBody3D::set_axis_lock); - ClassDB::bind_method(D_METHOD("get_axis_lock", "axis"), &RigidBody3D::get_axis_lock); - ClassDB::bind_method(D_METHOD("get_colliding_bodies"), &RigidBody3D::get_colliding_bodies); - BIND_VMETHOD(MethodInfo("_integrate_forces", PropertyInfo(Variant::OBJECT, "state", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsDirectBodyState3D"))); + GDVIRTUAL_BIND(_integrate_forces, "state"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Rigid,Static,Character,Kinematic"), "set_mode", "get_mode"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01"), "set_mass", "get_mass"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "weight", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01", PROPERTY_USAGE_EDITOR), "set_weight", "get_weight"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Dynamic,Static,DynamicLocked,Kinematic"), "set_mode", "get_mode"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,65535,0.01,exp"), "set_mass", "get_mass"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "physics_material_override", PROPERTY_HINT_RESOURCE_TYPE, "PhysicsMaterial"), "set_physics_material_override", "get_physics_material_override"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_scale", PROPERTY_HINT_RANGE, "-128,128,0.01"), "set_gravity_scale", "get_gravity_scale"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "custom_integrator"), "set_use_custom_integrator", "is_using_custom_integrator"); @@ -824,13 +1034,6 @@ void RigidBody3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "contact_monitor"), "set_contact_monitor", "is_contact_monitor_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "sleeping"), "set_sleeping", "is_sleeping"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "can_sleep"), "set_can_sleep", "is_able_to_sleep"); - ADD_GROUP("Axis Lock", "axis_lock_"); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_linear_x"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_LINEAR_X); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_linear_y"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_LINEAR_Y); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_linear_z"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_LINEAR_Z); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_angular_x"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_ANGULAR_X); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_angular_y"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_ANGULAR_Y); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_angular_z"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_ANGULAR_Z); ADD_GROUP("Linear", "linear_"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "linear_velocity"), "set_linear_velocity", "get_linear_velocity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_linear_damp", "get_linear_damp"); @@ -838,39 +1041,21 @@ void RigidBody3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "angular_velocity"), "set_angular_velocity", "get_angular_velocity"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_angular_damp", "get_angular_damp"); - ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); - ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::INT, "body_id"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); + ADD_SIGNAL(MethodInfo("body_shape_entered", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); + ADD_SIGNAL(MethodInfo("body_shape_exited", PropertyInfo(Variant::RID, "body_rid"), PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"), PropertyInfo(Variant::INT, "body_shape"), PropertyInfo(Variant::INT, "local_shape"))); ADD_SIGNAL(MethodInfo("body_entered", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("body_exited", PropertyInfo(Variant::OBJECT, "body", PROPERTY_HINT_RESOURCE_TYPE, "Node"))); ADD_SIGNAL(MethodInfo("sleeping_state_changed")); - BIND_ENUM_CONSTANT(MODE_RIGID); + BIND_ENUM_CONSTANT(MODE_DYNAMIC); BIND_ENUM_CONSTANT(MODE_STATIC); - BIND_ENUM_CONSTANT(MODE_CHARACTER); + BIND_ENUM_CONSTANT(MODE_DYNAMIC_LOCKED); BIND_ENUM_CONSTANT(MODE_KINEMATIC); } RigidBody3D::RigidBody3D() : - PhysicsBody3D(PhysicsServer3D::BODY_MODE_RIGID) { - mode = MODE_RIGID; - - mass = 1; - max_contacts_reported = 0; - state = nullptr; - - gravity_scale = 1; - linear_damp = -1; - angular_damp = -1; - - //angular_velocity=0; - sleeping = false; - ccd = false; - - custom_integrator = false; - contact_monitor = nullptr; - can_sleep = true; - - PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed"); + PhysicsBody3D(PhysicsServer3D::BODY_MODE_DYNAMIC) { + PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &RigidBody3D::_direct_state_changed)); } RigidBody3D::~RigidBody3D() { @@ -889,379 +1074,357 @@ void RigidBody3D::_reload_physics_characteristics() { } } -////////////////////////////////////////////////////// -////////////////////////// - -Ref<KinematicCollision3D> KinematicBody3D::_move(const Vector3 &p_motion, bool p_infinite_inertia, bool p_exclude_raycast_shapes, bool p_test_only) { - Collision col; - if (move_and_collide(p_motion, p_infinite_inertia, col, p_exclude_raycast_shapes, p_test_only)) { - if (motion_cache.is_null()) { - motion_cache.instance(); - motion_cache->owner = this; - } - - motion_cache->collision = col; - - return motion_cache; - } - - return Ref<KinematicCollision3D>(); -} - -Vector3 KinematicBody3D::get_linear_velocity() const { - return linear_velocity; -} +/////////////////////////////////////// -Vector3 KinematicBody3D::get_angular_velocity() const { - return angular_velocity; -} +//so, if you pass 45 as limit, avoid numerical precision errors when angle is 45. +#define FLOOR_ANGLE_THRESHOLD 0.01 -bool KinematicBody3D::move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_exclude_raycast_shapes, bool p_test_only) { - Transform gt = get_global_transform(); - PhysicsServer3D::MotionResult result; - bool colliding = PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), gt, p_motion, p_infinite_inertia, &result, p_exclude_raycast_shapes); +bool CharacterBody3D::move_and_slide() { + bool was_on_floor = on_floor; - if (colliding) { - r_collision.collider_metadata = result.collider_metadata; - r_collision.collider_shape = result.collider_shape; - r_collision.collider_vel = result.collider_velocity; - r_collision.collision = result.collision_point; - r_collision.normal = result.collision_normal; - r_collision.collider = result.collider_id; - r_collision.collider_rid = result.collider; - r_collision.travel = result.motion; - r_collision.remainder = result.remainder; - r_collision.local_shape = result.collision_local_shape; - } + // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky + float delta = Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time(); for (int i = 0; i < 3; i++) { if (locked_axis & (1 << i)) { - result.motion[i] = 0; + linear_velocity[i] = 0.0; } } - if (!p_test_only) { - gt.origin += result.motion; - set_global_transform(gt); - } - - return colliding; -} - -//so, if you pass 45 as limit, avoid numerical precision errors when angle is 45. -#define FLOOR_ANGLE_THRESHOLD 0.01 - -Vector3 KinematicBody3D::move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_up_direction, bool p_stop_on_slope, int p_max_slides, float p_floor_max_angle, bool p_infinite_inertia) { - Vector3 body_velocity = p_linear_velocity; - Vector3 body_velocity_normal = body_velocity.normalized(); - Vector3 up_direction = p_up_direction.normalized(); - - for (int i = 0; i < 3; i++) { - if (locked_axis & (1 << i)) { - body_velocity[i] = 0; + Vector3 current_floor_velocity = floor_velocity; + if ((on_floor || on_wall) && on_floor_body.is_valid()) { + //this approach makes sure there is less delay between the actual body velocity and the one we saved + PhysicsDirectBodyState3D *bs = PhysicsServer3D::get_singleton()->body_get_direct_state(on_floor_body); + if (bs) { + Transform3D gt = get_global_transform(); + Vector3 local_position = gt.origin - bs->get_transform().origin; + current_floor_velocity = bs->get_velocity_at_local_position(local_position); } } - // Hack in order to work with calling from _process as well as from _physics_process; calling from thread is risky - Vector3 motion = (floor_velocity + body_velocity) * (Engine::get_singleton()->is_in_physics_frame() ? get_physics_process_delta_time() : get_process_delta_time()); - + motion_results.clear(); on_floor = false; - on_floor_body = RID(); on_ceiling = false; on_wall = false; - colliders.clear(); floor_normal = Vector3(); floor_velocity = Vector3(); - while (p_max_slides) { - Collision collision; - bool found_collision = false; + if (!current_floor_velocity.is_equal_approx(Vector3()) && on_floor_body.is_valid()) { + PhysicsServer3D::MotionResult floor_result; + Set<RID> exclude; + exclude.insert(on_floor_body); + if (move_and_collide(current_floor_velocity * delta, floor_result, margin, false, false, false, exclude)) { + motion_results.push_back(floor_result); + _set_collision_direction(floor_result); + } + } - for (int i = 0; i < 2; ++i) { - bool collided; - if (i == 0) { //collide - collided = move_and_collide(motion, p_infinite_inertia, collision); - if (!collided) { - motion = Vector3(); //clear because no collision happened and motion completed - } - } else { //separate raycasts (if any) - collided = separate_raycast_shapes(p_infinite_inertia, collision); - if (collided) { - collision.remainder = motion; //keep - collision.travel = Vector3(); + on_floor_body = RID(); + Vector3 motion = linear_velocity * delta; + + // No sliding on first attempt to keep floor motion stable when possible, + // when stop on slope is enabled. + bool sliding_enabled = !floor_stop_on_slope; + + for (int iteration = 0; iteration < max_slides; ++iteration) { + PhysicsServer3D::MotionResult result; + bool collided = move_and_collide(motion, result, margin, false, !sliding_enabled); + if (collided) { + motion_results.push_back(result); + _set_collision_direction(result); + + if (on_floor && floor_stop_on_slope && (linear_velocity.normalized() + up_direction).length() < 0.01) { + Transform3D gt = get_global_transform(); + if (result.travel.length() > margin) { + gt.origin -= result.travel.slide(up_direction); + } else { + gt.origin -= result.travel; } + set_global_transform(gt); + linear_velocity = Vector3(); + motion = Vector3(); + break; } - if (collided) { - found_collision = true; - - colliders.push_back(collision); - motion = collision.remainder; + if (result.remainder.is_equal_approx(Vector3())) { + motion = Vector3(); + break; + } - if (up_direction == Vector3()) { - //all is a wall - on_wall = true; + if (sliding_enabled || !on_floor) { + Vector3 slide_motion = result.remainder.slide(result.collision_normal); + if (slide_motion.dot(linear_velocity) > 0.0) { + motion = slide_motion; } else { - if (Math::acos(collision.normal.dot(up_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor - - on_floor = true; - floor_normal = collision.normal; - on_floor_body = collision.collider_rid; - floor_velocity = collision.collider_vel; - - if (p_stop_on_slope) { - if ((body_velocity_normal + up_direction).length() < 0.01 && collision.travel.length() < 1) { - Transform gt = get_global_transform(); - gt.origin -= collision.travel.slide(up_direction); - set_global_transform(gt); - return Vector3(); - } - } - } else if (Math::acos(collision.normal.dot(-up_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling - on_ceiling = true; - } else { - on_wall = true; - } + motion = Vector3(); } + } else { + motion = result.remainder; + } + } + + sliding_enabled = true; - motion = motion.slide(collision.normal); - body_velocity = body_velocity.slide(collision.normal); + if (!collided || motion.is_equal_approx(Vector3())) { + break; + } + } - for (int j = 0; j < 3; j++) { - if (locked_axis & (1 << j)) { - body_velocity[j] = 0; + if (was_on_floor && !on_floor && !snap.is_equal_approx(Vector3())) { + // Apply snap. + Transform3D gt = get_global_transform(); + PhysicsServer3D::MotionResult result; + if (move_and_collide(snap, result, margin, true, false, true)) { + bool apply = true; + if (up_direction != Vector3()) { + if (result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { + on_floor = true; + floor_normal = result.collision_normal; + on_floor_body = result.collider; + floor_velocity = result.collider_velocity; + if (floor_stop_on_slope) { + // move and collide may stray the object a bit because of pre un-stucking, + // so only ensure that motion happens on floor direction in this case. + if (result.travel.length() > margin) { + result.travel = result.travel.project(up_direction); + } else { + result.travel = Vector3(); + } } + } else { + apply = false; //snapped with floor direction, but did not snap to a floor, do not snap. } } + if (apply) { + gt.origin += result.travel; + set_global_transform(gt); + } } + } - if (!found_collision || motion == Vector3()) { - break; - } + if (!on_floor && !on_wall) { + // Add last platform velocity when just left a moving platform. + linear_velocity += current_floor_velocity; + } - --p_max_slides; + // Reset the gravity accumulation when touching the ground. + if (on_floor && linear_velocity.dot(up_direction) <= 0) { + linear_velocity = linear_velocity.slide(up_direction); } - return body_velocity; + return motion_results.size() > 0; } -Vector3 KinematicBody3D::move_and_slide_with_snap(const Vector3 &p_linear_velocity, const Vector3 &p_snap, const Vector3 &p_up_direction, bool p_stop_on_slope, int p_max_slides, float p_floor_max_angle, bool p_infinite_inertia) { - Vector3 up_direction = p_up_direction.normalized(); - bool was_on_floor = on_floor; - - Vector3 ret = move_and_slide(p_linear_velocity, up_direction, p_stop_on_slope, p_max_slides, p_floor_max_angle, p_infinite_inertia); - if (!was_on_floor || p_snap == Vector3()) { - return ret; - } - - Collision col; - Transform gt = get_global_transform(); - - if (move_and_collide(p_snap, p_infinite_inertia, col, false, true)) { - bool apply = true; - if (up_direction != Vector3()) { - if (Math::acos(col.normal.dot(up_direction)) <= p_floor_max_angle + FLOOR_ANGLE_THRESHOLD) { - on_floor = true; - floor_normal = col.normal; - on_floor_body = col.collider_rid; - floor_velocity = col.collider_vel; - if (p_stop_on_slope) { - // move and collide may stray the object a bit because of pre un-stucking, - // so only ensure that motion happens on floor direction in this case. - col.travel = col.travel.project(up_direction); - } - } else { - apply = false; //snapped with floor direction, but did not snap to a floor, do not snap. +void CharacterBody3D::_set_collision_direction(const PhysicsServer3D::MotionResult &p_result) { + if (up_direction == Vector3()) { + //all is a wall + on_wall = true; + } else { + if (p_result.get_angle(up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //floor + on_floor = true; + floor_normal = p_result.collision_normal; + on_floor_body = p_result.collider; + floor_velocity = p_result.collider_velocity; + } else if (p_result.get_angle(-up_direction) <= floor_max_angle + FLOOR_ANGLE_THRESHOLD) { //ceiling + on_ceiling = true; + } else { + on_wall = true; + // Don't apply wall velocity when the collider is a CharacterBody3D. + if (Object::cast_to<CharacterBody3D>(ObjectDB::get_instance(p_result.collider_id)) == nullptr) { + on_floor_body = p_result.collider; + floor_velocity = p_result.collider_velocity; } } - if (apply) { - gt.origin += col.travel; - set_global_transform(gt); - } } - - return ret; } -bool KinematicBody3D::is_on_floor() const { - return on_floor; +void CharacterBody3D::set_safe_margin(real_t p_margin) { + margin = p_margin; } -bool KinematicBody3D::is_on_wall() const { - return on_wall; +real_t CharacterBody3D::get_safe_margin() const { + return margin; } -bool KinematicBody3D::is_on_ceiling() const { - return on_ceiling; +Vector3 CharacterBody3D::get_linear_velocity() const { + return linear_velocity; } -Vector3 KinematicBody3D::get_floor_normal() const { - return floor_normal; +void CharacterBody3D::set_linear_velocity(const Vector3 &p_velocity) { + linear_velocity = p_velocity; } -Vector3 KinematicBody3D::get_floor_velocity() const { - return floor_velocity; +bool CharacterBody3D::is_on_floor() const { + return on_floor; } -bool KinematicBody3D::test_move(const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia) { - ERR_FAIL_COND_V(!is_inside_tree(), false); - - return PhysicsServer3D::get_singleton()->body_test_motion(get_rid(), p_from, p_motion, p_infinite_inertia); +bool CharacterBody3D::is_on_floor_only() const { + return on_floor && !on_wall && !on_ceiling; } -bool KinematicBody3D::separate_raycast_shapes(bool p_infinite_inertia, Collision &r_collision) { - PhysicsServer3D::SeparationResult sep_res[8]; //max 8 rays - - Transform gt = get_global_transform(); - - Vector3 recover; - int hits = PhysicsServer3D::get_singleton()->body_test_ray_separation(get_rid(), gt, p_infinite_inertia, recover, sep_res, 8, margin); - int deepest = -1; - float deepest_depth; - for (int i = 0; i < hits; i++) { - if (deepest == -1 || sep_res[i].collision_depth > deepest_depth) { - deepest = i; - deepest_depth = sep_res[i].collision_depth; - } - } - - gt.origin += recover; - set_global_transform(gt); +bool CharacterBody3D::is_on_wall() const { + return on_wall; +} - if (deepest != -1) { - r_collision.collider = sep_res[deepest].collider_id; - r_collision.collider_metadata = sep_res[deepest].collider_metadata; - r_collision.collider_shape = sep_res[deepest].collider_shape; - r_collision.collider_vel = sep_res[deepest].collider_velocity; - r_collision.collision = sep_res[deepest].collision_point; - r_collision.normal = sep_res[deepest].collision_normal; - r_collision.local_shape = sep_res[deepest].collision_local_shape; - r_collision.travel = recover; - r_collision.remainder = Vector3(); +bool CharacterBody3D::is_on_wall_only() const { + return on_wall && !on_floor && !on_ceiling; +} - return true; - } else { - return false; - } +bool CharacterBody3D::is_on_ceiling() const { + return on_ceiling; } -void KinematicBody3D::set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock) { - if (p_lock) { - locked_axis |= p_axis; - } else { - locked_axis &= (~p_axis); - } - PhysicsServer3D::get_singleton()->body_set_axis_lock(get_rid(), p_axis, p_lock); +bool CharacterBody3D::is_on_ceiling_only() const { + return on_ceiling && !on_floor && !on_wall; } -bool KinematicBody3D::get_axis_lock(PhysicsServer3D::BodyAxis p_axis) const { - return PhysicsServer3D::get_singleton()->body_is_axis_locked(get_rid(), p_axis); +Vector3 CharacterBody3D::get_floor_normal() const { + return floor_normal; } -void KinematicBody3D::set_safe_margin(float p_margin) { - margin = p_margin; - PhysicsServer3D::get_singleton()->body_set_kinematic_safe_margin(get_rid(), margin); +real_t CharacterBody3D::get_floor_angle(const Vector3 &p_up_direction) const { + ERR_FAIL_COND_V(p_up_direction == Vector3(), 0); + return Math::acos(floor_normal.dot(p_up_direction)); } -float KinematicBody3D::get_safe_margin() const { - return margin; +Vector3 CharacterBody3D::get_platform_velocity() const { + return floor_velocity; } -int KinematicBody3D::get_slide_count() const { - return colliders.size(); +int CharacterBody3D::get_slide_collision_count() const { + return motion_results.size(); } -KinematicBody3D::Collision KinematicBody3D::get_slide_collision(int p_bounce) const { - ERR_FAIL_INDEX_V(p_bounce, colliders.size(), Collision()); - return colliders[p_bounce]; +PhysicsServer3D::MotionResult CharacterBody3D::get_slide_collision(int p_bounce) const { + ERR_FAIL_INDEX_V(p_bounce, motion_results.size(), PhysicsServer3D::MotionResult()); + return motion_results[p_bounce]; } -Ref<KinematicCollision3D> KinematicBody3D::_get_slide_collision(int p_bounce) { - ERR_FAIL_INDEX_V(p_bounce, colliders.size(), Ref<KinematicCollision3D>()); +Ref<KinematicCollision3D> CharacterBody3D::_get_slide_collision(int p_bounce) { + ERR_FAIL_INDEX_V(p_bounce, motion_results.size(), Ref<KinematicCollision3D>()); if (p_bounce >= slide_colliders.size()) { slide_colliders.resize(p_bounce + 1); } if (slide_colliders[p_bounce].is_null()) { - slide_colliders.write[p_bounce].instance(); + slide_colliders.write[p_bounce].instantiate(); slide_colliders.write[p_bounce]->owner = this; } - slide_colliders.write[p_bounce]->collision = colliders[p_bounce]; + slide_colliders.write[p_bounce]->result = motion_results[p_bounce]; return slide_colliders[p_bounce]; } -void KinematicBody3D::_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; - colliders.clear(); - floor_velocity = Vector3(); +Ref<KinematicCollision3D> CharacterBody3D::_get_last_slide_collision() { + if (motion_results.size() == 0) { + return Ref<KinematicCollision3D>(); } + return _get_slide_collision(motion_results.size() - 1); } -void KinematicBody3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("_direct_state_changed"), &KinematicBody3D::_direct_state_changed); +bool CharacterBody3D::is_floor_stop_on_slope_enabled() const { + return floor_stop_on_slope; +} - ClassDB::bind_method(D_METHOD("move_and_collide", "rel_vec", "infinite_inertia", "exclude_raycast_shapes", "test_only"), &KinematicBody3D::_move, DEFVAL(true), DEFVAL(true), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("move_and_slide", "linear_velocity", "up_direction", "stop_on_slope", "max_slides", "floor_max_angle", "infinite_inertia"), &KinematicBody3D::move_and_slide, DEFVAL(Vector3(0, 0, 0)), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)), DEFVAL(true)); - ClassDB::bind_method(D_METHOD("move_and_slide_with_snap", "linear_velocity", "snap", "up_direction", "stop_on_slope", "max_slides", "floor_max_angle", "infinite_inertia"), &KinematicBody3D::move_and_slide_with_snap, DEFVAL(Vector3(0, 0, 0)), DEFVAL(false), DEFVAL(4), DEFVAL(Math::deg2rad((float)45)), DEFVAL(true)); +void CharacterBody3D::set_floor_stop_on_slope_enabled(bool p_enabled) { + floor_stop_on_slope = p_enabled; +} - ClassDB::bind_method(D_METHOD("test_move", "from", "rel_vec", "infinite_inertia"), &KinematicBody3D::test_move, DEFVAL(true)); +int CharacterBody3D::get_max_slides() const { + return max_slides; +} - ClassDB::bind_method(D_METHOD("is_on_floor"), &KinematicBody3D::is_on_floor); - ClassDB::bind_method(D_METHOD("is_on_ceiling"), &KinematicBody3D::is_on_ceiling); - ClassDB::bind_method(D_METHOD("is_on_wall"), &KinematicBody3D::is_on_wall); - ClassDB::bind_method(D_METHOD("get_floor_normal"), &KinematicBody3D::get_floor_normal); - ClassDB::bind_method(D_METHOD("get_floor_velocity"), &KinematicBody3D::get_floor_velocity); +void CharacterBody3D::set_max_slides(int p_max_slides) { + ERR_FAIL_COND(p_max_slides < 1); + max_slides = p_max_slides; +} - ClassDB::bind_method(D_METHOD("set_axis_lock", "axis", "lock"), &KinematicBody3D::set_axis_lock); - ClassDB::bind_method(D_METHOD("get_axis_lock", "axis"), &KinematicBody3D::get_axis_lock); +real_t CharacterBody3D::get_floor_max_angle() const { + return floor_max_angle; +} - ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &KinematicBody3D::set_safe_margin); - ClassDB::bind_method(D_METHOD("get_safe_margin"), &KinematicBody3D::get_safe_margin); +void CharacterBody3D::set_floor_max_angle(real_t p_radians) { + floor_max_angle = p_radians; +} - ClassDB::bind_method(D_METHOD("get_slide_count"), &KinematicBody3D::get_slide_count); - ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &KinematicBody3D::_get_slide_collision); +const Vector3 &CharacterBody3D::get_snap() const { + return snap; +} - ADD_GROUP("Axis Lock", "axis_lock_"); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_motion_x"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_LINEAR_X); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_motion_y"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_LINEAR_Y); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_motion_z"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_LINEAR_Z); +void CharacterBody3D::set_snap(const Vector3 &p_snap) { + snap = p_snap; +} - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin"); +const Vector3 &CharacterBody3D::get_up_direction() const { + return up_direction; } -void KinematicBody3D::_direct_state_changed(Object *p_state) { -#ifdef DEBUG_ENABLED - PhysicsDirectBodyState3D *state = Object::cast_to<PhysicsDirectBodyState3D>(p_state); -#else - PhysicsDirectBodyState3D *state = (PhysicsDirectBodyState3D *)p_state; //trust it -#endif +void CharacterBody3D::set_up_direction(const Vector3 &p_up_direction) { + up_direction = p_up_direction.normalized(); +} - linear_velocity = state->get_linear_velocity(); - angular_velocity = state->get_angular_velocity(); +void CharacterBody3D::_notification(int p_what) { + 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; + } } -KinematicBody3D::KinematicBody3D() : - PhysicsBody3D(PhysicsServer3D::BODY_MODE_KINEMATIC) { - locked_axis = 0; - on_floor = false; - on_ceiling = false; - on_wall = false; +void CharacterBody3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("move_and_slide"), &CharacterBody3D::move_and_slide); + + ClassDB::bind_method(D_METHOD("set_linear_velocity", "linear_velocity"), &CharacterBody3D::set_linear_velocity); + ClassDB::bind_method(D_METHOD("get_linear_velocity"), &CharacterBody3D::get_linear_velocity); + + ClassDB::bind_method(D_METHOD("set_safe_margin", "pixels"), &CharacterBody3D::set_safe_margin); + ClassDB::bind_method(D_METHOD("get_safe_margin"), &CharacterBody3D::get_safe_margin); + ClassDB::bind_method(D_METHOD("is_floor_stop_on_slope_enabled"), &CharacterBody3D::is_floor_stop_on_slope_enabled); + ClassDB::bind_method(D_METHOD("set_floor_stop_on_slope_enabled", "enabled"), &CharacterBody3D::set_floor_stop_on_slope_enabled); + ClassDB::bind_method(D_METHOD("get_max_slides"), &CharacterBody3D::get_max_slides); + ClassDB::bind_method(D_METHOD("set_max_slides", "max_slides"), &CharacterBody3D::set_max_slides); + ClassDB::bind_method(D_METHOD("get_floor_max_angle"), &CharacterBody3D::get_floor_max_angle); + ClassDB::bind_method(D_METHOD("set_floor_max_angle", "radians"), &CharacterBody3D::set_floor_max_angle); + ClassDB::bind_method(D_METHOD("get_snap"), &CharacterBody3D::get_snap); + ClassDB::bind_method(D_METHOD("set_snap", "snap"), &CharacterBody3D::set_snap); + ClassDB::bind_method(D_METHOD("get_up_direction"), &CharacterBody3D::get_up_direction); + ClassDB::bind_method(D_METHOD("set_up_direction", "up_direction"), &CharacterBody3D::set_up_direction); + + ClassDB::bind_method(D_METHOD("is_on_floor"), &CharacterBody3D::is_on_floor); + ClassDB::bind_method(D_METHOD("is_on_floor_only"), &CharacterBody3D::is_on_floor_only); + ClassDB::bind_method(D_METHOD("is_on_ceiling"), &CharacterBody3D::is_on_ceiling); + ClassDB::bind_method(D_METHOD("is_on_ceiling_only"), &CharacterBody3D::is_on_ceiling_only); + ClassDB::bind_method(D_METHOD("is_on_wall"), &CharacterBody3D::is_on_wall); + ClassDB::bind_method(D_METHOD("is_on_wall_only"), &CharacterBody3D::is_on_wall_only); + ClassDB::bind_method(D_METHOD("get_floor_normal"), &CharacterBody3D::get_floor_normal); + ClassDB::bind_method(D_METHOD("get_floor_angle", "up_direction"), &CharacterBody3D::get_floor_angle, DEFVAL(Vector3(0.0, 1.0, 0.0))); + ClassDB::bind_method(D_METHOD("get_platform_velocity"), &CharacterBody3D::get_platform_velocity); + + ClassDB::bind_method(D_METHOD("get_slide_collision_count"), &CharacterBody3D::get_slide_collision_count); + ClassDB::bind_method(D_METHOD("get_slide_collision", "slide_idx"), &CharacterBody3D::_get_slide_collision); + ClassDB::bind_method(D_METHOD("get_last_slide_collision"), &CharacterBody3D::_get_last_slide_collision); - set_safe_margin(0.001); - PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "linear_velocity"), "set_linear_velocity", "get_linear_velocity"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "max_slides", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR), "set_max_slides", "get_max_slides"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "snap"), "set_snap", "get_snap"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "up_direction"), "set_up_direction", "get_up_direction"); + ADD_GROUP("Floor", "floor_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "floor_max_angle", PROPERTY_HINT_RANGE, "0,180,0.1,radians"), "set_floor_max_angle", "get_floor_max_angle"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "floor_stop_on_slope"), "set_floor_stop_on_slope_enabled", "is_floor_stop_on_slope_enabled"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "collision/safe_margin", PROPERTY_HINT_RANGE, "0.001,256,0.001"), "set_safe_margin", "get_safe_margin"); } -KinematicBody3D::~KinematicBody3D() { - if (motion_cache.is_valid()) { - motion_cache->owner = nullptr; - } +CharacterBody3D::CharacterBody3D() : + PhysicsBody3D(PhysicsServer3D::BODY_MODE_KINEMATIC) { +} +CharacterBody3D::~CharacterBody3D() { for (int i = 0; i < slide_colliders.size(); i++) { if (slide_colliders[i].is_valid()) { slide_colliders.write[i]->owner = nullptr; @@ -1272,39 +1435,48 @@ KinematicBody3D::~KinematicBody3D() { /////////////////////////////////////// Vector3 KinematicCollision3D::get_position() const { - return collision.collision; + return result.collision_point; } Vector3 KinematicCollision3D::get_normal() const { - return collision.normal; + return result.collision_normal; } Vector3 KinematicCollision3D::get_travel() const { - return collision.travel; + return result.travel; } Vector3 KinematicCollision3D::get_remainder() const { - return collision.remainder; + return result.remainder; +} + +real_t KinematicCollision3D::get_angle(const Vector3 &p_up_direction) const { + ERR_FAIL_COND_V(p_up_direction == Vector3(), 0); + return result.get_angle(p_up_direction); } Object *KinematicCollision3D::get_local_shape() const { if (!owner) { return nullptr; } - uint32_t ownerid = owner->shape_find_owner(collision.local_shape); + uint32_t ownerid = owner->shape_find_owner(result.collision_local_shape); return owner->shape_owner_get_owner(ownerid); } Object *KinematicCollision3D::get_collider() const { - if (collision.collider.is_valid()) { - return ObjectDB::get_instance(collision.collider); + if (result.collider_id.is_valid()) { + return ObjectDB::get_instance(result.collider_id); } return nullptr; } ObjectID KinematicCollision3D::get_collider_id() const { - return collision.collider; + return result.collider_id; +} + +RID KinematicCollision3D::get_collider_rid() const { + return result.collider; } Object *KinematicCollision3D::get_collider_shape() const { @@ -1312,7 +1484,7 @@ Object *KinematicCollision3D::get_collider_shape() const { if (collider) { CollisionObject3D *obj2d = Object::cast_to<CollisionObject3D>(collider); if (obj2d) { - uint32_t ownerid = obj2d->shape_find_owner(collision.collider_shape); + uint32_t ownerid = obj2d->shape_find_owner(result.collider_shape); return obj2d->shape_owner_get_owner(ownerid); } } @@ -1321,11 +1493,11 @@ Object *KinematicCollision3D::get_collider_shape() const { } int KinematicCollision3D::get_collider_shape_index() const { - return collision.collider_shape; + return result.collider_shape; } Vector3 KinematicCollision3D::get_collider_velocity() const { - return collision.collider_vel; + return result.collider_velocity; } Variant KinematicCollision3D::get_collider_metadata() const { @@ -1337,9 +1509,11 @@ void KinematicCollision3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_normal"), &KinematicCollision3D::get_normal); ClassDB::bind_method(D_METHOD("get_travel"), &KinematicCollision3D::get_travel); ClassDB::bind_method(D_METHOD("get_remainder"), &KinematicCollision3D::get_remainder); + ClassDB::bind_method(D_METHOD("get_angle", "up_direction"), &KinematicCollision3D::get_angle, DEFVAL(Vector3(0.0, 1.0, 0.0))); ClassDB::bind_method(D_METHOD("get_local_shape"), &KinematicCollision3D::get_local_shape); ClassDB::bind_method(D_METHOD("get_collider"), &KinematicCollision3D::get_collider); ClassDB::bind_method(D_METHOD("get_collider_id"), &KinematicCollision3D::get_collider_id); + ClassDB::bind_method(D_METHOD("get_collider_rid"), &KinematicCollision3D::get_collider_rid); ClassDB::bind_method(D_METHOD("get_collider_shape"), &KinematicCollision3D::get_collider_shape); ClassDB::bind_method(D_METHOD("get_collider_shape_index"), &KinematicCollision3D::get_collider_shape_index); ClassDB::bind_method(D_METHOD("get_collider_velocity"), &KinematicCollision3D::get_collider_velocity); @@ -1352,18 +1526,13 @@ void KinematicCollision3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "local_shape"), "", "get_local_shape"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "collider"), "", "get_collider"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collider_id"), "", "get_collider_id"); + ADD_PROPERTY(PropertyInfo(Variant::RID, "collider_rid"), "", "get_collider_rid"); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "collider_shape"), "", "get_collider_shape"); ADD_PROPERTY(PropertyInfo(Variant::INT, "collider_shape_index"), "", "get_collider_shape_index"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "collider_velocity"), "", "get_collider_velocity"); ADD_PROPERTY(PropertyInfo(Variant::NIL, "collider_metadata", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NIL_IS_VARIANT), "", "get_collider_metadata"); } -KinematicCollision3D::KinematicCollision3D() { - collision.collider_shape = 0; - collision.local_shape = 0; - owner = nullptr; -} - /////////////////////////////////////// bool PhysicalBone3D::JointData::_set(const StringName &p_name, const Variant &p_value, RID j) { @@ -1743,6 +1912,10 @@ bool PhysicalBone3D::SixDOFJointData::_set(const StringName &p_name, const Varia String path = p_name; + if (!path.begins_with("joint_constraints/")) { + return false; + } + Vector3::Axis axis; { const String axis_s = path.get_slicec('/', 1); @@ -1899,6 +2072,10 @@ bool PhysicalBone3D::SixDOFJointData::_get(const StringName &p_name, Variant &r_ String path = p_name; + if (!path.begins_with("joint_constraints/")) { + return false; + } + int axis; { const String axis_s = path.get_slicec('/', 1); @@ -1998,11 +2175,9 @@ bool PhysicalBone3D::_set(const StringName &p_name, const Variant &p_value) { } if (joint_data) { - if (joint_data->_set(p_name, p_value)) { + if (joint_data->_set(p_name, p_value, joint)) { #ifdef TOOLS_ENABLED - if (get_gizmo().is_valid()) { - get_gizmo()->redraw(); - } + update_gizmos(); #endif return true; } @@ -2053,27 +2228,27 @@ void PhysicalBone3D::_notification(int p_what) { update_bone_id(); reset_to_rest_position(); reset_physics_simulation_state(); - if (!joint.is_valid() && joint_data) { + if (joint_data) { _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); + bone_id = -1; } } parent_skeleton = nullptr; - if (joint.is_valid()) { - PhysicsServer3D::get_singleton()->free(joint); - joint = RID(); - } - break; - case NOTIFICATION_TRANSFORM_CHANGED: + PhysicsServer3D::get_singleton()->joint_clear(joint); + } break; + + case NOTIFICATION_TRANSFORM_CHANGED: { if (Engine::get_singleton()->is_editor_hint()) { update_offset(); } - break; + } break; } } @@ -2088,15 +2263,17 @@ void PhysicalBone3D::_direct_state_changed(Object *p_state) { #ifdef DEBUG_ENABLED state = Object::cast_to<PhysicsDirectBodyState3D>(p_state); + ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument"); #else state = (PhysicsDirectBodyState3D *)p_state; //trust it #endif - Transform global_transform(state->get_transform()); + Transform3D global_transform(state->get_transform()); set_ignore_transform_notification(true); set_global_transform(global_transform); set_ignore_transform_notification(false); + _on_transform_changed(); // Update skeleton if (parent_skeleton) { @@ -2110,8 +2287,6 @@ void PhysicalBone3D::_bind_methods() { ClassDB::bind_method(D_METHOD("apply_central_impulse", "impulse"), &PhysicalBone3D::apply_central_impulse); ClassDB::bind_method(D_METHOD("apply_impulse", "impulse", "position"), &PhysicalBone3D::apply_impulse, Vector3()); - ClassDB::bind_method(D_METHOD("_direct_state_changed"), &PhysicalBone3D::_direct_state_changed); - ClassDB::bind_method(D_METHOD("set_joint_type", "joint_type"), &PhysicalBone3D::set_joint_type); ClassDB::bind_method(D_METHOD("get_joint_type"), &PhysicalBone3D::get_joint_type); @@ -2119,8 +2294,6 @@ void PhysicalBone3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_joint_offset"), &PhysicalBone3D::get_joint_offset); ClassDB::bind_method(D_METHOD("set_joint_rotation", "euler"), &PhysicalBone3D::set_joint_rotation); ClassDB::bind_method(D_METHOD("get_joint_rotation"), &PhysicalBone3D::get_joint_rotation); - ClassDB::bind_method(D_METHOD("set_joint_rotation_degrees", "euler_degrees"), &PhysicalBone3D::set_joint_rotation_degrees); - ClassDB::bind_method(D_METHOD("get_joint_rotation_degrees"), &PhysicalBone3D::get_joint_rotation_degrees); ClassDB::bind_method(D_METHOD("set_body_offset", "offset"), &PhysicalBone3D::set_body_offset); ClassDB::bind_method(D_METHOD("get_body_offset"), &PhysicalBone3D::get_body_offset); @@ -2134,9 +2307,6 @@ void PhysicalBone3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_mass", "mass"), &PhysicalBone3D::set_mass); ClassDB::bind_method(D_METHOD("get_mass"), &PhysicalBone3D::get_mass); - ClassDB::bind_method(D_METHOD("set_weight", "weight"), &PhysicalBone3D::set_weight); - ClassDB::bind_method(D_METHOD("get_weight"), &PhysicalBone3D::get_weight); - ClassDB::bind_method(D_METHOD("set_friction", "friction"), &PhysicalBone3D::set_friction); ClassDB::bind_method(D_METHOD("get_friction"), &PhysicalBone3D::get_friction); @@ -2155,19 +2325,14 @@ void PhysicalBone3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_can_sleep", "able_to_sleep"), &PhysicalBone3D::set_can_sleep); ClassDB::bind_method(D_METHOD("is_able_to_sleep"), &PhysicalBone3D::is_able_to_sleep); - ClassDB::bind_method(D_METHOD("set_axis_lock", "axis", "lock"), &PhysicalBone3D::set_axis_lock); - ClassDB::bind_method(D_METHOD("get_axis_lock", "axis"), &PhysicalBone3D::get_axis_lock); - ADD_GROUP("Joint", "joint_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "joint_type", PROPERTY_HINT_ENUM, "None,PinJoint,ConeJoint,HingeJoint,SliderJoint,6DOFJoint"), "set_joint_type", "get_joint_type"); - ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "joint_offset"), "set_joint_offset", "get_joint_offset"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "joint_rotation_degrees", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_joint_rotation_degrees", "get_joint_rotation_degrees"); - ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "joint_rotation", PROPERTY_HINT_NONE, "", 0), "set_joint_rotation", "get_joint_rotation"); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "joint_offset"), "set_joint_offset", "get_joint_offset"); + ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "joint_rotation", PROPERTY_HINT_RANGE, "-360,360,0.01,or_lesser,or_greater,radians"), "set_joint_rotation", "get_joint_rotation"); - ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "body_offset"), "set_body_offset", "get_body_offset"); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "body_offset"), "set_body_offset", "get_body_offset"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01"), "set_mass", "get_mass"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "weight", PROPERTY_HINT_EXP_RANGE, "0.01,65535,0.01"), "set_weight", "get_weight"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "mass", PROPERTY_HINT_RANGE, "0.01,65535,0.01,exp"), "set_mass", "get_mass"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "friction", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_friction", "get_friction"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bounce", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_bounce", "get_bounce"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "gravity_scale", PROPERTY_HINT_RANGE, "-10,10,0.01"), "set_gravity_scale", "get_gravity_scale"); @@ -2175,14 +2340,6 @@ void PhysicalBone3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "angular_damp", PROPERTY_HINT_RANGE, "-1,100,0.001,or_greater"), "set_angular_damp", "get_angular_damp"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "can_sleep"), "set_can_sleep", "is_able_to_sleep"); - ADD_GROUP("Axis Lock", "axis_lock_"); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_linear_x"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_LINEAR_X); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_linear_y"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_LINEAR_Y); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_linear_z"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_LINEAR_Z); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_angular_x"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_ANGULAR_X); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_angular_y"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_ANGULAR_Y); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "axis_lock_angular_z"), "set_axis_lock", "get_axis_lock", PhysicsServer3D::BODY_AXIS_ANGULAR_Z); - BIND_ENUM_CONSTANT(JOINT_TYPE_NONE); BIND_ENUM_CONSTANT(JOINT_TYPE_PIN); BIND_ENUM_CONSTANT(JOINT_TYPE_CONE); @@ -2207,9 +2364,7 @@ void PhysicalBone3D::_update_joint_offset() { set_ignore_transform_notification(false); #ifdef TOOLS_ENABLED - if (get_gizmo().is_valid()) { - get_gizmo()->redraw(); - } + update_gizmos(); #endif } @@ -2221,27 +2376,24 @@ void PhysicalBone3D::_fix_joint_offset() { } void PhysicalBone3D::_reload_joint() { - if (joint.is_valid()) { - PhysicsServer3D::get_singleton()->free(joint); - joint = RID(); - } - if (!parent_skeleton) { + PhysicsServer3D::get_singleton()->joint_clear(joint); return; } PhysicalBone3D *body_a = parent_skeleton->get_physical_bone_parent(bone_id); if (!body_a) { + PhysicsServer3D::get_singleton()->joint_clear(joint); return; } - Transform joint_transf = get_global_transform() * joint_offset; - Transform local_a = body_a->get_global_transform().affine_inverse() * joint_transf; + Transform3D joint_transf = get_global_transform() * joint_offset; + Transform3D local_a = body_a->get_global_transform().affine_inverse() * joint_transf; local_a.orthonormalize(); switch (get_joint_type()) { case JOINT_TYPE_PIN: { - joint = PhysicsServer3D::get_singleton()->joint_create_pin(body_a->get_rid(), local_a.origin, get_rid(), joint_offset.origin); + PhysicsServer3D::get_singleton()->joint_make_pin(joint, body_a->get_rid(), local_a.origin, get_rid(), joint_offset.origin); const PinJointData *pjd(static_cast<const PinJointData *>(joint_data)); PhysicsServer3D::get_singleton()->pin_joint_set_param(joint, PhysicsServer3D::PIN_JOINT_BIAS, pjd->bias); PhysicsServer3D::get_singleton()->pin_joint_set_param(joint, PhysicsServer3D::PIN_JOINT_DAMPING, pjd->damping); @@ -2249,7 +2401,7 @@ void PhysicalBone3D::_reload_joint() { } break; case JOINT_TYPE_CONE: { - joint = PhysicsServer3D::get_singleton()->joint_create_cone_twist(body_a->get_rid(), local_a, get_rid(), joint_offset); + PhysicsServer3D::get_singleton()->joint_make_cone_twist(joint, body_a->get_rid(), local_a, get_rid(), joint_offset); const ConeJointData *cjd(static_cast<const ConeJointData *>(joint_data)); PhysicsServer3D::get_singleton()->cone_twist_joint_set_param(joint, PhysicsServer3D::CONE_TWIST_JOINT_SWING_SPAN, cjd->swing_span); PhysicsServer3D::get_singleton()->cone_twist_joint_set_param(joint, PhysicsServer3D::CONE_TWIST_JOINT_TWIST_SPAN, cjd->twist_span); @@ -2259,7 +2411,7 @@ void PhysicalBone3D::_reload_joint() { } break; case JOINT_TYPE_HINGE: { - joint = PhysicsServer3D::get_singleton()->joint_create_hinge(body_a->get_rid(), local_a, get_rid(), joint_offset); + PhysicsServer3D::get_singleton()->joint_make_hinge(joint, body_a->get_rid(), local_a, get_rid(), joint_offset); const HingeJointData *hjd(static_cast<const HingeJointData *>(joint_data)); PhysicsServer3D::get_singleton()->hinge_joint_set_flag(joint, PhysicsServer3D::HINGE_JOINT_FLAG_USE_LIMIT, hjd->angular_limit_enabled); PhysicsServer3D::get_singleton()->hinge_joint_set_param(joint, PhysicsServer3D::HINGE_JOINT_LIMIT_UPPER, hjd->angular_limit_upper); @@ -2270,7 +2422,7 @@ void PhysicalBone3D::_reload_joint() { } break; case JOINT_TYPE_SLIDER: { - joint = PhysicsServer3D::get_singleton()->joint_create_slider(body_a->get_rid(), local_a, get_rid(), joint_offset); + PhysicsServer3D::get_singleton()->joint_make_slider(joint, body_a->get_rid(), local_a, get_rid(), joint_offset); const SliderJointData *sjd(static_cast<const SliderJointData *>(joint_data)); PhysicsServer3D::get_singleton()->slider_joint_set_param(joint, PhysicsServer3D::SLIDER_JOINT_LINEAR_LIMIT_UPPER, sjd->linear_limit_upper); PhysicsServer3D::get_singleton()->slider_joint_set_param(joint, PhysicsServer3D::SLIDER_JOINT_LINEAR_LIMIT_LOWER, sjd->linear_limit_lower); @@ -2285,7 +2437,7 @@ void PhysicalBone3D::_reload_joint() { } break; case JOINT_TYPE_6DOF: { - joint = PhysicsServer3D::get_singleton()->joint_create_generic_6dof(body_a->get_rid(), local_a, get_rid(), joint_offset); + PhysicsServer3D::get_singleton()->joint_make_generic_6dof(joint, body_a->get_rid(), local_a, get_rid(), joint_offset); const SixDOFJointData *g6dofjd(static_cast<const SixDOFJointData *>(joint_data)); for (int axis = 0; axis < 3; ++axis) { PhysicsServer3D::get_singleton()->generic_6dof_joint_set_flag(joint, static_cast<Vector3::Axis>(axis), PhysicsServer3D::G6DOF_JOINT_FLAG_ENABLE_LINEAR_LIMIT, g6dofjd->axis_data[axis].linear_limit_enabled); @@ -2329,11 +2481,11 @@ void PhysicalBone3D::_set_gizmo_move_joint(bool p_move_joint) { } #ifdef TOOLS_ENABLED -Transform PhysicalBone3D::get_global_gizmo_transform() const { +Transform3D PhysicalBone3D::get_global_gizmo_transform() const { return gizmo_move_joint ? get_global_transform() * joint_offset : get_global_transform(); } -Transform PhysicalBone3D::get_local_gizmo_transform() const { +Transform3D PhysicalBone3D::get_local_gizmo_transform() const { return gizmo_move_joint ? get_transform() * joint_offset : get_transform(); } #endif @@ -2378,10 +2530,8 @@ void PhysicalBone3D::set_joint_type(JointType p_joint_type) { _reload_joint(); #ifdef TOOLS_ENABLED - _change_notify(); - if (get_gizmo().is_valid()) { - get_gizmo()->redraw(); - } + notify_property_list_changed(); + update_gizmos(); #endif } @@ -2389,14 +2539,13 @@ PhysicalBone3D::JointType PhysicalBone3D::get_joint_type() const { return joint_data ? joint_data->get_joint_type() : JOINT_TYPE_NONE; } -void PhysicalBone3D::set_joint_offset(const Transform &p_offset) { +void PhysicalBone3D::set_joint_offset(const Transform3D &p_offset) { joint_offset = p_offset; _update_joint_offset(); - _change_notify("joint_rotation_degrees"); } -const Transform &PhysicalBone3D::get_joint_offset() const { +const Transform3D &PhysicalBone3D::get_joint_offset() const { return joint_offset; } @@ -2404,26 +2553,17 @@ void PhysicalBone3D::set_joint_rotation(const Vector3 &p_euler_rad) { joint_offset.basis.set_euler_scale(p_euler_rad, joint_offset.basis.get_scale()); _update_joint_offset(); - _change_notify("joint_offset"); } Vector3 PhysicalBone3D::get_joint_rotation() const { return joint_offset.basis.get_rotation(); } -void PhysicalBone3D::set_joint_rotation_degrees(const Vector3 &p_euler_deg) { - set_joint_rotation(p_euler_deg * Math_PI / 180.0); -} - -Vector3 PhysicalBone3D::get_joint_rotation_degrees() const { - return get_joint_rotation() * 180.0 / Math_PI; -} - -const Transform &PhysicalBone3D::get_body_offset() const { +const Transform3D &PhysicalBone3D::get_body_offset() const { return body_offset; } -void PhysicalBone3D::set_body_offset(const Transform &p_offset) { +void PhysicalBone3D::set_body_offset(const Transform3D &p_offset) { body_offset = p_offset; body_offset_inverse = body_offset.affine_inverse(); @@ -2469,14 +2609,6 @@ real_t PhysicalBone3D::get_mass() const { return mass; } -void PhysicalBone3D::set_weight(real_t p_weight) { - set_mass(p_weight / real_t(GLOBAL_DEF("physics/3d/default_gravity", 9.8))); -} - -real_t PhysicalBone3D::get_weight() const { - return mass * real_t(GLOBAL_DEF("physics/3d/default_gravity", 9.8)); -} - void PhysicalBone3D::set_friction(real_t p_friction) { ERR_FAIL_COND(p_friction < 0 || p_friction > 1); @@ -2537,16 +2669,9 @@ bool PhysicalBone3D::is_able_to_sleep() const { return can_sleep; } -void PhysicalBone3D::set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock) { - PhysicsServer3D::get_singleton()->body_set_axis_lock(get_rid(), p_axis, p_lock); -} - -bool PhysicalBone3D::get_axis_lock(PhysicsServer3D::BodyAxis p_axis) const { - return PhysicsServer3D::get_singleton()->body_is_axis_locked(get_rid(), p_axis); -} - PhysicalBone3D::PhysicalBone3D() : PhysicsBody3D(PhysicsServer3D::BODY_MODE_STATIC) { + joint = PhysicsServer3D::get_singleton()->joint_create(); reset_physics_simulation_state(); } @@ -2554,6 +2679,7 @@ PhysicalBone3D::~PhysicalBone3D() { if (joint_data) { memdelete(joint_data); } + PhysicsServer3D::get_singleton()->free(joint); } void PhysicalBone3D::update_bone_id() { @@ -2567,7 +2693,6 @@ void PhysicalBone3D::update_bone_id() { if (-1 != bone_id) { // Assert the unbind from old node parent_skeleton->unbind_physical_bone_from_bone(bone_id); - parent_skeleton->unbind_child_node_from_bone(bone_id, this); } bone_id = new_bone_id; @@ -2582,7 +2707,7 @@ void PhysicalBone3D::update_bone_id() { void PhysicalBone3D::update_offset() { #ifdef TOOLS_ENABLED if (parent_skeleton) { - Transform bone_transform(parent_skeleton->get_global_transform()); + Transform3D bone_transform(parent_skeleton->get_global_transform()); if (-1 != bone_id) { bone_transform *= parent_skeleton->get_bone_global_pose(bone_id); } @@ -2602,10 +2727,10 @@ void PhysicalBone3D::_start_physics_simulation() { return; } reset_to_rest_position(); - PhysicsServer3D::get_singleton()->body_set_mode(get_rid(), PhysicsServer3D::BODY_MODE_RIGID); + 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(), this, "_direct_state_changed"); + PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &PhysicalBone3D::_direct_state_changed)); set_as_top_level(true); _internal_simulate_physics = true; } @@ -2615,17 +2740,17 @@ 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); } if (_internal_simulate_physics) { - PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), nullptr, ""); - parent_skeleton->set_bone_global_pose_override(bone_id, Transform(), 0.0, false); + PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), Callable()); + parent_skeleton->set_bone_global_pose_override(bone_id, Transform3D(), 0.0, false); set_as_top_level(false); _internal_simulate_physics = false; } diff --git a/scene/3d/physics_body_3d.h b/scene/3d/physics_body_3d.h index 9830a55183..17e688451d 100644 --- a/scene/3d/physics_body_3d.h +++ b/scene/3d/physics_body_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,47 +31,43 @@ #ifndef PHYSICS_BODY_3D_H #define PHYSICS_BODY_3D_H -#include "core/vset.h" +#include "core/templates/vset.h" #include "scene/3d/collision_object_3d.h" #include "scene/resources/physics_material.h" #include "servers/physics_server_3d.h" #include "skeleton_3d.h" +class KinematicCollision3D; + class PhysicsBody3D : public CollisionObject3D { GDCLASS(PhysicsBody3D, CollisionObject3D); - uint32_t collision_layer; - uint32_t collision_mask; - - void _set_layers(uint32_t p_mask); - uint32_t _get_layers() const; - protected: static void _bind_methods(); PhysicsBody3D(PhysicsServer3D::BodyMode p_mode); -public: - virtual Vector3 get_linear_velocity() const; - virtual Vector3 get_angular_velocity() const; - virtual float get_inverse_mass() const; + Ref<KinematicCollision3D> motion_cache; - void set_collision_layer(uint32_t p_layer); - uint32_t get_collision_layer() const; + uint16_t locked_axis = 0; - void set_collision_mask(uint32_t p_mask); - uint32_t get_collision_mask() const; + Ref<KinematicCollision3D> _move(const Vector3 &p_motion, bool p_test_only = false, real_t p_margin = 0.001); - void set_collision_layer_bit(int p_bit, bool p_value); - bool get_collision_layer_bit(int p_bit) const; +public: + bool move_and_collide(const Vector3 &p_motion, PhysicsServer3D::MotionResult &r_result, real_t p_margin, bool p_test_only = false, bool p_cancel_sliding = true, bool p_collide_separation_ray = false, const Set<RID> &p_exclude = Set<RID>()); + bool test_move(const Transform3D &p_from, const Vector3 &p_motion, const Ref<KinematicCollision3D> &r_collision = Ref<KinematicCollision3D>(), real_t p_margin = 0.001); - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; + void set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock); + bool get_axis_lock(PhysicsServer3D::BodyAxis p_axis) const; + + virtual Vector3 get_linear_velocity() const; + virtual Vector3 get_angular_velocity() const; + virtual real_t get_inverse_mass() const; TypedArray<PhysicsBody3D> get_collision_exceptions(); void add_collision_exception_with(Node *p_node); //must be physicsbody void remove_collision_exception_with(Node *p_node); - PhysicsBody3D(); + virtual ~PhysicsBody3D(); }; class StaticBody3D : public PhysicsBody3D { @@ -80,9 +76,20 @@ class StaticBody3D : public PhysicsBody3D { Vector3 constant_linear_velocity; Vector3 constant_angular_velocity; + Vector3 linear_velocity; + Vector3 angular_velocity; + Ref<PhysicsMaterial> physics_material_override; + bool kinematic_motion = false; + bool sync_to_physics = false; + + Transform3D last_valid_transform; + + void _direct_state_changed(Object *p_state); + protected: + void _notification(int p_what); static void _bind_methods(); public: @@ -95,11 +102,23 @@ public: Vector3 get_constant_linear_velocity() const; Vector3 get_constant_angular_velocity() const; + virtual Vector3 get_linear_velocity() const override; + virtual Vector3 get_angular_velocity() const override; + + virtual TypedArray<String> get_configuration_warnings() const override; + StaticBody3D(); - ~StaticBody3D(); private: void _reload_physics_characteristics(); + + void _update_kinematic_motion(); + + void set_kinematic_motion_enabled(bool p_enabled); + bool is_kinematic_motion_enabled() const; + + void set_sync_to_physics(bool p_enable); + bool is_sync_to_physics_enabled() const; }; class RigidBody3D : public PhysicsBody3D { @@ -107,38 +126,40 @@ class RigidBody3D : public PhysicsBody3D { public: enum Mode { - MODE_RIGID, + MODE_DYNAMIC, MODE_STATIC, - MODE_CHARACTER, + MODE_DYNAMIC_LOCKED, MODE_KINEMATIC, }; + GDVIRTUAL1(_integrate_forces, PhysicsDirectBodyState3D *) + protected: - bool can_sleep; - PhysicsDirectBodyState3D *state; - Mode mode; + bool can_sleep = true; + PhysicsDirectBodyState3D *state = nullptr; + Mode mode = MODE_DYNAMIC; - real_t mass; + real_t mass = 1.0; Ref<PhysicsMaterial> physics_material_override; Vector3 linear_velocity; Vector3 angular_velocity; Basis inverse_inertia_tensor; - real_t gravity_scale; - real_t linear_damp; - real_t angular_damp; + real_t gravity_scale = 1.0; + real_t linear_damp = -1.0; + real_t angular_damp = -1.0; - bool sleeping; - bool ccd; + bool sleeping = false; + bool ccd = false; - int max_contacts_reported; + int max_contacts_reported = 0; - bool custom_integrator; + bool custom_integrator = false; struct ShapePair { - int body_shape; - int local_shape; - bool tagged; + int body_shape = 0; + int local_shape = 0; + bool tagged = false; bool operator<(const ShapePair &p_sp) const { if (body_shape == p_sp.body_shape) { return local_shape < p_sp.local_shape; @@ -155,25 +176,27 @@ protected: } }; struct RigidBody3D_RemoveAction { + RID rid; ObjectID body_id; ShapePair pair; }; struct BodyState { + RID rid; //int rc; - bool in_tree; + bool in_tree = false; VSet<ShapePair> shapes; }; struct ContactMonitor { - bool locked; + bool locked = false; Map<ObjectID, BodyState> body_map; }; - ContactMonitor *contact_monitor; + ContactMonitor *contact_monitor = nullptr; void _body_enter_tree(ObjectID p_id); void _body_exit_tree(ObjectID p_id); - void _body_inout(int p_status, ObjectID p_instance, int p_body_shape, int p_local_shape); + void _body_inout(int p_status, const RID &p_body, ObjectID p_instance, int p_body_shape, int p_local_shape); virtual void _direct_state_changed(Object *p_state); void _notification(int p_what); @@ -186,10 +209,7 @@ public: void set_mass(real_t p_mass); real_t get_mass() const; - virtual float get_inverse_mass() const override { return 1.0 / mass; } - - void set_weight(real_t p_weight); - real_t get_weight() const; + virtual real_t get_inverse_mass() const override { return 1.0 / mass; } void set_physics_material_override(const Ref<PhysicsMaterial> &p_physics_material_override); Ref<PhysicsMaterial> get_physics_material_override() const; @@ -202,7 +222,7 @@ public: void set_angular_velocity(const Vector3 &p_velocity); Vector3 get_angular_velocity() const override; - Basis get_inverse_inertia_tensor(); + Basis get_inverse_inertia_tensor() const; void set_gravity_scale(real_t p_gravity_scale); real_t get_gravity_scale() const; @@ -231,9 +251,6 @@ public: void set_use_continuous_collision_detection(bool p_enable); bool is_using_continuous_collision_detection() const; - void set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock); - bool get_axis_lock(PhysicsServer3D::BodyAxis p_axis) const; - Array get_colliding_bodies() const; void add_central_force(const Vector3 &p_force); @@ -244,7 +261,7 @@ public: void apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position = Vector3()); void apply_torque_impulse(const Vector3 &p_impulse); - virtual String get_configuration_warning() const override; + virtual TypedArray<String> get_configuration_warnings() const override; RigidBody3D(); ~RigidBody3D(); @@ -257,88 +274,86 @@ VARIANT_ENUM_CAST(RigidBody3D::Mode); class KinematicCollision3D; -class KinematicBody3D : public PhysicsBody3D { - GDCLASS(KinematicBody3D, PhysicsBody3D); - -public: - struct Collision { - Vector3 collision; - Vector3 normal; - Vector3 collider_vel; - ObjectID collider; - RID collider_rid; - int collider_shape; - Variant collider_metadata; - Vector3 remainder; - Vector3 travel; - int local_shape; - }; +class CharacterBody3D : public PhysicsBody3D { + GDCLASS(CharacterBody3D, PhysicsBody3D); private: - Vector3 linear_velocity; - Vector3 angular_velocity; + real_t margin = 0.001; - uint16_t locked_axis; + bool floor_stop_on_slope = false; + int max_slides = 4; + real_t floor_max_angle = Math::deg2rad((real_t)45.0); + Vector3 snap; + Vector3 up_direction = Vector3(0.0, 1.0, 0.0); - float margin; + Vector3 linear_velocity; Vector3 floor_normal; Vector3 floor_velocity; RID on_floor_body; - bool on_floor; - bool on_ceiling; - bool on_wall; - Vector<Collision> colliders; + bool on_floor = false; + bool on_ceiling = false; + bool on_wall = false; + Vector<PhysicsServer3D::MotionResult> motion_results; Vector<Ref<KinematicCollision3D>> slide_colliders; - Ref<KinematicCollision3D> motion_cache; - _FORCE_INLINE_ bool _ignores_mode(PhysicsServer3D::BodyMode) const; - - Ref<KinematicCollision3D> _move(const Vector3 &p_motion, bool p_infinite_inertia = true, bool p_exclude_raycast_shapes = true, bool p_test_only = false); Ref<KinematicCollision3D> _get_slide_collision(int p_bounce); + Ref<KinematicCollision3D> _get_last_slide_collision(); -protected: - void _notification(int p_what); - static void _bind_methods(); + void _set_collision_direction(const PhysicsServer3D::MotionResult &p_result); - virtual void _direct_state_changed(Object *p_state); + void set_safe_margin(real_t p_margin); + real_t get_safe_margin() const; -public: - virtual Vector3 get_linear_velocity() const override; - virtual Vector3 get_angular_velocity() const override; + bool is_floor_stop_on_slope_enabled() const; + void set_floor_stop_on_slope_enabled(bool p_enabled); - bool move_and_collide(const Vector3 &p_motion, bool p_infinite_inertia, Collision &r_collision, bool p_exclude_raycast_shapes = true, bool p_test_only = false); - bool test_move(const Transform &p_from, const Vector3 &p_motion, bool p_infinite_inertia); + int get_max_slides() const; + void set_max_slides(int p_max_slides); - bool separate_raycast_shapes(bool p_infinite_inertia, Collision &r_collision); + real_t get_floor_max_angle() const; + void set_floor_max_angle(real_t p_radians); - void set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock); - bool get_axis_lock(PhysicsServer3D::BodyAxis p_axis) const; + const Vector3 &get_snap() const; + void set_snap(const Vector3 &p_snap); + + const Vector3 &get_up_direction() const; + void set_up_direction(const Vector3 &p_up_direction); + +protected: + void _notification(int p_what); + static void _bind_methods(); - void set_safe_margin(float p_margin); - float get_safe_margin() const; +public: + bool move_and_slide(); + + virtual Vector3 get_linear_velocity() const override; + void set_linear_velocity(const Vector3 &p_velocity); - Vector3 move_and_slide(const Vector3 &p_linear_velocity, const Vector3 &p_up_direction = Vector3(0, 0, 0), bool p_stop_on_slope = false, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45), bool p_infinite_inertia = true); - Vector3 move_and_slide_with_snap(const Vector3 &p_linear_velocity, const Vector3 &p_snap, const Vector3 &p_up_direction = Vector3(0, 0, 0), bool p_stop_on_slope = false, int p_max_slides = 4, float p_floor_max_angle = Math::deg2rad((float)45), bool p_infinite_inertia = true); bool is_on_floor() const; + bool is_on_floor_only() const; bool is_on_wall() const; + bool is_on_wall_only() const; bool is_on_ceiling() const; + bool is_on_ceiling_only() const; Vector3 get_floor_normal() const; - Vector3 get_floor_velocity() const; + real_t get_floor_angle(const Vector3 &p_up_direction = Vector3(0.0, 1.0, 0.0)) const; + Vector3 get_platform_velocity() const; - int get_slide_count() const; - Collision get_slide_collision(int p_bounce) const; + int get_slide_collision_count() const; + PhysicsServer3D::MotionResult get_slide_collision(int p_bounce) const; - KinematicBody3D(); - ~KinematicBody3D(); + CharacterBody3D(); + ~CharacterBody3D(); }; -class KinematicCollision3D : public Reference { - GDCLASS(KinematicCollision3D, Reference); +class KinematicCollision3D : public RefCounted { + GDCLASS(KinematicCollision3D, RefCounted); - KinematicBody3D *owner; - friend class KinematicBody3D; - KinematicBody3D::Collision collision; + PhysicsBody3D *owner = nullptr; + friend class PhysicsBody3D; + friend class CharacterBody3D; + PhysicsServer3D::MotionResult result; protected: static void _bind_methods(); @@ -348,15 +363,15 @@ public: Vector3 get_normal() const; Vector3 get_travel() const; Vector3 get_remainder() const; + real_t get_angle(const Vector3 &p_up_direction = Vector3(0.0, 1.0, 0.0)) const; Object *get_local_shape() const; Object *get_collider() const; ObjectID get_collider_id() const; + RID get_collider_rid() const; Object *get_collider_shape() const; int get_collider_shape_index() const; Vector3 get_collider_velocity() const; Variant get_collider_metadata() const; - - KinematicCollision3D(); }; class PhysicalBone3D : public PhysicsBody3D { @@ -376,7 +391,7 @@ public: virtual JointType get_joint_type() { return JOINT_TYPE_NONE; } /// "j" is used to set the parameter inside the PhysicsServer3D - virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID()); + virtual bool _set(const StringName &p_name, const Variant &p_value, RID j); virtual bool _get(const StringName &p_name, Variant &r_ret) const; virtual void _get_property_list(List<PropertyInfo> *p_list) const; @@ -386,105 +401,91 @@ public: struct PinJointData : public JointData { virtual JointType get_joint_type() { return JOINT_TYPE_PIN; } - virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID()); + virtual bool _set(const StringName &p_name, const Variant &p_value, RID j); virtual bool _get(const StringName &p_name, Variant &r_ret) const; virtual void _get_property_list(List<PropertyInfo> *p_list) const; real_t bias = 0.3; - real_t damping = 1.; - real_t impulse_clamp = 0; - - PinJointData() {} + real_t damping = 1.0; + real_t impulse_clamp = 0.0; }; struct ConeJointData : public JointData { virtual JointType get_joint_type() { return JOINT_TYPE_CONE; } - virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID()); + virtual bool _set(const StringName &p_name, const Variant &p_value, RID j); virtual bool _get(const StringName &p_name, Variant &r_ret) const; virtual void _get_property_list(List<PropertyInfo> *p_list) const; - real_t swing_span; + real_t swing_span = Math_PI * 0.25; real_t twist_span = Math_PI; real_t bias = 0.3; real_t softness = 0.8; real_t relaxation = 1.; - - ConeJointData() : - swing_span(Math_PI * 0.25) {} }; struct HingeJointData : public JointData { virtual JointType get_joint_type() { return JOINT_TYPE_HINGE; } - virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID()); + virtual bool _set(const StringName &p_name, const Variant &p_value, RID j); virtual bool _get(const StringName &p_name, Variant &r_ret) const; virtual void _get_property_list(List<PropertyInfo> *p_list) const; bool angular_limit_enabled = false; - real_t angular_limit_upper; - real_t angular_limit_lower; + real_t angular_limit_upper = Math_PI * 0.5; + real_t angular_limit_lower = -Math_PI * 0.5; real_t angular_limit_bias = 0.3; real_t angular_limit_softness = 0.9; real_t angular_limit_relaxation = 1.; - - HingeJointData() : - - angular_limit_upper(Math_PI * 0.5), - angular_limit_lower(-Math_PI * 0.5) {} }; struct SliderJointData : public JointData { virtual JointType get_joint_type() { return JOINT_TYPE_SLIDER; } - virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID()); + virtual bool _set(const StringName &p_name, const Variant &p_value, RID j); virtual bool _get(const StringName &p_name, Variant &r_ret) const; virtual void _get_property_list(List<PropertyInfo> *p_list) const; - real_t linear_limit_upper = 1.; - real_t linear_limit_lower = -1.; - real_t linear_limit_softness = 1.; + real_t linear_limit_upper = 1.0; + real_t linear_limit_lower = -1.0; + real_t linear_limit_softness = 1.0; real_t linear_limit_restitution = 0.7; - real_t linear_limit_damping = 1.; - real_t angular_limit_upper = 0; - real_t angular_limit_lower = 0; - real_t angular_limit_softness = 1.; + real_t linear_limit_damping = 1.0; + real_t angular_limit_upper = 0.0; + real_t angular_limit_lower = 0.0; + real_t angular_limit_softness = 1.0; real_t angular_limit_restitution = 0.7; - real_t angular_limit_damping = 1.; - - SliderJointData() {} + real_t angular_limit_damping = 1.0; }; struct SixDOFJointData : public JointData { struct SixDOFAxisData { bool linear_limit_enabled = true; - real_t linear_limit_upper = 0; - real_t linear_limit_lower = 0; + real_t linear_limit_upper = 0.0; + real_t linear_limit_lower = 0.0; real_t linear_limit_softness = 0.7; real_t linear_restitution = 0.5; - real_t linear_damping = 1.; + real_t linear_damping = 1.0; bool linear_spring_enabled = false; - real_t linear_spring_stiffness = 0; - real_t linear_spring_damping = 0; - real_t linear_equilibrium_point = 0; + real_t linear_spring_stiffness = 0.0; + real_t linear_spring_damping = 0.0; + real_t linear_equilibrium_point = 0.0; bool angular_limit_enabled = true; - real_t angular_limit_upper = 0; - real_t angular_limit_lower = 0; + real_t angular_limit_upper = 0.0; + real_t angular_limit_lower = 0.0; real_t angular_limit_softness = 0.5; - real_t angular_restitution = 0; - real_t angular_damping = 1.; + real_t angular_restitution = 0.0; + real_t angular_damping = 1.0; real_t erp = 0.5; bool angular_spring_enabled = false; - real_t angular_spring_stiffness = 0; - real_t angular_spring_damping = 0.; - real_t angular_equilibrium_point = 0; - - SixDOFAxisData() {} + real_t angular_spring_stiffness = 0.0; + real_t angular_spring_damping = 0.0; + real_t angular_equilibrium_point = 0.0; }; virtual JointType get_joint_type() { return JOINT_TYPE_6DOF; } - virtual bool _set(const StringName &p_name, const Variant &p_value, RID j = RID()); + virtual bool _set(const StringName &p_name, const Variant &p_value, RID j); virtual bool _get(const StringName &p_name, Variant &r_ret) const; virtual void _get_property_list(List<PropertyInfo> *p_list) const; @@ -500,23 +501,23 @@ private: #endif JointData *joint_data = nullptr; - Transform joint_offset; + Transform3D joint_offset; RID joint; Skeleton3D *parent_skeleton = nullptr; - Transform body_offset; - Transform body_offset_inverse; + Transform3D body_offset; + Transform3D body_offset_inverse; bool simulate_physics = false; bool _internal_simulate_physics = false; int bone_id = -1; String bone_name; - real_t bounce = 0; - real_t mass = 1; - real_t friction = 1; - real_t gravity_scale = 1; - real_t linear_damp = -1; - real_t angular_damp = -1; + real_t bounce = 0.0; + real_t mass = 1.0; + real_t friction = 1.0; + real_t gravity_scale = 1.0; + real_t linear_damp = -1.0; + real_t angular_damp = -1.0; bool can_sleep = true; protected: @@ -541,8 +542,8 @@ public: public: #ifdef TOOLS_ENABLED - virtual Transform get_global_gizmo_transform() const override; - virtual Transform get_local_gizmo_transform() const override; + virtual Transform3D get_global_gizmo_transform() const override; + virtual Transform3D get_local_gizmo_transform() const override; #endif const JointData *get_joint_data() const; @@ -553,17 +554,14 @@ public: void set_joint_type(JointType p_joint_type); JointType get_joint_type() const; - void set_joint_offset(const Transform &p_offset); - const Transform &get_joint_offset() const; + void set_joint_offset(const Transform3D &p_offset); + const Transform3D &get_joint_offset() const; void set_joint_rotation(const Vector3 &p_euler_rad); Vector3 get_joint_rotation() const; - void set_joint_rotation_degrees(const Vector3 &p_euler_deg); - Vector3 get_joint_rotation_degrees() const; - - void set_body_offset(const Transform &p_offset); - const Transform &get_body_offset() const; + void set_body_offset(const Transform3D &p_offset); + const Transform3D &get_body_offset() const; void set_simulate_physics(bool p_simulate); bool get_simulate_physics(); @@ -575,9 +573,6 @@ public: void set_mass(real_t p_mass); real_t get_mass() const; - void set_weight(real_t p_weight); - real_t get_weight() const; - void set_friction(real_t p_friction); real_t get_friction() const; @@ -596,9 +591,6 @@ public: void set_can_sleep(bool p_active); bool is_able_to_sleep() const; - void set_axis_lock(PhysicsServer3D::BodyAxis p_axis, bool p_lock); - bool get_axis_lock(PhysicsServer3D::BodyAxis p_axis) const; - void apply_central_impulse(const Vector3 &p_impulse); void apply_impulse(const Vector3 &p_impulse, const Vector3 &p_position = Vector3()); diff --git a/scene/3d/physics_joint_3d.cpp b/scene/3d/physics_joint_3d.cpp index af4d6ae152..12938946a0 100644 --- a/scene/3d/physics_joint_3d.cpp +++ b/scene/3d/physics_joint_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,47 +30,90 @@ #include "physics_joint_3d.h" -void Joint3D::_update_joint(bool p_only_free) { - if (joint.is_valid()) { - if (ba.is_valid() && bb.is_valid()) { - PhysicsServer3D::get_singleton()->body_remove_collision_exception(ba, bb); - } +#include "scene/scene_string_names.h" - PhysicsServer3D::get_singleton()->free(joint); - joint = RID(); - ba = RID(); - bb = RID(); +void Joint3D::_disconnect_signals() { + Node *node_a = get_node_or_null(a); + PhysicsBody3D *body_a = Object::cast_to<PhysicsBody3D>(node_a); + if (body_a) { + body_a->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Joint3D::_body_exit_tree)); } + Node *node_b = get_node_or_null(b); + PhysicsBody3D *body_b = Object::cast_to<PhysicsBody3D>(node_b); + if (body_b) { + body_b->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Joint3D::_body_exit_tree)); + } +} + +void Joint3D::_body_exit_tree() { + _disconnect_signals(); + _update_joint(true); +} + +void Joint3D::_update_joint(bool p_only_free) { + if (ba.is_valid() && bb.is_valid()) { + PhysicsServer3D::get_singleton()->body_remove_collision_exception(ba, bb); + PhysicsServer3D::get_singleton()->body_remove_collision_exception(bb, ba); + } + + ba = RID(); + bb = RID(); + + configured = false; + if (p_only_free || !is_inside_tree()) { + PhysicsServer3D::get_singleton()->joint_clear(joint); + warning = String(); + update_configuration_warnings(); return; } - Node *node_a = has_node(get_node_a()) ? get_node(get_node_a()) : (Node *)nullptr; - Node *node_b = has_node(get_node_b()) ? get_node(get_node_b()) : (Node *)nullptr; + Node *node_a = get_node_or_null(a); + Node *node_b = get_node_or_null(b); PhysicsBody3D *body_a = Object::cast_to<PhysicsBody3D>(node_a); PhysicsBody3D *body_b = Object::cast_to<PhysicsBody3D>(node_b); - if (!body_a && body_b) { - SWAP(body_a, body_b); + if (node_a && !body_a && node_b && !body_b) { + warning = TTR("Node A and Node B must be PhysicsBody3Ds"); + } else if (node_a && !body_a) { + warning = TTR("Node A must be a PhysicsBody3D"); + } else if (node_b && !body_b) { + warning = TTR("Node B must be a PhysicsBody3D"); + } else if (!body_a && !body_b) { + warning = TTR("Joint is not connected to any PhysicsBody3Ds"); + } else if (body_a == body_b) { + warning = TTR("Node A and Node B must be different PhysicsBody3Ds"); + } else { + warning = String(); } - if (!body_a) { + update_configuration_warnings(); + + if (!warning.is_empty()) { + PhysicsServer3D::get_singleton()->joint_clear(joint); return; } - joint = _configure_joint(body_a, body_b); + configured = true; - if (!joint.is_valid()) { - return; + if (body_a) { + _configure_joint(joint, body_a, body_b); + } else if (body_b) { + _configure_joint(joint, body_b, nullptr); } PhysicsServer3D::get_singleton()->joint_set_solver_priority(joint, solver_priority); - ba = body_a->get_rid(); + if (body_a) { + ba = body_a->get_rid(); + body_a->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Joint3D::_body_exit_tree)); + } + if (body_b) { bb = body_b->get_rid(); + body_b->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &Joint3D::_body_exit_tree)); } PhysicsServer3D::get_singleton()->joint_disable_collisions_between_bodies(joint, exclude_from_collision); @@ -81,6 +124,10 @@ void Joint3D::set_node_a(const NodePath &p_node_a) { return; } + if (joint.is_valid()) { + _disconnect_signals(); + } + a = p_node_a; _update_joint(); } @@ -93,6 +140,11 @@ void Joint3D::set_node_b(const NodePath &p_node_b) { if (b == p_node_b) { return; } + + if (joint.is_valid()) { + _disconnect_signals(); + } + b = p_node_b; _update_joint(); } @@ -119,6 +171,7 @@ void Joint3D::_notification(int p_what) { } break; case NOTIFICATION_EXIT_TREE: { if (joint.is_valid()) { + _disconnect_signals(); _update_joint(true); } } break; @@ -137,6 +190,16 @@ bool Joint3D::get_exclude_nodes_from_collision() const { return exclude_from_collision; } +TypedArray<String> Joint3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node3D::get_configuration_warnings(); + + if (!warning.is_empty()) { + warnings.push_back(warning); + } + + return warnings; +} + void Joint3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_node_a", "node"), &Joint3D::set_node_a); ClassDB::bind_method(D_METHOD("get_node_a"), &Joint3D::get_node_a); @@ -150,17 +213,20 @@ void Joint3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_exclude_nodes_from_collision", "enable"), &Joint3D::set_exclude_nodes_from_collision); ClassDB::bind_method(D_METHOD("get_exclude_nodes_from_collision"), &Joint3D::get_exclude_nodes_from_collision); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "nodes/node_a", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "CollisionObject3D"), "set_node_a", "get_node_a"); - ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "nodes/node_b", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "CollisionObject3D"), "set_node_b", "get_node_b"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "nodes/node_a", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "PhysicsBody3D"), "set_node_a", "get_node_a"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "nodes/node_b", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "PhysicsBody3D"), "set_node_b", "get_node_b"); ADD_PROPERTY(PropertyInfo(Variant::INT, "solver/priority", PROPERTY_HINT_RANGE, "1,8,1"), "set_solver_priority", "get_solver_priority"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collision/exclude_nodes"), "set_exclude_nodes_from_collision", "get_exclude_nodes_from_collision"); } Joint3D::Joint3D() { - exclude_from_collision = true; - solver_priority = 1; set_notify_transform(true); + joint = PhysicsServer3D::get_singleton()->joint_create(); +} + +Joint3D::~Joint3D() { + PhysicsServer3D::get_singleton()->free(joint); } /////////////////////////////////// @@ -178,35 +244,34 @@ void PinJoint3D::_bind_methods() { BIND_ENUM_CONSTANT(PARAM_IMPULSE_CLAMP); } -void PinJoint3D::set_param(Param p_param, float p_value) { +void PinJoint3D::set_param(Param p_param, real_t p_value) { ERR_FAIL_INDEX(p_param, 3); params[p_param] = p_value; - if (get_joint().is_valid()) { + if (is_configured()) { PhysicsServer3D::get_singleton()->pin_joint_set_param(get_joint(), PhysicsServer3D::PinJointParam(p_param), p_value); } } -float PinJoint3D::get_param(Param p_param) const { +real_t PinJoint3D::get_param(Param p_param) const { ERR_FAIL_INDEX_V(p_param, 3, 0); return params[p_param]; } -RID PinJoint3D::_configure_joint(PhysicsBody3D *body_a, PhysicsBody3D *body_b) { +void PinJoint3D::_configure_joint(RID p_joint, PhysicsBody3D *body_a, PhysicsBody3D *body_b) { Vector3 pinpos = get_global_transform().origin; - Vector3 local_a = body_a->get_global_transform().affine_inverse().xform(pinpos); + Vector3 local_a = body_a->to_local(pinpos); Vector3 local_b; if (body_b) { - local_b = body_b->get_global_transform().affine_inverse().xform(pinpos); + local_b = body_b->to_local(pinpos); } else { local_b = pinpos; } - RID j = PhysicsServer3D::get_singleton()->joint_create_pin(body_a->get_rid(), local_a, body_b ? body_b->get_rid() : RID(), local_b); + PhysicsServer3D::get_singleton()->joint_make_pin(p_joint, body_a->get_rid(), local_a, body_b ? body_b->get_rid() : RID(), local_b); for (int i = 0; i < 3; i++) { - PhysicsServer3D::get_singleton()->pin_joint_set_param(j, PhysicsServer3D::PinJointParam(i), params[i]); + PhysicsServer3D::get_singleton()->pin_joint_set_param(p_joint, PhysicsServer3D::PinJointParam(i), params[i]); } - return j; } PinJoint3D::PinJoint3D() { @@ -219,19 +284,19 @@ PinJoint3D::PinJoint3D() { /////////////////////////////////// -void HingeJoint3D::_set_upper_limit(float p_limit) { +void HingeJoint3D::_set_upper_limit(real_t p_limit) { set_param(PARAM_LIMIT_UPPER, Math::deg2rad(p_limit)); } -float HingeJoint3D::_get_upper_limit() const { +real_t HingeJoint3D::_get_upper_limit() const { return Math::rad2deg(get_param(PARAM_LIMIT_UPPER)); } -void HingeJoint3D::_set_lower_limit(float p_limit) { +void HingeJoint3D::_set_lower_limit(real_t p_limit) { set_param(PARAM_LIMIT_LOWER, Math::deg2rad(p_limit)); } -float HingeJoint3D::_get_lower_limit() const { +real_t HingeJoint3D::_get_lower_limit() const { return Math::rad2deg(get_param(PARAM_LIMIT_LOWER)); } @@ -276,17 +341,17 @@ void HingeJoint3D::_bind_methods() { BIND_ENUM_CONSTANT(FLAG_MAX); } -void HingeJoint3D::set_param(Param p_param, float p_value) { +void HingeJoint3D::set_param(Param p_param, real_t p_value) { ERR_FAIL_INDEX(p_param, PARAM_MAX); params[p_param] = p_value; - if (get_joint().is_valid()) { + if (is_configured()) { PhysicsServer3D::get_singleton()->hinge_joint_set_param(get_joint(), PhysicsServer3D::HingeJointParam(p_param), p_value); } - update_gizmo(); + update_gizmos(); } -float HingeJoint3D::get_param(Param p_param) const { +real_t HingeJoint3D::get_param(Param p_param) const { ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); return params[p_param]; } @@ -294,11 +359,11 @@ float HingeJoint3D::get_param(Param p_param) const { void HingeJoint3D::set_flag(Flag p_flag, bool p_value) { ERR_FAIL_INDEX(p_flag, FLAG_MAX); flags[p_flag] = p_value; - if (get_joint().is_valid()) { + if (is_configured()) { PhysicsServer3D::get_singleton()->hinge_joint_set_flag(get_joint(), PhysicsServer3D::HingeJointFlag(p_flag), p_value); } - update_gizmo(); + update_gizmos(); } bool HingeJoint3D::get_flag(Flag p_flag) const { @@ -306,30 +371,29 @@ bool HingeJoint3D::get_flag(Flag p_flag) const { return flags[p_flag]; } -RID HingeJoint3D::_configure_joint(PhysicsBody3D *body_a, PhysicsBody3D *body_b) { - Transform gt = get_global_transform(); - Transform ainv = body_a->get_global_transform().affine_inverse(); +void HingeJoint3D::_configure_joint(RID p_joint, PhysicsBody3D *body_a, PhysicsBody3D *body_b) { + Transform3D gt = get_global_transform(); + Transform3D ainv = body_a->get_global_transform().affine_inverse(); - Transform local_a = ainv * gt; + Transform3D local_a = ainv * gt; local_a.orthonormalize(); - Transform local_b = gt; + Transform3D local_b = gt; if (body_b) { - Transform binv = body_b->get_global_transform().affine_inverse(); + Transform3D binv = body_b->get_global_transform().affine_inverse(); local_b = binv * gt; } local_b.orthonormalize(); - RID j = PhysicsServer3D::get_singleton()->joint_create_hinge(body_a->get_rid(), local_a, body_b ? body_b->get_rid() : RID(), local_b); + PhysicsServer3D::get_singleton()->joint_make_hinge(p_joint, body_a->get_rid(), local_a, body_b ? body_b->get_rid() : RID(), local_b); for (int i = 0; i < PARAM_MAX; i++) { - PhysicsServer3D::get_singleton()->hinge_joint_set_param(j, PhysicsServer3D::HingeJointParam(i), params[i]); + PhysicsServer3D::get_singleton()->hinge_joint_set_param(p_joint, PhysicsServer3D::HingeJointParam(i), params[i]); } for (int i = 0; i < FLAG_MAX; i++) { set_flag(Flag(i), flags[i]); - PhysicsServer3D::get_singleton()->hinge_joint_set_flag(j, PhysicsServer3D::HingeJointFlag(i), flags[i]); + PhysicsServer3D::get_singleton()->hinge_joint_set_flag(p_joint, PhysicsServer3D::HingeJointFlag(i), flags[i]); } - return j; } HingeJoint3D::HingeJoint3D() { @@ -350,19 +414,19 @@ HingeJoint3D::HingeJoint3D() { ////////////////////////////////// -void SliderJoint3D::_set_upper_limit_angular(float p_limit_angular) { +void SliderJoint3D::_set_upper_limit_angular(real_t p_limit_angular) { set_param(PARAM_ANGULAR_LIMIT_UPPER, Math::deg2rad(p_limit_angular)); } -float SliderJoint3D::_get_upper_limit_angular() const { +real_t SliderJoint3D::_get_upper_limit_angular() const { return Math::rad2deg(get_param(PARAM_ANGULAR_LIMIT_UPPER)); } -void SliderJoint3D::_set_lower_limit_angular(float p_limit_angular) { +void SliderJoint3D::_set_lower_limit_angular(real_t p_limit_angular) { set_param(PARAM_ANGULAR_LIMIT_LOWER, Math::deg2rad(p_limit_angular)); } -float SliderJoint3D::_get_lower_limit_angular() const { +real_t SliderJoint3D::_get_lower_limit_angular() const { return Math::rad2deg(get_param(PARAM_ANGULAR_LIMIT_LOWER)); } @@ -427,41 +491,39 @@ void SliderJoint3D::_bind_methods() { BIND_ENUM_CONSTANT(PARAM_MAX); } -void SliderJoint3D::set_param(Param p_param, float p_value) { +void SliderJoint3D::set_param(Param p_param, real_t p_value) { ERR_FAIL_INDEX(p_param, PARAM_MAX); params[p_param] = p_value; - if (get_joint().is_valid()) { + if (is_configured()) { PhysicsServer3D::get_singleton()->slider_joint_set_param(get_joint(), PhysicsServer3D::SliderJointParam(p_param), p_value); } - update_gizmo(); + update_gizmos(); } -float SliderJoint3D::get_param(Param p_param) const { +real_t SliderJoint3D::get_param(Param p_param) const { ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); return params[p_param]; } -RID SliderJoint3D::_configure_joint(PhysicsBody3D *body_a, PhysicsBody3D *body_b) { - Transform gt = get_global_transform(); - Transform ainv = body_a->get_global_transform().affine_inverse(); +void SliderJoint3D::_configure_joint(RID p_joint, PhysicsBody3D *body_a, PhysicsBody3D *body_b) { + Transform3D gt = get_global_transform(); + Transform3D ainv = body_a->get_global_transform().affine_inverse(); - Transform local_a = ainv * gt; + Transform3D local_a = ainv * gt; local_a.orthonormalize(); - Transform local_b = gt; + Transform3D local_b = gt; if (body_b) { - Transform binv = body_b->get_global_transform().affine_inverse(); + Transform3D binv = body_b->get_global_transform().affine_inverse(); local_b = binv * gt; } local_b.orthonormalize(); - RID j = PhysicsServer3D::get_singleton()->joint_create_slider(body_a->get_rid(), local_a, body_b ? body_b->get_rid() : RID(), local_b); + PhysicsServer3D::get_singleton()->joint_make_slider(p_joint, body_a->get_rid(), local_a, body_b ? body_b->get_rid() : RID(), local_b); for (int i = 0; i < PARAM_MAX; i++) { - PhysicsServer3D::get_singleton()->slider_joint_set_param(j, PhysicsServer3D::SliderJointParam(i), params[i]); + PhysicsServer3D::get_singleton()->slider_joint_set_param(p_joint, PhysicsServer3D::SliderJointParam(i), params[i]); } - - return j; } SliderJoint3D::SliderJoint3D() { @@ -492,19 +554,19 @@ SliderJoint3D::SliderJoint3D() { ////////////////////////////////// -void ConeTwistJoint3D::_set_swing_span(float p_limit_angular) { +void ConeTwistJoint3D::_set_swing_span(real_t p_limit_angular) { set_param(PARAM_SWING_SPAN, Math::deg2rad(p_limit_angular)); } -float ConeTwistJoint3D::_get_swing_span() const { +real_t ConeTwistJoint3D::_get_swing_span() const { return Math::rad2deg(get_param(PARAM_SWING_SPAN)); } -void ConeTwistJoint3D::_set_twist_span(float p_limit_angular) { +void ConeTwistJoint3D::_set_twist_span(real_t p_limit_angular) { set_param(PARAM_TWIST_SPAN, Math::deg2rad(p_limit_angular)); } -float ConeTwistJoint3D::_get_twist_span() const { +real_t ConeTwistJoint3D::_get_twist_span() const { return Math::rad2deg(get_param(PARAM_TWIST_SPAN)); } @@ -533,45 +595,43 @@ void ConeTwistJoint3D::_bind_methods() { BIND_ENUM_CONSTANT(PARAM_MAX); } -void ConeTwistJoint3D::set_param(Param p_param, float p_value) { +void ConeTwistJoint3D::set_param(Param p_param, real_t p_value) { ERR_FAIL_INDEX(p_param, PARAM_MAX); params[p_param] = p_value; - if (get_joint().is_valid()) { + if (is_configured()) { PhysicsServer3D::get_singleton()->cone_twist_joint_set_param(get_joint(), PhysicsServer3D::ConeTwistJointParam(p_param), p_value); } - update_gizmo(); + update_gizmos(); } -float ConeTwistJoint3D::get_param(Param p_param) const { +real_t ConeTwistJoint3D::get_param(Param p_param) const { ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); return params[p_param]; } -RID ConeTwistJoint3D::_configure_joint(PhysicsBody3D *body_a, PhysicsBody3D *body_b) { - Transform gt = get_global_transform(); +void ConeTwistJoint3D::_configure_joint(RID p_joint, PhysicsBody3D *body_a, PhysicsBody3D *body_b) { + Transform3D gt = get_global_transform(); //Vector3 cone_twistpos = gt.origin; //Vector3 cone_twistdir = gt.basis.get_axis(2); - Transform ainv = body_a->get_global_transform().affine_inverse(); + Transform3D ainv = body_a->get_global_transform().affine_inverse(); - Transform local_a = ainv * gt; + Transform3D local_a = ainv * gt; local_a.orthonormalize(); - Transform local_b = gt; + Transform3D local_b = gt; if (body_b) { - Transform binv = body_b->get_global_transform().affine_inverse(); + Transform3D binv = body_b->get_global_transform().affine_inverse(); local_b = binv * gt; } local_b.orthonormalize(); - RID j = PhysicsServer3D::get_singleton()->joint_create_cone_twist(body_a->get_rid(), local_a, body_b ? body_b->get_rid() : RID(), local_b); + PhysicsServer3D::get_singleton()->joint_make_cone_twist(p_joint, body_a->get_rid(), local_a, body_b ? body_b->get_rid() : RID(), local_b); for (int i = 0; i < PARAM_MAX; i++) { - PhysicsServer3D::get_singleton()->cone_twist_joint_set_param(j, PhysicsServer3D::ConeTwistJointParam(i), params[i]); + PhysicsServer3D::get_singleton()->cone_twist_joint_set_param(p_joint, PhysicsServer3D::ConeTwistJointParam(i), params[i]); } - - return j; } ConeTwistJoint3D::ConeTwistJoint3D() { @@ -584,51 +644,51 @@ ConeTwistJoint3D::ConeTwistJoint3D() { ///////////////////////////////////////////////////////////////////// -void Generic6DOFJoint3D::_set_angular_hi_limit_x(float p_limit_angular) { +void Generic6DOFJoint3D::_set_angular_hi_limit_x(real_t p_limit_angular) { set_param_x(PARAM_ANGULAR_UPPER_LIMIT, Math::deg2rad(p_limit_angular)); } -float Generic6DOFJoint3D::_get_angular_hi_limit_x() const { +real_t Generic6DOFJoint3D::_get_angular_hi_limit_x() const { return Math::rad2deg(get_param_x(PARAM_ANGULAR_UPPER_LIMIT)); } -void Generic6DOFJoint3D::_set_angular_lo_limit_x(float p_limit_angular) { +void Generic6DOFJoint3D::_set_angular_lo_limit_x(real_t p_limit_angular) { set_param_x(PARAM_ANGULAR_LOWER_LIMIT, Math::deg2rad(p_limit_angular)); } -float Generic6DOFJoint3D::_get_angular_lo_limit_x() const { +real_t Generic6DOFJoint3D::_get_angular_lo_limit_x() const { return Math::rad2deg(get_param_x(PARAM_ANGULAR_LOWER_LIMIT)); } -void Generic6DOFJoint3D::_set_angular_hi_limit_y(float p_limit_angular) { +void Generic6DOFJoint3D::_set_angular_hi_limit_y(real_t p_limit_angular) { set_param_y(PARAM_ANGULAR_UPPER_LIMIT, Math::deg2rad(p_limit_angular)); } -float Generic6DOFJoint3D::_get_angular_hi_limit_y() const { +real_t Generic6DOFJoint3D::_get_angular_hi_limit_y() const { return Math::rad2deg(get_param_y(PARAM_ANGULAR_UPPER_LIMIT)); } -void Generic6DOFJoint3D::_set_angular_lo_limit_y(float p_limit_angular) { +void Generic6DOFJoint3D::_set_angular_lo_limit_y(real_t p_limit_angular) { set_param_y(PARAM_ANGULAR_LOWER_LIMIT, Math::deg2rad(p_limit_angular)); } -float Generic6DOFJoint3D::_get_angular_lo_limit_y() const { +real_t Generic6DOFJoint3D::_get_angular_lo_limit_y() const { return Math::rad2deg(get_param_y(PARAM_ANGULAR_LOWER_LIMIT)); } -void Generic6DOFJoint3D::_set_angular_hi_limit_z(float p_limit_angular) { +void Generic6DOFJoint3D::_set_angular_hi_limit_z(real_t p_limit_angular) { set_param_z(PARAM_ANGULAR_UPPER_LIMIT, Math::deg2rad(p_limit_angular)); } -float Generic6DOFJoint3D::_get_angular_hi_limit_z() const { +real_t Generic6DOFJoint3D::_get_angular_hi_limit_z() const { return Math::rad2deg(get_param_z(PARAM_ANGULAR_UPPER_LIMIT)); } -void Generic6DOFJoint3D::_set_angular_lo_limit_z(float p_limit_angular) { +void Generic6DOFJoint3D::_set_angular_lo_limit_z(real_t p_limit_angular) { set_param_z(PARAM_ANGULAR_LOWER_LIMIT, Math::deg2rad(p_limit_angular)); } -float Generic6DOFJoint3D::_get_angular_lo_limit_z() const { +real_t Generic6DOFJoint3D::_get_angular_lo_limit_z() const { return Math::rad2deg(get_param_z(PARAM_ANGULAR_LOWER_LIMIT)); } @@ -669,9 +729,6 @@ void Generic6DOFJoint3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_flag_z", "flag", "value"), &Generic6DOFJoint3D::set_flag_z); ClassDB::bind_method(D_METHOD("get_flag_z", "flag"), &Generic6DOFJoint3D::get_flag_z); - ClassDB::bind_method(D_METHOD("set_precision", "precision"), &Generic6DOFJoint3D::set_precision); - ClassDB::bind_method(D_METHOD("get_precision"), &Generic6DOFJoint3D::get_precision); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "linear_limit_x/enabled"), "set_flag_x", "get_flag_x", FLAG_ENABLE_LINEAR_LIMIT); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_x/upper_distance"), "set_param_x", "get_param_x", PARAM_LINEAR_UPPER_LIMIT); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "linear_limit_x/lower_distance"), "set_param_x", "get_param_x", PARAM_LINEAR_LOWER_LIMIT); @@ -760,8 +817,6 @@ void Generic6DOFJoint3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_z/damping"), "set_param_z", "get_param_z", PARAM_ANGULAR_SPRING_DAMPING); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_spring_z/equilibrium_point"), "set_param_z", "get_param_z", PARAM_ANGULAR_SPRING_EQUILIBRIUM_POINT); - ADD_PROPERTY(PropertyInfo(Variant::INT, "precision", PROPERTY_HINT_RANGE, "1,99999,1"), "set_precision", "get_precision"); - BIND_ENUM_CONSTANT(PARAM_LINEAR_LOWER_LIMIT); BIND_ENUM_CONSTANT(PARAM_LINEAR_UPPER_LIMIT); BIND_ENUM_CONSTANT(PARAM_LINEAR_LIMIT_SOFTNESS); @@ -795,45 +850,45 @@ void Generic6DOFJoint3D::_bind_methods() { BIND_ENUM_CONSTANT(FLAG_MAX); } -void Generic6DOFJoint3D::set_param_x(Param p_param, float p_value) { +void Generic6DOFJoint3D::set_param_x(Param p_param, real_t p_value) { ERR_FAIL_INDEX(p_param, PARAM_MAX); params_x[p_param] = p_value; - if (get_joint().is_valid()) { + if (is_configured()) { PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(get_joint(), Vector3::AXIS_X, PhysicsServer3D::G6DOFJointAxisParam(p_param), p_value); } - update_gizmo(); + update_gizmos(); } -float Generic6DOFJoint3D::get_param_x(Param p_param) const { +real_t Generic6DOFJoint3D::get_param_x(Param p_param) const { ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); return params_x[p_param]; } -void Generic6DOFJoint3D::set_param_y(Param p_param, float p_value) { +void Generic6DOFJoint3D::set_param_y(Param p_param, real_t p_value) { ERR_FAIL_INDEX(p_param, PARAM_MAX); params_y[p_param] = p_value; - if (get_joint().is_valid()) { + if (is_configured()) { PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(get_joint(), Vector3::AXIS_Y, PhysicsServer3D::G6DOFJointAxisParam(p_param), p_value); } - update_gizmo(); + update_gizmos(); } -float Generic6DOFJoint3D::get_param_y(Param p_param) const { +real_t Generic6DOFJoint3D::get_param_y(Param p_param) const { ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); return params_y[p_param]; } -void Generic6DOFJoint3D::set_param_z(Param p_param, float p_value) { +void Generic6DOFJoint3D::set_param_z(Param p_param, real_t p_value) { ERR_FAIL_INDEX(p_param, PARAM_MAX); params_z[p_param] = p_value; - if (get_joint().is_valid()) { + if (is_configured()) { PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(get_joint(), Vector3::AXIS_Z, PhysicsServer3D::G6DOFJointAxisParam(p_param), p_value); } - update_gizmo(); + update_gizmos(); } -float Generic6DOFJoint3D::get_param_z(Param p_param) const { +real_t Generic6DOFJoint3D::get_param_z(Param p_param) const { ERR_FAIL_INDEX_V(p_param, PARAM_MAX, 0); return params_z[p_param]; } @@ -841,10 +896,10 @@ float Generic6DOFJoint3D::get_param_z(Param p_param) const { void Generic6DOFJoint3D::set_flag_x(Flag p_flag, bool p_enabled) { ERR_FAIL_INDEX(p_flag, FLAG_MAX); flags_x[p_flag] = p_enabled; - if (get_joint().is_valid()) { + if (is_configured()) { PhysicsServer3D::get_singleton()->generic_6dof_joint_set_flag(get_joint(), Vector3::AXIS_X, PhysicsServer3D::G6DOFJointAxisFlag(p_flag), p_enabled); } - update_gizmo(); + update_gizmos(); } bool Generic6DOFJoint3D::get_flag_x(Flag p_flag) const { @@ -855,10 +910,10 @@ bool Generic6DOFJoint3D::get_flag_x(Flag p_flag) const { void Generic6DOFJoint3D::set_flag_y(Flag p_flag, bool p_enabled) { ERR_FAIL_INDEX(p_flag, FLAG_MAX); flags_y[p_flag] = p_enabled; - if (get_joint().is_valid()) { + if (is_configured()) { PhysicsServer3D::get_singleton()->generic_6dof_joint_set_flag(get_joint(), Vector3::AXIS_Y, PhysicsServer3D::G6DOFJointAxisFlag(p_flag), p_enabled); } - update_gizmo(); + update_gizmos(); } bool Generic6DOFJoint3D::get_flag_y(Flag p_flag) const { @@ -869,10 +924,10 @@ bool Generic6DOFJoint3D::get_flag_y(Flag p_flag) const { void Generic6DOFJoint3D::set_flag_z(Flag p_flag, bool p_enabled) { ERR_FAIL_INDEX(p_flag, FLAG_MAX); flags_z[p_flag] = p_enabled; - if (get_joint().is_valid()) { + if (is_configured()) { PhysicsServer3D::get_singleton()->generic_6dof_joint_set_flag(get_joint(), Vector3::AXIS_Z, PhysicsServer3D::G6DOFJointAxisFlag(p_flag), p_enabled); } - update_gizmo(); + update_gizmos(); } bool Generic6DOFJoint3D::get_flag_z(Flag p_flag) const { @@ -880,45 +935,35 @@ bool Generic6DOFJoint3D::get_flag_z(Flag p_flag) const { return flags_z[p_flag]; } -void Generic6DOFJoint3D::set_precision(int p_precision) { - precision = p_precision; - - PhysicsServer3D::get_singleton()->generic_6dof_joint_set_precision( - get_joint(), - precision); -} - -RID Generic6DOFJoint3D::_configure_joint(PhysicsBody3D *body_a, PhysicsBody3D *body_b) { - Transform gt = get_global_transform(); +void Generic6DOFJoint3D::_configure_joint(RID p_joint, PhysicsBody3D *body_a, PhysicsBody3D *body_b) { + Transform3D gt = get_global_transform(); //Vector3 cone_twistpos = gt.origin; //Vector3 cone_twistdir = gt.basis.get_axis(2); - Transform ainv = body_a->get_global_transform().affine_inverse(); + Transform3D ainv = body_a->get_global_transform().affine_inverse(); - Transform local_a = ainv * gt; + Transform3D local_a = ainv * gt; local_a.orthonormalize(); - Transform local_b = gt; + Transform3D local_b = gt; if (body_b) { - Transform binv = body_b->get_global_transform().affine_inverse(); + Transform3D binv = body_b->get_global_transform().affine_inverse(); local_b = binv * gt; } local_b.orthonormalize(); - RID j = PhysicsServer3D::get_singleton()->joint_create_generic_6dof(body_a->get_rid(), local_a, body_b ? body_b->get_rid() : RID(), local_b); + PhysicsServer3D::get_singleton()->joint_make_generic_6dof(p_joint, body_a->get_rid(), local_a, body_b ? body_b->get_rid() : RID(), local_b); for (int i = 0; i < PARAM_MAX; i++) { - PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(j, Vector3::AXIS_X, PhysicsServer3D::G6DOFJointAxisParam(i), params_x[i]); - PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(j, Vector3::AXIS_Y, PhysicsServer3D::G6DOFJointAxisParam(i), params_y[i]); - PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(j, Vector3::AXIS_Z, PhysicsServer3D::G6DOFJointAxisParam(i), params_z[i]); + PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(p_joint, Vector3::AXIS_X, PhysicsServer3D::G6DOFJointAxisParam(i), params_x[i]); + PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(p_joint, Vector3::AXIS_Y, PhysicsServer3D::G6DOFJointAxisParam(i), params_y[i]); + PhysicsServer3D::get_singleton()->generic_6dof_joint_set_param(p_joint, Vector3::AXIS_Z, PhysicsServer3D::G6DOFJointAxisParam(i), params_z[i]); } for (int i = 0; i < FLAG_MAX; i++) { - PhysicsServer3D::get_singleton()->generic_6dof_joint_set_flag(j, Vector3::AXIS_X, PhysicsServer3D::G6DOFJointAxisFlag(i), flags_x[i]); - PhysicsServer3D::get_singleton()->generic_6dof_joint_set_flag(j, Vector3::AXIS_Y, PhysicsServer3D::G6DOFJointAxisFlag(i), flags_y[i]); - PhysicsServer3D::get_singleton()->generic_6dof_joint_set_flag(j, Vector3::AXIS_Z, PhysicsServer3D::G6DOFJointAxisFlag(i), flags_z[i]); + PhysicsServer3D::get_singleton()->generic_6dof_joint_set_flag(p_joint, Vector3::AXIS_X, PhysicsServer3D::G6DOFJointAxisFlag(i), flags_x[i]); + PhysicsServer3D::get_singleton()->generic_6dof_joint_set_flag(p_joint, Vector3::AXIS_Y, PhysicsServer3D::G6DOFJointAxisFlag(i), flags_y[i]); + PhysicsServer3D::get_singleton()->generic_6dof_joint_set_flag(p_joint, Vector3::AXIS_Z, PhysicsServer3D::G6DOFJointAxisFlag(i), flags_z[i]); } - - return j; } Generic6DOFJoint3D::Generic6DOFJoint3D() { diff --git a/scene/3d/physics_joint_3d.h b/scene/3d/physics_joint_3d.h index 8e2de82527..3e0ea38a5c 100644 --- a/scene/3d/physics_joint_3d.h +++ b/scene/3d/physics_joint_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -44,19 +44,27 @@ class Joint3D : public Node3D { NodePath a; NodePath b; - int solver_priority; - bool exclude_from_collision; + int solver_priority = 1; + bool exclude_from_collision = true; + String warning; + bool configured = false; protected: + void _disconnect_signals(); + void _body_exit_tree(); void _update_joint(bool p_only_free = false); void _notification(int p_what); - virtual RID _configure_joint(PhysicsBody3D *body_a, PhysicsBody3D *body_b) = 0; + virtual void _configure_joint(RID p_joint, PhysicsBody3D *body_a, PhysicsBody3D *body_b) = 0; static void _bind_methods(); + _FORCE_INLINE_ bool is_configured() const { return configured; } + public: + virtual TypedArray<String> get_configuration_warnings() const override; + void set_node_a(const NodePath &p_node_a); NodePath get_node_a() const; @@ -71,6 +79,7 @@ public: RID get_joint() const { return joint; } Joint3D(); + ~Joint3D(); }; /////////////////////////////////////////// @@ -86,13 +95,13 @@ public: }; protected: - float params[3]; - virtual RID _configure_joint(PhysicsBody3D *body_a, PhysicsBody3D *body_b) override; + real_t params[3]; + virtual void _configure_joint(RID p_joint, PhysicsBody3D *body_a, PhysicsBody3D *body_b) override; static void _bind_methods(); public: - void set_param(Param p_param, float p_value); - float get_param(Param p_param) const; + void set_param(Param p_param, real_t p_value); + real_t get_param(Param p_param) const; PinJoint3D(); }; @@ -122,20 +131,20 @@ public: }; protected: - float params[PARAM_MAX]; + real_t params[PARAM_MAX]; bool flags[FLAG_MAX]; - virtual RID _configure_joint(PhysicsBody3D *body_a, PhysicsBody3D *body_b) override; + virtual void _configure_joint(RID p_joint, PhysicsBody3D *body_a, PhysicsBody3D *body_b) override; static void _bind_methods(); - void _set_upper_limit(float p_limit); - float _get_upper_limit() const; + void _set_upper_limit(real_t p_limit); + real_t _get_upper_limit() const; - void _set_lower_limit(float p_limit); - float _get_lower_limit() const; + void _set_lower_limit(real_t p_limit); + real_t _get_lower_limit() const; public: - void set_param(Param p_param, float p_value); - float get_param(Param p_param) const; + void set_param(Param p_param, real_t p_value); + real_t get_param(Param p_param) const; void set_flag(Flag p_flag, bool p_value); bool get_flag(Flag p_flag) const; @@ -179,19 +188,19 @@ public: }; protected: - void _set_upper_limit_angular(float p_limit_angular); - float _get_upper_limit_angular() const; + void _set_upper_limit_angular(real_t p_limit_angular); + real_t _get_upper_limit_angular() const; - void _set_lower_limit_angular(float p_limit_angular); - float _get_lower_limit_angular() const; + void _set_lower_limit_angular(real_t p_limit_angular); + real_t _get_lower_limit_angular() const; - float params[PARAM_MAX]; - virtual RID _configure_joint(PhysicsBody3D *body_a, PhysicsBody3D *body_b) override; + real_t params[PARAM_MAX]; + virtual void _configure_joint(RID p_joint, PhysicsBody3D *body_a, PhysicsBody3D *body_b) override; static void _bind_methods(); public: - void set_param(Param p_param, float p_value); - float get_param(Param p_param) const; + void set_param(Param p_param, real_t p_value); + real_t get_param(Param p_param) const; SliderJoint3D(); }; @@ -203,7 +212,6 @@ class ConeTwistJoint3D : public Joint3D { public: enum Param { - PARAM_SWING_SPAN, PARAM_TWIST_SPAN, PARAM_BIAS, @@ -213,19 +221,19 @@ public: }; protected: - void _set_swing_span(float p_limit_angular); - float _get_swing_span() const; + void _set_swing_span(real_t p_limit_angular); + real_t _get_swing_span() const; - void _set_twist_span(float p_limit_angular); - float _get_twist_span() const; + void _set_twist_span(real_t p_limit_angular); + real_t _get_twist_span() const; - float params[PARAM_MAX]; - virtual RID _configure_joint(PhysicsBody3D *body_a, PhysicsBody3D *body_b) override; + real_t params[PARAM_MAX]; + virtual void _configure_joint(RID p_joint, PhysicsBody3D *body_a, PhysicsBody3D *body_b) override; static void _bind_methods(); public: - void set_param(Param p_param, float p_value); - float get_param(Param p_param) const; + void set_param(Param p_param, real_t p_value); + real_t get_param(Param p_param) const; ConeTwistJoint3D(); }; @@ -237,7 +245,6 @@ class Generic6DOFJoint3D : public Joint3D { public: enum Param { - PARAM_LINEAR_LOWER_LIMIT = PhysicsServer3D::G6DOF_JOINT_LINEAR_LOWER_LIMIT, PARAM_LINEAR_UPPER_LIMIT = PhysicsServer3D::G6DOF_JOINT_LINEAR_UPPER_LIMIT, PARAM_LINEAR_LIMIT_SOFTNESS = PhysicsServer3D::G6DOF_JOINT_LINEAR_LIMIT_SOFTNESS, @@ -274,45 +281,43 @@ public: }; protected: - void _set_angular_hi_limit_x(float p_limit_angular); - float _get_angular_hi_limit_x() const; + void _set_angular_hi_limit_x(real_t p_limit_angular); + real_t _get_angular_hi_limit_x() const; - void _set_angular_hi_limit_y(float p_limit_angular); - float _get_angular_hi_limit_y() const; + void _set_angular_hi_limit_y(real_t p_limit_angular); + real_t _get_angular_hi_limit_y() const; - void _set_angular_hi_limit_z(float p_limit_angular); - float _get_angular_hi_limit_z() const; + void _set_angular_hi_limit_z(real_t p_limit_angular); + real_t _get_angular_hi_limit_z() const; - void _set_angular_lo_limit_x(float p_limit_angular); - float _get_angular_lo_limit_x() const; + void _set_angular_lo_limit_x(real_t p_limit_angular); + real_t _get_angular_lo_limit_x() const; - void _set_angular_lo_limit_y(float p_limit_angular); - float _get_angular_lo_limit_y() const; + void _set_angular_lo_limit_y(real_t p_limit_angular); + real_t _get_angular_lo_limit_y() const; - void _set_angular_lo_limit_z(float p_limit_angular); - float _get_angular_lo_limit_z() const; + void _set_angular_lo_limit_z(real_t p_limit_angular); + real_t _get_angular_lo_limit_z() const; - float params_x[PARAM_MAX]; + real_t params_x[PARAM_MAX]; bool flags_x[FLAG_MAX]; - float params_y[PARAM_MAX]; + real_t params_y[PARAM_MAX]; bool flags_y[FLAG_MAX]; - float params_z[PARAM_MAX]; + real_t params_z[PARAM_MAX]; bool flags_z[FLAG_MAX]; - int precision = 1; - - virtual RID _configure_joint(PhysicsBody3D *body_a, PhysicsBody3D *body_b) override; + virtual void _configure_joint(RID p_joint, PhysicsBody3D *body_a, PhysicsBody3D *body_b) override; static void _bind_methods(); public: - void set_param_x(Param p_param, float p_value); - float get_param_x(Param p_param) const; + void set_param_x(Param p_param, real_t p_value); + real_t get_param_x(Param p_param) const; - void set_param_y(Param p_param, float p_value); - float get_param_y(Param p_param) const; + void set_param_y(Param p_param, real_t p_value); + real_t get_param_y(Param p_param) const; - void set_param_z(Param p_param, float p_value); - float get_param_z(Param p_param) const; + void set_param_z(Param p_param, real_t p_value); + real_t get_param_z(Param p_param) const; void set_flag_x(Flag p_flag, bool p_enabled); bool get_flag_x(Flag p_flag) const; @@ -323,11 +328,6 @@ public: void set_flag_z(Flag p_flag, bool p_enabled); bool get_flag_z(Flag p_flag) const; - void set_precision(int p_precision); - int get_precision() const { - return precision; - } - Generic6DOFJoint3D(); }; diff --git a/scene/3d/position_3d.cpp b/scene/3d/position_3d.cpp index e63c588e28..9747465103 100644 --- a/scene/3d/position_3d.cpp +++ b/scene/3d/position_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -29,7 +29,6 @@ /*************************************************************************/ #include "position_3d.h" -#include "scene/resources/mesh.h" Position3D::Position3D() { } diff --git a/scene/3d/position_3d.h b/scene/3d/position_3d.h index 1c5f05ef95..065b14c3bd 100644 --- a/scene/3d/position_3d.h +++ b/scene/3d/position_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/3d/proximity_group_3d.cpp b/scene/3d/proximity_group_3d.cpp index 1a0677c603..c8c61a9f00 100644 --- a/scene/3d/proximity_group_3d.cpp +++ b/scene/3d/proximity_group_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,7 +32,7 @@ #include "core/math/math_funcs.h" -void ProximityGroup3D::clear_groups() { +void ProximityGroup3D::_clear_groups() { Map<StringName, uint32_t>::Element *E; { @@ -43,21 +43,21 @@ void ProximityGroup3D::clear_groups() { while (E && num < size) { if (E->get() != group_version) { remove_list[num++] = E->key(); - }; + } E = E->next(); - }; + } for (int i = 0; i < num; i++) { groups.erase(remove_list[i]); - }; - }; + } + } if (E) { - clear_groups(); // call until we go through the whole list - }; -}; + _clear_groups(); // call until we go through the whole list + } +} -void ProximityGroup3D::update_groups() { +void ProximityGroup3D::_update_groups() { if (grid_radius == Vector3(0, 0, 0)) { return; } @@ -68,20 +68,20 @@ void ProximityGroup3D::update_groups() { Vector3 vcell = pos / cell_size; int cell[3] = { Math::fast_ftoi(vcell.x), Math::fast_ftoi(vcell.y), Math::fast_ftoi(vcell.z) }; - add_groups(cell, group_name, 0); + _add_groups(cell, group_name, 0); - clear_groups(); -}; + _clear_groups(); +} -void ProximityGroup3D::add_groups(int *p_cell, String p_base, int p_depth) { +void ProximityGroup3D::_add_groups(int *p_cell, String p_base, int p_depth) { p_base = p_base + "|"; if (grid_radius[p_depth] == 0) { if (p_depth == 2) { _new_group(p_base); } else { - add_groups(p_cell, p_base, p_depth + 1); - }; - }; + _add_groups(p_cell, p_base, p_depth + 1); + } + } int start = p_cell[p_depth] - grid_radius[p_depth]; int end = p_cell[p_depth] + grid_radius[p_depth]; @@ -91,72 +91,73 @@ void ProximityGroup3D::add_groups(int *p_cell, String p_base, int p_depth) { if (p_depth == 2) { _new_group(gname); } else { - add_groups(p_cell, gname, p_depth + 1); - }; - }; -}; + _add_groups(p_cell, gname, p_depth + 1); + } + } +} void ProximityGroup3D::_new_group(StringName p_name) { const Map<StringName, uint32_t>::Element *E = groups.find(p_name); if (!E) { add_to_group(p_name); - }; + } groups[p_name] = group_version; -}; +} void ProximityGroup3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_EXIT_TREE: ++group_version; - clear_groups(); + _clear_groups(); break; case NOTIFICATION_TRANSFORM_CHANGED: - update_groups(); + _update_groups(); break; - }; -}; + } +} -void ProximityGroup3D::broadcast(String p_name, Variant p_params) { +void ProximityGroup3D::broadcast(String p_method, Variant p_parameters) { Map<StringName, uint32_t>::Element *E; E = groups.front(); while (E) { - get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFAULT, E->key(), "_proximity_group_broadcast", p_name, p_params); + get_tree()->call_group_flags(SceneTree::GROUP_CALL_DEFAULT, E->key(), "_proximity_group_broadcast", p_method, p_parameters); E = E->next(); - }; -}; + } +} -void ProximityGroup3D::_proximity_group_broadcast(String p_name, Variant p_params) { +void ProximityGroup3D::_proximity_group_broadcast(String p_method, Variant p_parameters) { if (dispatch_mode == MODE_PROXY) { - get_parent()->call(p_name, p_params); + ERR_FAIL_COND(!is_inside_tree()); + get_parent()->call(p_method, p_parameters); } else { - emit_signal("broadcast", p_name, p_params); - }; -}; + emit_signal(SNAME("broadcast"), p_method, p_parameters); + } +} void ProximityGroup3D::set_group_name(const String &p_group_name) { group_name = p_group_name; -}; +} String ProximityGroup3D::get_group_name() const { return group_name; -}; +} void ProximityGroup3D::set_dispatch_mode(DispatchMode p_mode) { dispatch_mode = p_mode; -}; +} ProximityGroup3D::DispatchMode ProximityGroup3D::get_dispatch_mode() const { return dispatch_mode; -}; +} void ProximityGroup3D::set_grid_radius(const Vector3 &p_radius) { grid_radius = p_radius; -}; +} Vector3 ProximityGroup3D::get_grid_radius() const { return grid_radius; -}; +} void ProximityGroup3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_group_name", "name"), &ProximityGroup3D::set_group_name); @@ -165,19 +166,21 @@ void ProximityGroup3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_dispatch_mode"), &ProximityGroup3D::get_dispatch_mode); ClassDB::bind_method(D_METHOD("set_grid_radius", "radius"), &ProximityGroup3D::set_grid_radius); ClassDB::bind_method(D_METHOD("get_grid_radius"), &ProximityGroup3D::get_grid_radius); - ClassDB::bind_method(D_METHOD("broadcast", "name", "parameters"), &ProximityGroup3D::broadcast); - ClassDB::bind_method(D_METHOD("_proximity_group_broadcast", "name", "params"), &ProximityGroup3D::_proximity_group_broadcast); + + ClassDB::bind_method(D_METHOD("broadcast", "method", "parameters"), &ProximityGroup3D::broadcast); + + ClassDB::bind_method(D_METHOD("_proximity_group_broadcast", "method", "parameters"), &ProximityGroup3D::_proximity_group_broadcast); ADD_PROPERTY(PropertyInfo(Variant::STRING, "group_name"), "set_group_name", "get_group_name"); ADD_PROPERTY(PropertyInfo(Variant::INT, "dispatch_mode", PROPERTY_HINT_ENUM, "Proxy,Signal"), "set_dispatch_mode", "get_dispatch_mode"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "grid_radius"), "set_grid_radius", "get_grid_radius"); - ADD_SIGNAL(MethodInfo("broadcast", PropertyInfo(Variant::STRING, "group_name"), PropertyInfo(Variant::ARRAY, "parameters"))); + ADD_SIGNAL(MethodInfo("broadcast", PropertyInfo(Variant::STRING, "method"), PropertyInfo(Variant::ARRAY, "parameters"))); BIND_ENUM_CONSTANT(MODE_PROXY); BIND_ENUM_CONSTANT(MODE_SIGNAL); -}; +} ProximityGroup3D::ProximityGroup3D() { set_notify_transform(true); -}; +} diff --git a/scene/3d/proximity_group_3d.h b/scene/3d/proximity_group_3d.h index dd3a2f0a87..e45adc3040 100644 --- a/scene/3d/proximity_group_3d.h +++ b/scene/3d/proximity_group_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -35,7 +35,6 @@ class ProximityGroup3D : public Node3D { GDCLASS(ProximityGroup3D, Node3D); - OBJ_CATEGORY("3D"); public: enum DispatchMode { @@ -43,25 +42,25 @@ public: MODE_SIGNAL, }; -public: - void clear_groups(); - void update_groups(); - - void _notification(int p_what); - - DispatchMode dispatch_mode = MODE_PROXY; - +private: Map<StringName, uint32_t> groups; - String group_name; - float cell_size = 1.0; + String group_name; + DispatchMode dispatch_mode = MODE_PROXY; Vector3 grid_radius = Vector3(1, 1, 1); + + real_t cell_size = 1.0; uint32_t group_version = 0; - void add_groups(int *p_cell, String p_base, int p_depth); + void _clear_groups(); + void _update_groups(); + void _add_groups(int *p_cell, String p_base, int p_depth); void _new_group(StringName p_name); - void _proximity_group_broadcast(String p_name, Variant p_params); + void _proximity_group_broadcast(String p_method, Variant p_parameters); + +protected: + void _notification(int p_what); static void _bind_methods(); @@ -75,7 +74,7 @@ public: void set_grid_radius(const Vector3 &p_radius); Vector3 get_grid_radius() const; - void broadcast(String p_name, Variant p_params); + void broadcast(String p_method, Variant p_parameters); ProximityGroup3D(); ~ProximityGroup3D() {} @@ -83,4 +82,4 @@ public: VARIANT_ENUM_CAST(ProximityGroup3D::DispatchMode); -#endif +#endif // PROXIMITY_GROUP_H diff --git a/scene/3d/ray_cast_3d.cpp b/scene/3d/ray_cast_3d.cpp index 69f09ef569..fd4c6e7416 100644 --- a/scene/3d/ray_cast_3d.cpp +++ b/scene/3d/ray_cast_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,16 +31,17 @@ #include "ray_cast_3d.h" #include "collision_object_3d.h" -#include "core/engine.h" #include "mesh_instance_3d.h" -#include "servers/physics_server_3d.h" void RayCast3D::set_target_position(const Vector3 &p_point) { target_position = p_point; - if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_collisions_hint())) { - update_gizmo(); - } - if (is_inside_tree() && get_tree()->is_debugging_collisions_hint()) { + update_gizmos(); + + if (Engine::get_singleton()->is_editor_hint()) { + if (is_inside_tree()) { + _update_debug_shape_vertices(); + } + } else if (debug_shape) { _update_debug_shape(); } } @@ -57,18 +58,22 @@ uint32_t RayCast3D::get_collision_mask() const { return collision_mask; } -void RayCast3D::set_collision_mask_bit(int p_bit, bool p_value) { +void RayCast3D::set_collision_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); uint32_t mask = get_collision_mask(); if (p_value) { - mask |= 1 << p_bit; + mask |= 1 << (p_layer_number - 1); } else { - mask &= ~(1 << p_bit); + mask &= ~(1 << (p_layer_number - 1)); } set_collision_mask(mask); } -bool RayCast3D::get_collision_mask_bit(int p_bit) const { - return get_collision_mask() & (1 << p_bit); +bool RayCast3D::get_collision_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_mask() & (1 << (p_layer_number - 1)); } bool RayCast3D::is_colliding() const { @@ -97,7 +102,7 @@ Vector3 RayCast3D::get_collision_normal() const { void RayCast3D::set_enabled(bool p_enabled) { enabled = p_enabled; - update_gizmo(); + update_gizmos(); if (is_inside_tree() && !Engine::get_singleton()->is_editor_hint()) { set_physics_process_internal(p_enabled); @@ -146,16 +151,19 @@ bool RayCast3D::get_exclude_parent_body() const { void RayCast3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { + if (Engine::get_singleton()->is_editor_hint()) { + _update_debug_shape_vertices(); + } if (enabled && !Engine::get_singleton()->is_editor_hint()) { set_physics_process_internal(true); - - if (get_tree()->is_debugging_collisions_hint()) { - _update_debug_shape(); - } } else { set_physics_process_internal(false); } + if (get_tree()->is_debugging_collisions_hint()) { + _update_debug_shape(); + } + if (Object::cast_to<CollisionObject3D>(get_parent())) { if (exclude_parent_body) { exclude.insert(Object::cast_to<CollisionObject3D>(get_parent())->get_rid()); @@ -183,10 +191,7 @@ void RayCast3D::_notification(int p_what) { bool prev_collision_state = collided; _update_raycast_state(); if (prev_collision_state != collided && get_tree()->is_debugging_collisions_hint()) { - if (debug_material.is_valid()) { - Ref<StandardMaterial3D> line_material = static_cast<Ref<StandardMaterial3D>>(debug_material); - line_material->set_albedo(collided ? Color(1.0, 0, 0) : Color(1.0, 0.8, 0.6)); - } + _update_debug_shape_material(true); } } break; @@ -200,7 +205,7 @@ void RayCast3D::_update_raycast_state() { PhysicsDirectSpaceState3D *dss = PhysicsServer3D::get_singleton()->space_get_direct_state(w3d->get_space()); ERR_FAIL_COND(!dss); - Transform gt = get_global_transform(); + Transform3D gt = get_global_transform(); Vector3 to = target_position; if (to == Vector3()) { @@ -298,8 +303,8 @@ void RayCast3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collision_mask", "mask"), &RayCast3D::set_collision_mask); ClassDB::bind_method(D_METHOD("get_collision_mask"), &RayCast3D::get_collision_mask); - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &RayCast3D::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &RayCast3D::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &RayCast3D::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &RayCast3D::get_collision_mask_value); ClassDB::bind_method(D_METHOD("set_exclude_parent_body", "mask"), &RayCast3D::set_exclude_parent_body); ClassDB::bind_method(D_METHOD("get_exclude_parent_body"), &RayCast3D::get_exclude_parent_body); @@ -310,6 +315,12 @@ void RayCast3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_collide_with_bodies", "enable"), &RayCast3D::set_collide_with_bodies); ClassDB::bind_method(D_METHOD("is_collide_with_bodies_enabled"), &RayCast3D::is_collide_with_bodies_enabled); + ClassDB::bind_method(D_METHOD("set_debug_shape_custom_color", "debug_shape_custom_color"), &RayCast3D::set_debug_shape_custom_color); + ClassDB::bind_method(D_METHOD("get_debug_shape_custom_color"), &RayCast3D::get_debug_shape_custom_color); + + ClassDB::bind_method(D_METHOD("set_debug_shape_thickness", "debug_shape_thickness"), &RayCast3D::set_debug_shape_thickness); + ClassDB::bind_method(D_METHOD("get_debug_shape_thickness"), &RayCast3D::get_debug_shape_thickness); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enabled"), "set_enabled", "is_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "exclude_parent"), "set_exclude_parent_body", "get_exclude_parent_body"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "target_position"), "set_target_position", "get_target_position"); @@ -318,16 +329,80 @@ void RayCast3D::_bind_methods() { ADD_GROUP("Collide With", "collide_with"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_areas", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_areas", "is_collide_with_areas_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "collide_with_bodies", PROPERTY_HINT_LAYERS_3D_PHYSICS), "set_collide_with_bodies", "is_collide_with_bodies_enabled"); + + ADD_GROUP("Debug Shape", "debug_shape"); + ADD_PROPERTY(PropertyInfo(Variant::COLOR, "debug_shape_custom_color"), "set_debug_shape_custom_color", "get_debug_shape_custom_color"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "debug_shape_thickness", PROPERTY_HINT_RANGE, "1,5"), "set_debug_shape_thickness", "get_debug_shape_thickness"); } -void RayCast3D::_create_debug_shape() { - if (!debug_material.is_valid()) { - debug_material = Ref<StandardMaterial3D>(memnew(StandardMaterial3D)); +float RayCast3D::get_debug_shape_thickness() const { + return debug_shape_thickness; +} + +void RayCast3D::_update_debug_shape_vertices() { + debug_shape_vertices.clear(); + debug_line_vertices.clear(); + + if (target_position == Vector3()) { + return; + } + + debug_line_vertices.push_back(Vector3()); + debug_line_vertices.push_back(target_position); + + if (debug_shape_thickness > 1) { + float scale_factor = 100.0; + Vector3 dir = Vector3(target_position).normalized(); + // Draw truncated pyramid + Vector3 normal = (fabs(dir.x) + fabs(dir.y) > CMP_EPSILON) ? Vector3(-dir.y, dir.x, 0).normalized() : Vector3(0, -dir.z, dir.y).normalized(); + normal *= debug_shape_thickness / scale_factor; + int vertices_strip_order[14] = { 4, 5, 0, 1, 2, 5, 6, 4, 7, 0, 3, 2, 7, 6 }; + for (int v = 0; v < 14; v++) { + Vector3 vertex = vertices_strip_order[v] < 4 ? normal : normal / 3.0 + target_position; + debug_shape_vertices.push_back(vertex.rotated(dir, Math_PI * (0.5 * (vertices_strip_order[v] % 4) + 0.25))); + } + } +} + +void RayCast3D::set_debug_shape_thickness(const float p_debug_shape_thickness) { + debug_shape_thickness = p_debug_shape_thickness; + update_gizmos(); + + if (Engine::get_singleton()->is_editor_hint()) { + if (is_inside_tree()) { + _update_debug_shape_vertices(); + } + } else if (debug_shape) { + _update_debug_shape(); + } +} - Ref<StandardMaterial3D> line_material = static_cast<Ref<StandardMaterial3D>>(debug_material); - line_material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); - line_material->set_albedo(Color(1.0, 0.8, 0.6)); +const Vector<Vector3> &RayCast3D::get_debug_shape_vertices() const { + return debug_shape_vertices; +} + +const Vector<Vector3> &RayCast3D::get_debug_line_vertices() const { + return debug_line_vertices; +} + +void RayCast3D::set_debug_shape_custom_color(const Color &p_color) { + debug_shape_custom_color = p_color; + if (debug_material.is_valid()) { + _update_debug_shape_material(); } +} + +Ref<StandardMaterial3D> RayCast3D::get_debug_material() { + _update_debug_shape_material(); + return debug_material; +} + +const Color &RayCast3D::get_debug_shape_custom_color() const { + return debug_shape_custom_color; +} + +void RayCast3D::_create_debug_shape() { + _update_debug_shape_material(); Ref<ArrayMesh> mesh = memnew(ArrayMesh); @@ -338,6 +413,37 @@ void RayCast3D::_create_debug_shape() { debug_shape = mi; } +void RayCast3D::_update_debug_shape_material(bool p_check_collision) { + if (!debug_material.is_valid()) { + Ref<StandardMaterial3D> material = memnew(StandardMaterial3D); + debug_material = material; + + material->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); + // Use double-sided rendering so that the RayCast can be seen if the camera is inside. + material->set_cull_mode(BaseMaterial3D::CULL_DISABLED); + material->set_transparency(BaseMaterial3D::TRANSPARENCY_ALPHA); + } + + Color color = debug_shape_custom_color; + if (color == Color(0.0, 0.0, 0.0)) { + // Use the default debug shape color defined in the Project Settings. + color = get_tree()->get_debug_collisions_color(); + } + + if (p_check_collision && collided) { + if ((color.get_h() < 0.055 || color.get_h() > 0.945) && color.get_s() > 0.5 && color.get_v() > 0.5) { + // If base color is already quite reddish, highlight collision with green color + color = Color(0.0, 1.0, 0.0, color.a); + } else { + // Else, highlight collision with red color + color = Color(1.0, 0, 0, color.a); + } + } + + Ref<StandardMaterial3D> material = static_cast<Ref<StandardMaterial3D>>(debug_material); + material->set_albedo(color); +} + void RayCast3D::_update_debug_shape() { if (!enabled) { return; @@ -348,23 +454,34 @@ void RayCast3D::_update_debug_shape() { } MeshInstance3D *mi = static_cast<MeshInstance3D *>(debug_shape); - if (!mi->get_mesh().is_valid()) { + Ref<ArrayMesh> mesh = mi->get_mesh(); + if (!mesh.is_valid()) { return; } - Ref<ArrayMesh> mesh = mi->get_mesh(); + _update_debug_shape_vertices(); + mesh->clear_surfaces(); Array a; a.resize(Mesh::ARRAY_MAX); - Vector<Vector3> verts; - verts.push_back(Vector3()); - verts.push_back(target_position); - a[Mesh::ARRAY_VERTEX] = verts; + uint32_t flags = 0; + int surface_count = 0; - mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, a); - mesh->surface_set_material(0, debug_material); + if (!debug_line_vertices.is_empty()) { + a[Mesh::ARRAY_VERTEX] = debug_line_vertices; + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_LINES, a, Array(), Dictionary(), flags); + mesh->surface_set_material(surface_count, debug_material); + ++surface_count; + } + + if (!debug_shape_vertices.is_empty()) { + a[Mesh::ARRAY_VERTEX] = debug_shape_vertices; + mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLE_STRIP, a, Array(), Dictionary(), flags); + mesh->surface_set_material(surface_count, debug_material); + ++surface_count; + } } void RayCast3D::_clear_debug_shape() { @@ -383,13 +500,4 @@ void RayCast3D::_clear_debug_shape() { } RayCast3D::RayCast3D() { - enabled = true; - collided = false; - against_shape = 0; - collision_mask = 1; - target_position = Vector3(0, -1, 0); - debug_shape = nullptr; - exclude_parent_body = true; - collide_with_areas = false; - collide_with_bodies = true; } diff --git a/scene/3d/ray_cast_3d.h b/scene/3d/ray_cast_3d.h index f4fe7ba621..3828bfb4c4 100644 --- a/scene/3d/ray_cast_3d.h +++ b/scene/3d/ray_cast_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,28 +36,34 @@ class RayCast3D : public Node3D { GDCLASS(RayCast3D, Node3D); - bool enabled; - bool collided; + bool enabled = true; + bool collided = false; ObjectID against; - int against_shape; + int against_shape = 0; Vector3 collision_point; Vector3 collision_normal; - Vector3 target_position; + Vector3 target_position = Vector3(0, -1, 0); Set<RID> exclude; - uint32_t collision_mask; - bool exclude_parent_body; + uint32_t collision_mask = 1; + bool exclude_parent_body = true; - Node *debug_shape; + Node *debug_shape = nullptr; Ref<Material> debug_material; + Color debug_shape_custom_color = Color(0.0, 0.0, 0.0); + int debug_shape_thickness = 2; + Vector<Vector3> debug_shape_vertices; + Vector<Vector3> debug_line_vertices; void _create_debug_shape(); void _update_debug_shape(); + void _update_debug_shape_material(bool p_check_collision = false); + void _update_debug_shape_vertices(); void _clear_debug_shape(); - bool collide_with_areas; - bool collide_with_bodies; + bool collide_with_areas = false; + bool collide_with_bodies = true; protected: void _notification(int p_what); @@ -80,12 +86,23 @@ public: void set_collision_mask(uint32_t p_mask); uint32_t get_collision_mask() const; - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; + void set_collision_mask_value(int p_layer_number, bool p_value); + bool get_collision_mask_value(int p_layer_number) const; void set_exclude_parent_body(bool p_exclude_parent_body); bool get_exclude_parent_body() const; + const Color &get_debug_shape_custom_color() const; + void set_debug_shape_custom_color(const Color &p_color); + + const Vector<Vector3> &get_debug_shape_vertices() const; + const Vector<Vector3> &get_debug_line_vertices() const; + + Ref<StandardMaterial3D> get_debug_material(); + + float get_debug_shape_thickness() const; + void set_debug_shape_thickness(const float p_debug_thickness); + void force_raycast_update(); bool is_colliding() const; Object *get_collider() const; diff --git a/scene/3d/reflection_probe.cpp b/scene/3d/reflection_probe.cpp index c7948395d3..719dbedd94 100644 --- a/scene/3d/reflection_probe.cpp +++ b/scene/3d/reflection_probe.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -42,7 +42,7 @@ float ReflectionProbe::get_intensity() const { void ReflectionProbe::set_ambient_mode(AmbientMode p_mode) { ambient_mode = p_mode; RS::get_singleton()->reflection_probe_set_ambient_mode(probe, RS::ReflectionProbeAmbientMode(p_mode)); - _change_notify(); + notify_property_list_changed(); } ReflectionProbe::AmbientMode ReflectionProbe::get_ambient_mode() const { @@ -76,6 +76,15 @@ float ReflectionProbe::get_max_distance() const { return max_distance; } +void ReflectionProbe::set_lod_threshold(float p_pixels) { + lod_threshold = p_pixels; + RS::get_singleton()->reflection_probe_set_lod_threshold(probe, p_pixels); +} + +float ReflectionProbe::get_lod_threshold() const { + return lod_threshold; +} + void ReflectionProbe::set_extents(const Vector3 &p_extents) { extents = p_extents; @@ -86,14 +95,13 @@ void ReflectionProbe::set_extents(const Vector3 &p_extents) { if (extents[i] - 0.01 < ABS(origin_offset[i])) { origin_offset[i] = SGN(origin_offset[i]) * (extents[i] - 0.01); - _change_notify("origin_offset"); } } RS::get_singleton()->reflection_probe_set_extents(probe, extents); RS::get_singleton()->reflection_probe_set_origin_offset(probe, origin_offset); - _change_notify("extents"); - update_gizmo(); + + update_gizmos(); } Vector3 ReflectionProbe::get_extents() const { @@ -111,8 +119,7 @@ void ReflectionProbe::set_origin_offset(const Vector3 &p_extents) { RS::get_singleton()->reflection_probe_set_extents(probe, extents); RS::get_singleton()->reflection_probe_set_origin_offset(probe, origin_offset); - _change_notify("origin_offset"); - update_gizmo(); + update_gizmos(); } Vector3 ReflectionProbe::get_origin_offset() const { @@ -199,6 +206,9 @@ void ReflectionProbe::_bind_methods() { ClassDB::bind_method(D_METHOD("set_max_distance", "max_distance"), &ReflectionProbe::set_max_distance); ClassDB::bind_method(D_METHOD("get_max_distance"), &ReflectionProbe::get_max_distance); + ClassDB::bind_method(D_METHOD("set_lod_threshold", "ratio"), &ReflectionProbe::set_lod_threshold); + ClassDB::bind_method(D_METHOD("get_lod_threshold"), &ReflectionProbe::get_lod_threshold); + ClassDB::bind_method(D_METHOD("set_extents", "extents"), &ReflectionProbe::set_extents); ClassDB::bind_method(D_METHOD("get_extents"), &ReflectionProbe::get_extents); @@ -220,18 +230,19 @@ void ReflectionProbe::_bind_methods() { ClassDB::bind_method(D_METHOD("set_update_mode", "mode"), &ReflectionProbe::set_update_mode); ClassDB::bind_method(D_METHOD("get_update_mode"), &ReflectionProbe::get_update_mode); - ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "Once,Always"), "set_update_mode", "get_update_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "update_mode", PROPERTY_HINT_ENUM, "Once (Fast),Always (Slow)"), "set_update_mode", "get_update_mode"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "intensity", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_intensity", "get_intensity"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_EXP_RANGE, "0,16384,0.1,or_greater"), "set_max_distance", "get_max_distance"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "max_distance", PROPERTY_HINT_RANGE, "0,16384,0.1,or_greater,exp"), "set_max_distance", "get_max_distance"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents"), "set_extents", "get_extents"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "origin_offset"), "set_origin_offset", "get_origin_offset"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "box_projection"), "set_enable_box_projection", "is_box_projection_enabled"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interior"), "set_as_interior", "is_set_as_interior"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "enable_shadows"), "set_enable_shadows", "are_shadows_enabled"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cull_mask", PROPERTY_HINT_LAYERS_3D_RENDER), "set_cull_mask", "get_cull_mask"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lod_threshold", PROPERTY_HINT_RANGE, "0,1024,0.1"), "set_lod_threshold", "get_lod_threshold"); ADD_GROUP("Ambient", "ambient_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "ambient_mode", PROPERTY_HINT_ENUM, "Disabled,Environment,ConstantColor"), "set_ambient_mode", "get_ambient_mode"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "ambient_mode", PROPERTY_HINT_ENUM, "Disabled,Environment,Constant Color"), "set_ambient_mode", "get_ambient_mode"); ADD_PROPERTY(PropertyInfo(Variant::COLOR, "ambient_color", PROPERTY_HINT_COLOR_NO_ALPHA), "set_ambient_color", "get_ambient_color"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ambient_color_energy", PROPERTY_HINT_RANGE, "0,16,0.01"), "set_ambient_color_energy", "get_ambient_color_energy"); @@ -244,19 +255,6 @@ void ReflectionProbe::_bind_methods() { } ReflectionProbe::ReflectionProbe() { - intensity = 1.0; - ambient_mode = AMBIENT_ENVIRONMENT; - ambient_color = Color(0, 0, 0); - ambient_color_energy = 1.0; - max_distance = 0; - extents = Vector3(1, 1, 1); - origin_offset = Vector3(0, 0, 0); - box_projection = false; - interior = false; - enable_shadows = false; - cull_mask = (1 << 20) - 1; - update_mode = UPDATE_ONCE; - probe = RenderingServer::get_singleton()->reflection_probe_create(); RS::get_singleton()->instance_set_base(get_instance(), probe); set_disable_scale(true); diff --git a/scene/3d/reflection_probe.h b/scene/3d/reflection_probe.h index 56177d0f95..d1b9b12f65 100644 --- a/scene/3d/reflection_probe.h +++ b/scene/3d/reflection_probe.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,9 +32,6 @@ #define REFLECTIONPROBE_H #include "scene/3d/visual_instance_3d.h" -#include "scene/resources/sky.h" -#include "scene/resources/texture.h" -#include "servers/rendering_server.h" class ReflectionProbe : public VisualInstance3D { GDCLASS(ReflectionProbe, VisualInstance3D); @@ -53,19 +50,20 @@ public: private: RID probe; - float intensity; - float max_distance; - Vector3 extents; - Vector3 origin_offset; - bool box_projection; - bool enable_shadows; - bool interior; - AmbientMode ambient_mode; - Color ambient_color; - float ambient_color_energy; - - uint32_t cull_mask; - UpdateMode update_mode; + float intensity = 1.0; + float max_distance = 0.0; + Vector3 extents = Vector3(10, 10, 10); + Vector3 origin_offset = Vector3(0, 0, 0); + bool box_projection = false; + bool enable_shadows = false; + bool interior = false; + AmbientMode ambient_mode = AMBIENT_ENVIRONMENT; + Color ambient_color = Color(0, 0, 0); + float ambient_color_energy = 1.0; + float lod_threshold = 1.0; + + uint32_t cull_mask = (1 << 20) - 1; + UpdateMode update_mode = UPDATE_ONCE; protected: static void _bind_methods(); @@ -90,6 +88,9 @@ public: void set_max_distance(float p_distance); float get_max_distance() const; + void set_lod_threshold(float p_pixels); + float get_lod_threshold() const; + void set_extents(const Vector3 &p_extents); Vector3 get_extents() const; diff --git a/scene/3d/remote_transform_3d.cpp b/scene/3d/remote_transform_3d.cpp index 358f9346f8..d5fb1fa6ab 100644 --- a/scene/3d/remote_transform_3d.cpp +++ b/scene/3d/remote_transform_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,7 +34,7 @@ void RemoteTransform3D::_update_cache() { cache = ObjectID(); if (has_node(remote_node)) { Node *node = get_node(remote_node); - if (!node || this == node || node->is_a_parent_of(this) || this->is_a_parent_of(node)) { + if (!node || this == node || node->is_ancestor_of(this) || this->is_ancestor_of(node)) { return; } @@ -65,7 +65,7 @@ void RemoteTransform3D::_update_remote() { if (update_remote_position && update_remote_rotation && update_remote_scale) { n->set_global_transform(get_global_transform()); } else { - Transform our_trans = get_global_transform(); + Transform3D our_trans = get_global_transform(); if (update_remote_rotation) { n->set_rotation(our_trans.basis.get_rotation()); @@ -76,7 +76,7 @@ void RemoteTransform3D::_update_remote() { } if (update_remote_position) { - Transform n_trans = n->get_global_transform(); + Transform3D n_trans = n->get_global_transform(); n_trans.set_origin(our_trans.get_origin()); n->set_global_transform(n_trans); @@ -87,7 +87,7 @@ void RemoteTransform3D::_update_remote() { if (update_remote_position && update_remote_rotation && update_remote_scale) { n->set_transform(get_transform()); } else { - Transform our_trans = get_transform(); + Transform3D our_trans = get_transform(); if (update_remote_rotation) { n->set_rotation(our_trans.basis.get_rotation()); @@ -98,7 +98,7 @@ void RemoteTransform3D::_update_remote() { } if (update_remote_position) { - Transform n_trans = n->get_transform(); + Transform3D n_trans = n->get_transform(); n_trans.set_origin(our_trans.get_origin()); n->set_transform(n_trans); @@ -133,7 +133,7 @@ void RemoteTransform3D::set_remote_node(const NodePath &p_remote_node) { _update_remote(); } - update_configuration_warning(); + update_configuration_warnings(); } NodePath RemoteTransform3D::get_remote_node() const { @@ -179,17 +179,14 @@ void RemoteTransform3D::force_update_cache() { _update_cache(); } -String RemoteTransform3D::get_configuration_warning() const { - String warning = Node3D::get_configuration_warning(); +TypedArray<String> RemoteTransform3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (!has_node(remote_node) || !Object::cast_to<Node3D>(get_node(remote_node))) { - if (!warning.empty()) { - warning += "\n\n"; - } - warning += TTR("The \"Remote Path\" property must point to a valid Node3D or Node3D-derived node to work."); + warnings.push_back(TTR("The \"Remote Path\" property must point to a valid Node3D or Node3D-derived node to work.")); } - return warning; + return warnings; } void RemoteTransform3D::_bind_methods() { @@ -217,10 +214,5 @@ void RemoteTransform3D::_bind_methods() { } RemoteTransform3D::RemoteTransform3D() { - use_global_coordinates = true; - update_remote_position = true; - update_remote_rotation = true; - update_remote_scale = true; - set_notify_transform(true); } diff --git a/scene/3d/remote_transform_3d.h b/scene/3d/remote_transform_3d.h index 0ce30f7fce..321bd3b51e 100644 --- a/scene/3d/remote_transform_3d.h +++ b/scene/3d/remote_transform_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -40,10 +40,10 @@ class RemoteTransform3D : public Node3D { ObjectID cache; - bool use_global_coordinates; - bool update_remote_position; - bool update_remote_rotation; - bool update_remote_scale; + bool use_global_coordinates = true; + bool update_remote_position = true; + bool update_remote_rotation = true; + bool update_remote_scale = true; void _update_remote(); void _update_cache(); @@ -70,7 +70,7 @@ public: void force_update_cache(); - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; RemoteTransform3D(); }; diff --git a/scene/3d/skeleton_3d.cpp b/scene/3d/skeleton_3d.cpp index 6723ca04b9..94fb49ae81 100644 --- a/scene/3d/skeleton_3d.cpp +++ b/scene/3d/skeleton_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,11 +30,10 @@ #include "skeleton_3d.h" -#include "core/engine.h" -#include "core/message_queue.h" -#include "core/project_settings.h" -#include "core/type_info.h" +#include "core/object/message_queue.h" +#include "core/variant/type_info.h" #include "scene/3d/physics_body_3d.h" +#include "scene/resources/skeleton_modification_3d.h" #include "scene/resources/surface_tool.h" #include "scene/scene_string_names.h" @@ -72,6 +71,13 @@ SkinReference::~SkinReference() { bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) { String path = p_path; +#ifndef _3D_DISABLED + if (path.begins_with("modification_stack")) { + set_modification_stack(p_value); + return true; + } +#endif //_3D_DISABLED + if (!path.begins_with("bones/")) { return false; } @@ -94,20 +100,6 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) { set_bone_enabled(which, p_value); } else if (what == "pose") { set_bone_pose(which, p_value); - } else if (what == "bound_children") { - Array children = p_value; - - if (is_inside_tree()) { - bones.write[which].nodes_bound.clear(); - - for (int i = 0; i < children.size(); i++) { - NodePath npath = children[i]; - ERR_CONTINUE(npath.operator String() == ""); - Node *node = get_node(npath); - ERR_CONTINUE(!node); - bind_child_node_to_bone(which, node); - } - } } else { return false; } @@ -118,6 +110,13 @@ bool Skeleton3D::_set(const StringName &p_path, const Variant &p_value) { bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const { String path = p_path; +#ifndef _3D_DISABLED + if (path.begins_with("modification_stack")) { + r_ret = modification_stack; + return true; + } +#endif //_3D_DISABLED + if (!path.begins_with("bones/")) { return false; } @@ -137,19 +136,6 @@ bool Skeleton3D::_get(const StringName &p_path, Variant &r_ret) const { r_ret = is_bone_enabled(which); } else if (what == "pose") { r_ret = get_bone_pose(which); - } else if (what == "bound_children") { - Array children; - - for (const List<ObjectID>::Element *E = bones[which].nodes_bound.front(); E; E = E->next()) { - Object *obj = ObjectDB::get_instance(E->get()); - ERR_CONTINUE(!obj); - Node *node = Object::cast_to<Node>(obj); - ERR_CONTINUE(!node); - NodePath npath = get_path_to(node); - children.push_back(npath); - } - - r_ret = children; } else { return false; } @@ -162,11 +148,18 @@ void Skeleton3D::_get_property_list(List<PropertyInfo> *p_list) const { String prep = "bones/" + itos(i) + "/"; p_list->push_back(PropertyInfo(Variant::STRING, prep + "name", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); p_list->push_back(PropertyInfo(Variant::INT, prep + "parent", PROPERTY_HINT_RANGE, "-1," + itos(bones.size() - 1) + ",1", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::TRANSFORM, prep + "rest", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + "rest", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); p_list->push_back(PropertyInfo(Variant::BOOL, prep + "enabled", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::TRANSFORM, prep + "pose", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); - p_list->push_back(PropertyInfo(Variant::ARRAY, prep + "bound_children", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); + p_list->push_back(PropertyInfo(Variant::TRANSFORM3D, prep + "pose", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR)); } + +#ifndef _3D_DISABLED + p_list->push_back( + PropertyInfo(Variant::OBJECT, "modification_stack", + PROPERTY_HINT_RESOURCE_TYPE, + "SkeletonModificationStack3D", + PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE)); +#endif //_3D_DISABLED } void Skeleton3D::_update_process_order() { @@ -177,47 +170,32 @@ void Skeleton3D::_update_process_order() { Bone *bonesptr = bones.ptrw(); int len = bones.size(); - process_order.resize(len); - int *order = process_order.ptrw(); + parentless_bones.clear(); + + for (int i = 0; i < len; i++) { + bonesptr[i].child_bones.clear(); + } + for (int i = 0; i < len; i++) { if (bonesptr[i].parent >= len) { //validate this just in case ERR_PRINT("Bone " + itos(i) + " has invalid parent: " + itos(bonesptr[i].parent)); bonesptr[i].parent = -1; } - order[i] = i; - bonesptr[i].sort_index = i; - } - //now check process order - int pass_count = 0; - while (pass_count < len * len) { - //using bubblesort because of simplicity, it won't run every frame though. - //bublesort worst case is O(n^2), and this may be an infinite loop if cyclic - bool swapped = false; - for (int i = 0; i < len; i++) { - int parent_idx = bonesptr[order[i]].parent; - if (parent_idx < 0) { - continue; //do nothing because it has no parent - } - //swap indices - int parent_order = bonesptr[parent_idx].sort_index; - if (parent_order > i) { - bonesptr[order[i]].sort_index = parent_order; - bonesptr[parent_idx].sort_index = i; - //swap order - SWAP(order[i], order[parent_order]); - swapped = true; - } - } - if (!swapped) { - break; - } - pass_count++; - } + if (bonesptr[i].parent != -1) { + int parent_bone_idx = bonesptr[i].parent; - if (pass_count == len * len) { - ERR_PRINT("Skeleton3D parenthood graph is cyclic"); + // Check to see if this node is already added to the parent: + if (bonesptr[parent_bone_idx].child_bones.find(i) < 0) { + // Add the child node + bonesptr[parent_bone_idx].child_bones.push_back(i); + } else { + ERR_PRINT("Skeleton3D parenthood graph is cyclic"); + } + } else { + parentless_bones.push_back(i); + } } process_order_dirty = false; @@ -228,74 +206,12 @@ void Skeleton3D::_notification(int p_what) { case NOTIFICATION_UPDATE_SKELETON: { RenderingServer *rs = RenderingServer::get_singleton(); Bone *bonesptr = bones.ptrw(); - int len = bones.size(); - - _update_process_order(); - - const int *order = process_order.ptr(); - - for (int i = 0; i < len; i++) { - Bone &b = bonesptr[order[i]]; - if (b.global_pose_override_amount >= 0.999) { - b.pose_global = b.global_pose_override; - } else { - if (b.disable_rest) { - if (b.enabled) { - Transform pose = b.pose; - if (b.custom_pose_enable) { - pose = b.custom_pose * pose; - } - if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global * pose; - } else { - b.pose_global = pose; - } - } else { - if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global; - } else { - b.pose_global = Transform(); - } - } - - } else { - if (b.enabled) { - Transform pose = b.pose; - if (b.custom_pose_enable) { - pose = b.custom_pose * pose; - } - if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global * (b.rest * pose); - } else { - b.pose_global = b.rest * pose; - } - } else { - if (b.parent >= 0) { - b.pose_global = bonesptr[b.parent].pose_global * b.rest; - } else { - b.pose_global = b.rest; - } - } - } - - if (b.global_pose_override_amount >= CMP_EPSILON) { - b.pose_global = b.pose_global.interpolate_with(b.global_pose_override, b.global_pose_override_amount); - } - } - - if (b.global_pose_override_reset) { - b.global_pose_override_amount = 0.0; - } + int len = bones.size(); + dirty = false; - for (List<ObjectID>::Element *E = b.nodes_bound.front(); E; E = E->next()) { - Object *obj = ObjectDB::get_instance(E->get()); - ERR_CONTINUE(!obj); - Node3D *node_3d = Object::cast_to<Node3D>(obj); - ERR_CONTINUE(!node_3d); - node_3d->set_transform(b.pose_global); - } - } + // Update bone transforms + force_update_all_bone_transforms(); //update skins for (Set<SkinReference *>::Element *E = skin_bindings.front(); E; E = E->next()) { @@ -304,7 +220,7 @@ void Skeleton3D::_notification(int p_what) { uint32_t bind_count = skin->get_bind_count(); if (E->get()->bind_count != bind_count) { - RS::get_singleton()->skeleton_allocate(skeleton, bind_count); + RS::get_singleton()->skeleton_allocate_data(skeleton, bind_count); E->get()->bind_count = bind_count; E->get()->skin_bone_indices.resize(bind_count); E->get()->skin_bone_indices_ptrs = E->get()->skin_bone_indices.ptrw(); @@ -353,8 +269,6 @@ void Skeleton3D::_notification(int p_what) { } } - dirty = false; - #ifdef TOOLS_ENABLED emit_signal(SceneStringNames::get_singleton()->pose_updated); #endif // TOOLS_ENABLED @@ -365,48 +279,180 @@ void Skeleton3D::_notification(int p_what) { case NOTIFICATION_INTERNAL_PHYSICS_PROCESS: { // This is active only if the skeleton animates the physical bones // and the state of the bone is not active. - if (animate_physical_bones) { - for (int i = 0; i < bones.size(); i += 1) { - if (bones[i].physical_bone) { - if (bones[i].physical_bone->is_simulating_physics() == false) { - bones[i].physical_bone->reset_to_rest_position(); + if (Engine::get_singleton()->is_editor_hint()) { + if (animate_physical_bones) { + for (int i = 0; i < bones.size(); i += 1) { + if (bones[i].physical_bone) { + if (bones[i].physical_bone->is_simulating_physics() == false) { + bones[i].physical_bone->reset_to_rest_position(); + } } } } } + + if (modification_stack.is_valid()) { + execute_modifications(get_physics_process_delta_time(), SkeletonModificationStack3D::EXECUTION_MODE::execution_mode_physics_process); + } + + } break; +#endif // _3D_DISABLED + +#ifndef _3D_DISABLED + case NOTIFICATION_INTERNAL_PROCESS: { + if (modification_stack.is_valid()) { + execute_modifications(get_process_delta_time(), SkeletonModificationStack3D::EXECUTION_MODE::execution_mode_process); + } } break; +#endif // _3D_DISABLED + +#ifndef _3D_DISABLED case NOTIFICATION_READY: { - if (Engine::get_singleton()->is_editor_hint()) { - set_physics_process_internal(true); + set_physics_process_internal(true); + set_process_internal(true); + + if (modification_stack.is_valid()) { + set_modification_stack(modification_stack); } } break; -#endif +#endif // _3D_DISABLED } } void Skeleton3D::clear_bones_global_pose_override() { for (int i = 0; i < bones.size(); i += 1) { bones.write[i].global_pose_override_amount = 0; + bones.write[i].global_pose_override_reset = true; } _make_dirty(); } -void Skeleton3D::set_bone_global_pose_override(int p_bone, const Transform &p_pose, float p_amount, bool p_persistent) { - ERR_FAIL_INDEX(p_bone, bones.size()); +void Skeleton3D::set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); bones.write[p_bone].global_pose_override_amount = p_amount; bones.write[p_bone].global_pose_override = p_pose; bones.write[p_bone].global_pose_override_reset = !p_persistent; _make_dirty(); } -Transform Skeleton3D::get_bone_global_pose(int p_bone) const { - ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform()); +Transform3D Skeleton3D::get_bone_global_pose_override(int p_bone) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); + return bones[p_bone].global_pose_override; +} + +Transform3D Skeleton3D::get_bone_global_pose(int p_bone) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); if (dirty) { const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON); } return bones[p_bone].pose_global; } +Transform3D Skeleton3D::get_bone_global_pose_no_override(int p_bone) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); + if (dirty) { + const_cast<Skeleton3D *>(this)->notification(NOTIFICATION_UPDATE_SKELETON); + } + return bones[p_bone].pose_global_no_override; +} + +void Skeleton3D::clear_bones_local_pose_override() { + for (int i = 0; i < bones.size(); i += 1) { + bones.write[i].local_pose_override_amount = 0; + } + _make_dirty(); +} + +void Skeleton3D::set_bone_local_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); + bones.write[p_bone].local_pose_override_amount = p_amount; + bones.write[p_bone].local_pose_override = p_pose; + bones.write[p_bone].local_pose_override_reset = !p_persistent; + _make_dirty(); +} + +Transform3D Skeleton3D::get_bone_local_pose_override(int p_bone) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); + return bones[p_bone].local_pose_override; +} + +void Skeleton3D::update_bone_rest_forward_vector(int p_bone, bool p_force_update) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); + + if (bones[p_bone].rest_bone_forward_vector.length_squared() > 0 && p_force_update == false) { + update_bone_rest_forward_axis(p_bone, p_force_update); + } + + // If it is a child/leaf bone... + if (get_bone_parent(p_bone) > 0) { + bones.write[p_bone].rest_bone_forward_vector = bones[p_bone].rest.origin.normalized(); + } else { + // If it has children... + Vector<int> child_bones = get_bone_children(p_bone); + if (child_bones.size() > 0) { + Vector3 combined_child_dir = Vector3(0, 0, 0); + for (int i = 0; i < child_bones.size(); i++) { + combined_child_dir += bones[child_bones[i]].rest.origin.normalized(); + } + combined_child_dir = combined_child_dir / child_bones.size(); + bones.write[p_bone].rest_bone_forward_vector = combined_child_dir.normalized(); + } else { + WARN_PRINT_ONCE("Cannot calculate forward direction for bone " + itos(p_bone)); + WARN_PRINT_ONCE("Assuming direction of (0, 1, 0) for bone"); + bones.write[p_bone].rest_bone_forward_vector = Vector3(0, 1, 0); + } + } + update_bone_rest_forward_axis(p_bone, p_force_update); +} + +void Skeleton3D::update_bone_rest_forward_axis(int p_bone, bool p_force_update) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); + if (bones[p_bone].rest_bone_forward_axis > -1 && p_force_update == false) { + return; + } + + Vector3 forward_axis_absolute = bones[p_bone].rest_bone_forward_vector.abs(); + if (forward_axis_absolute.x > forward_axis_absolute.y && forward_axis_absolute.x > forward_axis_absolute.z) { + if (bones[p_bone].rest_bone_forward_vector.x > 0) { + bones.write[p_bone].rest_bone_forward_axis = BONE_AXIS_X_FORWARD; + } else { + bones.write[p_bone].rest_bone_forward_axis = BONE_AXIS_NEGATIVE_X_FORWARD; + } + } else if (forward_axis_absolute.y > forward_axis_absolute.x && forward_axis_absolute.y > forward_axis_absolute.z) { + if (bones[p_bone].rest_bone_forward_vector.y > 0) { + bones.write[p_bone].rest_bone_forward_axis = BONE_AXIS_Y_FORWARD; + } else { + bones.write[p_bone].rest_bone_forward_axis = BONE_AXIS_NEGATIVE_Y_FORWARD; + } + } else { + if (bones[p_bone].rest_bone_forward_vector.z > 0) { + bones.write[p_bone].rest_bone_forward_axis = BONE_AXIS_Z_FORWARD; + } else { + bones.write[p_bone].rest_bone_forward_axis = BONE_AXIS_NEGATIVE_Z_FORWARD; + } + } +} + +Vector3 Skeleton3D::get_bone_axis_forward_vector(int p_bone) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Vector3(0, 0, 0)); + return bones[p_bone].rest_bone_forward_vector; +} + +int Skeleton3D::get_bone_axis_forward_enum(int p_bone) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, -1); + return bones[p_bone].rest_bone_forward_axis; +} + // skeleton creation api void Skeleton3D::add_bone(const String &p_name) { ERR_FAIL_COND(p_name == "" || p_name.find(":") != -1 || p_name.find("/") != -1); @@ -421,7 +467,7 @@ void Skeleton3D::add_bone(const String &p_name) { process_order_dirty = true; version++; _make_dirty(); - update_gizmo(); + update_gizmos(); } int Skeleton3D::find_bone(const String &p_name) const { @@ -435,10 +481,22 @@ int Skeleton3D::find_bone(const String &p_name) const { } String Skeleton3D::get_bone_name(int p_bone) const { - ERR_FAIL_INDEX_V(p_bone, bones.size(), ""); - + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, ""); return bones[p_bone].name; } +void Skeleton3D::set_bone_name(int p_bone, const String &p_name) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); + + for (int i = 0; i < bone_size; i++) { + if (i != p_bone) { + ERR_FAIL_COND(bones[i].name == p_name); + } + } + + bones.write[p_bone].name = p_name; +} bool Skeleton3D::is_bone_parent_of(int p_bone, int p_parent_bone_id) const { int parent_of_bone = get_bone_parent(p_bone); @@ -459,7 +517,8 @@ int Skeleton3D::get_bone_count() const { } void Skeleton3D::set_bone_parent(int p_bone, int p_parent) { - ERR_FAIL_INDEX(p_bone, bones.size()); + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); ERR_FAIL_COND(p_parent != -1 && (p_parent < 0)); bones.write[p_bone].parent = p_parent; @@ -468,7 +527,8 @@ void Skeleton3D::set_bone_parent(int p_bone, int p_parent) { } void Skeleton3D::unparent_bone_and_rest(int p_bone) { - ERR_FAIL_INDEX(p_bone, bones.size()); + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); _update_process_order(); @@ -485,77 +545,93 @@ void Skeleton3D::unparent_bone_and_rest(int p_bone) { } void Skeleton3D::set_bone_disable_rest(int p_bone, bool p_disable) { - ERR_FAIL_INDEX(p_bone, bones.size()); + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); bones.write[p_bone].disable_rest = p_disable; } bool Skeleton3D::is_bone_rest_disabled(int p_bone) const { - ERR_FAIL_INDEX_V(p_bone, bones.size(), false); + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, false); return bones[p_bone].disable_rest; } int Skeleton3D::get_bone_parent(int p_bone) const { - ERR_FAIL_INDEX_V(p_bone, bones.size(), -1); + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, -1); return bones[p_bone].parent; } -void Skeleton3D::set_bone_rest(int p_bone, const Transform &p_rest) { - ERR_FAIL_INDEX(p_bone, bones.size()); +Vector<int> Skeleton3D::get_bone_children(int p_bone) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Vector<int>()); + return bones[p_bone].child_bones; +} + +void Skeleton3D::set_bone_children(int p_bone, Vector<int> p_children) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); + bones.write[p_bone].child_bones = p_children; - bones.write[p_bone].rest = p_rest; + process_order_dirty = true; _make_dirty(); } -Transform Skeleton3D::get_bone_rest(int p_bone) const { - ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform()); +void Skeleton3D::add_bone_child(int p_bone, int p_child) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); + bones.write[p_bone].child_bones.push_back(p_child); - return bones[p_bone].rest; + process_order_dirty = true; + _make_dirty(); } -void Skeleton3D::set_bone_enabled(int p_bone, bool p_enabled) { - ERR_FAIL_INDEX(p_bone, bones.size()); +void Skeleton3D::remove_bone_child(int p_bone, int p_child) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); - bones.write[p_bone].enabled = p_enabled; + int child_idx = bones[p_bone].child_bones.find(p_child); + if (child_idx >= 0) { + bones.write[p_bone].child_bones.remove(child_idx); + } else { + WARN_PRINT("Cannot remove child bone: Child bone not found."); + } + + process_order_dirty = true; _make_dirty(); } -bool Skeleton3D::is_bone_enabled(int p_bone) const { - ERR_FAIL_INDEX_V(p_bone, bones.size(), false); - return bones[p_bone].enabled; +Vector<int> Skeleton3D::get_parentless_bones() { + return parentless_bones; } -void Skeleton3D::bind_child_node_to_bone(int p_bone, Node *p_node) { - ERR_FAIL_NULL(p_node); - ERR_FAIL_INDEX(p_bone, bones.size()); +void Skeleton3D::set_bone_rest(int p_bone, const Transform3D &p_rest) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); - ObjectID id = p_node->get_instance_id(); - - for (const List<ObjectID>::Element *E = bones[p_bone].nodes_bound.front(); E; E = E->next()) { - if (E->get() == id) { - return; // already here - } - } + bones.write[p_bone].rest = p_rest; + _make_dirty(); +} +Transform3D Skeleton3D::get_bone_rest(int p_bone) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); - bones.write[p_bone].nodes_bound.push_back(id); + return bones[p_bone].rest; } -void Skeleton3D::unbind_child_node_from_bone(int p_bone, Node *p_node) { - ERR_FAIL_NULL(p_node); - ERR_FAIL_INDEX(p_bone, bones.size()); +void Skeleton3D::set_bone_enabled(int p_bone, bool p_enabled) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); - ObjectID id = p_node->get_instance_id(); - bones.write[p_bone].nodes_bound.erase(id); + bones.write[p_bone].enabled = p_enabled; + _make_dirty(); } -void Skeleton3D::get_bound_child_nodes_to_bone(int p_bone, List<Node *> *p_bound) const { - ERR_FAIL_INDEX(p_bone, bones.size()); - - for (const List<ObjectID>::Element *E = bones[p_bone].nodes_bound.front(); E; E = E->next()) { - Object *obj = ObjectDB::get_instance(E->get()); - ERR_CONTINUE(!obj); - p_bound->push_back(Object::cast_to<Node>(obj)); - } +bool Skeleton3D::is_bone_enabled(int p_bone) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, false); + return bones[p_bone].enabled; } void Skeleton3D::clear_bones() { @@ -567,32 +643,35 @@ void Skeleton3D::clear_bones() { // posing api -void Skeleton3D::set_bone_pose(int p_bone, const Transform &p_pose) { - ERR_FAIL_INDEX(p_bone, bones.size()); +void Skeleton3D::set_bone_pose(int p_bone, const Transform3D &p_pose) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); bones.write[p_bone].pose = p_pose; if (is_inside_tree()) { _make_dirty(); } } - -Transform Skeleton3D::get_bone_pose(int p_bone) const { - ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform()); +Transform3D Skeleton3D::get_bone_pose(int p_bone) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); return bones[p_bone].pose; } -void Skeleton3D::set_bone_custom_pose(int p_bone, const Transform &p_custom_pose) { - ERR_FAIL_INDEX(p_bone, bones.size()); +void Skeleton3D::set_bone_custom_pose(int p_bone, const Transform3D &p_custom_pose) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); //ERR_FAIL_COND( !is_inside_scene() ); - bones.write[p_bone].custom_pose_enable = (p_custom_pose != Transform()); + bones.write[p_bone].custom_pose_enable = (p_custom_pose != Transform3D()); bones.write[p_bone].custom_pose = p_custom_pose; _make_dirty(); } -Transform Skeleton3D::get_bone_custom_pose(int p_bone) const { - ERR_FAIL_INDEX_V(p_bone, bones.size(), Transform()); +Transform3D Skeleton3D::get_bone_custom_pose(int p_bone) const { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, Transform3D()); return bones[p_bone].custom_pose; } @@ -605,30 +684,26 @@ void Skeleton3D::_make_dirty() { dirty = true; } -int Skeleton3D::get_process_order(int p_idx) { - ERR_FAIL_INDEX_V(p_idx, bones.size(), -1); +void Skeleton3D::localize_rests() { _update_process_order(); - return process_order[p_idx]; -} -Vector<int> Skeleton3D::get_bone_process_orders() { - _update_process_order(); - return process_order; -} + Vector<int> bones_to_process = get_parentless_bones(); + while (bones_to_process.size() > 0) { + int current_bone_idx = bones_to_process[0]; + bones_to_process.erase(current_bone_idx); -void Skeleton3D::localize_rests() { - _update_process_order(); + if (bones[current_bone_idx].parent >= 0) { + set_bone_rest(current_bone_idx, bones[bones[current_bone_idx].parent].rest.affine_inverse() * bones[current_bone_idx].rest); + } - for (int i = bones.size() - 1; i >= 0; i--) { - int idx = process_order[i]; - if (bones[idx].parent >= 0) { - set_bone_rest(idx, bones[bones[idx].parent].rest.affine_inverse() * bones[idx].rest); + // Add the bone's children to the list of bones to be processed + int child_bone_size = bones[current_bone_idx].child_bones.size(); + for (int i = 0; i < child_bone_size; i++) { + bones_to_process.push_back(bones[current_bone_idx].child_bones[i]); } } } -#ifndef _3D_DISABLED - void Skeleton3D::set_animate_physical_bones(bool p_animate) { animate_physical_bones = p_animate; @@ -651,7 +726,8 @@ bool Skeleton3D::get_animate_physical_bones() const { } void Skeleton3D::bind_physical_bone_to_bone(int p_bone, PhysicalBone3D *p_physical_bone) { - ERR_FAIL_INDEX(p_bone, bones.size()); + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); ERR_FAIL_COND(bones[p_bone].physical_bone); ERR_FAIL_COND(!p_physical_bone); bones.write[p_bone].physical_bone = p_physical_bone; @@ -660,20 +736,23 @@ void Skeleton3D::bind_physical_bone_to_bone(int p_bone, PhysicalBone3D *p_physic } void Skeleton3D::unbind_physical_bone_from_bone(int p_bone) { - ERR_FAIL_INDEX(p_bone, bones.size()); + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone, bone_size); bones.write[p_bone].physical_bone = nullptr; _rebuild_physical_bones_cache(); } PhysicalBone3D *Skeleton3D::get_physical_bone(int p_bone) { - ERR_FAIL_INDEX_V(p_bone, bones.size(), nullptr); + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr); return bones[p_bone].physical_bone; } PhysicalBone3D *Skeleton3D::get_physical_bone_parent(int p_bone) { - ERR_FAIL_INDEX_V(p_bone, bones.size(), nullptr); + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr); if (bones[p_bone].cache_parent_physical_bone) { return bones[p_bone].cache_parent_physical_bone; @@ -683,7 +762,8 @@ PhysicalBone3D *Skeleton3D::get_physical_bone_parent(int p_bone) { } PhysicalBone3D *Skeleton3D::_get_physical_bone_parent(int p_bone) { - ERR_FAIL_INDEX_V(p_bone, bones.size(), nullptr); + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone, bone_size, nullptr); const int parent_bone = bones[p_bone].parent; if (0 > parent_bone) { @@ -702,7 +782,7 @@ void Skeleton3D::_rebuild_physical_bones_cache() { const int b_size = bones.size(); for (int i = 0; i < b_size; ++i) { PhysicalBone3D *parent_pb = _get_physical_bone_parent(i); - if (parent_pb != bones[i].physical_bone) { + if (parent_pb != bones[i].cache_parent_physical_bone) { bones.write[i].cache_parent_physical_bone = parent_pb; if (bones[i].physical_bone) { bones[i].physical_bone->_on_bone_parent_changed(); @@ -789,8 +869,6 @@ void Skeleton3D::physical_bones_remove_collision_exception(RID p_exception) { _physical_bones_add_remove_collision_exception(false, this, p_exception); } -#endif // _3D_DISABLED - void Skeleton3D::_skin_changed() { _make_dirty(); } @@ -809,22 +887,27 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) { //when skeletons did not support skins. It is also used by gizmo //to display the skeleton. - skin.instance(); + skin.instantiate(); skin->set_bind_count(bones.size()); _update_process_order(); //just in case // pose changed, rebuild cache of inverses const Bone *bonesptr = bones.ptr(); int len = bones.size(); - const int *order = process_order.ptr(); // calculate global rests and invert them - for (int i = 0; i < len; i++) { - const Bone &b = bonesptr[order[i]]; + Vector<int> bones_to_process = get_parentless_bones(); + while (bones_to_process.size() > 0) { + int current_bone_idx = bones_to_process[0]; + bones_to_process.erase(current_bone_idx); + const Bone &b = bonesptr[current_bone_idx]; + + // Note: the code below may not work by default. May need to track an integer for the bone pose index order + // in the while loop, instead of using current_bone_idx. if (b.parent >= 0) { - skin->set_bind_pose(order[i], skin->get_bind_pose(b.parent) * b.rest); + skin->set_bind_pose(current_bone_idx, skin->get_bind_pose(b.parent) * b.rest); } else { - skin->set_bind_pose(order[i], b.rest); + skin->set_bind_pose(current_bone_idx, b.rest); } } @@ -838,7 +921,7 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) { ERR_FAIL_COND_V(skin.is_null(), Ref<SkinReference>()); Ref<SkinReference> skin_ref; - skin_ref.instance(); + skin_ref.instantiate(); skin_ref->skeleton_node = this; skin_ref->bind_count = 0; @@ -848,27 +931,213 @@ Ref<SkinReference> Skeleton3D::register_skin(const Ref<Skin> &p_skin) { skin_bindings.insert(skin_ref.operator->()); - skin->connect_compat("changed", skin_ref.operator->(), "_skin_changed"); + skin->connect("changed", Callable(skin_ref.operator->(), "_skin_changed")); _make_dirty(); //skin needs to be updated, so update skeleton return skin_ref; } +void Skeleton3D::force_update_all_bone_transforms() { + _update_process_order(); + + for (int i = 0; i < parentless_bones.size(); i++) { + force_update_bone_children_transforms(parentless_bones[i]); + } +} + +void Skeleton3D::force_update_bone_children_transforms(int p_bone_idx) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX(p_bone_idx, bone_size); + + Bone *bonesptr = bones.ptrw(); + List<int> bones_to_process = List<int>(); + bones_to_process.push_back(p_bone_idx); + + while (bones_to_process.size() > 0) { + int current_bone_idx = bones_to_process[0]; + bones_to_process.erase(current_bone_idx); + + Bone &b = bonesptr[current_bone_idx]; + + if (b.disable_rest) { + if (b.enabled) { + Transform3D pose = b.pose; + if (b.custom_pose_enable) { + pose = b.custom_pose * pose; + } + if (b.parent >= 0) { + b.pose_global = bonesptr[b.parent].pose_global * pose; + b.pose_global_no_override = b.pose_global; + } else { + b.pose_global = pose; + b.pose_global_no_override = b.pose_global; + } + } else { + if (b.parent >= 0) { + b.pose_global = bonesptr[b.parent].pose_global; + b.pose_global_no_override = b.pose_global; + } else { + b.pose_global = Transform3D(); + b.pose_global_no_override = b.pose_global; + } + } + + } else { + if (b.enabled) { + Transform3D pose = b.pose; + if (b.custom_pose_enable) { + pose = b.custom_pose * pose; + } + if (b.parent >= 0) { + b.pose_global = bonesptr[b.parent].pose_global * (b.rest * pose); + b.pose_global_no_override = b.pose_global; + } else { + b.pose_global = b.rest * pose; + b.pose_global_no_override = b.pose_global; + } + } else { + if (b.parent >= 0) { + b.pose_global = bonesptr[b.parent].pose_global * b.rest; + b.pose_global_no_override = b.pose_global; + } else { + b.pose_global = b.rest; + b.pose_global_no_override = b.pose_global; + } + } + } + + if (b.local_pose_override_amount >= CMP_EPSILON) { + Transform3D override_local_pose; + if (b.parent >= 0) { + override_local_pose = bonesptr[b.parent].pose_global * (b.rest * b.local_pose_override); + } else { + override_local_pose = (b.rest * b.local_pose_override); + } + b.pose_global = b.pose_global.interpolate_with(override_local_pose, b.local_pose_override_amount); + } + + if (b.global_pose_override_amount >= CMP_EPSILON) { + b.pose_global = b.pose_global.interpolate_with(b.global_pose_override, b.global_pose_override_amount); + } + + if (b.local_pose_override_reset) { + b.local_pose_override_amount = 0.0; + } + if (b.global_pose_override_reset) { + b.global_pose_override_amount = 0.0; + } + + // Add the bone's children to the list of bones to be processed + int child_bone_size = b.child_bones.size(); + for (int i = 0; i < child_bone_size; i++) { + bones_to_process.push_back(b.child_bones[i]); + } + + emit_signal(SceneStringNames::get_singleton()->bone_pose_changed, current_bone_idx); + } +} + // helper functions -Transform Skeleton3D::bone_transform_to_world_transform(Transform p_bone_transform) { - return get_global_transform() * p_bone_transform; + +Transform3D Skeleton3D::global_pose_to_world_transform(Transform3D p_global_pose) { + return get_global_transform() * p_global_pose; } -Transform Skeleton3D::world_transform_to_bone_transform(Transform p_world_transform) { +Transform3D Skeleton3D::world_transform_to_global_pose(Transform3D p_world_transform) { return get_global_transform().affine_inverse() * p_world_transform; } +Transform3D Skeleton3D::global_pose_to_local_pose(int p_bone_idx, Transform3D p_global_pose) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone_idx, bone_size, Transform3D()); + if (bones[p_bone_idx].parent >= 0) { + int parent_bone_idx = bones[p_bone_idx].parent; + Transform3D conversion_transform = (bones[parent_bone_idx].pose_global * bones[p_bone_idx].rest); + return conversion_transform.affine_inverse() * p_global_pose; + } else { + return p_global_pose; + } +} + +Transform3D Skeleton3D::local_pose_to_global_pose(int p_bone_idx, Transform3D p_local_pose) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone_idx, bone_size, Transform3D()); + if (bones[p_bone_idx].parent >= 0) { + int parent_bone_idx = bones[p_bone_idx].parent; + Transform3D conversion_transform = (bones[parent_bone_idx].pose_global * bones[p_bone_idx].rest); + return conversion_transform * p_local_pose; + } else { + return p_local_pose; + } +} + +Basis Skeleton3D::global_pose_z_forward_to_bone_forward(int p_bone_idx, Basis p_basis) { + const int bone_size = bones.size(); + ERR_FAIL_INDEX_V(p_bone_idx, bone_size, Basis()); + Basis return_basis = p_basis; + + if (bones[p_bone_idx].rest_bone_forward_axis < 0) { + update_bone_rest_forward_vector(p_bone_idx, true); + } + + if (bones[p_bone_idx].rest_bone_forward_axis == BONE_AXIS_X_FORWARD) { + return_basis.rotate_local(Vector3(0, 1, 0), (Math_PI / 2.0)); + } else if (bones[p_bone_idx].rest_bone_forward_axis == BONE_AXIS_NEGATIVE_X_FORWARD) { + return_basis.rotate_local(Vector3(0, 1, 0), -(Math_PI / 2.0)); + } else if (bones[p_bone_idx].rest_bone_forward_axis == BONE_AXIS_Y_FORWARD) { + return_basis.rotate_local(Vector3(1, 0, 0), -(Math_PI / 2.0)); + } else if (bones[p_bone_idx].rest_bone_forward_axis == BONE_AXIS_NEGATIVE_Y_FORWARD) { + return_basis.rotate_local(Vector3(1, 0, 0), (Math_PI / 2.0)); + } else if (bones[p_bone_idx].rest_bone_forward_axis == BONE_AXIS_Z_FORWARD) { + // Do nothing! + } else if (bones[p_bone_idx].rest_bone_forward_axis == BONE_AXIS_NEGATIVE_Z_FORWARD) { + return_basis.rotate_local(Vector3(0, 0, 1), Math_PI); + } + + return return_basis; +} + +// Modifications + +#ifndef _3D_DISABLED + +void Skeleton3D::set_modification_stack(Ref<SkeletonModificationStack3D> p_stack) { + if (modification_stack.is_valid()) { + modification_stack->is_setup = false; + modification_stack->set_skeleton(nullptr); + } + + modification_stack = p_stack; + if (modification_stack.is_valid()) { + modification_stack->set_skeleton(this); + modification_stack->setup(); + } +} +Ref<SkeletonModificationStack3D> Skeleton3D::get_modification_stack() { + return modification_stack; +} + +void Skeleton3D::execute_modifications(real_t p_delta, int p_execution_mode) { + if (!modification_stack.is_valid()) { + return; + } + + // Needed to avoid the issue where the stack looses reference to the skeleton when the scene is saved. + if (modification_stack->skeleton != this) { + modification_stack->set_skeleton(this); + } + + modification_stack->execute(p_delta, p_execution_mode); +} + +#endif // _3D_DISABLED + void Skeleton3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_bone_process_orders"), &Skeleton3D::get_bone_process_orders); ClassDB::bind_method(D_METHOD("add_bone", "name"), &Skeleton3D::add_bone); ClassDB::bind_method(D_METHOD("find_bone", "name"), &Skeleton3D::find_bone); ClassDB::bind_method(D_METHOD("get_bone_name", "bone_idx"), &Skeleton3D::get_bone_name); + ClassDB::bind_method(D_METHOD("set_bone_name", "bone_idx", "name"), &Skeleton3D::set_bone_name); ClassDB::bind_method(D_METHOD("get_bone_parent", "bone_idx"), &Skeleton3D::get_bone_parent); ClassDB::bind_method(D_METHOD("set_bone_parent", "bone_idx", "parent_idx"), &Skeleton3D::set_bone_parent); @@ -877,6 +1146,13 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("unparent_bone_and_rest", "bone_idx"), &Skeleton3D::unparent_bone_and_rest); + ClassDB::bind_method(D_METHOD("get_bone_children", "bone_idx"), &Skeleton3D::get_bone_children); + ClassDB::bind_method(D_METHOD("set_bone_children", "bone_idx", "bone_children"), &Skeleton3D::set_bone_children); + ClassDB::bind_method(D_METHOD("add_bone_child", "bone_idx", "child_bone_idx"), &Skeleton3D::add_bone_child); + ClassDB::bind_method(D_METHOD("remove_bone_child", "bone_idx", "child_bone_idx"), &Skeleton3D::remove_bone_child); + + ClassDB::bind_method(D_METHOD("get_parentless_bones"), &Skeleton3D::get_parentless_bones); + ClassDB::bind_method(D_METHOD("get_bone_rest", "bone_idx"), &Skeleton3D::get_bone_rest); ClassDB::bind_method(D_METHOD("set_bone_rest", "bone_idx", "rest"), &Skeleton3D::set_bone_rest); @@ -887,10 +1163,6 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_bone_disable_rest", "bone_idx", "disable"), &Skeleton3D::set_bone_disable_rest); ClassDB::bind_method(D_METHOD("is_bone_rest_disabled", "bone_idx"), &Skeleton3D::is_bone_rest_disabled); - ClassDB::bind_method(D_METHOD("bind_child_node_to_bone", "bone_idx", "node"), &Skeleton3D::bind_child_node_to_bone); - ClassDB::bind_method(D_METHOD("unbind_child_node_from_bone", "bone_idx", "node"), &Skeleton3D::unbind_child_node_from_bone); - ClassDB::bind_method(D_METHOD("get_bound_child_nodes_to_bone", "bone_idx"), &Skeleton3D::_get_bound_child_nodes_to_bone); - ClassDB::bind_method(D_METHOD("clear_bones"), &Skeleton3D::clear_bones); ClassDB::bind_method(D_METHOD("get_bone_pose", "bone_idx"), &Skeleton3D::get_bone_pose); @@ -898,15 +1170,26 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("clear_bones_global_pose_override"), &Skeleton3D::clear_bones_global_pose_override); ClassDB::bind_method(D_METHOD("set_bone_global_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton3D::set_bone_global_pose_override, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_bone_global_pose_override", "bone_idx"), &Skeleton3D::get_bone_global_pose_override); ClassDB::bind_method(D_METHOD("get_bone_global_pose", "bone_idx"), &Skeleton3D::get_bone_global_pose); + ClassDB::bind_method(D_METHOD("get_bone_global_pose_no_override", "bone_idx"), &Skeleton3D::get_bone_global_pose_no_override); + + ClassDB::bind_method(D_METHOD("clear_bones_local_pose_override"), &Skeleton3D::clear_bones_local_pose_override); + ClassDB::bind_method(D_METHOD("set_bone_local_pose_override", "bone_idx", "pose", "amount", "persistent"), &Skeleton3D::set_bone_local_pose_override, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("get_bone_local_pose_override", "bone_idx"), &Skeleton3D::get_bone_local_pose_override); ClassDB::bind_method(D_METHOD("get_bone_custom_pose", "bone_idx"), &Skeleton3D::get_bone_custom_pose); ClassDB::bind_method(D_METHOD("set_bone_custom_pose", "bone_idx", "custom_pose"), &Skeleton3D::set_bone_custom_pose); - ClassDB::bind_method(D_METHOD("bone_transform_to_world_transform", "bone_transform"), &Skeleton3D::bone_transform_to_world_transform); - ClassDB::bind_method(D_METHOD("world_transform_to_bone_transform", "world_transform"), &Skeleton3D::world_transform_to_bone_transform); + ClassDB::bind_method(D_METHOD("force_update_all_bone_transforms"), &Skeleton3D::force_update_all_bone_transforms); + ClassDB::bind_method(D_METHOD("force_update_bone_child_transform", "bone_idx"), &Skeleton3D::force_update_bone_children_transforms); -#ifndef _3D_DISABLED + // Helper functions + ClassDB::bind_method(D_METHOD("global_pose_to_world_transform", "global_pose"), &Skeleton3D::global_pose_to_world_transform); + ClassDB::bind_method(D_METHOD("world_transform_to_global_pose", "world_transform"), &Skeleton3D::world_transform_to_global_pose); + ClassDB::bind_method(D_METHOD("global_pose_to_local_pose", "bone_idx", "global_pose"), &Skeleton3D::global_pose_to_local_pose); + ClassDB::bind_method(D_METHOD("local_pose_to_global_pose", "bone_idx", "local_pose"), &Skeleton3D::local_pose_to_global_pose); + ClassDB::bind_method(D_METHOD("global_pose_z_forward_to_bone_forward", "bone_idx", "basis"), &Skeleton3D::global_pose_z_forward_to_bone_forward); ClassDB::bind_method(D_METHOD("set_animate_physical_bones"), &Skeleton3D::set_animate_physical_bones); ClassDB::bind_method(D_METHOD("get_animate_physical_bones"), &Skeleton3D::get_animate_physical_bones); @@ -916,6 +1199,12 @@ void Skeleton3D::_bind_methods() { ClassDB::bind_method(D_METHOD("physical_bones_add_collision_exception", "exception"), &Skeleton3D::physical_bones_add_collision_exception); ClassDB::bind_method(D_METHOD("physical_bones_remove_collision_exception", "exception"), &Skeleton3D::physical_bones_remove_collision_exception); + // Modifications + ClassDB::bind_method(D_METHOD("set_modification_stack", "modification_stack"), &Skeleton3D::set_modification_stack); + ClassDB::bind_method(D_METHOD("get_modification_stack"), &Skeleton3D::get_modification_stack); + ClassDB::bind_method(D_METHOD("execute_modifications", "delta", "execution_mode"), &Skeleton3D::execute_modifications); + +#ifndef _3D_DISABLED ADD_PROPERTY(PropertyInfo(Variant::BOOL, "animate_physical_bones"), "set_animate_physical_bones", "get_animate_physical_bones"); #endif // _3D_DISABLED @@ -923,14 +1212,12 @@ void Skeleton3D::_bind_methods() { ADD_SIGNAL(MethodInfo("pose_updated")); #endif // TOOLS_ENABLED + ADD_SIGNAL(MethodInfo("bone_pose_changed", PropertyInfo(Variant::INT, "bone_idx"))); + BIND_CONSTANT(NOTIFICATION_UPDATE_SKELETON); } Skeleton3D::Skeleton3D() { - animate_physical_bones = true; - dirty = false; - version = 1; - process_order_dirty = true; } Skeleton3D::~Skeleton3D() { diff --git a/scene/3d/skeleton_3d.h b/scene/3d/skeleton_3d.h index a21891a32e..c8a19db813 100644 --- a/scene/3d/skeleton_3d.h +++ b/scene/3d/skeleton_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,20 +31,17 @@ #ifndef SKELETON_3D_H #define SKELETON_3D_H -#include "core/rid.h" #include "scene/3d/node_3d.h" +#include "scene/resources/skeleton_modification_3d.h" #include "scene/resources/skin.h" -#ifndef _3D_DISABLED typedef int BoneId; class PhysicalBone3D; -#endif // _3D_DISABLED - class Skeleton3D; -class SkinReference : public Reference { - GDCLASS(SkinReference, Reference) +class SkinReference : public RefCounted { + GDCLASS(SkinReference, RefCounted) friend class Skeleton3D; Skeleton3D *skeleton_node; @@ -65,6 +62,8 @@ public: ~SkinReference(); }; +class SkeletonModificationStack3D; + class Skeleton3D : public Node3D { GDCLASS(Skeleton3D, Node3D); @@ -76,27 +75,34 @@ private: bool enabled; int parent; - int sort_index; //used for re-sorting process order - bool disable_rest; - Transform rest; + bool disable_rest = false; + Transform3D rest; - Transform pose; - Transform pose_global; + Transform3D pose; + Transform3D pose_global; + Transform3D pose_global_no_override; - bool custom_pose_enable; - Transform custom_pose; + bool custom_pose_enable = false; + Transform3D custom_pose; - float global_pose_override_amount; - bool global_pose_override_reset; - Transform global_pose_override; + real_t global_pose_override_amount = 0.0; + bool global_pose_override_reset = false; + Transform3D global_pose_override; -#ifndef _3D_DISABLED - PhysicalBone3D *physical_bone; - PhysicalBone3D *cache_parent_physical_bone; -#endif // _3D_DISABLED + PhysicalBone3D *physical_bone = nullptr; + PhysicalBone3D *cache_parent_physical_bone = nullptr; + + real_t local_pose_override_amount; + bool local_pose_override_reset; + Transform3D local_pose_override; - List<ObjectID> nodes_bound; + Vector<int> child_bones; + + // The forward direction vector and rest bone forward axis are cached because they do not change + // 99% of the time, but recalculating them can be expensive on models with many bones. + Vector3 rest_bone_forward_vector; + int rest_bone_forward_axis = -1; Bone() { parent = -1; @@ -109,6 +115,12 @@ private: physical_bone = nullptr; cache_parent_physical_bone = nullptr; #endif // _3D_DISABLED + local_pose_override_amount = 0; + local_pose_override_reset = false; + child_bones = Vector<int>(); + + rest_bone_forward_vector = Vector3(0, 0, 0); + rest_bone_forward_axis = -1; } }; @@ -116,27 +128,16 @@ private: void _skin_changed(); - bool animate_physical_bones; + bool animate_physical_bones = true; Vector<Bone> bones; - Vector<int> process_order; bool process_order_dirty; - void _make_dirty(); - bool dirty; - - uint64_t version; + Vector<int> parentless_bones; - // bind helpers - Array _get_bound_child_nodes_to_bone(int p_bone) const { - Array bound; - List<Node *> children; - get_bound_child_nodes_to_bone(p_bone, &children); + void _make_dirty(); + bool dirty = false; - for (int i = 0; i < children.size(); i++) { - bound.push_back(children[i]); - } - return bound; - } + uint64_t version = 1; void _update_process_order(); @@ -147,9 +148,21 @@ protected: void _notification(int p_what); static void _bind_methods(); +#ifndef _3D_DISABLED + Ref<SkeletonModificationStack3D> modification_stack; +#endif // _3D_DISABLED + public: - enum { + enum Bone_Forward_Axis { + BONE_AXIS_X_FORWARD = 0, + BONE_AXIS_Y_FORWARD = 1, + BONE_AXIS_Z_FORWARD = 2, + BONE_AXIS_NEGATIVE_X_FORWARD = 3, + BONE_AXIS_NEGATIVE_Y_FORWARD = 4, + BONE_AXIS_NEGATIVE_Z_FORWARD = 5, + }; + enum { NOTIFICATION_UPDATE_SKELETON = 50 }; @@ -157,6 +170,7 @@ public: void add_bone(const String &p_name); int find_bone(const String &p_name) const; String get_bone_name(int p_bone) const; + void set_bone_name(int p_bone, const String &p_name); bool is_bone_parent_of(int p_bone_id, int p_parent_bone_id) const; @@ -165,46 +179,69 @@ public: void unparent_bone_and_rest(int p_bone); + Vector<int> get_bone_children(int p_bone); + void set_bone_children(int p_bone, Vector<int> p_children); + void add_bone_child(int p_bone, int p_child); + void remove_bone_child(int p_bone, int p_child); + Vector<int> get_parentless_bones(); + void set_bone_disable_rest(int p_bone, bool p_disable); bool is_bone_rest_disabled(int p_bone) const; int get_bone_count() const; - void set_bone_rest(int p_bone, const Transform &p_rest); - Transform get_bone_rest(int p_bone) const; - Transform get_bone_global_pose(int p_bone) const; - - void clear_bones_global_pose_override(); - void set_bone_global_pose_override(int p_bone, const Transform &p_pose, float p_amount, bool p_persistent = false); + void set_bone_rest(int p_bone, const Transform3D &p_rest); + Transform3D get_bone_rest(int p_bone) const; + Transform3D get_bone_global_pose(int p_bone) const; + Transform3D get_bone_global_pose_no_override(int p_bone) const; void set_bone_enabled(int p_bone, bool p_enabled); bool is_bone_enabled(int p_bone) const; - - void bind_child_node_to_bone(int p_bone, Node *p_node); - void unbind_child_node_from_bone(int p_bone, Node *p_node); - void get_bound_child_nodes_to_bone(int p_bone, List<Node *> *p_bound) const; - void clear_bones(); // posing api - void set_bone_pose(int p_bone, const Transform &p_pose); - Transform get_bone_pose(int p_bone) const; + void set_bone_pose(int p_bone, const Transform3D &p_pose); + Transform3D get_bone_pose(int p_bone) const; + + void set_bone_custom_pose(int p_bone, const Transform3D &p_custom_pose); + Transform3D get_bone_custom_pose(int p_bone) const; - void set_bone_custom_pose(int p_bone, const Transform &p_custom_pose); - Transform get_bone_custom_pose(int p_bone) const; + void clear_bones_global_pose_override(); + Transform3D get_bone_global_pose_override(int p_bone) const; + void set_bone_global_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent = false); + + void clear_bones_local_pose_override(); + Transform3D get_bone_local_pose_override(int p_bone) const; + void set_bone_local_pose_override(int p_bone, const Transform3D &p_pose, real_t p_amount, bool p_persistent = false); void localize_rests(); // used for loaders and tools - int get_process_order(int p_idx); - Vector<int> get_bone_process_orders(); Ref<SkinReference> register_skin(const Ref<Skin> &p_skin); + void force_update_all_bone_transforms(); + void force_update_bone_children_transforms(int bone_idx); + + void update_bone_rest_forward_vector(int p_bone, bool p_force_update = false); + void update_bone_rest_forward_axis(int p_bone, bool p_force_update = false); + Vector3 get_bone_axis_forward_vector(int p_bone); + int get_bone_axis_forward_enum(int p_bone); + // Helper functions - Transform bone_transform_to_world_transform(Transform p_transform); - Transform world_transform_to_bone_transform(Transform p_transform); + Transform3D global_pose_to_world_transform(Transform3D p_global_pose); + Transform3D world_transform_to_global_pose(Transform3D p_transform); + Transform3D global_pose_to_local_pose(int p_bone_idx, Transform3D p_global_pose); + Transform3D local_pose_to_global_pose(int p_bone_idx, Transform3D p_local_pose); + Basis global_pose_z_forward_to_bone_forward(int p_bone_idx, Basis p_basis); + + // Modifications #ifndef _3D_DISABLED + Ref<SkeletonModificationStack3D> get_modification_stack(); + void set_modification_stack(Ref<SkeletonModificationStack3D> p_stack); + void execute_modifications(real_t p_delta, int p_execution_mode); +#endif // _3D_DISABLED + // Physical bone API void set_animate_physical_bones(bool p_animate); @@ -226,7 +263,6 @@ public: void physical_bones_start_simulation_on(const TypedArray<StringName> &p_bones); void physical_bones_add_collision_exception(RID p_exception); void physical_bones_remove_collision_exception(RID p_exception); -#endif // _3D_DISABLED public: Skeleton3D(); diff --git a/scene/3d/skeleton_ik_3d.cpp b/scene/3d/skeleton_ik_3d.cpp index 32d7afd5df..a891566633 100644 --- a/scene/3d/skeleton_ik_3d.cpp +++ b/scene/3d/skeleton_ik_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -63,7 +63,6 @@ bool FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain chain.chain_root.bone = p_task->root_bone; chain.chain_root.initial_transform = p_task->skeleton->get_bone_global_pose(chain.chain_root.bone); chain.chain_root.current_pos = chain.chain_root.initial_transform.origin; - chain.chain_root.pb = p_task->skeleton->get_physical_bone(chain.chain_root.bone); chain.middle_chain_item = nullptr; // Holds all IDs that are composing a single chain in reverse order @@ -86,7 +85,7 @@ bool FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain chain_sub_tip = p_task->skeleton->get_bone_parent(chain_sub_tip); } - BoneId middle_chain_item_id = (((float)sub_chain_size) * 0.5); + BoneId middle_chain_item_id = (BoneId)(sub_chain_size * 0.5); // Build chain by reading chain ids in reverse order // For each chain item id will be created a ChainItem if doesn't exists @@ -96,8 +95,6 @@ bool FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain if (!child_ci) { child_ci = sub_chain->add_child(chain_ids[i]); - child_ci->pb = p_task->skeleton->get_physical_bone(child_ci->bone); - child_ci->initial_transform = p_task->skeleton->get_bone_global_pose(child_ci->bone); child_ci->current_pos = child_ci->initial_transform.origin; @@ -123,7 +120,7 @@ bool FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain if (p_force_simple_chain) { // NOTE: - // This is an "hack" that force to create only one tip per chain since the solver of multi tip (end effector) + // This is a "hack" that force to create only one tip per chain since the solver of multi tip (end effector) // is not yet created. // Remove this code when this is done break; @@ -132,21 +129,7 @@ bool FabrikInverseKinematic::build_chain(Task *p_task, bool p_force_simple_chain return true; } -void FabrikInverseKinematic::update_chain(const Skeleton3D *p_sk, ChainItem *p_chain_item) { - if (!p_chain_item) { - return; - } - - p_chain_item->initial_transform = p_sk->get_bone_global_pose(p_chain_item->bone); - p_chain_item->current_pos = p_chain_item->initial_transform.origin; - - ChainItem *items = p_chain_item->children.ptrw(); - for (int i = 0; i < p_chain_item->children.size(); i += 1) { - update_chain(p_sk, items + i); - } -} - -void FabrikInverseKinematic::solve_simple(Task *p_task, bool p_solve_magnet) { +void FabrikInverseKinematic::solve_simple(Task *p_task, bool p_solve_magnet, Vector3 p_origin_pos) { real_t distance_to_goal(1e4); real_t previous_distance_to_goal(0); int can_solve(p_task->max_iterations); @@ -155,7 +138,7 @@ void FabrikInverseKinematic::solve_simple(Task *p_task, bool p_solve_magnet) { --can_solve; solve_simple_backwards(p_task->chain, p_solve_magnet); - solve_simple_forwards(p_task->chain, p_solve_magnet); + solve_simple_forwards(p_task->chain, p_solve_magnet, p_origin_pos); distance_to_goal = (p_task->chain.tips[0].chain_item->current_pos - p_task->chain.tips[0].end_effector->goal_transform.origin).length(); } @@ -193,18 +176,18 @@ void FabrikInverseKinematic::solve_simple_backwards(Chain &r_chain, bool p_solve } } -void FabrikInverseKinematic::solve_simple_forwards(Chain &r_chain, bool p_solve_magnet) { +void FabrikInverseKinematic::solve_simple_forwards(Chain &r_chain, bool p_solve_magnet, Vector3 p_origin_pos) { if (p_solve_magnet && !r_chain.middle_chain_item) { return; } ChainItem *sub_chain_root(&r_chain.chain_root); - Vector3 origin(r_chain.chain_root.initial_transform.origin); + Vector3 origin = p_origin_pos; while (sub_chain_root) { // Reach the tip sub_chain_root->current_pos = origin; - if (!sub_chain_root->children.empty()) { + if (!sub_chain_root->children.is_empty()) { ChainItem &child(sub_chain_root->children.write[0]); // Is not tip @@ -229,7 +212,7 @@ void FabrikInverseKinematic::solve_simple_forwards(Chain &r_chain, bool p_solve_ } } -FabrikInverseKinematic::Task *FabrikInverseKinematic::create_simple_task(Skeleton3D *p_sk, BoneId root_bone, BoneId tip_bone, const Transform &goal_transform) { +FabrikInverseKinematic::Task *FabrikInverseKinematic::create_simple_task(Skeleton3D *p_sk, BoneId root_bone, BoneId tip_bone, const Transform3D &goal_transform) { FabrikInverseKinematic::EndEffector ee; ee.tip_bone = tip_bone; @@ -253,17 +236,17 @@ void FabrikInverseKinematic::free_task(Task *p_task) { } } -void FabrikInverseKinematic::set_goal(Task *p_task, const Transform &p_goal) { +void FabrikInverseKinematic::set_goal(Task *p_task, const Transform3D &p_goal) { p_task->goal_global_transform = p_goal; } -void FabrikInverseKinematic::make_goal(Task *p_task, const Transform &p_inverse_transf, real_t blending_delta) { +void FabrikInverseKinematic::make_goal(Task *p_task, const Transform3D &p_inverse_transf, real_t blending_delta) { if (blending_delta >= 0.99f) { // Update the end_effector (local transform) without blending p_task->end_effectors.write[0].goal_transform = p_inverse_transf * p_task->goal_global_transform; } else { // End effector in local transform - const Transform end_effector_pose(p_task->skeleton->get_bone_global_pose(p_task->end_effectors.write[0].tip_bone)); + const Transform3D end_effector_pose(p_task->skeleton->get_bone_global_pose_no_override(p_task->end_effectors[0].tip_bone)); // Update the end_effector (local transform) by blending with current pose p_task->end_effectors.write[0].goal_transform = end_effector_pose.interpolate_with(p_inverse_transf * p_task->goal_global_transform, blending_delta); @@ -272,36 +255,42 @@ void FabrikInverseKinematic::make_goal(Task *p_task, const Transform &p_inverse_ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position) { if (blending_delta <= 0.01f) { - return; // Skip solving - } + // Before skipping, make sure we undo the global pose overrides + ChainItem *ci(&p_task->chain.chain_root); + while (ci) { + p_task->skeleton->set_bone_global_pose_override(ci->bone, ci->initial_transform, 0.0, false); - p_task->skeleton->set_bone_global_pose_override(p_task->chain.chain_root.bone, Transform(), 0.0, true); + if (!ci->children.is_empty()) { + ci = &ci->children.write[0]; + } else { + ci = nullptr; + } + } - if (p_task->chain.middle_chain_item) { - p_task->skeleton->set_bone_global_pose_override(p_task->chain.middle_chain_item->bone, Transform(), 0.0, true); + return; // Skip solving } - for (int i = 0; i < p_task->chain.tips.size(); i += 1) { - p_task->skeleton->set_bone_global_pose_override(p_task->chain.tips[i].chain_item->bone, Transform(), 0.0, true); - } + // Update the initial root transform so its synced with any animation changes + _update_chain(p_task->skeleton, &p_task->chain.chain_root); - make_goal(p_task, p_task->skeleton->get_global_transform().affine_inverse().scaled(p_task->skeleton->get_global_transform().get_basis().get_scale()), blending_delta); + p_task->skeleton->set_bone_global_pose_override(p_task->chain.chain_root.bone, Transform3D(), 0.0, false); + Vector3 origin_pos = p_task->skeleton->get_bone_global_pose(p_task->chain.chain_root.bone).origin; - update_chain(p_task->skeleton, &p_task->chain.chain_root); + make_goal(p_task, p_task->skeleton->get_global_transform().affine_inverse(), blending_delta); if (p_use_magnet && p_task->chain.middle_chain_item) { p_task->chain.magnet_position = p_task->chain.middle_chain_item->initial_transform.origin.lerp(p_magnet_position, blending_delta); - solve_simple(p_task, true); + solve_simple(p_task, true, origin_pos); } - solve_simple(p_task, false); + solve_simple(p_task, false, origin_pos); // Assign new bone position. ChainItem *ci(&p_task->chain.chain_root); while (ci) { - Transform new_bone_pose(ci->initial_transform); + Transform3D new_bone_pose(ci->initial_transform); new_bone_pose.origin = ci->current_pos; - if (!ci->children.empty()) { + if (!ci->children.is_empty()) { /// Rotate basis const Vector3 initial_ori((ci->children[0].initial_transform.origin - ci->initial_transform.origin).normalized()); const Vector3 rot_axis(initial_ori.cross(ci->current_ori).normalized()); @@ -310,6 +299,7 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove const real_t rot_angle(Math::acos(CLAMP(initial_ori.dot(ci->current_ori), -1, 1))); new_bone_pose.basis.rotate(rot_axis, rot_angle); } + } else { // Set target orientation to tip if (override_tip_basis) { @@ -319,9 +309,13 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove } } + // IK should not affect scale, so undo any scaling + new_bone_pose.basis.orthonormalize(); + new_bone_pose.basis.scale(p_task->skeleton->get_bone_global_pose(ci->bone).basis.get_scale()); + p_task->skeleton->set_bone_global_pose_override(ci->bone, new_bone_pose, 1.0, true); - if (!ci->children.empty()) { + if (!ci->children.is_empty()) { ci = &ci->children.write[0]; } else { ci = nullptr; @@ -329,6 +323,20 @@ void FabrikInverseKinematic::solve(Task *p_task, real_t blending_delta, bool ove } } +void FabrikInverseKinematic::_update_chain(const Skeleton3D *p_sk, ChainItem *p_chain_item) { + if (!p_chain_item) { + return; + } + + p_chain_item->initial_transform = p_sk->get_bone_global_pose_no_override(p_chain_item->bone); + p_chain_item->current_pos = p_chain_item->initial_transform.origin; + + ChainItem *items = p_chain_item->children.ptrw(); + for (int i = 0; i < p_chain_item->children.size(); i += 1) { + _update_chain(p_sk, items + i); + } +} + void SkeletonIK3D::_validate_property(PropertyInfo &property) const { if (property.name == "root_bone" || property.name == "tip_bone") { if (skeleton) { @@ -389,7 +397,7 @@ void SkeletonIK3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "root_bone"), "set_root_bone", "get_root_bone"); ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "tip_bone"), "set_tip_bone", "get_tip_bone"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "interpolation", PROPERTY_HINT_RANGE, "0,1,0.001"), "set_interpolation", "get_interpolation"); - ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM, "target"), "set_target_transform", "get_target_transform"); + ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM3D, "target"), "set_target_transform", "get_target_transform"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "override_tip_basis"), "set_override_tip_basis", "is_override_tip_basis"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_magnet"), "set_use_magnet", "is_using_magnet"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "magnet"), "set_magnet_position", "get_magnet_position"); @@ -453,12 +461,12 @@ real_t SkeletonIK3D::get_interpolation() const { return interpolation; } -void SkeletonIK3D::set_target_transform(const Transform &p_target) { +void SkeletonIK3D::set_target_transform(const Transform3D &p_target) { target = p_target; reload_goal(); } -const Transform &SkeletonIK3D::get_target_transform() const { +const Transform3D &SkeletonIK3D::get_target_transform() const { return target; } @@ -524,9 +532,12 @@ void SkeletonIK3D::start(bool p_one_time) { void SkeletonIK3D::stop() { set_process_internal(false); + if (skeleton) { + skeleton->clear_bones_global_pose_override(); + } } -Transform SkeletonIK3D::_get_target_transform() { +Transform3D SkeletonIK3D::_get_target_transform() { if (!target_node_override && !target_node_path_override.is_empty()) { target_node_override = Object::cast_to<Node3D>(get_node(target_node_path_override)); } diff --git a/scene/3d/skeleton_ik_3d.h b/scene/3d/skeleton_ik_3d.h index 80acc3e937..4cf08e7c99 100644 --- a/scene/3d/skeleton_ik_3d.h +++ b/scene/3d/skeleton_ik_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,17 +33,12 @@ #ifndef _3D_DISABLED -/** - * @author AndreaCatania - */ - -#include "core/math/transform.h" #include "scene/3d/skeleton_3d.h" class FabrikInverseKinematic { struct EndEffector { BoneId tip_bone; - Transform goal_transform; + Transform3D goal_transform; }; struct ChainItem { @@ -52,17 +47,14 @@ class FabrikInverseKinematic { // Bone info BoneId bone = -1; - PhysicalBone3D *pb = nullptr; - real_t length = 0; + real_t length = 0.0; /// Positions relative to root bone - Transform initial_transform; + Transform3D initial_transform; Vector3 current_pos; // Direction from this bone to child Vector3 current_ori; - ChainItem() {} - ChainItem *find_child(const BoneId p_bone_id); ChainItem *add_child(const BoneId p_bone_id); }; @@ -76,15 +68,11 @@ class FabrikInverseKinematic { ChainTip(ChainItem *p_chain_item, const EndEffector *p_end_effector) : chain_item(p_chain_item), end_effector(p_end_effector) {} - - ChainTip(const ChainTip &p_other_ct) : - chain_item(p_other_ct.chain_item), - end_effector(p_other_ct.end_effector) {} }; struct Chain { ChainItem chain_root; - ChainItem *middle_chain_item; + ChainItem *middle_chain_item = nullptr; Vector<ChainTip> tips; Vector3 magnet_position; }; @@ -104,7 +92,7 @@ public: BoneId root_bone = -1; Vector<EndEffector> end_effectors; - Transform goal_global_transform; + Transform3D goal_global_transform; Task() {} }; @@ -113,20 +101,20 @@ private: /// Init a chain that starts from the root to tip static bool build_chain(Task *p_task, bool p_force_simple_chain = true); - static void update_chain(const Skeleton3D *p_sk, ChainItem *p_chain_item); - - static void solve_simple(Task *p_task, bool p_solve_magnet); + static void solve_simple(Task *p_task, bool p_solve_magnet, Vector3 p_origin_pos); /// Special solvers that solve only chains with one end effector static void solve_simple_backwards(Chain &r_chain, bool p_solve_magnet); - static void solve_simple_forwards(Chain &r_chain, bool p_solve_magnet); + static void solve_simple_forwards(Chain &r_chain, bool p_solve_magnet, Vector3 p_origin_pos); public: - static Task *create_simple_task(Skeleton3D *p_sk, BoneId root_bone, BoneId tip_bone, const Transform &goal_transform); + static Task *create_simple_task(Skeleton3D *p_sk, BoneId root_bone, BoneId tip_bone, const Transform3D &goal_transform); static void free_task(Task *p_task); // The goal of chain should be always in local space - static void set_goal(Task *p_task, const Transform &p_goal); - static void make_goal(Task *p_task, const Transform &p_inverse_transf, real_t blending_delta); + static void set_goal(Task *p_task, const Transform3D &p_goal); + static void make_goal(Task *p_task, const Transform3D &p_inverse_transf, real_t blending_delta); static void solve(Task *p_task, real_t blending_delta, bool override_tip_basis, bool p_use_magnet, const Vector3 &p_magnet_position); + + static void _update_chain(const Skeleton3D *p_skeleton, ChainItem *p_chain_item); }; class SkeletonIK3D : public Node { @@ -134,8 +122,8 @@ class SkeletonIK3D : public Node { StringName root_bone; StringName tip_bone; - real_t interpolation = 1; - Transform target; + real_t interpolation = 1.0; + Transform3D target; NodePath target_node_path_override; bool override_tip_basis = true; bool use_magnet = false; @@ -168,8 +156,8 @@ public: void set_interpolation(real_t p_interpolation); real_t get_interpolation() const; - void set_target_transform(const Transform &p_target); - const Transform &get_target_transform() const; + void set_target_transform(const Transform3D &p_target); + const Transform3D &get_target_transform() const; void set_target_node(const NodePath &p_node); NodePath get_target_node(); @@ -197,7 +185,7 @@ public: void stop(); private: - Transform _get_target_transform(); + Transform3D _get_target_transform(); void reload_chain(); void reload_goal(); void _solve_chain(); diff --git a/scene/3d/soft_body_3d.cpp b/scene/3d/soft_body_3d.cpp index 94e9555a1a..28ff3e9412 100644 --- a/scene/3d/soft_body_3d.cpp +++ b/scene/3d/soft_body_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,14 +30,7 @@ #include "soft_body_3d.h" -#include "core/list.h" -#include "core/object.h" -#include "core/os/os.h" -#include "core/rid.h" -#include "scene/3d/collision_object_3d.h" #include "scene/3d/physics_body_3d.h" -#include "scene/3d/skeleton_3d.h" -#include "servers/physics_server_3d.h" SoftBodyRenderingServerHandler::SoftBodyRenderingServerHandler() {} @@ -48,27 +41,28 @@ void SoftBodyRenderingServerHandler::prepare(RID p_mesh, int p_surface) { mesh = p_mesh; surface = p_surface; -#ifndef _MSC_VER -#warning Softbody is not working, needs to be redone considering that these functions no longer exist -#endif -#if 0 - const uint32_t surface_format = RS::get_singleton()->mesh_surface_get_format(mesh, surface); - const int surface_vertex_len = RS::get_singleton()->mesh_surface_get_array_len(mesh, p_surface); - const int surface_index_len = RS::get_singleton()->mesh_surface_get_array_index_len(mesh, p_surface); + + RS::SurfaceData surface_data = RS::get_singleton()->mesh_get_surface(mesh, surface); + uint32_t surface_offsets[RS::ARRAY_MAX]; + uint32_t vertex_stride; + uint32_t attrib_stride; + uint32_t skin_stride; + RS::get_singleton()->mesh_surface_make_offsets_from_format(surface_data.format, surface_data.vertex_count, surface_data.index_count, surface_offsets, vertex_stride, attrib_stride, skin_stride); - buffer = RS::get_singleton()->mesh_surface_get_array(mesh, surface); - stride = RS::get_singleton()->mesh_surface_make_offsets_from_format(surface_format, surface_vertex_len, surface_index_len, surface_offsets); + buffer = surface_data.vertex_data; + stride = vertex_stride; offset_vertices = surface_offsets[RS::ARRAY_VERTEX]; offset_normal = surface_offsets[RS::ARRAY_NORMAL]; -#endif } void SoftBodyRenderingServerHandler::clear() { - if (mesh.is_valid()) { - buffer.resize(0); - } + buffer.resize(0); + stride = 0; + offset_vertices = 0; + offset_normal = 0; + surface = 0; mesh = RID(); } @@ -77,19 +71,19 @@ void SoftBodyRenderingServerHandler::open() { } void SoftBodyRenderingServerHandler::close() { - //write_buffer.release(); + write_buffer = nullptr; } void SoftBodyRenderingServerHandler::commit_changes() { - RS::get_singleton()->mesh_surface_update_region(mesh, surface, 0, buffer); + RS::get_singleton()->mesh_surface_update_vertex_region(mesh, surface, 0, buffer); } void SoftBodyRenderingServerHandler::set_vertex(int p_vertex_id, const void *p_vector3) { - copymem(&write_buffer[p_vertex_id * stride + offset_vertices], p_vector3, sizeof(float) * 3); + memcpy(&write_buffer[p_vertex_id * stride + offset_vertices], p_vector3, sizeof(float) * 3); } void SoftBodyRenderingServerHandler::set_normal(int p_vertex_id, const void *p_vector3) { - copymem(&write_buffer[p_vertex_id * stride + offset_normal], p_vector3, sizeof(float) * 3); + memcpy(&write_buffer[p_vertex_id * stride + offset_normal], p_vector3, sizeof(float) * 3); } void SoftBodyRenderingServerHandler::set_aabb(const AABB &p_aabb) { @@ -245,13 +239,11 @@ bool SoftBody3D::_get_property_pinned_points(int p_item, const String &p_what, V return true; } -void SoftBody3D::_changed_callback(Object *p_changed, const char *p_prop) { +void SoftBody3D::_softbody_changed() { prepare_physics_server(); _reset_points_offsets(); #ifdef TOOLS_ENABLED - if (p_changed == this) { - update_configuration_warning(); - } + update_configuration_warnings(); #endif } @@ -259,19 +251,22 @@ void SoftBody3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_WORLD: { if (Engine::get_singleton()->is_editor_hint()) { - add_change_receptor(this); + // I have no idea what this is supposed to do, it's really weird + // leaving for upcoming PK work on physics + //add_change_receptor(this); } RID space = get_world_3d()->get_space(); 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(); @@ -283,47 +278,61 @@ void SoftBody3D::_notification(int p_what) { set_notify_transform(false); // Required to be top level with Transform at center of world in order to modify RenderingServer only to support custom Transform set_as_top_level(true); - set_transform(Transform()); + 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; - } -#ifdef TOOLS_ENABLED + case NOTIFICATION_DISABLED: { + if (is_inside_tree() && (disable_mode == DISABLE_MODE_REMOVE)) { + prepare_physics_server(); + } + } break; - if (p_what == NOTIFICATION_LOCAL_TRANSFORM_CHANGED) { - if (Engine::get_singleton()->is_editor_hint()) { - update_configuration_warning(); - } - } + case NOTIFICATION_ENABLED: { + if (is_inside_tree() && (disable_mode == DISABLE_MODE_REMOVE)) { + prepare_physics_server(); + } + } break; +#ifdef TOOLS_ENABLED + case NOTIFICATION_LOCAL_TRANSFORM_CHANGED: { + if (Engine::get_singleton()->is_editor_hint()) { + update_configuration_warnings(); + } + } break; #endif + } } void SoftBody3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_physics_rid"), &SoftBody3D::get_physics_rid); + ClassDB::bind_method(D_METHOD("set_collision_mask", "collision_mask"), &SoftBody3D::set_collision_mask); ClassDB::bind_method(D_METHOD("get_collision_mask"), &SoftBody3D::get_collision_mask); ClassDB::bind_method(D_METHOD("set_collision_layer", "collision_layer"), &SoftBody3D::set_collision_layer); ClassDB::bind_method(D_METHOD("get_collision_layer"), &SoftBody3D::get_collision_layer); - ClassDB::bind_method(D_METHOD("set_collision_mask_bit", "bit", "value"), &SoftBody3D::set_collision_mask_bit); - ClassDB::bind_method(D_METHOD("get_collision_mask_bit", "bit"), &SoftBody3D::get_collision_mask_bit); + ClassDB::bind_method(D_METHOD("set_collision_mask_value", "layer_number", "value"), &SoftBody3D::set_collision_mask_value); + ClassDB::bind_method(D_METHOD("get_collision_mask_value", "layer_number"), &SoftBody3D::get_collision_mask_value); - ClassDB::bind_method(D_METHOD("set_collision_layer_bit", "bit", "value"), &SoftBody3D::set_collision_layer_bit); - ClassDB::bind_method(D_METHOD("get_collision_layer_bit", "bit"), &SoftBody3D::get_collision_layer_bit); + ClassDB::bind_method(D_METHOD("set_collision_layer_value", "layer_number", "value"), &SoftBody3D::set_collision_layer_value); + ClassDB::bind_method(D_METHOD("get_collision_layer_value", "layer_number"), &SoftBody3D::get_collision_layer_value); 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); @@ -337,18 +346,9 @@ void SoftBody3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_linear_stiffness", "linear_stiffness"), &SoftBody3D::set_linear_stiffness); ClassDB::bind_method(D_METHOD("get_linear_stiffness"), &SoftBody3D::get_linear_stiffness); - ClassDB::bind_method(D_METHOD("set_areaAngular_stiffness", "areaAngular_stiffness"), &SoftBody3D::set_areaAngular_stiffness); - ClassDB::bind_method(D_METHOD("get_areaAngular_stiffness"), &SoftBody3D::get_areaAngular_stiffness); - - ClassDB::bind_method(D_METHOD("set_volume_stiffness", "volume_stiffness"), &SoftBody3D::set_volume_stiffness); - ClassDB::bind_method(D_METHOD("get_volume_stiffness"), &SoftBody3D::get_volume_stiffness); - ClassDB::bind_method(D_METHOD("set_pressure_coefficient", "pressure_coefficient"), &SoftBody3D::set_pressure_coefficient); ClassDB::bind_method(D_METHOD("get_pressure_coefficient"), &SoftBody3D::get_pressure_coefficient); - ClassDB::bind_method(D_METHOD("set_pose_matching_coefficient", "pose_matching_coefficient"), &SoftBody3D::set_pose_matching_coefficient); - ClassDB::bind_method(D_METHOD("get_pose_matching_coefficient"), &SoftBody3D::get_pose_matching_coefficient); - ClassDB::bind_method(D_METHOD("set_damping_coefficient", "damping_coefficient"), &SoftBody3D::set_damping_coefficient); ClassDB::bind_method(D_METHOD("get_damping_coefficient"), &SoftBody3D::get_damping_coefficient); @@ -366,37 +366,31 @@ void SoftBody3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "simulation_precision", PROPERTY_HINT_RANGE, "1,100,1"), "set_simulation_precision", "get_simulation_precision"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "total_mass", PROPERTY_HINT_RANGE, "0.01,10000,1"), "set_total_mass", "get_total_mass"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "linear_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_linear_stiffness", "get_linear_stiffness"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "areaAngular_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_areaAngular_stiffness", "get_areaAngular_stiffness"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "volume_stiffness", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_volume_stiffness", "get_volume_stiffness"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pressure_coefficient"), "set_pressure_coefficient", "get_pressure_coefficient"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_damping_coefficient", "get_damping_coefficient"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "drag_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_drag_coefficient", "get_drag_coefficient"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "pose_matching_coefficient", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_pose_matching_coefficient", "get_pose_matching_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); } -String SoftBody3D::get_configuration_warning() const { - String warning = MeshInstance3D::get_configuration_warning(); +TypedArray<String> SoftBody3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (get_mesh().is_null()) { - if (!warning.empty()) { - warning += "\n\n"; - } - - warning += TTR("This body will be ignored until you set a mesh."); + warnings.push_back(TTR("This body will be ignored until you set a mesh.")); } - Transform t = get_transform(); + Transform3D t = get_transform(); if ((ABS(t.basis.get_axis(0).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(1).length() - 1.0) > 0.05 || ABS(t.basis.get_axis(2).length() - 1.0) > 0.05)) { - if (!warning.empty()) { - warning += "\n\n"; - } - - warning += TTR("Size changes to SoftBody3D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead."); + warnings.push_back(TTR("Size changes to SoftBody3D will be overridden by the physics engine when running.\nChange the size in children collision shapes instead.")); } - return warning; + return warnings; } void SoftBody3D::_update_physics_server() { @@ -425,8 +419,8 @@ void SoftBody3D::_draw_soft_mesh() { /// Necessary in order to render the mesh correctly (Soft body nodes are in global space) simulation_started = true; - call_deferred("set_as_toplevel", true); - call_deferred("set_transform", Transform()); + call_deferred(SNAME("set_as_top_level"), true); + call_deferred(SNAME("set_transform"), Transform3D()); } _update_physics_server(); @@ -439,6 +433,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()); @@ -448,8 +443,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)); @@ -470,7 +466,7 @@ void SoftBody3D::become_mesh_owner() { mesh_owner = true; Vector<Ref<Material>> copy_materials; - copy_materials.append_array(materials); + copy_materials.append_array(surface_override_materials); ERR_FAIL_COND(!mesh->get_surface_count()); @@ -480,18 +476,17 @@ void SoftBody3D::become_mesh_owner() { Dictionary surface_lods = mesh->surface_get_lods(0); uint32_t surface_format = mesh->surface_get_format(0); - surface_format &= ~(Mesh::ARRAY_COMPRESS_NORMAL); surface_format |= Mesh::ARRAY_FLAG_USE_DYNAMIC_UPDATE; Ref<ArrayMesh> soft_mesh; - soft_mesh.instance(); + soft_mesh.instantiate(); soft_mesh->add_surface_from_arrays(Mesh::PRIMITIVE_TRIANGLES, surface_arrays, surface_blend_arrays, surface_lods, surface_format); soft_mesh->surface_set_material(0, mesh->surface_get_material(0)); set_mesh(soft_mesh); for (int i = copy_materials.size() - 1; 0 <= i; --i) { - set_surface_material(i, copy_materials[i]); + set_surface_override_material(i, copy_materials[i]); } } } @@ -514,32 +509,62 @@ uint32_t SoftBody3D::get_collision_layer() const { return collision_layer; } -void SoftBody3D::set_collision_mask_bit(int p_bit, bool p_value) { - uint32_t mask = get_collision_mask(); +void SoftBody3D::set_collision_layer_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); + uint32_t collision_layer = get_collision_layer(); if (p_value) { - mask |= 1 << p_bit; + collision_layer |= 1 << (p_layer_number - 1); } else { - mask &= ~(1 << p_bit); + collision_layer &= ~(1 << (p_layer_number - 1)); } - set_collision_mask(mask); + set_collision_layer(collision_layer); } -bool SoftBody3D::get_collision_mask_bit(int p_bit) const { - return get_collision_mask() & (1 << p_bit); +bool SoftBody3D::get_collision_layer_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_layer() & (1 << (p_layer_number - 1)); } -void SoftBody3D::set_collision_layer_bit(int p_bit, bool p_value) { - uint32_t layer = get_collision_layer(); +void SoftBody3D::set_collision_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 32, "Collision layer number must be between 1 and 32 inclusive."); + uint32_t mask = get_collision_mask(); if (p_value) { - layer |= 1 << p_bit; + mask |= 1 << (p_layer_number - 1); } else { - layer &= ~(1 << p_bit); + mask &= ~(1 << (p_layer_number - 1)); + } + set_collision_mask(mask); +} + +bool SoftBody3D::get_collision_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Collision layer number must be between 1 and 32 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 32, false, "Collision layer number must be between 1 and 32 inclusive."); + return get_collision_mask() & (1 << (p_layer_number - 1)); +} + +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(); } - set_collision_layer(layer); } -bool SoftBody3D::get_collision_layer_bit(int p_bit) const { - return get_collision_layer() & (1 << p_bit); +SoftBody3D::DisableMode SoftBody3D::get_disable_mode() const { + return disable_mode; } void SoftBody3D::set_parent_collision_ignore(const NodePath &p_parent_collision_ignore) { @@ -565,8 +590,7 @@ Array SoftBody3D::get_collision_exceptions() { List<RID> exceptions; PhysicsServer3D::get_singleton()->soft_body_get_collision_exceptions(physics_rid, &exceptions); Array ret; - for (List<RID>::Element *E = exceptions.front(); E; E = E->next()) { - RID body = E->get(); + for (const RID &body : exceptions) { ObjectID instance_id = PhysicsServer3D::get_singleton()->body_get_object_instance_id(body); Object *obj = ObjectDB::get_instance(instance_id); PhysicsBody3D *physics_body = Object::cast_to<PhysicsBody3D>(obj); @@ -613,34 +637,10 @@ real_t SoftBody3D::get_linear_stiffness() { return PhysicsServer3D::get_singleton()->soft_body_get_linear_stiffness(physics_rid); } -void SoftBody3D::set_areaAngular_stiffness(real_t p_areaAngular_stiffness) { - PhysicsServer3D::get_singleton()->soft_body_set_areaAngular_stiffness(physics_rid, p_areaAngular_stiffness); -} - -real_t SoftBody3D::get_areaAngular_stiffness() { - return PhysicsServer3D::get_singleton()->soft_body_get_areaAngular_stiffness(physics_rid); -} - -void SoftBody3D::set_volume_stiffness(real_t p_volume_stiffness) { - PhysicsServer3D::get_singleton()->soft_body_set_volume_stiffness(physics_rid, p_volume_stiffness); -} - -real_t SoftBody3D::get_volume_stiffness() { - return PhysicsServer3D::get_singleton()->soft_body_get_volume_stiffness(physics_rid); -} - real_t SoftBody3D::get_pressure_coefficient() { return PhysicsServer3D::get_singleton()->soft_body_get_pressure_coefficient(physics_rid); } -void SoftBody3D::set_pose_matching_coefficient(real_t p_pose_matching_coefficient) { - PhysicsServer3D::get_singleton()->soft_body_set_pose_matching_coefficient(physics_rid, p_pose_matching_coefficient); -} - -real_t SoftBody3D::get_pose_matching_coefficient() { - return PhysicsServer3D::get_singleton()->soft_body_get_pose_matching_coefficient(physics_rid); -} - void SoftBody3D::set_pressure_coefficient(real_t p_pressure_coefficient) { PhysicsServer3D::get_singleton()->soft_body_set_pressure_coefficient(physics_rid, p_pressure_coefficient); } @@ -725,7 +725,7 @@ void SoftBody3D::_update_cache_pin_points_datas() { w[i].spatial_attachment = Object::cast_to<Node3D>(get_node(w[i].spatial_attachment_path)); } if (!w[i].spatial_attachment) { - ERR_PRINT("Node3D node not defined in the pinned point, Softbody undefined behaviour!"); + ERR_PRINT("Node3D node not defined in the pinned point, this is undefined behavior for SoftBody3D!"); } } } @@ -769,7 +769,9 @@ void SoftBody3D::_reset_points_offsets() { PinnedPoint *w = pinned_points.ptrw(); for (int i = pinned_points.size() - 1; 0 <= i; --i) { if (!r[i].spatial_attachment) { - w[i].spatial_attachment = Object::cast_to<Node3D>(get_node(r[i].spatial_attachment_path)); + if (!r[i].spatial_attachment_path.is_empty() && has_node(r[i].spatial_attachment_path)) { + w[i].spatial_attachment = Object::cast_to<Node3D>(get_node(r[i].spatial_attachment_path)); + } } if (!r[i].spatial_attachment) { diff --git a/scene/3d/soft_body_3d.h b/scene/3d/soft_body_3d.h index c59a0b3aa3..46b185a32c 100644 --- a/scene/3d/soft_body_3d.h +++ b/scene/3d/soft_body_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,20 +32,21 @@ #define SOFT_PHYSICS_BODY_H #include "scene/3d/mesh_instance_3d.h" +#include "servers/physics_server_3d.h" class SoftBody3D; -class SoftBodyRenderingServerHandler { +class SoftBodyRenderingServerHandler : public RenderingServerHandler { friend class SoftBody3D; RID mesh; - int surface; + int surface = 0; Vector<uint8_t> buffer; - uint32_t stride; - uint32_t offset_vertices; - uint32_t offset_normal; + uint32_t stride = 0; + uint32_t offset_vertices = 0; + uint32_t offset_normal = 0; - uint8_t *write_buffer; + uint8_t *write_buffer = nullptr; private: SoftBodyRenderingServerHandler(); @@ -57,15 +58,20 @@ private: void commit_changes(); public: - void set_vertex(int p_vertex_id, const void *p_vector3); - void set_normal(int p_vertex_id, const void *p_vector3); - void set_aabb(const AABB &p_aabb); + void set_vertex(int p_vertex_id, const void *p_vector3) override; + void set_normal(int p_vertex_id, const void *p_vector3) override; + void set_aabb(const AABB &p_aabb) override; }; 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; @@ -82,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; @@ -91,13 +99,15 @@ private: bool pinned_points_cache_dirty = true; Ref<ArrayMesh> debug_mesh_cache; - class MeshInstance3D *debug_mesh; + class MeshInstance3D *debug_mesh = nullptr; - bool capture_input_on_drag; + bool capture_input_on_drag = false; bool ray_pickable = true; void _update_pickable(); + void _softbody_changed(); + protected: bool _set(const StringName &p_name, const Variant &p_value); bool _get(const StringName &p_name, Variant &r_ret) const; @@ -107,12 +117,10 @@ protected: bool _set_property_pinned_points_attachment(int p_item, const String &p_what, const Variant &p_value); bool _get_property_pinned_points(int p_item, const String &p_what, Variant &r_ret) const; - virtual void _changed_callback(Object *p_changed, const char *p_prop) override; - void _notification(int p_what); static void _bind_methods(); - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; protected: void _update_physics_server(); @@ -122,17 +130,22 @@ public: void prepare_physics_server(); void become_mesh_owner(); + RID get_physics_rid() const { return physics_rid; } + void set_collision_mask(uint32_t p_mask); uint32_t get_collision_mask() const; void set_collision_layer(uint32_t p_layer); uint32_t get_collision_layer() const; - void set_collision_mask_bit(int p_bit, bool p_value); - bool get_collision_mask_bit(int p_bit) const; + void set_collision_layer_value(int p_layer_number, bool p_value); + bool get_collision_layer_value(int p_layer_number) const; - void set_collision_layer_bit(int p_bit, bool p_value); - bool get_collision_layer_bit(int p_bit) const; + void set_collision_mask_value(int p_layer_number, bool p_value); + bool get_collision_mask_value(int p_layer_number) 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; @@ -149,18 +162,9 @@ public: void set_linear_stiffness(real_t p_linear_stiffness); real_t get_linear_stiffness(); - void set_areaAngular_stiffness(real_t p_areaAngular_stiffness); - real_t get_areaAngular_stiffness(); - - void set_volume_stiffness(real_t p_volume_stiffness); - real_t get_volume_stiffness(); - void set_pressure_coefficient(real_t p_pressure_coefficient); real_t get_pressure_coefficient(); - void set_pose_matching_coefficient(real_t p_pose_matching_coefficient); - real_t get_pose_matching_coefficient(); - void set_damping_coefficient(real_t p_damping_coefficient); real_t get_damping_coefficient(); @@ -198,4 +202,6 @@ private: int _has_pinned_point(int p_point_index) const; }; +VARIANT_ENUM_CAST(SoftBody3D::DisableMode); + #endif // SOFT_PHYSICS_BODY_H diff --git a/scene/3d/spring_arm_3d.cpp b/scene/3d/spring_arm_3d.cpp index 9775ecc6c6..4748a9d889 100644 --- a/scene/3d/spring_arm_3d.cpp +++ b/scene/3d/spring_arm_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,11 +30,6 @@ #include "spring_arm_3d.h" -#include "core/engine.h" -#include "scene/3d/collision_object_3d.h" -#include "scene/resources/sphere_shape_3d.h" -#include "servers/physics_server_3d.h" - void SpringArm3D::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: @@ -78,13 +73,13 @@ void SpringArm3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "margin"), "set_margin", "get_margin"); } -float SpringArm3D::get_length() const { +real_t SpringArm3D::get_length() const { return spring_length; } -void SpringArm3D::set_length(float p_length) { +void SpringArm3D::set_length(real_t p_length) { if (is_inside_tree() && (Engine::get_singleton()->is_editor_hint() || get_tree()->is_debugging_collisions_hint())) { - update_gizmo(); + update_gizmos(); } spring_length = p_length; @@ -106,11 +101,11 @@ uint32_t SpringArm3D::get_mask() { return mask; } -float SpringArm3D::get_margin() { +real_t SpringArm3D::get_margin() { return margin; } -void SpringArm3D::set_margin(float p_margin) { +void SpringArm3D::set_margin(real_t p_margin) { margin = p_margin; } @@ -126,7 +121,7 @@ void SpringArm3D::clear_excluded_objects() { excluded_objects.clear(); } -float SpringArm3D::get_hit_length() { +real_t SpringArm3D::get_hit_length() { return current_spring_length; } @@ -143,7 +138,7 @@ void SpringArm3D::process_spring() { PhysicsDirectSpaceState3D::RayResult r; bool intersected = get_world_3d()->get_direct_space_state()->intersect_ray(get_global_transform().origin, get_global_transform().origin + motion, r, excluded_objects, mask); if (intersected) { - float dist = get_global_transform().origin.distance_to(r.position); + real_t dist = get_global_transform().origin.distance_to(r.position); dist -= margin; motion_delta = dist / (spring_length); } @@ -153,7 +148,7 @@ void SpringArm3D::process_spring() { } current_spring_length = spring_length * motion_delta; - Transform childs_transform; + Transform3D childs_transform; childs_transform.origin = get_global_transform().origin + cast_direction * (spring_length * motion_delta); for (int i = get_child_count() - 1; 0 <= i; --i) { diff --git a/scene/3d/spring_arm_3d.h b/scene/3d/spring_arm_3d.h index 7f6fe2f1a2..63505ab9d3 100644 --- a/scene/3d/spring_arm_3d.h +++ b/scene/3d/spring_arm_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -38,19 +38,19 @@ class SpringArm3D : public Node3D { Ref<Shape3D> shape; Set<RID> excluded_objects; - float spring_length = 1; - float current_spring_length = 0; + real_t spring_length = 1.0; + real_t current_spring_length = 0.0; bool keep_child_basis = false; uint32_t mask = 1; - float margin = 0.01; + real_t margin = 0.01; protected: void _notification(int p_what); static void _bind_methods(); public: - void set_length(float p_length); - float get_length() const; + void set_length(real_t p_length); + real_t get_length() const; void set_shape(Ref<Shape3D> p_shape); Ref<Shape3D> get_shape() const; void set_mask(uint32_t p_mask); @@ -58,9 +58,9 @@ public: void add_excluded_object(RID p_rid); bool remove_excluded_object(RID p_rid); void clear_excluded_objects(); - float get_hit_length(); - void set_margin(float p_margin); - float get_margin(); + real_t get_hit_length(); + void set_margin(real_t p_margin); + real_t get_margin(); SpringArm3D() {} diff --git a/scene/3d/sprite_3d.cpp b/scene/3d/sprite_3d.cpp index 9f4d64cb32..b9a2736918 100644 --- a/scene/3d/sprite_3d.cpp +++ b/scene/3d/sprite_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -60,8 +60,8 @@ void SpriteBase3D::_propagate_color_changed() { color_dirty = true; _queue_update(); - for (List<SpriteBase3D *>::Element *E = children.front(); E; E = E->next()) { - E->get()->_propagate_color_changed(); + for (SpriteBase3D *&E : children) { + E->_propagate_color_changed(); } } @@ -132,12 +132,12 @@ Color SpriteBase3D::get_modulate() const { return modulate; } -void SpriteBase3D::set_pixel_size(float p_amount) { +void SpriteBase3D::set_pixel_size(real_t p_amount) { pixel_size = p_amount; _queue_update(); } -float SpriteBase3D::get_pixel_size() const { +real_t SpriteBase3D::get_pixel_size() const { return pixel_size; } @@ -164,6 +164,8 @@ void SpriteBase3D::_im_update() { _draw(); pending_update = false; + + //texture->draw_rect_region(ci,dst_rect,src_rect,modulate); } void SpriteBase3D::_queue_update() { @@ -172,7 +174,7 @@ void SpriteBase3D::_queue_update() { } triangle_mesh.unref(); - update_gizmo(); + update_gizmos(); pending_update = true; call_deferred(SceneStringNames::get_singleton()->_im_update); @@ -201,7 +203,7 @@ Ref<TriangleMesh> SpriteBase3D::generate_triangle_mesh() const { return Ref<TriangleMesh>(); } - float pixel_size = get_pixel_size(); + real_t pixel_size = get_pixel_size(); Vector2 vertices[4] = { @@ -314,6 +316,9 @@ void SpriteBase3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_item_rect"), &SpriteBase3D::get_item_rect); ClassDB::bind_method(D_METHOD("generate_triangle_mesh"), &SpriteBase3D::generate_triangle_mesh); + ClassDB::bind_method(D_METHOD("_queue_update"), &SpriteBase3D::_queue_update); + ClassDB::bind_method(D_METHOD("_im_update"), &SpriteBase3D::_im_update); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "centered"), "set_centered", "is_centered"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "offset"), "set_offset", "get_offset"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "flip_h"), "set_flip_h", "is_flipped_h"); @@ -340,39 +345,93 @@ void SpriteBase3D::_bind_methods() { } SpriteBase3D::SpriteBase3D() { - color_dirty = true; - centered = true; - hflip = false; - vflip = false; - parent_sprite = nullptr; - pI = nullptr; - for (int i = 0; i < FLAG_MAX; i++) { flags[i] = i == FLAG_TRANSPARENT || i == FLAG_DOUBLE_SIDED; } - alpha_cut = ALPHA_CUT_DISABLED; - billboard_mode = StandardMaterial3D::BILLBOARD_DISABLED; - axis = Vector3::AXIS_Z; - pixel_size = 0.01; - modulate = Color(1, 1, 1, 1); - pending_update = false; - opacity = 1.0; - immediate = RenderingServer::get_singleton()->immediate_create(); - set_base(immediate); + material = RenderingServer::get_singleton()->material_create(); + // Set defaults for material, names need to match up those in StandardMaterial3D + RS::get_singleton()->material_set_param(material, "albedo", Color(1, 1, 1, 1)); + RS::get_singleton()->material_set_param(material, "specular", 0.5); + RS::get_singleton()->material_set_param(material, "metallic", 0.0); + RS::get_singleton()->material_set_param(material, "roughness", 1.0); + RS::get_singleton()->material_set_param(material, "uv1_offset", Vector3(0, 0, 0)); + RS::get_singleton()->material_set_param(material, "uv1_scale", Vector3(1, 1, 1)); + RS::get_singleton()->material_set_param(material, "uv2_offset", Vector3(0, 0, 0)); + RS::get_singleton()->material_set_param(material, "uv2_scale", Vector3(1, 1, 1)); + RS::get_singleton()->material_set_param(material, "alpha_scissor_threshold", 0.98); + + mesh = RenderingServer::get_singleton()->mesh_create(); + + PackedVector3Array mesh_vertices; + PackedVector3Array mesh_normals; + PackedFloat32Array mesh_tangents; + PackedColorArray mesh_colors; + PackedVector2Array mesh_uvs; + PackedInt32Array indices; + + mesh_vertices.resize(4); + mesh_normals.resize(4); + mesh_tangents.resize(16); + mesh_colors.resize(4); + mesh_uvs.resize(4); + + // create basic mesh and store format information + for (int i = 0; i < 4; i++) { + mesh_normals.write[i] = Vector3(0.0, 0.0, 0.0); + mesh_tangents.write[i * 4 + 0] = 0.0; + mesh_tangents.write[i * 4 + 1] = 0.0; + mesh_tangents.write[i * 4 + 2] = 0.0; + mesh_tangents.write[i * 4 + 3] = 0.0; + mesh_colors.write[i] = Color(1.0, 1.0, 1.0, 1.0); + mesh_uvs.write[i] = Vector2(0.0, 0.0); + mesh_vertices.write[i] = Vector3(0.0, 0.0, 0.0); + } + + indices.resize(6); + indices.write[0] = 0; + indices.write[1] = 1; + indices.write[2] = 2; + indices.write[3] = 0; + indices.write[4] = 2; + indices.write[5] = 3; + + Array mesh_array; + mesh_array.resize(RS::ARRAY_MAX); + mesh_array[RS::ARRAY_VERTEX] = mesh_vertices; + mesh_array[RS::ARRAY_NORMAL] = mesh_normals; + mesh_array[RS::ARRAY_TANGENT] = mesh_tangents; + mesh_array[RS::ARRAY_COLOR] = mesh_colors; + mesh_array[RS::ARRAY_TEX_UV] = mesh_uvs; + mesh_array[RS::ARRAY_INDEX] = indices; + + RS::SurfaceData sd; + RS::get_singleton()->mesh_create_surface_data_from_arrays(&sd, RS::PRIMITIVE_TRIANGLES, mesh_array); + + mesh_surface_format = sd.format; + vertex_buffer = sd.vertex_data; + attribute_buffer = sd.attribute_data; + + sd.material = material; + + RS::get_singleton()->mesh_surface_make_offsets_from_format(sd.format, sd.vertex_count, sd.index_count, mesh_surface_offsets, vertex_stride, attrib_stride, skin_stride); + RS::get_singleton()->mesh_add_surface(mesh, sd); + set_base(mesh); } SpriteBase3D::~SpriteBase3D() { - RenderingServer::get_singleton()->free(immediate); + RenderingServer::get_singleton()->free(mesh); + RenderingServer::get_singleton()->free(material); } /////////////////////////////////////////// void Sprite3D::_draw() { - RID immediate = get_immediate(); - - RS::get_singleton()->immediate_clear(immediate); + if (get_base() != get_mesh()) { + set_base(get_mesh()); + } if (!texture.is_valid()) { + set_base(RID()); return; } Vector2 tsize = texture->get_size(); @@ -411,7 +470,7 @@ void Sprite3D::_draw() { Color color = _get_color_accum(); color.a *= get_opacity(); - float pixel_size = get_pixel_size(); + real_t pixel_size = get_pixel_size(); Vector2 vertices[4] = { @@ -442,6 +501,7 @@ void Sprite3D::_draw() { SWAP(uvs[0], uvs[1]); SWAP(uvs[2], uvs[3]); } + if (is_flipped_v()) { SWAP(uvs[0], uvs[3]); SWAP(uvs[1], uvs[2]); @@ -458,11 +518,6 @@ void Sprite3D::_draw() { tangent = Plane(1, 0, 0, 1); } - RID mat = StandardMaterial3D::get_material_rid_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y); - RS::get_singleton()->immediate_set_material(immediate, mat); - - RS::get_singleton()->immediate_begin(immediate, RS::PRIMITIVE_TRIANGLES, texture->get_rid()); - int x_axis = ((axis + 1) % 3); int y_axis = ((axis + 2) % 3); @@ -482,31 +537,80 @@ void Sprite3D::_draw() { AABB aabb; - for (int i = 0; i < 6; i++) { - static const int index[6] = { 0, 1, 2, 0, 2, 3 }; + // Everything except position and UV is compressed + uint8_t *vertex_write_buffer = vertex_buffer.ptrw(); + uint8_t *attribute_write_buffer = attribute_buffer.ptrw(); + + uint32_t v_normal; + { + Vector3 n = normal * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5); + + uint32_t value = 0; + value |= CLAMP(int(n.x * 1023.0), 0, 1023); + value |= CLAMP(int(n.y * 1023.0), 0, 1023) << 10; + value |= CLAMP(int(n.z * 1023.0), 0, 1023) << 20; + + v_normal = value; + } + uint32_t v_tangent; + { + Plane t = tangent; + uint32_t value = 0; + value |= CLAMP(int((t.normal.x * 0.5 + 0.5) * 1023.0), 0, 1023); + value |= CLAMP(int((t.normal.y * 0.5 + 0.5) * 1023.0), 0, 1023) << 10; + value |= CLAMP(int((t.normal.z * 0.5 + 0.5) * 1023.0), 0, 1023) << 20; + if (t.d > 0) { + value |= 3 << 30; + } + v_tangent = value; + } - RS::get_singleton()->immediate_normal(immediate, normal); - RS::get_singleton()->immediate_tangent(immediate, tangent); - RS::get_singleton()->immediate_color(immediate, color); - RS::get_singleton()->immediate_uv(immediate, uvs[i]); + uint8_t v_color[4] = { + uint8_t(CLAMP(color.r * 255.0, 0.0, 255.0)), + uint8_t(CLAMP(color.g * 255.0, 0.0, 255.0)), + uint8_t(CLAMP(color.b * 255.0, 0.0, 255.0)), + uint8_t(CLAMP(color.a * 255.0, 0.0, 255.0)) + }; + for (int i = 0; i < 4; i++) { Vector3 vtx; - vtx[x_axis] = vertices[index[i]][0]; - vtx[y_axis] = vertices[index[i]][1]; - RS::get_singleton()->immediate_vertex(immediate, vtx); + vtx[x_axis] = vertices[i][0]; + vtx[y_axis] = vertices[i][1]; if (i == 0) { aabb.position = vtx; aabb.size = Vector3(); } else { aabb.expand_to(vtx); } + + float v_uv[2] = { (float)uvs[i].x, (float)uvs[i].y }; + memcpy(&attribute_write_buffer[i * attrib_stride + mesh_surface_offsets[RS::ARRAY_TEX_UV]], v_uv, 8); + + float v_vertex[3] = { (float)vtx.x, (float)vtx.y, (float)vtx.z }; + + memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_VERTEX]], &v_vertex, sizeof(float) * 3); + memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_NORMAL]], &v_normal, 4); + memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_TANGENT]], &v_tangent, 4); + memcpy(&attribute_write_buffer[i * attrib_stride + mesh_surface_offsets[RS::ARRAY_COLOR]], v_color, 4); } + + RID mesh = get_mesh(); + RS::get_singleton()->mesh_surface_update_vertex_region(mesh, 0, 0, vertex_buffer); + RS::get_singleton()->mesh_surface_update_attribute_region(mesh, 0, 0, attribute_buffer); + + RS::get_singleton()->mesh_set_custom_aabb(mesh, aabb); set_aabb(aabb); - RS::get_singleton()->immediate_end(immediate); -} -void Sprite3D::_texture_changed() { - _queue_update(); + RID shader_rid; + StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, &shader_rid); + if (last_shader != shader_rid) { + RS::get_singleton()->material_set_shader(get_material(), shader_rid); + last_shader = shader_rid; + } + if (last_texture != texture->get_rid()) { + RS::get_singleton()->material_set_param(get_material(), "texture_albedo", texture->get_rid()); + last_texture = texture->get_rid(); + } } void Sprite3D::set_texture(const Ref<Texture2D> &p_texture) { @@ -514,11 +618,11 @@ void Sprite3D::set_texture(const Ref<Texture2D> &p_texture) { return; } if (texture.is_valid()) { - texture->disconnect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Sprite3D::_texture_changed)); + texture->disconnect(CoreStringNames::get_singleton()->changed, Callable(this, "_queue_update")); } texture = p_texture; if (texture.is_valid()) { - texture->connect(CoreStringNames::get_singleton()->changed, callable_mp(this, &Sprite3D::_texture_changed)); + texture->connect(CoreStringNames::get_singleton()->changed, Callable(this, "_queue_update")); } _queue_update(); } @@ -527,7 +631,7 @@ Ref<Texture2D> Sprite3D::get_texture() const { return texture; } -void Sprite3D::set_region(bool p_region) { +void Sprite3D::set_region_enabled(bool p_region) { if (p_region == region) { return; } @@ -536,7 +640,7 @@ void Sprite3D::set_region(bool p_region) { _queue_update(); } -bool Sprite3D::is_region() const { +bool Sprite3D::is_region_enabled() const { return region; } @@ -559,8 +663,6 @@ void Sprite3D::set_frame(int p_frame) { _queue_update(); - _change_notify("frame"); - _change_notify("frame_coords"); emit_signal(SceneStringNames::get_singleton()->frame_changed); } @@ -568,22 +670,22 @@ int Sprite3D::get_frame() const { return frame; } -void Sprite3D::set_frame_coords(const Vector2 &p_coord) { - ERR_FAIL_INDEX(int(p_coord.x), hframes); - ERR_FAIL_INDEX(int(p_coord.y), vframes); +void Sprite3D::set_frame_coords(const Vector2i &p_coord) { + ERR_FAIL_INDEX(p_coord.x, hframes); + ERR_FAIL_INDEX(p_coord.y, vframes); - set_frame(int(p_coord.y) * hframes + int(p_coord.x)); + set_frame(p_coord.y * hframes + p_coord.x); } -Vector2 Sprite3D::get_frame_coords() const { - return Vector2(frame % hframes, frame / hframes); +Vector2i Sprite3D::get_frame_coords() const { + return Vector2i(frame % hframes, frame / hframes); } void Sprite3D::set_vframes(int p_amount) { ERR_FAIL_COND(p_amount < 1); vframes = p_amount; _queue_update(); - _change_notify(); + notify_property_list_changed(); } int Sprite3D::get_vframes() const { @@ -594,7 +696,7 @@ void Sprite3D::set_hframes(int p_amount) { ERR_FAIL_COND(p_amount < 1); hframes = p_amount; _queue_update(); - _change_notify(); + notify_property_list_changed(); } int Sprite3D::get_hframes() const { @@ -647,8 +749,8 @@ void Sprite3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_texture", "texture"), &Sprite3D::set_texture); ClassDB::bind_method(D_METHOD("get_texture"), &Sprite3D::get_texture); - ClassDB::bind_method(D_METHOD("set_region", "enabled"), &Sprite3D::set_region); - ClassDB::bind_method(D_METHOD("is_region"), &Sprite3D::is_region); + ClassDB::bind_method(D_METHOD("set_region_enabled", "enabled"), &Sprite3D::set_region_enabled); + ClassDB::bind_method(D_METHOD("is_region_enabled"), &Sprite3D::is_region_enabled); ClassDB::bind_method(D_METHOD("set_region_rect", "rect"), &Sprite3D::set_region_rect); ClassDB::bind_method(D_METHOD("get_region_rect"), &Sprite3D::get_region_rect); @@ -665,31 +767,28 @@ void Sprite3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_hframes", "hframes"), &Sprite3D::set_hframes); ClassDB::bind_method(D_METHOD("get_hframes"), &Sprite3D::get_hframes); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture2D"), "set_texture", "get_texture"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), "set_texture", "get_texture"); ADD_GROUP("Animation", ""); ADD_PROPERTY(PropertyInfo(Variant::INT, "hframes", PROPERTY_HINT_RANGE, "1,16384,1"), "set_hframes", "get_hframes"); ADD_PROPERTY(PropertyInfo(Variant::INT, "vframes", PROPERTY_HINT_RANGE, "1,16384,1"), "set_vframes", "get_vframes"); ADD_PROPERTY(PropertyInfo(Variant::INT, "frame"), "set_frame", "get_frame"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "frame_coords", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_EDITOR), "set_frame_coords", "get_frame_coords"); ADD_GROUP("Region", "region_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "region_enabled"), "set_region", "is_region"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "region_enabled"), "set_region_enabled", "is_region_enabled"); ADD_PROPERTY(PropertyInfo(Variant::RECT2, "region_rect"), "set_region_rect", "get_region_rect"); ADD_SIGNAL(MethodInfo("frame_changed")); } Sprite3D::Sprite3D() { - region = false; - frame = 0; - vframes = 1; - hframes = 1; } //////////////////////////////////////// void AnimatedSprite3D::_draw() { - RID immediate = get_immediate(); - RS::get_singleton()->immediate_clear(immediate); + if (get_base() != get_mesh()) { + set_base(get_mesh()); + } if (frames.is_null()) { return; @@ -705,6 +804,7 @@ void AnimatedSprite3D::_draw() { Ref<Texture2D> texture = frames->get_frame(animation, frame); if (!texture.is_valid()) { + set_base(RID()); return; //no texuture no life } Vector2 tsize = texture->get_size(); @@ -737,7 +837,7 @@ void AnimatedSprite3D::_draw() { Color color = _get_color_accum(); color.a *= get_opacity(); - float pixel_size = get_pixel_size(); + real_t pixel_size = get_pixel_size(); Vector2 vertices[4] = { @@ -784,12 +884,6 @@ void AnimatedSprite3D::_draw() { tangent = Plane(1, 0, 0, -1); } - RID mat = StandardMaterial3D::get_material_rid_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y); - - RS::get_singleton()->immediate_set_material(immediate, mat); - - RS::get_singleton()->immediate_begin(immediate, RS::PRIMITIVE_TRIANGLES, texture->get_rid()); - int x_axis = ((axis + 1) % 3); int y_axis = ((axis + 2) % 3); @@ -809,30 +903,79 @@ void AnimatedSprite3D::_draw() { AABB aabb; - for (int i = 0; i < 6; i++) { - static const int indices[6] = { - 0, 1, 2, - 0, 2, 3 - }; + // Everything except position and UV is compressed + uint8_t *vertex_write_buffer = vertex_buffer.ptrw(); + uint8_t *attribute_write_buffer = attribute_buffer.ptrw(); + + uint32_t v_normal; + { + Vector3 n = normal * Vector3(0.5, 0.5, 0.5) + Vector3(0.5, 0.5, 0.5); + + uint32_t value = 0; + value |= CLAMP(int(n.x * 1023.0), 0, 1023); + value |= CLAMP(int(n.y * 1023.0), 0, 1023) << 10; + value |= CLAMP(int(n.z * 1023.0), 0, 1023) << 20; + + v_normal = value; + } + uint32_t v_tangent; + { + Plane t = tangent; + uint32_t value = 0; + value |= CLAMP(int((t.normal.x * 0.5 + 0.5) * 1023.0), 0, 1023); + value |= CLAMP(int((t.normal.y * 0.5 + 0.5) * 1023.0), 0, 1023) << 10; + value |= CLAMP(int((t.normal.z * 0.5 + 0.5) * 1023.0), 0, 1023) << 20; + if (t.d > 0) { + value |= 3 << 30; + } + v_tangent = value; + } - RS::get_singleton()->immediate_normal(immediate, normal); - RS::get_singleton()->immediate_tangent(immediate, tangent); - RS::get_singleton()->immediate_color(immediate, color); - RS::get_singleton()->immediate_uv(immediate, uvs[i]); + uint8_t v_color[4] = { + uint8_t(CLAMP(color.r * 255.0, 0.0, 255.0)), + uint8_t(CLAMP(color.g * 255.0, 0.0, 255.0)), + uint8_t(CLAMP(color.b * 255.0, 0.0, 255.0)), + uint8_t(CLAMP(color.a * 255.0, 0.0, 255.0)) + }; + for (int i = 0; i < 4; i++) { Vector3 vtx; - vtx[x_axis] = vertices[indices[i]][0]; - vtx[y_axis] = vertices[indices[i]][1]; - RS::get_singleton()->immediate_vertex(immediate, vtx); + vtx[x_axis] = vertices[i][0]; + vtx[y_axis] = vertices[i][1]; if (i == 0) { aabb.position = vtx; aabb.size = Vector3(); } else { aabb.expand_to(vtx); } + + float v_uv[2] = { (float)uvs[i].x, (float)uvs[i].y }; + memcpy(&attribute_write_buffer[i * attrib_stride + mesh_surface_offsets[RS::ARRAY_TEX_UV]], v_uv, 8); + + float v_vertex[3] = { (float)vtx.x, (float)vtx.y, (float)vtx.z }; + memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_VERTEX]], &v_vertex, sizeof(float) * 3); + memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_NORMAL]], &v_normal, 4); + memcpy(&vertex_write_buffer[i * vertex_stride + mesh_surface_offsets[RS::ARRAY_TANGENT]], &v_tangent, 4); + memcpy(&attribute_write_buffer[i * attrib_stride + mesh_surface_offsets[RS::ARRAY_COLOR]], v_color, 4); } + + RID mesh = get_mesh(); + RS::get_singleton()->mesh_surface_update_vertex_region(mesh, 0, 0, vertex_buffer); + RS::get_singleton()->mesh_surface_update_attribute_region(mesh, 0, 0, attribute_buffer); + + RS::get_singleton()->mesh_set_custom_aabb(mesh, aabb); set_aabb(aabb); - RS::get_singleton()->immediate_end(immediate); + + RID shader_rid; + StandardMaterial3D::get_material_for_2d(get_draw_flag(FLAG_SHADED), get_draw_flag(FLAG_TRANSPARENT), get_draw_flag(FLAG_DOUBLE_SIDED), get_alpha_cut_mode() == ALPHA_CUT_DISCARD, get_alpha_cut_mode() == ALPHA_CUT_OPAQUE_PREPASS, get_billboard_mode() == StandardMaterial3D::BILLBOARD_ENABLED, get_billboard_mode() == StandardMaterial3D::BILLBOARD_FIXED_Y, &shader_rid); + if (last_shader != shader_rid) { + RS::get_singleton()->material_set_shader(get_material(), shader_rid); + last_shader = shader_rid; + } + if (last_texture != texture->get_rid()) { + RS::get_singleton()->material_set_param(get_material(), "texture_albedo", texture->get_rid()); + last_texture = texture->get_rid(); + } } void AnimatedSprite3D::_validate_property(PropertyInfo &property) const { @@ -853,7 +996,7 @@ void AnimatedSprite3D::_validate_property(PropertyInfo &property) const { } property.hint_string += String(E->get()); - if (animation == E->get()) { + if (animation == E) { current_found = true; } } @@ -894,7 +1037,7 @@ void AnimatedSprite3D::_notification(int p_what) { return; //do nothing } - float remaining = get_process_delta_time(); + double remaining = get_process_delta_time(); while (remaining) { if (timeout <= 0) { @@ -907,15 +1050,16 @@ void AnimatedSprite3D::_notification(int p_what) { } else { frame = fc - 1; } + emit_signal(SceneStringNames::get_singleton()->animation_finished); } else { frame++; } _queue_update(); - _change_notify("frame"); + emit_signal(SceneStringNames::get_singleton()->frame_changed); } - float to_process = MIN(timeout, remaining); + double to_process = MIN(timeout, remaining); remaining -= to_process; timeout -= to_process; } @@ -925,11 +1069,11 @@ void AnimatedSprite3D::_notification(int p_what) { void AnimatedSprite3D::set_sprite_frames(const Ref<SpriteFrames> &p_frames) { if (frames.is_valid()) { - frames->disconnect("changed", callable_mp(this, &AnimatedSprite3D::_res_changed)); + frames->disconnect("changed", Callable(this, "_res_changed")); } frames = p_frames; if (frames.is_valid()) { - frames->connect("changed", callable_mp(this, &AnimatedSprite3D::_res_changed)); + frames->connect("changed", Callable(this, "_res_changed")); } if (!frames.is_valid()) { @@ -938,10 +1082,10 @@ void AnimatedSprite3D::set_sprite_frames(const Ref<SpriteFrames> &p_frames) { set_frame(frame); } - _change_notify(); + notify_property_list_changed(); _reset_timeout(); _queue_update(); - update_configuration_warning(); + update_configuration_warnings(); } Ref<SpriteFrames> AnimatedSprite3D::get_sprite_frames() const { @@ -955,9 +1099,8 @@ void AnimatedSprite3D::set_frame(int p_frame) { if (frames->has_animation(animation)) { int limit = frames->get_frame_count(animation); - if (p_frame >= limit) { + if (p_frame >= limit) p_frame = limit - 1; - } } if (p_frame < 0) { @@ -971,7 +1114,6 @@ void AnimatedSprite3D::set_frame(int p_frame) { frame = p_frame; _reset_timeout(); _queue_update(); - _change_notify("frame"); emit_signal(SceneStringNames::get_singleton()->frame_changed); } @@ -1007,8 +1149,6 @@ Rect2 AnimatedSprite3D::get_item_rect() const { void AnimatedSprite3D::_res_changed() { set_frame(frame); - _change_notify("frame"); - _change_notify("animation"); _queue_update(); } @@ -1037,7 +1177,7 @@ void AnimatedSprite3D::stop() { } bool AnimatedSprite3D::is_playing() const { - return is_processing(); + return playing; } void AnimatedSprite3D::_reset_timeout() { @@ -1065,7 +1205,7 @@ void AnimatedSprite3D::set_animation(const StringName &p_animation) { animation = p_animation; _reset_timeout(); set_frame(0); - _change_notify(); + notify_property_list_changed(); _queue_update(); } @@ -1073,17 +1213,13 @@ StringName AnimatedSprite3D::get_animation() const { return animation; } -String AnimatedSprite3D::get_configuration_warning() const { - String warning = SpriteBase3D::get_configuration_warning(); - +TypedArray<String> AnimatedSprite3D::get_configuration_warnings() const { + TypedArray<String> warnings = SpriteBase3D::get_configuration_warnings(); if (frames.is_null()) { - if (!warning.empty()) { - warning += "\n\n"; - } - warning += TTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite3D to display frames."); + warnings.push_back(TTR("A SpriteFrames resource must be created or set in the \"Frames\" property in order for AnimatedSprite3D to display frames.")); } - return warning; + return warnings; } void AnimatedSprite3D::_bind_methods() { @@ -1103,17 +1239,16 @@ void AnimatedSprite3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_frame", "frame"), &AnimatedSprite3D::set_frame); ClassDB::bind_method(D_METHOD("get_frame"), &AnimatedSprite3D::get_frame); + ClassDB::bind_method(D_METHOD("_res_changed"), &AnimatedSprite3D::_res_changed); + ADD_SIGNAL(MethodInfo("frame_changed")); + ADD_SIGNAL(MethodInfo("animation_finished")); ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "frames", PROPERTY_HINT_RESOURCE_TYPE, "SpriteFrames"), "set_sprite_frames", "get_sprite_frames"); - ADD_PROPERTY(PropertyInfo(Variant::STRING_NAME, "animation"), "set_animation", "get_animation"); + ADD_PROPERTY(PropertyInfo(Variant::STRING, "animation"), "set_animation", "get_animation"); ADD_PROPERTY(PropertyInfo(Variant::INT, "frame"), "set_frame", "get_frame"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "playing"), "_set_playing", "_is_playing"); } AnimatedSprite3D::AnimatedSprite3D() { - frame = 0; - playing = false; - animation = "default"; - timeout = 0; } diff --git a/scene/3d/sprite_3d.h b/scene/3d/sprite_3d.h index cb8467aac6..90c2a309e1 100644 --- a/scene/3d/sprite_3d.h +++ b/scene/3d/sprite_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,8 +31,8 @@ #ifndef SPRITE_3D_H #define SPRITE_3D_H -#include "scene/2d/animated_sprite_2d.h" #include "scene/3d/visual_instance_3d.h" +#include "scene/resources/sprite_frames.h" class SpriteBase3D : public GeometryInstance3D { GDCLASS(SpriteBase3D, GeometryInstance3D); @@ -55,32 +55,33 @@ public: }; private: - bool color_dirty; + bool color_dirty = true; Color color_accum; - SpriteBase3D *parent_sprite; + SpriteBase3D *parent_sprite = nullptr; List<SpriteBase3D *> children; - List<SpriteBase3D *>::Element *pI; + List<SpriteBase3D *>::Element *pI = nullptr; - bool centered; + bool centered = true; Point2 offset; - bool hflip; - bool vflip; + bool hflip = false; + bool vflip = false; - Color modulate; - float opacity; + Color modulate = Color(1, 1, 1, 1); + float opacity = 1.0; - Vector3::Axis axis; - float pixel_size; + Vector3::Axis axis = Vector3::AXIS_Z; + real_t pixel_size = 0.01; AABB aabb; - RID immediate; + RID mesh; + RID material; - bool flags[FLAG_MAX]; - AlphaCutMode alpha_cut; - StandardMaterial3D::BillboardMode billboard_mode; - bool pending_update; + bool flags[FLAG_MAX] = {}; + AlphaCutMode alpha_cut = ALPHA_CUT_DISABLED; + StandardMaterial3D::BillboardMode billboard_mode = StandardMaterial3D::BILLBOARD_DISABLED; + bool pending_update = false; void _im_update(); void _propagate_color_changed(); @@ -91,7 +92,17 @@ protected: static void _bind_methods(); virtual void _draw() = 0; _FORCE_INLINE_ void set_aabb(const AABB &p_aabb) { aabb = p_aabb; } - _FORCE_INLINE_ RID &get_immediate() { return immediate; } + _FORCE_INLINE_ RID &get_mesh() { return mesh; } + _FORCE_INLINE_ RID &get_material() { return material; } + + uint32_t mesh_surface_offsets[RS::ARRAY_MAX]; + PackedByteArray vertex_buffer; + PackedByteArray attribute_buffer; + uint32_t vertex_stride; + uint32_t attrib_stride; + uint32_t skin_stride; + uint32_t mesh_surface_format; + void _queue_update(); public: @@ -107,8 +118,8 @@ public: void set_flip_v(bool p_flip); bool is_flipped_v() const; - void set_region(bool p_region); - bool is_region() const; + void set_region_enabled(bool p_region); + bool is_region_enabled() const; void set_region_rect(const Rect2 &p_region_rect); Rect2 get_region_rect() const; @@ -119,8 +130,8 @@ public: void set_opacity(float p_amount); float get_opacity() const; - void set_pixel_size(float p_amount); - float get_pixel_size() const; + void set_pixel_size(real_t p_amount); + real_t get_pixel_size() const; void set_axis(Vector3::Axis p_axis); Vector3::Axis get_axis() const; @@ -147,15 +158,16 @@ class Sprite3D : public SpriteBase3D { GDCLASS(Sprite3D, SpriteBase3D); Ref<Texture2D> texture; - bool region; + bool region = false; Rect2 region_rect; - int frame; + int frame = 0; - int vframes; - int hframes; + int vframes = 1; + int hframes = 1; - void _texture_changed(); + RID last_shader; + RID last_texture; protected: virtual void _draw() override; @@ -167,8 +179,8 @@ public: void set_texture(const Ref<Texture2D> &p_texture); Ref<Texture2D> get_texture() const; - void set_region(bool p_region); - bool is_region() const; + void set_region_enabled(bool p_region); + bool is_region_enabled() const; void set_region_rect(const Rect2 &p_region_rect); Rect2 get_region_rect() const; @@ -176,8 +188,8 @@ public: void set_frame(int p_frame); int get_frame() const; - void set_frame_coords(const Vector2 &p_coord); - Vector2 get_frame_coords() const; + void set_frame_coords(const Vector2i &p_coord); + Vector2i get_frame_coords() const; void set_vframes(int p_amount); int get_vframes() const; @@ -195,18 +207,13 @@ class AnimatedSprite3D : public SpriteBase3D { GDCLASS(AnimatedSprite3D, SpriteBase3D); Ref<SpriteFrames> frames; - bool playing; - StringName animation; - int frame; - - bool centered; + bool playing = false; + StringName animation = "default"; + int frame = 0; - float timeout; + bool centered = false; - bool hflip; - bool vflip; - - Color modulate; + double timeout = 0.0; void _res_changed(); @@ -214,6 +221,9 @@ class AnimatedSprite3D : public SpriteBase3D { void _set_playing(bool p_playing); bool _is_playing() const; + RID last_shader; + RID last_texture; + protected: virtual void _draw() override; static void _bind_methods(); @@ -236,10 +246,11 @@ public: virtual Rect2 get_item_rect() const override; - virtual String get_configuration_warning() const override; + virtual TypedArray<String> get_configuration_warnings() const override; AnimatedSprite3D(); }; VARIANT_ENUM_CAST(SpriteBase3D::DrawFlags); VARIANT_ENUM_CAST(SpriteBase3D::AlphaCutMode); + #endif // SPRITE_3D_H diff --git a/scene/3d/vehicle_body_3d.cpp b/scene/3d/vehicle_body_3d.cpp index b58f313c16..92c0e09947 100644 --- a/scene/3d/vehicle_body_3d.cpp +++ b/scene/3d/vehicle_body_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -79,40 +79,40 @@ 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); - - 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; + 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(); + } break; + + case NOTIFICATION_EXIT_TREE: { + VehicleBody3D *cb = Object::cast_to<VehicleBody3D>(get_parent()); + if (!cb) { + return; + } + cb->wheels.erase(this); + body = nullptr; + } break; } } -String VehicleWheel3D::get_configuration_warning() const { - String warning = Node3D::get_configuration_warning(); +TypedArray<String> VehicleWheel3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (!Object::cast_to<VehicleBody3D>(get_parent())) { - if (!warning.empty()) { - warning += "\n\n"; - } - warning += TTR("VehicleWheel3D serves to provide a wheel system to a VehicleBody3D. Please use it as a child of a VehicleBody3D."); + warnings.push_back(TTR("VehicleWheel3D serves to provide a wheel system to a VehicleBody3D. Please use it as a child of a VehicleBody3D.")); } - return warning; + return warnings; } void VehicleWheel3D::_update(PhysicsDirectBodyState3D *s) { @@ -147,77 +147,77 @@ void VehicleWheel3D::_update(PhysicsDirectBodyState3D *s) { } } -void VehicleWheel3D::set_radius(float p_radius) { +void VehicleWheel3D::set_radius(real_t p_radius) { m_wheelRadius = p_radius; - update_gizmo(); + update_gizmos(); } -float VehicleWheel3D::get_radius() const { +real_t VehicleWheel3D::get_radius() const { return m_wheelRadius; } -void VehicleWheel3D::set_suspension_rest_length(float p_length) { +void VehicleWheel3D::set_suspension_rest_length(real_t p_length) { m_suspensionRestLength = p_length; - update_gizmo(); + update_gizmos(); } -float VehicleWheel3D::get_suspension_rest_length() const { +real_t VehicleWheel3D::get_suspension_rest_length() const { return m_suspensionRestLength; } -void VehicleWheel3D::set_suspension_travel(float p_length) { +void VehicleWheel3D::set_suspension_travel(real_t p_length) { m_maxSuspensionTravelCm = p_length / 0.01; } -float VehicleWheel3D::get_suspension_travel() const { +real_t VehicleWheel3D::get_suspension_travel() const { return m_maxSuspensionTravelCm * 0.01; } -void VehicleWheel3D::set_suspension_stiffness(float p_value) { +void VehicleWheel3D::set_suspension_stiffness(real_t p_value) { m_suspensionStiffness = p_value; } -float VehicleWheel3D::get_suspension_stiffness() const { +real_t VehicleWheel3D::get_suspension_stiffness() const { return m_suspensionStiffness; } -void VehicleWheel3D::set_suspension_max_force(float p_value) { +void VehicleWheel3D::set_suspension_max_force(real_t p_value) { m_maxSuspensionForce = p_value; } -float VehicleWheel3D::get_suspension_max_force() const { +real_t VehicleWheel3D::get_suspension_max_force() const { return m_maxSuspensionForce; } -void VehicleWheel3D::set_damping_compression(float p_value) { +void VehicleWheel3D::set_damping_compression(real_t p_value) { m_wheelsDampingCompression = p_value; } -float VehicleWheel3D::get_damping_compression() const { +real_t VehicleWheel3D::get_damping_compression() const { return m_wheelsDampingCompression; } -void VehicleWheel3D::set_damping_relaxation(float p_value) { +void VehicleWheel3D::set_damping_relaxation(real_t p_value) { m_wheelsDampingRelaxation = p_value; } -float VehicleWheel3D::get_damping_relaxation() const { +real_t VehicleWheel3D::get_damping_relaxation() const { return m_wheelsDampingRelaxation; } -void VehicleWheel3D::set_friction_slip(float p_value) { +void VehicleWheel3D::set_friction_slip(real_t p_value) { m_frictionSlip = p_value; } -float VehicleWheel3D::get_friction_slip() const { +real_t VehicleWheel3D::get_friction_slip() const { return m_frictionSlip; } -void VehicleWheel3D::set_roll_influence(float p_value) { +void VehicleWheel3D::set_roll_influence(real_t p_value) { m_rollInfluence = p_value; } -float VehicleWheel3D::get_roll_influence() const { +real_t VehicleWheel3D::get_roll_influence() const { return m_rollInfluence; } @@ -295,27 +295,27 @@ void VehicleWheel3D::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "damping_relaxation"), "set_damping_relaxation", "get_damping_relaxation"); } -void VehicleWheel3D::set_engine_force(float p_engine_force) { +void VehicleWheel3D::set_engine_force(real_t p_engine_force) { m_engineForce = p_engine_force; } -float VehicleWheel3D::get_engine_force() const { +real_t VehicleWheel3D::get_engine_force() const { return m_engineForce; } -void VehicleWheel3D::set_brake(float p_brake) { +void VehicleWheel3D::set_brake(real_t p_brake) { m_brake = p_brake; } -float VehicleWheel3D::get_brake() const { +real_t VehicleWheel3D::get_brake() const { return m_brake; } -void VehicleWheel3D::set_steering(float p_steering) { +void VehicleWheel3D::set_steering(real_t p_steering) { m_steering = p_steering; } -float VehicleWheel3D::get_steering() const { +real_t VehicleWheel3D::get_steering() const { return m_steering; } @@ -335,45 +335,21 @@ bool VehicleWheel3D::is_used_as_steering() const { return steers; } -float VehicleWheel3D::get_skidinfo() const { +real_t VehicleWheel3D::get_skidinfo() const { return m_skidInfo; } -float VehicleWheel3D::get_rpm() const { +real_t VehicleWheel3D::get_rpm() const { return m_rpm; } VehicleWheel3D::VehicleWheel3D() { - steers = false; - engine_traction = false; - m_steering = real_t(0.); - m_engineForce = real_t(0.); - m_rotation = real_t(0.); - m_deltaRotation = real_t(0.); - m_brake = real_t(0.); - m_rollInfluence = real_t(0.1); - - m_suspensionRestLength = 0.15; - m_wheelRadius = 0.5; //0.28; - m_suspensionStiffness = 5.88; - m_wheelsDampingCompression = 0.83; - m_wheelsDampingRelaxation = 0.88; - m_frictionSlip = 10.5; - m_bIsFrontWheel = false; - m_maxSuspensionTravelCm = 500; - m_maxSuspensionForce = 6000; - - m_suspensionRelativeVelocity = 0; - m_clippedInvContactDotSuspension = 1.0; - m_raycastInfo.m_isInContact = false; - - body = nullptr; } void VehicleBody3D::_update_wheel_transform(VehicleWheel3D &wheel, PhysicsDirectBodyState3D *s) { wheel.m_raycastInfo.m_isInContact = false; - Transform chassisTrans = s->get_transform(); + Transform3D chassisTrans = s->get_transform(); /* if (interpolatedTransform && (getRigidBody()->getMotionState())) { getRigidBody()->getMotionState()->getWorldTransform(chassisTrans); @@ -431,7 +407,7 @@ real_t VehicleBody3D::_ray_cast(int p_idx, PhysicsDirectBodyState3D *s) { PhysicsDirectSpaceState3D *ss = s->get_space_state(); - bool col = ss->intersect_ray(source, target, rr, exclude); + bool col = ss->intersect_ray(source, target, rr, exclude, get_collision_mask()); wheel.m_raycastInfo.m_groundObject = nullptr; @@ -564,7 +540,7 @@ void VehicleBody3D::_resolve_single_bilateral(PhysicsDirectBodyState3D *s, const Vector3 vel = vel1 - vel2; Basis b2trans; - float b2invmass = 0; + real_t b2invmass = 0; Vector3 b2lv; Vector3 b2av; Vector3 b2invinertia; //todo @@ -587,7 +563,7 @@ void VehicleBody3D::_resolve_single_bilateral(PhysicsDirectBodyState3D *s, const b2invmass); // FIXME: rel_vel assignment here is overwritten by the following assignment. - // What seems to be intended in the next next assignment is: rel_vel = normal.dot(rel_vel); + // What seems to be intended in the next assignment is: rel_vel = normal.dot(rel_vel); // Investigate why. real_t rel_vel = jac.getRelativeVelocity( s->get_linear_velocity(), @@ -622,8 +598,8 @@ VehicleBody3D::btVehicleWheelContactPoint::btVehicleWheelContactPoint(PhysicsDir m_frictionPositionWorld(frictionPosWorld), m_frictionDirectionWorld(frictionDirectionWorld), m_maxImpulse(maxImpulse) { - float denom0 = 0; - float denom1 = 0; + real_t denom0 = 0; + real_t denom1 = 0; { Vector3 r0 = frictionPosWorld - s->get_transform().origin; @@ -634,7 +610,6 @@ VehicleBody3D::btVehicleWheelContactPoint::btVehicleWheelContactPoint(PhysicsDir /* TODO: Why is this code unused? if (body1) { - Vector3 r0 = frictionPosWorld - body1->get_global_transform().origin; Vector3 c0 = (r0).cross(frictionDirectionWorld); Vector3 vec = s->get_inverse_inertia_tensor().xform_inv(c0).cross(r0); @@ -812,7 +787,7 @@ void VehicleBody3D::_update_friction(PhysicsDirectBodyState3D *s) { Vector3 sideImp = m_axle[wheel] * m_sideImpulse[wheel]; #if defined ROLLING_INFLUENCE_FIX // fix. It only worked if car's up was along Y - VT. - Vector3 vChassisWorldUp = s->get_transform().basis.transposed()[1]; //getRigidBody()->getCenterOfMassTransform().getBasis().getColumn(m_indexUpAxis); + Vector3 vChassisWorldUp = s->get_transform().basis.transposed()[1]; //getRigidBody()->getCenterOfMassTransform3D().getBasis().getColumn(m_indexUpAxis); rel_pos -= vChassisWorldUp * (vChassisWorldUp.dot(rel_pos) * (1.f - wheelInfo.m_rollInfluence)); #else rel_pos[1] *= wheelInfo.m_rollInfluence; //? @@ -831,8 +806,9 @@ void VehicleBody3D::_direct_state_changed(Object *p_state) { RigidBody3D::_direct_state_changed(p_state); state = Object::cast_to<PhysicsDirectBodyState3D>(p_state); + ERR_FAIL_NULL_MSG(state, "Method '_direct_state_changed' must receive a valid PhysicsDirectBodyState3D object as argument"); - float step = state->get_step(); + real_t step = state->get_step(); for (int i = 0; i < wheels.size(); i++) { _update_wheel(i, state); @@ -868,7 +844,7 @@ void VehicleBody3D::_direct_state_changed(Object *p_state) { Vector3 vel = state->get_linear_velocity() + (state->get_angular_velocity()).cross(relpos); // * mPos); if (wheel.m_raycastInfo.m_isInContact) { - const Transform &chassisWorldTransform = state->get_transform(); + const Transform3D &chassisWorldTransform = state->get_transform(); Vector3 fwd( chassisWorldTransform.basis[0][Vector3::AXIS_Z], @@ -892,7 +868,7 @@ void VehicleBody3D::_direct_state_changed(Object *p_state) { state = nullptr; } -void VehicleBody3D::set_engine_force(float p_engine_force) { +void VehicleBody3D::set_engine_force(real_t p_engine_force) { engine_force = p_engine_force; for (int i = 0; i < wheels.size(); i++) { VehicleWheel3D &wheelInfo = *wheels[i]; @@ -902,11 +878,11 @@ void VehicleBody3D::set_engine_force(float p_engine_force) { } } -float VehicleBody3D::get_engine_force() const { +real_t VehicleBody3D::get_engine_force() const { return engine_force; } -void VehicleBody3D::set_brake(float p_brake) { +void VehicleBody3D::set_brake(real_t p_brake) { brake = p_brake; for (int i = 0; i < wheels.size(); i++) { VehicleWheel3D &wheelInfo = *wheels[i]; @@ -914,11 +890,11 @@ void VehicleBody3D::set_brake(float p_brake) { } } -float VehicleBody3D::get_brake() const { +real_t VehicleBody3D::get_brake() const { return brake; } -void VehicleBody3D::set_steering(float p_steering) { +void VehicleBody3D::set_steering(real_t p_steering) { m_steeringValue = p_steering; for (int i = 0; i < wheels.size(); i++) { VehicleWheel3D &wheelInfo = *wheels[i]; @@ -928,7 +904,7 @@ void VehicleBody3D::set_steering(float p_steering) { } } -float VehicleBody3D::get_steering() const { +real_t VehicleBody3D::get_steering() const { return m_steeringValue; } @@ -949,18 +925,8 @@ void VehicleBody3D::_bind_methods() { } VehicleBody3D::VehicleBody3D() { - m_pitchControl = 0; - m_currentVehicleSpeedKmHour = real_t(0.); - m_steeringValue = real_t(0.); - - engine_force = 0; - brake = 0; - - state = nullptr; - ccd = false; - exclude.insert(get_rid()); - //PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), this, "_direct_state_changed"); + //PhysicsServer3D::get_singleton()->body_set_force_integration_callback(get_rid(), callable_mp(this, &VehicleBody3D::_direct_state_changed)); set_mass(40); } diff --git a/scene/3d/vehicle_body_3d.h b/scene/3d/vehicle_body_3d.h index 0b4b3a4440..2c10205ea3 100644 --- a/scene/3d/vehicle_body_3d.h +++ b/scene/3d/vehicle_body_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -40,54 +40,54 @@ class VehicleWheel3D : public Node3D { friend class VehicleBody3D; - Transform m_worldTransform; - Transform local_xform; - bool engine_traction; - bool steers; + Transform3D m_worldTransform; + Transform3D local_xform; + bool engine_traction = false; + bool steers = false; Vector3 m_chassisConnectionPointCS; //const Vector3 m_wheelDirectionCS; //const Vector3 m_wheelAxleCS; // const or modified by steering - real_t m_suspensionRestLength; - real_t m_maxSuspensionTravelCm; - real_t m_wheelRadius; + real_t m_suspensionRestLength = 0.15; + real_t m_maxSuspensionTravelCm = 500.0; + real_t m_wheelRadius = 0.5; - real_t m_suspensionStiffness; - real_t m_wheelsDampingCompression; - real_t m_wheelsDampingRelaxation; - real_t m_frictionSlip; - real_t m_maxSuspensionForce; - bool m_bIsFrontWheel; + real_t m_suspensionStiffness = 5.88; + real_t m_wheelsDampingCompression = 0.83; + real_t m_wheelsDampingRelaxation = 0.88; + real_t m_frictionSlip = 10.5; + real_t m_maxSuspensionForce = 6000.0; + bool m_bIsFrontWheel = false; - VehicleBody3D *body; + VehicleBody3D *body = nullptr; //btVector3 m_wheelAxleCS; // const or modified by steering ? - real_t m_steering; - real_t m_rotation; - real_t m_deltaRotation; - real_t m_rpm; - real_t m_rollInfluence; - real_t m_engineForce; - real_t m_brake; + real_t m_steering = 0.0; + real_t m_rotation = 0.0; + real_t m_deltaRotation = 0.0; + real_t m_rpm = 0.0; + real_t m_rollInfluence = 0.1; + real_t m_engineForce = 0.0; + real_t m_brake = 0.0; - real_t m_clippedInvContactDotSuspension; - real_t m_suspensionRelativeVelocity; + real_t m_clippedInvContactDotSuspension = 1.0; + real_t m_suspensionRelativeVelocity = 0.0; //calculated by suspension - real_t m_wheelsSuspensionForce; - real_t m_skidInfo; + real_t m_wheelsSuspensionForce = 0.0; + real_t m_skidInfo = 0.0; struct RaycastInfo { //set by raycaster Vector3 m_contactNormalWS; //contactnormal Vector3 m_contactPointWS; //raycast hitpoint - real_t m_suspensionLength; + real_t m_suspensionLength = 0.0; Vector3 m_hardPointWS; //raycast starting point Vector3 m_wheelDirectionWS; //direction in worldspace Vector3 m_wheelAxleWS; // axle in worldspace - bool m_isInContact; - PhysicsBody3D *m_groundObject; //could be general void* ptr + bool m_isInContact = false; + PhysicsBody3D *m_groundObject = nullptr; //could be general void* ptr } m_raycastInfo; void _update(PhysicsDirectBodyState3D *s); @@ -97,29 +97,29 @@ protected: static void _bind_methods(); public: - void set_radius(float p_radius); - float get_radius() const; + void set_radius(real_t p_radius); + real_t get_radius() const; - void set_suspension_rest_length(float p_length); - float get_suspension_rest_length() const; + void set_suspension_rest_length(real_t p_length); + real_t get_suspension_rest_length() const; - void set_suspension_travel(float p_length); - float get_suspension_travel() const; + void set_suspension_travel(real_t p_length); + real_t get_suspension_travel() const; - void set_suspension_stiffness(float p_value); - float get_suspension_stiffness() const; + void set_suspension_stiffness(real_t p_value); + real_t get_suspension_stiffness() const; - void set_suspension_max_force(float p_value); - float get_suspension_max_force() const; + void set_suspension_max_force(real_t p_value); + real_t get_suspension_max_force() const; - void set_damping_compression(float p_value); - float get_damping_compression() const; + void set_damping_compression(real_t p_value); + real_t get_damping_compression() const; - void set_damping_relaxation(float p_value); - float get_damping_relaxation() const; + void set_damping_relaxation(real_t p_value); + real_t get_damping_relaxation() const; - void set_friction_slip(float p_value); - float get_friction_slip() const; + void set_friction_slip(real_t p_value); + real_t get_friction_slip() const; void set_use_as_traction(bool p_enable); bool is_used_as_traction() const; @@ -129,23 +129,23 @@ public: bool is_in_contact() const; - void set_roll_influence(float p_value); - float get_roll_influence() const; + void set_roll_influence(real_t p_value); + real_t get_roll_influence() const; - float get_skidinfo() const; + real_t get_skidinfo() const; - float get_rpm() const; + real_t get_rpm() const; - void set_engine_force(float p_engine_force); - float get_engine_force() const; + void set_engine_force(real_t p_engine_force); + real_t get_engine_force() const; - void set_brake(float p_brake); - float get_brake() const; + void set_brake(real_t p_brake); + real_t get_brake() const; - void set_steering(float p_steering); - float get_steering() const; + void set_steering(real_t p_steering); + real_t get_steering() const; - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; VehicleWheel3D(); }; @@ -153,12 +153,12 @@ public: class VehicleBody3D : public RigidBody3D { GDCLASS(VehicleBody3D, RigidBody3D); - float engine_force; - float brake; + real_t engine_force = 0.0; + real_t brake = 0.0; - real_t m_pitchControl; - real_t m_steeringValue; - real_t m_currentVehicleSpeedKmHour; + real_t m_pitchControl = 0.0; + real_t m_steeringValue = 0.0; + real_t m_currentVehicleSpeedKmHour = 0.0; Set<RID> exclude; @@ -168,12 +168,12 @@ class VehicleBody3D : public RigidBody3D { Vector<real_t> m_sideImpulse; struct btVehicleWheelContactPoint { - PhysicsDirectBodyState3D *m_s; - PhysicsBody3D *m_body1; + PhysicsDirectBodyState3D *m_s = nullptr; + PhysicsBody3D *m_body1 = nullptr; Vector3 m_frictionPositionWorld; Vector3 m_frictionDirectionWorld; - real_t m_jacDiagABInv; - real_t m_maxImpulse; + real_t m_jacDiagABInv = 0.0; + real_t m_maxImpulse = 0.0; btVehicleWheelContactPoint(PhysicsDirectBodyState3D *s, PhysicsBody3D *body1, const Vector3 &frictionPosWorld, const Vector3 &frictionDirectionWorld, real_t maxImpulse); }; @@ -195,14 +195,14 @@ class VehicleBody3D : public RigidBody3D { void _direct_state_changed(Object *p_state) override; public: - void set_engine_force(float p_engine_force); - float get_engine_force() const; + void set_engine_force(real_t p_engine_force); + real_t get_engine_force() const; - void set_brake(float p_brake); - float get_brake() const; + void set_brake(real_t p_brake); + real_t get_brake() const; - void set_steering(float p_steering); - float get_steering() const; + void set_steering(real_t p_steering); + real_t get_steering() const; VehicleBody3D(); }; diff --git a/scene/3d/velocity_tracker_3d.cpp b/scene/3d/velocity_tracker_3d.cpp index db10f3273b..200664a41b 100644 --- a/scene/3d/velocity_tracker_3d.cpp +++ b/scene/3d/velocity_tracker_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -29,7 +29,6 @@ /*************************************************************************/ #include "velocity_tracker_3d.h" -#include "core/engine.h" void VelocityTracker3D::set_track_physics_step(bool p_track_physics_step) { physics_step = p_track_physics_step; @@ -45,7 +44,7 @@ void VelocityTracker3D::update_position(const Vector3 &p_position) { if (physics_step) { ph.frame = Engine::get_singleton()->get_physics_frames(); } else { - ph.frame = Engine::get_singleton()->get_idle_frame_ticks(); + ph.frame = Engine::get_singleton()->get_frame_ticks(); } if (position_history_len == 0 || position_history[0].frame != ph.frame) { //in same frame, use latest @@ -61,29 +60,29 @@ void VelocityTracker3D::update_position(const Vector3 &p_position) { Vector3 VelocityTracker3D::get_tracked_linear_velocity() const { Vector3 linear_velocity; - float max_time = 1 / 5.0; //maximum time to interpolate a velocity + double max_time = 1 / 5.0; //maximum time to interpolate a velocity Vector3 distance_accum; - float time_accum = 0.0; - float base_time = 0.0; + double time_accum = 0.0; + double base_time = 0.0; if (position_history_len) { if (physics_step) { uint64_t base = Engine::get_singleton()->get_physics_frames(); - base_time = float(base - position_history[0].frame) / Engine::get_singleton()->get_iterations_per_second(); + base_time = double(base - position_history[0].frame) / Engine::get_singleton()->get_physics_ticks_per_second(); } else { - uint64_t base = Engine::get_singleton()->get_idle_frame_ticks(); + uint64_t base = Engine::get_singleton()->get_frame_ticks(); base_time = double(base - position_history[0].frame) / 1000000.0; } } for (int i = 0; i < position_history_len - 1; i++) { - float delta = 0.0; + double delta = 0.0; uint64_t diff = position_history[i].frame - position_history[i + 1].frame; Vector3 distance = position_history[i].position - position_history[i + 1].position; if (physics_step) { - delta = float(diff) / Engine::get_singleton()->get_iterations_per_second(); + delta = double(diff) / Engine::get_singleton()->get_physics_ticks_per_second(); } else { delta = double(diff) / 1000000.0; } @@ -109,7 +108,7 @@ void VelocityTracker3D::reset(const Vector3 &p_new_pos) { if (physics_step) { ph.frame = Engine::get_singleton()->get_physics_frames(); } else { - ph.frame = Engine::get_singleton()->get_idle_frame_ticks(); + ph.frame = Engine::get_singleton()->get_frame_ticks(); } position_history.write[0] = ph; @@ -128,6 +127,4 @@ void VelocityTracker3D::_bind_methods() { VelocityTracker3D::VelocityTracker3D() { position_history.resize(4); // should be configurable - position_history_len = 0; - physics_step = false; } diff --git a/scene/3d/velocity_tracker_3d.h b/scene/3d/velocity_tracker_3d.h index a7ab3dce4d..827c3f5bd8 100644 --- a/scene/3d/velocity_tracker_3d.h +++ b/scene/3d/velocity_tracker_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,17 +33,17 @@ #include "scene/3d/node_3d.h" -class VelocityTracker3D : public Reference { - GDCLASS(VelocityTracker3D, Reference); +class VelocityTracker3D : public RefCounted { + GDCLASS(VelocityTracker3D, RefCounted); struct PositionHistory { - uint64_t frame; + uint64_t frame = 0; Vector3 position; }; - bool physics_step; + bool physics_step = false; Vector<PositionHistory> position_history; - int position_history_len; + int position_history_len = 0; protected: static void _bind_methods(); diff --git a/scene/3d/visibility_notifier_3d.cpp b/scene/3d/visibility_notifier_3d.cpp deleted file mode 100644 index a64b0df1cc..0000000000 --- a/scene/3d/visibility_notifier_3d.cpp +++ /dev/null @@ -1,254 +0,0 @@ -/*************************************************************************/ -/* visibility_notifier_3d.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -#include "visibility_notifier_3d.h" - -#include "core/engine.h" -#include "scene/3d/camera_3d.h" -#include "scene/3d/physics_body_3d.h" -#include "scene/animation/animation_player.h" -#include "scene/scene_string_names.h" - -void VisibilityNotifier3D::_enter_camera(Camera3D *p_camera) { - ERR_FAIL_COND(cameras.has(p_camera)); - cameras.insert(p_camera); - if (cameras.size() == 1) { - emit_signal(SceneStringNames::get_singleton()->screen_entered); - _screen_enter(); - } - - emit_signal(SceneStringNames::get_singleton()->camera_entered, p_camera); -} - -void VisibilityNotifier3D::_exit_camera(Camera3D *p_camera) { - ERR_FAIL_COND(!cameras.has(p_camera)); - cameras.erase(p_camera); - - emit_signal(SceneStringNames::get_singleton()->camera_exited, p_camera); - if (cameras.size() == 0) { - emit_signal(SceneStringNames::get_singleton()->screen_exited); - - _screen_exit(); - } -} - -void VisibilityNotifier3D::set_aabb(const AABB &p_aabb) { - if (aabb == p_aabb) { - return; - } - aabb = p_aabb; - - if (is_inside_world()) { - get_world_3d()->_update_notifier(this, get_global_transform().xform(aabb)); - } - - _change_notify("aabb"); - update_gizmo(); -} - -AABB VisibilityNotifier3D::get_aabb() const { - return aabb; -} - -void VisibilityNotifier3D::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_ENTER_WORLD: { - get_world_3d()->_register_notifier(this, get_global_transform().xform(aabb)); - } break; - case NOTIFICATION_TRANSFORM_CHANGED: { - get_world_3d()->_update_notifier(this, get_global_transform().xform(aabb)); - } break; - case NOTIFICATION_EXIT_WORLD: { - get_world_3d()->_remove_notifier(this); - } break; - } -} - -bool VisibilityNotifier3D::is_on_screen() const { - return cameras.size() != 0; -} - -void VisibilityNotifier3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_aabb", "rect"), &VisibilityNotifier3D::set_aabb); - ClassDB::bind_method(D_METHOD("get_aabb"), &VisibilityNotifier3D::get_aabb); - ClassDB::bind_method(D_METHOD("is_on_screen"), &VisibilityNotifier3D::is_on_screen); - - ADD_PROPERTY(PropertyInfo(Variant::AABB, "aabb"), "set_aabb", "get_aabb"); - - ADD_SIGNAL(MethodInfo("camera_entered", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera3D"))); - ADD_SIGNAL(MethodInfo("camera_exited", PropertyInfo(Variant::OBJECT, "camera", PROPERTY_HINT_RESOURCE_TYPE, "Camera3D"))); - ADD_SIGNAL(MethodInfo("screen_entered")); - ADD_SIGNAL(MethodInfo("screen_exited")); -} - -VisibilityNotifier3D::VisibilityNotifier3D() { - aabb = AABB(Vector3(-1, -1, -1), Vector3(2, 2, 2)); - set_notify_transform(true); -} - -////////////////////////////////////// - -void VisibilityEnabler3D::_screen_enter() { - for (Map<Node *, Variant>::Element *E = nodes.front(); E; E = E->next()) { - _change_node_state(E->key(), true); - } - - visible = true; -} - -void VisibilityEnabler3D::_screen_exit() { - for (Map<Node *, Variant>::Element *E = nodes.front(); E; E = E->next()) { - _change_node_state(E->key(), false); - } - - visible = false; -} - -void VisibilityEnabler3D::_find_nodes(Node *p_node) { - bool add = false; - Variant meta; - - { - RigidBody3D *rb = Object::cast_to<RigidBody3D>(p_node); - if (rb && ((rb->get_mode() == RigidBody3D::MODE_CHARACTER || rb->get_mode() == RigidBody3D::MODE_RIGID))) { - add = true; - meta = rb->get_mode(); - } - } - - { - AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node); - if (ap) { - add = true; - } - } - - if (add) { - p_node->connect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &VisibilityEnabler3D::_node_removed), varray(p_node), CONNECT_ONESHOT); - nodes[p_node] = meta; - _change_node_state(p_node, false); - } - - for (int i = 0; i < p_node->get_child_count(); i++) { - Node *c = p_node->get_child(i); - if (c->get_filename() != String()) { - continue; //skip, instance - } - - _find_nodes(c); - } -} - -void VisibilityEnabler3D::_notification(int p_what) { - if (p_what == NOTIFICATION_ENTER_TREE) { - if (Engine::get_singleton()->is_editor_hint()) { - return; - } - - Node *from = this; - //find where current scene starts - while (from->get_parent() && from->get_filename() == String()) { - from = from->get_parent(); - } - - _find_nodes(from); - } - - if (p_what == NOTIFICATION_EXIT_TREE) { - if (Engine::get_singleton()->is_editor_hint()) { - return; - } - - for (Map<Node *, Variant>::Element *E = nodes.front(); E; E = E->next()) { - if (!visible) { - _change_node_state(E->key(), true); - } - E->key()->disconnect(SceneStringNames::get_singleton()->tree_exiting, callable_mp(this, &VisibilityEnabler3D::_node_removed)); - } - - nodes.clear(); - } -} - -void VisibilityEnabler3D::_change_node_state(Node *p_node, bool p_enabled) { - ERR_FAIL_COND(!nodes.has(p_node)); - - if (enabler[ENABLER_FREEZE_BODIES]) { - RigidBody3D *rb = Object::cast_to<RigidBody3D>(p_node); - if (rb) { - rb->set_sleeping(!p_enabled); - } - } - - if (enabler[ENABLER_PAUSE_ANIMATIONS]) { - AnimationPlayer *ap = Object::cast_to<AnimationPlayer>(p_node); - - if (ap) { - ap->set_active(p_enabled); - } - } -} - -void VisibilityEnabler3D::_node_removed(Node *p_node) { - if (!visible) { - _change_node_state(p_node, true); - } - nodes.erase(p_node); -} - -void VisibilityEnabler3D::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_enabler", "enabler", "enabled"), &VisibilityEnabler3D::set_enabler); - ClassDB::bind_method(D_METHOD("is_enabler_enabled", "enabler"), &VisibilityEnabler3D::is_enabler_enabled); - - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "pause_animations"), "set_enabler", "is_enabler_enabled", ENABLER_PAUSE_ANIMATIONS); - ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "freeze_bodies"), "set_enabler", "is_enabler_enabled", ENABLER_FREEZE_BODIES); - - BIND_ENUM_CONSTANT(ENABLER_PAUSE_ANIMATIONS); - BIND_ENUM_CONSTANT(ENABLER_FREEZE_BODIES); - BIND_ENUM_CONSTANT(ENABLER_MAX); -} - -void VisibilityEnabler3D::set_enabler(Enabler p_enabler, bool p_enable) { - ERR_FAIL_INDEX(p_enabler, ENABLER_MAX); - enabler[p_enabler] = p_enable; -} - -bool VisibilityEnabler3D::is_enabler_enabled(Enabler p_enabler) const { - ERR_FAIL_INDEX_V(p_enabler, ENABLER_MAX, false); - return enabler[p_enabler]; -} - -VisibilityEnabler3D::VisibilityEnabler3D() { - for (int i = 0; i < ENABLER_MAX; i++) { - enabler[i] = true; - } - - visible = false; -} diff --git a/scene/3d/visible_on_screen_notifier_3d.cpp b/scene/3d/visible_on_screen_notifier_3d.cpp new file mode 100644 index 0000000000..3d0bc3df9c --- /dev/null +++ b/scene/3d/visible_on_screen_notifier_3d.cpp @@ -0,0 +1,199 @@ +/*************************************************************************/ +/* visible_on_screen_notifier_3d.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/*************************************************************************/ + +#include "visible_on_screen_notifier_3d.h" + +#include "scene/scene_string_names.h" + +void VisibleOnScreenNotifier3D::_visibility_enter() { + if (!is_inside_tree() || Engine::get_singleton()->is_editor_hint()) { + return; + } + + on_screen = true; + emit_signal(SceneStringNames::get_singleton()->screen_entered); + _screen_enter(); +} +void VisibleOnScreenNotifier3D::_visibility_exit() { + if (!is_inside_tree() || Engine::get_singleton()->is_editor_hint()) { + return; + } + + on_screen = false; + emit_signal(SceneStringNames::get_singleton()->screen_exited); + _screen_exit(); +} + +void VisibleOnScreenNotifier3D::set_aabb(const AABB &p_aabb) { + if (aabb == p_aabb) { + return; + } + aabb = p_aabb; + + RS::get_singleton()->visibility_notifier_set_aabb(get_base(), aabb); + + update_gizmos(); +} + +AABB VisibleOnScreenNotifier3D::get_aabb() const { + return aabb; +} + +bool VisibleOnScreenNotifier3D::is_on_screen() const { + return on_screen; +} + +void VisibleOnScreenNotifier3D::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE || p_what == NOTIFICATION_EXIT_TREE) { + on_screen = false; + } +} + +void VisibleOnScreenNotifier3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_aabb", "rect"), &VisibleOnScreenNotifier3D::set_aabb); + ClassDB::bind_method(D_METHOD("is_on_screen"), &VisibleOnScreenNotifier3D::is_on_screen); + + ADD_PROPERTY(PropertyInfo(Variant::AABB, "aabb"), "set_aabb", "get_aabb"); + + ADD_SIGNAL(MethodInfo("screen_entered")); + ADD_SIGNAL(MethodInfo("screen_exited")); +} + +Vector<Face3> VisibleOnScreenNotifier3D::get_faces(uint32_t p_usage_flags) const { + return Vector<Face3>(); +} + +VisibleOnScreenNotifier3D::VisibleOnScreenNotifier3D() { + RID notifier = RS::get_singleton()->visibility_notifier_create(); + RS::get_singleton()->visibility_notifier_set_aabb(notifier, aabb); + RS::get_singleton()->visibility_notifier_set_callbacks(notifier, callable_mp(this, &VisibleOnScreenNotifier3D::_visibility_enter), callable_mp(this, &VisibleOnScreenNotifier3D::_visibility_exit)); + set_base(notifier); +} +VisibleOnScreenNotifier3D::~VisibleOnScreenNotifier3D() { + RID base = get_base(); + set_base(RID()); + RS::get_singleton()->free(base); +} + +////////////////////////////////////// + +void VisibleOnScreenEnabler3D::_screen_enter() { + _update_enable_mode(true); +} + +void VisibleOnScreenEnabler3D::_screen_exit() { + _update_enable_mode(false); +} + +void VisibleOnScreenEnabler3D::set_enable_mode(EnableMode p_mode) { + enable_mode = p_mode; + if (is_inside_tree()) { + _update_enable_mode(is_on_screen()); + } +} +VisibleOnScreenEnabler3D::EnableMode VisibleOnScreenEnabler3D::get_enable_mode() { + return enable_mode; +} + +void VisibleOnScreenEnabler3D::set_enable_node_path(NodePath p_path) { + if (enable_node_path == p_path) { + return; + } + enable_node_path = p_path; + if (is_inside_tree()) { + node_id = ObjectID(); + Node *node = get_node(enable_node_path); + if (node) { + node_id = node->get_instance_id(); + _update_enable_mode(is_on_screen()); + } + } +} +NodePath VisibleOnScreenEnabler3D::get_enable_node_path() { + return enable_node_path; +} + +void VisibleOnScreenEnabler3D::_update_enable_mode(bool p_enable) { + Node *node = static_cast<Node *>(ObjectDB::get_instance(node_id)); + if (node) { + if (p_enable) { + switch (enable_mode) { + case ENABLE_MODE_INHERIT: { + node->set_process_mode(PROCESS_MODE_INHERIT); + } break; + case ENABLE_MODE_ALWAYS: { + node->set_process_mode(PROCESS_MODE_ALWAYS); + } break; + case ENABLE_MODE_WHEN_PAUSED: { + node->set_process_mode(PROCESS_MODE_WHEN_PAUSED); + } break; + } + } else { + node->set_process_mode(PROCESS_MODE_DISABLED); + } + } +} +void VisibleOnScreenEnabler3D::_notification(int p_what) { + if (p_what == NOTIFICATION_ENTER_TREE) { + if (Engine::get_singleton()->is_editor_hint()) { + return; + } + + node_id = ObjectID(); + Node *node = get_node(enable_node_path); + if (node) { + node_id = node->get_instance_id(); + node->set_process_mode(PROCESS_MODE_DISABLED); + } + } + + if (p_what == NOTIFICATION_EXIT_TREE) { + node_id = ObjectID(); + } +} + +void VisibleOnScreenEnabler3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_enable_mode", "mode"), &VisibleOnScreenEnabler3D::set_enable_mode); + ClassDB::bind_method(D_METHOD("get_enable_mode"), &VisibleOnScreenEnabler3D::get_enable_mode); + + ClassDB::bind_method(D_METHOD("set_enable_node_path", "path"), &VisibleOnScreenEnabler3D::set_enable_node_path); + ClassDB::bind_method(D_METHOD("get_enable_node_path"), &VisibleOnScreenEnabler3D::get_enable_node_path); + + ADD_GROUP("Enabling", "enable_"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "enable_mode", PROPERTY_HINT_ENUM, "Inherit,Always,WhenPaused"), "set_enable_mode", "get_enable_mode"); + ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "enable_node_path"), "set_enable_node_path", "get_enable_node_path"); + + BIND_ENUM_CONSTANT(ENABLE_MODE_INHERIT); + BIND_ENUM_CONSTANT(ENABLE_MODE_ALWAYS); + BIND_ENUM_CONSTANT(ENABLE_MODE_WHEN_PAUSED); +} + +VisibleOnScreenEnabler3D::VisibleOnScreenEnabler3D() { +} diff --git a/scene/3d/visibility_notifier_3d.h b/scene/3d/visible_on_screen_notifier_3d.h index 35f6c02e83..fb7137c4f0 100644 --- a/scene/3d/visibility_notifier_3d.h +++ b/scene/3d/visible_on_screen_notifier_3d.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* visibility_notifier_3d.h */ +/* visible_on_screen_notifier_3d.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,18 +28,22 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef VISIBILITY_NOTIFIER_H -#define VISIBILITY_NOTIFIER_H +#ifndef VISIBLE_ON_SCREEN_NOTIFIER_3D_H +#define VISIBLE_ON_SCREEN_NOTIFIER_3D_H -#include "scene/3d/node_3d.h" +#include "scene/3d/visual_instance_3d.h" +class World3D; class Camera3D; -class VisibilityNotifier3D : public Node3D { - GDCLASS(VisibilityNotifier3D, Node3D); +class VisibleOnScreenNotifier3D : public VisualInstance3D { + GDCLASS(VisibleOnScreenNotifier3D, VisualInstance3D); - Set<Camera3D *> cameras; + AABB aabb = AABB(Vector3(-1, -1, -1), Vector3(2, 2, 2)); - AABB aabb; +private: + bool on_screen = false; + void _visibility_enter(); + void _visibility_exit(); protected: virtual void _screen_enter() {} @@ -47,53 +51,51 @@ protected: void _notification(int p_what); static void _bind_methods(); - friend struct SpatialIndexer; - - void _enter_camera(Camera3D *p_camera); - void _exit_camera(Camera3D *p_camera); public: void set_aabb(const AABB &p_aabb); - AABB get_aabb() const; + virtual AABB get_aabb() const override; bool is_on_screen() const; - VisibilityNotifier3D(); + virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override; + + VisibleOnScreenNotifier3D(); + ~VisibleOnScreenNotifier3D(); }; -class VisibilityEnabler3D : public VisibilityNotifier3D { - GDCLASS(VisibilityEnabler3D, VisibilityNotifier3D); +class VisibleOnScreenEnabler3D : public VisibleOnScreenNotifier3D { + GDCLASS(VisibleOnScreenEnabler3D, VisibleOnScreenNotifier3D); public: - enum Enabler { - ENABLER_PAUSE_ANIMATIONS, - ENABLER_FREEZE_BODIES, - ENABLER_MAX + enum EnableMode { + ENABLE_MODE_INHERIT, + ENABLE_MODE_ALWAYS, + ENABLE_MODE_WHEN_PAUSED, }; protected: + ObjectID node_id; virtual void _screen_enter() override; virtual void _screen_exit() override; - bool visible; - - void _find_nodes(Node *p_node); - - Map<Node *, Variant> nodes; - void _node_removed(Node *p_node); - bool enabler[ENABLER_MAX]; - - void _change_node_state(Node *p_node, bool p_enabled); + EnableMode enable_mode = ENABLE_MODE_INHERIT; + NodePath enable_node_path = NodePath(".."); void _notification(int p_what); static void _bind_methods(); + void _update_enable_mode(bool p_enable); + public: - void set_enabler(Enabler p_enabler, bool p_enable); - bool is_enabler_enabled(Enabler p_enabler) const; + void set_enable_mode(EnableMode p_mode); + EnableMode get_enable_mode(); + + void set_enable_node_path(NodePath p_path); + NodePath get_enable_node_path(); - VisibilityEnabler3D(); + VisibleOnScreenEnabler3D(); }; -VARIANT_ENUM_CAST(VisibilityEnabler3D::Enabler); +VARIANT_ENUM_CAST(VisibleOnScreenEnabler3D::EnableMode); #endif // VISIBILITY_NOTIFIER_H diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index a1c498e8ab..73c2887983 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,8 +31,6 @@ #include "visual_instance_3d.h" #include "scene/scene_string_names.h" -#include "servers/rendering_server.h" -#include "skeleton_3d.h" AABB VisualInstance3D::get_transformed_aabb() const { return get_global_transform().xform(get_aabb()); @@ -43,7 +41,6 @@ void VisualInstance3D::_update_visibility() { return; } - _change_notify("visible"); RS::get_singleton()->instance_set_visible(get_instance(), is_visible_in_tree()); } @@ -62,7 +59,7 @@ void VisualInstance3D::_notification(int p_what) { } break; case NOTIFICATION_TRANSFORM_CHANGED: { - Transform gt = get_global_transform(); + Transform3D gt = get_global_transform(); RenderingServer::get_singleton()->instance_set_transform(instance, gt); } break; case NOTIFICATION_EXIT_WORLD: { @@ -94,18 +91,22 @@ uint32_t VisualInstance3D::get_layer_mask() const { return layers; } -void VisualInstance3D::set_layer_mask_bit(int p_layer, bool p_enable) { - ERR_FAIL_INDEX(p_layer, 32); - if (p_enable) { - set_layer_mask(layers | (1 << p_layer)); +void VisualInstance3D::set_layer_mask_value(int p_layer_number, bool p_value) { + ERR_FAIL_COND_MSG(p_layer_number < 1, "Render layer number must be between 1 and 20 inclusive."); + ERR_FAIL_COND_MSG(p_layer_number > 20, "Render layer number must be between 1 and 20 inclusive."); + uint32_t mask = get_layer_mask(); + if (p_value) { + mask |= 1 << (p_layer_number - 1); } else { - set_layer_mask(layers & (~(1 << p_layer))); + mask &= ~(1 << (p_layer_number - 1)); } + set_layer_mask(mask); } -bool VisualInstance3D::get_layer_mask_bit(int p_layer) const { - ERR_FAIL_INDEX_V(p_layer, 32, false); - return (layers & (1 << p_layer)); +bool VisualInstance3D::get_layer_mask_value(int p_layer_number) const { + ERR_FAIL_COND_V_MSG(p_layer_number < 1, false, "Render layer number must be between 1 and 20 inclusive."); + ERR_FAIL_COND_V_MSG(p_layer_number > 20, false, "Render layer number must be between 1 and 20 inclusive."); + return layers & (1 << (p_layer_number - 1)); } void VisualInstance3D::_bind_methods() { @@ -115,8 +116,8 @@ void VisualInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_instance"), &VisualInstance3D::get_instance); ClassDB::bind_method(D_METHOD("set_layer_mask", "mask"), &VisualInstance3D::set_layer_mask); ClassDB::bind_method(D_METHOD("get_layer_mask"), &VisualInstance3D::get_layer_mask); - ClassDB::bind_method(D_METHOD("set_layer_mask_bit", "layer", "enabled"), &VisualInstance3D::set_layer_mask_bit); - ClassDB::bind_method(D_METHOD("get_layer_mask_bit", "layer"), &VisualInstance3D::get_layer_mask_bit); + ClassDB::bind_method(D_METHOD("set_layer_mask_value", "layer_number", "value"), &VisualInstance3D::set_layer_mask_value); + ClassDB::bind_method(D_METHOD("get_layer_mask_value", "layer_number"), &VisualInstance3D::get_layer_mask_value); ClassDB::bind_method(D_METHOD("get_transformed_aabb"), &VisualInstance3D::get_transformed_aabb); @@ -135,7 +136,6 @@ RID VisualInstance3D::get_base() const { VisualInstance3D::VisualInstance3D() { instance = RenderingServer::get_singleton()->instance_create(); RenderingServer::get_singleton()->instance_attach_object_instance_id(instance, get_instance_id()); - layers = 1; set_notify_transform(true); } @@ -152,40 +152,42 @@ Ref<Material> GeometryInstance3D::get_material_override() const { return material_override; } -void GeometryInstance3D::set_lod_min_distance(float p_dist) { - lod_min_distance = p_dist; - RS::get_singleton()->instance_geometry_set_draw_range(get_instance(), lod_min_distance, lod_max_distance, lod_min_hysteresis, lod_max_hysteresis); +void GeometryInstance3D::set_visibility_range_begin(float p_dist) { + visibility_range_begin = p_dist; + RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin); + update_configuration_warnings(); } -float GeometryInstance3D::get_lod_min_distance() const { - return lod_min_distance; +float GeometryInstance3D::get_visibility_range_begin() const { + return visibility_range_begin; } -void GeometryInstance3D::set_lod_max_distance(float p_dist) { - lod_max_distance = p_dist; - RS::get_singleton()->instance_geometry_set_draw_range(get_instance(), lod_min_distance, lod_max_distance, lod_min_hysteresis, lod_max_hysteresis); +void GeometryInstance3D::set_visibility_range_end(float p_dist) { + visibility_range_end = p_dist; + RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin); + update_configuration_warnings(); } -float GeometryInstance3D::get_lod_max_distance() const { - return lod_max_distance; +float GeometryInstance3D::get_visibility_range_end() const { + return visibility_range_end; } -void GeometryInstance3D::set_lod_min_hysteresis(float p_dist) { - lod_min_hysteresis = p_dist; - RS::get_singleton()->instance_geometry_set_draw_range(get_instance(), lod_min_distance, lod_max_distance, lod_min_hysteresis, lod_max_hysteresis); +void GeometryInstance3D::set_visibility_range_begin_margin(float p_dist) { + visibility_range_begin_margin = p_dist; + RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin); } -float GeometryInstance3D::get_lod_min_hysteresis() const { - return lod_min_hysteresis; +float GeometryInstance3D::get_visibility_range_begin_margin() const { + return visibility_range_begin_margin; } -void GeometryInstance3D::set_lod_max_hysteresis(float p_dist) { - lod_max_hysteresis = p_dist; - RS::get_singleton()->instance_geometry_set_draw_range(get_instance(), lod_min_distance, lod_max_distance, lod_min_hysteresis, lod_max_hysteresis); +void GeometryInstance3D::set_visibility_range_end_margin(float p_dist) { + visibility_range_end_margin = p_dist; + RS::get_singleton()->instance_geometry_set_visibility_range(get_instance(), visibility_range_begin, visibility_range_end, visibility_range_begin_margin, visibility_range_end_margin); } -float GeometryInstance3D::get_lod_max_hysteresis() const { - return lod_max_hysteresis; +float GeometryInstance3D::get_visibility_range_end_margin() const { + return visibility_range_end_margin; } void GeometryInstance3D::_notification(int p_what) { @@ -240,17 +242,16 @@ bool GeometryInstance3D::_get(const StringName &p_name, Variant &r_ret) const { void GeometryInstance3D::_get_property_list(List<PropertyInfo> *p_list) const { List<PropertyInfo> pinfo; RS::get_singleton()->instance_geometry_get_shader_parameter_list(get_instance(), &pinfo); - for (List<PropertyInfo>::Element *E = pinfo.front(); E; E = E->next()) { - PropertyInfo pi = E->get(); + for (PropertyInfo &pi : pinfo) { bool has_def_value = false; Variant def_value = RS::get_singleton()->instance_geometry_get_shader_parameter_default_value(get_instance(), pi.name); if (def_value.get_type() != Variant::NIL) { has_def_value = true; } if (instance_uniforms.has(pi.name)) { - pi.usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE | (has_def_value ? (PROPERTY_USAGE_CHECKABLE | PROPERTY_USAGE_CHECKED) : 0); + pi.usage = PROPERTY_USAGE_EDITOR | PROPERTY_USAGE_STORAGE | (has_def_value ? (PROPERTY_USAGE_CHECKABLE | PROPERTY_USAGE_CHECKED) : PROPERTY_USAGE_NONE); } else { - pi.usage = PROPERTY_USAGE_EDITOR | (has_def_value ? PROPERTY_USAGE_CHECKABLE : 0); //do not save if not changed + pi.usage = PROPERTY_USAGE_EDITOR | (has_def_value ? PROPERTY_USAGE_CHECKABLE : PROPERTY_USAGE_NONE); //do not save if not changed } pi.name = "shader_params/" + pi.name; @@ -278,6 +279,16 @@ float GeometryInstance3D::get_extra_cull_margin() const { return extra_cull_margin; } +void GeometryInstance3D::set_lod_bias(float p_bias) { + ERR_FAIL_COND(p_bias < 0.0); + lod_bias = p_bias; + RS::get_singleton()->instance_geometry_set_lod_bias(get_instance(), lod_bias); +} + +float GeometryInstance3D::get_lod_bias() const { + return lod_bias; +} + void GeometryInstance3D::set_shader_instance_uniform(const StringName &p_uniform, const Variant &p_value) { if (p_value.get_type() == Variant::NIL) { Variant def_value = RS::get_singleton()->instance_geometry_get_shader_parameter_default_value(get_instance(), p_uniform); @@ -285,7 +296,12 @@ void GeometryInstance3D::set_shader_instance_uniform(const StringName &p_uniform instance_uniforms.erase(p_value); } else { instance_uniforms[p_uniform] = p_value; - RS::get_singleton()->instance_geometry_set_shader_parameter(get_instance(), p_uniform, p_value); + if (p_value.get_type() == Variant::OBJECT) { + RID tex_id = p_value; + RS::get_singleton()->instance_geometry_set_shader_parameter(get_instance(), p_uniform, tex_id); + } else { + RS::get_singleton()->instance_geometry_set_shader_parameter(get_instance(), p_uniform, p_value); + } } } @@ -330,6 +346,25 @@ GeometryInstance3D::GIMode GeometryInstance3D::get_gi_mode() const { return gi_mode; } +void GeometryInstance3D::set_ignore_occlusion_culling(bool p_enabled) { + ignore_occlusion_culling = p_enabled; + RS::get_singleton()->instance_geometry_set_flag(get_instance(), RS::INSTANCE_FLAG_IGNORE_OCCLUSION_CULLING, ignore_occlusion_culling); +} + +bool GeometryInstance3D::is_ignoring_occlusion_culling() { + return ignore_occlusion_culling; +} + +TypedArray<String> GeometryInstance3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); + + if (!Math::is_zero_approx(visibility_range_end) && visibility_range_end <= visibility_range_begin) { + warnings.push_back(TTR("The GeometryInstance3D visibility range's End distance is set to a non-zero value, but is lower than the Begin distance.\nThis means the GeometryInstance3D will never be visible.\nTo resolve this, set the End distance to 0 or to a value greater than the Begin distance.")); + } + + return warnings; +} + void GeometryInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_material_override", "material"), &GeometryInstance3D::set_material_override); ClassDB::bind_method(D_METHOD("get_material_override"), &GeometryInstance3D::get_material_override); @@ -337,20 +372,23 @@ void GeometryInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_cast_shadows_setting", "shadow_casting_setting"), &GeometryInstance3D::set_cast_shadows_setting); ClassDB::bind_method(D_METHOD("get_cast_shadows_setting"), &GeometryInstance3D::get_cast_shadows_setting); - ClassDB::bind_method(D_METHOD("set_lod_max_hysteresis", "mode"), &GeometryInstance3D::set_lod_max_hysteresis); - ClassDB::bind_method(D_METHOD("get_lod_max_hysteresis"), &GeometryInstance3D::get_lod_max_hysteresis); + ClassDB::bind_method(D_METHOD("set_lod_bias", "bias"), &GeometryInstance3D::set_lod_bias); + ClassDB::bind_method(D_METHOD("get_lod_bias"), &GeometryInstance3D::get_lod_bias); - ClassDB::bind_method(D_METHOD("set_lod_max_distance", "mode"), &GeometryInstance3D::set_lod_max_distance); - ClassDB::bind_method(D_METHOD("get_lod_max_distance"), &GeometryInstance3D::get_lod_max_distance); + ClassDB::bind_method(D_METHOD("set_visibility_range_end_margin", "distance"), &GeometryInstance3D::set_visibility_range_end_margin); + ClassDB::bind_method(D_METHOD("get_visibility_range_end_margin"), &GeometryInstance3D::get_visibility_range_end_margin); - ClassDB::bind_method(D_METHOD("set_shader_instance_uniform", "uniform", "value"), &GeometryInstance3D::set_shader_instance_uniform); - ClassDB::bind_method(D_METHOD("get_shader_instance_uniform", "uniform"), &GeometryInstance3D::get_shader_instance_uniform); + ClassDB::bind_method(D_METHOD("set_visibility_range_end", "distance"), &GeometryInstance3D::set_visibility_range_end); + ClassDB::bind_method(D_METHOD("get_visibility_range_end"), &GeometryInstance3D::get_visibility_range_end); - ClassDB::bind_method(D_METHOD("set_lod_min_hysteresis", "mode"), &GeometryInstance3D::set_lod_min_hysteresis); - ClassDB::bind_method(D_METHOD("get_lod_min_hysteresis"), &GeometryInstance3D::get_lod_min_hysteresis); + ClassDB::bind_method(D_METHOD("set_visibility_range_begin_margin", "distance"), &GeometryInstance3D::set_visibility_range_begin_margin); + ClassDB::bind_method(D_METHOD("get_visibility_range_begin_margin"), &GeometryInstance3D::get_visibility_range_begin_margin); - ClassDB::bind_method(D_METHOD("set_lod_min_distance", "mode"), &GeometryInstance3D::set_lod_min_distance); - ClassDB::bind_method(D_METHOD("get_lod_min_distance"), &GeometryInstance3D::get_lod_min_distance); + ClassDB::bind_method(D_METHOD("set_visibility_range_begin", "distance"), &GeometryInstance3D::set_visibility_range_begin); + ClassDB::bind_method(D_METHOD("get_visibility_range_begin"), &GeometryInstance3D::get_visibility_range_begin); + + ClassDB::bind_method(D_METHOD("set_shader_instance_uniform", "uniform", "value"), &GeometryInstance3D::set_shader_instance_uniform); + ClassDB::bind_method(D_METHOD("get_shader_instance_uniform", "uniform"), &GeometryInstance3D::get_shader_instance_uniform); ClassDB::bind_method(D_METHOD("set_extra_cull_margin", "margin"), &GeometryInstance3D::set_extra_cull_margin); ClassDB::bind_method(D_METHOD("get_extra_cull_margin"), &GeometryInstance3D::get_extra_cull_margin); @@ -361,23 +399,28 @@ void GeometryInstance3D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_gi_mode", "mode"), &GeometryInstance3D::set_gi_mode); ClassDB::bind_method(D_METHOD("get_gi_mode"), &GeometryInstance3D::get_gi_mode); + ClassDB::bind_method(D_METHOD("set_ignore_occlusion_culling", "ignore_culling"), &GeometryInstance3D::set_ignore_occlusion_culling); + ClassDB::bind_method(D_METHOD("is_ignoring_occlusion_culling"), &GeometryInstance3D::is_ignoring_occlusion_culling); + ClassDB::bind_method(D_METHOD("set_custom_aabb", "aabb"), &GeometryInstance3D::set_custom_aabb); ClassDB::bind_method(D_METHOD("get_aabb"), &GeometryInstance3D::get_aabb); ADD_GROUP("Geometry", ""); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material_override", PROPERTY_HINT_RESOURCE_TYPE, "ShaderMaterial,StandardMaterial3D", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE), "set_material_override", "get_material_override"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "material_override", PROPERTY_HINT_RESOURCE_TYPE, "BaseMaterial3D,ShaderMaterial", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DEFERRED_SET_RESOURCE), "set_material_override", "get_material_override"); ADD_PROPERTY(PropertyInfo(Variant::INT, "cast_shadow", PROPERTY_HINT_ENUM, "Off,On,Double-Sided,Shadows Only"), "set_cast_shadows_setting", "get_cast_shadows_setting"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "extra_cull_margin", PROPERTY_HINT_RANGE, "0,16384,0.01"), "set_extra_cull_margin", "get_extra_cull_margin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "lod_bias", PROPERTY_HINT_RANGE, "0.001,128,0.001"), "set_lod_bias", "get_lod_bias"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ignore_occlusion_culling"), "set_ignore_occlusion_culling", "is_ignoring_occlusion_culling"); ADD_GROUP("Global Illumination", "gi_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_mode", PROPERTY_HINT_ENUM, "Disabled,Baked,Dynamic"), "set_gi_mode", "get_gi_mode"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_lightmap_scale", PROPERTY_HINT_ENUM, "1x,2x,4x,8x"), "set_lightmap_scale", "get_lightmap_scale"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "gi_lightmap_scale", PROPERTY_HINT_ENUM, String::utf8("1×,2×,4×,8×")), "set_lightmap_scale", "get_lightmap_scale"); - ADD_GROUP("LOD", "lod_"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_min_distance", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_lod_min_distance", "get_lod_min_distance"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_min_hysteresis", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_lod_min_hysteresis", "get_lod_min_hysteresis"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_max_distance", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_lod_max_distance", "get_lod_max_distance"); - ADD_PROPERTY(PropertyInfo(Variant::INT, "lod_max_hysteresis", PROPERTY_HINT_RANGE, "0,32768,0.01"), "set_lod_max_hysteresis", "get_lod_max_hysteresis"); + ADD_GROUP("Visibility Range", "visibility_range_"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01"), "set_visibility_range_begin", "get_visibility_range_begin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_begin_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01"), "set_visibility_range_begin_margin", "get_visibility_range_begin_margin"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_end", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01"), "set_visibility_range_end", "get_visibility_range_end"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "visibility_range_end_margin", PROPERTY_HINT_RANGE, "0.0,4096.0,0.01"), "set_visibility_range_end_margin", "get_visibility_range_end_margin"); //ADD_SIGNAL( MethodInfo("visibility_changed")); @@ -398,15 +441,5 @@ void GeometryInstance3D::_bind_methods() { } GeometryInstance3D::GeometryInstance3D() { - lod_min_distance = 0; - lod_max_distance = 0; - lod_min_hysteresis = 0; - lod_max_hysteresis = 0; - - gi_mode = GI_MODE_DISABLED; - lightmap_scale = LIGHTMAP_SCALE_1X; - - shadow_casting_setting = SHADOW_CASTING_SETTING_ON; - extra_cull_margin = 0; //RS::get_singleton()->instance_geometry_set_baked_light_texture_index(get_instance(),0); } diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h index 195674f62d..8d24e13d47 100644 --- a/scene/3d/visual_instance_3d.h +++ b/scene/3d/visual_instance_3d.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,10 +31,7 @@ #ifndef VISUAL_INSTANCE_H #define VISUAL_INSTANCE_H -#include "core/math/face3.h" -#include "core/rid.h" #include "scene/3d/node_3d.h" -#include "scene/resources/material.h" class VisualInstance3D : public Node3D { GDCLASS(VisualInstance3D, Node3D); @@ -42,7 +39,7 @@ class VisualInstance3D : public Node3D { RID base; RID instance; - uint32_t layers; + uint32_t layers = 1; RID _get_visual_instance_rid() const; @@ -72,8 +69,8 @@ public: void set_layer_mask(uint32_t p_mask); uint32_t get_layer_mask() const; - void set_layer_mask_bit(int p_layer, bool p_enable); - bool get_layer_mask_bit(int p_layer) const; + void set_layer_mask_value(int p_layer_number, bool p_enable); + bool get_layer_mask_value(int p_layer_number) const; VisualInstance3D(); ~VisualInstance3D(); @@ -105,19 +102,25 @@ public: }; private: - ShadowCastingSetting shadow_casting_setting; + ShadowCastingSetting shadow_casting_setting = SHADOW_CASTING_SETTING_ON; Ref<Material> material_override; - float lod_min_distance; - float lod_max_distance; - float lod_min_hysteresis; - float lod_max_hysteresis; + + float visibility_range_begin = 0.0; + float visibility_range_end = 0.0; + float visibility_range_begin_margin = 0.0; + float visibility_range_end_margin = 0.0; + + Vector<NodePath> visibility_range_children; + + float lod_bias = 1.0; mutable HashMap<StringName, Variant> instance_uniforms; mutable HashMap<StringName, StringName> instance_uniform_property_remap; - float extra_cull_margin; - LightmapScale lightmap_scale; - GIMode gi_mode; + float extra_cull_margin = 0.0; + LightmapScale lightmap_scale = LIGHTMAP_SCALE_1X; + GIMode gi_mode = GI_MODE_DISABLED; + bool ignore_occlusion_culling = false; const StringName *_instance_uniform_get_remap(const StringName p_name) const; @@ -133,17 +136,20 @@ public: void set_cast_shadows_setting(ShadowCastingSetting p_shadow_casting_setting); ShadowCastingSetting get_cast_shadows_setting() const; - void set_lod_min_distance(float p_dist); - float get_lod_min_distance() const; + void set_visibility_range_begin(float p_dist); + float get_visibility_range_begin() const; - void set_lod_max_distance(float p_dist); - float get_lod_max_distance() const; + void set_visibility_range_end(float p_dist); + float get_visibility_range_end() const; - void set_lod_min_hysteresis(float p_dist); - float get_lod_min_hysteresis() const; + void set_visibility_range_begin_margin(float p_dist); + float get_visibility_range_begin_margin() const; - void set_lod_max_hysteresis(float p_dist); - float get_lod_max_hysteresis() const; + void set_visibility_range_end_margin(float p_dist); + float get_visibility_range_end_margin() const; + + void set_visibility_range_parent(const Node *p_parent); + void clear_visibility_range_parent(); void set_material_override(const Ref<Material> &p_material); Ref<Material> get_material_override() const; @@ -151,6 +157,9 @@ public: void set_extra_cull_margin(float p_margin); float get_extra_cull_margin() const; + void set_lod_bias(float p_bias); + float get_lod_bias() const; + void set_gi_mode(GIMode p_mode); GIMode get_gi_mode() const; @@ -162,6 +171,10 @@ public: void set_custom_aabb(AABB aabb); + void set_ignore_occlusion_culling(bool p_enabled); + bool is_ignoring_occlusion_culling(); + + TypedArray<String> get_configuration_warnings() const override; GeometryInstance3D(); }; diff --git a/scene/3d/gi_probe.cpp b/scene/3d/voxel_gi.cpp index ab30d2fec5..d3d12d94e9 100644 --- a/scene/3d/gi_probe.cpp +++ b/scene/3d/voxel_gi.cpp @@ -1,12 +1,12 @@ /*************************************************************************/ -/* gi_probe.cpp */ +/* voxel_gi.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,15 +28,13 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "gi_probe.h" +#include "voxel_gi.h" -#include "core/os/os.h" - -#include "core/method_bind_ext.gen.inc" #include "mesh_instance_3d.h" +#include "multimesh_instance_3d.h" #include "voxelizer.h" -void GIProbeData::_set_data(const Dictionary &p_data) { +void VoxelGIData::_set_data(const Dictionary &p_data) { ERR_FAIL_COND(!p_data.has("bounds")); ERR_FAIL_COND(!p_data.has("octree_size")); ERR_FAIL_COND(!p_data.has("octree_cells")); @@ -56,19 +54,19 @@ void GIProbeData::_set_data(const Dictionary &p_data) { } else if (p_data.has("octree_df_png")) { Vector<uint8_t> octree_df_png = p_data["octree_df_png"]; Ref<Image> img; - img.instance(); + img.instantiate(); Error err = img->load_png_from_buffer(octree_df_png); ERR_FAIL_COND(err != OK); ERR_FAIL_COND(img->get_format() != Image::FORMAT_L8); octree_df = img->get_data(); } Vector<int> octree_levels = p_data["level_counts"]; - Transform to_cell_xform = p_data["to_cell_xform"]; + Transform3D to_cell_xform = p_data["to_cell_xform"]; allocate(to_cell_xform, bounds, octree_size, octree_cells, octree_data, octree_df, octree_levels); } -Dictionary GIProbeData::_get_data() const { +Dictionary VoxelGIData::_get_data() const { Dictionary d; d["bounds"] = get_bounds(); Vector3i otsize = get_octree_size(); @@ -77,7 +75,7 @@ Dictionary GIProbeData::_get_data() const { d["octree_data"] = get_data_cells(); if (otsize != Vector3i()) { Ref<Image> img; - img.instance(); + img.instantiate(); img->create(otsize.x * otsize.y, otsize.z, false, Image::FORMAT_L8, get_distance_field()); Vector<uint8_t> df_png = img->save_png_to_buffer(); ERR_FAIL_COND_V(df_png.size() == 0, Dictionary()); @@ -91,186 +89,141 @@ Dictionary GIProbeData::_get_data() const { return d; } -void GIProbeData::allocate(const Transform &p_to_cell_xform, const AABB &p_aabb, const Vector3 &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) { - RS::get_singleton()->gi_probe_allocate(probe, p_to_cell_xform, p_aabb, p_octree_size, p_octree_cells, p_data_cells, p_distance_field, p_level_counts); +void VoxelGIData::allocate(const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3 &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts) { + RS::get_singleton()->voxel_gi_allocate_data(probe, p_to_cell_xform, p_aabb, p_octree_size, p_octree_cells, p_data_cells, p_distance_field, p_level_counts); bounds = p_aabb; to_cell_xform = p_to_cell_xform; octree_size = p_octree_size; } -AABB GIProbeData::get_bounds() const { +AABB VoxelGIData::get_bounds() const { return bounds; } -Vector3 GIProbeData::get_octree_size() const { +Vector3 VoxelGIData::get_octree_size() const { return octree_size; } -Vector<uint8_t> GIProbeData::get_octree_cells() const { - return RS::get_singleton()->gi_probe_get_octree_cells(probe); +Vector<uint8_t> VoxelGIData::get_octree_cells() const { + return RS::get_singleton()->voxel_gi_get_octree_cells(probe); } -Vector<uint8_t> GIProbeData::get_data_cells() const { - return RS::get_singleton()->gi_probe_get_data_cells(probe); +Vector<uint8_t> VoxelGIData::get_data_cells() const { + return RS::get_singleton()->voxel_gi_get_data_cells(probe); } -Vector<uint8_t> GIProbeData::get_distance_field() const { - return RS::get_singleton()->gi_probe_get_distance_field(probe); +Vector<uint8_t> VoxelGIData::get_distance_field() const { + return RS::get_singleton()->voxel_gi_get_distance_field(probe); } -Vector<int> GIProbeData::get_level_counts() const { - return RS::get_singleton()->gi_probe_get_level_counts(probe); +Vector<int> VoxelGIData::get_level_counts() const { + return RS::get_singleton()->voxel_gi_get_level_counts(probe); } -Transform GIProbeData::get_to_cell_xform() const { +Transform3D VoxelGIData::get_to_cell_xform() const { return to_cell_xform; } -void GIProbeData::set_dynamic_range(float p_range) { - RS::get_singleton()->gi_probe_set_dynamic_range(probe, p_range); +void VoxelGIData::set_dynamic_range(float p_range) { + RS::get_singleton()->voxel_gi_set_dynamic_range(probe, p_range); dynamic_range = p_range; } -float GIProbeData::get_dynamic_range() const { +float VoxelGIData::get_dynamic_range() const { return dynamic_range; } -void GIProbeData::set_propagation(float p_propagation) { - RS::get_singleton()->gi_probe_set_propagation(probe, p_propagation); +void VoxelGIData::set_propagation(float p_propagation) { + RS::get_singleton()->voxel_gi_set_propagation(probe, p_propagation); propagation = p_propagation; } -float GIProbeData::get_propagation() const { +float VoxelGIData::get_propagation() const { return propagation; } -void GIProbeData::set_anisotropy_strength(float p_anisotropy_strength) { - RS::get_singleton()->gi_probe_set_anisotropy_strength(probe, p_anisotropy_strength); - anisotropy_strength = p_anisotropy_strength; -} - -float GIProbeData::get_anisotropy_strength() const { - return anisotropy_strength; -} - -void GIProbeData::set_energy(float p_energy) { - RS::get_singleton()->gi_probe_set_energy(probe, p_energy); +void VoxelGIData::set_energy(float p_energy) { + RS::get_singleton()->voxel_gi_set_energy(probe, p_energy); energy = p_energy; } -float GIProbeData::get_energy() const { +float VoxelGIData::get_energy() const { return energy; } -void GIProbeData::set_ao(float p_ao) { - RS::get_singleton()->gi_probe_set_ao(probe, p_ao); - ao = p_ao; -} - -float GIProbeData::get_ao() const { - return ao; -} - -void GIProbeData::set_ao_size(float p_ao_size) { - RS::get_singleton()->gi_probe_set_ao_size(probe, p_ao_size); - ao_size = p_ao_size; -} - -float GIProbeData::get_ao_size() const { - return ao_size; -} - -void GIProbeData::set_bias(float p_bias) { - RS::get_singleton()->gi_probe_set_bias(probe, p_bias); +void VoxelGIData::set_bias(float p_bias) { + RS::get_singleton()->voxel_gi_set_bias(probe, p_bias); bias = p_bias; } -float GIProbeData::get_bias() const { +float VoxelGIData::get_bias() const { return bias; } -void GIProbeData::set_normal_bias(float p_normal_bias) { - RS::get_singleton()->gi_probe_set_normal_bias(probe, p_normal_bias); +void VoxelGIData::set_normal_bias(float p_normal_bias) { + RS::get_singleton()->voxel_gi_set_normal_bias(probe, p_normal_bias); normal_bias = p_normal_bias; } -float GIProbeData::get_normal_bias() const { +float VoxelGIData::get_normal_bias() const { return normal_bias; } -void GIProbeData::set_interior(bool p_enable) { - RS::get_singleton()->gi_probe_set_interior(probe, p_enable); +void VoxelGIData::set_interior(bool p_enable) { + RS::get_singleton()->voxel_gi_set_interior(probe, p_enable); interior = p_enable; } -bool GIProbeData::is_interior() const { +bool VoxelGIData::is_interior() const { return interior; } -void GIProbeData::set_use_two_bounces(bool p_enable) { - RS::get_singleton()->gi_probe_set_use_two_bounces(probe, p_enable); +void VoxelGIData::set_use_two_bounces(bool p_enable) { + RS::get_singleton()->voxel_gi_set_use_two_bounces(probe, p_enable); use_two_bounces = p_enable; } -bool GIProbeData::is_using_two_bounces() const { +bool VoxelGIData::is_using_two_bounces() const { return use_two_bounces; } -RID GIProbeData::get_rid() const { +RID VoxelGIData::get_rid() const { return probe; } -void GIProbeData::_validate_property(PropertyInfo &property) const { - if (property.name == "anisotropy_strength") { - bool anisotropy_enabled = ProjectSettings::get_singleton()->get("rendering/quality/gi_probes/anisotropic"); - if (!anisotropy_enabled) { - property.usage = PROPERTY_USAGE_NOEDITOR; - } - } -} - -void GIProbeData::_bind_methods() { - ClassDB::bind_method(D_METHOD("allocate", "to_cell_xform", "aabb", "octree_size", "octree_cells", "data_cells", "distance_field", "level_counts"), &GIProbeData::allocate); - - ClassDB::bind_method(D_METHOD("get_bounds"), &GIProbeData::get_bounds); - ClassDB::bind_method(D_METHOD("get_octree_size"), &GIProbeData::get_octree_size); - ClassDB::bind_method(D_METHOD("get_to_cell_xform"), &GIProbeData::get_to_cell_xform); - ClassDB::bind_method(D_METHOD("get_octree_cells"), &GIProbeData::get_octree_cells); - ClassDB::bind_method(D_METHOD("get_data_cells"), &GIProbeData::get_data_cells); - ClassDB::bind_method(D_METHOD("get_level_counts"), &GIProbeData::get_level_counts); +void VoxelGIData::_bind_methods() { + ClassDB::bind_method(D_METHOD("allocate", "to_cell_xform", "aabb", "octree_size", "octree_cells", "data_cells", "distance_field", "level_counts"), &VoxelGIData::allocate); - ClassDB::bind_method(D_METHOD("set_dynamic_range", "dynamic_range"), &GIProbeData::set_dynamic_range); - ClassDB::bind_method(D_METHOD("get_dynamic_range"), &GIProbeData::get_dynamic_range); + ClassDB::bind_method(D_METHOD("get_bounds"), &VoxelGIData::get_bounds); + ClassDB::bind_method(D_METHOD("get_octree_size"), &VoxelGIData::get_octree_size); + ClassDB::bind_method(D_METHOD("get_to_cell_xform"), &VoxelGIData::get_to_cell_xform); + ClassDB::bind_method(D_METHOD("get_octree_cells"), &VoxelGIData::get_octree_cells); + ClassDB::bind_method(D_METHOD("get_data_cells"), &VoxelGIData::get_data_cells); + ClassDB::bind_method(D_METHOD("get_level_counts"), &VoxelGIData::get_level_counts); - ClassDB::bind_method(D_METHOD("set_energy", "energy"), &GIProbeData::set_energy); - ClassDB::bind_method(D_METHOD("get_energy"), &GIProbeData::get_energy); + ClassDB::bind_method(D_METHOD("set_dynamic_range", "dynamic_range"), &VoxelGIData::set_dynamic_range); + ClassDB::bind_method(D_METHOD("get_dynamic_range"), &VoxelGIData::get_dynamic_range); - ClassDB::bind_method(D_METHOD("set_bias", "bias"), &GIProbeData::set_bias); - ClassDB::bind_method(D_METHOD("get_bias"), &GIProbeData::get_bias); + ClassDB::bind_method(D_METHOD("set_energy", "energy"), &VoxelGIData::set_energy); + ClassDB::bind_method(D_METHOD("get_energy"), &VoxelGIData::get_energy); - ClassDB::bind_method(D_METHOD("set_normal_bias", "bias"), &GIProbeData::set_normal_bias); - ClassDB::bind_method(D_METHOD("get_normal_bias"), &GIProbeData::get_normal_bias); + ClassDB::bind_method(D_METHOD("set_bias", "bias"), &VoxelGIData::set_bias); + ClassDB::bind_method(D_METHOD("get_bias"), &VoxelGIData::get_bias); - ClassDB::bind_method(D_METHOD("set_propagation", "propagation"), &GIProbeData::set_propagation); - ClassDB::bind_method(D_METHOD("get_propagation"), &GIProbeData::get_propagation); + ClassDB::bind_method(D_METHOD("set_normal_bias", "bias"), &VoxelGIData::set_normal_bias); + ClassDB::bind_method(D_METHOD("get_normal_bias"), &VoxelGIData::get_normal_bias); - ClassDB::bind_method(D_METHOD("set_anisotropy_strength", "strength"), &GIProbeData::set_anisotropy_strength); - ClassDB::bind_method(D_METHOD("get_anisotropy_strength"), &GIProbeData::get_anisotropy_strength); + ClassDB::bind_method(D_METHOD("set_propagation", "propagation"), &VoxelGIData::set_propagation); + ClassDB::bind_method(D_METHOD("get_propagation"), &VoxelGIData::get_propagation); - ClassDB::bind_method(D_METHOD("set_ao", "ao"), &GIProbeData::set_ao); - ClassDB::bind_method(D_METHOD("get_ao"), &GIProbeData::get_ao); + ClassDB::bind_method(D_METHOD("set_interior", "interior"), &VoxelGIData::set_interior); + ClassDB::bind_method(D_METHOD("is_interior"), &VoxelGIData::is_interior); - ClassDB::bind_method(D_METHOD("set_ao_size", "strength"), &GIProbeData::set_ao_size); - ClassDB::bind_method(D_METHOD("get_ao_size"), &GIProbeData::get_ao_size); + ClassDB::bind_method(D_METHOD("set_use_two_bounces", "enable"), &VoxelGIData::set_use_two_bounces); + ClassDB::bind_method(D_METHOD("is_using_two_bounces"), &VoxelGIData::is_using_two_bounces); - ClassDB::bind_method(D_METHOD("set_interior", "interior"), &GIProbeData::set_interior); - ClassDB::bind_method(D_METHOD("is_interior"), &GIProbeData::is_interior); - - ClassDB::bind_method(D_METHOD("set_use_two_bounces", "enable"), &GIProbeData::set_use_two_bounces); - ClassDB::bind_method(D_METHOD("is_using_two_bounces"), &GIProbeData::is_using_two_bounces); - - ClassDB::bind_method(D_METHOD("_set_data", "data"), &GIProbeData::_set_data); - ClassDB::bind_method(D_METHOD("_get_data"), &GIProbeData::_get_data); + ClassDB::bind_method(D_METHOD("_set_data", "data"), &VoxelGIData::_set_data); + ClassDB::bind_method(D_METHOD("_get_data"), &VoxelGIData::_get_data); ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "_data", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_data", "_get_data"); @@ -279,36 +232,22 @@ void GIProbeData::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bias", PROPERTY_HINT_RANGE, "0,8,0.01"), "set_bias", "get_bias"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "normal_bias", PROPERTY_HINT_RANGE, "0,8,0.01"), "set_normal_bias", "get_normal_bias"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "propagation", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_propagation", "get_propagation"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "anisotropy_strength", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_anisotropy_strength", "get_anisotropy_strength"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ao", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_ao", "get_ao"); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ao_size", PROPERTY_HINT_RANGE, "0,1,0.01"), "set_ao_size", "get_ao_size"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_two_bounces"), "set_use_two_bounces", "is_using_two_bounces"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interior"), "set_interior", "is_interior"); } -GIProbeData::GIProbeData() { - ao = 0.0; - ao_size = 0.5; - dynamic_range = 4; - energy = 1.0; - bias = 1.5; - normal_bias = 0.0; - propagation = 0.7; - anisotropy_strength = 0.5; - interior = false; - use_two_bounces = false; - - probe = RS::get_singleton()->gi_probe_create(); +VoxelGIData::VoxelGIData() { + probe = RS::get_singleton()->voxel_gi_create(); } -GIProbeData::~GIProbeData() { +VoxelGIData::~VoxelGIData() { RS::get_singleton()->free(probe); } ////////////////////// ////////////////////// -void GIProbe::set_probe_data(const Ref<GIProbeData> &p_data) { +void VoxelGI::set_probe_data(const Ref<VoxelGIData> &p_data) { if (p_data.is_valid()) { RS::get_singleton()->instance_set_base(get_instance(), p_data->get_rid()); } else { @@ -318,45 +257,44 @@ void GIProbe::set_probe_data(const Ref<GIProbeData> &p_data) { probe_data = p_data; } -Ref<GIProbeData> GIProbe::get_probe_data() const { +Ref<VoxelGIData> VoxelGI::get_probe_data() const { return probe_data; } -void GIProbe::set_subdiv(Subdiv p_subdiv) { +void VoxelGI::set_subdiv(Subdiv p_subdiv) { ERR_FAIL_INDEX(p_subdiv, SUBDIV_MAX); subdiv = p_subdiv; - update_gizmo(); + update_gizmos(); } -GIProbe::Subdiv GIProbe::get_subdiv() const { +VoxelGI::Subdiv VoxelGI::get_subdiv() const { return subdiv; } -void GIProbe::set_extents(const Vector3 &p_extents) { +void VoxelGI::set_extents(const Vector3 &p_extents) { extents = p_extents; - update_gizmo(); - _change_notify("extents"); + update_gizmos(); } -Vector3 GIProbe::get_extents() const { +Vector3 VoxelGI::get_extents() const { return extents; } -void GIProbe::_find_meshes(Node *p_at_node, List<PlotMesh> &plot_meshes) { +void VoxelGI::_find_meshes(Node *p_at_node, List<PlotMesh> &plot_meshes) { MeshInstance3D *mi = Object::cast_to<MeshInstance3D>(p_at_node); if (mi && mi->get_gi_mode() == GeometryInstance3D::GI_MODE_BAKED && mi->is_visible_in_tree()) { Ref<Mesh> mesh = mi->get_mesh(); if (mesh.is_valid()) { AABB aabb = mesh->get_aabb(); - Transform xf = get_global_transform().affine_inverse() * mi->get_global_transform(); + Transform3D xf = get_global_transform().affine_inverse() * mi->get_global_transform(); if (AABB(-extents, extents * 2).intersects(xf.xform(aabb))) { PlotMesh pm; pm.local_xform = xf; pm.mesh = mesh; for (int i = 0; i < mesh->get_surface_count(); i++) { - pm.instance_materials.push_back(mi->get_surface_material(i)); + pm.instance_materials.push_back(mi->get_surface_override_material(i)); } pm.override_material = mi->get_material_override(); plot_meshes.push_back(pm); @@ -369,7 +307,7 @@ void GIProbe::_find_meshes(Node *p_at_node, List<PlotMesh> &plot_meshes) { if (s->is_visible_in_tree()) { Array meshes = p_at_node->call("get_meshes"); for (int i = 0; i < meshes.size(); i += 2) { - Transform mxf = meshes[i]; + Transform3D mxf = meshes[i]; Ref<Mesh> mesh = meshes[i + 1]; if (!mesh.is_valid()) { continue; @@ -377,7 +315,7 @@ void GIProbe::_find_meshes(Node *p_at_node, List<PlotMesh> &plot_meshes) { AABB aabb = mesh->get_aabb(); - Transform xf = get_global_transform().affine_inverse() * (s->get_global_transform() * mxf); + Transform3D xf = get_global_transform().affine_inverse() * (s->get_global_transform() * mxf); if (AABB(-extents, extents * 2).intersects(xf.xform(aabb))) { PlotMesh pm; @@ -395,11 +333,11 @@ void GIProbe::_find_meshes(Node *p_at_node, List<PlotMesh> &plot_meshes) { } } -GIProbe::BakeBeginFunc GIProbe::bake_begin_function = nullptr; -GIProbe::BakeStepFunc GIProbe::bake_step_function = nullptr; -GIProbe::BakeEndFunc GIProbe::bake_end_function = nullptr; +VoxelGI::BakeBeginFunc VoxelGI::bake_begin_function = nullptr; +VoxelGI::BakeStepFunc VoxelGI::bake_step_function = nullptr; +VoxelGI::BakeEndFunc VoxelGI::bake_end_function = nullptr; -Vector3i GIProbe::get_estimated_cell_size() const { +Vector3i VoxelGI::get_estimated_cell_size() const { static const int subdiv_value[SUBDIV_MAX] = { 6, 7, 8, 9 }; int cell_subdiv = subdiv_value[subdiv]; int axis_cell_size[3]; @@ -425,16 +363,19 @@ Vector3i GIProbe::get_estimated_cell_size() const { return Vector3i(axis_cell_size[0], axis_cell_size[1], axis_cell_size[2]); } -void GIProbe::bake(Node *p_from_node, bool p_create_visual_debug) { +void VoxelGI::bake(Node *p_from_node, bool p_create_visual_debug) { static const int subdiv_value[SUBDIV_MAX] = { 6, 7, 8, 9 }; + p_from_node = p_from_node ? p_from_node : get_parent(); + ERR_FAIL_NULL(p_from_node); + Voxelizer baker; baker.begin_bake(subdiv_value[subdiv], AABB(-extents, extents * 2.0)); List<PlotMesh> mesh_list; - _find_meshes(p_from_node ? p_from_node : get_parent(), mesh_list); + _find_meshes(p_from_node, mesh_list); if (bake_begin_function) { bake_begin_function(mesh_list.size() + 1); @@ -442,14 +383,14 @@ void GIProbe::bake(Node *p_from_node, bool p_create_visual_debug) { int pmc = 0; - for (List<PlotMesh>::Element *E = mesh_list.front(); E; E = E->next()) { + for (PlotMesh &E : mesh_list) { if (bake_step_function) { bake_step_function(pmc, RTR("Plotting Meshes") + " " + itos(pmc) + "/" + itos(mesh_list.size())); } pmc++; - baker.plot_mesh(E->get().local_xform, E->get().mesh, E->get().instance_materials, E->get().override_material); + baker.plot_mesh(E.local_xform, E.mesh, E.instance_materials, E.override_material); } if (bake_step_function) { bake_step_function(pmc++, RTR("Finishing Plot")); @@ -464,7 +405,7 @@ void GIProbe::bake(Node *p_from_node, bool p_create_visual_debug) { mmi->set_multimesh(baker.create_debug_multimesh()); add_child(mmi); #ifdef TOOLS_ENABLED - if (get_tree()->get_edited_scene_root() == this) { + if (is_inside_tree() && get_tree()->get_edited_scene_root() == this) { mmi->set_owner(this); } else { mmi->set_owner(get_owner()); @@ -474,10 +415,10 @@ void GIProbe::bake(Node *p_from_node, bool p_create_visual_debug) { #endif } else { - Ref<GIProbeData> probe_data = get_probe_data(); + Ref<VoxelGIData> probe_data = get_probe_data(); if (probe_data.is_null()) { - probe_data.instance(); + probe_data.instantiate(); } if (bake_step_function) { @@ -486,7 +427,7 @@ void GIProbe::bake(Node *p_from_node, bool p_create_visual_debug) { Vector<uint8_t> df = baker.get_sdf_3d_image(); - probe_data->allocate(baker.get_to_cell_space_xform(), AABB(-extents, extents * 2.0), baker.get_giprobe_octree_size(), baker.get_giprobe_octree_cells(), baker.get_giprobe_data_cells(), df, baker.get_giprobe_level_cell_count()); + probe_data->allocate(baker.get_to_cell_space_xform(), AABB(-extents, extents * 2.0), baker.get_voxel_gi_octree_size(), baker.get_voxel_gi_octree_cells(), baker.get_voxel_gi_data_cells(), df, baker.get_voxel_gi_level_cell_count()); set_probe_data(probe_data); #ifdef TOOLS_ENABLED @@ -498,50 +439,49 @@ void GIProbe::bake(Node *p_from_node, bool p_create_visual_debug) { bake_end_function(); } - _change_notify(); //bake property may have changed + notify_property_list_changed(); //bake property may have changed } -void GIProbe::_debug_bake() { +void VoxelGI::_debug_bake() { bake(nullptr, true); } -AABB GIProbe::get_aabb() const { +AABB VoxelGI::get_aabb() const { return AABB(-extents, extents * 2); } -Vector<Face3> GIProbe::get_faces(uint32_t p_usage_flags) const { +Vector<Face3> VoxelGI::get_faces(uint32_t p_usage_flags) const { return Vector<Face3>(); } -String GIProbe::get_configuration_warning() const { - String warning = VisualInstance3D::get_configuration_warning(); +TypedArray<String> VoxelGI::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (RenderingServer::get_singleton()->is_low_end()) { - if (!warning.empty()) { - warning += "\n\n"; - } - warning += TTR("GIProbes are not supported by the GLES2 video driver.\nUse a BakedLightmap instead."); + warnings.push_back(TTR("VoxelGIs are not supported by the GLES2 video driver.\nUse a LightmapGI instead.")); + } else if (probe_data.is_null()) { + warnings.push_back(TTR("No VoxelGI data set, so this node is disabled. Bake static objects to enable GI.")); } - return warning; + return warnings; } -void GIProbe::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_probe_data", "data"), &GIProbe::set_probe_data); - ClassDB::bind_method(D_METHOD("get_probe_data"), &GIProbe::get_probe_data); +void VoxelGI::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_probe_data", "data"), &VoxelGI::set_probe_data); + ClassDB::bind_method(D_METHOD("get_probe_data"), &VoxelGI::get_probe_data); - ClassDB::bind_method(D_METHOD("set_subdiv", "subdiv"), &GIProbe::set_subdiv); - ClassDB::bind_method(D_METHOD("get_subdiv"), &GIProbe::get_subdiv); + ClassDB::bind_method(D_METHOD("set_subdiv", "subdiv"), &VoxelGI::set_subdiv); + ClassDB::bind_method(D_METHOD("get_subdiv"), &VoxelGI::get_subdiv); - ClassDB::bind_method(D_METHOD("set_extents", "extents"), &GIProbe::set_extents); - ClassDB::bind_method(D_METHOD("get_extents"), &GIProbe::get_extents); + ClassDB::bind_method(D_METHOD("set_extents", "extents"), &VoxelGI::set_extents); + ClassDB::bind_method(D_METHOD("get_extents"), &VoxelGI::get_extents); - ClassDB::bind_method(D_METHOD("bake", "from_node", "create_visual_debug"), &GIProbe::bake, DEFVAL(Variant()), DEFVAL(false)); - ClassDB::bind_method(D_METHOD("debug_bake"), &GIProbe::_debug_bake); + ClassDB::bind_method(D_METHOD("bake", "from_node", "create_visual_debug"), &VoxelGI::bake, DEFVAL(Variant()), DEFVAL(false)); + ClassDB::bind_method(D_METHOD("debug_bake"), &VoxelGI::_debug_bake); ClassDB::set_method_flags(get_class_static(), _scs_create("debug_bake"), METHOD_FLAGS_DEFAULT | METHOD_FLAG_EDITOR); ADD_PROPERTY(PropertyInfo(Variant::INT, "subdiv", PROPERTY_HINT_ENUM, "64,128,256,512"), "set_subdiv", "get_subdiv"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR3, "extents"), "set_extents", "get_extents"); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "data", PROPERTY_HINT_RESOURCE_TYPE, "GIProbeData", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "set_probe_data", "get_probe_data"); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "data", PROPERTY_HINT_RESOURCE_TYPE, "VoxelGIData", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_DO_NOT_SHARE_ON_DUPLICATE), "set_probe_data", "get_probe_data"); BIND_ENUM_CONSTANT(SUBDIV_64); BIND_ENUM_CONSTANT(SUBDIV_128); @@ -550,14 +490,11 @@ void GIProbe::_bind_methods() { BIND_ENUM_CONSTANT(SUBDIV_MAX); } -GIProbe::GIProbe() { - subdiv = SUBDIV_128; - extents = Vector3(10, 10, 10); - - gi_probe = RS::get_singleton()->gi_probe_create(); +VoxelGI::VoxelGI() { + voxel_gi = RS::get_singleton()->voxel_gi_create(); set_disable_scale(true); } -GIProbe::~GIProbe() { - RS::get_singleton()->free(gi_probe); +VoxelGI::~VoxelGI() { + RS::get_singleton()->free(voxel_gi); } diff --git a/scene/3d/gi_probe.h b/scene/3d/voxel_gi.h index 2dadf48a06..5d0dda1ba3 100644 --- a/scene/3d/gi_probe.h +++ b/scene/3d/voxel_gi.h @@ -1,12 +1,12 @@ /*************************************************************************/ -/* gi_probe.h */ +/* voxel_gi.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,48 +28,43 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef GIPROBE_H -#define GIPROBE_H +#ifndef VOXEL_GI_H +#define VOXEL_GI_H -#include "multimesh_instance_3d.h" #include "scene/3d/visual_instance_3d.h" -class GIProbeData : public Resource { - GDCLASS(GIProbeData, Resource); +class VoxelGIData : public Resource { + GDCLASS(VoxelGIData, Resource); RID probe; void _set_data(const Dictionary &p_data); Dictionary _get_data() const; - Transform to_cell_xform; + Transform3D to_cell_xform; AABB bounds; Vector3 octree_size; - float dynamic_range; - float energy; - float bias; - float normal_bias; - float propagation; - float anisotropy_strength; - float ao; - float ao_size; - bool interior; - bool use_two_bounces; + float dynamic_range = 4.0; + float energy = 1.0; + float bias = 1.5; + float normal_bias = 0.0; + float propagation = 0.7; + bool interior = false; + bool use_two_bounces = false; protected: static void _bind_methods(); - void _validate_property(PropertyInfo &property) const override; public: - void allocate(const Transform &p_to_cell_xform, const AABB &p_aabb, const Vector3 &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts); + void allocate(const Transform3D &p_to_cell_xform, const AABB &p_aabb, const Vector3 &p_octree_size, const Vector<uint8_t> &p_octree_cells, const Vector<uint8_t> &p_data_cells, const Vector<uint8_t> &p_distance_field, const Vector<int> &p_level_counts); AABB get_bounds() const; Vector3 get_octree_size() const; Vector<uint8_t> get_octree_cells() const; Vector<uint8_t> get_data_cells() const; Vector<uint8_t> get_distance_field() const; Vector<int> get_level_counts() const; - Transform get_to_cell_xform() const; + Transform3D get_to_cell_xform() const; void set_dynamic_range(float p_range); float get_dynamic_range() const; @@ -77,15 +72,6 @@ public: void set_propagation(float p_propagation); float get_propagation() const; - void set_anisotropy_strength(float p_anisotropy_strength); - float get_anisotropy_strength() const; - - void set_ao(float p_ao); - float get_ao() const; - - void set_ao_size(float p_ao_size); - float get_ao_size() const; - void set_energy(float p_energy); float get_energy() const; @@ -103,12 +89,12 @@ public: virtual RID get_rid() const override; - GIProbeData(); - ~GIProbeData(); + VoxelGIData(); + ~VoxelGIData(); }; -class GIProbe : public VisualInstance3D { - GDCLASS(GIProbe, VisualInstance3D); +class VoxelGI : public VisualInstance3D { + GDCLASS(VoxelGI, VisualInstance3D); public: enum Subdiv { @@ -125,18 +111,18 @@ public: typedef void (*BakeEndFunc)(); private: - Ref<GIProbeData> probe_data; + Ref<VoxelGIData> probe_data; - RID gi_probe; + RID voxel_gi; - Subdiv subdiv; - Vector3 extents; + Subdiv subdiv = SUBDIV_128; + Vector3 extents = Vector3(10, 10, 10); struct PlotMesh { Ref<Material> override_material; Vector<Ref<Material>> instance_materials; Ref<Mesh> mesh; - Transform local_xform; + Transform3D local_xform; }; void _find_meshes(Node *p_at_node, List<PlotMesh> &plot_meshes); @@ -150,8 +136,8 @@ public: static BakeStepFunc bake_step_function; static BakeEndFunc bake_end_function; - void set_probe_data(const Ref<GIProbeData> &p_data); - Ref<GIProbeData> get_probe_data() const; + void set_probe_data(const Ref<VoxelGIData> &p_data); + Ref<VoxelGIData> get_probe_data() const; void set_subdiv(Subdiv p_subdiv); Subdiv get_subdiv() const; @@ -165,12 +151,12 @@ public: virtual AABB get_aabb() const override; virtual Vector<Face3> get_faces(uint32_t p_usage_flags) const override; - virtual String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; - GIProbe(); - ~GIProbe(); + VoxelGI(); + ~VoxelGI(); }; -VARIANT_ENUM_CAST(GIProbe::Subdiv) +VARIANT_ENUM_CAST(VoxelGI::Subdiv) -#endif // GIPROBE_H +#endif // VOXEL_GI_H diff --git a/scene/3d/voxelizer.cpp b/scene/3d/voxelizer.cpp index de5496ee35..2d32379d69 100644 --- a/scene/3d/voxelizer.cpp +++ b/scene/3d/voxelizer.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -29,24 +29,19 @@ /*************************************************************************/ #include "voxelizer.h" -#include "core/math/geometry_3d.h" -#include "core/os/os.h" -#include "core/os/threaded_array_processor.h" - -#include <stdlib.h> static _FORCE_INLINE_ void get_uv_and_normal(const Vector3 &p_pos, const Vector3 *p_vtx, const Vector2 *p_uv, const Vector3 *p_normal, Vector2 &r_uv, Vector3 &r_normal) { - if (p_pos.distance_squared_to(p_vtx[0]) < CMP_EPSILON2) { + if (p_pos.is_equal_approx(p_vtx[0])) { r_uv = p_uv[0]; r_normal = p_normal[0]; return; } - if (p_pos.distance_squared_to(p_vtx[1]) < CMP_EPSILON2) { + if (p_pos.is_equal_approx(p_vtx[1])) { r_uv = p_uv[1]; r_normal = p_normal[1]; return; } - if (p_pos.distance_squared_to(p_vtx[2]) < CMP_EPSILON2) { + if (p_pos.is_equal_approx(p_vtx[2])) { r_uv = p_uv[2]; r_normal = p_normal[2]; return; @@ -56,20 +51,20 @@ static _FORCE_INLINE_ void get_uv_and_normal(const Vector3 &p_pos, const Vector3 Vector3 v1 = p_vtx[2] - p_vtx[0]; Vector3 v2 = p_pos - p_vtx[0]; - float d00 = v0.dot(v0); - float d01 = v0.dot(v1); - float d11 = v1.dot(v1); - float d20 = v2.dot(v0); - float d21 = v2.dot(v1); - float denom = (d00 * d11 - d01 * d01); + real_t d00 = v0.dot(v0); + real_t d01 = v0.dot(v1); + real_t d11 = v1.dot(v1); + real_t d20 = v2.dot(v0); + real_t d21 = v2.dot(v1); + real_t denom = (d00 * d11 - d01 * d01); if (denom == 0) { r_uv = p_uv[0]; r_normal = p_normal[0]; return; } - float v = (d11 * d20 - d01 * d21) / denom; - float w = (d00 * d21 - d01 * d20) / denom; - float u = 1.0f - v - w; + real_t v = (d11 * d20 - d01 * d21) / denom; + real_t w = (d00 * d21 - d01 * d20) / denom; + real_t u = 1.0f - v - w; r_uv = p_uv[0] * u + p_uv[1] * v + p_uv[2] * w; r_normal = (p_normal[0] * u + p_normal[1] * v + p_normal[2] * w).normalized(); @@ -81,7 +76,7 @@ void Voxelizer::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, co //find best axis to map to, for scanning values int closest_axis = 0; - float closest_dot = 0; + real_t closest_dot = 0; Plane plane = Plane(p_vtx[0], p_vtx[1], p_vtx[2]); Vector3 normal = plane.normal; @@ -89,7 +84,7 @@ void Voxelizer::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, co for (int i = 0; i < 3; i++) { Vector3 axis; axis[i] = 1.0; - float dot = ABS(normal.dot(axis)); + real_t dot = ABS(normal.dot(axis)); if (i == 0 || dot > closest_dot) { closest_axis = i; closest_dot = dot; @@ -103,8 +98,8 @@ void Voxelizer::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, co Vector3 t2; t2[(closest_axis + 2) % 3] = 1.0; - t1 *= p_aabb.size[(closest_axis + 1) % 3] / float(color_scan_cell_width); - t2 *= p_aabb.size[(closest_axis + 2) % 3] / float(color_scan_cell_width); + t1 *= p_aabb.size[(closest_axis + 1) % 3] / real_t(color_scan_cell_width); + t2 *= p_aabb.size[(closest_axis + 2) % 3] / real_t(color_scan_cell_width); Color albedo_accum; Color emission_accum; @@ -114,10 +109,10 @@ void Voxelizer::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, co //map to a grid average in the best axis for this face for (int i = 0; i < color_scan_cell_width; i++) { - Vector3 ofs_i = float(i) * t1; + Vector3 ofs_i = real_t(i) * t1; for (int j = 0; j < color_scan_cell_width; j++) { - Vector3 ofs_j = float(j) * t2; + Vector3 ofs_j = real_t(j) * t2; Vector3 from = p_aabb.position + ofs_i + ofs_j; Vector3 to = from + t1 + t2 + axis * p_aabb.size[closest_axis]; @@ -151,12 +146,12 @@ void Voxelizer::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, co Vector2 uv; Vector3 lnormal; get_uv_and_normal(intersection, p_vtx, p_uv, p_normal, uv, lnormal); - if (lnormal == Vector3()) { //just in case normal as nor provided + if (lnormal == Vector3()) { //just in case normal is not provided lnormal = normal; } - int uv_x = CLAMP(int(Math::fposmod(uv.x, 1.0f) * bake_texture_size), 0, bake_texture_size - 1); - int uv_y = CLAMP(int(Math::fposmod(uv.y, 1.0f) * bake_texture_size), 0, bake_texture_size - 1); + int uv_x = CLAMP(int(Math::fposmod(uv.x, (real_t)1.0) * bake_texture_size), 0, bake_texture_size - 1); + int uv_y = CLAMP(int(Math::fposmod(uv.y, (real_t)1.0) * bake_texture_size), 0, bake_texture_size - 1); int ofs = uv_y * bake_texture_size + uv_x; albedo_accum.r += p_material.albedo[ofs].r; @@ -183,12 +178,12 @@ void Voxelizer::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, co Vector3 lnormal; Vector2 uv; get_uv_and_normal(inters, p_vtx, p_uv, p_normal, uv, normal); - if (lnormal == Vector3()) { //just in case normal as nor provided + if (lnormal == Vector3()) { //just in case normal is not provided lnormal = normal; } - int uv_x = CLAMP(Math::fposmod(uv.x, 1.0f) * bake_texture_size, 0, bake_texture_size - 1); - int uv_y = CLAMP(Math::fposmod(uv.y, 1.0f) * bake_texture_size, 0, bake_texture_size - 1); + int uv_x = CLAMP(Math::fposmod(uv.x, (real_t)1.0) * bake_texture_size, 0, bake_texture_size - 1); + int uv_y = CLAMP(Math::fposmod(uv.y, (real_t)1.0) * bake_texture_size, 0, bake_texture_size - 1); int ofs = uv_y * bake_texture_size + uv_x; @@ -294,7 +289,7 @@ void Voxelizer::_plot_face(int p_idx, int p_level, int p_x, int p_y, int p_z, co Vector<Color> Voxelizer::_get_bake_texture(Ref<Image> p_image, const Color &p_color_mul, const Color &p_color_add) { Vector<Color> ret; - if (p_image.is_null() || p_image->empty()) { + if (p_image.is_null() || p_image->is_empty()) { ret.resize(bake_texture_size * bake_texture_size); for (int i = 0; i < bake_texture_size * bake_texture_size; i++) { ret.write[i] = p_color_add; @@ -344,7 +339,7 @@ Voxelizer::MaterialCache Voxelizer::_get_material_cache(Ref<Material> p_material Ref<Image> img_albedo; if (albedo_tex.is_valid()) { - img_albedo = albedo_tex->get_data(); + img_albedo = albedo_tex->get_image(); mc.albedo = _get_bake_texture(img_albedo, mat->get_albedo(), Color(0, 0, 0)); // albedo texture, color is multiplicative } else { mc.albedo = _get_bake_texture(img_albedo, Color(1, 1, 1), mat->get_albedo()); // no albedo texture, color is additive @@ -358,7 +353,7 @@ Voxelizer::MaterialCache Voxelizer::_get_material_cache(Ref<Material> p_material Ref<Image> img_emission; if (emission_tex.is_valid()) { - img_emission = emission_tex->get_data(); + img_emission = emission_tex->get_image(); } if (mat->get_emission_operator() == StandardMaterial3D::EMISSION_OP_ADD) { @@ -378,7 +373,7 @@ Voxelizer::MaterialCache Voxelizer::_get_material_cache(Ref<Material> p_material return mc; } -void Voxelizer::plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, const Vector<Ref<Material>> &p_materials, const Ref<Material> &p_override_material) { +void Voxelizer::plot_mesh(const Transform3D &p_xform, Ref<Mesh> &p_mesh, const Vector<Ref<Material>> &p_materials, const Ref<Material> &p_override_material) { for (int i = 0; i < p_mesh->get_surface_count(); i++) { if (p_mesh->surface_get_primitive_type(i) != Mesh::PRIMITIVE_TRIANGLES) { continue; //only triangles @@ -580,7 +575,6 @@ void Voxelizer::_fixup_plot(int p_idx, int p_level) { /*if (bake_light.size()) { for(int i=0;i<6;i++) { - } }*/ @@ -637,7 +631,7 @@ void Voxelizer::begin_bake(int p_subdiv, const AABB &p_bounds) { } axis_cell_size[i] = axis_cell_size[longest_axis]; - float axis_size = po2_bounds.size[longest_axis]; + real_t axis_size = po2_bounds.size[longest_axis]; //shrink until fit subdiv while (axis_size / 2.0 >= po2_bounds.size[i]) { @@ -648,11 +642,11 @@ void Voxelizer::begin_bake(int p_subdiv, const AABB &p_bounds) { po2_bounds.size[i] = po2_bounds.size[longest_axis]; } - Transform to_bounds; + Transform3D to_bounds; to_bounds.basis.scale(Vector3(po2_bounds.size[longest_axis], po2_bounds.size[longest_axis], po2_bounds.size[longest_axis])); to_bounds.origin = po2_bounds.position; - Transform to_grid; + Transform3D to_grid; to_grid.basis.scale(Vector3(axis_cell_size[longest_axis], axis_cell_size[longest_axis], axis_cell_size[longest_axis])); to_cell_space = to_grid * to_bounds.affine_inverse(); @@ -669,19 +663,19 @@ void Voxelizer::end_bake() { //create the data for visual server -int Voxelizer::get_gi_probe_octree_depth() const { +int Voxelizer::get_voxel_gi_octree_depth() const { return cell_subdiv; } -Vector3i Voxelizer::get_giprobe_octree_size() const { +Vector3i Voxelizer::get_voxel_gi_octree_size() const { return Vector3i(axis_cell_size[0], axis_cell_size[1], axis_cell_size[2]); } -int Voxelizer::get_giprobe_cell_count() const { +int Voxelizer::get_voxel_gi_cell_count() const { return bake_cells.size(); } -Vector<uint8_t> Voxelizer::get_giprobe_octree_cells() const { +Vector<uint8_t> Voxelizer::get_voxel_gi_octree_cells() const { Vector<uint8_t> data; data.resize((8 * 4) * bake_cells.size()); //8 uint32t values { @@ -701,7 +695,7 @@ Vector<uint8_t> Voxelizer::get_giprobe_octree_cells() const { return data; } -Vector<uint8_t> Voxelizer::get_giprobe_data_cells() const { +Vector<uint8_t> Voxelizer::get_voxel_gi_data_cells() const { Vector<uint8_t> data; data.resize((4 * 4) * bake_cells.size()); //8 uint32t values { @@ -756,7 +750,7 @@ Vector<uint8_t> Voxelizer::get_giprobe_data_cells() const { return data; } -Vector<int> Voxelizer::get_giprobe_level_cell_count() const { +Vector<int> Voxelizer::get_voxel_gi_level_cell_count() const { uint32_t cell_count = bake_cells.size(); const Cell *cells = bake_cells.ptr(); Vector<int> level_count; @@ -820,7 +814,7 @@ static void edt(float *f, int stride, int n) { #undef square Vector<uint8_t> Voxelizer::get_sdf_3d_image() const { - Vector3i octree_size = get_giprobe_octree_size(); + Vector3i octree_size = get_voxel_gi_octree_size(); uint32_t float_count = octree_size.x * octree_size.y * octree_size.z; float *work_memory = memnew_arr(float, float_count); @@ -892,7 +886,7 @@ Vector<uint8_t> Voxelizer::get_sdf_3d_image() const { void Voxelizer::_debug_mesh(int p_idx, int p_level, const AABB &p_aabb, Ref<MultiMesh> &p_multimesh, int &idx) { if (p_level == cell_subdiv - 1) { Vector3 center = p_aabb.position + p_aabb.size * 0.5; - Transform xform; + Transform3D xform; xform.origin = center; xform.basis.scale(p_aabb.size * 0.5); p_multimesh->set_instance_transform(idx, xform); @@ -932,14 +926,14 @@ void Voxelizer::_debug_mesh(int p_idx, int p_level, const AABB &p_aabb, Ref<Mult Ref<MultiMesh> Voxelizer::create_debug_multimesh() { Ref<MultiMesh> mm; - mm.instance(); + mm.instantiate(); mm->set_transform_format(MultiMesh::TRANSFORM_3D); mm->set_use_colors(true); mm->set_instance_count(leaf_voxel_count); Ref<ArrayMesh> mesh; - mesh.instance(); + mesh.instantiate(); { Array arr; @@ -955,7 +949,7 @@ Ref<MultiMesh> Voxelizer::create_debug_multimesh() { Vector3 face_points[4]; for (int j = 0; j < 4; j++) { - float v[3]; + real_t v[3]; v[0] = 1.0; v[1] = 1 - 2 * ((j >> 1) & 1); v[2] = v[1] * (1 - 2 * (j & 1)); @@ -986,7 +980,7 @@ Ref<MultiMesh> Voxelizer::create_debug_multimesh() { { Ref<StandardMaterial3D> fsm; - fsm.instance(); + fsm.instantiate(); fsm->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true); fsm->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true); fsm->set_shading_mode(StandardMaterial3D::SHADING_MODE_UNSHADED); @@ -1003,12 +997,9 @@ Ref<MultiMesh> Voxelizer::create_debug_multimesh() { return mm; } -Transform Voxelizer::get_to_cell_space_xform() const { +Transform3D Voxelizer::get_to_cell_space_xform() const { return to_cell_space; } Voxelizer::Voxelizer() { - sorted = false; - color_scan_cell_width = 4; - bake_texture_size = 128; } diff --git a/scene/3d/voxelizer.h b/scene/3d/voxelizer.h index 3546fd7729..09c126bc4e 100644 --- a/scene/3d/voxelizer.h +++ b/scene/3d/voxelizer.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,8 +31,6 @@ #ifndef VOXEL_LIGHT_BAKER_H #define VOXEL_LIGHT_BAKER_H -#include "core/math/vector3i.h" -#include "scene/3d/mesh_instance_3d.h" #include "scene/resources/multimesh.h" class Voxelizer { @@ -44,35 +42,25 @@ private: struct Cell { uint32_t children[8]; - float albedo[3]; //albedo in RGB24 - float emission[3]; //accumulated light in 16:16 fixed point (needs to be integer for moving lights fast) - float normal[3]; - uint32_t used_sides; - float alpha; //used for upsampling - uint16_t x; - uint16_t y; - uint16_t z; - uint16_t level; + float albedo[3] = {}; //albedo in RGB24 + float emission[3] = {}; //accumulated light in 16:16 fixed point (needs to be integer for moving lights fast) + float normal[3] = {}; + uint32_t used_sides = 0; + float alpha = 0.0; //used for upsampling + uint16_t x = 0; + uint16_t y = 0; + uint16_t z = 0; + uint16_t level = 0; Cell() { for (int i = 0; i < 8; i++) { children[i] = CHILD_EMPTY; } - - for (int i = 0; i < 3; i++) { - emission[i] = 0; - albedo[i] = 0; - normal[i] = 0; - } - alpha = 0; - used_sides = 0; - x = y = z = 0; - level = 0; } }; Vector<Cell> bake_cells; - int cell_subdiv; + int cell_subdiv = 0; struct CellSort { union { @@ -82,10 +70,10 @@ private: uint64_t x : 16; uint64_t level : 16; }; - uint64_t key; + uint64_t key = 0; }; - int32_t index; + int32_t index = 0; _FORCE_INLINE_ bool operator<(const CellSort &p_cell_sort) const { return key < p_cell_sort.key; @@ -101,16 +89,16 @@ private: Map<Ref<Material>, MaterialCache> material_cache; AABB original_bounds; AABB po2_bounds; - int axis_cell_size[3]; + int axis_cell_size[3] = {}; - Transform to_cell_space; + Transform3D to_cell_space; - int color_scan_cell_width; - int bake_texture_size; - float cell_size; + int color_scan_cell_width = 4; + int bake_texture_size = 128; + float cell_size = 0.0; - int max_original_cells; - int leaf_voxel_count; + int max_original_cells = 0; + int leaf_voxel_count = 0; Vector<Color> _get_bake_texture(Ref<Image> p_image, const Color &p_color_mul, const Color &p_color_add); MaterialCache _get_material_cache(Ref<Material> p_material); @@ -119,25 +107,25 @@ private: void _fixup_plot(int p_idx, int p_level); void _debug_mesh(int p_idx, int p_level, const AABB &p_aabb, Ref<MultiMesh> &p_multimesh, int &idx); - bool sorted; + bool sorted = false; void _sort(); public: void begin_bake(int p_subdiv, const AABB &p_bounds); - void plot_mesh(const Transform &p_xform, Ref<Mesh> &p_mesh, const Vector<Ref<Material>> &p_materials, const Ref<Material> &p_override_material); + void plot_mesh(const Transform3D &p_xform, Ref<Mesh> &p_mesh, const Vector<Ref<Material>> &p_materials, const Ref<Material> &p_override_material); void end_bake(); - int get_gi_probe_octree_depth() const; - Vector3i get_giprobe_octree_size() const; - int get_giprobe_cell_count() const; - Vector<uint8_t> get_giprobe_octree_cells() const; - Vector<uint8_t> get_giprobe_data_cells() const; - Vector<int> get_giprobe_level_cell_count() const; + int get_voxel_gi_octree_depth() const; + Vector3i get_voxel_gi_octree_size() const; + int get_voxel_gi_cell_count() const; + Vector<uint8_t> get_voxel_gi_octree_cells() const; + Vector<uint8_t> get_voxel_gi_data_cells() const; + Vector<int> get_voxel_gi_level_cell_count() const; Vector<uint8_t> get_sdf_3d_image() const; Ref<MultiMesh> create_debug_multimesh(); - Transform get_to_cell_space_xform() const; + Transform3D get_to_cell_space_xform() const; Voxelizer(); }; diff --git a/scene/3d/world_environment.cpp b/scene/3d/world_environment.cpp index 3c12d4991e..26fa43b969 100644 --- a/scene/3d/world_environment.cpp +++ b/scene/3d/world_environment.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,56 +30,75 @@ #include "world_environment.h" +#include "scene/3d/node_3d.h" #include "scene/main/window.h" void WorldEnvironment::_notification(int p_what) { if (p_what == Node3D::NOTIFICATION_ENTER_WORLD || p_what == Node3D::NOTIFICATION_ENTER_TREE) { if (environment.is_valid()) { - if (get_viewport()->find_world_3d()->get_environment().is_valid()) { - WARN_PRINT("World already has an environment (Another WorldEnvironment?), overriding."); - } - get_viewport()->find_world_3d()->set_environment(environment); add_to_group("_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); + _update_current_environment(); } if (camera_effects.is_valid()) { - if (get_viewport()->find_world_3d()->get_camera_effects().is_valid()) { - WARN_PRINT("World already has a camera effects (Another WorldEnvironment?), overriding."); - } - get_viewport()->find_world_3d()->set_camera_effects(camera_effects); add_to_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); + _update_current_camera_effects(); } } else if (p_what == Node3D::NOTIFICATION_EXIT_WORLD || p_what == Node3D::NOTIFICATION_EXIT_TREE) { - if (environment.is_valid() && get_viewport()->find_world_3d()->get_environment() == environment) { - get_viewport()->find_world_3d()->set_environment(Ref<Environment>()); + if (environment.is_valid()) { remove_from_group("_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); + _update_current_environment(); } - if (camera_effects.is_valid() && get_viewport()->find_world_3d()->get_camera_effects() == camera_effects) { - get_viewport()->find_world_3d()->set_camera_effects(Ref<CameraEffects>()); + if (camera_effects.is_valid()) { remove_from_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); + _update_current_camera_effects(); } } } -void WorldEnvironment::set_environment(const Ref<Environment> &p_environment) { - if (is_inside_tree() && environment.is_valid() && get_viewport()->find_world_3d()->get_environment() == environment) { +void WorldEnvironment::_update_current_environment() { + WorldEnvironment *first = Object::cast_to<WorldEnvironment>(get_tree()->get_first_node_in_group("_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()))); + + if (first) { + get_viewport()->find_world_3d()->set_environment(first->environment); + } else { get_viewport()->find_world_3d()->set_environment(Ref<Environment>()); + } + get_tree()->call_group("_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings"); +} + +void WorldEnvironment::_update_current_camera_effects() { + WorldEnvironment *first = Object::cast_to<WorldEnvironment>(get_tree()->get_first_node_in_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()))); + if (first) { + get_viewport()->find_world_3d()->set_camera_effects(first->camera_effects); + } else { + get_viewport()->find_world_3d()->set_camera_effects(Ref<CameraEffects>()); + } + + get_tree()->call_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), "update_configuration_warnings"); +} + +void WorldEnvironment::set_environment(const Ref<Environment> &p_environment) { + if (environment == p_environment) { + return; + } + if (is_inside_tree() && environment.is_valid()) { remove_from_group("_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); - //clean up } environment = p_environment; + if (is_inside_tree() && environment.is_valid()) { - if (get_viewport()->find_world_3d()->get_environment().is_valid()) { - WARN_PRINT("World already has an environment (Another WorldEnvironment?), overriding."); - } - get_viewport()->find_world_3d()->set_environment(environment); add_to_group("_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); } - update_configuration_warning(); + if (is_inside_tree()) { + _update_current_environment(); + } else { + update_configuration_warnings(); + } } Ref<Environment> WorldEnvironment::get_environment() const { @@ -87,53 +106,50 @@ Ref<Environment> WorldEnvironment::get_environment() const { } void WorldEnvironment::set_camera_effects(const Ref<CameraEffects> &p_camera_effects) { + if (camera_effects == p_camera_effects) { + return; + } + if (is_inside_tree() && camera_effects.is_valid() && get_viewport()->find_world_3d()->get_camera_effects() == camera_effects) { - get_viewport()->find_world_3d()->set_camera_effects(Ref<CameraEffects>()); remove_from_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); - //clean up } camera_effects = p_camera_effects; if (is_inside_tree() && camera_effects.is_valid()) { - if (get_viewport()->find_world_3d()->get_camera_effects().is_valid()) { - WARN_PRINT("World already has an camera_effects (Another WorldEnvironment?), overriding."); - } - get_viewport()->find_world_3d()->set_camera_effects(camera_effects); add_to_group("_world_camera_effects_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id())); } - update_configuration_warning(); + if (is_inside_tree()) { + _update_current_camera_effects(); + } else { + update_configuration_warnings(); + } } Ref<CameraEffects> WorldEnvironment::get_camera_effects() const { return camera_effects; } -String WorldEnvironment::get_configuration_warning() const { - String warning = Node::get_configuration_warning(); +TypedArray<String> WorldEnvironment::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); if (!environment.is_valid()) { - if (!warning.empty()) { - warning += "\n\n"; - } - warning += TTR("WorldEnvironment requires its \"Environment\" property to contain an Environment to have a visible effect."); + warnings.push_back(TTR("WorldEnvironment requires its \"Environment\" property to contain an Environment to have a visible effect.")); } if (!is_inside_tree()) { - return warning; + return warnings; } - List<Node *> nodes; - get_tree()->get_nodes_in_group("_world_environment_" + itos(get_viewport()->find_world_3d()->get_scenario().get_id()), &nodes); + if (environment.is_valid() && get_viewport()->find_world_3d()->get_environment() != environment) { + warnings.push_back(("Only the first Environment has an effect in a scene (or set of instantiated scenes).")); + } - if (nodes.size() > 1) { - if (!warning.empty()) { - warning += "\n\n"; - } - warning += TTR("Only one WorldEnvironment is allowed per scene (or set of instanced scenes)."); + if (camera_effects.is_valid() && get_viewport()->find_world_3d()->get_camera_effects() != camera_effects) { + warnings.push_back(TTR("Only one WorldEnvironment is allowed per scene (or set of instantiated scenes).")); } - return warning; + return warnings; } void WorldEnvironment::_bind_methods() { diff --git a/scene/3d/world_environment.h b/scene/3d/world_environment.h index 3fd3dc0b50..310d1e96a5 100644 --- a/scene/3d/world_environment.h +++ b/scene/3d/world_environment.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,7 +31,7 @@ #ifndef SCENARIO_FX_H #define SCENARIO_FX_H -#include "scene/3d/node_3d.h" +#include "scene/main/node.h" #include "scene/resources/camera_effects.h" #include "scene/resources/environment.h" @@ -41,6 +41,9 @@ class WorldEnvironment : public Node { Ref<Environment> environment; Ref<CameraEffects> camera_effects; + void _update_current_environment(); + void _update_current_camera_effects(); + protected: void _notification(int p_what); static void _bind_methods(); @@ -52,7 +55,7 @@ public: void set_camera_effects(const Ref<CameraEffects> &p_camera_effects); Ref<CameraEffects> get_camera_effects() const; - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; WorldEnvironment(); }; diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp index c0015aa338..ebfb58e9fe 100644 --- a/scene/3d/xr_nodes.cpp +++ b/scene/3d/xr_nodes.cpp @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,9 +30,8 @@ #include "xr_nodes.h" -#include "core/input/input.h" +#include "scene/main/viewport.h" #include "servers/xr/xr_interface.h" -#include "servers/xr_server.h" //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -55,23 +54,18 @@ void XRCamera3D::_notification(int p_what) { }; }; -String XRCamera3D::get_configuration_warning() const { - if (!is_visible() || !is_inside_tree()) { - return String(); - } - - String warning = Camera3D::get_configuration_warning(); +TypedArray<String> XRCamera3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); - // must be child node of XROrigin3D! - XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent()); - if (origin == nullptr) { - if (!warning.empty()) { - warning += "\n\n"; - } - warning += TTR("XRCamera3D must have an XROrigin3D node as its parent."); - }; + if (is_visible() && is_inside_tree()) { + // must be child node of XROrigin3D! + XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent()); + if (origin == nullptr) { + warnings.push_back(TTR("XRCamera3D must have an XROrigin3D node as its parent.")); + }; + } - return warning; + return warnings; }; Vector3 XRCamera3D::project_local_ray_normal(const Point2 &p_pos) const { @@ -91,9 +85,10 @@ Vector3 XRCamera3D::project_local_ray_normal(const Point2 &p_pos) const { Vector2 cpos = get_viewport()->get_camera_coords(p_pos); Vector3 ray; - CameraMatrix cm = xr_interface->get_projection_for_eye(XRInterface::EYE_MONO, viewport_size.aspect(), get_znear(), get_zfar()); + // Just use the first view, if multiple views are supported this function has no good result + CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far()); Vector2 screen_he = cm.get_viewport_half_extents(); - ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -get_znear()).normalized(); + ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -get_near()).normalized(); return ray; }; @@ -113,7 +108,8 @@ Point2 XRCamera3D::unproject_position(const Vector3 &p_pos) const { Size2 viewport_size = get_viewport()->get_visible_rect().size; - CameraMatrix cm = xr_interface->get_projection_for_eye(XRInterface::EYE_MONO, viewport_size.aspect(), get_znear(), get_zfar()); + // Just use the first view, if multiple views are supported this function has no good result + CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far()); Plane p(get_camera_transform().xform_inv(p_pos), 1.0); @@ -127,7 +123,7 @@ Point2 XRCamera3D::unproject_position(const Vector3 &p_pos) const { return res; }; -Vector3 XRCamera3D::project_position(const Point2 &p_point, float p_z_depth) const { +Vector3 XRCamera3D::project_position(const Point2 &p_point, real_t p_z_depth) const { // get our XRServer XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL_V(xr_server, Vector3()); @@ -142,7 +138,8 @@ Vector3 XRCamera3D::project_position(const Point2 &p_point, float p_z_depth) con Size2 viewport_size = get_viewport()->get_visible_rect().size; - CameraMatrix cm = xr_interface->get_projection_for_eye(XRInterface::EYE_MONO, viewport_size.aspect(), get_znear(), get_zfar()); + // Just use the first view, if multiple views are supported this function has no good result + CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far()); Vector2 vp_he = cm.get_viewport_half_extents(); @@ -170,7 +167,8 @@ Vector<Plane> XRCamera3D::get_frustum() const { ERR_FAIL_COND_V(!is_inside_world(), Vector<Plane>()); Size2 viewport_size = get_viewport()->get_visible_rect().size; - CameraMatrix cm = xr_interface->get_projection_for_eye(XRInterface::EYE_MONO, viewport_size.aspect(), get_znear(), get_zfar()); + // TODO Just use the first view for now, this is mostly for debugging so we may look into using our combined projection here. + CameraMatrix cm = xr_interface->get_projection_for_view(0, viewport_size.aspect(), get_near(), get_far()); return cm.get_projection_planes(get_camera_transform()); }; @@ -190,8 +188,8 @@ void XRController3D::_notification(int p_what) { ERR_FAIL_NULL(xr_server); // find the tracker for our controller - XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); - if (tracker == nullptr) { + Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); + if (!tracker.is_valid()) { // this controller is currently turned off is_active = false; button_states = 0; @@ -205,13 +203,13 @@ void XRController3D::_notification(int p_what) { // check button states for (int i = 0; i < 16; i++) { bool was_pressed = (button_states & mask) == mask; - bool is_pressed = Input::get_singleton()->is_joy_button_pressed(joy_id, i); + bool is_pressed = Input::get_singleton()->is_joy_button_pressed(joy_id, (JoyButton)i); if (!was_pressed && is_pressed) { - emit_signal("button_pressed", i); + emit_signal(SNAME("button_pressed"), i); button_states += mask; } else if (was_pressed && !is_pressed) { - emit_signal("button_release", i); + emit_signal(SNAME("button_released"), i); button_states -= mask; }; @@ -226,7 +224,7 @@ void XRController3D::_notification(int p_what) { Ref<Mesh> trackerMesh = tracker->get_mesh(); if (mesh != trackerMesh) { mesh = trackerMesh; - emit_signal("mesh_updated", mesh); + emit_signal(SNAME("mesh_updated"), mesh); } }; }; break; @@ -247,7 +245,7 @@ void XRController3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_joystick_axis", "axis"), &XRController3D::get_joystick_axis); ClassDB::bind_method(D_METHOD("get_is_active"), &XRController3D::get_is_active); - ClassDB::bind_method(D_METHOD("get_hand"), &XRController3D::get_hand); + ClassDB::bind_method(D_METHOD("get_tracker_hand"), &XRController3D::get_tracker_hand); ClassDB::bind_method(D_METHOD("get_rumble"), &XRController3D::get_rumble); ClassDB::bind_method(D_METHOD("set_rumble", "rumble"), &XRController3D::set_rumble); @@ -257,7 +255,7 @@ void XRController3D::_bind_methods() { ClassDB::bind_method(D_METHOD("get_mesh"), &XRController3D::get_mesh); ADD_SIGNAL(MethodInfo("button_pressed", PropertyInfo(Variant::INT, "button"))); - ADD_SIGNAL(MethodInfo("button_release", PropertyInfo(Variant::INT, "button"))); + ADD_SIGNAL(MethodInfo("button_released", PropertyInfo(Variant::INT, "button"))); ADD_SIGNAL(MethodInfo("mesh_updated", PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"))); }; @@ -265,7 +263,7 @@ void XRController3D::set_controller_id(int p_controller_id) { // We don't check any bounds here, this controller may not yet be active and just be a place holder until it is. // Note that setting this to 0 means this node is not bound to a controller yet. controller_id = p_controller_id; - update_configuration_warning(); + update_configuration_warnings(); }; int XRController3D::get_controller_id() const { @@ -277,12 +275,12 @@ String XRController3D::get_controller_name() const { XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL_V(xr_server, String()); - XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); - if (tracker == nullptr) { + Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); + if (!tracker.is_valid()) { return String("Not connected"); }; - return tracker->get_name(); + return tracker->get_tracker_name(); }; int XRController3D::get_joystick_id() const { @@ -290,8 +288,8 @@ int XRController3D::get_joystick_id() const { XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL_V(xr_server, 0); - XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); - if (tracker == nullptr) { + Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); + if (!tracker.is_valid()) { // No tracker? no joystick id... (0 is our first joystick) return -1; }; @@ -305,7 +303,7 @@ bool XRController3D::is_button_pressed(int p_button) const { return false; }; - return Input::get_singleton()->is_joy_button_pressed(joy_id, p_button); + return Input::get_singleton()->is_joy_button_pressed(joy_id, (JoyButton)p_button); }; float XRController3D::get_joystick_axis(int p_axis) const { @@ -314,7 +312,7 @@ float XRController3D::get_joystick_axis(int p_axis) const { return 0.0; }; - return Input::get_singleton()->get_joy_axis(joy_id, p_axis); + return Input::get_singleton()->get_joy_axis(joy_id, (JoyAxis)p_axis); }; real_t XRController3D::get_rumble() const { @@ -322,8 +320,8 @@ real_t XRController3D::get_rumble() const { XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL_V(xr_server, 0.0); - XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); - if (tracker == nullptr) { + Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); + if (!tracker.is_valid()) { return 0.0; }; @@ -335,8 +333,8 @@ void XRController3D::set_rumble(real_t p_rumble) { XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL(xr_server); - XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); - if (tracker != nullptr) { + Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); + if (tracker.is_valid()) { tracker->set_rumble(p_rumble); }; }; @@ -349,43 +347,35 @@ bool XRController3D::get_is_active() const { return is_active; }; -XRPositionalTracker::TrackerHand XRController3D::get_hand() const { +XRPositionalTracker::TrackerHand XRController3D::get_tracker_hand() const { // get our XRServer XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL_V(xr_server, XRPositionalTracker::TRACKER_HAND_UNKNOWN); - XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); - if (tracker == nullptr) { + Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); + if (!tracker.is_valid()) { return XRPositionalTracker::TRACKER_HAND_UNKNOWN; }; - return tracker->get_hand(); + return tracker->get_tracker_hand(); }; -String XRController3D::get_configuration_warning() const { - if (!is_visible() || !is_inside_tree()) { - return String(); - } - - String warning = Node3D::get_configuration_warning(); +TypedArray<String> XRController3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); - // must be child node of XROrigin! - XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent()); - if (origin == nullptr) { - if (!warning.empty()) { - warning += "\n\n"; + if (is_visible() && is_inside_tree()) { + // must be child node of XROrigin! + XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent()); + if (origin == nullptr) { + warnings.push_back(TTR("XRController3D must have an XROrigin3D node as its parent.")); } - warning += TTR("XRController3D must have an XROrigin3D node as its parent."); - }; - if (controller_id == 0) { - if (!warning.empty()) { - warning += "\n\n"; + if (controller_id == 0) { + warnings.push_back(TTR("The controller ID must not be 0 or this controller won't be bound to an actual controller.")); } - warning += TTR("The controller ID must not be 0 or this controller won't be bound to an actual controller."); - }; + } - return warning; + return warnings; }; //////////////////////////////////////////////////////////////////////////////////////////////////// @@ -404,13 +394,13 @@ void XRAnchor3D::_notification(int p_what) { ERR_FAIL_NULL(xr_server); // find the tracker for our anchor - XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_ANCHOR, anchor_id); - if (tracker == nullptr) { + Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_ANCHOR, anchor_id); + if (!tracker.is_valid()) { // this anchor is currently not available is_active = false; } else { is_active = true; - Transform transform; + Transform3D transform; // we'll need our world_scale real_t world_scale = xr_server->get_world_scale(); @@ -431,7 +421,7 @@ void XRAnchor3D::_notification(int p_what) { Ref<Mesh> trackerMesh = tracker->get_mesh(); if (mesh != trackerMesh) { mesh = trackerMesh; - emit_signal("mesh_updated", mesh); + emit_signal(SNAME("mesh_updated"), mesh); } }; }; break; @@ -459,7 +449,7 @@ void XRAnchor3D::set_anchor_id(int p_anchor_id) { // We don't check any bounds here, this anchor may not yet be active and just be a place holder until it is. // Note that setting this to 0 means this node is not bound to an anchor yet. anchor_id = p_anchor_id; - update_configuration_warning(); + update_configuration_warnings(); }; int XRAnchor3D::get_anchor_id() const { @@ -475,46 +465,38 @@ String XRAnchor3D::get_anchor_name() const { XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL_V(xr_server, String()); - XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_ANCHOR, anchor_id); - if (tracker == nullptr) { + Ref<XRPositionalTracker> tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_ANCHOR, anchor_id); + if (!tracker.is_valid()) { return String("Not connected"); }; - return tracker->get_name(); + return tracker->get_tracker_name(); }; bool XRAnchor3D::get_is_active() const { return is_active; }; -String XRAnchor3D::get_configuration_warning() const { - if (!is_visible() || !is_inside_tree()) { - return String(); - } - - String warning = Node3D::get_configuration_warning(); +TypedArray<String> XRAnchor3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); - // must be child node of XROrigin3D! - XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent()); - if (origin == nullptr) { - if (!warning.empty()) { - warning += "\n\n"; + if (is_visible() && is_inside_tree()) { + // must be child node of XROrigin3D! + XROrigin3D *origin = Object::cast_to<XROrigin3D>(get_parent()); + if (origin == nullptr) { + warnings.push_back(TTR("XRAnchor3D must have an XROrigin3D node as its parent.")); } - warning += TTR("XRAnchor3D must have an XROrigin3D node as its parent."); - }; - if (anchor_id == 0) { - if (!warning.empty()) { - warning += "\n\n"; + if (anchor_id == 0) { + warnings.push_back(TTR("The anchor ID must not be 0 or this anchor won't be bound to an actual anchor.")); } - warning += TTR("The anchor ID must not be 0 or this anchor won't be bound to an actual anchor."); - }; + } - return warning; + return warnings; }; Plane XRAnchor3D::get_plane() const { - Vector3 location = get_translation(); + Vector3 location = get_position(); Basis orientation = get_transform().basis; Plane plane(location, orientation.get_axis(1).normalized()); @@ -528,21 +510,21 @@ Ref<Mesh> XRAnchor3D::get_mesh() const { //////////////////////////////////////////////////////////////////////////////////////////////////// -String XROrigin3D::get_configuration_warning() const { - if (!is_visible() || !is_inside_tree()) { - return String(); - } - - String warning = Node3D::get_configuration_warning(); +TypedArray<String> XROrigin3D::get_configuration_warnings() const { + TypedArray<String> warnings = Node::get_configuration_warnings(); - if (tracked_camera == nullptr) { - if (!warning.empty()) { - warning += "\n\n"; + if (is_visible() && is_inside_tree()) { + if (tracked_camera == nullptr) { + warnings.push_back(TTR("XROrigin3D requires an XRCamera3D child node.")); } - warning += TTR("XROrigin3D requires an XRCamera3D child node."); } - return warning; + bool xr_enabled = GLOBAL_GET("rendering/xr/enabled"); + if (!xr_enabled) { + warnings.push_back(TTR("XR is not enabled in rendering project settings. Stereoscopic output is not supported unless this is enabled.")); + } + + return warnings; }; void XROrigin3D::_bind_methods() { @@ -561,7 +543,7 @@ void XROrigin3D::clear_tracked_camera_if(XRCamera3D *p_tracked_camera) { }; }; -float XROrigin3D::get_world_scale() const { +real_t XROrigin3D::get_world_scale() const { // get our XRServer XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL_V(xr_server, 1.0); @@ -569,7 +551,7 @@ float XROrigin3D::get_world_scale() const { return xr_server->get_world_scale(); }; -void XROrigin3D::set_world_scale(float p_world_scale) { +void XROrigin3D::set_world_scale(real_t p_world_scale) { // get our XRServer XRServer *xr_server = XRServer::get_singleton(); ERR_FAIL_NULL(xr_server); @@ -597,7 +579,7 @@ void XROrigin3D::_notification(int p_what) { Ref<XRInterface> xr_interface = xr_server->get_primary_interface(); if (xr_interface.is_valid() && tracked_camera != nullptr) { // get our positioning transform for our headset - Transform t = xr_interface->get_transform_for_eye(XRInterface::EYE_MONO, Transform()); + Transform3D t = xr_interface->get_camera_transform(); // now apply this to our camera tracked_camera->set_transform(t); diff --git a/scene/3d/xr_nodes.h b/scene/3d/xr_nodes.h index 751b2b68cb..6e54ff83d7 100644 --- a/scene/3d/xr_nodes.h +++ b/scene/3d/xr_nodes.h @@ -5,8 +5,8 @@ /* GODOT ENGINE */ /* https://godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -32,8 +32,6 @@ #define XR_NODES_H #include "scene/3d/camera_3d.h" -#include "scene/3d/node_3d.h" -#include "scene/resources/mesh.h" #include "servers/xr/xr_positional_tracker.h" /** @@ -50,11 +48,11 @@ protected: void _notification(int p_what); public: - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; virtual Vector3 project_local_ray_normal(const Point2 &p_pos) const override; virtual Point2 unproject_position(const Vector3 &p_pos) const override; - virtual Vector3 project_position(const Point2 &p_point, float p_z_depth) const override; + virtual Vector3 project_position(const Point2 &p_point, real_t p_z_depth) const override; virtual Vector<Plane> get_frustum() const override; XRCamera3D() {} @@ -93,11 +91,11 @@ public: void set_rumble(real_t p_rumble); bool get_is_active() const; - XRPositionalTracker::TrackerHand get_hand() const; + XRPositionalTracker::TrackerHand get_tracker_hand() const; Ref<Mesh> get_mesh() const; - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; XRController3D() {} ~XRController3D() {} @@ -133,7 +131,7 @@ public: Ref<Mesh> get_mesh() const; - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; XRAnchor3D() {} ~XRAnchor3D() {} @@ -158,13 +156,13 @@ protected: static void _bind_methods(); public: - String get_configuration_warning() const override; + TypedArray<String> get_configuration_warnings() const override; void set_tracked_camera(XRCamera3D *p_tracked_camera); void clear_tracked_camera_if(XRCamera3D *p_tracked_camera); - float get_world_scale() const; - void set_world_scale(float p_world_scale); + real_t get_world_scale() const; + void set_world_scale(real_t p_world_scale); XROrigin3D() {} ~XROrigin3D() {} |